mardi 11 février 2014

MODBUS Calcul du CRC sur 16 bits en C#

Voici un exemple simple d'application WinForm en C# de Calcul du CRC sur 16 bits d'une trame en hexadécimal. Il y a beaucoup de littérature sur ce sujet mais pas d'exemple simple d'implémentation de l'algorithme de calcul du CRC véritablement en C#.



http://portelatine.chez-alice.fr/electronique/ecc_cce/ecc_crc.html
Code Correcteur d'Erreur

Je me suis donc lancé dans l'exercice. Voici donc l'interface de notre application WinForm :

WinForm C# de Calcul du CRC d'une trame (Requête)
Requête : C'est une trame en hexadécimal dont je vais calculer le CRC MSB (most significant byte ou poid fort) et LSB (poid faible) en cliquant sur le bouton "Calculer CRC".

Au delà d'implémenter et de valider l'algorithme de calcul du CRC sur 16 bits d'une trame par exemple de protocole ModBus, cet exemple est intéressant à plusieurs titres :
  • manipulation des strings en chars et conversion en byte[] et réciproquement
  • filtrage des caractères dans une TextBox pour n'obtenir que les caractères hexadécimaux
Tout se passe dans la TextBox, on va filtrer les caractères pour ne laisser "passer" que les caractères hexadécimaux et les contrôles. Sur le bouton on implémentera l'algorithme de calcul d'un CRC sur 16 bits.

Petit rappel rapide sur le calcul hexadécimal

En binaire, sur 4 bits, on peut compter jusqu'à (1111) binaire = (15) décimal = (F) hexadécimal
Sur un octet (8 bits = 1 Byte = 2 fois 4 bits) (1111 1111) binaire = (255) décimal = (FF) hexadécimal
Notation : binaire/hexadécimale :
1 = 1
11 = 3
111 = 7
1000 = 8
1010 = A
1011 = B
1100 = C
1101 = D
1110 = E
1111 = F
Sur un octet : FF que l'on notera (0xFF) hexadécimal = (255) décimal

Donc dans notre TextBox, on ne pourra entrer qu'un nombre pair de caractères correspondant à un nombre d'octets de la trame (ou Requête) en notation hexadécimal.

Algorithme du CRC 16 en C#

En C#, c'est à dire très proche du C ou du C++, j'écris l'algorithme de la façon suivante :

int CalculCRC16(byte[] octets)
{
   int crc = 0xFFFF;
   int i, done = 0;
   byte todo;
   int nb = octets.Length;

   if (nb >= 0)
   {
    do 
    {
     todo = octets[done];
     crc ^= todo;
     for (i = 0; i < 8; i++)
     {
      if (crc % 2 != 0)
      {
       crc = (crc >> 1) ^ 0xA001;
      }
      else
      {
       crc = crc >> 1;
      }
     }
     done++;
    } while (done < nb);
   }

return crc;
}

Afin d'utiliser les opérateurs de l'algèbre de bool :
^ : XOR ou OU exclusif
>> 1 : décalage à droite de 1 bit
l'algorithme prend en entré un tableau de byte : byte[] et retourne le CRC en int sur 16 bits.

Conversion d'une string en byte[]

Mon TextBox possède une propriété Text que je transforme en byte[] grâce à la fonction :

static public byte[] StringHexaToByteArray(string str)
{
char[] s = str.ToCharArray();
byte[] b = new byte[s.Length/2];

int oct = 0;
for (int i = 0; i < s.Length/2; i++)
{
string octet = string.Concat(s[oct], s[oct + 1]);
oct = oct + 2;
int hexa = int.Parse(octet, System.Globalization.NumberStyles.HexNumber);
b[i] = (byte)hexa;
}

return b;
}

On voit ici apparaître le faite qu'un byte est une paire de caractères de la chaîne str passée en paramètre c'est à dire la string TextBox.Text.

On note au passage une instruction bien sympa qui permet d'afficher un int en hexa :

string hexa = crc.ToString("X");

Simple, non ?

Filtrage des caractères non hexadécimaux

En ce qui concerne les caractères tapés au clavier le filtrage se fait dans la fonction :

private void textBoxRequete_KeyPress(object sender, KeyPressEventArgs e)
{
char [] _hexa = {'A','B','C','D','E','F'};
List<char> hexa = new List<char>(_hexa);

if (char.IsLower(e.KeyChar))
{
e.KeyChar = char.ToUpper(e.KeyChar);


if (char.IsLetterOrDigit(e.KeyChar) == false && char.IsControl(e.KeyChar) == false)
{
e.Handled = true;
}

if (char.IsLetter(e.KeyChar) == true && hexa.Contains(e.KeyChar) == false)
{
e.Handled = true;
}
}

La bibliothèque de fonctions static de char ne possède pas de fonction IsHexa, on est donc obligé d'y palier en ajoutant un petit traitement de List<T>.

Cette fonction est câblée sur le KeyPressEventHandler mais cela ne suffit pas car l'utilisateur peut encore copier/coller une chaîne de texte dans la TextBox. On cablera donc sur EventHandler la fonction :

private void textBoxRequete_TextChanged(object sender, EventArgs e)
{
char[] _hexa = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
List<char> hexa = new List<char>(_hexa);

string filter = string.Empty;
char f;
foreach (char c in textBoxRequete.Text)
{
f = char.ToUpper(c);
if (hexa.Contains(f))
{
filter += f;
}
}
textBoxRequete.Text = filter;
}

Pour les explications du code s'est fini ! Et pour le reste c'est de la tambouille de programmeur.

Quelques exemples de validation :
CRC16(0x00) = 0x40BF

Calcul du CRC de la trame 0x00
Sur un site que personnellement, je trouve fantastique : Sitelec Calcul du CRC16 on trouvera la trame :

CRC16(0x02062329000D) = 0x7092

Donc pour vérifier l'implémentation (valider) :

Calcul du CRC de la trame 0x02062329000D
On peut penser que l'implémentation de notre algorithme est correcte.

Bibliographie CRC


Pour optimiser cet algo il faut précalculer des tables dont certaine exeplications se trouvent ici :

Et sur ce site bien plus que le calcul de CRC :

Download source code in C#

Requirements :
  • Visual Studio 2010 
  • Langage C#

 

lundi 27 janvier 2014

Minimize Windows Application into System Tray

Comment gérer la minimisation d'une application Windows sous forme d'icône dans la partie état de la barre des tâches afin qu'elle apparaisse icônisée ? Voici une problématique que l'on aime bien résoudre en tant que développeur car cela donne un aspect très professionnel à nos applications.

Requirements :
Visual Studio 2010
Langage C#

Voici donc une petite application Windows Form qui démontre comment utiliser, gérer les différents éléments concernant ces problématiques deux boutons :
"Show In Syst Tray" : pour faire apparaître ou disparaître l'application dans la barre d'état
"Show In Task Bar" : pour faire apparaître ou disparaître l'application de la barre des tâches
richTextBoxMessage : pour afficher les messages à l'utilisateur

Minimize Windows Application into System Tray
Autre aspect intéressant de cet exemple c'est la gestion du menu contextuel de l'application icônisée dans la barre d'état de la barre des tâche ; j'ai choisi d'utiliser le menu contextuel de l'application et de le faire apparaître une fois que l'application est icônisée dans la barre d'état :

Menu contextuel de la barre d'état
Le menu contextuel est ainsi facile à gérer depuis l'application principale :

Contextuel Menu in Syst Tray

Download Source Code

Je vous laisse découvrir le reste dans le code source :

  • utilisation d'icônes personnalisées
  • gestion des ballons et de leurs contenus ...