diff --git a/Datasheet.Designer.cs b/Datasheet.Designer.cs index a4a2cca..c0a8279 100644 --- a/Datasheet.Designer.cs +++ b/Datasheet.Designer.cs @@ -60,6 +60,7 @@ this.firmwarePathLabel = new System.Windows.Forms.Label(); this.WriteFirmware = new System.Windows.Forms.Button(); this.groupBox3 = new System.Windows.Forms.GroupBox(); + this.firmwareProgressBar = new System.Windows.Forms.ProgressBar(); this.groupBox1.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.nudModbusID)).BeginInit(); this.groupBox2.SuspendLayout(); @@ -371,6 +372,7 @@ // // groupBox3 // + this.groupBox3.Controls.Add(this.firmwareProgressBar); this.groupBox3.Controls.Add(this.BrowseFirmware); this.groupBox3.Controls.Add(this.WriteFirmware); this.groupBox3.Controls.Add(this.label11); @@ -382,6 +384,13 @@ this.groupBox3.TabStop = false; this.groupBox3.Text = "Прошивка"; // + // firmwareProgressBar + // + this.firmwareProgressBar.Location = new System.Drawing.Point(176, 38); + this.firmwareProgressBar.Name = "firmwareProgressBar"; + this.firmwareProgressBar.Size = new System.Drawing.Size(330, 23); + this.firmwareProgressBar.TabIndex = 19; + // // Datasheet // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -442,5 +451,6 @@ private System.Windows.Forms.Label firmwarePathLabel; private System.Windows.Forms.Button WriteFirmware; private System.Windows.Forms.GroupBox groupBox3; + private System.Windows.Forms.ProgressBar firmwareProgressBar; } } \ No newline at end of file diff --git a/Datasheet.cs b/Datasheet.cs index 77861ca..43ba283 100644 --- a/Datasheet.cs +++ b/Datasheet.cs @@ -1,10 +1,12 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics; using System.Drawing; using System.IO; using System.IO.Ports; using System.Linq; +using System.Security.Cryptography; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; @@ -20,7 +22,7 @@ namespace Gidrolock_Modbus_Scanner SerialPort port = Modbus.port; bool isPolling = false; - bool isAwaitingResponse = false; + static bool isAwaitingResponse = false; bool isValveClosed = false; @@ -31,7 +33,8 @@ namespace Gidrolock_Modbus_Scanner List wirelessSensors; public static string firmwarePath; - + static int timeout = 1000; + Stopwatch stopwatch = new Stopwatch(); Thread fileThread = new Thread((ThreadStart)delegate { @@ -40,16 +43,14 @@ namespace Gidrolock_Modbus_Scanner ofd.RestoreDirectory = true; if (ofd.ShowDialog() == DialogResult.OK) - { firmwarePath = ofd.FileName; - //firmwarePathLabel.Text = ofd.FileName; - } }); - public Datasheet(byte modbusID, Device device) : base() + public Datasheet(byte modbusID, Device device) : base() { InitializeComponent(); - + firmwareProgressBar.Minimum = 0; + firmwareProgressBar.Maximum = 100; nudModbusID.Minimum = 1; nudModbusID.Maximum = 246; nudModbusID.Value = modbusID; @@ -110,7 +111,7 @@ namespace Gidrolock_Modbus_Scanner sensorPanel.Controls[i].BackColor = i % 2 == 0 ? Color.White : Color.LightGray; - sensorPanel.Update(); + sensorPanel.Update(); } private async void buttonPoll_Click(object sender, EventArgs e) @@ -245,15 +246,17 @@ namespace Gidrolock_Modbus_Scanner bool res = false; Modbus.ReadRegAsync(modbusID, (FunctionCode)entry.registerType, entry.address, entry.length); isAwaitingResponse = true; - Task.Delay(2000).ContinueWith(_ => + + stopwatch.Restart(); + + while (isAwaitingResponse) { - if (isAwaitingResponse) + if (stopwatch.ElapsedMilliseconds > 1000) { - MessageBox.Show("Превышено время ожидания ответа от устройства."); - isAwaitingResponse = false; + Console.WriteLine("Response timed out."); + break; } - }); - while (isAwaitingResponse) { continue; } + } if (latestMessage != null && latestMessage.Status != ModbusStatus.Error) res = true; @@ -265,17 +268,18 @@ namespace Gidrolock_Modbus_Scanner private async void buttonSetID_Click(object sender, EventArgs e) { byte newID = (byte)nudModbusID.Value; // should prevent assigning wrong ID if UpDown is fiddled with in the middle of request + isAwaitingResponse = true; Modbus.WriteSingleAsync(FunctionCode.WriteRegister, modbusID, 128, newID); - await Task.Delay(2000).ContinueWith(_ => + + stopwatch.Restart(); + while (isAwaitingResponse) { - if (isAwaitingResponse) + if (stopwatch.ElapsedMilliseconds > 1000) { - MessageBox.Show("Превышено время ожидания ответа от устройства."); - isAwaitingResponse = false; + Console.WriteLine("Response timed out."); + break; } - return false; - }); - while (isAwaitingResponse) { continue; } + } if (latestMessage != null && latestMessage.Status != ModbusStatus.Error) modbusID = newID; @@ -284,18 +288,18 @@ namespace Gidrolock_Modbus_Scanner private async void buttonValve_Click(object sender, EventArgs e) { ushort value = isValveClosed ? (ushort)0 : (ushort)0xFF00; + isAwaitingResponse = true; Modbus.WriteSingleAsync(FunctionCode.WriteCoil, modbusID, device.valveStatus.address, value); - Task.Delay(2000).ContinueWith(_ => + stopwatch.Restart(); + while (isAwaitingResponse) { - if (isAwaitingResponse) + if (stopwatch.ElapsedMilliseconds > 1000) { - MessageBox.Show("Превышено время ожидания ответа от устройства."); - isAwaitingResponse = false; + Console.WriteLine("Response timed out."); + break; } - return false; - }); - while (isAwaitingResponse) { continue; } + } if (latestMessage != null && latestMessage.Status != ModbusStatus.Error) { @@ -308,18 +312,18 @@ namespace Gidrolock_Modbus_Scanner private async void buttonAlarm_Click(object sender, EventArgs e) { ushort value = alarmStatus ? (ushort)0 : (ushort)0xFF00; + isAwaitingResponse = true; Modbus.WriteSingleAsync(FunctionCode.WriteCoil, modbusID, device.alarmStatus.address, value); - Task.Delay(2000).ContinueWith(_ => + stopwatch.Restart(); + while (isAwaitingResponse) { - if (isAwaitingResponse) + if (stopwatch.ElapsedMilliseconds > 1000) { - MessageBox.Show("Превышено время ожидания ответа от устройства."); - isAwaitingResponse = false; + Console.WriteLine("Response timed out."); + break; } - return false; - }); - while (isAwaitingResponse) { continue; } + } if (latestMessage != null && latestMessage.Status != ModbusStatus.Error) { @@ -332,18 +336,18 @@ namespace Gidrolock_Modbus_Scanner private async void buttonCleaning_Click(object sender, EventArgs e) { ushort value = cleaningStatus ? (ushort)0 : (ushort)0xFF00; + isAwaitingResponse = true; Modbus.WriteSingleAsync(FunctionCode.WriteCoil, modbusID, device.cleaningMode.address, value); - Task.Delay(2000).ContinueWith(_ => + stopwatch.Restart(); + while (isAwaitingResponse) { - if (isAwaitingResponse) + if (stopwatch.ElapsedMilliseconds > 1000) { - MessageBox.Show("Превышено время ожидания ответа от устройства."); - isAwaitingResponse = false; + Console.WriteLine("Response timed out."); + break; } - return false; - }); - while (isAwaitingResponse) { continue; } + } if (latestMessage != null && latestMessage.Status != ModbusStatus.Error) { @@ -364,18 +368,20 @@ namespace Gidrolock_Modbus_Scanner // send speed value to device // await for response + isAwaitingResponse = true; Modbus.WriteSingleAsync(FunctionCode.WriteRegister, modbusID, device.baudRate.address, newSpeed); - Task.Delay(2000).ContinueWith(_ => + + stopwatch.Restart(); + while (isAwaitingResponse) { - if (isAwaitingResponse) + if (stopwatch.ElapsedMilliseconds > 1000) { - MessageBox.Show("Превышено время ожидания ответа от устройства."); - isAwaitingResponse = false; + Console.WriteLine("Response timed out."); + break; } - return false; - }); - while (isAwaitingResponse) { continue; } - if (latestMessage.Status != ModbusStatus.Error) + } + + if (latestMessage != null && latestMessage.Status != ModbusStatus.Error) { port.Close(); port.BaudRate = newSpeed; @@ -396,30 +402,54 @@ namespace Gidrolock_Modbus_Scanner fileThread.Join(); } catch (Exception err) { MessageBox.Show(err.Message); } + + firmwarePathLabel.Invoke(new MethodInvoker(delegate { firmwarePathLabel.Text = firmwarePath; })); } - private void WriteFirmware_Click(object sender, EventArgs e) + private async void WriteFirmware_Click(object sender, EventArgs e) { + if (firmwarePath is null || firmwarePath.Length == 0) + { + MessageBox.Show("Выберите файл прошивки."); + return; + } + FileStream fileStream = File.OpenRead(firmwarePath); long bytesLeft = fileStream.Length; - int offset = 0; - int count; - byte[] buffer; + long bytesTotal = fileStream.Length; + int count = 64; + byte[] buffer = new byte[count]; byte[] bdma; + short _flashAddr = 0; + byte[] flashAddr = new byte[2]; + byte[] CRC; List message; - int dma = 0; - Task.Run(() => + bool responseReceived = false; + Modbus.ResponseReceived += (sndr, msg) => { responseReceived = true; }; + bool firstMessageSent = false; + long bytesWritten = 0; + await Task.Run(() => { while (bytesLeft > 0) { + if (firstMessageSent) // after first message the device is sent into recovery mode which only supports 9600 bps + { + port.Close(); + port.BaudRate = 9600; + port.Open(); + } + count = bytesLeft > 64 ? 64 : (int)bytesLeft; buffer = new byte[count]; - fileStream.Read(buffer, offset, count); + fileStream.Read(buffer, 0, count); bdma = new byte[2]; - bdma[0] = (byte)((dma & 0xFF_00) >> 16); - bdma[1] = (byte)(dma & 0x00_FF); + bdma[0] = (byte)((bytesLeft & 0xFF_00) >> 8); + bdma[1] = (byte)(bytesLeft & 0x00_FF); + + flashAddr[0] = (byte)((_flashAddr & 0xFF_00) >> 8); + flashAddr[1] = (byte)(_flashAddr & 0x00_FF); message = new List(); message.Add(modbusID); // device ID @@ -429,40 +459,101 @@ namespace Gidrolock_Modbus_Scanner message.Add(0x00); // regCnt (?) message.Add(0x21); // regCnt (?) message.Add(0x42); // data bytecount - message.Add(bdma[0]); - message.Add(bdma[1]); + message.Add(flashAddr[0]); + message.Add(flashAddr[1]); - for (int i = 0; i < buffer.Length; i++) + try { - message.Add(buffer[i]); + for (int i = 0; i < buffer.Length; i++) + { + message.Add(buffer[i]); + } + message.Add(0x00); + message.Add(0x00); + CRC = new byte[2]; + Modbus.GetCRC(message.ToArray(), ref CRC); + message[message.Count - 2] = CRC[0]; + message[message.Count - 1] = CRC[1]; + + Console.WriteLine("Outgoing firmware message: " + Modbus.ByteArrayToString(message.ToArray())); + } + catch (Exception ex) + { + MessageBox.Show(ex.Message); + return; } - byte[] CRC = new byte[2]; - Modbus.GetCRC(message.ToArray(), ref CRC); - message.Add(CRC[0]); - message.Add(CRC[1]); - Console.WriteLine("Outgoing firmware message: " + Modbus.ByteArrayToString(message.ToArray())); + responseReceived = false; + + while (true) + { + + isAwaitingResponse = true; + port.Write(message.ToArray(), 0, message.Count); + stopwatch.Restart(); + + while (isAwaitingResponse) + { + if (stopwatch.ElapsedMilliseconds > 1000) + { + Console.WriteLine("Response timed out."); + break; + } + } + + if (responseReceived) + break; + } + bytesLeft -= count; + bytesWritten += count; + firmwareProgressBar.Increment( (int)(bytesWritten / bytesTotal) * 100 ); + + _flashAddr += (short)count; + if (port.BaudRate != 9600) + firstMessageSent = true; + + if (bytesLeft <= 0) + Console.WriteLine("Reached the end of firmware file."); + } + + /* Final Message */ + message = new List(); + message.Add(modbusID); // device ID + message.Add(0x10); // function code + message.Add(0xFF); // register address + message.Add(0xFF); // register address + message.Add(0x00); // regCnt (?) + message.Add(0x21); // regCnt (?) + message.Add(0x00); // data bytecount + message.Add(0x00); // CRC + message.Add(0x00); // CRC + CRC = new byte[2]; + Modbus.GetCRC(message.ToArray(), ref CRC); + message[message.Count - 2] = CRC[0]; + message[message.Count - 1] = CRC[1]; + Console.WriteLine("Outgoing firmware message: " + Modbus.ByteArrayToString(message.ToArray())); + + while (true) + { isAwaitingResponse = true; port.Write(message.ToArray(), 0, message.Count); + stopwatch.Restart(); - Task.Delay(2000).ContinueWith(_ => + while (isAwaitingResponse) { - if (isAwaitingResponse) + if (stopwatch.ElapsedMilliseconds > 250) { - MessageBox.Show("Превышено время ожидания ответа от устройства."); - isAwaitingResponse = false; - return; + Console.WriteLine("Response timed out."); + break; } - }); - while (isAwaitingResponse) { continue; } - - bytesLeft -= count; - offset += count; - dma += 32; + } + if (responseReceived) + break; } }); + } } public class Sensor : FlowLayoutPanel diff --git a/Main.cs b/Main.cs index 6acbed8..e06acd6 100644 --- a/Main.cs +++ b/Main.cs @@ -3,6 +3,8 @@ using System.Collections.Generic; using System.Threading.Tasks; using System.Windows.Forms; using System.IO.Ports; +using System.Reflection; +using System.Diagnostics; namespace Gidrolock_Modbus_Scanner { @@ -20,6 +22,7 @@ namespace Gidrolock_Modbus_Scanner DateTime dateTime; Datasheet datasheet; + Stopwatch stopwatch = new Stopwatch(); #region Initialization public App() { @@ -77,8 +80,10 @@ namespace Gidrolock_Modbus_Scanner } #endregion + private async void ButtonConnect_Click(object sender, EventArgs e) { + if (cBoxPorts.SelectedItem.ToString() == "COM1") { DialogResult res = MessageBox.Show("Выбран серийный порт COM1, который обычно является портом PS/2 или RS-232, не подключенным к Modbus устройству. Продолжить?", "Внимание", MessageBoxButtons.OKCancel); @@ -123,17 +128,16 @@ namespace Gidrolock_Modbus_Scanner latestMessage = null; isAwaitingResponse = true; var send = Modbus.ReadRegAsync((byte)upDownModbusID.Value, FunctionCode.ReadInput, 200, 6); - await Task.Delay(2000).ContinueWith(_ => + stopwatch.Restart(); + while (isAwaitingResponse) { - if (isAwaitingResponse) + if (stopwatch.ElapsedMilliseconds > 1000) { - isAwaitingResponse = false; - MessageBox.Show("Истекло время ожидания ответа от устройства."); + Console.WriteLine("Response timed out."); + break; } - return; - }); + } - while (isAwaitingResponse) { continue; } if (latestMessage is null) return; if (latestMessage.Status == ModbusStatus.Error) diff --git a/Modbus.cs b/Modbus.cs index f986b35..708f791 100644 --- a/Modbus.cs +++ b/Modbus.cs @@ -5,6 +5,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; +using System.Collections.Generic; using static System.Net.Mime.MediaTypeNames; namespace Gidrolock_Modbus_Scanner @@ -12,8 +13,7 @@ namespace Gidrolock_Modbus_Scanner public static class Modbus { public static SerialPort port = new SerialPort(); - - + public static event EventHandler ResponseReceived = delegate { }; public static void Init() @@ -235,17 +235,55 @@ namespace Gidrolock_Modbus_Scanner CRC[1] = CRCHigh = (byte)((CRCFull >> 8) & 0xFF); CRC[0] = CRCLow = (byte)(CRCFull & 0xFF); } - #endregion - - static void PortDataReceived(object sender, EventArgs e) + public static bool CheckResponse(byte[] response) // Проверка пакета на контрольную сумму { + //Perform a basic CRC check: + byte[] CRC = new byte[2]; + try + { + CRC[0] = response[response.Length - 2]; + CRC[1] = response[response.Length - 1]; + } + catch (Exception err) + { + return false; + } + GetCRC(response, ref CRC); + + if (CRC[0] == response[response.Length - 2] && CRC[1] == response[response.Length - 1]) + return true; + else + return false; + } + #endregion + /* + static byte[] buffer = new byte[255]; + static int offset = 0; + static Thread timer = new Thread(new ThreadStart(() => + { + Thread.Sleep(50); + offset = 0; + buffer = new byte[255]; + port.DiscardInBuffer(); + })); + */ + static void PortDataReceived(object sender, EventArgs e) + { try { Thread.Sleep(50); byte[] message = new byte[port.BytesToRead]; //Console.WriteLine("Bytes to read:" + port.BytesToRead); port.Read(message, 0, port.BytesToRead); + if (message.Length == 0) + return; Console.WriteLine("Incoming message: " + ByteArrayToString(message, false)); + if (!CheckResponse(message)) + { + Console.WriteLine("Bad CRC or not a modbus message!"); + return; + } + if (message[1] <= 0x04) // read functions { //Console.WriteLine("It's a read message"); @@ -270,6 +308,7 @@ namespace Gidrolock_Modbus_Scanner MessageBox.Show(err.Message, "Modbus message reception error"); } port.DiscardInBuffer(); + } } diff --git a/Screenshot_1.png b/Screenshot_1.png new file mode 100644 index 0000000..c3cc1dc Binary files /dev/null and b/Screenshot_1.png differ