Files
gidrolock-configurator/Main.cs
nikzori 5ca452031a update
2024-12-16 11:35:20 +03:00

499 lines
19 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;
using System.Text.RegularExpressions;
using System.Net.Sockets;
using System.IO;
using Newtonsoft.Json;
namespace Gidrolock_Modbus_Scanner
{
public partial class App : Form
{
public static int[] BaudRate = new int[]
{
110, 300, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 57600, 76800, 115200, 230300, 460800, 921600
};
public static int[] DataBits = new int[] { 7, 8 };
Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
byte[] message = new byte[255];
public bool isAwaitingResponse = false;
public short[] res = new short[12];
SerialPort port = Modbus.port;
public int expectedLength = 0;
Datasheet datasheet;
public SelectedPath selectedPath = SelectedPath.Folder;
public static Device device; // Deserialized .json object
string path = String.Empty; // Path to selected file/folder
string defaultPath = Application.StartupPath + "\\Configs"; // Default folder path
OpenFileDialog ofd = new OpenFileDialog();
FolderBrowserDialog fbd = new FolderBrowserDialog();
Dictionary<CheckEntry, List<Device>> juju = new Dictionary<CheckEntry, List<Device>>(); // dictionary for device identification
string[] configs;
public byte[] latestMessage;
DateTime dateTime;
#region Initialization
public App()
{
InitializeComponent();
Modbus.Init();
Modbus.ResponseReceived += OnResponseReceived;
ofd.InitialDirectory = Application.StartupPath;
ofd.Filter = "JSON files (*.json)|*.json";
ofd.FilterIndex = 2;
ofd.RestoreDirectory = true;
this.UpDown_ModbusID.Value = 30;
TextBox_Log.Text = "Приложение готово к работе.";
CBox_Function.Items.Add("01 Read Coil");
CBox_Function.Items.Add("02 Read Discrete Input");
CBox_Function.Items.Add("03 Read Holding Register");
CBox_Function.Items.Add("04 Read Input Register");
CBox_Function.Items.Add("05 Write Single Coil");
CBox_Function.Items.Add("06 Write Single Register");
CBox_Function.Items.Add("0F Write Multiple Coils");
CBox_Function.Items.Add("10 Write Multiple Registers");
CBox_Function.SelectedItem = CBox_Function.Items[0];
CBox_BaudRate.Items.Add("110");
CBox_BaudRate.Items.Add("300");
CBox_BaudRate.Items.Add("1200");
CBox_BaudRate.Items.Add("2400");
CBox_BaudRate.Items.Add("4800");
CBox_BaudRate.Items.Add("9600");
CBox_BaudRate.Items.Add("14400");
CBox_BaudRate.Items.Add("19200");
CBox_BaudRate.Items.Add("28800");
CBox_BaudRate.Items.Add("38400");
CBox_BaudRate.Items.Add("57600");
CBox_BaudRate.Items.Add("76800");
CBox_BaudRate.Items.Add("115200");
CBox_BaudRate.Items.Add("230300");
CBox_BaudRate.Items.Add("460800");
CBox_BaudRate.Items.Add("921600");
CBox_BaudRate.SelectedIndex = 5;
CBox_DataBits.Items.Add("7");
CBox_DataBits.Items.Add("8");
CBox_DataBits.SelectedIndex = 1;
CBox_StopBits.Items.Add("Нет");
CBox_StopBits.Items.Add("1");
CBox_StopBits.Items.Add("1.5");
CBox_StopBits.Items.Add("2");
CBox_StopBits.SelectedIndex = 1;
CBox_Parity.Items.Add("Нет");
CBox_Parity.Items.Add("Четн.");
CBox_Parity.Items.Add("Нечетн.");
CBox_Parity.SelectedIndex = 0;
UpDown_RegAddress.Minimum = 0;
UpDown_RegAddress.Maximum = 65535;
UpDown_Value.Minimum = 0;
UpDown_Value.Maximum = 65535; // 2^16
Radio_SerialPort.Checked = true;
GBox_Ethernet.Enabled = false;
TBox_IP.Text = "192.168.3.7";
TBox_Port.Text = "8887";
TBox_Timeout.Text = "3";
if (Directory.GetDirectories(Application.StartupPath).Contains(Application.StartupPath + "\\Configs") == false)
{
Task.Delay(1500).ContinueWith(t =>
{
MessageBox.Show("Приложение не нашло стандартную папку для конфигураций. Была создана папка 'Configs' в папке с приложением.");
Directory.CreateDirectory(Application.StartupPath + "\\Configs");
Console.WriteLine("New initial directory for OpenFile: " + ofd.InitialDirectory);
});
}
ofd.InitialDirectory = Application.StartupPath + "\\Configs";
path = defaultPath;
UpdatePathLabel();
}
void UpdatePathLabel()
{
Label_ConfPath.Text = path;
}
void App_FormClosed(object sender, FormClosedEventArgs e)
{
port.Close();
if (!port.IsOpen)
Application.Exit();
}
void Form1_Load(object sender, EventArgs e)
{
CBox_Ports.Items.AddRange(SerialPort.GetPortNames());
if (CBox_Ports.Items.Count > 0)
CBox_Ports.SelectedIndex = 0;
Init();
}
void Init()
{
if (UpDown_ModbusID.Value == 0)
Button_Connect.Text = "Найти адрес";
else Button_Connect.Text = "Подключиться";
}
#endregion
// Send a custom message
async Task SendMessageAsync(FunctionCode functionCode, ushort address, ushort length)
{
if (CBox_Ports.Text == "")
MessageBox.Show("Необходимо выбрать COM порт.", "Ошибка", MessageBoxButtons.OK);
if (UpDown_ModbusID.Value == 0)
MessageBox.Show("Глобальное вещание пока не поддерживается");
/* - Port Setup - */
if (port.IsOpen)
port.Close();
port.Handshake = Handshake.None;
port.PortName = CBox_Ports.Text;
port.BaudRate = BaudRate[CBox_BaudRate.SelectedIndex];
port.Parity = Parity.None;
port.DataBits = DataBits[CBox_DataBits.SelectedIndex];
port.StopBits = (StopBits)CBox_StopBits.SelectedIndex;
port.ReadTimeout = 3000;
port.WriteTimeout = 3000;
message = new byte[255];
port.Open();
/* - Reading from Registers - */
if (CBox_Function.SelectedIndex < 4)
{
try
{
var send = await Modbus.ReadRegAsync(port, (byte)UpDown_ModbusID.Value, functionCode, address, length);
isAwaitingResponse = true;
await Task.Delay(port.ReadTimeout).ContinueWith(_ =>
{
if (isAwaitingResponse)
{
MessageBox.Show("Истекло время ожидания ответа.", "Ошибка");
port.Close();
}
});
}
catch (Exception err)
{
port.Close();
MessageBox.Show(err.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
/* - Writing to Registers - */
else
{
try
{
if (CBox_Function.SelectedIndex < 6) // Single Registers
{
byte[] request = new byte[8];
Modbus.BuildMessage((byte)UpDown_ModbusID.Value, (byte)(1 + functionCode), address, length, ref request);
string messageParsed = Modbus.ByteArrayToString(request);
var send = await Modbus.WriteSingle(port, functionCode, (byte)UpDown_ModbusID.Value, address, (ushort)UpDown_Value.Value);
}
else // Multiple Registers
{
byte[] request = new byte[(int)UpDown_RegLength.Value * 2 + 6];
}
}
catch (Exception err)
{
port.Close();
MessageBox.Show(err.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
private async void ButtonConnect_Click(object sender, EventArgs e)
{
progressBar1.Value = 0;
if (path == String.Empty)
{
MessageBox.Show("Выберите конфигурацию для подключения и опроса устройства.");
return;
}
if (CBox_Ports.SelectedItem.ToString() == "COM1")
{
DialogResult res = MessageBox.Show("Выбран серийный порт COM1, который обычно является портом PS/2 или RS-232, не подключенным к Modbus устройству. Продолжить?", "Внимание", MessageBoxButtons.OKCancel);
if (res == DialogResult.Cancel)
return;
}
/* - Port Setup - */
if (port.IsOpen)
port.Close();
port.Handshake = Handshake.None;
port.PortName = CBox_Ports.Text;
port.BaudRate = BaudRate[CBox_BaudRate.SelectedIndex];
port.Parity = Parity.None;
port.DataBits = DataBits[CBox_DataBits.SelectedIndex];
port.StopBits = (StopBits)CBox_StopBits.SelectedIndex;
port.ReadTimeout = 3000;
port.WriteTimeout = 3000;
message = new byte[255];
port.Open();
/* - Checking - */
if (selectedPath == SelectedPath.File)
{
AddLog("Попытка подключиться к устройству " + device.name);
try
{
datasheet = new Datasheet((byte)UpDown_ModbusID.Value);
datasheet.Show();
}
catch (Exception err)
{
MessageBox.Show(err.Message);
}
var fileContent = string.Empty;
var filePath = string.Empty;
//Read the contents of the file into a stream
var fileStream = ofd.OpenFile();
using (StreamReader reader = new StreamReader(fileStream))
{
fileContent = reader.ReadToEnd();
}
progressBar1.Value = 100;
try
{
device = JsonConvert.DeserializeObject<Device>(fileContent);
Label_ConfigTip.Text = device.name;
}
catch (Exception err)
{
MessageBox.Show(err.Message);
}
}
else
{
string[] _configs = Directory.GetFiles(path, "*.json");
if (configs != _configs)
{
// something changed in the config folder, or we haven't gone through configs,
// remake the dictionary
configs = _configs;
juju = new Dictionary<CheckEntry, List<Device>>();
var fileContent = string.Empty;
FileStream fileStream;
Device _device;
foreach (string path in configs)
{
fileStream = File.OpenRead(path);
using (StreamReader reader = new StreamReader(fileStream))
fileContent = reader.ReadToEnd();
// get device object from .json
_device = JsonConvert.DeserializeObject<Device>(fileContent);
// compare device object to key of each dictionary;
// add to that dictionary if device's check entry registers match the key
foreach (CheckEntry ce in juju.Keys)
{
if (_device.checkEntry.address == ce.address && _device.checkEntry.length == ce.length && _device.checkEntry.dataType == ce.dataType)
{
juju[ce].Add(_device);
break;
}
}
}
}
// all configs are sorted out, we can poll for each checkEntry
foreach (CheckEntry ce in juju.Keys)
{
byte[] response;
// send read request to device,
// check for error codes or timeouts
// if we get normal response, go through
}
// with dictionaries arranged, go through every key, discarding those that return error
// if device returns a coherent reply, go through expected values associated with the key
}
/*
if (Radio_SerialPort.Checked)
await SendMessageAsync(FunctionCode.InputRegister, 200, 6);
//else EthernetParse();
*/
}
async void EthernetParse()
{
string ipText = TBox_IP.Text;
string portText = TBox_Port.Text;
Regex ip = new Regex(@"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b");
Regex port = new Regex(@"\d");
if (!ip.IsMatch(ipText))
MessageBox.Show("Неправильный формат IP-адреса.");
else if (!port.IsMatch(portText))
MessageBox.Show("Неправильный формат TCP-порта.");
else
{
int portParsed = Int32.Parse(portText);
await socket.ConnectAsync(ipText, portParsed);
byte[] data = new byte[8];
Modbus.BuildMessage(0x1E, 0x03, 128, 1, ref data);
AddLog("Sending to " + ipText + ":" + portText + ":" + Modbus.ByteArrayToString(data));
// set up an event listener to receive the response
await SocketDataTransfer(data);
}
}
void CBox_Ports_Click(object sender, EventArgs e)
{
CBox_Ports.Items.Clear();
CBox_Ports.Items.AddRange(SerialPort.GetPortNames());
}
void OnResponseReceived(object sender, ModbusResponseEventArgs e)
{
isAwaitingResponse = false;
TextBox_Log.Invoke((MethodInvoker)delegate { AddLog("Получен ответ: " + Modbus.ByteArrayToString(e.Message)); });
TextBox_Log.Invoke((MethodInvoker)delegate { AddLog("UTF-8: " + e.Text); });
}
void AddLog(string message)
{
dateTime = DateTime.Now;
TextBox_Log.AppendText(Environment.NewLine + "[" + dateTime.Hour + ":" + dateTime.Minute + ":" + dateTime.Second + "] " + message);
}
private async void Button_SendCommand_Click(object sender, EventArgs e)
{
FunctionCode functionCode = (FunctionCode)CBox_Function.SelectedIndex + 1;
ushort address = (ushort)UpDown_RegAddress.Value;
ushort length = (ushort)UpDown_RegLength.Value;
await SendMessageAsync(functionCode, address, length);
}
private void OnSelectedFunctionChanged(object sender, EventArgs e)
{
if (CBox_Function.SelectedIndex < 4)
UpDown_Value.Enabled = false;
else
{
if (CBox_Function.SelectedIndex == 4 || CBox_Function.SelectedIndex == 6)
UpDown_Value.Maximum = 1;
else UpDown_Value.Maximum = 65535;
UpDown_Value.Enabled = true;
}
}
private void Radio_SerialPort_CheckedChanged(object sender, EventArgs e)
{
if (Radio_SerialPort.Checked)
GBox_Serial.Enabled = true;
else GBox_Serial.Enabled = false;
}
private void Radio_Ethernet_CheckedChanged(object sender, EventArgs e)
{
if (Radio_Ethernet.Checked)
GBox_Ethernet.Enabled = true;
else GBox_Ethernet.Enabled = false;
}
private async Task<bool> SocketDataTransfer(byte[] data)
{
await Task.Run(() => { socket.Send(data); });
byte[] res = new byte[64];
await Task.Run(() =>
{
while (true)
{
int bytesReceived = socket.Receive(res);
if (bytesReceived == 0)
break;
string resParsed = "";
Modbus.ParseResponse(res, ref resParsed);
Console.Out.WriteLine("Received data on TCP socket: " + resParsed);
AddLog("Response from TCP Server: " + resParsed);
}
});
return true;
}
private void LoadConfig(object sender, EventArgs e)
{
if (ofd.ShowDialog() == DialogResult.OK)
{
//Get the path of specified file
path = ofd.FileName;
Label_ConfPath.Text = ofd.FileName;
selectedPath = SelectedPath.File;
}
UpdatePathLabel();
}
private void LoadFolder(object sender, EventArgs e)
{
fbd.RootFolder = Environment.SpecialFolder.MyComputer;
if (fbd.ShowDialog() == DialogResult.OK)
{
path = fbd.SelectedPath;
Label_ConfPath.Text = fbd.SelectedPath;
selectedPath = SelectedPath.Folder;
}
UpdatePathLabel();
}
private void CBox_Ports_Click(object sender, MouseEventArgs e)
{
}
}
}
public enum FunctionCode { ReadCoil = 1, ReadDiscrete = 2, ReadHolding = 3, ReadInput = 4, WriteCoil = 5, WriteRegister = 6, WriteMultCoils = 15, WriteMultRegisters = 16 };
public enum SelectedPath { File, Folder };