finishing setup for writing values in DGV

This commit is contained in:
nikzori
2024-12-24 15:11:41 +03:00
parent 604cb9d459
commit 8c78abcd1e
4 changed files with 138 additions and 45 deletions

View File

@@ -8,6 +8,7 @@ using System.Linq;
using System.Net; using System.Net;
using System.Runtime.Remoting.Messaging; using System.Runtime.Remoting.Messaging;
using System.Text; using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
@@ -47,12 +48,15 @@ namespace Gidrolock_Modbus_Scanner
DGV_Device.Columns.Add("#", "#"); DGV_Device.Columns.Add("#", "#");
DGV_Device.Columns[0].FillWeight = 20; DGV_Device.Columns[0].FillWeight = 20;
DGV_Device.Columns[0].ReadOnly = true;
DGV_Device.Columns.Add("Name", "Имя"); DGV_Device.Columns.Add("Name", "Имя");
DGV_Device.Columns[1].FillWeight = 40; DGV_Device.Columns[1].FillWeight = 40;
DGV_Device.Columns[1].ReadOnly = true;
DGV_Device.Columns.Add("Value", "Значение"); DGV_Device.Columns.Add("Value", "Значение");
DGV_Device.Columns[2].FillWeight = 60; DGV_Device.Columns[2].FillWeight = 60;
DGV_Device.Columns.Add("Address", "Адрес"); DGV_Device.Columns.Add("Address", "Адрес");
DGV_Device.Columns[3].FillWeight = 30; DGV_Device.Columns[3].FillWeight = 30;
DGV_Device.Columns[3].ReadOnly = true;
DGV_Device.Columns.Add(new DataGridViewCheckBoxColumn()); DGV_Device.Columns.Add(new DataGridViewCheckBoxColumn());
DGV_Device.Columns[4].Name = "Опрос"; DGV_Device.Columns[4].Name = "Опрос";
DGV_Device.Columns[4].FillWeight = 20; DGV_Device.Columns[4].FillWeight = 20;
@@ -60,6 +64,14 @@ namespace Gidrolock_Modbus_Scanner
int rowCount = 0; int rowCount = 0;
DGV_Device.CellEndEdit += (o, e) =>
{
if (e.ColumnIndex == 2)
{
}
};
foreach (Entry e in entries) foreach (Entry e in entries)
{ {
if (e.length > 1) if (e.length > 1)
@@ -68,6 +80,8 @@ namespace Gidrolock_Modbus_Scanner
if ((e.length == 2 && e.dataType == "uint32") || e.dataType == "string") if ((e.length == 2 && e.dataType == "uint32") || e.dataType == "string")
{ {
DGV_Device.Rows.Add(rowCount, e.name, "", e.address); DGV_Device.Rows.Add(rowCount, e.name, "", e.address);
if (e.registerType == RegisterType.Input || e.registerType == RegisterType.Discrete)
DGV_Device.Rows[rowCount].Cells[2].ReadOnly = true;
rowCount++; rowCount++;
} }
else else
@@ -77,6 +91,17 @@ namespace Gidrolock_Modbus_Scanner
if (i < e.labels.Count) if (i < e.labels.Count)
DGV_Device.Rows.Add(rowCount, e.name + ": " + e.labels[i], "", e.address + i); DGV_Device.Rows.Add(rowCount, e.name + ": " + e.labels[i], "", e.address + i);
else DGV_Device.Rows.Add(rowCount, e.address + i, "", e.address + i); else DGV_Device.Rows.Add(rowCount, e.address + i, "", e.address + i);
if (i != 0) // Hide rightmost cells for extra registers in the Entry
{
DGV_Device.Rows[rowCount].Cells[4] = new DataGridViewTextBoxCell();
DGV_Device.Rows[rowCount].Cells[4].Value = "";
DGV_Device.Rows[rowCount].Cells[4].Style.ForeColor = Color.DarkGray;
DGV_Device.Rows[rowCount].Cells[4].Style.BackColor = Color.DarkGray;
DGV_Device.Rows[rowCount].Cells[4].ReadOnly = true;
}
if (e.registerType == RegisterType.Input || e.registerType == RegisterType.Discrete)
DGV_Device.Rows[rowCount].Cells[2].ReadOnly = true;
rowCount++; rowCount++;
} }
} }
@@ -85,20 +110,35 @@ namespace Gidrolock_Modbus_Scanner
else else
{ {
DGV_Device.Rows.Add(rowCount, e.name, "", e.address); DGV_Device.Rows.Add(rowCount, e.name, "", e.address);
if (e.registerType == RegisterType.Input || e.registerType == RegisterType.Discrete)
DGV_Device.Rows[rowCount].Cells[2].ReadOnly = true;
if (e.registerType == RegisterType.Coil)
{
DGV_Device.Rows[rowCount].Cells[2] = new DataGridViewComboBoxCell();
var cbc = DGV_Device.Rows[rowCount].Cells[2] as DataGridViewComboBoxCell;
if (e.valueParse != null)
{
if (e.valueParse.ContainsKey("false"))
cbc.Items.Add(e.valueParse["false"]);
if (e.valueParse.ContainsKey("true"))
cbc.Items.Add(e.valueParse["true"]);
}
else
{
cbc.Items.Add("False");
cbc.Items.Add("True");
}
}
rowCount++; rowCount++;
} }
} }
foreach (DataGridViewRow row in DGV_Device.Rows) foreach (DataGridViewRow row in DGV_Device.Rows)
{ {
row.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter; row.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
if (row.Cells[2] is DataGridViewComboBoxCell)
row.Cells[2].Style.Alignment = DataGridViewContentAlignment.MiddleLeft;
} }
/*
for (int i = 0; i < entries.Count; i++)
{
DGV_Device.Rows.Add(i.ToString(), entries[i].name, "", entries[i].address);
DGV_Device.Rows[i].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
}
*/
foreach (DataGridViewColumn column in DGV_Device.Columns) foreach (DataGridViewColumn column in DGV_Device.Columns)
column.SortMode = DataGridViewColumnSortMode.NotSortable; // disabling sorting for now column.SortMode = DataGridViewColumnSortMode.NotSortable; // disabling sorting for now
@@ -121,8 +161,9 @@ namespace Gidrolock_Modbus_Scanner
DataGridViewCheckBoxCell chbox = DGV_Device.Rows[activeDGVIndex].Cells[4] as DataGridViewCheckBoxCell; DataGridViewCheckBoxCell chbox = DGV_Device.Rows[activeDGVIndex].Cells[4] as DataGridViewCheckBoxCell;
if (Convert.ToBoolean(chbox.Value)) if (Convert.ToBoolean(chbox.Value))
{ {
Console.WriteLine("Polling for " + device.entries[activeEntryIndex].name); //Console.WriteLine("Polling for " + device.entries[activeEntryIndex].name);
await PollForEntry(entries[activeEntryIndex]).ContinueWith(_ => Task.Delay(150)); await PollForEntry(entries[activeEntryIndex]);
Thread.Sleep(150);
} }
else //need to skip multiple dgv entries without accidentaly skipping entries else //need to skip multiple dgv entries without accidentaly skipping entries
{ {
@@ -155,20 +196,22 @@ namespace Gidrolock_Modbus_Scanner
public async Task PollForEntry(Entry entry) public async Task PollForEntry(Entry entry)
{ {
byte[] message = new byte[8]; byte[] message = new byte[8];
var send = await Modbus.ReadRegAsync(port, slaveID, (FunctionCode)entry.registerType, entry.address, entry.length); var send = Modbus.ReadRegAsync(port, slaveID, (FunctionCode)entry.registerType, entry.address, entry.length);
isAwaitingResponse = true; isAwaitingResponse = true;
Task delay = Task.WhenAny(Task.Delay(timeout), Task.Run(() => { while (isAwaitingResponse) { } return; })).ContinueWith((t) => Task delay = Task.WhenAny(Task.Delay(timeout), Task.Run(() => { while (isAwaitingResponse) { } return true; })).ContinueWith((t) =>
{ {
if (isAwaitingResponse) if (isAwaitingResponse)
{ {
Console.WriteLine("Response timed out."); Console.WriteLine("Response timed out.");
isAwaitingResponse = false; isAwaitingResponse = false;
} }
return false;
}); });
await delay; await delay;
} }
void PublishResponse(object sender, ModbusResponseEventArgs e) void PublishResponse(object sender, ModbusResponseEventArgs e)
@@ -189,14 +232,43 @@ namespace Gidrolock_Modbus_Scanner
if (entries[activeEntryIndex].labels is null || entries[activeEntryIndex].labels.Count == 0) // assume that no labels = 1 entry if (entries[activeEntryIndex].labels is null || entries[activeEntryIndex].labels.Count == 0) // assume that no labels = 1 entry
{ {
if (entries[activeEntryIndex].valueParse is null || entries[activeEntryIndex].valueParse.Keys.Count == 0) if (entries[activeEntryIndex].valueParse is null || entries[activeEntryIndex].valueParse.Keys.Count == 0)
DGV_Device.Rows[activeEntryIndex].Cells[2].Value = e.Data[0] > 0x00 ? "true" : "false"; {
if (entries[activeEntryIndex].registerType == RegisterType.Coil)
{
DGV_Device.Invoke((MethodInvoker)delegate
{
var cbc = DGV_Device.Rows[activeDGVIndex].Cells[2] as DataGridViewComboBoxCell;
cbc.Value = e.Data[0] > 0x00 ? cbc.Items[1] : cbc.Items[0];
});
}
else else
{ {
try { DGV_Device.Rows[activeEntryIndex].Cells[2].Value = e.Data[0] > 0x00 ? entries[activeEntryIndex].valueParse["true"] : entries[activeEntryIndex].valueParse["false"]; } DGV_Device.Invoke((MethodInvoker)delegate
{
DGV_Device.Rows[activeDGVIndex].Cells[2].Value = e.Data[0] > 0x00 ? "true" : "false";
});
}
}
else
{
try { DGV_Device.Rows[activeDGVIndex].Cells[2].Value = e.Data[0] > 0x00 ? entries[activeEntryIndex].valueParse["true"] : entries[activeEntryIndex].valueParse["false"]; }
catch (Exception err) catch (Exception err)
{ {
MessageBox.Show("Value parsing error for bool entry: " + entries[activeEntryIndex].name + "; " + err.Message); if (entries[activeEntryIndex].registerType == RegisterType.Coil)
DGV_Device.Rows[activeEntryIndex].Cells[2].Value = e.Data[0] > 0x00 ? "true" : "false"; {
DGV_Device.Invoke((MethodInvoker)delegate
{
var cbc = DGV_Device.Rows[activeDGVIndex].Cells[2] as DataGridViewComboBoxCell;
cbc.Value = e.Data[0] > 0x00 ? cbc.Items[1] : cbc.Items[0];
});
}
else
{
DGV_Device.Invoke((MethodInvoker)delegate
{
DGV_Device.Rows[activeDGVIndex].Cells[2].Value = e.Data[0] > 0x00 ? "true" : "false";
});
}
} }
} }
activeDGVIndex++; activeDGVIndex++;
@@ -214,7 +286,21 @@ namespace Gidrolock_Modbus_Scanner
} }
for (int i = 0; i < entries[activeEntryIndex].labels.Count; i++) for (int i = 0; i < entries[activeEntryIndex].labels.Count; i++)
{ {
DGV_Device.Rows[activeDGVIndex].Cells[2].Value = values[i]; if (entries[activeEntryIndex].registerType == RegisterType.Coil)
{
DGV_Device.Invoke((MethodInvoker)delegate
{
var cbc = DGV_Device.Rows[activeEntryIndex].Cells[2] as DataGridViewComboBoxCell;
cbc.Value = e.Data[0] > 0x00 ? cbc.Items[1] : cbc.Items[0];
});
}
else
{
DGV_Device.Invoke((MethodInvoker)delegate
{
DGV_Device.Rows[activeEntryIndex].Cells[2].Value = e.Data[0] > 0x00 ? "true": "false";
});
}
activeDGVIndex++; activeDGVIndex++;
} }
} }
@@ -227,7 +313,7 @@ namespace Gidrolock_Modbus_Scanner
{ {
//Array.Reverse(e.Data); // this was necessary, but something changed, idk //Array.Reverse(e.Data); // this was necessary, but something changed, idk
Console.WriteLine("ushort parsed value: " + value); //Console.WriteLine("ushort parsed value: " + value);
DGV_Device.Rows[activeDGVIndex].Cells[2].Value = value; DGV_Device.Rows[activeDGVIndex].Cells[2].Value = value;
} }
else else
@@ -249,15 +335,19 @@ namespace Gidrolock_Modbus_Scanner
try try
{ {
List<ushort> values = new List<ushort>(); List<ushort> values = new List<ushort>();
for (int i = 0; i < dbc - 2; i += 2) for (int i = 0; i < dbc; i += 2)
{ {
ushort s = BitConverter.ToUInt16(e.Data, i); ushort s = BitConverter.ToUInt16(e.Data, i);
Console.WriteLine("ushort value: " + s);
values.Add(s); values.Add(s);
} }
//Console.WriteLine("ushort values count: " + values.Count);
//Console.WriteLine("entity labels count: " + entries[activeEntryIndex].labels.Count);
for (int i = 0; i < entries[activeEntryIndex].labels.Count; i++) for (int i = 0; i < entries[activeEntryIndex].labels.Count; i++)
{
DGV_Device.Invoke((MethodInvoker)delegate
{ {
DGV_Device.Rows[activeDGVIndex].Cells[2].Value = values[i]; DGV_Device.Rows[activeDGVIndex].Cells[2].Value = values[i];
});
activeDGVIndex++; activeDGVIndex++;
} }
} }
@@ -300,9 +390,7 @@ namespace Gidrolock_Modbus_Scanner
//MessageBox.Show("Получен ответ от устройства: " + dataCleaned, "Успех", MessageBoxButtons.OK); //MessageBox.Show("Получен ответ от устройства: " + dataCleaned, "Успех", MessageBoxButtons.OK);
port.DiscardInBuffer(); port.DiscardInBuffer();
} }
catch (Exception err) { catch (Exception err) { MessageBox.Show(err.Message, "Publish response error"); }
MessageBox.Show(err.Message, "Publish response error");
}
} }
if (activeDGVIndex >= DGV_Device.Rows.Count) if (activeDGVIndex >= DGV_Device.Rows.Count)

