Files
gidrolock-configurator/Main.cs

382 lines
15 KiB
C#
Raw Permalink Normal View History

2024-11-05 10:31:38 +03:00
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;
namespace Gidrolock_Modbus_Scanner
{
public partial class App : Form
{
2024-11-05 10:31:38 +03:00
public bool isAwaitingResponse = false;
public short[] res = new short[12];
SerialPort port = Modbus.port;
2024-11-05 16:57:05 +03:00
public int expectedLength = 0;
2025-01-24 11:46:36 +03:00
ModbusResponseEventArgs latestMessage;
2024-12-05 15:57:07 +03:00
2025-01-14 11:31:23 +03:00
public Dictionary<string, string> models = new Dictionary<string, string>();
2025-01-21 14:38:59 +03:00
2025-01-24 11:46:36 +03:00
DateTime dateTime;
2025-01-21 14:38:59 +03:00
Datasheet datasheet;
#region Initialization
2024-11-05 10:31:38 +03:00
public App()
{
InitializeComponent();
Modbus.Init();
Modbus.ResponseReceived += OnResponseReceived;
this.upDownModbusID.Value = 0;
2024-11-05 16:57:05 +03:00
TextBox_Log.Text = "Приложение готово к работе.";
2025-01-24 17:54:04 +03:00
cBoxSpeed.Items.Add("1200");
cBoxSpeed.Items.Add("2400");
cBoxSpeed.Items.Add("4800");
cBoxSpeed.Items.Add("9600");
cBoxSpeed.Items.Add("14400");
cBoxSpeed.Items.Add("19200");
cBoxSpeed.Items.Add("38400");
cBoxSpeed.Items.Add("57600");
cBoxSpeed.Items.Add("115200");
cBoxSpeed.SelectedIndex = 3;
upDownModbusID.Value = 30;
upDownModbusID.Minimum = 1;
upDownModbusID.Maximum = 247;
2025-01-14 11:31:23 +03:00
2025-02-14 10:22:15 +03:00
models.Add("Standard Wi-Fi", "STW485");
models.Add("Standard Radio", "STR485");
2025-01-24 11:46:36 +03:00
models.Add("Premium Plus", "PRPLS1");
2025-01-14 11:31:23 +03:00
models.Add("Inteli", "INTELI");
models.Add("Premium", "BUP485");
/* - Version Check - */
System.Reflection.Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
System.Diagnostics.FileVersionInfo fvi = System.Diagnostics.FileVersionInfo.GetVersionInfo(assembly.Location);
string version = fvi.FileVersion;
Console.WriteLine("Version: " + version);
2025-01-21 14:38:59 +03:00
Modbus.ResponseReceived += (sndr, msg) =>
{
2025-01-24 11:46:36 +03:00
latestMessage = msg;
isAwaitingResponse = false;
2025-01-21 14:38:59 +03:00
};
}
2024-11-05 10:31:38 +03:00
void App_FormClosed(object sender, FormClosedEventArgs e)
{
port.Close();
}
void Form1_Load(object sender, EventArgs e)
{
2025-01-24 17:54:04 +03:00
cBoxPorts.Items.AddRange(SerialPort.GetPortNames());
if (cBoxPorts.Items.Count > 0)
cBoxPorts.SelectedIndex = 0;
2024-11-05 10:31:38 +03:00
}
#endregion
2024-11-05 10:31:38 +03:00
2024-11-05 16:57:05 +03:00
private async void ButtonConnect_Click(object sender, EventArgs e)
{
2025-01-24 17:54:04 +03:00
if (cBoxPorts.SelectedItem.ToString() == "COM1")
2024-12-05 15:57:07 +03:00
{
DialogResult res = MessageBox.Show("Выбран серийный порт COM1, который обычно является портом PS/2 или RS-232, не подключенным к Modbus устройству. Продолжить?", "Внимание", MessageBoxButtons.OKCancel);
if (res == DialogResult.Cancel)
return;
}
if (upDownModbusID.Value == 0)
2024-12-19 10:41:52 +03:00
{
2025-01-14 11:31:23 +03:00
DialogResult res = MessageBox.Show("Указан Modbus ID 0 — глобальное вещание. Устройства не смогут отвечать на сообщения. Продолжить?", "Внимание", MessageBoxButtons.OKCancel);
2024-12-19 10:41:52 +03:00
if (res == DialogResult.Cancel)
return;
}
2025-01-14 11:31:23 +03:00
try
{
2025-01-14 11:31:23 +03:00
/* - Port Setup - */
if (port.IsOpen)
port.Close();
2025-01-14 11:31:23 +03:00
port.Handshake = Handshake.None;
2025-01-24 17:54:04 +03:00
port.PortName = cBoxPorts.Text;
port.BaudRate = Int32.Parse(cBoxSpeed.Items[cBoxSpeed.SelectedIndex].ToString());
2025-01-14 11:31:23 +03:00
port.Parity = Parity.None;
port.DataBits = 8;
port.StopBits = StopBits.One;
2024-12-16 14:31:14 +03:00
2025-01-14 11:31:23 +03:00
port.ReadTimeout = 3000;
port.WriteTimeout = 3000;
port.ReadBufferSize = 8192;
2025-01-14 11:31:23 +03:00
port.Open();
// send read request for 6 registers starting from 200
// if we've got error or a timeout, show appropriate errors
// else parse response to unicode and go through every .json
// if matching model is found, instantiate device window
2025-01-24 11:46:36 +03:00
await Task.Run(async () =>
2025-01-14 11:31:23 +03:00
{
2025-01-24 11:46:36 +03:00
// send message
latestMessage = null;
isAwaitingResponse = true;
var send = Modbus.ReadRegAsync((byte)upDownModbusID.Value, FunctionCode.ReadInput, 200, 6);
2025-01-24 11:46:36 +03:00
await Task.Delay(2000).ContinueWith(_ =>
{
if (isAwaitingResponse)
{
isAwaitingResponse = false;
MessageBox.Show("Истекло время ожидания ответа от устройства.");
}
return;
});
while (isAwaitingResponse) { continue; }
if (latestMessage.Status == ModbusStatus.Error)
return;
2025-01-14 11:31:23 +03:00
// confirm the model
2025-01-24 11:46:36 +03:00
string response = ByteArrayToUnicode(latestMessage.Data);
int index = 0;
string match = "";
string model = "";
foreach (string key in models.Keys)
2024-12-16 14:31:14 +03:00
{
if (models[key] == response)
2024-12-16 14:31:14 +03:00
{
match = models[key];
model = key;
break;
2024-12-16 14:31:14 +03:00
}
index++;
}
2024-12-18 16:49:58 +03:00
if (match == "") // if no matches found
MessageBox.Show("Подключенное устройство не соответствует ни одной из поддерживаемых моделей.");
2025-01-21 14:38:59 +03:00
else // match found
2024-12-16 14:33:13 +03:00
{
// instantiate panel
AddLog("Открываю панель конфигурации.");
Device device = GetDevice((DeviceType)index);
StartDatasheet(device);
2024-12-16 14:33:13 +03:00
}
2025-01-24 11:46:36 +03:00
});
2025-01-14 11:31:23 +03:00
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
2024-12-05 11:10:53 +03:00
}
2025-01-21 14:38:59 +03:00
void StartDatasheet(Device device)
{
datasheet = new Datasheet((byte)upDownModbusID.Value, device);
2025-01-24 11:46:36 +03:00
Application.Run(datasheet);
2025-01-21 14:38:59 +03:00
}
2024-12-05 11:10:53 +03:00
2024-11-05 10:31:38 +03:00
void CBox_Ports_Click(object sender, EventArgs e)
{
2025-01-24 17:54:04 +03:00
cBoxPorts.Items.Clear();
cBoxPorts.Items.AddRange(SerialPort.GetPortNames());
2024-11-05 10:31:38 +03:00
}
void OnResponseReceived(object sender, ModbusResponseEventArgs e)
2024-11-05 10:31:38 +03:00
{
isAwaitingResponse = false;
AddLog("Получен ответ: " + Modbus.ByteArrayToString(e.Message));
2025-01-14 11:31:23 +03:00
switch (e.Status)
{
case (ModbusStatus.ReadSuccess):
2024-12-18 14:28:06 +03:00
string values = "";
if (e.Message[1] <= 0x02)
{
for (int i = 0; i < e.Data.Length; i++)
{
values += Convert.ToString(e.Data[i], 2).PadLeft(8, '0') + " ";
}
AddLog("Bin: " + values);
}
if (e.Message[1] == 0x03 || e.Message[1] == 0x04)
{
2024-12-18 14:28:06 +03:00
for (int i = 0; i < e.Data.Length; i += 2)
{
values += ((e.Data[i] << 8) + e.Data[i + 1]).ToString() + "; ";
}
AddLog("Dec: " + values);
AddLog("Unicode: " + ByteArrayToUnicode(e.Data));
2025-01-14 11:31:23 +03:00
}
break;
case (ModbusStatus.WriteSuccess):
AddLog("Write success;");
break;
case (ModbusStatus.Error):
string errorDesc;
switch (e.Message[2])
{
case (0x01):
errorDesc = "01 - Illegal Function";
break;
case (0x02):
errorDesc = "02 - Illegal Data Address";
break;
case (0x03):
errorDesc = "03 - Illegal Data Value";
break;
case (0x04):
errorDesc = "04 - Slave Device Failure";
break;
case (0x05):
errorDesc = "05 - Acknowledge";
break;
case (0x06):
errorDesc = "06 - Slave Device Busy";
break;
case (0x07):
errorDesc = "07 - Negative Acknowledge";
break;
case (0x08):
errorDesc = "08 - Memory Parity Error";
break;
case (0x0A):
errorDesc = "10 - Gateway Path Unavailable";
break;
case (0x0B):
errorDesc = "11 - Gateway Target Device Failed to Respond";
break;
default:
errorDesc = "Unknown error code";
break;
}
AddLog("Error code: " + errorDesc);
break;
}
2024-11-05 16:57:05 +03:00
}
void AddLog(string message)
{
2024-12-16 11:35:20 +03:00
dateTime = DateTime.Now;
TextBox_Log.Invoke((MethodInvoker)delegate { TextBox_Log.AppendText(Environment.NewLine + "[" + dateTime.Hour.ToString().PadLeft(2, '0') + ":" + dateTime.Minute.ToString().PadLeft(2, '0') + ":" + dateTime.Second.ToString().PadLeft(2, '0') + "] " + message); });
2024-11-05 16:57:05 +03:00
}
2025-01-14 11:31:23 +03:00
public static string ByteArrayToUnicode(byte[] input)
{
// stupid fucking WinForm textbox breaks from null symbols
// stupid fucking Encoding class does byte-by-byte conversion
2025-01-14 11:31:23 +03:00
List<char> result = new List<char>(input.Length / 2);
byte[] flip = input;
2025-01-24 11:46:36 +03:00
//Array.Reverse(flip); // stupid fucking BitConverter is little-endian and spits out chinese nonsense otherwise
for (int i = 0; i < flip.Length; i++)
{
2025-01-24 11:46:36 +03:00
if (flip[i] == 0x00)
continue;
else result.Add(Convert.ToChar(flip[i]));
}
2025-01-24 11:46:36 +03:00
//result.Reverse();
return new string(result.ToArray());
}
2025-01-14 11:31:23 +03:00
2025-01-21 14:38:59 +03:00
public Device GetDevice(DeviceType dt)
{
Device d = new Device();
switch (dt)
{
2025-02-14 10:22:15 +03:00
case DeviceType.StandardWifi:
2025-01-24 11:46:36 +03:00
d.name = "Standard Wi-Fi RS485";
2025-01-21 14:38:59 +03:00
d.id = 30;
d.modelName = "STW485";
2025-01-28 09:35:16 +03:00
d.firmware = new Entry(RegisterType.Input, 250, 6);
d.baudRate = new Entry(RegisterType.Holding, 110);
2025-01-21 14:38:59 +03:00
d.valveStatus = new Entry(RegisterType.Coil, 1202);
d.alarmStatus = new Entry(RegisterType.Coil, 1201);
d.hasCleaningMode = true;
d.cleaningMode = new Entry(RegisterType.Coil, 3);
d.hasBattery = false;
d.wiredSensors = 2;
d.hasScenarioSensor = true;
2025-01-24 17:54:04 +03:00
d.sensorAlarm = new Entry(RegisterType.Discrete, 1343, 24);
2025-01-21 14:38:59 +03:00
d.radioStatus = new Entry(RegisterType.Input, 1215, 21);
2025-02-14 10:22:15 +03:00
break;
case DeviceType.StandardRadio:
d.name = "Standard Radio RS485";
d.id = 30;
d.modelName = "STR485";
d.firmware = new Entry(RegisterType.Input, 250, 6);
d.baudRate = new Entry(RegisterType.Holding, 110);
d.valveStatus = new Entry(RegisterType.Coil, 1202);
d.alarmStatus = new Entry(RegisterType.Coil, 1201);
d.hasCleaningMode = true;
d.cleaningMode = new Entry(RegisterType.Coil, 3);
d.hasBattery = false;
d.wiredSensors = 2;
d.hasScenarioSensor = false;
d.sensorAlarm = new Entry(RegisterType.Discrete, 1343, 23);
d.radioStatus = new Entry(RegisterType.Input, 1215, 21);
2025-01-21 14:38:59 +03:00
break;
case DeviceType.Inteli:
d.modelName = "Inteli";
2025-01-28 09:35:16 +03:00
d.id = 26;
d.modelName = "INTELI";
d.baudRate = new Entry(RegisterType.Holding, 129);
2025-01-21 14:38:59 +03:00
break;
case DeviceType.PremiumPlus:
2025-01-24 11:46:36 +03:00
d.name = "Premium Plus Wi-Fi";
d.id = 30;
d.modelName = "PRPLS1";
2025-01-28 09:35:16 +03:00
d.firmware = new Entry(RegisterType.Input, 250, 6);
d.baudRate = new Entry(RegisterType.Holding, 110);
2025-01-24 11:46:36 +03:00
d.valveStatus = new Entry(RegisterType.Coil, 1202);
d.alarmStatus = new Entry(RegisterType.Coil, 1201);
d.hasCleaningMode = true;
d.cleaningMode = new Entry(RegisterType.Coil, 3);
d.hasBattery = true;
d.batteryCharge = new Entry(RegisterType.Input, 1207);
d.wiredSensors = 7;
d.hasScenarioSensor = true;
2025-01-24 17:54:04 +03:00
d.sensorAlarm = new Entry(RegisterType.Discrete, 1343, 29);
d.radioStatus = new Entry(RegisterType.Input, 1215, 21);
2025-01-21 14:38:59 +03:00
break;
case DeviceType.Premium:
d.modelName = "Premium";
d.id = 26;
d.modelName = "BUP485";
d.baudRate = new Entry(RegisterType.Holding, 110);
2025-01-21 14:38:59 +03:00
break;
default:
break;
}
return d;
}
2024-11-05 10:31:38 +03:00
}
}
2024-11-05 10:31:38 +03:00
public enum FunctionCode { ReadCoil = 1, ReadDiscrete = 2, ReadHolding = 3, ReadInput = 4, WriteCoil = 5, WriteRegister = 6, WriteMultCoils = 15, WriteMultRegisters = 16 };
2025-01-21 14:38:59 +03:00
//public enum SelectedPath { File, Folder };
2025-01-24 17:54:04 +03:00
2025-02-14 10:22:15 +03:00
public enum DeviceType { StandardWifi, StandardRadio, PremiumPlus, Inteli, Premium };