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 ModbusRTUMasterTool : BaseProcess
{
private ModbusSerialMaster Master = null;
string CommunicatorName { get { return Params.Inputs["通讯口名"]?.ToString(); } }
int ReadTimeout { get { return Convert.ToInt32(Params.Inputs["读超时时间"]?.ToString()); } }
int WriteTimeout { get { return Convert.ToInt32(Params.Inputs["写超时时间"]?.ToString()); } }
int Retries { get { return Convert.ToInt32(Params.Inputs["失败重测次数"]?.ToString()); } }
int WaitToRetryMilliseconds { get { return Convert.ToInt32(Params.Inputs["失败重测延时"]?.ToString()); } }
public ModbusRTUMasterTool()
{
strProcessName = "MoudbusRTU主站工具";
strProcessClass = "LB_VisionProcesses.Processes.ModbusRTUMasterTool";
// 初始化参数
Params.Inputs.Add("读超时时间", 500);
Params.Inputs.Add("写超时时间", 500);
Params.Inputs.Add("失败重测次数", 2);
Params.Inputs.Add("失败重测延时", 15);
// 连接参数
Params.Inputs.Add("通讯口名", "");
Params.Inputs.Add("首地址", 100);
Params.Inputs.Add("寄存器个数", 300);
// 读写参数
Params.Inputs.Add("通讯类型", ModbusType.Write.ToString());
Params.Inputs.Add("功能码", ModbusFunctionCode.HoldingRegisters.ToString());
Params.Inputs.Add("设备地址", 1);
Params.Inputs.Add("寄存器地址", 0);
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 (Exception ex) { Msg = $"通讯异常,原因是:{ex.Message}"; msg = new ushort[] { }; return false; }
}
bool ModbusWrite(ModbusFunctionCode FunctionCode, byte slaveAddress, ushort startAddress, ushort numberOfPoints, string msg)
{
if (Master == null)
return false;
if (msg.Length == 0)
return true;
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);
//Master.WriteMultipleCoilsAsync(slaveAddress, startAddress, result);
break;
case ModbusFunctionCode.DiscreteInputs:
case ModbusFunctionCode.InputRegisters:
case ModbusFunctionCode.HoldingRegisters:
Master.WriteMultipleRegisters(slaveAddress, startAddress, data);
//Master.WriteMultipleRegistersAsync(slaveAddress, startAddress, data);
break;
default:
return false;
}
return true;
}
catch (Exception ex) { Msg = $"通讯异常,原因是:{ex.Message}"; 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 = this.CommunicatorName;
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 = WriteTimeout;
Master.Transport.ReadTimeout = ReadTimeout;
Master.Transport.WaitToRetryMilliseconds = WaitToRetryMilliseconds;
Master.Transport.Retries = Retries;
if (Master == null)
{
Msg = $"通讯口[{CommunicatorName}]不存在";
Result = false;
return Result;
}
}
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:
Result = 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:
Result = 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();
Master = null;
return;
}
public override object Clone()
{
return MemberwiseClone();
}
}
}