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; }
|
}
|
|
/// <summary>
|
/// 加载算法
|
/// </summary>
|
/// <param name="fullPath">完整路径带.json</param>
|
/// <returns></returns>
|
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<ProcessParams>(strJson);
|
if (Params == null)
|
return false;
|
|
Params.FixDeserializedData();
|
|
return true;
|
}
|
catch { return false; }
|
}
|
|
/// <summary>
|
/// 保存算法
|
/// </summary>
|
/// <param name="filePath">不带.json</param>
|
/// <returns></returns>
|
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<ProcessParams>(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();
|
}
|
}
|
}
|