357 lines
13 KiB
C#
357 lines
13 KiB
C#
|
|
using Common;
|
|||
|
|
using Common.Models;
|
|||
|
|
using CommunityToolkit.Mvvm.Messaging;
|
|||
|
|
using MegaRobo.C00225155.DataAccess;
|
|||
|
|
using MegaRobo.C00225155.Entities;
|
|||
|
|
using MegaRobo.C00225155.Entities.Entity_DB;
|
|||
|
|
using MegaRobo.C00225155.Entities.ToWeb;
|
|||
|
|
using MegaRobo.Contract;
|
|||
|
|
using MegaRobo.ControlDevices;
|
|||
|
|
using MegaRobo.ControlDevices.Abstractions;
|
|||
|
|
using MegaRobo.ControlDevices.Models;
|
|||
|
|
using MegaRobo.Entities;
|
|||
|
|
using MegaRobo.Logger;
|
|||
|
|
using MegaRobo.PipetteTool.HamiltonConsole.PipetteDevices.HamiltonDevices;
|
|||
|
|
using MegaRobo.Utility.Files;
|
|||
|
|
using System;
|
|||
|
|
using System.Collections.Concurrent;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using System.IO;
|
|||
|
|
using System.Linq;
|
|||
|
|
using System.Net.Http;
|
|||
|
|
using System.Reflection;
|
|||
|
|
using System.Runtime.CompilerServices;
|
|||
|
|
using System.Security.Cryptography;
|
|||
|
|
using System.Text;
|
|||
|
|
using System.Text.RegularExpressions;
|
|||
|
|
using System.Threading.Tasks;
|
|||
|
|
using System.Windows.Controls;
|
|||
|
|
|
|||
|
|
namespace MegaRobo.C00225155.AppServer.ExecuteWorks;
|
|||
|
|
|
|||
|
|
public abstract class StationServiceBase : ModbusWorkServiceBase
|
|||
|
|
{
|
|||
|
|
#region 服务字段
|
|||
|
|
protected WorkService workService { get; set; }
|
|||
|
|
protected AppConfigService appConfigService;
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// PLC通讯的ModbusClient
|
|||
|
|
/// </summary>
|
|||
|
|
protected IModbusClientExtend ModbusClient;
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// PLC 名称
|
|||
|
|
/// </summary>
|
|||
|
|
protected DeviceNames PlcName;
|
|||
|
|
|
|||
|
|
protected IDataAccessService dataAccessService;
|
|||
|
|
|
|||
|
|
protected IDictionary<string, string> PipetteErrorDic = new Dictionary<string, string>();
|
|||
|
|
|
|||
|
|
//protected StationProperty StationProp = new StationProperty();
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region Field\Property
|
|||
|
|
/// <summary>
|
|||
|
|
/// 从数据库中加载的所有PLC报警点位
|
|||
|
|
/// </summary>
|
|||
|
|
protected IList<PlcPoint> WarnPointList { get; set; }
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 从CSV文件中加载的所有报警点位、报警信息
|
|||
|
|
/// </summary>
|
|||
|
|
protected List<PLCErrorPoint> PLCErrorInfoList { get; set; } = new List<PLCErrorPoint>();
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 当前报警信息,触发事件
|
|||
|
|
/// </summary>
|
|||
|
|
protected List<PLCErrorPoint> PLCErrorLists { get; set; } = new List<PLCErrorPoint>();
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
///扫描PLC 错误信息集合事件
|
|||
|
|
/// </summary>
|
|||
|
|
public event EventHandler<List<PLCErrorPoint>> OnScanPlcErrorListEvent;
|
|||
|
|
|
|||
|
|
protected bool Runing = false;
|
|||
|
|
|
|||
|
|
protected string stationName = string.Empty;
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
protected StationServiceBase(ICoreService coreService, WorkService workService, DeviceNames plcName, string stationName)
|
|||
|
|
{
|
|||
|
|
this.PlcName = plcName;
|
|||
|
|
this.stationName = stationName;
|
|||
|
|
this.CoreService = coreService;
|
|||
|
|
this.workService = workService;// 关联上级WorkService(便于跨工站交互)
|
|||
|
|
appConfigService = this.CoreService.GetServiceInstance<AppConfigService>();// 配置服务
|
|||
|
|
dataAccessService = this.CoreService.GetServiceInstance<IDataAccessService>();// 数据访问服务
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
protected override void LoadServices()
|
|||
|
|
{
|
|||
|
|
this.ModbusClient = this.CoreService.GetServiceInstance<IModbusClientExtend>(this.PlcName.ToString());
|
|||
|
|
if (this.ModbusClient is not null)
|
|||
|
|
{
|
|||
|
|
this.ModbusClient.ScannedEvent += this.ModbusClientBaseOnScannedEvent;
|
|||
|
|
this.ModbusClient.PulseTickAction += this.OnPulseTick;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
protected override void LoadDatas()
|
|||
|
|
{
|
|||
|
|
base.LoadDatas();
|
|||
|
|
RegisterPipetteErrorCode();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
protected override async void RegisterPoints()
|
|||
|
|
{
|
|||
|
|
//转移至 StartupService 统一注册
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
protected void RegisterPipetteErrorCode()
|
|||
|
|
{
|
|||
|
|
PipetteErrorDic = new Dictionary<string, string>()
|
|||
|
|
{
|
|||
|
|
{ "00", "执行成功" },
|
|||
|
|
{ "20", "硬件问题,请联系Hamilton"},
|
|||
|
|
{ "30", "未知命令,请参考命令表"},
|
|||
|
|
{ "31", "未知参数,请参考命令表"},
|
|||
|
|
{ "32", "发送的参数值超出预定范围,请参考命令表"},
|
|||
|
|
{ "35", "电源电压超出运行范围,请确保电源电压是48V+-5%"},
|
|||
|
|
{ "36", "关闭紧急停止"},
|
|||
|
|
{ "38", "空液体类型"},
|
|||
|
|
{ "39", "液体类型写保护"},
|
|||
|
|
{ "40", "一次只能发送一个命令"},
|
|||
|
|
{ "50", "Z轴初始化失败,硬件问题,请联系Hamilton"},
|
|||
|
|
{ "51", "请初始化泵驱动"},
|
|||
|
|
{ "52", "泵驱动初始化失败,硬件问题,请联系Hamilton"},
|
|||
|
|
{ "53", "tip 头容量不够,无法吸入ai 值得液体,改变ai值或更改tip 头"},
|
|||
|
|
{ "54", "达到泵的最大值"},
|
|||
|
|
{ "55", "吸液体积校验失败,吸液量不满足当前Tip头的最小值"},
|
|||
|
|
{ "56", "选择的液体类型要求用导电tip 头,请确认液体类型和tip 头类型编号一致"},
|
|||
|
|
{ "57", "确定液体类型一致和tip 头类型编号"},
|
|||
|
|
{ "60", "ZI初始化失败,硬件未工作,请联系Hamilton"},
|
|||
|
|
{ "61", "Z轴驱动初始化失败"},
|
|||
|
|
{ "62", "Z 轴驱动移动错误 硬件未工作,请联系Hamilton"},
|
|||
|
|
{ "63", "移动碰触到试管底部"},
|
|||
|
|
{ "64", "Z 轴,驱动达不到gy(要移动到的位置) 值,请检查参数"},
|
|||
|
|
{ "65", "Z 轴,驱动达不到te(转移高度)值,请检查参数"},
|
|||
|
|
{ "66", "Z 轴,驱动达不到ce(容器底部高度) 值,请检查参数"},
|
|||
|
|
{ "67", "Z 轴,驱动达不到cf(液面的高度) 值,请检查参数"},
|
|||
|
|
{ "68", "Z 轴,驱动达不到zp(开始探测液面的位置) 值,请检查参数"},
|
|||
|
|
{ "69", "Z 轴,驱动达不到tr(丢弃Tip头位置) 值,请检查参数"},
|
|||
|
|
{ "70", "在zp 和ce 两个值之间未检测到液面,请检查参数设置"},
|
|||
|
|
{ "71", "液体不足"},
|
|||
|
|
{ "72", "压力传感器自动校准失效,硬件未工作,请联系Hamilton"},
|
|||
|
|
{ "74", "提前检测到液面"},
|
|||
|
|
{ "75", "未取到tip 头"},
|
|||
|
|
{ "76", "tip 头已经取到"},
|
|||
|
|
{ "80", "吸液过程中检测到堵塞"},
|
|||
|
|
{ "81", "吸液过程中检测到吸空"},
|
|||
|
|
{ "82", "吸液过程中检测到气泡"},
|
|||
|
|
{ "83", "分配过程中检测到堵塞"},
|
|||
|
|
{ "84", "分配过程中检测到气泡"},
|
|||
|
|
{ "85", "未与数字电平通信上,硬件问题,请联系Hamilton"},
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
protected override void LoadPlcWarnInfoSetting()
|
|||
|
|
{
|
|||
|
|
base.LoadPlcWarnInfoSetting();
|
|||
|
|
this.PLCErrorInfoList.Clear();
|
|||
|
|
WarnPointList = new List<PlcPoint>();
|
|||
|
|
string filePath = string.Empty;
|
|||
|
|
if (this.PlcName == DeviceNames.Plc1)//投料站
|
|||
|
|
{
|
|||
|
|
filePath = "DataBox\\PlcWarnSetting_Dose.csv";
|
|||
|
|
}
|
|||
|
|
else if (this.PlcName.Equals(DeviceNames.Plc2)) //反应站
|
|||
|
|
{
|
|||
|
|
filePath = "DataBox\\PlcWarnSetting_React.csv";
|
|||
|
|
}
|
|||
|
|
string[] lines = File.ReadAllLines(filePath, System.Text.Encoding.GetEncoding("utf-8"));
|
|||
|
|
|
|||
|
|
foreach (string line in lines)
|
|||
|
|
{
|
|||
|
|
string key = string.Empty;
|
|||
|
|
string[] columns = line.Split(',');
|
|||
|
|
if (columns.Length != 2)
|
|||
|
|
{
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
key = columns[0].Replace("MX", "");
|
|||
|
|
string value = columns[1];
|
|||
|
|
if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(value)) continue;
|
|||
|
|
if (!double.TryParse(key, out double _point)) continue;
|
|||
|
|
|
|||
|
|
PLCErrorPoint point = new PLCErrorPoint() { ErrorAddress = key, ErrorInfo = value };
|
|||
|
|
this.PLCErrorInfoList.Add(point);
|
|||
|
|
}
|
|||
|
|
var alladdress = PLCErrorInfoList.Select(x => x.ErrorAddress.Split(".")[0]).Distinct().ToList();
|
|||
|
|
var client = this.CoreService.GetServiceInstance<IModbusClientExtend>("Plc1");
|
|||
|
|
foreach (var addr in alladdress)
|
|||
|
|
{
|
|||
|
|
client.Register<System.Drawing.Point>(ushort.Parse(addr), $"报警点位-{addr}", FunType.ReadHoldingRegisters);
|
|||
|
|
PlcPoint plcPoint = new PlcPoint()
|
|||
|
|
{
|
|||
|
|
Address = ushort.Parse(addr), Descr = $"报警点位-{addr}", FunType = FunType.ReadHoldingRegisters
|
|||
|
|
};
|
|||
|
|
WarnPointList.Add(plcPoint);
|
|||
|
|
}
|
|||
|
|
//this.WarnPointList =
|
|||
|
|
//this.WarnPointList = this.dataAccessService.LoadEntitiesAsync<PlcPoint>(x => x.Name == this.PlcName.ToString() && x.Level == 2).Result;
|
|||
|
|
this.Runing = true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
protected string GetMethodName([CallerMemberName] string memberName = "")
|
|||
|
|
{
|
|||
|
|
return memberName;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//protected virtual async Task InitPreAsync()
|
|||
|
|
//{
|
|||
|
|
// await Task.Delay(0);
|
|||
|
|
//}
|
|||
|
|
|
|||
|
|
#region PLC读取/写入值操作
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
private ushort _lastTickValue = 1; //先缓存, 尽量减少通讯次数
|
|||
|
|
protected override void OnPulseTick()
|
|||
|
|
{
|
|||
|
|
var currValue = (ushort)(DateTime.Now.Second % 2); //间隔两秒给一次心跳
|
|||
|
|
if (currValue == 0)
|
|||
|
|
{
|
|||
|
|
//this._lastTickValue = currValue;
|
|||
|
|
this.ModbusClient.Command.WriteValue<ushort>(PlcPoints_MaterialDose.W_PCHeartPluse[(ushort)this.PlcName], 1);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#region ModBus
|
|||
|
|
|
|||
|
|
protected async void LoadPlcAlarmInfoSetting()
|
|||
|
|
{
|
|||
|
|
//注册提示点位
|
|||
|
|
// this.WarnPointList = await this.DataAccessService.LoadEntitiesAsync<PlcPoint>(x =>
|
|||
|
|
// x.Name == this.PlcName.ToString() && x.Level == 3);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//protected IList<PlcPoint> ScanPlcAlarmInfoList(int? takeCount = null)
|
|||
|
|
//{
|
|||
|
|
// return new List<PlcPoint>();
|
|||
|
|
//}
|
|||
|
|
|
|||
|
|
private DateTime lastWarnDateTime = DateTime.Now;
|
|||
|
|
|
|||
|
|
//public Dictionary<ushort, ushort> WarnDictionary = new Dictionary<ushort, ushort>();
|
|||
|
|
|
|||
|
|
protected virtual void ModbusClientBaseOnScannedEvent(object sender, EventArgs e)
|
|||
|
|
{
|
|||
|
|
if (!this.Runing)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
DateTime now = DateTime.Now;
|
|||
|
|
if ((now - this.lastWarnDateTime).Seconds >= 5)
|
|||
|
|
{
|
|||
|
|
this.lastWarnDateTime = now;
|
|||
|
|
ScanPLCWarnList();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public override IList<PlcPoint> ScanPlcWarnInfoList(int? takeCount = null)
|
|||
|
|
{
|
|||
|
|
IList<PlcPoint> warnPointList = new List<PlcPoint>();
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
foreach (var plcPoint in this.WarnPointList)
|
|||
|
|
{
|
|||
|
|
//这里的错误报警信息都是1个字,2个字节,16位
|
|||
|
|
var pointInfo = this.ModbusClient.ReadScannedValue<ushort>(plcPoint.Address);
|
|||
|
|
if (pointInfo.Value != 0)
|
|||
|
|
{
|
|||
|
|
//if (!this.WarnDictionary.ContainsKey(pointInfo.Address) || this.WarnDictionary[pointInfo.Address] != pointInfo.Value)
|
|||
|
|
//{
|
|||
|
|
// this.Logger.LogError($"{this.stationName}异常点位[{pointInfo.Address}]值[{pointInfo.Value}]");
|
|||
|
|
// this.WarnDictionary[pointInfo.Address] = pointInfo.Value;
|
|||
|
|
//}
|
|||
|
|
//pointInfo.LastValue = pointInfo.Value.ToString();
|
|||
|
|
warnPointList.Add(pointInfo);
|
|||
|
|
}
|
|||
|
|
// else
|
|||
|
|
// {
|
|||
|
|
// //复位直接清空 不处理
|
|||
|
|
// if(this.WranDictionary.ContainsKey(pointInfo.Address))
|
|||
|
|
// this.WranDictionary.Remove(pointInfo.Address);
|
|||
|
|
// }
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
this.Logger.LogError($"{this.stationName}异常信息[{ex.Message}]");
|
|||
|
|
}
|
|||
|
|
return warnPointList;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 扫描PLC报警信息,触发事件
|
|||
|
|
/// </summary>
|
|||
|
|
private void ScanPLCWarnList()
|
|||
|
|
{
|
|||
|
|
PLCErrorLists.Clear();
|
|||
|
|
foreach (var plcPoint in WarnPointList)
|
|||
|
|
{
|
|||
|
|
var pointInfo = this.ModbusClient.ReadScannedValue<ushort>(plcPoint.Address);
|
|||
|
|
bool isError = false;
|
|||
|
|
if (pointInfo.Value != 0)
|
|||
|
|
{
|
|||
|
|
for (int i = 0; i < 16; i++)
|
|||
|
|
{
|
|||
|
|
if (((pointInfo.Value >> i) & 1) == 1)
|
|||
|
|
{
|
|||
|
|
var addr = plcPoint.Address.ToString() + "." + i.ToString();
|
|||
|
|
var point = PLCErrorInfoList.FirstOrDefault(p => p.ErrorAddress.Equals(addr));
|
|||
|
|
if (point != null)
|
|||
|
|
{
|
|||
|
|
isError = !point.ErrorInfo.Contains("提示");
|
|||
|
|
PLCErrorLists.Add(point);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (isError)
|
|||
|
|
{
|
|||
|
|
//给BS那边只需要给报警信息
|
|||
|
|
PLCErrorLists.Add(new PLCErrorPoint() { ErrorAddress = plcPoint.Address.ToString(), ErrorInfo = pointInfo.Value.ToString() }); //这边定义好传给BS的值
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (PLCErrorLists.Count > 0)
|
|||
|
|
{
|
|||
|
|
this.OnScanPlcErrorListEvent?.Invoke(this, PLCErrorLists);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 用于移液枪取运行状态值
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="resultString"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
protected string GetPipetteReturnCode(string resultString)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
string pattern = @"er(\d{2})";
|
|||
|
|
MatchCollection matches = Regex.Matches(resultString, pattern);
|
|||
|
|
return matches[0].Groups[1].Value;
|
|||
|
|
}
|
|||
|
|
catch (Exception)
|
|||
|
|
{
|
|||
|
|
return resultString;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|