Files
gidrolock-configurator/Datasheet.cs
2024-12-25 13:59:04 +03:00

419 lines
20 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO.Ports;
using System.Linq;
using System.Net;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Gidrolock_Modbus_Scanner
{
public partial class Datasheet : Form
{
bool isPolling = false;
bool isAwaitingResponse = false;
byte[] message = new byte[255];
int timeout = 3000;
int pollDelay = 250; // delay between each entry poll, ms
byte slaveID;
Device device = App.device;
List<Entry> entries;
int activeEntryIndex; // entry index for modbus responses
int activeDGVIndex; // index for DGV rows
SerialPort port = Modbus.port;
bool closed = false;
public Datasheet(byte slaveID)
{
Modbus.ResponseReceived += PublishResponse;
this.slaveID = slaveID;
entries = device.entries;
InitializeComponent();
Label_DeviceName.Text = device.name;
Label_Description.Text = device.description;
DGV_Device.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
DGV_Device.MultiSelect = false;
DGV_Device.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.DisplayedCellsExceptHeaders;
DGV_Device.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
DGV_Device.Columns.Add("#", "#");
DGV_Device.Columns[0].FillWeight = 20;
DGV_Device.Columns[0].ReadOnly = true;
DGV_Device.Columns.Add("Name", "Имя");
DGV_Device.Columns[1].FillWeight = 40;
DGV_Device.Columns[1].ReadOnly = true;
DGV_Device.Columns.Add("Value", "Значение");
DGV_Device.Columns[2].FillWeight = 60;
DGV_Device.Columns.Add("Address", "Адрес");
DGV_Device.Columns[3].FillWeight = 30;
DGV_Device.Columns[3].ReadOnly = true;
DGV_Device.Columns.Add(new DataGridViewCheckBoxColumn());
DGV_Device.Columns[4].Name = "Опрос";
DGV_Device.Columns[4].FillWeight = 20;
DGV_Device.Columns[4].ValueType = typeof(bool);
int rowCount = 0;
DGV_Device.CellEndEdit += (o, e) =>
{
if (e.ColumnIndex == 2)
{
int y = e.RowIndex;
Console.WriteLine("Row " + y + " current value: " + DGV_Device.Rows[y].Cells[2].Value.ToString());
}
};
foreach (Entry e in entries)
{
if (e.length > 1)
{
// multi-register entry check
if ((e.length == 2 && e.dataType == "uint32") || e.dataType == "string")
{
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++;
}
else
{
for (int i = 0; i < e.length; i++)
{
if (i < e.labels.Count)
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);
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++;
}
}
}
else
{
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++;
}
}
foreach (DataGridViewRow row in DGV_Device.Rows)
{
row.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
if (row.Cells[2] is DataGridViewComboBoxCell)
row.Cells[2].Style.Alignment = DataGridViewContentAlignment.MiddleLeft;
}
foreach (DataGridViewColumn column in DGV_Device.Columns)
column.SortMode = DataGridViewColumnSortMode.NotSortable; // disabling sorting for now
FormClosing += (s, e) => { closed = true; };
Task.Run(() => AutoPollAsync());
}
public async void AutoPollAsync()
{
if (!port.IsOpen)
port.Open();
port.ReadTimeout = timeout;
try
{
while (!closed)
{
if (isPolling)
{
// holy fuck DGV is awful
DataGridViewCheckBoxCell chbox = DGV_Device.Rows[activeDGVIndex].Cells[4] as DataGridViewCheckBoxCell;
if (Convert.ToBoolean(chbox.Value))
{
//Console.WriteLine("Polling for " + device.entries[activeEntryIndex].name);
await PollForEntry(entries[activeEntryIndex]);
Thread.Sleep(150);
}
else //need to skip multiple dgv entries without accidentaly skipping entries
{
if (device.entries[activeEntryIndex].labels is null || device.entries[activeEntryIndex].labels.Count == 0)
activeDGVIndex++;
else
{
for (int i = 0; i < device.entries[activeEntryIndex].labels.Count; i++)
{
activeDGVIndex++;
}
}
}
activeEntryIndex++;
if (activeEntryIndex >= device.entries.Count)
activeEntryIndex = 0;
if (activeDGVIndex >= DGV_Device.RowCount)
activeDGVIndex = 0;
}
}
}
catch (Exception err)
{
MessageBox.Show(err.Message, "AutoPollAsync");
}
}
public async Task PollForEntry(Entry entry)
{
byte[] message = new byte[8];
var send = Modbus.ReadRegAsync(port, slaveID, (FunctionCode)entry.registerType, entry.address, entry.length);
isAwaitingResponse = true;
Task delay = Task.WhenAny(Task.Delay(timeout), Task.Run(() => { while (isAwaitingResponse) { } return true; })).ContinueWith((t) =>
{
if (isAwaitingResponse)
{
Console.WriteLine("Response timed out.");
isAwaitingResponse = false;
}
return false;
});
await delay;
}
void PublishResponse(object sender, ModbusResponseEventArgs e)
{
if (isAwaitingResponse)
{
try
{
if (entries[activeEntryIndex].readOnce)
{
DataGridViewCheckBoxCell chbox = DGV_Device.Rows[activeDGVIndex].Cells[4] as DataGridViewCheckBoxCell;
chbox.Value = false;
}
int dbc = e.Message[2]; // data byte count
switch (entries[activeEntryIndex].dataType)
{
case ("bool"):
if (entries[activeEntryIndex].labels is null || entries[activeEntryIndex].labels.Count == 0) // assume that no labels = 1 entry
{
//no valueParse keys
if (entries[activeEntryIndex].valueParse is null || entries[activeEntryIndex].valueParse.Keys.Count == 0)
{
// coil combobox
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];
});
}
// discrete inputs
else
{
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)
{
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
{
DGV_Device.Invoke((MethodInvoker)delegate
{
DGV_Device.Rows[activeDGVIndex].Cells[2].Value = e.Data[0] > 0x00 ? "True" : "False";
});
}
}
}
activeDGVIndex++;
}
else
{
List<bool> values = new List<bool>();
for (int i = 0; i < dbc; i++)
{
for (int j = 0; j < 8; j++)
{
bool res = (((e.Data[i] >> j) & 0x01) >= 1) ? true : false;
values.Add(res);
}
}
for (int i = 0; i < entries[activeEntryIndex].labels.Count; i++)
{
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
{
DGV_Device.Invoke((MethodInvoker)delegate
{
DGV_Device.Rows[activeDGVIndex].Cells[2].Value = e.Data[0] > 0x00 ? "true": "false";
});
}
activeDGVIndex++;
}
}
break;
case ("uint16"):
ushort value = BitConverter.ToUInt16(e.Data, 0);
if (entries[activeEntryIndex].labels is null || entries[activeEntryIndex].labels.Count == 0) // single value
{
if (entries[activeEntryIndex].valueParse is null || entries[activeEntryIndex].valueParse.Keys.Count == 0)
{
//Array.Reverse(e.Data); // this was necessary, but something changed, idk
//Console.WriteLine("ushort parsed value: " + value);
DGV_Device.Rows[activeDGVIndex].Cells[2].Value = value;
}
else
{
try
{
DGV_Device.Rows[activeDGVIndex].Cells[2].Value = entries[activeEntryIndex].valueParse[value.ToString()];
}
catch (Exception err)
{
DGV_Device.Rows[activeDGVIndex].Cells[2].Value = value; MessageBox.Show("Error parsing uint value at address: " + entries[activeEntryIndex].address + "; " + err.Message, "uint16 parse");
}
}
activeDGVIndex++;
}
else // value group
{
try
{
List<ushort> values = new List<ushort>();
for (int i = 0; i < dbc; i += 2)
{
ushort s = BitConverter.ToUInt16(e.Data, i);
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++)
{
if (device.entries[activeEntryIndex].valueParse != null)
{
DGV_Device.Invoke((MethodInvoker)delegate
{
DGV_Device.Rows[activeDGVIndex].Cells[2].Value = device.entries[activeEntryIndex].valueParse[values[i].ToString()];
});
}
else
{
DGV_Device.Rows[activeDGVIndex].Cells[2].Value = values[i];
}
activeDGVIndex++;
}
}
catch (Exception err)
{
DGV_Device.Rows[activeDGVIndex].Cells[2].Value = value; MessageBox.Show("Error parsing uint value at address: " + entries[activeEntryIndex].address + "; " + err.Message, "uint16 group req parse");
}
}
break;
case ("uint32"):
Array.Reverse(e.Data);
DGV_Device.Rows[activeDGVIndex].Cells[2].Value = BitConverter.ToUInt32(e.Data, 0);
activeDGVIndex++;
break;
case ("string"):
List<byte> bytes = new List<byte>();
for (int i = 0; i < e.Data.Length; i++)
{
if (e.Data[i] != 0)
bytes.Add(e.Data[i]);
}
bytes.Reverse();
DGV_Device.Rows[activeDGVIndex].Cells[2].Value = System.Text.Encoding.UTF8.GetString(bytes.ToArray());
activeDGVIndex++;
break;
default:
MessageBox.Show("Wrong data type set for entry " + entries[activeEntryIndex].name);
activeDGVIndex++;
break;
}
if (activeDGVIndex >= DGV_Device.RowCount)
activeDGVIndex = 0;
//MessageBox.Show("Получен ответ от устройства: " + dataCleaned, "Успех", MessageBoxButtons.OK);
port.DiscardInBuffer();
}
catch (Exception err) { MessageBox.Show(err.Message, "Publish response error"); }
}
if (activeDGVIndex >= DGV_Device.Rows.Count)
activeDGVIndex = 0;
isAwaitingResponse = false;
}
private void Button_StartStop_Click(object sender, EventArgs e)
{
isPolling = !isPolling;
Button_StartStop.Text = (isPolling ? "Стоп" : "Старт");
}
}
}