// // ================ // Shared Resources // ================ // // This class is a container for all shared resources that may be needed // by the drivers served by the Local Server. // // NOTES: // // * ALL DECLARATIONS MUST BE STATIC HERE!! INSTANCES OF THIS CLASS MUST NEVER BE CREATED! // // Written by: Bob Denny 29-May-2007 // Modified by Chris Rowland and Peter Simpson to hamdle multiple hardware devices March 2011 // using System; using System.Collections.Generic; using System.Text; using ASCOM; using ASCOM.Utilities; namespace ASCOM.Meade.net { public class ProfileProperties { // properies that are part of the profile public string ComPort { get; set; } public bool TraceLogger { get; set; } } /// /// The resources shared by all drivers and devices, in this example it's a serial port with a shared SendMessage method /// an idea for locking the message and handling connecting is given. /// In reality extensive changes will probably be needed. /// Multiple drivers means that several applications connect to the same hardware device, aka a hub. /// Multiple devices means that there are more than one instance of the hardware, such as two focusers. /// In this case there needs to be multiple instances of the hardware connector, each with it's own connection count. /// public static class SharedResources { // object used for locking to prevent multiple drivers accessing common code at the same time private static readonly object lockObject = new object(); // Shared serial port. This will allow multiple drivers to use one single serial port. private static ASCOM.Utilities.Serial s_sharedSerial; // Shared serial port // // Public access to shared resources // #region single serial port connector // // this region shows a way that a single serial port could be connected to by multiple // drivers. // // Connected is used to handle the connections to the port. // // SendMessage is a way that messages could be sent to the hardware without // conflicts between different drivers. // // All this is for a single connection, multiple connections would need multiple ports // and a way to handle connecting and disconnection from them - see the // multi driver handling section for ideas. // /// /// Shared serial port /// public static ASCOM.Utilities.Serial SharedSerial => s_sharedSerial ?? (s_sharedSerial = new ASCOM.Utilities.Serial()); /// /// number of connections to the shared serial port /// public static int Connections { get; set; } = 0; public static void SendBlind(string message) { lock (lockObject) { SharedSerial.ClearBuffers(); SharedSerial.Transmit(message); } } public static bool SendBool(string message) { SharedSerial.ClearBuffers(); return SendChar(message) == "1"; } /// /// Example of a shared SendMessage method, the lock /// prevents different drivers tripping over one another. /// It needs error handling and assumes that the message will be sent unchanged /// and that the reply will always be terminated by a "#" character. /// /// /// public static string SendString(string message) { lock (lockObject) { SharedSerial.ClearBuffers(); SharedSerial.Transmit(message); return SharedSerial.ReceiveTerminated("#").TrimEnd('#'); } } public static string SendChar(string message) { lock (lockObject) { SharedSerial.ClearBuffers(); SharedSerial.Transmit(message); return SharedSerial.ReceiveCounted(1); } } public static string ReadTerminated() { lock (lockObject) { return SharedSerial.ReceiveTerminated("#"); } } /// /// Example of handling connecting to and disconnection from the /// shared serial port. /// Needs error handling /// the port name etc. needs to be set up first, this could be done by the driver /// checking Connected and if it's false setting up the port before setting connected to true. /// It could also be put here. /// public static bool Connected { set { lock (lockObject) { if (value) { if (Connections == 0) SharedSerial.Connected = true; Connections++; } else { Connections--; if (Connections <= 0) { SharedSerial.Connected = false; } } } } get { return SharedSerial.Connected; } } #endregion #region Profile internal static string driverID = "ASCOM.MeadeGeneric.Telescope"; // Constants used for Profile persistence internal static string comPortProfileName = "COM Port"; internal static string traceStateProfileName = "Trace Level"; public static void WriteProfile(ProfileProperties profileProperties) { lock (lockObject) { using (Profile driverProfile = new Profile()) { driverProfile.DeviceType = "Telescope"; driverProfile.WriteValue(driverID, traceStateProfileName, profileProperties.TraceLogger.ToString()); driverProfile.WriteValue(driverID, comPortProfileName, profileProperties.ComPort); } } } private static readonly string comPortDefault = "COM1"; internal static string traceStateDefault = "false"; public static ProfileProperties ReadProfile() { lock (lockObject) { ProfileProperties profileProperties = new ProfileProperties(); using (Profile driverProfile = new Profile()) { driverProfile.DeviceType = "Telescope"; profileProperties.ComPort = driverProfile.GetValue(driverID, comPortProfileName, string.Empty, comPortDefault); profileProperties.TraceLogger = Convert.ToBoolean(driverProfile.GetValue(driverID, traceStateProfileName, string.Empty, traceStateDefault)); } return profileProperties; } } #endregion #region SetupDialog public static void SetupDialog() { // consider only showing the setup dialog if not connected // or call a different dialog if connected if (Connections > 0) { System.Windows.Forms.MessageBox.Show("Already connected, please disconnect before altering settings"); return; } var profileProperties = ReadProfile(); using (SetupDialogForm F = new SetupDialogForm()) { F.SetProfile(profileProperties); var result = F.ShowDialog(); if (result == System.Windows.Forms.DialogResult.OK) { profileProperties = F.GetProfile(); WriteProfile(profileProperties); // Persist device configuration values to the ASCOM Profile store } } } #endregion #region AutostarProducts public const string AUTOSTAR497 = "Autostar"; public const string AUTOSTAR497_31EE = "31Ee"; #endregion #region Multi Driver handling public static string ProductName { get; private set; } = string.Empty; public static string FirmwareVersion { get; private set; } = string.Empty; // this section illustrates how multiple drivers could be handled, // it's for drivers where multiple connections to the hardware can be made and ensures that the // hardware is only disconnected from when all the connected devices have disconnected. // It is NOT a complete solution! This is to give ideas of what can - or should be done. // // An alternative would be to move the hardware control here, handle connecting and disconnecting, // and provide the device with a suitable connection to the hardware. // /// /// dictionary carrying device connections. /// The Key is the connection number that identifies the device, it could be the COM port name, /// USB ID or IP Address, the Value is the DeviceHardware class /// private static Dictionary connectedDevices = new Dictionary(); /// /// This is called in the driver Connect(true) property, /// it add the device id to the list of devices if it's not there and increments the device count. /// /// public static void Connect(string deviceId) { lock (lockObject) { if (!connectedDevices.ContainsKey(deviceId)) connectedDevices.Add(deviceId, new DeviceHardware()); connectedDevices[deviceId].count++; // increment the value if (deviceId == "Serial") { if (connectedDevices[deviceId].count == 1) { var profileProperties = ReadProfile(); SharedResources.SharedSerial.PortName = profileProperties.ComPort; SharedResources.SharedSerial.DTREnable = false; SharedResources.SharedSerial.RTSEnable = false; SharedResources.SharedSerial.DataBits = 8; SharedResources.SharedSerial.StopBits = SerialStopBits.One; SharedResources.SharedSerial.Parity = SerialParity.None; SharedResources.SharedSerial.Speed = SerialSpeed.ps9600; SharedResources.SharedSerial.Handshake = SerialHandshake.None; SharedResources.SharedSerial.Connected = true; ProductName = SendString(":GVP#"); FirmwareVersion = SendString(":GVN#"); } } } } public static void Disconnect(string deviceId) { lock (lockObject) { if (connectedDevices.ContainsKey(deviceId)) { connectedDevices[deviceId].count--; if (connectedDevices[deviceId].count <= 0) { connectedDevices.Remove(deviceId); if (deviceId == "Serial") { SharedResources.SharedSerial.Connected = false; } } } } } public static bool IsConnected(string deviceId) { if (connectedDevices.ContainsKey(deviceId)) return (connectedDevices[deviceId].count > 0); else return false; } #endregion public static void Lock(Action action) { lock (lockObject) { action(); } } public static T Lock(Func func) { lock (lockObject) { return func(); } } /// /// Skeleton of a hardware class, all this does is hold a count of the connections, /// in reality extra code will be needed to handle the hardware in some way /// public class DeviceHardware { private int _count; internal int count { set => _count = value; get => _count; } internal DeviceHardware() { count = 0; } } //#region ServedClassName attribute ///// ///// This is only needed if the driver is targeted at platform 5.5, it is included with Platform 6 ///// //[global::System.AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] //public sealed class ServedClassNameAttribute : Attribute //{ // // See the attribute guidelines at // // http://go.microsoft.com/fwlink/?LinkId=85236 // /// // /// Gets or sets the 'friendly name' of the served class, as registered with the ASCOM Chooser. // /// // /// The 'friendly name' of the served class. // public string DisplayName { get; private set; } // /// // /// Initializes a new instance of the class. // /// // /// The 'friendly name' of the served class. // public ServedClassNameAttribute(string servedClassName) // { // DisplayName = servedClassName; // } //} //#endregion } }