Modbus sniffer for Function code 03, 06, 16

Hello I’m a student and I’m currently working for a small association and they asked me to create a modbus sniffer for the function code 0x03, 0x06 and 0x10. I’ve never used c# before in my life and they gave me a little code already written. I tried to read and understand it all and it seems correct for me. But when I start the debugging it doesn’t go very well. I will put here part of the code and another part in the first comment.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>using Modbus_Connect_4;
using System;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.IO.Ports;
using System.Web;
using System.Windows.Forms;
using System.Collections.Generic;
namespace Modbus_Connect_4
{
public partial class Form1 : Form
{
private DataSet data;
private static SerialPort serialPort;
// private static TimeSpan differenza;
private static DateTime oravecchia = DateTime.Now;
private static DateTime ora;
private static string file;
private static BackgroundWorker backgroundWorker1 = new BackgroundWorker();
// richieste, risposte e il tema inizialmente predefinite a true
private static Boolean richieste = true;
private static Boolean risposte = true;
private Boolean chiaro = true;
// impostazioni necessarie per iniziare il funzionamento dell'app inizializzate a null
private String porta = null;
private String baudrate = null;
private String databits = null;
private String parita = null;
private String stopbits = null;
// seconde impostazioni che i predecessori ci hanno lasciato (non ho capito a cosa servano, credo per la comunicazione tra 2 porte e quindi leggevano il doppio delle informazioni)
// private String porta2;
// private String baudrate2;
// private String databits2;
// private String parita2;
// private String stopbits2;
// enum con i fc richiesti
enum Function_Code
{
SingleWrite = 3,
MultiWrite = 6,
Read = 16
}
public Form1()
{
InitializeComponent();
// file dove verranno registrati i dati raccolti e letti dal programma
file = "ModbusConnect4_LOG_" + DateTime.Now.ToString("dd_MM_yyyy_HH_mm_ss") + ".log";
private CancellationTokenSource cancellationTokenSource;
int marginButton = 35; // Margine dei button
int marginTable = 20; // Margine della tabella
int buttonWidth = 100; // lunghezza dei button
int buttonHeight = 30; // altezza dei button
// Imposta posizione e dimensioni per button1
button1.Anchor = AnchorStyles.Top | AnchorStyles.Left;
button1.Location = new Point(marginButton, marginButton);
button1.Size = new Size(buttonWidth, buttonHeight);
// Imposta posizione e dimensioni per button2 nell'angolo in alto a destra
button2.Anchor = AnchorStyles.Top | AnchorStyles.Right;
button2.Location = new Point(this.ClientSize.Width - buttonWidth - marginButton, marginButton);
button2.Width = buttonWidth;
button2.Height = buttonHeight;
// Imposta posizione e dimensioni per button5 vicino al button 1
button5.Anchor = AnchorStyles.Top | AnchorStyles.Left;
button5.Size = new Size(buttonWidth, buttonHeight);
button5.Location = new Point(button1.Right + marginButton, marginButton);
// Imposta posizione e dimensioni per button3 = request
button3.Anchor = AnchorStyles.Top | AnchorStyles.Left;
button3.Size = new Size(buttonWidth, buttonHeight);
button3.Location = new Point(button5.Right + marginButton, marginButton);
// Imposta posizione e dimensioni per button4 = response
button4.Anchor = AnchorStyles.Top | AnchorStyles.Left;
button4.Size = new Size(buttonWidth, buttonHeight);
button4.Location = new Point(button3.Right + marginButton, marginButton);
// Imposta posizione verticale per dataGridView1 in base alla posizione dei pulsanti
int verticalPosition = button1.Bottom + marginTable;
// Imposta posizione e dimensioni per dataGridView1
dataGridView1.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom;
dataGridView1.Location = new Point(marginTable, verticalPosition);
dataGridView1.Size = new Size(this.ClientSize.Width - 2 * marginTable, this.ClientSize.Height - verticalPosition - marginTable);
string[] portNames = SerialPort.GetPortNames();
string[] baud = { "300", "1200", "9600", "19200", "28800", "57600", "115200" };
string[] databit = { "7 Bit", "8 Bit" };
string[] parity = { "Nessuna", "Odd", "Even" };
string[] stop = { "1 Bit", "2 Bit" };
data = new DataSet("prova");
//settaggio tabella
DataTable table = new DataTable("prova2");
table.Columns.Add("Ora");
table.Columns.Add("Differenza (ms)");
table.Columns.Add("ID");
table.Columns.Add("FC");
table.Columns.Add("N.Byte/Indirizzo iniziale");
table.Columns.Add("N registri");
table.Columns.Add("Contenuto Registri");
table.Columns.Add("CRC");
data.Tables.Add(table);
dataGridView1.DataSource = data.Tables["prova2"];
// divisione in colonne della tabella dove vengono riportati i dati
dataGridView1.Columns["Ora"].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
dataGridView1.Columns["ID"].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
dataGridView1.Columns["FC"].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
dataGridView1.Columns["N.Byte/Indirizzo iniziale"].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
dataGridView1.Columns["N registri"].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
dataGridView1.Columns["Contenuto Registri"].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
dataGridView1.Columns["CRC"].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
// impostazione del tema chiaro come predefinito
celleChiare();
//settaggio Worker
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = true;
backgroundWorker1.DoWork += backgroundWorker1_DoWork;
backgroundWorker1.ProgressChanged += backgroundWorker1_ProgressChanged;
backgroundWorker1.RunWorkerCompleted += backgroundWorker1_RunWorkerCompleted;
//input informazioni porta seriale
// scelta della porta seriale da dover leggere
foreach (string port in portNames)
{
ToolStripMenuItem portItem = new ToolStripMenuItem(port);
portItem.Click += PortItem_Click;
portItem.CheckOnClick = true;
portaToolStripMenuItem.DropDownItems.Add(portItem);
}
// scelta del baud della porta seriale
foreach (string port in baud)
{
ToolStripMenuItem baudratemenu = new ToolStripMenuItem(port);
baudratemenu.Click += baudItem_Click;
baudratemenu.CheckOnClick = true;
baudrateToolStripMenuItem.DropDownItems.Add(baudratemenu);
}
baudrateToolStripMenuItem.DropDownItems.Add(new ToolStripSeparator());
baudrateToolStripMenuItem.DropDownItems.Add("Personalizzato [WIP]");
// scelta del databit della porta seriale
foreach (string port in databit)
{
ToolStripMenuItem databitsmenu = new ToolStripMenuItem(port);
databitsmenu.Click += databitsItem_Click;
databitsmenu.CheckOnClick = true;
dataBitsToolStripMenuItem.DropDownItems.Add(databitsmenu);
}
// scelta della parity della porta seriale
foreach (string port in parity)
{
ToolStripMenuItem paritamenu = new ToolStripMenuItem(port);
paritamenu.Click += paritaItem_Click;
paritamenu.CheckOnClick = true;
paritaToolStripMenuItem.DropDownItems.Add(paritamenu);
}
foreach (string port in stop)
{
ToolStripMenuItem stopmenu = new ToolStripMenuItem(port);
stopmenu.Click += StopItem_Click;
stopmenu.CheckOnClick = true;
stopBitToolStripMenuItem.DropDownItems.Add(stopmenu);
}
/*
* per la seconda porta non in uso
*
foreach (string port in portNames)
{
ToolStripMenuItem portItem = new ToolStripMenuItem(port);
portItem.Click += PortItem_Click2;
portItem.CheckOnClick = true;
porta2ToolStripMenuItem1.DropDownItems.Add(portItem);
}
foreach (string port in baud)
{
ToolStripMenuItem baudratemenu = new ToolStripMenuItem(port);
baudratemenu.Click += baudItem_Click2;
baudratemenu.CheckOnClick = true;
baudrate2ToolStripMenuItem1.DropDownItems.Add(baudratemenu);
}
baudrate2ToolStripMenuItem1.DropDownItems.Add(new ToolStripSeparator());
baudrate2ToolStripMenuItem1.DropDownItems.Add("Personalizzato [WIP]");
foreach (string port in databit)
{
ToolStripMenuItem databitsmenu = new ToolStripMenuItem(port);
databitsmenu.Click += databitsItem_Click2;
databitsmenu.CheckOnClick = true;
dataBits2ToolStripMenuItem1.DropDownItems.Add(databitsmenu);
}
foreach (string port in parity)
{
ToolStripMenuItem paritamenu = new ToolStripMenuItem(port);
paritamenu.Click += paritaItem_Click2;
paritamenu.CheckOnClick = true;
parita2ToolStripMenuItem1.DropDownItems.Add(paritamenu);
}
foreach (string port in stop)
{
ToolStripMenuItem stopmenu = new ToolStripMenuItem(port);
stopmenu.Click += stopItem_Click2;
stopmenu.CheckOnClick = true;
stopBits2ToolStripMenuItem.DropDownItems.Add(stopmenu);
}*/
}
// lavoro che il backgroundworker deve svolgere quando chiamato
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
//imposta i valori della porta seriale al worker
int baudRate = Int32.Parse(baudrate);
Parity parity = CalculateParity();
int dataBits = ParseDataBits();
StopBits stopBits = ParseStopBits();
/*int baudRate2 = Int32.Parse(baudrate2);
var parity2 = CalculateParity2();
var dataBits2 = ParseDataBits2();
var stopBits2 = ParseStopBits2();*/
serialPort = new SerialPort(porta, baudRate, parity, dataBits, stopBits);
serialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
// aprire la porta se possibile, altrimenti messaggio di errore
try
{
serialPort.Open();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
// porta seriale rimane aperta finchè il backgroundworker lavora
while (!backgroundWorker1.CancellationPending)
{
System.Threading.Thread.Sleep(1);
}
serialPort.Close();
}
// Funzione che aggiorna la tabella
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (e.UserState is string[] values)
{
if (data != null)
{
DataTable table = data.Tables["prova2"];
// se la tabella esiste
if (table != null)
{
// aggiunge la riga con i valori dell'array values
table.Rows.Add(values);
// aggiorna la tabella
dataGridView1.Refresh();
dataGridView1.Update();
}
}
}
}
// Funzione che finisce il funzionamento del backgroundworker
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
MessageBox.Show("An error occurred: " + e.Error.Message);
}
else if (e.Result is Exception ex)
{
MessageBox.Show("An error occurred: " + ex.Message);
}
}
// Codice del button5 o button per fermare la lettura
private void ButtonStop_Click(object sender, EventArgs e)
{
// richiama una funzione che ha la sua stessa funzione
fermaLetturaToolStripMenuItem_Click(sender, e);
}
// Codice del button1 o button per iniziare la lettura
private void Start_Click(object sender, EventArgs e)
{
// richiama una funzione che ha la sua stessa funzione
avviaLetturaToolStripMenuItem_Click(sender, e);
}
// Funzione che gestisce i dati ricevuti
private static void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
string hexLine = BitConverter.ToString(ReadBytesFromSerialPort(sp)).Replace("-", " ");
ParseModbusLine(hexLine);
}
// Funzione che legge i bytes dalla porta seriale
private static byte[] ReadBytesFromSerialPort(SerialPort sp)
{
int bytesToRead = sp.BytesToRead;
// creazione array di bytes dove viene contenuto ciò che viene letto
byte[] buffer = new byte[bytesToRead];
sp.Read(buffer, 0, bytesToRead);
return buffer;
}
// Funzione che analizza la stringa di bytes che gli viene inviata
public static void ParseModbusLine(string hexLine)
{
Console.WriteLine(hexLine);
// stringa viene divisa
string[] bytes = hexLine.Split(' ');
// se la lunghezza è minore di 5 non può essere ne una request, ne una response di modbus fc03, fc06, fc16
if (bytes.Length < 5)
{
return;
}
// indirizzo dello slave salvato
string slaveAddress = bytes[0];
// tipo di function code salvato
string functionCode = bytes[1];
// controllo se la stringa passata è una Request o una Response
if (functionCode == Function_Code.Read.ToString("x"))
{
if ((bytes.Length - 5) % 2 == 0)
{
ParseModbusResponse(bytes);
}
else
{
ParseModbusRequest(bytes);
}
}
}
// Funzione in caso la stringa passata precedentemente sia una Request, stringa viene salvata su file e su griglia
public static void ParseModbusRequest(string[] bytes)
{
if (bytes.Length < 8)
{
return;
}
// Aggiornamento orario attuale
ora = DateTime.Now;
// Calcola la differenza tra i due tempi
TimeSpan differenza = ora - oravecchia;
// Aggiorna il tempo precedente per il prossimo calcolo
oravecchia = ora;
// Visualizza la differenza in millisecondi (o in qualsiasi formato desiderato)
// MessageBox.Show("Differenza di tempo: " + differenza.TotalMilliseconds + " millisecondi");
// salvataggio della stringa passata
string slaveAddress = bytes[0];
string functionCode = bytes[1];
string addressFirstRegister = bytes[2] + bytes[3];
string amountRegisters = bytes[4] + bytes[5];
string crc = bytes[6] + bytes[7];
// Preparazione valori da dover inviare alla DataGridView
string[] values = new string[]
{
ora.ToString("o"),
differenza.TotalMilliseconds + " ms",
slaveAddress,
functionCode + " - Read Request",
addressFirstRegister,
amountRegisters,
string.Empty, // Non vengono specificati i registri in caso di Request
crc
};
// Controllo che il backgroundworker sia libero per poter scrivere i dati in tabella e nel file
if (backgroundWorker1.IsBusy)
{
// aspettare che il backgroundworker possa procedere
System.Threading.Thread.Sleep(200);
if (richieste == true)
{
// creazione della cartella se non è già presente nel PC
try
{
Directory.CreateDirectory(@"c:modbus");
}
catch (Exception ex)
{
// non si fa nulla
}
// rootPath per scrivere nel file
string rootPath = @"C:modbus";
string filePath = Path.Combine(rootPath, file);
// scrittura nel file
using (StreamWriter outputFile = new StreamWriter(filePath, true))
{
// ogni riga in values viene scritta nel file
foreach (string line in values)
{
outputFile.WriteLine(line);
}
// Riga vuota per staccare ogni riga
outputFile.WriteLine("n");
}
// riportare i progressi al backgroundworker
backgroundWorker1.ReportProgress(0, values);
}
} else // stessi passaggi se il backgroundworker non è da aspettare
{
if (richieste == true)
{
// creazione della cartella se non è già presente nel PC
try
{
Directory.CreateDirectory(@"c:modbus");
}
catch (Exception ex)
{
// non si fa nulla
}
// rootPath per scrivere nel file
string rootPath = @"C:modbus";
string filePath = Path.Combine(rootPath, file);
// scrittura nel file
using (StreamWriter outputFile = new StreamWriter(filePath, true))
{
// ogni riga in values viene scritta nel file
foreach (string line in values)
{
outputFile.WriteLine(line);
}
// Riga vuota per staccare ogni riga
outputFile.WriteLine("n");
}
// riportare i progressi al backgroundworker
backgroundWorker1.ReportProgress(0, values);
}
}
}
// Funzione in caso la stringa passata precedentemente sia una Response, stringa viene salvata su file e sulla griglia
public static void ParseModbusResponse(string[] bytes)
{
// le Response delle nostre fc possono essere di 5 o 7 bytes
if (bytes.Length <= 5)
{
return;
}
// informazioni della stringa vengono salvate
string slaveAddress = bytes[0];
string functionCode = bytes[1];
// numero di bytes letti
string numberOfBytes = bytes[2];
// prova di conversione in bytes della stringa numberOfBytes, se non possibile la funzione termina
if (!byte.TryParse(numberOfBytes, System.Globalization.NumberStyles.HexNumber, null, out byte byteCount))
{
// in caso la converisone avvenisse il valore è contenuto in byteCount
return;
}
// lunghezza aspettata dalla conversione
int expectedLength = 3 + byteCount + 2;
// se la lunghezza è minore rispetto alla lunghezza aspettata la funzione termina
if (bytes.Length < expectedLength)
{
return;
}
// creazione del CRC
string crc = bytes[bytes.Length - 2] + bytes[bytes.Length - 1];
for (int i = 3; i < 3 + byteCount; i += 2)
{
if (i + 1 < bytes.Length)
{
string registerValue = bytes[i] + bytes[i + 1];
// aggiornamento dell'orario
ora = DateTime.Now;
// Calcola la differenza tra i due tempi
TimeSpan differenza = ora - oravecchia;
// Aggiorna il tempo precedente per il prossimo calcolo
oravecchia = ora;
// Prepara i valori da inviare alla DataGridView
string[] values = new string[]
{
ora.ToString("o"),
differenza.TotalMilliseconds + " ms",
slaveAddress,
functionCode+ " - Read Response",
numberOfBytes,
"1", // Since we are adding one register per row
registerValue,
crc
};
// Riporta i valori al backgroundworker per aggiornare la DataGridView
if (backgroundWorker1.IsBusy)
{
// se bisogna aspettare backgroundworker una sleep
System.Threading.Thread.Sleep(200);
if (risposte==true)
{
try
{
// creazione della directory se non ancora presente sul PC
Directory.CreateDirectory(@"c:modbus");
}
catch (Exception ex)
{
// in caso di eccezione non si fa nulla
}
// rootpath dove è presente il file dove devono essere scritti i dati
string rootPath = @"C:modbus";
string filePath = Path.Combine(rootPath, file);
// scrittura sul file tramite StreamWriter
using (StreamWriter outputFile = new StreamWriter(filePath, true))
{
foreach (string line in values)
{
outputFile.WriteLine(line);
}
// Riga per separare i dati
outputFile.WriteLine("n");
}
// aggiornamenti dei dati inviati al backgroundworker per aggiornare la tabella
backgroundWorker1.ReportProgress(0, values);
}
} else
{
// ripetizione del codice se il backgroundworker non è busy
if (risposte==true)
{
try
{
// creazione cartella se non presente nel PC
Directory.CreateDirectory(@"c:modbus");
}
catch (Exception ex)
{
// nulla in caso di eccezione
}
// rootpath dove è stato creato il file dove verranno scritti i dati letti
string rootPath = @"C:modbus";
string filePath = Path.Combine(rootPath, file);
// StreamWriter per scrivere sui file
using (StreamWriter outputFile = new StreamWriter(filePath, true))
{
// scrittura di ogni riga presente in values sul file
foreach (string line in values)
{
outputFile.WriteLine(line);
}
outputFile.WriteLine("n");
}
backgroundWorker1.ReportProgress(0, values);
}
}
}
}
}
</code>
<code>using Modbus_Connect_4; using System; using System.ComponentModel; using System.Data; using System.Drawing; using System.IO; using System.IO.Ports; using System.Web; using System.Windows.Forms; using System.Collections.Generic; namespace Modbus_Connect_4 { public partial class Form1 : Form { private DataSet data; private static SerialPort serialPort; // private static TimeSpan differenza; private static DateTime oravecchia = DateTime.Now; private static DateTime ora; private static string file; private static BackgroundWorker backgroundWorker1 = new BackgroundWorker(); // richieste, risposte e il tema inizialmente predefinite a true private static Boolean richieste = true; private static Boolean risposte = true; private Boolean chiaro = true; // impostazioni necessarie per iniziare il funzionamento dell'app inizializzate a null private String porta = null; private String baudrate = null; private String databits = null; private String parita = null; private String stopbits = null; // seconde impostazioni che i predecessori ci hanno lasciato (non ho capito a cosa servano, credo per la comunicazione tra 2 porte e quindi leggevano il doppio delle informazioni) // private String porta2; // private String baudrate2; // private String databits2; // private String parita2; // private String stopbits2; // enum con i fc richiesti enum Function_Code { SingleWrite = 3, MultiWrite = 6, Read = 16 } public Form1() { InitializeComponent(); // file dove verranno registrati i dati raccolti e letti dal programma file = "ModbusConnect4_LOG_" + DateTime.Now.ToString("dd_MM_yyyy_HH_mm_ss") + ".log"; private CancellationTokenSource cancellationTokenSource; int marginButton = 35; // Margine dei button int marginTable = 20; // Margine della tabella int buttonWidth = 100; // lunghezza dei button int buttonHeight = 30; // altezza dei button // Imposta posizione e dimensioni per button1 button1.Anchor = AnchorStyles.Top | AnchorStyles.Left; button1.Location = new Point(marginButton, marginButton); button1.Size = new Size(buttonWidth, buttonHeight); // Imposta posizione e dimensioni per button2 nell'angolo in alto a destra button2.Anchor = AnchorStyles.Top | AnchorStyles.Right; button2.Location = new Point(this.ClientSize.Width - buttonWidth - marginButton, marginButton); button2.Width = buttonWidth; button2.Height = buttonHeight; // Imposta posizione e dimensioni per button5 vicino al button 1 button5.Anchor = AnchorStyles.Top | AnchorStyles.Left; button5.Size = new Size(buttonWidth, buttonHeight); button5.Location = new Point(button1.Right + marginButton, marginButton); // Imposta posizione e dimensioni per button3 = request button3.Anchor = AnchorStyles.Top | AnchorStyles.Left; button3.Size = new Size(buttonWidth, buttonHeight); button3.Location = new Point(button5.Right + marginButton, marginButton); // Imposta posizione e dimensioni per button4 = response button4.Anchor = AnchorStyles.Top | AnchorStyles.Left; button4.Size = new Size(buttonWidth, buttonHeight); button4.Location = new Point(button3.Right + marginButton, marginButton); // Imposta posizione verticale per dataGridView1 in base alla posizione dei pulsanti int verticalPosition = button1.Bottom + marginTable; // Imposta posizione e dimensioni per dataGridView1 dataGridView1.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom; dataGridView1.Location = new Point(marginTable, verticalPosition); dataGridView1.Size = new Size(this.ClientSize.Width - 2 * marginTable, this.ClientSize.Height - verticalPosition - marginTable); string[] portNames = SerialPort.GetPortNames(); string[] baud = { "300", "1200", "9600", "19200", "28800", "57600", "115200" }; string[] databit = { "7 Bit", "8 Bit" }; string[] parity = { "Nessuna", "Odd", "Even" }; string[] stop = { "1 Bit", "2 Bit" }; data = new DataSet("prova"); //settaggio tabella DataTable table = new DataTable("prova2"); table.Columns.Add("Ora"); table.Columns.Add("Differenza (ms)"); table.Columns.Add("ID"); table.Columns.Add("FC"); table.Columns.Add("N.Byte/Indirizzo iniziale"); table.Columns.Add("N registri"); table.Columns.Add("Contenuto Registri"); table.Columns.Add("CRC"); data.Tables.Add(table); dataGridView1.DataSource = data.Tables["prova2"]; // divisione in colonne della tabella dove vengono riportati i dati dataGridView1.Columns["Ora"].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; dataGridView1.Columns["ID"].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; dataGridView1.Columns["FC"].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; dataGridView1.Columns["N.Byte/Indirizzo iniziale"].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; dataGridView1.Columns["N registri"].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; dataGridView1.Columns["Contenuto Registri"].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; dataGridView1.Columns["CRC"].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; // impostazione del tema chiaro come predefinito celleChiare(); //settaggio Worker backgroundWorker1.WorkerReportsProgress = true; backgroundWorker1.WorkerSupportsCancellation = true; backgroundWorker1.DoWork += backgroundWorker1_DoWork; backgroundWorker1.ProgressChanged += backgroundWorker1_ProgressChanged; backgroundWorker1.RunWorkerCompleted += backgroundWorker1_RunWorkerCompleted; //input informazioni porta seriale // scelta della porta seriale da dover leggere foreach (string port in portNames) { ToolStripMenuItem portItem = new ToolStripMenuItem(port); portItem.Click += PortItem_Click; portItem.CheckOnClick = true; portaToolStripMenuItem.DropDownItems.Add(portItem); } // scelta del baud della porta seriale foreach (string port in baud) { ToolStripMenuItem baudratemenu = new ToolStripMenuItem(port); baudratemenu.Click += baudItem_Click; baudratemenu.CheckOnClick = true; baudrateToolStripMenuItem.DropDownItems.Add(baudratemenu); } baudrateToolStripMenuItem.DropDownItems.Add(new ToolStripSeparator()); baudrateToolStripMenuItem.DropDownItems.Add("Personalizzato [WIP]"); // scelta del databit della porta seriale foreach (string port in databit) { ToolStripMenuItem databitsmenu = new ToolStripMenuItem(port); databitsmenu.Click += databitsItem_Click; databitsmenu.CheckOnClick = true; dataBitsToolStripMenuItem.DropDownItems.Add(databitsmenu); } // scelta della parity della porta seriale foreach (string port in parity) { ToolStripMenuItem paritamenu = new ToolStripMenuItem(port); paritamenu.Click += paritaItem_Click; paritamenu.CheckOnClick = true; paritaToolStripMenuItem.DropDownItems.Add(paritamenu); } foreach (string port in stop) { ToolStripMenuItem stopmenu = new ToolStripMenuItem(port); stopmenu.Click += StopItem_Click; stopmenu.CheckOnClick = true; stopBitToolStripMenuItem.DropDownItems.Add(stopmenu); } /* * per la seconda porta non in uso * foreach (string port in portNames) { ToolStripMenuItem portItem = new ToolStripMenuItem(port); portItem.Click += PortItem_Click2; portItem.CheckOnClick = true; porta2ToolStripMenuItem1.DropDownItems.Add(portItem); } foreach (string port in baud) { ToolStripMenuItem baudratemenu = new ToolStripMenuItem(port); baudratemenu.Click += baudItem_Click2; baudratemenu.CheckOnClick = true; baudrate2ToolStripMenuItem1.DropDownItems.Add(baudratemenu); } baudrate2ToolStripMenuItem1.DropDownItems.Add(new ToolStripSeparator()); baudrate2ToolStripMenuItem1.DropDownItems.Add("Personalizzato [WIP]"); foreach (string port in databit) { ToolStripMenuItem databitsmenu = new ToolStripMenuItem(port); databitsmenu.Click += databitsItem_Click2; databitsmenu.CheckOnClick = true; dataBits2ToolStripMenuItem1.DropDownItems.Add(databitsmenu); } foreach (string port in parity) { ToolStripMenuItem paritamenu = new ToolStripMenuItem(port); paritamenu.Click += paritaItem_Click2; paritamenu.CheckOnClick = true; parita2ToolStripMenuItem1.DropDownItems.Add(paritamenu); } foreach (string port in stop) { ToolStripMenuItem stopmenu = new ToolStripMenuItem(port); stopmenu.Click += stopItem_Click2; stopmenu.CheckOnClick = true; stopBits2ToolStripMenuItem.DropDownItems.Add(stopmenu); }*/ } // lavoro che il backgroundworker deve svolgere quando chiamato private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { //imposta i valori della porta seriale al worker int baudRate = Int32.Parse(baudrate); Parity parity = CalculateParity(); int dataBits = ParseDataBits(); StopBits stopBits = ParseStopBits(); /*int baudRate2 = Int32.Parse(baudrate2); var parity2 = CalculateParity2(); var dataBits2 = ParseDataBits2(); var stopBits2 = ParseStopBits2();*/ serialPort = new SerialPort(porta, baudRate, parity, dataBits, stopBits); serialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler); // aprire la porta se possibile, altrimenti messaggio di errore try { serialPort.Open(); } catch (Exception ex) { MessageBox.Show(ex.Message); } // porta seriale rimane aperta finchè il backgroundworker lavora while (!backgroundWorker1.CancellationPending) { System.Threading.Thread.Sleep(1); } serialPort.Close(); } // Funzione che aggiorna la tabella private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) { if (e.UserState is string[] values) { if (data != null) { DataTable table = data.Tables["prova2"]; // se la tabella esiste if (table != null) { // aggiunge la riga con i valori dell'array values table.Rows.Add(values); // aggiorna la tabella dataGridView1.Refresh(); dataGridView1.Update(); } } } } // Funzione che finisce il funzionamento del backgroundworker private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { if (e.Error != null) { MessageBox.Show("An error occurred: " + e.Error.Message); } else if (e.Result is Exception ex) { MessageBox.Show("An error occurred: " + ex.Message); } } // Codice del button5 o button per fermare la lettura private void ButtonStop_Click(object sender, EventArgs e) { // richiama una funzione che ha la sua stessa funzione fermaLetturaToolStripMenuItem_Click(sender, e); } // Codice del button1 o button per iniziare la lettura private void Start_Click(object sender, EventArgs e) { // richiama una funzione che ha la sua stessa funzione avviaLetturaToolStripMenuItem_Click(sender, e); } // Funzione che gestisce i dati ricevuti private static void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e) { SerialPort sp = (SerialPort)sender; string hexLine = BitConverter.ToString(ReadBytesFromSerialPort(sp)).Replace("-", " "); ParseModbusLine(hexLine); } // Funzione che legge i bytes dalla porta seriale private static byte[] ReadBytesFromSerialPort(SerialPort sp) { int bytesToRead = sp.BytesToRead; // creazione array di bytes dove viene contenuto ciò che viene letto byte[] buffer = new byte[bytesToRead]; sp.Read(buffer, 0, bytesToRead); return buffer; } // Funzione che analizza la stringa di bytes che gli viene inviata public static void ParseModbusLine(string hexLine) { Console.WriteLine(hexLine); // stringa viene divisa string[] bytes = hexLine.Split(' '); // se la lunghezza è minore di 5 non può essere ne una request, ne una response di modbus fc03, fc06, fc16 if (bytes.Length < 5) { return; } // indirizzo dello slave salvato string slaveAddress = bytes[0]; // tipo di function code salvato string functionCode = bytes[1]; // controllo se la stringa passata è una Request o una Response if (functionCode == Function_Code.Read.ToString("x")) { if ((bytes.Length - 5) % 2 == 0) { ParseModbusResponse(bytes); } else { ParseModbusRequest(bytes); } } } // Funzione in caso la stringa passata precedentemente sia una Request, stringa viene salvata su file e su griglia public static void ParseModbusRequest(string[] bytes) { if (bytes.Length < 8) { return; } // Aggiornamento orario attuale ora = DateTime.Now; // Calcola la differenza tra i due tempi TimeSpan differenza = ora - oravecchia; // Aggiorna il tempo precedente per il prossimo calcolo oravecchia = ora; // Visualizza la differenza in millisecondi (o in qualsiasi formato desiderato) // MessageBox.Show("Differenza di tempo: " + differenza.TotalMilliseconds + " millisecondi"); // salvataggio della stringa passata string slaveAddress = bytes[0]; string functionCode = bytes[1]; string addressFirstRegister = bytes[2] + bytes[3]; string amountRegisters = bytes[4] + bytes[5]; string crc = bytes[6] + bytes[7]; // Preparazione valori da dover inviare alla DataGridView string[] values = new string[] { ora.ToString("o"), differenza.TotalMilliseconds + " ms", slaveAddress, functionCode + " - Read Request", addressFirstRegister, amountRegisters, string.Empty, // Non vengono specificati i registri in caso di Request crc }; // Controllo che il backgroundworker sia libero per poter scrivere i dati in tabella e nel file if (backgroundWorker1.IsBusy) { // aspettare che il backgroundworker possa procedere System.Threading.Thread.Sleep(200); if (richieste == true) { // creazione della cartella se non è già presente nel PC try { Directory.CreateDirectory(@"c:modbus"); } catch (Exception ex) { // non si fa nulla } // rootPath per scrivere nel file string rootPath = @"C:modbus"; string filePath = Path.Combine(rootPath, file); // scrittura nel file using (StreamWriter outputFile = new StreamWriter(filePath, true)) { // ogni riga in values viene scritta nel file foreach (string line in values) { outputFile.WriteLine(line); } // Riga vuota per staccare ogni riga outputFile.WriteLine("n"); } // riportare i progressi al backgroundworker backgroundWorker1.ReportProgress(0, values); } } else // stessi passaggi se il backgroundworker non è da aspettare { if (richieste == true) { // creazione della cartella se non è già presente nel PC try { Directory.CreateDirectory(@"c:modbus"); } catch (Exception ex) { // non si fa nulla } // rootPath per scrivere nel file string rootPath = @"C:modbus"; string filePath = Path.Combine(rootPath, file); // scrittura nel file using (StreamWriter outputFile = new StreamWriter(filePath, true)) { // ogni riga in values viene scritta nel file foreach (string line in values) { outputFile.WriteLine(line); } // Riga vuota per staccare ogni riga outputFile.WriteLine("n"); } // riportare i progressi al backgroundworker backgroundWorker1.ReportProgress(0, values); } } } // Funzione in caso la stringa passata precedentemente sia una Response, stringa viene salvata su file e sulla griglia public static void ParseModbusResponse(string[] bytes) { // le Response delle nostre fc possono essere di 5 o 7 bytes if (bytes.Length <= 5) { return; } // informazioni della stringa vengono salvate string slaveAddress = bytes[0]; string functionCode = bytes[1]; // numero di bytes letti string numberOfBytes = bytes[2]; // prova di conversione in bytes della stringa numberOfBytes, se non possibile la funzione termina if (!byte.TryParse(numberOfBytes, System.Globalization.NumberStyles.HexNumber, null, out byte byteCount)) { // in caso la converisone avvenisse il valore è contenuto in byteCount return; } // lunghezza aspettata dalla conversione int expectedLength = 3 + byteCount + 2; // se la lunghezza è minore rispetto alla lunghezza aspettata la funzione termina if (bytes.Length < expectedLength) { return; } // creazione del CRC string crc = bytes[bytes.Length - 2] + bytes[bytes.Length - 1]; for (int i = 3; i < 3 + byteCount; i += 2) { if (i + 1 < bytes.Length) { string registerValue = bytes[i] + bytes[i + 1]; // aggiornamento dell'orario ora = DateTime.Now; // Calcola la differenza tra i due tempi TimeSpan differenza = ora - oravecchia; // Aggiorna il tempo precedente per il prossimo calcolo oravecchia = ora; // Prepara i valori da inviare alla DataGridView string[] values = new string[] { ora.ToString("o"), differenza.TotalMilliseconds + " ms", slaveAddress, functionCode+ " - Read Response", numberOfBytes, "1", // Since we are adding one register per row registerValue, crc }; // Riporta i valori al backgroundworker per aggiornare la DataGridView if (backgroundWorker1.IsBusy) { // se bisogna aspettare backgroundworker una sleep System.Threading.Thread.Sleep(200); if (risposte==true) { try { // creazione della directory se non ancora presente sul PC Directory.CreateDirectory(@"c:modbus"); } catch (Exception ex) { // in caso di eccezione non si fa nulla } // rootpath dove è presente il file dove devono essere scritti i dati string rootPath = @"C:modbus"; string filePath = Path.Combine(rootPath, file); // scrittura sul file tramite StreamWriter using (StreamWriter outputFile = new StreamWriter(filePath, true)) { foreach (string line in values) { outputFile.WriteLine(line); } // Riga per separare i dati outputFile.WriteLine("n"); } // aggiornamenti dei dati inviati al backgroundworker per aggiornare la tabella backgroundWorker1.ReportProgress(0, values); } } else { // ripetizione del codice se il backgroundworker non è busy if (risposte==true) { try { // creazione cartella se non presente nel PC Directory.CreateDirectory(@"c:modbus"); } catch (Exception ex) { // nulla in caso di eccezione } // rootpath dove è stato creato il file dove verranno scritti i dati letti string rootPath = @"C:modbus"; string filePath = Path.Combine(rootPath, file); // StreamWriter per scrivere sui file using (StreamWriter outputFile = new StreamWriter(filePath, true)) { // scrittura di ogni riga presente in values sul file foreach (string line in values) { outputFile.WriteLine(line); } outputFile.WriteLine("n"); } backgroundWorker1.ReportProgress(0, values); } } } } } </code>
using Modbus_Connect_4;
using System;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.IO.Ports;
using System.Web;
using System.Windows.Forms;
using System.Collections.Generic;

namespace Modbus_Connect_4
{
    public partial class Form1 : Form
    {
        private DataSet data;
        private static SerialPort serialPort;


        // private static TimeSpan differenza;
        private static DateTime oravecchia = DateTime.Now;   
        private static DateTime ora;
        private static string file;
        private static BackgroundWorker backgroundWorker1 = new BackgroundWorker();

        // richieste, risposte e il tema inizialmente predefinite a true
        private static Boolean richieste = true;
        private static Boolean risposte = true;
        private Boolean chiaro = true;

        // impostazioni necessarie per iniziare il funzionamento dell'app inizializzate a null
        private String porta = null;
        private String baudrate = null;
        private String databits = null;
        private String parita = null;
        private String stopbits = null;

        // seconde impostazioni che i predecessori ci hanno lasciato (non ho capito a cosa servano, credo per la comunicazione tra 2 porte e quindi leggevano il doppio delle informazioni)
    //    private String porta2;
    //    private String baudrate2;
    //    private String databits2;
    //    private String parita2;
    //    private String stopbits2;

        // enum con i fc richiesti
        enum Function_Code
        {
            SingleWrite = 3,
            MultiWrite = 6,
            Read = 16
        }
        public Form1()
        {
            InitializeComponent();
            // file dove verranno registrati i dati raccolti e letti dal programma
            file = "ModbusConnect4_LOG_" + DateTime.Now.ToString("dd_MM_yyyy_HH_mm_ss") + ".log";
            private CancellationTokenSource cancellationTokenSource;

            int marginButton = 35; // Margine dei button
            int marginTable = 20; // Margine della tabella
            int buttonWidth = 100; // lunghezza dei button
            int buttonHeight = 30; // altezza dei button

            // Imposta posizione e dimensioni per button1
            button1.Anchor = AnchorStyles.Top | AnchorStyles.Left;
            button1.Location = new Point(marginButton, marginButton);
            button1.Size = new Size(buttonWidth, buttonHeight);

            // Imposta posizione e dimensioni per button2 nell'angolo in alto a destra
            button2.Anchor = AnchorStyles.Top | AnchorStyles.Right;
            button2.Location = new Point(this.ClientSize.Width - buttonWidth - marginButton, marginButton);
            button2.Width = buttonWidth;
            button2.Height = buttonHeight;

            // Imposta posizione e dimensioni per button5 vicino al button 1
            button5.Anchor = AnchorStyles.Top | AnchorStyles.Left;
            button5.Size = new Size(buttonWidth, buttonHeight);
            button5.Location = new Point(button1.Right + marginButton, marginButton);

            // Imposta posizione e dimensioni per button3 = request
            button3.Anchor = AnchorStyles.Top | AnchorStyles.Left;
            button3.Size = new Size(buttonWidth, buttonHeight);
            button3.Location = new Point(button5.Right + marginButton, marginButton);

            // Imposta posizione e dimensioni per button4 = response
            button4.Anchor = AnchorStyles.Top | AnchorStyles.Left;
            button4.Size = new Size(buttonWidth, buttonHeight);
            button4.Location = new Point(button3.Right + marginButton, marginButton);

            // Imposta posizione verticale per dataGridView1 in base alla posizione dei pulsanti
            int verticalPosition = button1.Bottom + marginTable;

            // Imposta posizione e dimensioni per dataGridView1
            dataGridView1.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom;
            dataGridView1.Location = new Point(marginTable, verticalPosition);
            dataGridView1.Size = new Size(this.ClientSize.Width - 2 * marginTable, this.ClientSize.Height - verticalPosition - marginTable);

            string[] portNames = SerialPort.GetPortNames();
            string[] baud = { "300", "1200", "9600", "19200", "28800", "57600", "115200" };
            string[] databit = { "7 Bit", "8 Bit" };
            string[] parity = { "Nessuna", "Odd", "Even" };
            string[] stop = { "1 Bit", "2 Bit" };

            data = new DataSet("prova");

            //settaggio tabella
            DataTable table = new DataTable("prova2");
            table.Columns.Add("Ora");
            table.Columns.Add("Differenza (ms)");
            table.Columns.Add("ID");
            table.Columns.Add("FC");
            table.Columns.Add("N.Byte/Indirizzo iniziale");
            table.Columns.Add("N registri");
            table.Columns.Add("Contenuto Registri");
            table.Columns.Add("CRC");
            data.Tables.Add(table);
            dataGridView1.DataSource = data.Tables["prova2"];

            // divisione in colonne della tabella dove vengono riportati i dati
            dataGridView1.Columns["Ora"].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
            dataGridView1.Columns["ID"].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
            dataGridView1.Columns["FC"].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
            dataGridView1.Columns["N.Byte/Indirizzo iniziale"].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
            dataGridView1.Columns["N registri"].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
            dataGridView1.Columns["Contenuto Registri"].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
            dataGridView1.Columns["CRC"].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;

            // impostazione del tema chiaro come predefinito
            celleChiare();


            //settaggio Worker
            backgroundWorker1.WorkerReportsProgress = true;
            backgroundWorker1.WorkerSupportsCancellation = true;
            backgroundWorker1.DoWork += backgroundWorker1_DoWork;
            backgroundWorker1.ProgressChanged += backgroundWorker1_ProgressChanged;
            backgroundWorker1.RunWorkerCompleted += backgroundWorker1_RunWorkerCompleted;


            //input informazioni porta seriale
            // scelta della porta seriale da dover leggere
            foreach (string port in portNames)
            {
                ToolStripMenuItem portItem = new ToolStripMenuItem(port);
                portItem.Click += PortItem_Click;
                portItem.CheckOnClick = true;
                portaToolStripMenuItem.DropDownItems.Add(portItem);
            }

            // scelta del baud della porta seriale
            foreach (string port in baud)
            {
                ToolStripMenuItem baudratemenu = new ToolStripMenuItem(port);
                baudratemenu.Click += baudItem_Click;
                baudratemenu.CheckOnClick = true;
                baudrateToolStripMenuItem.DropDownItems.Add(baudratemenu);
            }
            baudrateToolStripMenuItem.DropDownItems.Add(new ToolStripSeparator());
            baudrateToolStripMenuItem.DropDownItems.Add("Personalizzato [WIP]");

            // scelta del databit della porta seriale
            foreach (string port in databit)
            {
                ToolStripMenuItem databitsmenu = new ToolStripMenuItem(port);
                databitsmenu.Click += databitsItem_Click;
                databitsmenu.CheckOnClick = true;
                dataBitsToolStripMenuItem.DropDownItems.Add(databitsmenu);
            }

            // scelta della parity della porta seriale
            foreach (string port in parity)
            {
                ToolStripMenuItem paritamenu = new ToolStripMenuItem(port);
                paritamenu.Click += paritaItem_Click;
                paritamenu.CheckOnClick = true;
                paritaToolStripMenuItem.DropDownItems.Add(paritamenu);
            }

            foreach (string port in stop)
            {
                ToolStripMenuItem stopmenu = new ToolStripMenuItem(port);
                stopmenu.Click += StopItem_Click;
                stopmenu.CheckOnClick = true;
                stopBitToolStripMenuItem.DropDownItems.Add(stopmenu);
            }


            /*
             * per la seconda porta non in uso 
             * 
            foreach (string port in portNames)
            {
                ToolStripMenuItem portItem = new ToolStripMenuItem(port);
                portItem.Click += PortItem_Click2;
                portItem.CheckOnClick = true;
                porta2ToolStripMenuItem1.DropDownItems.Add(portItem);
            }

            foreach (string port in baud)
            {
                ToolStripMenuItem baudratemenu = new ToolStripMenuItem(port);
                baudratemenu.Click += baudItem_Click2;
                baudratemenu.CheckOnClick = true;
                baudrate2ToolStripMenuItem1.DropDownItems.Add(baudratemenu);
            }
            baudrate2ToolStripMenuItem1.DropDownItems.Add(new ToolStripSeparator());
            baudrate2ToolStripMenuItem1.DropDownItems.Add("Personalizzato [WIP]");


            foreach (string port in databit)
            {
                ToolStripMenuItem databitsmenu = new ToolStripMenuItem(port);
                databitsmenu.Click += databitsItem_Click2;
                databitsmenu.CheckOnClick = true;
                dataBits2ToolStripMenuItem1.DropDownItems.Add(databitsmenu);
            }


            foreach (string port in parity)
            {
                ToolStripMenuItem paritamenu = new ToolStripMenuItem(port);
                paritamenu.Click += paritaItem_Click2;
                paritamenu.CheckOnClick = true;
                parita2ToolStripMenuItem1.DropDownItems.Add(paritamenu);
            }

            foreach (string port in stop)
            {
                ToolStripMenuItem stopmenu = new ToolStripMenuItem(port);
                stopmenu.Click += stopItem_Click2;
                stopmenu.CheckOnClick = true;
                stopBits2ToolStripMenuItem.DropDownItems.Add(stopmenu);
            }*/
        }

        // lavoro che il backgroundworker deve svolgere quando chiamato
        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {

            //imposta i valori della porta seriale al worker
            int baudRate = Int32.Parse(baudrate);
            Parity parity = CalculateParity();
            int dataBits = ParseDataBits();
            StopBits stopBits = ParseStopBits();

            /*int baudRate2 = Int32.Parse(baudrate2);
            var parity2 = CalculateParity2();
            var dataBits2 = ParseDataBits2();
            var stopBits2 = ParseStopBits2();*/

            serialPort = new SerialPort(porta, baudRate, parity, dataBits, stopBits);

            serialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
            
            // aprire la porta se possibile, altrimenti messaggio di errore
            try
            {
                serialPort.Open();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }


            // porta seriale rimane aperta finchè il backgroundworker lavora
            while (!backgroundWorker1.CancellationPending)
            {
                System.Threading.Thread.Sleep(1);

            }

            serialPort.Close();

        }

        // Funzione che aggiorna la tabella
        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            if (e.UserState is string[] values)
            {
                if (data != null)
                {
                    DataTable table = data.Tables["prova2"];
                    // se la tabella esiste
                    if (table != null)
                    {
                        // aggiunge la riga con i valori dell'array values
                        table.Rows.Add(values);
                        // aggiorna la tabella
                        dataGridView1.Refresh();
                        dataGridView1.Update();
                    }
                }
            }
        }

        // Funzione che finisce il funzionamento del backgroundworker
        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                MessageBox.Show("An error occurred: " + e.Error.Message);
            }
            else if (e.Result is Exception ex)
            {
                MessageBox.Show("An error occurred: " + ex.Message);
            }
        }

        // Codice del button5 o button per fermare la lettura
        private void ButtonStop_Click(object sender, EventArgs e)
        {
            // richiama una funzione che ha la sua stessa funzione
            fermaLetturaToolStripMenuItem_Click(sender, e);
        }
        
        // Codice del button1 o button per iniziare la lettura
        private void Start_Click(object sender, EventArgs e)
        {
            // richiama una funzione che ha la sua stessa funzione
            avviaLetturaToolStripMenuItem_Click(sender, e);
        }

        // Funzione che gestisce i dati ricevuti
        private static void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
        {
            SerialPort sp = (SerialPort)sender;
            string hexLine = BitConverter.ToString(ReadBytesFromSerialPort(sp)).Replace("-", " ");

            ParseModbusLine(hexLine);
        }

        // Funzione che legge i bytes dalla porta seriale
        private static byte[] ReadBytesFromSerialPort(SerialPort sp)
        {
            int bytesToRead = sp.BytesToRead;
            // creazione array di bytes dove viene contenuto ciò che viene letto
            byte[] buffer = new byte[bytesToRead];
            sp.Read(buffer, 0, bytesToRead);
            return buffer;
        }

        // Funzione che analizza la stringa di bytes che gli viene inviata
        public static void ParseModbusLine(string hexLine)
        {
            Console.WriteLine(hexLine);
            // stringa viene divisa
            string[] bytes = hexLine.Split(' ');

            // se la lunghezza è minore di 5 non può essere ne una request, ne una response di modbus fc03, fc06, fc16
            if (bytes.Length < 5)
            {
                return;
            }

            // indirizzo dello slave salvato
            string slaveAddress = bytes[0];
            // tipo di function code salvato
            string functionCode = bytes[1];

            // controllo se la stringa passata è una Request o una Response
            if (functionCode == Function_Code.Read.ToString("x"))
            {
                if ((bytes.Length - 5) % 2 == 0)
                {
                    ParseModbusResponse(bytes);
                }
                else
                {
                    ParseModbusRequest(bytes);
                }
            }
        }

        // Funzione in caso la stringa passata precedentemente sia una Request, stringa viene salvata su file e su griglia
        public static void ParseModbusRequest(string[] bytes)
        {
            
            if (bytes.Length < 8)
            {
                return;
            }

            // Aggiornamento orario attuale
            ora = DateTime.Now;

            // Calcola la differenza tra i due tempi
            TimeSpan differenza = ora - oravecchia;

            // Aggiorna il tempo precedente per il prossimo calcolo
            oravecchia = ora;

            // Visualizza la differenza in millisecondi (o in qualsiasi formato desiderato)
            // MessageBox.Show("Differenza di tempo: " + differenza.TotalMilliseconds + " millisecondi");

            // salvataggio della stringa passata
            string slaveAddress = bytes[0];
            string functionCode = bytes[1];
            string addressFirstRegister = bytes[2] + bytes[3];
            string amountRegisters = bytes[4] + bytes[5];
            string crc = bytes[6] + bytes[7];

            // Preparazione valori da dover inviare alla DataGridView
            string[] values = new string[]
            {
                ora.ToString("o"),
                differenza.TotalMilliseconds + " ms",
                slaveAddress,
                functionCode + " - Read Request",
                addressFirstRegister,
                amountRegisters,
                string.Empty, // Non vengono specificati i registri in caso di Request
                crc
            };

            // Controllo che il backgroundworker sia libero per poter scrivere i dati in tabella e nel file
            if (backgroundWorker1.IsBusy)
            {
                // aspettare che il backgroundworker possa procedere
                System.Threading.Thread.Sleep(200);

                if (richieste == true)
                {
                    // creazione della cartella se non è già presente nel PC
                    try
                    {
                        Directory.CreateDirectory(@"c:modbus");
                    }
                    catch (Exception ex)
                    {
                        // non si fa nulla
                    }
                    // rootPath per scrivere nel file
                    string rootPath = @"C:modbus";
                    string filePath = Path.Combine(rootPath, file);

                    // scrittura nel file
                    using (StreamWriter outputFile = new StreamWriter(filePath, true))
                    {
                        // ogni riga in values viene scritta nel file
                        foreach (string line in values)
                        {
                            outputFile.WriteLine(line);
                        }
                        // Riga vuota per staccare ogni riga
                        outputFile.WriteLine("n");
                    }

                    // riportare i progressi al backgroundworker
                    backgroundWorker1.ReportProgress(0, values);
                }


            } else // stessi passaggi se il backgroundworker non è da aspettare
            {
                if (richieste == true)
                {
                    // creazione della cartella se non è già presente nel PC
                    try
                    {
                        Directory.CreateDirectory(@"c:modbus");
                    }
                    catch (Exception ex)
                    {
                        // non si fa nulla
                    }
                    // rootPath per scrivere nel file
                    string rootPath = @"C:modbus";
                    string filePath = Path.Combine(rootPath, file);

                    // scrittura nel file
                    using (StreamWriter outputFile = new StreamWriter(filePath, true))
                    {
                        // ogni riga in values viene scritta nel file
                        foreach (string line in values)
                        {
                            outputFile.WriteLine(line);
                        }
                        // Riga vuota per staccare ogni riga
                        outputFile.WriteLine("n");
                    }

                    // riportare i progressi al backgroundworker
                    backgroundWorker1.ReportProgress(0, values);
                }
            }
        }
        // Funzione in caso la stringa passata precedentemente sia una Response, stringa viene salvata su file e sulla griglia
        public static void ParseModbusResponse(string[] bytes)
        {
            // le Response delle nostre fc possono essere di 5 o 7 bytes
            if (bytes.Length <= 5)
            {
                return;
            }

            // informazioni della stringa vengono salvate
            string slaveAddress = bytes[0];
            string functionCode = bytes[1];
            // numero di bytes letti
            string numberOfBytes = bytes[2];

            // prova di conversione in bytes della stringa numberOfBytes, se non possibile la funzione termina
            if (!byte.TryParse(numberOfBytes, System.Globalization.NumberStyles.HexNumber, null, out byte byteCount))
            {
                // in caso la converisone avvenisse il valore è contenuto in byteCount
                return;
            }

            // lunghezza aspettata dalla conversione
            int expectedLength = 3 + byteCount + 2;

            // se la lunghezza è minore rispetto alla lunghezza aspettata la funzione termina
            if (bytes.Length < expectedLength)
            {
                return;
            }

            // creazione del CRC
            string crc = bytes[bytes.Length - 2] + bytes[bytes.Length - 1];

            
            for (int i = 3; i < 3 + byteCount; i += 2)
            {
                if (i + 1 < bytes.Length)
                {
                    string registerValue = bytes[i] + bytes[i + 1];


                    // aggiornamento dell'orario
                    ora = DateTime.Now;

                    // Calcola la differenza tra i due tempi
                    TimeSpan differenza = ora - oravecchia;

                    // Aggiorna il tempo precedente per il prossimo calcolo
                    oravecchia = ora;



                    // Prepara i valori da inviare alla DataGridView
                    string[] values = new string[]
                    {
                        ora.ToString("o"),
                        differenza.TotalMilliseconds + " ms",
                        slaveAddress,
                        functionCode+ " - Read Response",
                        numberOfBytes,
                        "1", // Since we are adding one register per row
                        registerValue,
                        crc
                    };

                    // Riporta i valori al backgroundworker per aggiornare la DataGridView
                    if (backgroundWorker1.IsBusy)
                    {
                        // se bisogna aspettare backgroundworker una sleep 
                        System.Threading.Thread.Sleep(200);
                        if (risposte==true)
                        {
                            try
                            {
                                // creazione della directory se non ancora presente sul PC
                                Directory.CreateDirectory(@"c:modbus");
                            }
                            catch (Exception ex)
                            {
                                // in caso di eccezione non si fa nulla
                            }
                            // rootpath dove è presente il file dove devono essere scritti i dati
                            string rootPath = @"C:modbus";
                            string filePath = Path.Combine(rootPath, file);

                            // scrittura sul file tramite StreamWriter
                            using (StreamWriter outputFile = new StreamWriter(filePath, true))
                            {
                                foreach (string line in values)
                                {
                                    outputFile.WriteLine(line);
                                }
                                // Riga per separare i dati
                                outputFile.WriteLine("n");
                            }
                        // aggiornamenti dei dati inviati al backgroundworker per aggiornare la tabella
                        backgroundWorker1.ReportProgress(0, values);
                        }

                    } else
                    {
                        // ripetizione del codice se il backgroundworker non è busy
                        if (risposte==true)
                        {

                            try
                            {
                                // creazione cartella se non presente nel PC
                                Directory.CreateDirectory(@"c:modbus");
                            }
                            catch (Exception ex)
                            {
                                // nulla in caso di eccezione
                            }
                            // rootpath dove è stato creato il file dove verranno scritti i dati letti
                            string rootPath = @"C:modbus";
                            string filePath = Path.Combine(rootPath, file);

                            // StreamWriter per scrivere sui file
                            using (StreamWriter outputFile = new StreamWriter(filePath, true))
                            {
                                // scrittura di ogni riga presente in values sul file
                                foreach (string line in values)
                                {
                                    outputFile.WriteLine(line);
                                }
                                outputFile.WriteLine("n");
                            }
                            backgroundWorker1.ReportProgress(0, values);
                        }
                    }
                }
            }
        }

        

It sometimes read and print data in the output but never in the datagridview

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật