Driver ASCOM

Michel Llibre - Club d'Astronomie de Quint-Fontsegrives

Ce document présente un exemple d'écriture d'un driver ASCOM en c# à l'aide de Visual Studio et de l'extension ASCOM qui l'on ajoute à Visual Studio par le biais de son menu Extensions/Gérer les extensions/...dans lequel on sélectionnera ASCOM Driver Project Templates. Cette extension est accessible dans Visual Studio lorsque la plateforme ASCOM est installée et ses composants pour les développeurs est installée sur l'ordinateur.

L'exemple traité est un driver pour une roue à filtre. Pour le test du driver cette roue est simulée à l'aide d'une carte Arduino. Les codes du périphérique (code C Arduino), du driver (C# .Net Framework) et d'une application cliente de test (C# .Net Framework) font l'objet de la dernière section de ce document. Ces deux derniers codes utilisent les modèles prédéfinis de l'ASCOM Driver Project Templates. Les autres sections expliquent les modifications qu'il faut apporter à ces modèles pour les adapter à notre exemple, puis comment se fait le déploiement du driver créé.  

Ce document est inspiré de :

Sites de téléchargement pour les outils utilisés :

 

Les composants pour développeurs ASCOM incluent une documentation installée localement dans le répertoire C:\Program Files (x86)\ASCOM\Platform 6 Developer Components\Developer Documentation.

Table des matières

1 Généralités sur l'application cliente

2 Programmation du Driver

3 Vérification du fonctionnement

4 Serveur de driver (pas testé)

5 Déploiement

6 Exemple : Driver FilterWheel simulé sur Arduino

6.1 Simulation de la roue à filtre avec l'Arduino :

6.2 Driver ASCOM en c# avec VS

6.2.1 Le driver

6.2.2 Une application cliente test

 

1 Généralités sur l'application cliente

Pour tester un driver il faut une application client qui va utiliser le périphérique via le driver.

Nous considérons ici une application cliente du driver ASCOM écrite en c# à l'aide de Visual Studio de type ASCOM Driver Test Forms App. Les généralités suivantes ont pur but essentiel la description de l'activation d'un driver ASCOM pour illustrer les étapes de l'écriture d'une application cliente minimale pour tester un driver.

Dans le code Form.cs de l'application cliente qui veut accéder à un périphérique via le driver ASCOM, on doit, en premier lieu, sélectionner ce driver ce qui se fait dans la callback associée à l'appui de l’utilisateur sur un bouton "Choose" de l'application cliente. Pour cela on utilise la méthode Choose du sdk ASCOM qui est associée au type de périphérique désiré : Cette méthode renvoie le string nom du driver (idname) sélectionné par l'usager, soit par exemple dans le cas d'une roue à filtre (FilterWheel) :  

private string idname = ASCOM.DriverAccess.FilterWheel.Choose("");  

Cette méthode affichera une liste de choix à l'usager et on pourra stocker son choix dans les settings de l'application :

Properties.Settings.Default.DriverId =  idname;  

Dans ce cas pour le trouver plus facilement lors d'une autre exécution sans avoir à consulter la liste, on fera plutôt l' appel suivant :

Properties.Settings.Default.DriverId = ASCOM.DriverAccess.FilterWheel.Choose(Properties.Settings.Default.DriverId);

qui proposera le nom choisi lors d'une exécution précédente. C'est ce qui est programmé dans l'appli du Framework  ASCOM Driver Test Forms App.

A partir du nom reçu, l'application cliente pourra crée l'objet driver du périphérique dont elle utilisera les méthodes et propriétés :

ASCOM.DriverAccess.FilterWheel myDriver = new ASCOM.DriverAccess.FilterWheel(Properties.Settings.Default.DriverId);

Cette création sera effectuée dans la callback de l'application qui est appelée lorsque l'utilisateur clique sur le bouton "Connect".

Si le couple driver-periph est bien installé, le test suivant sera vrai :

(this.myDriver != null) && (myDriver.Connected == true)

qui est le test généralement programmé dans la méthode get de bool IsConnected.

Ainsi le début de la classe application cliente d'une roue à filtre est le suivant :

    public partial class Form1 : Form

    {

        private ASCOM.DriverAccess.FilterWheel driver;

        private int nbFiltres;

        public Form1()

        {

            InitializeComponent();

            SetUIState();

        }

 

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)

        {

            if (IsConnected) driver.Connected = false;

            Properties.Settings.Default.Save();

        }

 

        private void buttonChoose_Click(object sender, EventArgs e)

        {

            Properties.Settings.Default.DriverId = ASCOM.DriverAccess.FilterWheel.Choose(Properties.Settings.Default.DriverId);

            SetUIState();

        }

 

        private void buttonConnect_Click(object sender, EventArgs e)

        {

            if (IsConnected) driver.Connected = false;

            else

            {

                driver = new ASCOM.DriverAccess.FilterWheel(Properties.Settings.Default.DriverId);

                driver.Connected = true;

            }

            SetUIState();

        }

 

        private void SetUIState()

        {

            buttonConnect.Enabled = !string.IsNullOrEmpty(Properties.Settings.Default.DriverId);

            buttonChoose.Enabled = !IsConnected;

            buttonConnect.Text = IsConnected ? "Disconnect" : "Connect";

        }

 

        private bool IsConnected

        {

            get

            {return ((this.driver != null) && (driver.Connected == true));}

        }

Ensuite il reste à effectuer la programmation des actions que l'on désire faire effectuer à la roue à filtre. Je ne m'étend pas davantage sur cette programmation, car on trouve beaucoup d'exemples de programmation d'applications clientes des drivers ASCOM, mais très peu d'exemples décrivant en détail la programmation du driver lui-même.

2 Programmation du Driver

Rappelons que l'appel par l'application cliente de la méthode FilterWheel.Choose, produit l'affichage de la popup standard ASCOM suivante :

 

Le titre de la popup est personnalisé avec le nom du périphérique choisi, ici "FilterWheel".

La liste est éventuellement préremplie par le nom du driver choisi lors d'un appel précédent et passé en argument de FilterWheel.Choose(arg). Si ce n'est pas le bon, on étend la liste et choisit le driver désiré. Il y a intérêt à cliquer sur le bouton Properties... avant de faire Ok pour choisir les paramètres du port (numéro COM,...) au cas où ils auraient changé. L'appui sur Properties ou sur Ok produit l'exécution de code au niveau du driver ASCOM choisi.

Le clic sur ce bouton Properties produit l'affichage de la boite de dialogue programmée dans le fichier Driver.cs du driver. Cette boite de dialogue est la méthode SetupDialog de la public class FilterWheel qui code le driver.

 

Le code, que l'on doit compléter, fournit par défaut la popup ci-dessus. Il est situé dans le fichier SetupDialogForm.cs modifiable en [Design] et/ou en code.

Le code par défaut remplit la liste des ports COM disponibles sur le P dans la méthode InitUI() de la classe SetupDialogForm. Si l'affichage convient, il n'y a rien à modifier dans cette classe.

Lorsque l'utilisateur à choisi le port COM et éventuellement coché la case qui permet d'enregistrer un log dans C:\Users\nom_userr\Documents\ASCOM, puis cliqué sur Ok, il y a retour à l'affichage de la popup ASCOM FilterWheel Chooser, où l'utilisateur va cliquer sur Ok, s'il est satisfait de son choix.

A ce stade le périphérique n'est pas connecté à l'application cliente via le driver, il est simplement choisi. Pour se connecter l'application cliente doit :

Dans le driver, pour réaliser cette connexion  le programmeur doit compléter la méthode set de public bool Connected et la méthode private bool IsConnected qui y est utilisée (pas que là).

Mais en premier lieu, il doit ajouter en global la définition de l'objet de la classe ASCOM.Utilities.Serial qui servira à la connexion :

        private ASCOM.Utilities.Serial objSerial = new ASCOM.Utilities.Serial();

La méthode IsConnected peut être la suivante :

       private bool IsConnected

        {

            get

            {

                connectedState = objSerial != null && objSerial.Connected; // Ajout MLL

                return connectedState;

            }

        }

et set et get de Connected peuvent être programmés comme suit :

        public bool Connected

        {

            get {return IsConnected;}

            set

            {

                if (value == IsConnected) return; // déjà comme désiré

                if (value) // demande de connexion

                {

                    objSerial = new ASCOM.Utilities.Serial();

                    objSerial.PortName = comPort;

                    objSerial.Speed = SerialSpeed.ps9600;

                    objSerial.Connected = connectedState = true;

                }

                else // demande de déconnexion

                 { objSerial.Connected = connectedState = false; }

            }

        }

 

Pour la communication entre le driver et le périphérique, la fonction la plus importante est la méthode public string CommandString(string command, bool raw).

De prime abord, elle n'est pas instanciée et son appel produit une exception.

        public string CommandString(string command, bool raw)

        {

            CheckConnected("CommandString");

            // TODO The optional CommandString method should either be implemented OR throw a MethodNotImplementedException

            // If implemented, CommandString must send the supplied command to the mount and wait for a response before returning this to the client

            throw new ASCOM.MethodNotImplementedException("CommandString");

        }

Voici une modification possible :

        public string CommandString(string command, bool raw)

        {

            CheckConnected("CommandString");

            objSerial.Transmit(command);

            string ret =  objSerial.ReceiveTerminated("#");

            return ret.Remove(ret.Length - 1, 1);

        }

qui envoie le string command sur la liaison et attend la réponse qui doit se terminer par le caractère #. Cette réponse est retournée à l'appelant, mais sans ce caractère de terminaison.

A priori la grande majorité des commandes et communications avec le périphérique peuvent se faire par l'intermédiaire de cette méthode.

Dans le cas de la roue à filtre les commandes principales sont les set et get de la méthode Position. Elles sont réalisées en modifiant le code original comme suit :

public short Position

{

   get {

      string ret = CommandString("GETFILTER#", true);

      fwPosition = short.Parse(ret);

      return fwPosition;

   }

   set {

      if ((value < 0) | (value > fwNames.Length - 1))

         throw new InvalidValueException("Numéro filtre invalide");      

      fwPosition = value;

      CommandString(string.Format("FILTER{0}#",value), true);

   }

}

 

3 Serveur de driver (pas testé)

Pour que le driver soit simultanément accessible par plusieurs applications, ou pour coupler plusieurs drivers sur une même liaison série on doit créer un ASCOM Local Server Driver. La méthode à suivre est décrite dans le menu général ASCOM rubrique "How to create a local server  based driver", ainsi que dans https://td0g.ca/2019/01/10/writing-an-ascom-local-server-driver/ que j'ai recopié dans ma doc "Serveur driver ASCOM.odt".

4 Déploiement

Le driver généré par VS+ASCOM en mode Debug est automatiquement enregistré dans ASCOM et dans la base des registres et est de ce fait accessible en interne par toutes les applis clientes. Si on fait un Clean de la solution cela efface ces accès (je n'ai pas vérifié). Si on compile le driver en mode Release, il n'est plus accessible ! Il faut l'installer de manière classique comme décrit ci-après.

Allez dans le menu Windows et dans Ascom cliquer sur le wizard Driver Install Script Generator qui affiche la fenêtre suivante (à droite : après mes entrées) :                                                                                                       

Cliquer sur Save génère le script mllFW Setup.iss (iss pour Inno Setup Script) dans le répertoire source indiqué et l'affiche dans la fenêtre d'InnoSetup :

;

; Script generated by the ASCOM Driver Installer Script Generator 6.6.0.0

; Generated by mll on 2023-10-30 (UTC)

;

[Setup]

AppID={{679ffda8-feb1-43e1-92ae-6820f71a1b79}

AppName=ASCOM mllFW FilterWheel Driver

AppVerName=ASCOM mllFW FilterWheel Driver 1.0

AppVersion=1.0

AppPublisher=mll <michel@llibre.fr>

AppPublisherURL=mailto:michel@llibre.fr

AppSupportURL=https://ascomtalk.groups.io/g/Help

AppUpdatesURL=https://ascom-standards.org/

VersionInfoVersion=1.0.0

MinVersion=6.1.7601

DefaultDirName="{cf}\ASCOM\FilterWheel"

DisableDirPage=yes

DisableProgramGroupPage=yes

OutputDir="."

OutputBaseFilename="mllFW Setup"

Compression=lzma

SolidCompression=yes

; Put there by Platform if Driver Installer Support selected

WizardImageFile="C:\Program Files (x86)\ASCOM\Platform 6 Developer Components\Installer Generator\Resources\WizardImage.bmp"

LicenseFile="C:\Program Files (x86)\ASCOM\Platform 6 Developer Components\Installer Generator\Resources\CreativeCommons.txt"

; {cf}\ASCOM\Uninstall\FilterWheel folder created by Platform, always

UninstallFilesDir="{cf}\ASCOM\Uninstall\FilterWheel\mllFW"

 

[Languages]

Name: "english"; MessagesFile: "compiler:Default.isl"

 

[Dirs]

Name: "{cf}\ASCOM\Uninstall\FilterWheel\mllFW"

; TODO: Add subfolders below {app} as needed (e.g. Name: "{app}\MyFolder")

 

[Files]

Source: "C:\Users\Utilisateur\source\repos\fwDriver\bin\Release\ASCOM.mllFW.FilterWheel.dll"; DestDir: "{app}"

; Require a read-me HTML to appear after installation, maybe driver's Help doc

Source: "C:\Users\Utilisateur\source\repos\fwDriver\ReadMe.htm"; DestDir: "{app}"; Flags: isreadme

; TODO: Add other files needed by your driver here (add subfolders above)

 

; Only if driver is .NET

[Run]

; Only for .NET assembly/in-proc drivers

Filename: "{dotnet4032}\regasm.exe"; Parameters: "/codebase ""{app}\ASCOM.mllFW.FilterWheel.dll"""; Flags: runhidden 32bit

Filename: "{dotnet4064}\regasm.exe"; Parameters: "/codebase ""{app}\ASCOM.mllFW.FilterWheel.dll"""; Flags: runhidden 64bit; Check: IsWin64



; Only if driver is .NET

[UninstallRun]

; Only for .NET assembly/in-proc drivers

Filename: "{dotnet4032}\regasm.exe"; Parameters: "-u ""{app}\ASCOM.mllFW.FilterWheel.dll"""; Flags: runhidden 32bit

; This helps to give a clean uninstall

Filename: "{dotnet4064}\regasm.exe"; Parameters: "/codebase ""{app}\ASCOM.mllFW.FilterWheel.dll"""; Flags: runhidden 64bit; Check: IsWin64

Filename: "{dotnet4064}\regasm.exe"; Parameters: "-u ""{app}\ASCOM.mllFW.FilterWheel.dll"""; Flags: runhidden 64bit; Check: IsWin64



[Code]

const

   REQUIRED_PLATFORM_VERSION = 6.2;    // Set this to the minimum required ASCOM Platform version for this application

 

//

// Function to return the ASCOM Platform's version number as a double.

//

function PlatformVersion(): Double;

var

   PlatVerString : String;

begin

   Result := 0.0;  // Initialise the return value in case we can't read the registry

   try

      if RegQueryStringValue(HKEY_LOCAL_MACHINE_32, 'Software\ASCOM','PlatformVersion', PlatVerString) then

      begin // Successfully read the value from the registry

         Result := StrToFloat(PlatVerString); // Create a double from the X.Y Platform version string

      end;

   except                                                                  

      ShowExceptionMessage;

      Result:= -1.0; // Indicate in the return value that an exception was generated

   end;

end;

 

//

// Before the installer UI appears, verify that the required ASCOM Platform version is installed.

//

function InitializeSetup(): Boolean;

var

   PlatformVersionNumber : double;

 begin

   Result := FALSE;  // Assume failure

   PlatformVersionNumber := PlatformVersion(); // Get the installed Platform version as a double

   If PlatformVersionNumber >= REQUIRED_PLATFORM_VERSION then  // Check whether we have the minimum required Platform or newer

      Result := TRUE

   else

      if PlatformVersionNumber = 0.0 then

         MsgBox('No ASCOM Platform is installed. Please install Platform ' + Format('%3.1f', [REQUIRED_PLATFORM_VERSION]) + ' or later from https://www.ascom-standards.org', mbCriticalError, MB_OK)

      else

         MsgBox('ASCOM Platform ' + Format('%3.1f', [REQUIRED_PLATFORM_VERSION]) + ' or later is required, but Platform '+ Format('%3.1f', [PlatformVersionNumber]) + ' is installed. Please install the latest Platform before continuing; you will find it at https://www.ascom-standards.org', mbCriticalError, MB_OK);

end;

// Code to enable the installer to uninstall previous versions of itself when a new version is installed

procedure CurStepChanged(CurStep: TSetupStep);

var

  ResultCode: Integer;

  UninstallExe: String;

  UninstallRegistry: String;

begin

  if (CurStep = ssInstall) then // Install step has started

   begin

      // Create the correct registry location name, which is based on the AppId

      UninstallRegistry := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\{#SetupSetting("AppId")}' + '_is1');

      // Check whether an extry exists

      if RegQueryStringValue(HKLM, UninstallRegistry, 'UninstallString', UninstallExe) then

        begin // Entry exists and previous version is installed so run its uninstaller quietly after informing the user

          MsgBox('Setup will now remove the previous version.', mbInformation, MB_OK);

          Exec(RemoveQuotes(UninstallExe), ' /SILENT', '', SW_SHOWNORMAL, ewWaitUntilTerminated, ResultCode);

          sleep(1000);    //Give enough time for the install screen to be repainted before continuing

        end

  end;

end;

 

Compilation :

La compilation de ce script génère  l'exécutable de déploiement mllFW Setup.exe.

Compilation et Exécution : sur la machine locale.

Voici les principales étapes de cette exécution :

Creating directory: C:\Program Files (x86)\Common Files\ASCOM\Uninstall\FilterWheel

Creating directory: C:\Program Files (x86)\Common Files\ASCOM\Uninstall\FilterWheel\mllFW

Directory for uninstall files: C:\Program Files (x86)\Common Files\ASCOM\Uninstall\FilterWheel\mllFW

Creating new uninstall log: C:\Program Files (x86)\Common Files\ASCOM\Uninstall\FilterWheel\mllFW\unins000.dat

-- File entry --

Dest filename: C:\Program Files (x86)\Common Files\ASCOM\Uninstall\FilterWheel\mllFW\unins000.exe

Time stamp of our file: 2023-10-30 11:07:02.761

Installing the file.

Successfully installed the file.

-- File entry --

Dest filename: C:\Program Files (x86)\Common Files\ASCOM\FilterWheel\ASCOM.mllFW.FilterWheel.dll

Time stamp of our file: 2023-10-30 10:56:12.000

Installing the file.

Successfully installed the file.

-- File entry --

Dest filename: C:\Program Files (x86)\Common Files\ASCOM\FilterWheel\ReadMe.htm

Time stamp of our file: 2023-10-15 22:26:50.000

Installing the file.

Successfully installed the file.

Saving uninstall information.

Creating new uninstall key: HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall\{679ffda8-feb1-43e1-92ae-6820f71a1b79}_is1

Writing uninstall key values.

Detected previous non administrative install? No

Detected previous administrative 64-bit install? No

Installation process succeeded.

-- Run entry --

Run as: Current user

Type: Exec

Filename: C:\Windows\Microsoft.NET\Framework\v4.0.30319\regasm.exe

Parameters: /codebase "C:\Program Files (x86)\Common Files\ASCOM\FilterWheel\ASCOM.mllFW.FilterWheel.dll"

Process exit code: 0

-- Run entry --

Run as: Current user

Type: Exec

Filename: C:\Windows\Microsoft.NET\Framework64\v4.0.30319\regasm.exe

Parameters: /codebase "C:\Program Files (x86)\Common Files\ASCOM\FilterWheel\ASCOM.mllFW.FilterWheel.dll"

Process exit code: 100

Need to restart Windows? No

-- Run entry --

Run as: Original user

Type: ShellExec

Filename: C:\Program Files (x86)\Common Files\ASCOM\FilterWheel\ReadMe.htm

Deinitializing Setup.

*** Setup exit code: 0

 

En résumé :

 

Transfert et Exécution :

Si on copie le fichier d'installation  sur une autre PC Windows, il y a de fortes chances que l'antivirus le refuse et le détruise. J'ai du désactiver Windows Defender pour pouvoir l'exécuter. L'exécution réalise sur le nouveau PC les étapes décrites ci-avant. Le driver est disponible sur le nouveau PC !

5 Exemple : Driver FilterWheel simulé sur Arduino

Pour faire un exemple complet, le périphérique choisi est une roue à filtré simulée sur une carte Arduino.

5.1 Simulation de la roue à filtre avec l'Arduino :

Ce simulateur ne reconnaît que 2 commandes reçues via la liaison USB :

Le changement de filtre ne se fait qu'au bout de 2 secondes. L'application doit envoyer des GETFILTER# pour s’assurer de la réalisation d'une commande de changement de filtre. Tant qu'elle reçoit -1# en réponse c'est que le changement est en cours.

Voici le programme testé :

int currentFilter = 0;

unsigned long tnew;

void setup()

{

 Serial.begin(9600);

 Serial.flush();

 tnew = millis();

}

void loop()

{

        String cmd;

        if (Serial.available() > 0)

 {

                cmd = Serial.readStringUntil('#');

                if (cmd == "GETFILTER") {

                        if (millis()>= tnew) Serial.print(currentFilter);

                        else Serial.print(-1);

  }

                else if (cmd == "FILTER0") MoveFilter(0);

                else if (cmd == "FILTER1") MoveFilter(1);

                else if (cmd == "FILTER2") MoveFilter(2);

                else if (cmd == "FILTER3") MoveFilter(3);

                Serial.println("#");

 }

}

 

void MoveFilter(int pos) {

        if (pos == currentFilter) Serial.print(currentFilter);

        else { tnew = millis() + 2000; Serial.print(-1);}

        currentFilter = pos;

}

 

Après avoir compilé et chargé ce programme dans l'Arduino, ouvrir une console liaison série genre Termite sur le bon port Série de l'Arduino et tester les commandes suivantes :

GETFILTER#

FILTER2#

GETFILTER#

etc...

5.2 Driver ASCOM en c# avec VS

Le Driver a 2 parties :

On peut rencontrer divers problèmes de configurations. Pour les éviter :

5.2.1 Le driver

Avec VS sélectionner "Créer un projet", puis dans les boutons/listes de filtre en haut à droite :

et dans les items proposés prendre : ASCOM device driver (c#)

Choisir un nom pour le projet (fwDriver) et dans la liste des classes périphériques proposées choisir : FilterWheel et donner un nom au driver  : mllFW. Retenir ce nom qui utilisé pour générer le nom du driver, à savoir ASCOM.mllFW.FilterWheel
Suivre les indications du fichier Readme.htm.

Choisir Debug puis Générer FwDriver ce qui doit se faire sans erreur. Ensuite avant de personnaliser le driver, le fichier Readme.htm préconise de créer une application client qui servira de projet de démarrage pour tester le driver (voir section suivante).

Voici les principales modifications à apporter au code Driver.cs du driver générique. Il s'agit essentiellement de la communication :

 

Et voici le listing complet de Driver.cs dont j'ai ôté les commentaires et directives d'affichage :

#define FilterWheel

 

using ASCOM;

using ASCOM.Astrometry;

using ASCOM.Astrometry.AstroUtils;

using ASCOM.DeviceInterface;

using ASCOM.Utilities;

using System;

using System.Collections;

using System.Collections.Generic;

using System.Diagnostics;

using System.Globalization;

using System.Runtime.InteropServices;

using System.Text;

 

namespace ASCOM.mllFW

{

   [Guid("c924e3d5-ac14-46b6-878e-a6cbf1842925")]

    [ClassInterface(ClassInterfaceType.None)]

    public class FilterWheel : IFilterWheelV2

    {

        internal static string driverID = "ASCOM.mllFW.FilterWheel";

        private static string driverDescription = "ASCOM FilterWheel Driver for mllFW.";

        private ASCOM.Utilities.Serial objSerial = new ASCOM.Utilities.Serial();

        internal static string comPortProfileName = "COM Port";

        internal static string comPortDefault = "COM1";

        internal static string traceStateProfileName = "Trace Level";

        internal static string traceStateDefault = "false";

        internal static string comPort;

 

        private bool connectedState;

        private Util utilities;

        private AstroUtils astroUtilities;

        internal TraceLogger tl;

 

        public FilterWheel()

        {

            tl = new TraceLogger("", "mllFW");

            ReadProfile();

            tl.LogMessage("FilterWheel", "Starting initialisation");

            connectedState = false;

            utilities = new Util();

            astroUtilities = new AstroUtils();

            tl.LogMessage("FilterWheel", "Completed initialisation");

        }

 

        public void SetupDialog()

        {

            if (IsConnected)

                System.Windows.Forms.MessageBox.Show("Already connected, just press OK");

            using (SetupDialogForm F = new SetupDialogForm(tl))

            {

                var result = F.ShowDialog();

                if (result == System.Windows.Forms.DialogResult.OK) WriteProfile();                

            }

        }

 

        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 ASCOM.ActionNotImplementedException("Action " + actionName + " is not implemented by this driver");

        }

 

        public void CommandBlind(string command, bool raw)

        {

            CheckConnected("CommandBlind");

            throw new ASCOM.MethodNotImplementedException("CommandBlind");

        }

 

       public bool CommandBool(string command, bool raw)

        {

            CheckConnected("CommandBool");

            throw new ASCOM.MethodNotImplementedException("CommandBool");

        }

 

        public string CommandString(string command, bool raw)

        {

            CheckConnected("CommandString");

            objSerial.Transmit(command);

            string ret =  objSerial.ReceiveTerminated("#");

            return ret.Remove(ret.Length - 1, 1);

        }

 

        public void Dispose()

        {

            tl.Enabled = false;

            tl.Dispose();

            tl = null;

            utilities.Dispose();

            utilities = null;

            astroUtilities.Dispose();

            astroUtilities = 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)

                {

                    connectedState = true;

                    LogMessage("Connected Set", "Connecting to port {0}", comPort);

                    objSerial = new ASCOM.Utilities.Serial();

                    objSerial.PortName = comPort;

                    objSerial.Speed = SerialSpeed.ps9600;

                    objSerial.Connected = true;

                }

                else

                {

                    connectedState = false;

                    LogMessage("Connected Set", "Disconnecting from port {0}", comPort);

                    objSerial.Connected = false;

                }

            }

        }

        public string Description

        {

            get

            {

                tl.LogMessage("Description Get", driverDescription);

                return driverDescription;

            }

        }

 

        public string DriverInfo

        {

            get

            {

                Version version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;

                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 = System.Reflection.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

        {

            get

            {

                LogMessage("InterfaceVersion Get", "2");

                return Convert.ToInt16("2");

            }

        }

 

        public string Name

        {

            get

            {

                string name = driverDescription; // Modif MLL

                tl.LogMessage("Name Get", name);

                return name;

            }

        }

 

        private int[] fwOffsets = new int[4] { 0, 0, 0, 0 };

        private string[] fwNames = new string[4] { "Red", "Green", "Blue", "Clear" };

        private short fwPosition = 0;

 

        public int[] FocusOffsets

        {

            get

            {

                foreach (int fwOffset in fwOffsets)                

                    tl.LogMessage("FocusOffsets Get", fwOffset.ToString());

                return fwOffsets;

            }

        }

 

        public string[] Names

        {

            get

            {

                foreach (string fwName in fwNames)

                    tl.LogMessage("Names Get", fwName);

                return fwNames;

            }

        }

 

        public short Position

        {

            get

            {

                string ret = CommandString("GETFILTER#", true);

                fwPosition = short.Parse(ret);

                tl.LogMessage("Position Get", fwPosition.ToString());

                return fwPosition;

            }

            set

            {

                tl.LogMessage("Position Set", value.ToString());

                if ((value < 0) | (value > fwNames.Length - 1))

                {

                    tl.LogMessage("", "Throwing InvalidValueException - Position: " + value.ToString() + ", Range: 0 to " + (fwNames.Length - 1).ToString());

                    throw new InvalidValueException("Position", value.ToString(), "0 to " + (fwNames.Length - 1).ToString());

                }

                fwPosition = value;

                CommandString(string.Format("FILTER{0}#",value), true);

            }

        }

 

        private static void RegUnregASCOM(bool bRegister)

        {

            using (var P = new ASCOM.Utilities.Profile())

            {

                P.DeviceType = "FilterWheel";

                if (bRegister) P.Register(driverID, driverDescription);              

                else P.Unregister(driverID);         

            }

        }

 

        [ComRegisterFunction]

        public static void RegisterASCOM(Type t)

        {

            RegUnregASCOM(true);

        }

 

        [ComUnregisterFunction]

        public static void UnregisterASCOM(Type t)

        {

            RegUnregASCOM(false);

        }

 

        private bool IsConnected

        {

            get

            {

 

                connectedState = objSerial != null && objSerial.Connected;

                return connectedState;

            }

        }

 

        private void CheckConnected(string message)

        {

            if (!IsConnected)

            {

                throw new ASCOM.NotConnectedException(message);

            }

        }

 

        internal void ReadProfile()

        {

            using (Profile driverProfile = new Profile())

            {

                driverProfile.DeviceType = "FilterWheel";

                tl.Enabled = Convert.ToBoolean(driverProfile.GetValue(driverID, traceStateProfileName, string.Empty, traceStateDefault));

                comPort = driverProfile.GetValue(driverID, comPortProfileName, string.Empty, comPortDefault);

            }

        }

 

        internal void WriteProfile()

        {

            using (Profile driverProfile = new Profile())

            {

                driverProfile.DeviceType = "FilterWheel";

                driverProfile.WriteValue(driverID, traceStateProfileName, tl.Enabled.ToString());

                driverProfile.WriteValue(driverID, comPortProfileName, comPort.ToString());

            }

        }

 

        internal void LogMessage(string identifier, string message, params object[] args)

        {

            var msg = string.Format(message, args);

            tl.LogMessage(identifier, msg);

        }

#endregion

    }

}

5.2.2 Une application cliente test

Dans VS faire Menu Fichier/Ajouter/Nouveau projet  ou clic droit sur la solution fwDriver dans l'explorateur de solutions et chois "Ajouter/Nouveau projet : j'ai choisi une  ASCOM Driver Test Forms App nommée fwAPP, et j'ai sélectionné FilterWheel, puis mllFW.

Dans l'Exploration de solution, Clic droit sur  fwAPP et sélectionner "Définir en tant que projet de démarrage". Dans les propriétés modifier le type de Sortie en Application Windows, puis tester la Générer la solution.

Pour tester la roue à filtre simulée, j'ai personnalisé la fenêtre affichée par l'application (fichiers Form1.cs) en y ajoutant des boutons "Previous" et "Next" pour changer de filtre et deux Textbox affichant le numéro du filtre et le nombre de filtres.

Voici la fenêtre modifiée :

 

et voici son code :

using System;

using System.Windows.Forms;

using System.Threading;

 

namespace ASCOM.mllFW

{

    public partial class Form1 : Form

    {

        private ASCOM.DriverAccess.FilterWheel driver;

        private int nbFiltres;

 

        public Form1()

        {

            InitializeComponent();

            SetUIState();

        }

 

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)

        {

            if (IsConnected)

                driver.Connected = false;

            Properties.Settings.Default.Save();

        }

 

        private void buttonChoose_Click(object sender, EventArgs e)

        {

            Properties.Settings.Default.DriverId = ASCOM.DriverAccess.FilterWheel.Choose(Properties.Settings.Default.DriverId);

            SetUIState();

        }

 

        private void buttonConnect_Click(object sender, EventArgs e)

        {

            if (IsConnected)

            {

                driver.Connected = false;

            }

            else

            {

                driver = new ASCOM.DriverAccess.FilterWheel(Properties.Settings.Default.DriverId);

                driver.Connected = true;

                nbFiltres = driver.Names.Length;

                labelNbFiltres.Text = nbFiltres.ToString();

                Thread.Sleep(1000); // Pour éviter un timeout !!

                labelNumero.Text = driver.Position.ToString();

 

            }

            SetUIState();

        }

 

        private void SetUIState()

        {

            buttonConnect.Enabled = !string.IsNullOrEmpty(Properties.Settings.Default.DriverId);

            bool c = IsConnected;

            buttonChoose.Enabled = !c;

            buttonConnect.Text = c ? "Disconnect" : "Connect";

        }

 

        private bool IsConnected

        {

            get

            {

                return ((this.driver != null) && (driver.Connected == true));

            }

        }

 

        private void buttonNext_Click(object sender, EventArgs e)

        {

            movePosition(1);

        }

        private void buttonPreviuos_Click(object sender, EventArgs e)

        {

            movePosition(-1);

        }

 

        private void movePosition(int n)

        {

            labelNumero.Text = "moving";

            labelNumero.Refresh(); // Sinon, pas affiché : effacé par l'affichage suivant.

            int newpos = driver.Position + n;

            if (newpos < 0) newpos = nbFiltres - 1;

            if (newpos >= nbFiltres) newpos = 0;

            driver.Position = (short)newpos;

            while (true)

            {

                newpos = driver.Position;

                if (newpos != -1) break;

                Thread.Sleep(200);

            }

            labelNumero.Text = newpos.ToString();

        }

 

    }

}