17
Main.cs
View File

@@ -14,7 +14,6 @@ using System.IO;
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Threading; using System.Threading;
namespace Gidrolock_Modbus_Scanner namespace Gidrolock_Modbus_Scanner
{ {
public partial class App : Form public partial class App : Form
@@ -103,7 +102,7 @@ namespace Gidrolock_Modbus_Scanner
CBox_Parity.Items.Add("Нечетн."); CBox_Parity.Items.Add("Нечетн.");
CBox_Parity.SelectedIndex = 0; CBox_Parity.SelectedIndex = 0;
UpDown_RegLength.Value = 0; UpDown_RegLength.Value = 1;
/* TCP Setup */ /* TCP Setup */
/* /*
Radio_SerialPort.Checked = true; Radio_SerialPort.Checked = true;
@@ -180,7 +179,7 @@ namespace Gidrolock_Modbus_Scanner
port.ReadTimeout = 3000; port.ReadTimeout = 3000;
port.WriteTimeout = 3000; port.WriteTimeout = 3000;
port.ReadBufferSize = 8192;
message = new byte[255]; message = new byte[255];
port.Open(); port.Open();
@@ -191,7 +190,7 @@ namespace Gidrolock_Modbus_Scanner
{ {
try try
{ {
await Modbus.ReadRegAsync(port, (byte)UpDown_ModbusID.Value, functionCode, address, length); Modbus.ReadRegAsync(port, (byte)UpDown_ModbusID.Value, functionCode, address, length);
isAwaitingResponse = true; isAwaitingResponse = true;
await Task.Delay(port.ReadTimeout).ContinueWith(_ => await Task.Delay(port.ReadTimeout).ContinueWith(_ =>
{ {
@@ -244,7 +243,7 @@ namespace Gidrolock_Modbus_Scanner
port.ReadTimeout = 3000; port.ReadTimeout = 3000;
port.WriteTimeout = 3000; port.WriteTimeout = 3000;
port.ReadBufferSize = 8192;
message = new byte[255]; message = new byte[255];
port.Open(); port.Open();
@@ -556,7 +555,7 @@ namespace Gidrolock_Modbus_Scanner
port.ReadTimeout = 3000; port.ReadTimeout = 3000;
port.WriteTimeout = 3000; port.WriteTimeout = 3000;
port.ReadBufferSize = 8192;
message = new byte[255]; message = new byte[255];
port.Open(); port.Open();
@@ -584,9 +583,9 @@ namespace Gidrolock_Modbus_Scanner
case (FunctionCode.WriteCoil): case (FunctionCode.WriteCoil):
Console.WriteLine("Trying to force single coil"); Console.WriteLine("Trying to force single coil");
if (valueLower == "true" || valueLower == "1") if (valueLower == "true" || valueLower == "1")
await Modbus.WriteSingleAsync(port, (FunctionCode)functionCode, (byte)UpDown_ModbusID.Value, (ushort)address, 0xFF_00); Modbus.WriteSingleAsync(port, (FunctionCode)functionCode, (byte)UpDown_ModbusID.Value, (ushort)address, 0xFF_00);
else if (valueLower == "false" || valueLower == "0") else if (valueLower == "false" || valueLower == "0")
await Modbus.WriteSingleAsync(port, (FunctionCode)functionCode, (byte)UpDown_ModbusID.Value, (ushort)address, 0x00_00); Modbus.WriteSingleAsync(port, (FunctionCode)functionCode, (byte)UpDown_ModbusID.Value, (ushort)address, 0x00_00);
else MessageBox.Show("Неподходящие значения для регистра типа Coil"); else MessageBox.Show("Неподходящие значения для регистра типа Coil");
break; break;
case (FunctionCode.WriteRegister): case (FunctionCode.WriteRegister):
@@ -641,7 +640,7 @@ namespace Gidrolock_Modbus_Scanner
} }
if (canWrite) if (canWrite)
await Modbus.WriteSingleAsync(port, (FunctionCode)functionCode, (byte)UpDown_ModbusID.Value, (ushort)address, Modbus.WriteSingleAsync(port, (FunctionCode)functionCode, (byte)UpDown_ModbusID.Value, (ushort)address,
(ushort)value); (ushort)value);
break; break;
default: default:

View File

@@ -2,6 +2,7 @@
using System.IO.Ports; using System.IO.Ports;
using System.Runtime.Remoting.Messaging; using System.Runtime.Remoting.Messaging;
using System.Text; using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
using static System.Net.Mime.MediaTypeNames; using static System.Net.Mime.MediaTypeNames;
@@ -63,7 +64,7 @@ namespace Gidrolock_Modbus_Scanner
#endregion #endregion
#region Read Functions #region Read Functions
public static async Task<bool> ReadRegAsync(SerialPort port, byte slaveID, FunctionCode functionCode, ushort address, ushort length) public static bool ReadRegAsync(SerialPort port, byte slaveID, FunctionCode functionCode, ushort address, ushort length)
{ {
//Ensure port is open: //Ensure port is open:
if (port.IsOpen) if (port.IsOpen)
@@ -83,7 +84,7 @@ namespace Gidrolock_Modbus_Scanner
//Send modbus message to Serial Port: //Send modbus message to Serial Port:
try try
{ {
await Task.Run(() => { port.Write(message, 0, message.Length); }); port.Write(message, 0, message.Length);
return true; return true;
} }
catch (Exception err) catch (Exception err)
@@ -105,7 +106,7 @@ namespace Gidrolock_Modbus_Scanner
#endregion #endregion
#region Write Single Coil/Register #region Write Single Coil/Register
public static async Task<bool> WriteSingleAsync(SerialPort port, FunctionCode functionCode, byte slaveID, ushort address, ushort value) public static bool WriteSingleAsync(SerialPort port, FunctionCode functionCode, byte slaveID, ushort address, ushort value)
{ {
//Ensure port is open: //Ensure port is open:
if (!port.IsOpen) if (!port.IsOpen)
@@ -239,24 +240,26 @@ namespace Gidrolock_Modbus_Scanner
{ {
try try
{ {
Thread.Sleep(50);
byte[] message = new byte[port.BytesToRead]; byte[] message = new byte[port.BytesToRead];
//Console.WriteLine("Bytes to read:" + port.BytesToRead);
port.Read(message, 0, port.BytesToRead); port.Read(message, 0, port.BytesToRead);
Console.WriteLine("Incoming message: " + ByteArrayToString(message, false)); //Console.WriteLine("Incoming message: " + ByteArrayToString(message, false));
if (message[1] <= 0x04) // read functions if (message[1] <= 0x04) // read functions
{ {
Console.WriteLine("It's a read message"); //Console.WriteLine("It's a read message");
ResponseReceived.Invoke(null, new ModbusResponseEventArgs(message, ModbusStatus.ReadSuccess)); ResponseReceived.Invoke(null, new ModbusResponseEventArgs(message, ModbusStatus.ReadSuccess));
} }
else else
{ {
if (message[1] <= 0x10) // write functions if (message[1] <= 0x10) // write functions
{ {
Console.WriteLine("It's a write message"); //Console.WriteLine("It's a write message");
ResponseReceived.Invoke(null, new ModbusResponseEventArgs(message, ModbusStatus.WriteSuccess)); ResponseReceived.Invoke(null, new ModbusResponseEventArgs(message, ModbusStatus.WriteSuccess));
} }
else // error codes else // error codes
{ {
Console.WriteLine("It's an error"); //Console.WriteLine("It's an error");
ResponseReceived.Invoke(null, new ModbusResponseEventArgs(message, ModbusStatus.Error)); ResponseReceived.Invoke(null, new ModbusResponseEventArgs(message, ModbusStatus.Error));
} }
} }
@@ -287,7 +290,7 @@ namespace Gidrolock_Modbus_Scanner
{ {
Data[i] = message[i + 3]; Data[i] = message[i + 3];
} }
Console.WriteLine("Read data: " + Modbus.ByteArrayToString(Data, false)); //Console.WriteLine("Read data: " + Modbus.ByteArrayToString(Data, false));
} }
else Data = new byte[1] {0x0F}; else Data = new byte[1] {0x0F};
} }

View File

@@ -22,6 +22,10 @@ Modbus TCP
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
"description" : "Smart valve controller unit with wired and wireless leak sensor support", "description" : "Smart valve controller unit with wired and wireless leak sensor support",
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Modbus ID <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
// <20><><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
"modbusID" : 30
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
// <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, // <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>,
@@ -69,8 +73,7 @@ Modbus TCP
### To-Do ### To-Do
1. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> `bool` - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.
2. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>. -- <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
3. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>. - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Modbus TCP
4. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Modbus TCP