using Admin.NET.Core.Service;
|
using DocumentFormat.OpenXml.Drawing;
|
using Furion.Logging;
|
using Microsoft.AspNetCore.SignalR;
|
using OfficeOpenXml.FormulaParsing.Excel.Functions.Text;
|
using System;
|
using WCS.Application.Util;
|
//using WCS.Application.Util;
|
|
namespace WCS.Application;
|
public static class PLCTaskAction
|
{
|
//服务运行状态
|
public static bool boRunningState = false;
|
//脱机模式
|
public static bool boOffline = false;
|
//自刷新
|
public static bool boRefresh = false;
|
//演示模式
|
public static bool boDemo = false;
|
//滚筒反转
|
public static bool boDrumReversal = false;
|
//出库锁定
|
public static bool boOutLock = false;
|
//入库锁定
|
public static bool boEnterLock = false;
|
|
public static List<PlcPositionInfo> listPositionInfo = new List<PlcPositionInfo>();
|
|
private static readonly ISqlSugarClient _db = SqlSugarSetup.ITenant.GetConnectionScope(SqlSugarConst.MainConfigId);
|
private static readonly SysCacheService sysCacheService = App.GetRequiredService<SysCacheService>();
|
private static readonly SysConfigService _sysConfigService = App.GetService<SysConfigService>();
|
|
private static List<WcsPlc> listPlc = new List<WcsPlc>();
|
private static List<WcsDevice> listPlcDevice = new List<WcsDevice>();
|
private static List<WcsPosition> listPlcPosition = new List<WcsPosition>();
|
private static List<WcsAlarmInfo> listAlarmInfo = new List<WcsAlarmInfo>();
|
|
private static List<PLCUtil> listPlcUtil = new List<PLCUtil>();
|
private static CancellationTokenSource cts = new CancellationTokenSource();//取消线程标识
|
//对外公布连接状态
|
public static List<PLCUtil> listPlcConn
|
{
|
get { return listPlcUtil; }
|
}
|
// 交互点集合
|
public static List<WcsDevice> plcDevices
|
{
|
get { return listPlcDevice; }
|
}
|
public static List<WcsPosition> plcPositions
|
{
|
get { return listPlcPosition; }
|
}
|
public static event EventHandler DeviceValueChangeEvent;
|
static PLCTaskAction()
|
{
|
//订阅事件
|
DeviceValueChangeEvent += PLCService.OnChangeEvent;
|
boRunningState = _sysConfigService.GetConfigValue<bool>("sys_RunningState").Result;
|
boOffline = _sysConfigService.GetConfigValue<bool>("sys_Offline").Result;
|
boRefresh = _sysConfigService.GetConfigValue<bool>("sys_Refresh").Result;
|
boDemo = _sysConfigService.GetConfigValue<bool>("sys_demo").Result;
|
boDrumReversal = _sysConfigService.GetConfigValue<bool>("sys_DrumReversal").Result;
|
boOutLock = _sysConfigService.GetConfigValue<bool>("sys_BoOutLock").Result;
|
boEnterLock = _sysConfigService.GetConfigValue<bool>("sys_BoEnterLock").Result;
|
}
|
/// <summary>
|
/// 初始化PLC连接
|
/// </summary>
|
public static void Init()
|
{
|
cts.Cancel();
|
listPlc = _db.Queryable<WcsPlc>()
|
.Where(s => s.Type == PLCTypeEnum.StackingMachine || s.Type == PLCTypeEnum.ConveyorLine || s.Type == PLCTypeEnum.BoxConveyorLine)
|
.Where(s => s.Enable == YesNoEnum.Y)
|
.ToList();
|
listPlcDevice = _db.Queryable<WcsDevice>().ToList();
|
listPlcPosition = _db.Queryable<WcsPosition>().ToList();
|
listAlarmInfo = _db.Queryable<WcsAlarmInfo>().ToList();
|
//等待几秒钟,把已有线程取消掉再连接
|
//Thread.Sleep(5000);
|
foreach (var modPlcUtil in listPlcUtil)
|
{
|
modPlcUtil.Close();
|
}
|
listPlcUtil.Clear();
|
foreach (var modPlc in listPlc)
|
{
|
var plc = new PLCUtil(modPlc);
|
listPlcUtil.Add(plc);
|
}
|
HubUtil.UpdateService(new PLCServiceModel()
|
{
|
BoRunningState = boRunningState,
|
BoOffline = boOffline,
|
BoRefresh = boRefresh
|
});
|
if (boRunningState)
|
{
|
cts = new CancellationTokenSource();
|
StartRead();
|
ConnectionStatus();
|
// StartWatchAlarm(); // 隐藏报警监控
|
// StartWatchPosition(); // 隐藏位置监控
|
}
|
}
|
/// <summary>
|
/// 开启读取plc线程
|
/// </summary>
|
public static void StartRead()
|
{
|
Console.WriteLine("开启读取plc线程");
|
foreach (var modPlc in listPlc)
|
{
|
Task.Run(() =>
|
{
|
var _modplc = modPlc;
|
while (true)
|
{
|
//取消线程 关闭PLC连接
|
if (cts.Token.IsCancellationRequested)
|
{
|
foreach (var modPlcUtil in listPlcUtil)
|
{
|
if (modPlcUtil != null && modPlcUtil.Connected)
|
modPlcUtil.Close();
|
}
|
break;
|
//throw new OperationCanceledException();
|
}
|
try
|
{
|
var modPlcUtil = listPlcUtil.FirstOrDefault(s => s != null && s.PlcId == modPlc.Id);
|
if (modPlcUtil == null)
|
{
|
Console.WriteLine($"连接plc{modPlc.IP}");
|
modPlcUtil = new PLCUtil(modPlc);
|
listPlcUtil.Add(modPlcUtil);
|
if (modPlcUtil.Connected)
|
Console.WriteLine($"连接plc{modPlc.IP}成功");
|
}
|
if (!modPlcUtil.Connected)
|
{
|
Thread.Sleep(10000);
|
modPlcUtil.Open();
|
continue;
|
}
|
var listDevice = listPlcDevice
|
.Where(s => s.PlcId == _modplc.Id && s.DeviceType == DeviceTypeEnum.Business)
|
.WhereIF(modPlc.Type == PLCTypeEnum.ConveyorLine, s => s.Level == DeviceLevelEnum.Station)
|
.WhereIF(modPlc.Type == PLCTypeEnum.StackingMachine, s => s.Level == DeviceLevelEnum.DB)
|
.ToList();
|
|
//循环读设备
|
foreach (var modDevice in listDevice)
|
{
|
if (modDevice.Text == "一楼主扫")
|
{
|
Console.WriteLine($"连接...{modPlc.IP}");
|
}
|
var (result, value) = modPlcUtil.GetPlcDBValue(modDevice.PosType, modDevice.DbNumber, modDevice.PlcPos);
|
//if (!result.IsSucceed)
|
// Console.WriteLine($"{modPlc.IP}错误{modDevice.DbNumber}.{modDevice.PlcPos}:"+result.Err);
|
if (result.IsSucceed)
|
{
|
//Console.WriteLine($"{modPlc.IP}读取{modDevice.DbNumber}.{modDevice.PlcPos}的值为:{value}");
|
//无流程跳出
|
if (value == 0 && modPlc.Type != PLCTypeEnum.BoxConveyorLine && modDevice.StationNum != "094" && modDevice.StationNum != "160" && modDevice.StationNum != "155" && modDevice.StationNum != "152" && modDevice.StationNum != "153")//
|
continue;
|
var dto = modDevice.Adapt<WcsDeviceDto>();
|
dto.Value = value;
|
dto.Type = _modplc.Type;
|
dto.PLCUtil = modPlcUtil;
|
dto.listStation = listPlcPosition.Where(s => s.DeviceId == modDevice.Id).ToList();
|
dto.listDevice = listDevice.Where(s => s.StationNum == modDevice.StationNum && s.Level == DeviceLevelEnum.Station).ToList();
|
//这里触发值变更事件
|
DeviceValueChangeEvent?.Invoke(dto, EventArgs.Empty);
|
}
|
else if (!modPlcUtil.Connected)
|
{
|
//Console.WriteLine($"连接断开...{modPlc.IP}");
|
//重新打开连接
|
modPlcUtil.Open();
|
//if (modPlcUtil.Connected)
|
// Console.WriteLine($"重连成功...{modPlc.IP}");
|
}
|
}
|
|
Thread.Sleep(1000);
|
}
|
catch (OperationCanceledException)
|
{
|
Console.WriteLine("中止线程");
|
}
|
catch (Exception ex)
|
{
|
Log.Error("读取PLC线程发生异常", ex);
|
}
|
}
|
}, cts.Token);
|
}
|
}
|
|
/// <summary>
|
/// 连接状态线程
|
/// </summary>
|
public static void ConnectionStatus()
|
{
|
Task.Run(() =>
|
{
|
try
|
{
|
Console.WriteLine("连接状态线程");
|
while (true)
|
{
|
//取消线程 关闭PLC连接
|
if (cts.Token.IsCancellationRequested)
|
{
|
foreach (var modPlcUtil in listPlcUtil)
|
{
|
if (modPlcUtil != null && modPlcUtil.Connected)
|
modPlcUtil.Close();
|
}
|
break;
|
//throw new OperationCanceledException();
|
}
|
//获取每个PLC连接状态
|
foreach (var modPlc in listPlc)
|
{
|
var modPlcUtil = listPlcUtil.FirstOrDefault(s => s.PlcId == modPlc.Id);
|
if (modPlcUtil == null)
|
modPlc.IsConn = false;
|
else
|
modPlc.IsConn = modPlcUtil.Connected;
|
if (sysCacheService.ExistKey("PLCCONN:" + modPlc.Id))
|
{
|
var cachePlc = sysCacheService.Get<WcsPlc>("PLCCONN:" + modPlc.Id);
|
if (cachePlc.IsConn != modPlc.IsConn)
|
{
|
//连接状态变更 通知前端
|
Console.WriteLine($"通知前端变更{modPlc.Text} {modPlc.IsConn}");
|
HubUtil.PublicPlcConn(modPlc);
|
}
|
sysCacheService.Set("PLCCONN:" + modPlc.Id, modPlc);
|
}
|
else
|
{
|
sysCacheService.Set("PLCCONN:" + modPlc.Id, modPlc);
|
HubUtil.PublicPlcConn(modPlc);
|
}
|
}
|
Thread.Sleep(1000);
|
}
|
}
|
catch (OperationCanceledException)
|
{
|
sysCacheService.RemoveByPrefixKey("PLCCONN");
|
Console.WriteLine("中止线程");
|
}
|
catch (Exception ex)
|
{
|
Log.Error("连接状态线程发生异常", ex);
|
}
|
|
}, cts.Token);
|
|
}
|
|
/// <summary>
|
/// 开启报警监控
|
/// </summary>
|
public static void StartWatchAlarm()
|
{
|
Task.Run(() =>
|
{
|
var listPlc = listAlarmInfo.GroupBy(s => new { s.PlcIP, s.PlcPort }).ToList();
|
List<PLCUtil> listPlcUtil = new List<PLCUtil>();
|
int i = 0;
|
foreach (var modPlc in listPlc)
|
{
|
listPlcUtil.Add(new PLCUtil(new WcsPlc() { Id = i++, IP = modPlc.Key.PlcIP, Port = modPlc.Key.PlcPort, PLCType = PLCEnum.S7_1500 }));
|
}
|
while (true)
|
{
|
//取消线程 关闭PLC连接
|
if (cts.Token.IsCancellationRequested)
|
{
|
foreach (var modPlcUtil in listPlcUtil)
|
{
|
if (modPlcUtil != null && modPlcUtil.Connected)
|
modPlcUtil.Close();
|
}
|
break;
|
//throw new OperationCanceledException();
|
}
|
foreach (var modUtil in listPlcUtil)
|
{
|
try
|
{
|
//报警点位
|
var listAlarm = listAlarmInfo.Where(s => s.PlcIP == modUtil.PlcIP.ToString()).ToList();
|
var listaddress = new Dictionary<string, PLCDataTypeEnum>();
|
foreach (var modAlarm in listAlarm)
|
{
|
listaddress.Add(modAlarm.AlarmCode, PLCDataTypeEnum.Bit);
|
}
|
var result = modUtil.GetPlcBatchDBValue(listaddress);
|
var listPulish = new List<WcsAlarmInfo>();
|
foreach (var item in result.Value)
|
{
|
var value = Convert.ToBoolean(item.Value);
|
var modAlarm = listAlarm.FirstOrDefault(s => s.AlarmCode == item.Key);
|
var stationNum = modAlarm.StationNum;
|
if (value && modAlarm.Status == YesNoEnum.N)
|
{
|
//修改报警状态同时记录报警日志
|
modAlarm.AlarmTime = DateTime.Now;
|
modAlarm.Status = YesNoEnum.Y;
|
listPulish.Add(modAlarm);
|
}
|
//取消报警
|
else if (value && modAlarm.Status == YesNoEnum.Y)
|
{
|
modAlarm.AlarmTime = null;
|
modAlarm.Status = YesNoEnum.N;
|
listPulish.Add(modAlarm);
|
}
|
}
|
if (listPulish.Count > 0)
|
{
|
_db.Updateable(listPulish).ExecuteCommand();
|
var listLog = new List<WcsAlarmLog>();
|
foreach (var mod in listPulish.Where(s => s.Status == YesNoEnum.Y))
|
{
|
WcsAlarmLog modLog = mod.Adapt<WcsAlarmLog>();
|
modLog.Id = 0;
|
listLog.Add(modLog);
|
}
|
_db.Insertable(listLog).ExecuteCommand();
|
HubUtil.PublicAlarm(listPulish);
|
}
|
}
|
catch (Exception ex)
|
{
|
Log.Error("报警监控发生异常", ex);
|
}
|
|
}
|
Thread.Sleep(10000);
|
}
|
}, cts.Token);
|
}
|
/// <summary>
|
/// 开启位置监控
|
/// </summary>
|
public static void StartWatchPosition()
|
{
|
Console.WriteLine("开启位置监控");
|
Task.Run(() =>
|
{
|
while (true)
|
{
|
//取消线程 关闭PLC连接
|
if (cts.Token.IsCancellationRequested)
|
{
|
foreach (var modPlcUtil in listPlcUtil)
|
{
|
if (modPlcUtil != null && modPlcUtil.Connected)
|
modPlcUtil.Close();
|
}
|
break;
|
//throw new OperationCanceledException();
|
}
|
try
|
{
|
foreach (var modPlcUtil in listPlcUtil)
|
{
|
if (modPlcUtil == null)
|
continue;
|
var plcIp = modPlcUtil.PlcIP;
|
var listDevice = listPlcDevice.Where(s => s.PlcId == modPlcUtil.PlcId && s.DeviceType == DeviceTypeEnum.Show).ToList();
|
var modPlc = listPlc.FirstOrDefault(s => s.Id == modPlcUtil.PlcId);
|
var listaddress = new Dictionary<string, PLCDataTypeEnum>();
|
foreach (var modDevice in listDevice)
|
{
|
listaddress.Add(modDevice.DbNumber + "." + modDevice.PlcPos, modDevice.PosType);
|
}
|
//批量读
|
var result = modPlcUtil.GetPlcBatchDBValue(listaddress);
|
//测试用
|
//foreach (var modDevice in listDevice)
|
//{
|
// if (modPlc.Type == PLCTypeEnum.StackingMachine)
|
// result.Value.Add(modDevice.DbNumber + "." + modDevice.PlcPos, new Random().Next(790000));
|
// else if (modPlc.Type == PLCTypeEnum.ConveyorLine)
|
// result.Value.Add(modDevice.DbNumber + "." + modDevice.PlcPos, new Random().Next(2) == 0);
|
//}
|
foreach (var item in result.Value)
|
{
|
var modDevice = listDevice.FirstOrDefault(s => item.Key == (s.DbNumber + "." + s.PlcPos));
|
if (modDevice == null)
|
continue;
|
switch (modPlc.Type)
|
{
|
case PLCTypeEnum.StackingMachine:
|
{
|
var value = Convert.ToInt32(item.Value);
|
var height = Math.Round(value / 790000d * 200).ToInt();
|
var modPosition = listPlcPosition.FirstOrDefault(s => s.DeviceId == modDevice.Id && s.Text == "目的工位");
|
string EndLocat = "";
|
if (modPosition != null)
|
{
|
var (result2, value2) = modPlcUtil.GetPlcDBValue(modPosition.PosType, modDevice.DbNumber, modPosition.PlcPos);
|
if (result2.IsSucceed)
|
EndLocat = Convert.ToString(value2);
|
}
|
if (modDevice.BoxHeight != height || modDevice.EndLocat != EndLocat)
|
{
|
modDevice.EndLocat = EndLocat;
|
modDevice.BoxHeight = Math.Round(value / 790000d * 200).ToInt();
|
var modInfo = new PlcPositionInfo()
|
{
|
Type = modPlc.Type,
|
StationNum = modDevice.StationNum,
|
BoxHeight = modDevice.BoxHeight,
|
EndLocat = EndLocat,
|
};
|
HubUtil.PublicPosition(modInfo);
|
UpdatePosition(modInfo);
|
}
|
}
|
break;
|
case PLCTypeEnum.ConveyorLine:
|
{
|
var bl = false;
|
int value = Convert.ToInt32(item.Value);
|
if (value <= 30)
|
{
|
bl = false;
|
}
|
else
|
{
|
bl = true;
|
}
|
|
if (bl != modDevice.BoHaveItem)
|
{
|
modDevice.BoHaveItem = bl;
|
var modInfo = new PlcPositionInfo() { Type = modPlc.Type, StationNum = modDevice.StationNum.PadLeft(3, '0'), BoHaveItem = bl };
|
HubUtil.PublicPosition(modInfo);
|
UpdatePosition(modInfo);
|
}
|
}
|
break;
|
default:
|
break;
|
}
|
}
|
}
|
Thread.Sleep(3000);
|
}
|
catch (Exception ex)
|
{
|
Log.Error("位置监控发生异常", ex);
|
}
|
}
|
});
|
}
|
|
/// <summary>
|
/// 发送信息到前端
|
/// </summary>
|
/// <param name="modInfo"></param>
|
private static void UpdatePosition(PlcPositionInfo modInfo)
|
{
|
var modTemp = listPositionInfo.FirstOrDefault(s => s.StationNum == modInfo.StationNum && modInfo.Type == s.Type);
|
if (modTemp == null)
|
{
|
listPositionInfo.Add(modInfo);
|
}
|
else
|
{
|
modTemp.BoHaveItem = modInfo.BoHaveItem;
|
}
|
}
|
/// <summary>
|
/// 停止服务
|
/// </summary>
|
public static void Stop()
|
{
|
cts.Cancel();
|
boRunningState = false;
|
}
|
}
|