.Net Framework 4.0 et bas niveau

Yo !
Denièrement je me suis pas mal frotté à la lisaison série au sein du framework.NET ... Et le moins que l'on puisse dire, c'est que j'ai vite vu les limites de l'API et que le boulot dessus n'était pas fini (et qu'a mon avis c'est pas prêt d'être le cas).
J'ai essayé pas mal de trucs, mais NON... décidément, c'est loin d'être fiable. Je me suis donc attelé à réutiliser la liaison série à l'ancienne, au moins ÇA, ça fonctionne. Je ne présenterais que l'utilisation que j'en ai faite, mais il est à noter que ce qui suit est réutilisable pour pas mal de trucs.
 
Tout d'abord, je vais vous présenter PInvoke, Il va nous fournir pas mal de code tout prêt pour aller taper dans les bonnes vieilles API Win32 non managées. On appelle ça des signatures.
Bon je sais pas si je vais vous l'apprendre, mais une liaison série, à l'origine, ça s'ouvre comme un fichier, ouais, un banal fichier. On utilise la fonction CreateFile. On va donc récupérer les signatures dont on a besoin sur PInvoke dans la section kernel32.dll :
 

[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern SafeFileHandle CreateFile(
    string fileName,
    [MarshalAs(UnmanagedType.U4)] FileAccess fileAccess,
    [MarshalAs(UnmanagedType.U4)] FileShare fileShare,
    IntPtr securityAttributes,
    [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
    int flags,
    IntPtr template);

[DllImport("kernel32.dll")]
static extern bool GetCommState(SafeFileHandle hFile, ref Dcb lpDCB);

[DllImport("kernel32.dll", SetLastError = true)]
static extern bool SetCommTimeouts(SafeFileHandle hFile, ref COMMTIMEOUTS
    lpCommTimeouts);


J'ai un peu modifié les signatures car [in] / [out] n'avaient plus trop l'air compatibles,... et dans le cas d'une liaison série ça passe sans. J'ai également remplacé les intPtr par des SafeFileHandle, de toutes façons ça reste des pointeurs camouflés par du code managé.

Il va  nous manquer quelques structures (lawl des structures en C# :) ) on les trouve aussi sur PInvoke, dans le menu de gauche (en haut !) :

 

public enum DtrControl : int
{
    Disable = 0,
    Enable = 1,
    Handshake = 2
} 
public enum RtsControl : int
{
    Disable = 0,
    Enable = 1,
    Handshake = 2,
    Toggle = 3
}
[StructLayout(LayoutKind.Sequential)]
internal struct Dcb
{
    internal uint DCBLength;
    internal uint BaudRate;
    private BitVector32 Flags;

    private ushort wReserved;        // not currently used 
    internal ushort XonLim;           // transmit XON threshold 
    internal ushort XoffLim;          // transmit XOFF threshold             

    internal byte ByteSize;
    internal Parity Parity;
    internal StopBits StopBits;

    internal sbyte XonChar;          // Tx and Rx XON character 
    internal sbyte XoffChar;         // Tx and Rx XOFF character 
    internal sbyte ErrorChar;        // error replacement character 
    internal sbyte EofChar;          // end of input character 
    internal sbyte EvtChar;          // received event character 
    private ushort wReserved1;       // reserved; do not use     

    private static readonly int fBinary;
    private static readonly int fParity;
    private static readonly int fOutxCtsFlow;
    private static readonly int fOutxDsrFlow;
    private static readonly BitVector32.Section fDtrControl;
    private static readonly int fDsrSensitivity;
    private static readonly int fTXContinueOnXoff;
    private static readonly int fOutX;
    private static readonly int fInX;
    private static readonly int fErrorChar;
    private static readonly int fNull;
    private static readonly BitVector32.Section fRtsControl;
    private static readonly int fAbortOnError;

    static Dcb()
    {
        // Create Boolean Mask
        int previousMask;
        fBinary = BitVector32.CreateMask();
        fParity = BitVector32.CreateMask(fBinary);
        fOutxCtsFlow = BitVector32.CreateMask(fParity);
        fOutxDsrFlow = BitVector32.CreateMask(fOutxCtsFlow);
        previousMask = BitVector32.CreateMask(fOutxDsrFlow);
        previousMask = BitVector32.CreateMask(previousMask);
        fDsrSensitivity = BitVector32.CreateMask(previousMask);
        fTXContinueOnXoff = BitVector32.CreateMask(fDsrSensitivity);
        fOutX = BitVector32.CreateMask(fTXContinueOnXoff);
        fInX = BitVector32.CreateMask(fOutX);
        fErrorChar = BitVector32.CreateMask(fInX);
        fNull = BitVector32.CreateMask(fErrorChar);
        previousMask = BitVector32.CreateMask(fNull);
        previousMask = BitVector32.CreateMask(previousMask);
        fAbortOnError = BitVector32.CreateMask(previousMask);

        // Create section Mask
        BitVector32.Section previousSection;
        previousSection = BitVector32.CreateSection(1);
        previousSection = BitVector32.CreateSection(1, previousSection);
        previousSection = BitVector32.CreateSection(1, previousSection);
        previousSection = BitVector32.CreateSection(1, previousSection);
        fDtrControl = BitVector32.CreateSection(2, previousSection);
        previousSection = BitVector32.CreateSection(1, fDtrControl);
        previousSection = BitVector32.CreateSection(1, previousSection);
        previousSection = BitVector32.CreateSection(1, previousSection);
        previousSection = BitVector32.CreateSection(1, previousSection);
        previousSection = BitVector32.CreateSection(1, previousSection);
        previousSection = BitVector32.CreateSection(1, previousSection);
        fRtsControl = BitVector32.CreateSection(3, previousSection);
        previousSection = BitVector32.CreateSection(1, fRtsControl);
    }

    public bool Binary
    {
        get { return Flags[fBinary]; }
        set { Flags[fBinary] = value; }
    }

    public bool CheckParity
    {
        get { return Flags[fParity]; }
        set { Flags[fParity] = value; }
    }

    public bool OutxCtsFlow
    {
        get { return Flags[fOutxCtsFlow]; }
        set { Flags[fOutxCtsFlow] = value; }
    }

    public bool OutxDsrFlow
    {
        get { return Flags[fOutxDsrFlow]; }
        set { Flags[fOutxDsrFlow] = value; }
    }

    public DtrControl DtrControl
    {
        get { return (DtrControl)Flags[fDtrControl]; }
        set { Flags[fDtrControl] = (int)value; }
    }

    public bool DsrSensitivity
    {
        get { return Flags[fDsrSensitivity]; }
        set { Flags[fDsrSensitivity] = value; }
    }

    public bool TxContinueOnXoff
    {
        get { return Flags[fTXContinueOnXoff]; }
        set { Flags[fTXContinueOnXoff] = value; }
    }

    public bool OutX
    {
        get { return Flags[fOutX]; }
        set { Flags[fOutX] = value; }
    }

    public bool InX
    {
        get { return Flags[fInX]; }
        set { Flags[fInX] = value; }
    }

    public bool ReplaceErrorChar
    {
        get { return Flags[fErrorChar]; }
        set { Flags[fErrorChar] = value; }
    }

    public bool Null
    {
        get { return Flags[fNull]; }
        set { Flags[fNull] = value; }
    }

    public RtsControl RtsControl
    {
        get { return (RtsControl)Flags[fRtsControl]; }
        set { Flags[fRtsControl] = (int)value; }
    }

    public bool AbortOnError
    {
        get { return Flags[fAbortOnError]; }
        set { Flags[fAbortOnError] = value; }
    }
}

struct COMMTIMEOUTS
{
 public UInt32 ReadIntervalTimeout;
 public UInt32 ReadTotalTimeoutMultiplier;
 public UInt32 ReadTotalTimeoutConstant;
 public UInt32 WriteTotalTimeoutMultiplier;
 public UInt32 WriteTotalTimeoutConstant;
}


Ouf ...
 Donc la on est prêt à ouvrir , configurer et utiliser la liaison série... L'intérêt la, ça va être d'utiliser un objet FileStream pour récupérer ce qui entre et sort par le SafeFileHandle (port COM) :

 

SafeFileHandle com = CreateFile(portName, FileAccess.ReadWrite, FileShare.None,
 IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);
if (com.IsInvalid)
{
    Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
}
else
{
    FileStream fs = new FileStream(com, FileAccess.ReadWrite);
    Dcb serialParams = new Dcb();
    serialParams.BaudRate = 19200;
    serialParams.ByteSize = 8;
    serialParams.Parity = 0; //No Parity
    serialParams.StopBits = 0; //One stop bit

    COMMTIMEOUTS timeouts = new COMMTIMEOUTS();
    timeouts.ReadIntervalTimeout = 50;
    timeouts.ReadTotalTimeoutConstant = 50;
    timeouts.ReadTotalTimeoutMultiplier = 10;

    timeouts.WriteTotalTimeoutConstant = 50;
    timeouts.WriteTotalTimeoutMultiplier = 10;

    GetCommState(com, ref serialParams);
    SetCommTimeouts(com, ref timeouts);
}

Une lecture et une écriture se présentent de cette façon :
 

//Lecture
Byte[] buf = new byte[1024];
fs.Read(buf, 0, 1024);
String res = Encoding.ASCII.GetString(buf);

//Ecriture
String stringToSend = "w00t on teh Serial !";
buf = new byte[1024];
buf = System.Text.Encoding.ASCII.GetBytes(stringToSend);
fs.Write(buf, 0, buf.Length);
 

A noter que c'est du code d'exemple et que ce genre de manipulation est à entourer de try/catch, de bien gérer les Exception et enfin qu'il faut penser à fermer le FileStream et le SafeFileHandle dans cet ordre une fois l'échange terminé. Je conseille de vous créer une petite classe pour encapsuler tout ça et la réutiliser à l'occase. En tout cas, avec cette methode j'ai bien amélioré la stabilité de l'appli (vu que je check tout manuellement et qu'on a accès à plus de paramètres de configuration qu'avec System.IO.Ports.SerialPort ) :)
 
Bon code !

Commentaires

Articles les plus consultés