DHDHSoftware/DH.Devices.PLC/XinJEPLCTcpNet.cs
2025-03-27 15:11:48 +08:00

979 lines
31 KiB
C#
Raw Permalink 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.Diagnostics;
using System.Linq;
using System.Net.NetworkInformation;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Xml.Linq;
using DH.Commons.Base;
using DH.Commons.Enums;
using DH.Commons.Models;
using HslCommunication;
using HslCommunication.Enthernet;
using HslCommunication.Profinet.XINJE;
using OpenCvSharp;
namespace DH.Devices.PLC
{
public class XinJEPLCTcpNet : PLCBase
{
private static XinJEPLCTcpNet _instance;
public static XinJEPLCTcpNet Instance
{
get
{
if (_instance == null)
_instance = new XinJEPLCTcpNet();
return _instance;
}
}
private XinJETcpNet TcpNet = new XinJETcpNet();
public override bool PLCConnect()
{
try
{
TcpNet.IpAddress = IP;
TcpNet.Port = Port;
TcpNet.ConnectTimeOut = 5000;
TcpNet.ReceiveTimeOut = 10000;
TcpNet.SleepTime = 0;
TcpNet.AddressStartWithZero = true;
TcpNet.IsStringReverse = false;
TcpNet.Station = 1;
switch (PLCType)
{
case EnumPLCType.XD网口:
TcpNet.Series = XinJESeries.XD;
TcpNet.DataFormat = HslCommunication.Core.DataFormat.CDAB;
break;
case EnumPLCType.XC网口:
TcpNet.Series = XinJESeries.XC;
TcpNet.DataFormat = HslCommunication.Core.DataFormat.CDAB;
break;
}
OperateResult ret = TcpNet.ConnectServer();
if (ret.IsSuccess)
{
Connected = true;
//初始化流程
InitProcess();
return true;
}
else
{
Connected = false;
throw new Exception($"{IP}:{Port}PLC连接失败!");
}
}
catch(Exception ex)
{
Connected = false;
throw new Exception($"{IP}:{Port}PLC连接失败!失败原因:{ex.ToString()}");
}
}
public override Int16 ReadInt16(string address)
{
try
{
if (Connected)
{
// 读取Int变量
var result = TcpNet.ReadInt16(address);
if (result.IsSuccess)
{
return result.Content;
}
else
{
throw new Exception($"PLC操作读取int16失败,地址{address}");
}
}
else
{
throw new Exception($"PLC未连接,地址{address}");
}
}
catch (Exception ex)
{
throw new Exception($"PLC操作读取int16失败,地址{address},失败原因:{ex.ToString()}");
}
}
public override Int32 ReadInt32(string address)
{
try
{
if (Connected)
{
// 读取Int变量
var result = TcpNet.ReadInt32(address);
if (result.IsSuccess)
{
return result.Content;
}
else
{
throw new Exception($"PLC操作读取int32失败,地址{address}");
}
}
else
{
throw new Exception($"PLC未连接,地址{address}");
}
}
catch (Exception ex)
{
throw new Exception($"PLC操作读取int32失败,地址{address},失败原因:{ex.ToString()}");
}
}
public override UInt16 ReadUInt16(string address)
{
try
{
if (Connected)
{
// 读取Int变量
var result = TcpNet.ReadUInt16(address);
if (result.IsSuccess)
{
return result.Content;
}
else
{
throw new Exception($"PLC操作读取uint16失败,地址{address}");
}
}
else
{
throw new Exception($"PLC未连接,地址{address}");
}
}
catch (Exception ex)
{
throw new Exception($"PLC操作读取uint16失败,地址{address},失败原因:{ex.ToString()}");
}
}
public override UInt32 ReadUInt32(string address)
{
try
{
if (Connected)
{
// 读取Int变量
var result = TcpNet.ReadUInt32(address);
if (result.IsSuccess)
{
return result.Content;
}
else
{
throw new Exception($"PLC操作uint32失败,地址{address}");
}
}
else
{
throw new Exception($"PLC未连接,地址{address}");
}
}
catch (Exception ex)
{
throw new Exception($"PLC操作读取uint32失败,地址{address},失败原因:{ex.ToString()}");
}
}
public override float ReadFloat(string address)
{
try
{
if (Connected)
{
// 读取Float变量
var result = TcpNet.ReadFloat(address);
if (result.IsSuccess)
{
return result.Content;
}
else
{
throw new Exception($"PLC操作读取float失败,地址{address}");
}
}
else
{
throw new Exception($"PLC未连接,地址{address}");
}
}
catch (Exception ex)
{
throw new Exception($"PLC操作读取float失败,地址{address},失败原因:{ex.ToString()}");
}
}
public override bool ReadBool(string address)
{
try
{ if (Connected)
{
// 读取Bool变量
var result = TcpNet.ReadBool(address);
if (result.IsSuccess)
{
return result.Content;
}
else
{
throw new Exception($"PLC操作读取bool失败,地址{address}");
}
}
else
{
throw new Exception($"PLC未连接,地址{address}");
}
}
catch (Exception ex)
{
throw new Exception($"PLC操作读取bool失败,地址{address},失败原因:{ex.ToString()}");
}
}
/// <summary>
/// 写单独地址 uint32 值
/// </summary>
/// <param name="address">地址</param>
/// <param name="writeValue">要写入的 int 值</param>
/// <param name="waitForReply">是否等待回复</param>
public override bool WriteUInt32(string address, UInt32 writeValue, bool waitForReply = true)
{
if (Connected)
{
if (string.IsNullOrEmpty(address))
{
return false;
}
int repeatTime = 3;
do
{
try
{
var result = TcpNet.Write(address, writeValue);
if (result.IsSuccess)
{
return true;
}
}
catch (Exception ex)
{
repeatTime--;
if (repeatTime <= 0)
{
throw new Exception($"PLC操作写入uint32失败,地址{address},失败原因:{ex.ToString()}");
}
}
} while (repeatTime > 0);
}
else
{
throw new Exception($"PLC未连接,地址{address}");
}
return false;
}
/// <summary>
/// 写单独地址 uint16 值
/// </summary>
/// <param name="address">地址</param>
/// <param name="writeValue">要写入的 int 值</param>
/// <param name="waitForReply">是否等待回复</param>
public override bool WriteUInt16(string address, UInt16 writeValue, bool waitForReply = true)
{
if (Connected)
{
if (string.IsNullOrEmpty(address))
{
return false;
}
int repeatTime = 3;
do
{
try
{
var result = TcpNet.Write(address, writeValue);
if (result.IsSuccess)
{
return true;
}
}
catch (Exception ex)
{
repeatTime--;
if (repeatTime <= 0)
{
throw new Exception($"PLC操作写入uint16失败,地址{address},失败原因:{ex.ToString()}");
}
}
} while (repeatTime > 0);
}
else
{
throw new Exception($"PLC未连接,地址{address}");
}
return false;
}
/// <summary>
/// 写单独地址 int32 值
/// </summary>
/// <param name="address">地址</param>
/// <param name="writeValue">要写入的 int 值</param>
/// <param name="waitForReply">是否等待回复</param>
public override bool WriteInt32(string address, Int32 writeValue, bool waitForReply = true)
{
if (Connected)
{
if (string.IsNullOrEmpty(address))
{
return false;
}
int repeatTime = 3;
do
{
try
{
var result = TcpNet.Write(address, writeValue);
if (result.IsSuccess)
{
return true;
}
}
catch (Exception ex)
{
repeatTime--;
if (repeatTime <= 0)
{
throw new Exception($"PLC操作写入int32失败,地址{address},失败原因:{ex.ToString()}");
}
}
} while (repeatTime > 0);
}
else
{
throw new Exception($"PLC未连接,地址{address}");
}
return false;
}
/// <summary>
/// 写单独地址 int16 值
/// </summary>
/// <param name="address">地址</param>
/// <param name="writeValue">要写入的 int 值</param>
/// <param name="waitForReply">是否等待回复</param>
public override bool WriteInt16(string address, Int16 writeValue, bool waitForReply = true)
{
if (Connected)
{
if (string.IsNullOrEmpty(address))
{
return false;
}
int repeatTime = 3;
do
{
try
{
var result = TcpNet.Write(address, writeValue);
if (result.IsSuccess)
{
return true;
}
}
catch (Exception ex)
{
repeatTime--;
if (repeatTime <= 0)
{
throw new Exception($"PLC操作写入int16失败,地址{address},失败原因:{ex.ToString()}");
}
}
} while (repeatTime > 0);
}
else
{
throw new Exception($"PLC未连接,地址{address}");
}
return false;
}
/// <summary>
/// 写单独地址 float 值
/// </summary>
/// <param name="address">地址</param>
/// <param name="writeValue">要写入的 float 值</param>
/// <param name="waitForReply">是否等待回复</param>
public override bool WriteFloat(string address, float writeValue, bool waitForReply = true)
{
if (Connected)
{
if (string.IsNullOrEmpty(address))
{
return false;
}
int repeatTime = 3;
do
{
try
{
var result = TcpNet.Write(address, writeValue);
if (result.IsSuccess)
{
return true;
}
}
catch (Exception ex)
{
repeatTime--;
if (repeatTime <= 0)
{
throw new Exception($"PLC操作写入float失败,地址{address},失败原因:{ex.ToString()}");
}
}
} while (repeatTime > 0);
}
else
{
throw new Exception($"PLC未连接,地址{address}");
}
return false;
}
/// <summary>
/// 写单独地址 bool 值
/// </summary>
/// <param name="address">地址</param>
/// <param name="writeValue">要写入的 bool 值</param>
/// <param name="waitForReply">是否等待回复</param>
public override bool WriteBool(string address, bool writeValue, bool waitForReply = true)
{
if(Connected)
{
if (string.IsNullOrEmpty(address))
{
return false;
}
int repeatTime = 3;
do
{
try
{
var result = TcpNet.Write(address, writeValue);
if (result.IsSuccess)
{
return true;
}
}
catch (Exception ex)
{
repeatTime--;
if (repeatTime <= 0)
{
throw new Exception($"PLC操作写入bool失败,地址{address},失败原因:{ex.ToString()}");
}
}
} while (repeatTime > 0);
Thread.Sleep(30);
}
else
{
throw new Exception($"PLC未连接,地址{address}");
}
return false;
}
public override bool PLCDisConnect()
{
if (Connected)
{
var res = TcpNet.ConnectClose();
if (res.IsSuccess)
{
Connected = false;
return true;
}
return false;
}
return false;
}
private void MonitorPieces()
{
ThreadStart ts = new ThreadStart(MonitorPiecesImpl);
Thread th = new Thread(ts);
th.Priority = ThreadPriority.AboveNormal;
th.IsBackground = false;
th.Start();
}
public TaskFactory _taskFactory = new TaskFactory(TaskCreationOptions.LongRunning, TaskContinuationOptions.LongRunning);
private Dictionary<ushort, uint> piecesCountDic = new Dictionary<ushort, uint>();
private uint piecesCount = 0;
/// <summary>
/// int,int 轴号 捕获位置
/// </summary>
public event Action<int, uint> OnNewPieces;
public void NewPieces(int axisIndex, uint pieceNumber)
{
_taskFactory.StartNew(() =>
{
Thread.CurrentThread.Priority = ThreadPriority.Highest;
OnNewPieces?.Invoke(axisIndex, pieceNumber);
});
}
public async Task HeartbeatAsync1()
{
PLCItem? pLCItem = PLCItemList.FirstOrDefault(u => u.Name == "心跳地址");
while (Connected)
{
if (pLCItem == null)
return;
WriteBool(pLCItem.Address, true);
await Task.Delay(900); // 非阻塞等待1秒
}
}
/// <summary>
/// 入料监听
/// </summary>
/// <param name="axisIndex"></param>
private void MonitorPiecesImpl()
{
PLCItem pLCItem= PLCItemList.FirstOrDefault(u => u.Name == "产品计数");
if (pLCItem == null)
return;
string Count = pLCItem.Address;
DateTime startTime = DateTime.Now;
DateTime endTime = startTime;
TimeSpan timeSpan = endTime - startTime;
Thread.CurrentThread.Priority = ThreadPriority.AboveNormal;
//while (CurrentState != DeviceState.DSClose && CurrentState != DeviceState.DSExcept && CurrentState != DeviceState.DSUninit)
while (Connected)
{
Stopwatch sw = new Stopwatch();
uint tmpPieceNumber = 0;
sw.Start();
// var ret = TcpNet.ReadUInt16("D1016");
var ret = TcpNet.ReadUInt32(Count);
sw.Stop();
if (ret.IsSuccess)
{
tmpPieceNumber = ret.Content;
}
if (ret.IsSuccess && ret.Content > piecesCount)
{
sw.Start();
// Console.WriteLine($"{DateTime.Now.ToString("HH:mm:ss.fff")} 板卡{station}产品入列触发{tmpPieceNumber}");
//LogAsync(DateTime.Now, LogLevel.Information, $"转盘{0}产品入列 {piecesCountDic[0]} size:{sum}");
if (tmpPieceNumber != piecesCount + 1)
{
//LogAsync(DateTime.Now, LogLevel.Information, $"入列触发丢失\t{tmpPieceNumber}");
// Console.WriteLine($"{DateTime.Now.ToString("HH:mm:ss.fff")}\t板卡{station}产品入列触发丢失,{piecesCountDic[station]}\t{tmpPieceNumber}");
}
piecesCount = tmpPieceNumber;
//NewPieces(ai, piecesCountDic[station]);
NewPieces(1, piecesCount);
sw.Stop();
startTime = DateTime.Now;
//if (idalarm)
//{
// idalarm = false;
// AlarmVibration(false);
//}
}
Thread.Sleep(1);
}
}
public void InitProcess()
{
//启用心跳
OpenHeartbeat(true);
//状态复位
StatusReset();
//关闭定位
VisionPos(false);
//写入流程加载点位配置
InitProcessAction();
//计数清零
CountToZero();
//停止转盘
TurnStart(false);
//转盘使能
TurnEnable(true);
//开启入料监听
MonitorPieces();
}
public void StartProcess()
{
//状态复位
StatusReset();
//关闭定位
VisionPos(false);
//写入流程启动点位配置
StartProcessAction();
//计数清零
CountToZero();
//转盘启动
TurnStart(true);
}
public void CloseProcess()
{
StatusReset();
VisionPos(false);
CountToZero();
TurnStart(false);
TurnEnable(false);
OpenHeartbeat(false);
PLCDisConnect();
}
public void InitProcessAction() =>
ProcessAction(ConfigModel.GlobalList?.FirstOrDefault()?.InitProcessList?.ToList() ?? new List<PLCItem>());
public void StartProcessAction() =>
ProcessAction(ConfigModel.GlobalList?.FirstOrDefault()?.StartProcessList?.ToList() ?? new List<PLCItem>());
public void StopProcessAction() =>
ProcessAction(ConfigModel.GlobalList?.FirstOrDefault()?.StopProcessList?.ToList() ?? new List<PLCItem>());
public void StartResetAction() =>
ProcessAction(ConfigModel.GlobalList?.FirstOrDefault()?.StartResetList?.ToList() ?? new List<PLCItem>());
public void StopResetAction() =>
ProcessAction(ConfigModel.GlobalList?.FirstOrDefault()?.StopResetList?.ToList() ?? new List<PLCItem>());
// 处理方法:无需再检查 null
private void ProcessAction(List<PLCItem> items)
{
if (items.Count == 0) return;
foreach (var item in items.OrderBy(x => x.StartIndex))
{
switch (item.Type)
{
case EnumPLCDataType.:
WriteUInt16(item.Address, ushort.Parse(item.Value));
break;
case EnumPLCDataType.:
WriteUInt32(item.Address, uint.Parse(item.Value));
break;
case EnumPLCDataType.:
WriteFloat(item.Address, float.Parse(item.Value));
break;
case EnumPLCDataType.:
WriteBool(item.Address, item.Value.Equals("true", StringComparison.OrdinalIgnoreCase));
break;
}
}
}
/// <summary>
/// 状态复位
/// </summary>
public void StatusReset()
{
PLCItem? pLCItem = PLCItemList.FirstOrDefault(u => u.Name == "状态复位");
if (pLCItem == null)
return;
WriteBool(pLCItem.Address, false);
}
/// <summary>
/// 开启关闭视觉定位
/// </summary>
public void VisionPos(bool b)
{
PLCItem? pLCItem = PLCItemList.FirstOrDefault(u => u.Name == "启用定位");
if (pLCItem == null)
return;
WriteBool(pLCItem.Address, b);
}
/// <summary>
/// 转盘启停
/// </summary>
public void TurnStart(bool b)
{
PLCItem? DiskRunItem = PLCItemList.FirstOrDefault(u => u.Name == "转盘启动");
if (DiskRunItem == null)
return;
WriteBool(DiskRunItem.Address, b);
Thread.Sleep(30);
piecesCount = 0;
}
/// <summary>
/// 转盘使能
/// </summary>
/// <param name="b"></param>
public void TurnEnable(bool b)
{
PLCItem? DiskOpenItem = PLCItemList.FirstOrDefault(u => u.Name == "转盘使能");
if (DiskOpenItem == null)
return;
WriteBool(DiskOpenItem.Address, b);
Thread.Sleep(30);
}
/// <summary>
/// 计数清零
/// </summary>
public void CountToZero()
{
PLCItem? pLCItem = PLCItemList.FirstOrDefault(u => u.Name == "计数清零");
if (pLCItem == null)
return;
WriteBool(pLCItem.Address, true);
Thread.Sleep(30);
}
public void RedLight(bool b)
{
PLCItem? pLCItem = PLCItemList.FirstOrDefault(u => u.Name == "指示灯红");
if (pLCItem == null)
return;
WriteBool(pLCItem.Address, b);
Thread.Sleep(30);
}
public void GreenLight(bool b)
{
PLCItem? pLCItem = PLCItemList.FirstOrDefault(u => u.Name == "指示灯绿");
if (pLCItem == null)
return;
WriteBool(pLCItem.Address, b);
Thread.Sleep(30);
}
public void YellowLight(bool b)
{
PLCItem? pLCItem = PLCItemList.FirstOrDefault(u => u.Name == "指示灯黄");
if (pLCItem == null)
return;
WriteBool(pLCItem.Address, b);
Thread.Sleep(30);
}
public void Buzzer(bool b)
{
PLCItem? pLCItem = PLCItemList.FirstOrDefault(u => u.Name == "蜂鸣器");
if (pLCItem == null)
return;
WriteBool(pLCItem.Address, b);
Thread.Sleep(30);
}
/// <summary>
/// 挡料电机操作
/// true: 顺时针
/// False: 逆时针
/// </summary>
/// <param name="u"></param>
public void FeedingMotor( bool direction)
{
// 设置最大等待时间,假设为 3 秒
int timeout = 3000;
int elapsedTime = 0;
int checkInterval = 100; // 每次检查等待 100ms
PLCItem pLCItem = PLCItemList.FirstOrDefault(u => u.Name == "挡料电机回原点");
if (pLCItem == null)
return;
PLCItem zerospeeditem = PLCItemList.FirstOrDefault(u => u.Name == "挡料电机回原点速度");
if (zerospeeditem == null)
return;
PLCItem CunSpeed = PLCItemList.FirstOrDefault(u => u.Name == "挡料电机速度");
if (CunSpeed == null)
return;
PLCItem CunClockwiseItem = PLCItemList.FirstOrDefault(u => u.Name == "挡料电机顺时针");
if (CunClockwiseItem == null)
return;
PLCItem CunCounterclockwiseItem = PLCItemList.FirstOrDefault(u => u.Name == "挡料电机逆时针");
if (CunCounterclockwiseItem == null)
return;
PLCItem CunPosItem = PLCItemList.FirstOrDefault(u => u.Name == "挡料电机位置");
if (CunPosItem == null)
return;
string CunToZero = pLCItem.Type + pLCItem.Address;
string CunToZeroSpeed = zerospeeditem.Type + zerospeeditem.Address;
string CunSpeedadress = CunSpeed.Type + CunSpeed.Address;
string CunClockwise = CunClockwiseItem.Type + CunClockwiseItem.Address;
string CunCounterclockwise = CunCounterclockwiseItem.Type + CunCounterclockwiseItem.Address;
string CunPos = CunPosItem.Type + CunPosItem.Address;
UInt16 zerospeed = UInt16.Parse(zerospeeditem.Value);
UInt16 cunSpeed = UInt16.Parse(CunSpeed.Value);
UInt16 u = UInt16.Parse(CunPosItem.Value);
// WriteBool(CountToZero, true);
// 检查是否不在原点,如果不在,则回原点
if (!ReadBool(CunToZero))
{
WriteUInt16(CunToZeroSpeed, zerospeed); // 速度
Thread.Sleep(30);
// 发送回原点指令
WriteBool(CunToZero, true);
Thread.Sleep(1000); // 给设备一些时间响应
// 等待回到原点
while (!ReadBool(CunToZero))
{
if (elapsedTime >= timeout)
{
break;
}
Thread.Sleep(checkInterval);
elapsedTime += checkInterval;
}
}
// 无论是刚回到原点还是已经在原点,执行目标位置、速度和方向设置
WriteUInt16(CunSpeedadress,cunSpeed);
Thread.Sleep(2000);
string dir = string.Empty;
if (direction)
{
WriteBool(CunClockwise, true); // 顺时针转动
dir = "顺时针";
}
else
{
WriteBool(CunCounterclockwise, true); // 逆时针转动
dir = "逆时针";
}
Thread.Sleep(30);
WriteUInt16(CunPos, u); // 目标位置
Thread.Sleep(2000);
}
/// <summary>
/// 转盘清料
/// </summary>
/// <param name="b"></param>
public void TurnClear(bool b)
{
PLCItem? pLCItem = PLCItemList.FirstOrDefault(u => u.Name == "转盘清料");
if (pLCItem == null)
return;
WriteBool(pLCItem.Address, b);
Thread.Sleep(30);
}
/// <summary>
/// 开启心跳
/// </summary>
/// <param name="v"></param>
public void OpenHeartbeat(bool b)
{
PLCItem? pLCItem = PLCItemList.FirstOrDefault(u => u.Name == "启用心跳");
if (pLCItem == null)
return;
WriteBool(pLCItem.Address, b);
Thread.Sleep(30);
if (b)
{
//开启心跳
Task.Run(async () => await HeartbeatAsync1());
}
}
int currentRegister = 0;
public void Blowing(uint productNumber, UInt16 value)
{
int Register = (int)((productNumber - 1) % 30);
currentRegister = 411 + Register;
WriteUInt16($"D{currentRegister}", value);
}
}
}