2024-05-31 10:14:57 +08:00
|
|
|
|
|
|
|
|
|
using HslCommunication;
|
|
|
|
|
using HslCommunication.Profinet.Melsec;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using System.Xml.Linq;
|
|
|
|
|
using static OpenCvSharp.FileStorage;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-07-13 13:10:27 +08:00
|
|
|
|
public class MelsecPLCTCPDriver
|
2024-05-31 10:14:57 +08:00
|
|
|
|
{
|
|
|
|
|
private MelsecMcNet melsecMc = new MelsecMcNet();
|
|
|
|
|
// private HslCommunication.ModBus.ModbusTcpNet melsecMc = new HslCommunication.ModBus.ModbusTcpNet();
|
|
|
|
|
//HslCommunication.Profinet.Melsec.MelsecMcServer melsecMc = new HslCommunication.Profinet.Melsec.MelsecMcServer();
|
|
|
|
|
|
|
|
|
|
#region PLCBase
|
|
|
|
|
public List<int> Read(string startAddress, int length)
|
|
|
|
|
{
|
|
|
|
|
PLCItem item = new PLCItem();
|
|
|
|
|
item.Address = startAddress;
|
|
|
|
|
item.ItemLength = length;
|
|
|
|
|
|
|
|
|
|
ReadItem(item);
|
|
|
|
|
//List<int> valueList = new List<int>();
|
|
|
|
|
//if (!string.IsNullOrWhiteSpace(item.ItemValue))
|
|
|
|
|
//{
|
|
|
|
|
// valueList = item.ItemValue.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList().ConvertAll(s => int.Parse(s)).ToList();
|
|
|
|
|
//}
|
|
|
|
|
return item.ItemValues;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void ReadItem(PLCItem item)
|
|
|
|
|
{
|
|
|
|
|
// item.PLCOpType = PLCOpType.Read;
|
|
|
|
|
item.ItemValues.Clear();
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
// 读取Int变量
|
|
|
|
|
var result = melsecMc.ReadInt32("D"+item.Address, (ushort)item.ItemLength);
|
|
|
|
|
if (result.IsSuccess)
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < result.Content.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
item.ItemValues.Add(result.Content[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// LogAsync(DateTime.Now, LogLevel.Error, $"{Name}未读取到数据:" + "地址:" + item.Address + ";长度:" + item.ItemLength + ";提示:" + result.Message);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
// LogAsync(DateTime.Now, LogLevel.Exception, $"{Name}读取异常:" + ex.GetExceptionMessage());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public int ReadInt(string address)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
// 读取Int变量
|
|
|
|
|
var result = melsecMc.ReadInt32("D"+address);
|
|
|
|
|
if (result.IsSuccess)
|
|
|
|
|
{
|
|
|
|
|
return result.Content;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// LogAsync(DateTime.Now, LogLevel.Error, $"{Name}未读取到数据:" + "地址:" + address + ";提示:" + result.Message);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
// LogAsync(DateTime.Now, LogLevel.Exception, $"{Name}读取异常:" + ex.GetExceptionMessage());
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void WriteItem(PLCItem item, bool waitForReply = true)
|
|
|
|
|
{
|
|
|
|
|
item.PLCOpType = PLCOpType.Write;
|
|
|
|
|
|
|
|
|
|
if (item.ItemValues == null || item.ItemValues.Count < 1)
|
|
|
|
|
{
|
|
|
|
|
// LogAsync(DateTime.Now, LogLevel.Error, $"{Name} 数据写入值不能为空");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int repeatTime = 3;
|
|
|
|
|
Stopwatch sw = new Stopwatch();
|
|
|
|
|
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var result = melsecMc.Write(item.Address, item.ItemValues.First());
|
|
|
|
|
if (result.IsSuccess)
|
|
|
|
|
{
|
|
|
|
|
repeatTime = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
if (repeatTime < 0)
|
|
|
|
|
{
|
|
|
|
|
//LogAsync(DateTime.Now, LogLevel.Exception, $"{Name}数据写入异常:{ex.GetExceptionMessage()}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} while (repeatTime > 0);
|
|
|
|
|
sw.Stop();
|
|
|
|
|
// LogAsync(DateTime.Now, LogLevel.Assist, $"{Name} WriteItem:{item.GetDisplayText()},写入 {item.ItemValues.First()}完成,耗时:{sw.ElapsedMilliseconds} ms");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 写单独地址
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="address">地址</param>
|
|
|
|
|
/// <param name="writeValue"></param>
|
|
|
|
|
/// <param name="waitForReply"></param>
|
|
|
|
|
public void WriteInt(string address, int writeValue, bool waitForReply = true)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(address))
|
|
|
|
|
{
|
|
|
|
|
// LogAsync(DateTime.Now, LogLevel.Error, $"{Name} 数据写入参数不能为空!");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int repeatTime = 3;
|
|
|
|
|
Stopwatch sw = new Stopwatch();
|
|
|
|
|
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
var result = melsecMc.Write("D"+address, writeValue);
|
|
|
|
|
if (result.IsSuccess)
|
|
|
|
|
{
|
|
|
|
|
repeatTime = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
if (repeatTime < 0)
|
|
|
|
|
{
|
|
|
|
|
// LogAsync(DateTime.Now, LogLevel.Exception, $"{Name}数据写入异常:{ex.GetExceptionMessage()}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} while (repeatTime > 0);
|
|
|
|
|
sw.Stop();
|
|
|
|
|
// LogAsync(DateTime.Now, LogLevel.Assist, $"{Name} {address},写入 {writeValue} 完成,耗时:{sw.ElapsedMilliseconds} ms");
|
|
|
|
|
}
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 写单独地址 string值
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="address">地址</param>
|
|
|
|
|
/// <param name="writeValue"></param>
|
|
|
|
|
/// <param name="waitForReply"></param>
|
|
|
|
|
public void WriteString(string address, string writeValue, bool waitForReply = true)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(address) || string.IsNullOrEmpty(writeValue))
|
|
|
|
|
{
|
|
|
|
|
// LogAsync(DateTime.Now, LogLevel.Error, $"{Name} 数据写入参数不能为空!");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int repeatTime = 3;
|
|
|
|
|
Stopwatch sw = new Stopwatch();
|
|
|
|
|
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var result = melsecMc.Write(address, writeValue);
|
|
|
|
|
if (result.IsSuccess)
|
|
|
|
|
{
|
|
|
|
|
repeatTime = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
if (repeatTime < 0)
|
|
|
|
|
{
|
|
|
|
|
// LogAsync(DateTime.Now, LogLevel.Exception, $"{Name}数据写入异常:{ex.GetExceptionMessage()}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} while (repeatTime > 0);
|
|
|
|
|
sw.Stop();
|
|
|
|
|
// LogAsync(DateTime.Now, LogLevel.Assist, $"{Name} {address},写入{writeValue}完成,耗时:{sw.ElapsedMilliseconds} ms");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 写单独地址 float值
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="address">地址</param>
|
|
|
|
|
/// <param name="writeValue"></param>
|
|
|
|
|
/// <param name="waitForReply"></param>
|
|
|
|
|
public void WriteFloat(string address, float writeValue, bool waitForReply = true)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(address))
|
|
|
|
|
{
|
|
|
|
|
//LogAsync(DateTime.Now, LogLevel.Error, $"{Name} 数据写入参数不能为空!");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int repeatTime = 3;
|
|
|
|
|
Stopwatch sw = new Stopwatch();
|
|
|
|
|
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var result = melsecMc.Write(address, writeValue);
|
|
|
|
|
if (result.IsSuccess)
|
|
|
|
|
{
|
|
|
|
|
repeatTime = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
if (repeatTime < 0)
|
|
|
|
|
{
|
|
|
|
|
// LogAsync(DateTime.Now, LogLevel.Exception, $"{Name}数据写入异常:{ex.GetExceptionMessage()}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} while (repeatTime > 0);
|
|
|
|
|
sw.Stop();
|
|
|
|
|
// LogAsync(DateTime.Now, LogLevel.Assist, $"{Name} {address},写入{writeValue}完成,耗时:{sw.ElapsedMilliseconds} ms");
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region DeviceBase
|
|
|
|
|
|
|
|
|
|
public void Start()
|
|
|
|
|
{
|
|
|
|
|
// IConfig.ip
|
|
|
|
|
|
|
|
|
|
melsecMc = new MelsecMcNet( "192.168.3.39", 502);
|
|
|
|
|
|
|
|
|
|
// 如果网络不太理想,配置了两个端口,一个有问题,立即切换另一个的话,可以配置如下的代码
|
|
|
|
|
// melsecMc.GetPipeSocket().SetMultiPorts(new int[] { 6000, 6001 });
|
|
|
|
|
//melsecMc = new HslCommunication.ModBus.ModbusTcpNet();
|
|
|
|
|
//melsecMc.IpAddress = "192.168.3.39";
|
|
|
|
|
//melsecMc.Port = 502;
|
|
|
|
|
//melsecMc.ConnectTimeOut = 10000; // 连接超时,单位毫秒
|
|
|
|
|
//melsecMc.ReceiveTimeOut = 5000; // 接收超时,单位毫秒
|
|
|
|
|
//melsecMc.Station = 1;
|
|
|
|
|
//melsecMc.AddressStartWithZero = true;
|
|
|
|
|
//melsecMc.IsStringReverse = false;
|
|
|
|
|
//melsecMc.DataFormat = HslCommunication.Core.DataFormat.CDAB;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// melsecMc.ServerStart(6000);
|
|
|
|
|
// 连接对象
|
|
|
|
|
OperateResult connect = melsecMc.ConnectServer();
|
|
|
|
|
if (connect.IsSuccess)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
Task.Run(() =>
|
|
|
|
|
{
|
|
|
|
|
HeartbeatMonitor();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// LogAsync(DateTime.Now, LogLevel.Exception, $"{Name}已开启异常:{connect.Message}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected void Stop()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
// 断开连接
|
|
|
|
|
// melsecMc.RemoteStop();
|
|
|
|
|
// OmronUDPNet.Stop();
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
// LogAsync(DateTime.Now, LogLevel.Exception, $"{Name}关闭异常:{ex.GetExceptionMessage()}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#region IMonitor
|
|
|
|
|
readonly byte[] scanBuffer = new byte[2048];
|
|
|
|
|
|
|
|
|
|
public List<int> GetMonitorValues(int startAddress, int length)
|
|
|
|
|
{
|
|
|
|
|
List<int> res = new List<int>();
|
|
|
|
|
// var result = melsecMc.ReadUInt16(startAddress.ToString(), (ushort)length);
|
|
|
|
|
// var result = melsecMc.ReadInt32("D" + startAddress, (ushort)length);
|
|
|
|
|
var result = melsecMc.ReadInt32("D" + startAddress, (ushort)length);
|
|
|
|
|
if (result.IsSuccess)
|
|
|
|
|
{
|
|
|
|
|
res = new List<int>(result.Content);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
//LogAsync(DateTime.Now, LogLevel.Error, $"{Name}未读取到数据:" + "地址:W" + startAddress + ";长度:" + length + ";提示:" + result.Message);
|
|
|
|
|
}
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
int HeartbeatCycle = 2;
|
|
|
|
|
string HeartbeatAddress = "D800";
|
|
|
|
|
bool CurrentState = false;
|
|
|
|
|
public event Action<int> Heartbeat;
|
|
|
|
|
public async void HeartbeatMonitor()
|
|
|
|
|
{
|
|
|
|
|
if (HeartbeatCycle <= 0)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
await Task.Run(async () =>
|
|
|
|
|
{
|
|
|
|
|
if (!string.IsNullOrEmpty(HeartbeatAddress)
|
|
|
|
|
&& HeartbeatCycle > 0)
|
|
|
|
|
{
|
|
|
|
|
while (CurrentState != false)
|
|
|
|
|
{
|
|
|
|
|
if (HeartbeatCycle <= 0)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
await Task.Delay(HeartbeatCycle * 1000);
|
|
|
|
|
var result = melsecMc.Write(HeartbeatAddress, 1);
|
|
|
|
|
if (!result.IsSuccess)
|
|
|
|
|
{
|
|
|
|
|
Heartbeat?.Invoke(1);
|
|
|
|
|
//LogAsync(DateTime.Now, LogLevel.Error, $"{this.Name} 心跳监听失败");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|