using Modbus.Device; using Modbus.Extensions.Enron; using LB_VisionProcesses.Communicators; using LB_VisionProcesses.Processes; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using System.Collections.Concurrent; using System.Diagnostics; using System.Text; using LB_VisionProcesses.Communicators.Modbus; namespace LB_VisionProcesses.Processes { [Serializable] public class ModbusTCPMasterTool : BaseProcess { private ModbusSerialMaster Master = null; string CommunicatorName { get { return Params.Inputs["通讯口名"]?.ToString(); } } public ModbusTCPMasterTool() { strProcessName = "MoudbusTCP主站工具"; strProcessClass = "LB_VisionProcesses.Processes.ModbusTCPMasterTool"; // 连接参数 Params.Inputs.Add("通讯口名", ""); Params.Inputs.Add("首地址", 100); Params.Inputs.Add("寄存器个数", 300); // 读写参数 Params.Inputs.Add("通讯类型", ModbusType.Write); Params.Inputs.Add("功能码", ModbusFunctionCode.HoldingRegisters); Params.Inputs.Add("设备地址", 1); Params.Inputs.Add("寄存器地址", 100); Params.Inputs.Add("读写寄存器个数", 1); Params.Inputs.Add("通讯消息", ""); Params.Outputs.Add("收到消息", ""); } bool ModbusRead(ModbusFunctionCode FunctionCode, byte slaveAddress, ushort startAddress, ushort numberOfPoints, out ushort[] msg) { if (Master == null) { msg = new ushort[] { }; return false; } try { switch (FunctionCode) { case ModbusFunctionCode.Coils: bool[] Coils = Master.ReadCoils(slaveAddress, startAddress, numberOfPoints); msg = new ushort[Coils.Length]; for (int i = 0; i < Coils.Length; i++) msg[i] = (ushort)(Coils[i] ? 1 : 0); break; case ModbusFunctionCode.DiscreteInputs: bool[] Inputs = Master.ReadInputs(slaveAddress, startAddress, numberOfPoints); msg = new ushort[Inputs.Length]; for (int i = 0; i < Inputs.Length; i++) msg[i] = (ushort)(Inputs[i] ? 1 : 0); break; case ModbusFunctionCode.HoldingRegisters: ushort[] HoldingRegisters = Master.ReadHoldingRegisters(slaveAddress, startAddress, numberOfPoints); msg = HoldingRegisters; break; case ModbusFunctionCode.InputRegisters: ushort[] InputRegisters = Master.ReadInputRegisters(slaveAddress, startAddress, numberOfPoints); msg = InputRegisters; break; default: msg = new ushort[] { }; return false; } return true; } catch { msg = new ushort[] { }; return false; } } bool ModbusWrite(ModbusFunctionCode FunctionCode, byte slaveAddress, ushort startAddress, ushort numberOfPoints, string msg) { if (Master == null) return false; try { // 分割字符串并转换为 ushort 数组 ushort[] data = msg.Split(' ', StringSplitOptions.RemoveEmptyEntries) .Select(hex => Convert.ToUInt16(hex)) .ToArray(); // (0x00 = false, 非0 = true) bool[] result = msg.Split(' ', StringSplitOptions.RemoveEmptyEntries) .Select(hex => Convert.ToByte(hex, 16) != 0) .ToArray(); switch (FunctionCode) { case ModbusFunctionCode.Coils: Master.WriteMultipleCoils(slaveAddress, startAddress, result); break; case ModbusFunctionCode.DiscreteInputs: case ModbusFunctionCode.InputRegisters: case ModbusFunctionCode.HoldingRegisters: Master.WriteMultipleRegisters(slaveAddress, startAddress, data); break; default: return false; } return true; } catch { return false; } } /// /// 加载算法 /// /// 完整路径带.json /// public override bool Load(string fullPath = null) { try { if (string.IsNullOrEmpty(fullPath)) return false; if (!fullPath.Contains(".json")) { Debug.WriteLine("文件路径不完整"); return false; } if (string.IsNullOrEmpty(fullPath) || fullPath.Trim() == "") { Debug.WriteLine("文件路径不完整"); return false; } // 获取不带文件名的目录路径 string directoryPath = Path.GetDirectoryName(fullPath); strProcessName = Path.GetFileNameWithoutExtension(fullPath); if (!File.Exists(fullPath)) { Debug.WriteLine("文件不存在创建空文件"); Save(directoryPath); return true; } string strJson = string.Empty; using (StreamReader streamReader = new StreamReader(fullPath, Encoding.UTF8)) { strJson = streamReader.ReadToEnd(); streamReader.Close(); } Params = JsonConvert.DeserializeObject(strJson); if (Params == null) return false; Params.FixDeserializedData(); return true; } catch { return false; } } /// /// 保存算法 /// /// 不带.json /// public override bool Save(string filePath = null) { try { if (string.IsNullOrEmpty(filePath) || filePath.Trim() == "") { Debug.WriteLine("文件路径不完整"); return false; } string strJson = string.Empty; var settings = new JsonSerializerSettings { Formatting = Newtonsoft.Json.Formatting.Indented, // 自定义缩进(4空格) ContractResolver = new DefaultContractResolver { NamingStrategy = new CamelCaseNamingStrategy() } }; strJson = JsonConvert.SerializeObject(Params, settings); Params = JsonConvert.DeserializeObject(strJson); if (Params == null) return false; //判断文件夹是否存在,防呆输入为文件名称 if (!Directory.Exists(filePath)) { try { Directory.CreateDirectory(filePath); } catch (Exception) { } } File.WriteAllText(filePath + "//" + strProcessName + ".json", strJson, Encoding.UTF8); return true; } catch { return false; } } public override bool Run() { try { InitRunParams(); Params.Outputs["收到消息"] = ""; string CommunicatorName = Params.Inputs["通讯口名"].ToString(); foreach (var port in IProcess.lstCommunicators) { if (port.CommunicatorName != CommunicatorName || !(port is ModbusRTUMaster RTU)) break; if (!RTU.bConnected) { Msg = $"通讯口[{CommunicatorName}]未连接"; Result = false; return Result; } // 断开已有连接 if (Master != null) Master.Dispose(); Master = ModbusSerialMaster.CreateRtu(RTU.SerialPort); Master.Transport.WriteTimeout = 2000; Master.Transport.ReadTimeout = 2000; Master.Transport.WaitToRetryMilliseconds = 500; Master.Transport.Retries = 3; } if (!Enum.TryParse(Params.Inputs["功能码"].ToString(), out ModbusFunctionCode FunctionCode)) { Msg = $"通讯口[{CommunicatorName}]功能码类型不正确,值为:{Params.Inputs["功能码"].ToString()}"; Result = false; return Result; } if (!Enum.TryParse(Params.Inputs["通讯类型"].ToString(), out ModbusType ModbusType)) { Msg = $"通讯口[{CommunicatorName}]通讯类型类型不正确,值为:{Params.Inputs["通讯类型"].ToString()}"; Result = false; return Result; } string WriteMsg = Params.Inputs["通讯消息"].ToString(); string ShouldReadMsg = Params.Inputs["通讯消息"].ToString(); byte slaveAddress = Convert.ToByte(Params.Inputs["设备地址"].ToString()); ushort startAddress = Convert.ToUInt16(Params.Inputs["寄存器地址"].ToString()); ushort numberOfPoints = Convert.ToUInt16(Params.Inputs["读写寄存器个数"].ToString()); switch (ModbusType) { case ModbusType.Read: ModbusRead(FunctionCode, slaveAddress, startAddress, numberOfPoints, out ushort[] readResult); // 直接输出读到的结果 if (string.IsNullOrEmpty(ShouldReadMsg) || ShouldReadMsg == "") Params.Outputs["收到消息"] = string.Join(" ", readResult); else { // 分割字符串并转换为 ushort 数组 ushort[] data = ShouldReadMsg.Split(' ', StringSplitOptions.RemoveEmptyEntries) .Select(hex => Convert.ToUInt16(hex)) .ToArray(); if (data != readResult) { Msg = $"通讯口[{CommunicatorName}]读到的结果错误,实际为{readResult}"; Result = false; } } break; case ModbusType.Write: ModbusWrite(FunctionCode, slaveAddress, startAddress, numberOfPoints, WriteMsg); break; default: Msg = $"通讯类型只支持读和写!"; Result = false; break; } } catch (Exception ex) { Params.Outputs.Add("收到消息", ""); Msg = $"通讯异常,原因是:{ex.Message}"; Result = false; } return Result; } public override void InitRunParams() { Result = true; Msg = ""; if (Record != null) Record.Dispose(); } public override void Dispose() { if (Master != null) Master.Dispose(); return; } public override object Clone() { return MemberwiseClone(); } } }