j78330
Messages postés10Date d'inscriptionmardi 17 septembre 2013StatutMembreDernière intervention18 juillet 2016
-
Modifié par BunoCS le 11/07/2016 à 09:55
j78330
Messages postés10Date d'inscriptionmardi 17 septembre 2013StatutMembreDernière intervention18 juillet 2016
-
13 juil. 2016 à 11:41
Bonjour,
j'essais de recréer un programme qui était à l'origine en vb6 (visual basic ) en C # pouvez me dire
s'il est bon s'il vous plait ?
Pour communiquer avec mon appareil j'utilise le protocole de liaison suivant :
Protocol MODBUS/JBUS en mode RTU .(9600 Bds,1 bit start,8 bit de données, 1 bit stop, sans parité
(RS485). la liaison série n'étant pas la tâche la plus prioritaire de l'appareil le logiciel de liaison doit prévoir 3 essais de dialogue avant de conclure à un défaut de transmission. il est inutile et déconseillé d'interroger
l'appareil plus d'une fois toutes les 500 ms.
L'appareil peut renvoyer les 3 codes d'erreur suivant à une demande de données :
01 : Fonction non reconnue
02 : Adresse de données incorrecte
03 : Valeur des données incorrecte
Programme en vb6 :
Public Sub ModBus(c() As Byte)
Dim Buffer
Dim x(100) As Byte
Dim Tentative, j As Integer
Dim td As Double
Dim delairep As Double
On Error GoTo finProcedure
Err.Clear
Tentative = 0
Form1.MSComm1.InputLen = 0 'Lecture du buffer par 1
début:
t = Timer
While Abs(t - Timer) < 0.05
Wend
Tentative = Tentative + 1
If Tentative > 3 Then 'Si à la troisième interrogation la réponse est toujours erronée opération abandonnée
Call MessageErreur(MessageMod(26), 1)
CodeErreur = 0
Exit Sub
End If
'test remote ADAC
If (Fonction = 5 And AdataL = 7) Then
x(0) = AdresADAC
Else
x(0) = AdresseModbus
End If
x(1) = Fonction
x(2) = AdataH
x(3) = AdataL
x(4) = LdataH
x(5) = LdataL
For i = 0 To 5
If x(i) < 0 Or x(i) > 255 Then
CodeErreur = 2
Exit Sub
End If
Next i
Call CalculCRC(x(), 5, CrcH, CrcL)
If Form1.MSComm1.PortOpen = False Then
CodeErreur = 3
Exit Sub
End If
Form1.MSComm1.InBufferCount = 0
If ToxGain = True Then '???
ToxGain = False
Form1.MSComm1.Output = Chr(x(0)) + Chr(x(1)) + Chr(x(2)) + Chr(x(3)) + Chr(x(4)) + Chr(x(5)) + Chr(CrcL) + Chr(CrcH)
Else
Form1.MSComm1.Output = Chr(x(0)) + Chr(Fonction) + Chr(AdataH) + Chr(AdataL) + Chr(LdataH) + Chr(LdataL) + Chr(CrcL) + Chr(CrcH)
End If
td = Timer
If Fonction = 3 Then
Buffer = (2 * LdataL + 5)
Else
Buffer = 8
End If
Do Until Form1.MSComm1.InBufferCount >= Buffer 'Nous attendons d'avoir au moins 1 réponse
'Debug.Print (Timer - td)
delairep = 0.4
If (x(2) = 0) Then
If x(3) = 31 Then
delairep = 4
End If
End If
If (Timer - td) > delairep Then 'Si le temps de récupération des données dépasse 0.1 seconde nous sortons de la boucle
Debug.Print " appel :"; AppelBloc; " inbuf= "; Form1.MSComm1.InBufferCount; "bufferdemande = "; Buffer
DoEvents
GoTo début
End If
If Form1.MSComm1.PortOpen = False Then 'L'utilisateur a décidé de couper la communication avec la sonde
CodeErreur = 0
Exit Sub
End If
Loop
'Debug.Print "temps de réponse = "; (Timer - td)
frmSequenceur.tmrMiseEnService.Enabled = True
Buffer = Form1.MSComm1.Input
For i = 0 To 2 'Boucle pour récupérer les trois premiers caractères
c(i) = AscB(MidB(Buffer, i + 1, 1))
Next i
'Ce n'est pas la bonne adresse de l'appareil
If Val(GetSetting(App.EXEName, "Général", "AdresseModBus", Setting)) <> AdresseModbus Then AdresseModbus = Val(GetSetting(App.EXEName, "Général", "AdresseModBus", Setting))
If c(0) <> AdresseModbus Then
Call MessageErreur(MessageMod(28) & AdresseModbus & MessageMod(99) & c(0), 1)
GoTo début
End If
'La sonde renvoi un code erreur
If c(1) >= 128 Then
Tentative = Tentative + 1
If c(2) = 1 And Tentative > 3 Then Call MessageErreur(MessageMod(29), 1)
If c(2) = 2 And Tentative > 3 Then Call MessageErreur(MessageMod(30), 1)
If c(2) = 3 And Tentative > 3 Then Call MessageErreur(MessageMod(31), 1)
Exit Sub
End If
On Error Resume Next
If Fonction = 3 Then
For j = 3 To (2 * LdataL + 4)
c(j) = AscB(MidB(Buffer, j + 1, 1))
Next j
Call CalculCRC(c(), j - 3, CrcH, CrcL)
If c(j - 1) <> CrcH Or c(j - 2) <> CrcL Then
GoTo début
End If
End If
If Fonction = 5 Or Fonction = 6 Then
For j = 3 To 7
c(j) = AscB(MidB(Buffer, j + 1, 1))
Next j
Call CalculCRC(c(), 5, CrcH, CrcL)
If c(6) <> CrcL Or c(7) <> CrcH Then
GoTo début
End If
End If
If Mid(Form1.lstInformations.List(Form1.lstInformations.ListIndex), 18, Len(Form1.lstInformations.List(Form1.lstInformations.ListIndex)) - 11) = MessageMod(26) Then
Call MessageErreur(MessageMod(32), 0)
End If
CodeErreur = 1
Exit Sub
finProcedure:
If Err.Number <> 0 Then MsgBox "l'erreur n° " & Err.Number & " est sur venu (" & Err.Description & " ). ", vbCritical, "ERREUR !!"
Resume Next
Err.Clear
End Sub
et voici le programme qui j'ai fait en c# :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;
using System.Diagnostics;
using System.Timers;
using System.Drawing;
namespace Test
{
public static class Com
{
static byte[] c = new byte[100];
public static byte[] b = new byte[100];
public static int AdresseModBus = 161;
public static int Fonction;
public static int AdataH;
public static int AdataL;
public static int LdataH;
public static int LdataL;
public static int ret = 0;
public static int r = 0;
public static int Demande;
public static long CodeErreur;
………
……..
}
public static byte[] ModBus(byte[] c)
{
// byte[] b = new byte[100];
int Tentative, j;
double td, delairep;
byte Buffer;
int t;
int crch, crcl;
int[] crchcrcl = new int[2];
Tentative = 0;
Program.principale_.serialPort1.ReceivedBytesThreshold = 1;
début:
t = DateTime.Now.Millisecond + DateTime.Now.Millisecond;
Console.Write(t - DateTime.Now.Millisecond);
while ((t - DateTime.Now.Second) <0.05) { }
Tentative++;
if (Tentative > 3)
{
CodeErreur = 0;
Program.principale_.Invoke(new messagecombobox(messageboxx), Program.principale_.Heure + " : Pas de réponse");
Program.principale_.ledcom.BackColor = Color.Red;
Application.Exit();
}
c[0] = (byte)AdresseModBus;
c[1] = (byte)Fonction;
c[2] = (byte)AdataH;
c[3] = (byte)AdataL;
c[4] = (byte)LdataH;
c[5] = (byte)LdataL;
c[6] = (byte)CalculCRC(c, 5)[1];
c[7] = (byte)CalculCRC(c, 5)[0];
for (int i = 0; i < 6; i++)
{
if (c[i] < 0 || c[i] > 255)
Application.Exit();
}
Program.principale_.serialPort1.DiscardInBuffer();
Program.principale_.serialPort1.Write(c, 0, 8);
if (Program.principale_.serialPort1.IsOpen == false)
{
CodeErreur = 3;
Program.principale_.lstInformations.Text = Program.principale_.Heure + "Pas de réponse du boitier";
Program.principale_.serialPort1.Close();
Program.principale_.ledcom.BackColor = Color.Red;
Application.Exit();
}
for (int i = 0; i <=2*LdataL+5; i++)
{
try
{
x[i] = (byte)Program.principale_.serialPort1.ReadByte();
}
catch { break; }
}
td = ((double)DateTime.Now.TimeOfDay.TotalSeconds);
if (Fonction == 3)
{
Buffer = Convert.ToByte(2 * LdataL + 5);
}
else
{
Buffer = 8;
}
int nbreOctetsRecu;
do
{
delairep = 1;
if (c[2] == 0)
{
if (c[3] == 98)
delairep = 4;
}
Console.WriteLine(((double)DateTime.Now.TimeOfDay.TotalSeconds) - td);
if ((((double)DateTime.Now.TimeOfDay.TotalSeconds) - td) > delairep)
{
Debug.Print(" inbuf = " + Program.principale_.serialPort1.BytesToRead + " bufferdemande = " + Buffer);
Application.DoEvents();
goto début;
}
if (Program.principale_.serialPort1.IsOpen == false)
{
CodeErreur = 0;
Program.principale_.lstInformations.Text = "Vous avez coupez la communication";
Application.Exit();
}
nbreOctetsRecu = Program.principale_.serialPort1.BytesToRead;
}
while (nbreOctetsRecu < Buffer);
Buffer = (byte)Program.principale_.serialPort1.ReadByte();
for (int i = 0; i <= 2; i++)
{
c[i] = (byte)("" + Buffer).ElementAt(i + 1);
}
if (c[0] != AdresseModBus)
{
MessageBox.Show("Ce n'est pas la bonne addresse" + " " + AdresseModBus + " " + c[0]);
goto début;
}
if (c[1] >= 128)
{
Tentative++;
Program.principale_.lstInformations.Text = "Pas de réponse du boitier";
}
if (Fonction == 3) // permet de lire des mots 16 bits
{
for (j = 3; j <= (2 * LdataL + 4); j++)
x[j] = (byte)("" + Buffer).ElementAt(j + 1);
crchcrcl = CalculCRC(x, j - 3);
crch = crchcrcl[0];
crcl = crchcrcl[1];
if (x[j - 1] != crch || x[j - 2] != crcl)
{ goto début; }
}
if (Fonction == 6 || Fonction == 5)// la fonction 6 permet d'écrire des mots de 16 bits; la fonction 5 permet de forcer un bit de commande à 1 ou a 0
{
for (j = 3; j <= 7; j++)
x[j] = (byte)("" + Buffer).ElementAt(j + 1);
crchcrcl = CalculCRC(x, j - 3);
crch = crchcrcl[0];
crcl = crchcrcl[1];
if (x[j - 1] != crch || x[j - 2] != crcl)
{ goto début; }
}
return x;
}
Je vous remercie d'avance pour vos réponse
EDIT : Ajout des balises de code (la coloration syntaxique).
Explications disponibles ICI
et merci à Buno d'avoir mis de la couleur dans ton message.
Je n'ai pas regardé le code VB6 car se sont deux philosophies différentes et appliquer l'une dans l'autre donne des résultats boiteux.
Les goto sont à proscrire, c'éatiot déjà vrai en vb6 (vb1 même je pense).
L'idée de base avec la programmation objet, est qu'un objet a une tache/fonction.
Ca n'est pas à l'objet qui gére le port com d'afficher un message, ou un texte dans un label.
Il génère un événement, change une propriété, les deux... et l'objet qui gére l'ihm affiche un message.
Comme ça, si tu passes de console à Winform, puis de winform à wpf, ton objet port com ne change pas.
La boucle while pour tes tentatives, c'est bloquant, ton programme ne peut rien faire d'autre pendant ce temps.
Utilise un timer.
Il est plus simple de s'abonner à l'évènement DataRecieved, quand il se génère ça veut dire qu'il y a quelques chose à lire.
Quand tu veux affecter un entier à une variable, tu peux utiliser un enum, ça permet plus tard, en maintenance de ne pas se préoccuper de ce que codeerreur 3 veut dire, la valeur de l'enum sera CodeErreur.ValeurIncorrect. Plus lisible.
Pour calculer un temps d'exécution tu peux utiliser la classe stopwatch
Voilà déjà pour une première lecture rapide
Quand j'étais petit, la mer Morte n'était que malade.
George Burns
j78330
Messages postés10Date d'inscriptionmardi 17 septembre 2013StatutMembreDernière intervention18 juillet 2016 11 juil. 2016 à 17:16
NHenry
Messages postés15083Date d'inscriptionvendredi 14 mars 2003StatutModérateurDernière intervention19 septembre 2023159 11 juil. 2016 à 21:08
De mon côté j'ajouterai 4 points :
Mets un peu plus de commentaires.
Ensuite,
t = DateTime.Now.Millisecond
Préfères peut être Environment.TickCount qui te retourne une valeur en millisecondes depuis le démarrage de la machine, un peu plus stable, je pense.
Evites de multiplier les instructions sur une ligne :
Et UN énorme point :
Je vois :
"début:"
dans ton code, cela indique un GOTO, instruction à bannir sauf cas extrêmes. De plus, évites les noms de références (variables/fonctions/étiquettes/...) avec des accents.
2 solutions, utiliser les contrôles de boucles (comme break ou continue) ou découpes ta fonction en autres plus petites.
Whismeril
Messages postés18608Date d'inscriptionmardi 11 mars 2003StatutContributeurDernière intervention24 septembre 2023629 11 juil. 2016 à 22:05
Il peut utiliser des else à ses if, ou un switch aussi.
11 juil. 2016 à 17:16