#define Focuser
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using ASCOM.Utilities;
using ASCOM.DeviceInterface;
using System.Globalization;
using System.Collections;
using System.Reflection;
using ASCOM.Meade.net.Wrapper;
using ASCOM.Utilities.Interfaces;
namespace ASCOM.Meade.net
{
//
// Your driver's DeviceID is ASCOM.Meade.net.Focuser
//
// The Guid attribute sets the CLSID for ASCOM.Meade.net.Focuser
// The ClassInterface/None addribute prevents an empty interface called
// _Meade.net from being created and used as the [default] interface
//
// TODO Replace the not implemented exceptions with code to implement the function or
// throw the appropriate ASCOM exception.
//
///
/// ASCOM Focuser Driver for Meade.net.
///
[Guid("a32ac647-bf0f-42f9-8ab0-d166fa5884ad")]
[ProgId("ASCOM.MeadeGeneric.focuser")]
[ServedClassName("Meade.net Focuser")]
[ClassInterface(ClassInterfaceType.None)]
public class Focuser : ReferenceCountedObjectBase, IFocuserV3
{
///
/// ASCOM DeviceID (COM ProgID) for this driver.
/// The DeviceID is used by ASCOM applications to load the driver at runtime.
///
//internal static string driverID = "ASCOM.Meade.net.Focuser";
internal static string driverID = Marshal.GenerateProgIdForType(MethodBase.GetCurrentMethod().DeclaringType);
// TODO Change the descriptive string for your driver then remove this line
///
/// Driver description that displays in the ASCOM Chooser.
///
private static string driverDescription = "Meade Generic";
internal static string comPort; // Variables to hold the currrent device configuration
///
/// Private variable to hold an ASCOM Utilities object
///
private readonly IUtil _utilities;
///
/// Variable to hold the trace logger object (creates a diagnostic log file with information that you specify)
///
internal static TraceLogger tl;
private readonly ISharedResourcesWrapper _sharedResourcesWrapper;
///
/// Initializes a new instance of the class.
/// Must be public for COM registration.
///
public Focuser()
{
//todo move this out to IOC
_utilities = new Util(); //Initialise util object
_sharedResourcesWrapper = new SharedResourcesWrapper();
Initialise();
}
private void Initialise()
{
tl = new TraceLogger("", "Meade.net.focusser");
tl.LogMessage("Focuser", "Starting initialisation");
ReadProfile(); // Read device configuration from the ASCOM Profile store
IsConnected = false; // Initialise connected to false
tl.LogMessage("Focuser", "Completed initialisation");
}
//
// PUBLIC COM INTERFACE IFocuserV3 IMPLEMENTATION
//
#region Common properties and methods.
///
/// Displays the Setup Dialog form.
/// If the user clicks the OK button to dismiss the form, then
/// the new settings are saved, otherwise the old values are reloaded.
/// THIS IS THE ONLY PLACE WHERE SHOWING USER INTERFACE IS ALLOWED!
///
public void SetupDialog()
{
tl.LogMessage("SetupDialog", "Opening setup dialog");
_sharedResourcesWrapper.SetupDialog();
ReadProfile();
tl.LogMessage("SetupDialog", "complete");
}
public ArrayList SupportedActions
{
get
{
tl.LogMessage("SupportedActions Get", "Returning empty arraylist");
return new ArrayList();
}
}
public string Action(string actionName, string actionParameters)
{
LogMessage("", "Action {0}, parameters {1} not implemented", actionName, actionParameters);
throw new ActionNotImplementedException("Action " + actionName + " is not implemented by this driver");
}
public void CommandBlind(string command, bool raw)
{
CheckConnected("CommandBlind");
// Call CommandString and return as soon as it finishes
//this.CommandString(command, raw);
_sharedResourcesWrapper.SendBlind(command);
// or
//throw new ASCOM.MethodNotImplementedException("CommandBlind");
// DO NOT have both these sections! One or the other
}
public bool CommandBool(string command, bool raw)
{
CheckConnected("CommandBool");
//string ret = CommandString(command, raw);
// TODO decode the return string and return true or false
// or
throw new MethodNotImplementedException("CommandBool");
// DO NOT have both these sections! One or the other
}
public string CommandString(string command, bool raw)
{
CheckConnected("CommandString");
// it's a good idea to put all the low level communication with the device here,
// then all communication calls this function
// you need something to ensure that only one command is in progress at a time
return _sharedResourcesWrapper.SendString(command);
throw new MethodNotImplementedException("CommandString");
}
public void Dispose()
{
// Clean up the tracelogger and util objects
tl.Enabled = false;
tl.Dispose();
tl = null;
}
public bool Connected
{
get
{
LogMessage("Connected", "Get {0}", IsConnected);
return IsConnected;
}
set
{
tl.LogMessage("Connected", "Set {0}", value);
if (value == IsConnected)
return;
if (value)
{
try
{
_sharedResourcesWrapper.Connect("Serial");
try
{
SelectSite(1);
SetLongFormat(true);
IsConnected = true;
}
catch (Exception)
{
_sharedResourcesWrapper.Disconnect("Serial");
throw;
}
}
catch (Exception ex)
{
LogMessage("Connected Set", "Error connecting to port {0} - {1}", comPort, ex.Message);
}
}
else
{
LogMessage("Connected Set", "Disconnecting from port {0}", comPort);
_sharedResourcesWrapper.Disconnect("Serial");
IsConnected = false;
}
}
}
private void SetLongFormat(bool setLongFormat)
{
_sharedResourcesWrapper.Lock(() =>
{
var result = _sharedResourcesWrapper.SendString(":GZ#");
//:GZ# Get telescope azimuth
//Returns: DDD*MM#T or DDD*MM’SS#
//The current telescope Azimuth depending on the selected precision.
bool isLongFormat = result.Length > 6;
if (isLongFormat != setLongFormat)
{
_utilities.WaitForMilliseconds(500);
_sharedResourcesWrapper.SendBlind(":U#");
//:U# Toggle between low/hi precision positions
//Low - RA displays and messages HH:MM.T sDD*MM
//High - Dec / Az / El displays and messages HH:MM: SS sDD*MM:SS
// Returns Nothing
}
});
}
private void SelectSite(int site)
{
_sharedResourcesWrapper.SendBlind($":W{site}#");
//:W#
//Set current site to, an ASCII digit in the range 1..4
//Returns: Nothing
}
public string Description
{
// TODO customise this device description
get
{
tl.LogMessage("Description Get", driverDescription);
return driverDescription;
}
}
public string DriverInfo
{
get
{
Version version = Assembly.GetExecutingAssembly().GetName().Version;
// TODO customise this driver description
string driverInfo = "Information about the driver itself. Version: " + String.Format(CultureInfo.InvariantCulture, "{0}.{1}", version.Major, version.Minor);
tl.LogMessage("DriverInfo Get", driverInfo);
return driverInfo;
}
}
public string DriverVersion
{
get
{
Version version = Assembly.GetExecutingAssembly().GetName().Version;
string driverVersion = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", version.Major, version.Minor);
tl.LogMessage("DriverVersion Get", driverVersion);
return driverVersion;
}
}
public short InterfaceVersion
{
// set by the driver wizard
get
{
LogMessage("InterfaceVersion Get", "3");
return Convert.ToInt16("3");
}
}
public string Name
{
get
{
//string name = "Short driver name - please customise";
string name = driverDescription;
tl.LogMessage("Name Get", name);
return name;
}
}
#endregion
#region IFocuser Implementation
public bool Absolute
{
get
{
tl.LogMessage("Absolute Get", false.ToString());
return false; // This is a relative focuser
}
}
public void Halt()
{
tl.LogMessage("Halt", "Halting");
CheckConnected("Halt");
//A single halt command is sometimes missed by the #909 apm, so let's do it a few times to be safe.
Stopwatch stopwatch = Stopwatch.StartNew();
while (stopwatch.ElapsedMilliseconds < 1000)
{
_sharedResourcesWrapper.SendBlind(":FQ#");
//:FQ# Halt Focuser Motion
//Returns: Nothing
_utilities.WaitForMilliseconds(250);
}
}
public bool IsMoving
{
get
{
tl.LogMessage("IsMoving Get", false.ToString());
return false; // This focuser always moves instantaneously so no need for IsMoving ever to be True
}
}
public bool Link
{
get
{
tl.LogMessage("Link Get", Connected.ToString());
return Connected; // Direct function to the connected method, the Link method is just here for backwards compatibility
}
set
{
tl.LogMessage("Link Set", value.ToString());
Connected = value; // Direct function to the connected method, the Link method is just here for backwards compatibility
}
}
private readonly int _maxIncrement = 7000;
public int MaxIncrement
{
get
{
tl.LogMessage("MaxIncrement Get", _maxIncrement.ToString());
return _maxIncrement; // Maximum change in one move
}
}
private readonly int _maxStep = 7000;
public int MaxStep
{
get
{
tl.LogMessage("MaxStep Get", _maxStep.ToString());
return _maxStep;
}
}
public void Move(int Position)
{
tl.LogMessage("Move", Position.ToString());
CheckConnected("Move");
//todo implement backlash compensation
//todo implement direction reverse
//todo implement dynamic braking
if (Position < -MaxIncrement || Position > MaxIncrement)
{
throw new InvalidValueException($"position out of range {-MaxIncrement} < {Position} < {MaxIncrement}");
}
if (Position == 0)
return;
if (Position > 0)
{
//desired move direction is out
MoveFocuser(true, Math.Abs(Position));
}
else
{
//desired move direction is in
MoveFocuser(false, Math.Abs(Position));
}
}
private void MoveFocuser(bool directionOut, int steps)
{
_sharedResourcesWrapper.Lock(() =>
{
//_sharedResourcesWrapper.SendBlind(":FF#");
//:FF# Set Focus speed to fastest setting
//Returns: Nothing
//:FS# Set Focus speed to slowest setting
//Returns: Nothing
//:F# Autostar, Autostar II – set focuser speed to where is an ASCII digit 1..4
//Returns: Nothing
//All others – Not Supported
_utilities.WaitForMilliseconds(100);
//A Single focus command sometimes gets lost on the #909, so sending lots of them solves the issue.
Stopwatch stopwatch = Stopwatch.StartNew();
while (stopwatch.ElapsedMilliseconds < steps)
{
_sharedResourcesWrapper.SendBlind(directionOut ? ":F+#" : ":F-#");
//:F+# Start Focuser moving inward (toward objective)
//Returns: None
//:F-# Start Focuser moving outward (away from objective)
//Returns: None
_utilities.WaitForMilliseconds(250);
}
Halt();
//This gives the focuser time to physically stop.
_utilities.WaitForMilliseconds(1000);
});
}
public int Position => throw new PropertyNotImplementedException("Position", false);
public double StepSize
{
get
{
tl.LogMessage("StepSize Get", "Not implemented");
throw new PropertyNotImplementedException("StepSize", false);
}
}
public bool TempComp
{
get
{
tl.LogMessage("TempComp Get", false.ToString());
return false;
}
set
{
tl.LogMessage("TempComp Set", "Not implemented");
throw new PropertyNotImplementedException("TempComp", false);
}
}
public bool TempCompAvailable
{
get
{
tl.LogMessage("TempCompAvailable Get", false.ToString());
return false; // Temperature compensation is not available in this driver
}
}
public double Temperature
{
get
{
tl.LogMessage("Temperature Get", "Not implemented");
throw new PropertyNotImplementedException("Temperature", false);
}
}
#endregion
#region Private properties and methods
// here are some useful properties and methods that can be used as required
// to help with driver development
#region ASCOM Registration
// Register or unregister driver for ASCOM. This is harmless if already
// registered or unregistered.
//
///
/// Register or unregister the driver with the ASCOM Platform.
/// This is harmless if the driver is already registered/unregistered.
///
/// If true, registers the driver, otherwise unregisters it.
private static void RegUnregASCOM(bool bRegister)
{
using (var P = new Profile())
{
P.DeviceType = "Focuser";
if (bRegister)
{
P.Register(driverID, driverDescription);
}
else
{
P.Unregister(driverID);
}
}
}
///
/// This function registers the driver with the ASCOM Chooser and
/// is called automatically whenever this class is registered for COM Interop.
///
/// Type of the class being registered, not used.
///
/// This method typically runs in two distinct situations:
///
/// -
/// In Visual Studio, when the project is successfully built.
/// For this to work correctly, the option Register for COM Interop
/// must be enabled in the project settings.
///
/// - During setup, when the installer registers the assembly for COM Interop.
///
/// This technique should mean that it is never necessary to manually register a driver with ASCOM.
///
[ComRegisterFunction]
public static void RegisterASCOM(Type t)
{
RegUnregASCOM(true);
}
///
/// This function unregisters the driver from the ASCOM Chooser and
/// is called automatically whenever this class is unregistered from COM Interop.
///
/// Type of the class being registered, not used.
///
/// This method typically runs in two distinct situations:
///
/// -
/// In Visual Studio, when the project is cleaned or prior to rebuilding.
/// For this to work correctly, the option Register for COM Interop
/// must be enabled in the project settings.
///
/// - During uninstall, when the installer unregisters the assembly from COM Interop.
///
/// This technique should mean that it is never necessary to manually unregister a driver from ASCOM.
///
[ComUnregisterFunction]
public static void UnregisterASCOM(Type t)
{
RegUnregASCOM(false);
}
#endregion
///
/// Returns true if there is a valid connection to the driver hardware
///
private bool IsConnected { get; set; }
///
/// Use this function to throw an exception if we aren't connected to the hardware
///
///
private void CheckConnected(string message)
{
if (!IsConnected)
{
throw new NotConnectedException(message);
}
}
///
/// Read the device configuration from the ASCOM Profile store
///
internal void ReadProfile()
{
var profileProperties = _sharedResourcesWrapper.ReadProfile();
tl.Enabled = profileProperties.TraceLogger;
comPort = profileProperties.ComPort;
}
///
/// Log helper function that takes formatted strings and arguments
///
///
///
///
internal static void LogMessage(string identifier, string message, params object[] args)
{
var msg = string.Format(message, args);
tl.LogMessage(identifier, msg);
}
#endregion
}
}