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; /// /// PLC通讯的ModbusClient /// protected IModbusClientExtend ModbusClient; /// /// PLC 名称 /// protected DeviceNames PlcName; protected IDataAccessService dataAccessService; protected IDictionary PipetteErrorDic = new Dictionary(); //protected StationProperty StationProp = new StationProperty(); #endregion #region Field\Property /// /// 从数据库中加载的所有PLC报警点位 /// protected IList WarnPointList { get; set; } /// /// 从CSV文件中加载的所有报警点位、报警信息 /// protected List PLCErrorInfoList { get; set; } = new List(); /// /// 当前报警信息,触发事件 /// protected List PLCErrorLists { get; set; } = new List(); /// ///扫描PLC 错误信息集合事件 /// public event EventHandler> 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();// 配置服务 dataAccessService = this.CoreService.GetServiceInstance();// 数据访问服务 } protected override void LoadServices() { this.ModbusClient = this.CoreService.GetServiceInstance(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() { { "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(); 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("Plc1"); foreach (var addr in alladdress) { client.Register(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(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(PlcPoints_MaterialDose.W_PCHeartPluse[(ushort)this.PlcName], 1); } } #region ModBus protected async void LoadPlcAlarmInfoSetting() { //注册提示点位 // this.WarnPointList = await this.DataAccessService.LoadEntitiesAsync(x => // x.Name == this.PlcName.ToString() && x.Level == 3); } //protected IList ScanPlcAlarmInfoList(int? takeCount = null) //{ // return new List(); //} private DateTime lastWarnDateTime = DateTime.Now; //public Dictionary WarnDictionary = new Dictionary(); 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 ScanPlcWarnInfoList(int? takeCount = null) { IList warnPointList = new List(); try { foreach (var plcPoint in this.WarnPointList) { //这里的错误报警信息都是1个字,2个字节,16位 var pointInfo = this.ModbusClient.ReadScannedValue(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; } /// /// 扫描PLC报警信息,触发事件 /// private void ScanPLCWarnList() { PLCErrorLists.Clear(); foreach (var plcPoint in WarnPointList) { var pointInfo = this.ModbusClient.ReadScannedValue(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 /// /// 用于移液枪取运行状态值 /// /// /// 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; } } }