rc1: fixed write timeouts

This commit is contained in:
nikzori
2025-04-09 15:26:50 +03:00
parent 96b77d4181
commit 4137a84fa3
2 changed files with 194 additions and 210 deletions

View File

@@ -18,7 +18,7 @@ namespace Gidrolock_Modbus_Scanner
byte modbusID; byte modbusID;
Device device; Device device;
ModbusResponseEventArgs latestMessage; public static ModbusResponseEventArgs latestMessage;
SerialPort port = Modbus.port; SerialPort port = Modbus.port;
bool isPolling = false; bool isPolling = false;
@@ -34,9 +34,7 @@ namespace Gidrolock_Modbus_Scanner
List<WirelessSensor> wirelessSensors; List<WirelessSensor> wirelessSensors;
public static string firmwarePath; public static string firmwarePath;
static int timeout = 1000;
Stopwatch stopwatch = new Stopwatch(); Stopwatch stopwatch = new Stopwatch();
Thread fileThread = new Thread((ThreadStart)delegate Thread fileThread = new Thread((ThreadStart)delegate
{ {
OpenFileDialog ofd = new OpenFileDialog(); OpenFileDialog ofd = new OpenFileDialog();
@@ -117,151 +115,158 @@ namespace Gidrolock_Modbus_Scanner
private async void buttonPoll_Click(object sender, EventArgs e) private async void buttonPoll_Click(object sender, EventArgs e)
{ {
if (isPolling)
return;
// hardcoded for now, probably easier to keep it like this in the future // hardcoded for now, probably easier to keep it like this in the future
try try
{ {
bool res = await PollEntry(device.firmware); await Task.Run(() =>
Console.WriteLine("Polling for alarm status, poll success: " + res);
if (res)
{ {
labelFirmware.Text = App.ByteArrayToUnicode(latestMessage.Data);
}
if (device.hasBattery) bool res = PollEntry(device.firmware);
{ Console.WriteLine("Polling for alarm status, poll success: " + res);
res = await PollEntry(device.batteryCharge);
if (res) if (res)
{ {
labelBattery.Text = latestMessage.Data.Last().ToString(); this.Invoke(new MethodInvoker(delegate { labelFirmware.Text = App.ByteArrayToUnicode(latestMessage.Data); }));
} }
}
res = await PollEntry(device.valveStatus); if (device.hasBattery)
Console.WriteLine("Polling for valve status, poll success: " + res);
if (res)
{
if (latestMessage.Data.Last() > 0)
{ {
isValveClosed = true; res = PollEntry(device.batteryCharge);
labelValve.Text = "Закрыт"; if (res)
buttonValve.Text = "Открыть"; {
this.Invoke(new MethodInvoker(delegate { labelBattery.Text = latestMessage.Data.Last().ToString(); }));
}
} }
else
{
isValveClosed = false;
labelValve.Text = "Открыт";
buttonValve.Text = "Закрыть";
}
}
res = await PollEntry(device.alarmStatus); res = PollEntry(device.valveStatus);
Console.WriteLine("Polling for alarm status, poll success: " + res); Console.WriteLine("Polling for valve status, poll success: " + res);
if (res)
{
Console.WriteLine("Alarm data: " + Modbus.ByteArrayToString(latestMessage.Data));
Console.WriteLine("Alarm data.last: " + latestMessage.Data.Last().ToString());
if (latestMessage.Data.Last() > 0)
{
alarmStatus = true;
buttonAlarm.Text = "Выключить";
labelAlarm.Text = "Протечка!";
}
else
{
alarmStatus = false;
buttonAlarm.Text = "Авария";
labelAlarm.Text = "нет";
}
}
if (device.hasCleaningMode)
{
res = await PollEntry(device.cleaningMode);
if (res) if (res)
{ {
if (latestMessage.Data.Last() > 0) if (latestMessage.Data.Last() > 0)
{ {
cleaningStatus = true; isValveClosed = true;
buttonCleaning.Text = "Выключить"; this.Invoke(new MethodInvoker(delegate { labelValve.Text = "Закрыт"; }));
labelCleaning.Text = "вкл"; this.Invoke(new MethodInvoker(delegate { buttonValve.Text = "Открыть"; }));
} }
else else
{ {
cleaningStatus = false; isValveClosed = false;
buttonCleaning.Text = "Включить"; this.Invoke(new MethodInvoker(delegate { labelValve.Text = "Открыт"; }));
labelCleaning.Text = "выкл"; this.Invoke(new MethodInvoker(delegate { buttonValve.Text = "Закрыть"; }));
} }
} }
}
if (device.wiredLineBreak != null || device.wiredLineBreak.Count > 0) res = PollEntry(device.alarmStatus);
{ Console.WriteLine("Polling for alarm status, poll success: " + res);
for (int i = 0; i < device.wiredLineBreak.Count; i++) if (res)
{ {
res = await PollEntry(device.wiredLineBreak[i]); Console.WriteLine("Alarm data: " + Modbus.ByteArrayToString(latestMessage.Data));
Console.WriteLine("Alarm data.last: " + latestMessage.Data.Last().ToString());
if (latestMessage.Data.Last() > 0)
{
alarmStatus = true;
this.Invoke(new MethodInvoker(delegate { buttonAlarm.Text = "Выключить"; }));
this.Invoke(new MethodInvoker(delegate { labelAlarm.Text = "Протечка!"; }));
}
else
{
alarmStatus = false;
this.Invoke(new MethodInvoker(delegate { buttonAlarm.Text = "Авария"; }));
this.Invoke(new MethodInvoker(delegate { labelAlarm.Text = "нет"; }));
}
}
if (device.hasCleaningMode)
{
res = PollEntry(device.cleaningMode);
if (res) if (res)
{ {
bool value = latestMessage.Data[0] > 0x00 ? true : false; if (latestMessage.Data.Last() > 0)
WiredSensor snsr = sensorPanel.Controls[i] as WiredSensor; {
snsr.labelBreak.Text = value ? "Обрыв!" : "ОК"; cleaningStatus = true;
this.Invoke(new MethodInvoker(delegate { buttonCleaning.Text = "Выключить"; }));
this.Invoke(new MethodInvoker(delegate { labelCleaning.Text = "вкл"; }));
}
else
{
cleaningStatus = false;
this.Invoke(new MethodInvoker(delegate { buttonCleaning.Text = "Включить"; }));
this.Invoke(new MethodInvoker(delegate { labelCleaning.Text = "выкл"; }));
}
} }
} }
}
res = await PollEntry(device.sensorAlarm); if (device.wiredLineBreak != null || device.wiredLineBreak.Count > 0)
if (res)
{
BitArray bArray = new BitArray(latestMessage.Data);
bool[] bools = new bool[bArray.Length];
bArray.CopyTo(bools, 0);
for (int i = 0; i < sensorPanel.Controls.Count; i++)
{ {
Sensor snsr = sensorPanel.Controls[i] as Sensor; for (int i = 0; i < device.wiredLineBreak.Count; i++)
snsr.labelLeak.Text = bools[i] ? "Протечка!" : "нет";
}
}
Console.WriteLine("Polling for radio status");
res = await PollEntry(device.radioStatus);
if (res)
{
List<byte> values = new List<byte>(latestMessage.Data.Length / 2);
for (int i = 1; i < latestMessage.Data.Length; i += 2)
values.Add(latestMessage.Data[i]);
int add = device.wiredSensors + (device.hasScenarioSensor ? 1 : 0);
for (int i = 0; i < sensorPanel.Controls.Count - add; i++)
{
WirelessSensor snsr = sensorPanel.Controls[i + add] as WirelessSensor;
string txt = "нет";
switch (values[i])
{ {
case 1: res = PollEntry(device.wiredLineBreak[i]);
txt = "норма"; if (res)
break; {
case 2: bool value = latestMessage.Data[0] > 0x00 ? true : false;
txt = "протечка"; WiredSensor snsr = sensorPanel.Controls[i] as WiredSensor;
break; snsr.Invoke(new MethodInvoker(delegate { snsr.labelBreak.Text = value ? "Обрыв!" : "ОК"; }));
case 3:
txt = "разряжен";
break;
case 4:
txt = "потеря";
break;
}
snsr.labelStatus.Text = txt;
}
}
}
}
}
res = PollEntry(device.sensorAlarm);
if (res)
{
BitArray bArray = new BitArray(latestMessage.Data);
bool[] bools = new bool[bArray.Length];
bArray.CopyTo(bools, 0);
for (int i = 0; i < sensorPanel.Controls.Count; i++)
{
Sensor snsr = sensorPanel.Controls[i] as Sensor;
snsr.Invoke(new MethodInvoker(delegate { snsr.labelLeak.Text = bools[i] ? "Протечка!" : "нет"; }));
}
}
Console.WriteLine("Polling for radio status");
res = PollEntry(device.radioStatus);
if (res)
{
List<byte> values = new List<byte>(latestMessage.Data.Length / 2);
for (int i = 1; i < latestMessage.Data.Length; i += 2)
values.Add(latestMessage.Data[i]);
int add = device.wiredSensors + (device.hasScenarioSensor ? 1 : 0);
for (int i = 0; i < sensorPanel.Controls.Count - add; i++)
{
WirelessSensor snsr = sensorPanel.Controls[i + add] as WirelessSensor;
string txt = "нет";
switch (values[i])
{
case 1:
txt = "норма";
break;
case 2:
txt = "протечка";
break;
case 3:
txt = "разряжен";
break;
case 4:
txt = "потеря";
break;
}
snsr.Invoke(new MethodInvoker(delegate { snsr.labelStatus.Text = txt; }));
}
}
});
} }
catch (Exception err) { MessageBox.Show(err.Message); } catch (Exception err) { MessageBox.Show(err.Message); }
} }
async Task<bool> PollEntry(Entry entry) bool PollEntry(Entry entry)
{ {
latestMessage = null; latestMessage = null;
bool res = false; bool res = false;
isAwaitingResponse = true; isAwaitingResponse = true;
isPolling = true;
Modbus.ReadRegAsync(modbusID, (FunctionCode)entry.registerType, entry.address, entry.length); Modbus.ReadRegAsync(modbusID, (FunctionCode)entry.registerType, entry.address, entry.length);
stopwatch.Restart(); stopwatch.Restart();
@@ -279,6 +284,34 @@ namespace Gidrolock_Modbus_Scanner
res = true; res = true;
Console.WriteLine("Poll attempt finished"); Console.WriteLine("Poll attempt finished");
isPolling = false;
return res;
}
bool SetEntry(Entry entry, ushort value)
{
latestMessage = null;
bool res = false;
isAwaitingResponse = true;
isPolling = true;
FunctionCode fc = FunctionCode.WriteCoil;
if (entry.registerType == RegisterType.Holding)
fc = FunctionCode.WriteRegister;
Modbus.WriteSingleAsync(fc, modbusID, entry.address, value);
stopwatch.Restart();
while (isAwaitingResponse && latestMessage == null)
{
if (stopwatch.ElapsedMilliseconds > port.ReadTimeout)
{
Console.WriteLine("Response timed out.");
break;
}
}
if (latestMessage != null && latestMessage.Status == ModbusStatus.WriteSuccess)
res = true;
isPolling = false;
return res; return res;
} }
@@ -293,7 +326,7 @@ namespace Gidrolock_Modbus_Scanner
stopwatch.Restart(); stopwatch.Restart();
while (isAwaitingResponse) while (isAwaitingResponse)
{ {
if (stopwatch.ElapsedMilliseconds > port.ReadTimeout) if (stopwatch.ElapsedMilliseconds > 10000)
{ {
Console.WriteLine("Response timed out."); Console.WriteLine("Response timed out.");
break; break;
@@ -307,48 +340,30 @@ namespace Gidrolock_Modbus_Scanner
// Кран // Кран
private async void buttonValve_Click(object sender, EventArgs e) private async void buttonValve_Click(object sender, EventArgs e)
{ {
ushort value = isValveClosed ? (ushort)0 : (ushort)0xFF00; try
isAwaitingResponse = true;
latestMessage = null;
Modbus.WriteSingleAsync(FunctionCode.WriteCoil, modbusID, device.valveStatus.address, value);
stopwatch.Restart();
while (isAwaitingResponse)
{ {
if (stopwatch.ElapsedMilliseconds > port.ReadTimeout) await Task.Run(() =>
{ {
Console.WriteLine("Response timed out."); ushort value = isValveClosed ? (ushort)0 : (ushort)0xFF00;
break; if (SetEntry(device.valveStatus, value))
} {
isValveClosed = !isValveClosed;
labelValve.Invoke(new MethodInvoker(delegate { labelValve.Text = isValveClosed ? "Закрыт" : "Открыт"; }));
buttonValve.Invoke(new MethodInvoker(delegate { buttonValve.Text = isValveClosed ? "Открыть" : "Закрыть"; }));
}
});
} }
catch (Exception err)
if (latestMessage != null && latestMessage.Status != ModbusStatus.Error)
{ {
isValveClosed = !isValveClosed; MessageBox.Show(err.Message, "Valve Set Error");
labelValve.Text = isValveClosed ? "Закрыт" : "Открыт";
buttonValve.Text = isValveClosed ? "Открыть" : "Закрыть";
} }
} }
// Авария // Авария
private async void buttonAlarm_Click(object sender, EventArgs e) private void buttonAlarm_Click(object sender, EventArgs e)
{ {
ushort value = alarmStatus ? (ushort)0 : (ushort)0xFF00; ushort value = alarmStatus ? (ushort)0 : (ushort)0xFF00;
isAwaitingResponse = true; if (SetEntry(device.alarmStatus, value))
latestMessage = null;
Modbus.WriteSingleAsync(FunctionCode.WriteCoil, modbusID, device.alarmStatus.address, value);
stopwatch.Restart();
while (isAwaitingResponse)
{
if (stopwatch.ElapsedMilliseconds > port.ReadTimeout)
{
Console.WriteLine("Response timed out.");
break;
}
}
if (latestMessage != null && latestMessage.Status != ModbusStatus.Error)
{ {
alarmStatus = !alarmStatus; alarmStatus = !alarmStatus;
labelAlarm.Text = alarmStatus ? "Протечка!" : "Нет"; labelAlarm.Text = alarmStatus ? "Протечка!" : "Нет";
@@ -357,24 +372,10 @@ namespace Gidrolock_Modbus_Scanner
} }
// Режим уборки // Режим уборки
private async void buttonCleaning_Click(object sender, EventArgs e) private void buttonCleaning_Click(object sender, EventArgs e)
{ {
ushort value = cleaningStatus ? (ushort)0 : (ushort)0xFF00; ushort value = cleaningStatus ? (ushort)0 : (ushort)0xFF00;
isAwaitingResponse = true; if (SetEntry(device.cleaningMode, value))
latestMessage = null;
Modbus.WriteSingleAsync(FunctionCode.WriteCoil, modbusID, device.cleaningMode.address, value);
stopwatch.Restart();
while (isAwaitingResponse)
{
if (stopwatch.ElapsedMilliseconds > 1000)
{
Console.WriteLine("Response timed out.");
break;
}
}
if (latestMessage != null && latestMessage.Status != ModbusStatus.Error)
{ {
cleaningStatus = !cleaningStatus; cleaningStatus = !cleaningStatus;
labelCleaning.Text = cleaningStatus ? "вкл" : "выкл"; labelCleaning.Text = cleaningStatus ? "вкл" : "выкл";
@@ -383,37 +384,19 @@ namespace Gidrolock_Modbus_Scanner
} }
// Задать скорость передачи данных для устройства // Задать скорость передачи данных для устройства
private async void buttonSetSpeed_Click(object sender, EventArgs e) private void buttonSetSpeed_Click(object sender, EventArgs e)
{ {
try try
{ {
string str = cBoxSpeed.Items[cBoxSpeed.SelectedIndex].ToString(); string str = cBoxSpeed.Items[cBoxSpeed.SelectedIndex].ToString();
str = str.Substring(0, str.Length - 2); //clip off two zeroes at the end str = str.Substring(0, str.Length - 2); //clip off two zeroes at the end
ushort newSpeed = (ushort)Int16.Parse(str); ushort newSpeed = (ushort)Int16.Parse(str);
//Console.WriteLine("Baudrate: " + newSpeed); Console.WriteLine("new speed: " + newSpeed);
if (SetEntry(device.baudRate, newSpeed))
// send speed value to device
// await for response
isAwaitingResponse = true;
latestMessage = null;
Modbus.WriteSingleAsync(FunctionCode.WriteRegister, modbusID, device.baudRate.address, newSpeed);
stopwatch.Restart();
while (isAwaitingResponse)
{ {
if (stopwatch.ElapsedMilliseconds > port.ReadTimeout) //port.Close();
{ port.BaudRate = newSpeed * 100;
Console.WriteLine("Response timed out."); //port.Open();
break;
}
}
if (latestMessage != null && latestMessage.Status != ModbusStatus.Error)
{
port.Close();
port.BaudRate = newSpeed;
port.Open();
} }
} }
catch (Exception ex) { MessageBox.Show(ex.Message); } catch (Exception ex) { MessageBox.Show(ex.Message); }

View File

@@ -257,23 +257,14 @@ namespace Gidrolock_Modbus_Scanner
return false; return false;
} }
#endregion #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 Stopwatch stopwatch = new Stopwatch(); static Stopwatch stopwatch = new Stopwatch();
static byte[] buffer = new byte[255]; static byte[] buffer = new byte[255];
static int offset = 0; static int offset = 0;
static int count = 0; static int count = 0;
static ModbusStatus responseStatus;
static bool bytecountFound = false; static bool bytecountFound = false;
static int expectedBytes = 0; static int expectedBytes = 0;
static void PortDataReceived(object sender, EventArgs e) static void PortDataReceived(object sender, EventArgs e)
@@ -284,13 +275,12 @@ namespace Gidrolock_Modbus_Scanner
bytecountFound = false; bytecountFound = false;
expectedBytes = 0; expectedBytes = 0;
Console.WriteLine("Port data received");
try try
{ {
stopwatch.Restart(); stopwatch.Restart();
while (stopwatch.ElapsedMilliseconds < port.ReadTimeout) while (stopwatch.ElapsedMilliseconds < port.ReadTimeout)
{ {
if (bytecountFound && offset >= expectedBytes + 5) if (bytecountFound && offset >= expectedBytes)
break; break;
if (port.BytesToRead > 0) if (port.BytesToRead > 0)
{ {
@@ -298,13 +288,31 @@ namespace Gidrolock_Modbus_Scanner
count = port.BytesToRead; count = port.BytesToRead;
port.Read(buffer, offset, count); port.Read(buffer, offset, count);
offset += count; offset += count;
if (!bytecountFound && offset >= 2) if (offset >= 1)
{ {
expectedBytes = buffer[2]; if (buffer[1] < 0x05)
responseStatus = ModbusStatus.ReadSuccess;
else if (buffer[1] < 0x10)
{
responseStatus = ModbusStatus.WriteSuccess;
expectedBytes = 8;
bytecountFound = true;
}
else
{
responseStatus = ModbusStatus.Error;
expectedBytes = 5;
bytecountFound = true;
}
}
if (responseStatus == ModbusStatus.ReadSuccess && !bytecountFound && offset >= 2)
{
expectedBytes = buffer[2] + 5;
Console.WriteLine("Found data byte count: " + expectedBytes); Console.WriteLine("Found data byte count: " + expectedBytes);
bytecountFound = true; bytecountFound = true;
} }
if (bytecountFound && offset >= expectedBytes + 5) // reached end of message if (bytecountFound && offset >= expectedBytes) // reached end of message
{ {
Console.WriteLine("Reached end of message"); Console.WriteLine("Reached end of message");
break; break;
@@ -315,8 +323,8 @@ namespace Gidrolock_Modbus_Scanner
// Console.WriteLine("Buffer: " + ByteArrayToString(buffer, false)); // Console.WriteLine("Buffer: " + ByteArrayToString(buffer, false));
// assume that the message ended // assume that the message ended
Console.WriteLine("Message reception ended"); Console.WriteLine("Message reception ended");
byte[] message = new byte[expectedBytes + 5]; byte[] message = new byte[expectedBytes];
for (int i = 0; i < expectedBytes + 5; i++) for (int i = 0; i < expectedBytes; i++)
message[i] = buffer[i]; message[i] = buffer[i];
Console.WriteLine("Incoming message: " + ByteArrayToString(message, false)); Console.WriteLine("Incoming message: " + ByteArrayToString(message, false));
@@ -324,16 +332,9 @@ namespace Gidrolock_Modbus_Scanner
if (!CheckResponse(message)) if (!CheckResponse(message))
Console.WriteLine("Bad CRC or not a modbus message!"); Console.WriteLine("Bad CRC or not a modbus message!");
if (message[1] <= 0x04) // read functions ResponseReceived.Invoke(null, new ModbusResponseEventArgs(message, responseStatus));
ResponseReceived.Invoke(null, new ModbusResponseEventArgs(message, ModbusStatus.ReadSuccess));
else
{
if (message[1] <= 0x10) // write functions
ResponseReceived.Invoke(null, new ModbusResponseEventArgs(message, ModbusStatus.WriteSuccess));
else // error codes
ResponseReceived.Invoke(null, new ModbusResponseEventArgs(message, ModbusStatus.Error));
}
} }
catch (Exception err) catch (Exception err)
{ {