using IACommService4CSharp; using Model; using PlcCom; using PlcComponent; using StandardLibrary; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Xml.Linq; namespace YangjieTester { public class BackgroundService { public static BackgroundService Instance { get; set; } public PlcDataMonitor MachineStateMonitor { get; private set; } public PlcDataMonitor AlarmMonitor { get; private set; } public PlcDataMonitor TipsMonitor { get; private set; } public IPlcComProtocol Protocol; private bool IsServiceAlive; private BackgroundService() { } public BackgroundService(IPlcComProtocol plcComProtocol):this() { this.Protocol = plcComProtocol; MachineStateMonitorInit(); AlarmMonitorInit(); TipsMonitorInit(); } public bool Start() { if (IsServiceAlive) return false; StartAlarmTipsMonitor(0,200); IsServiceAlive = true; return true; } public void Stop() { if (IsServiceAlive) { StopAlarmTipsMonitor(); } } void MachineStateMonitorInit() { MachineStateMonitor = new PlcDataMonitor(); MachineStateMonitor.Protocol = Protocol; MachineStateMonitor.ReadNodes = AppSession.MachineState.PlcAddressPropertyPathsPairs .Select(n => new Node(n.Key)) .ToList(); MachineStateMonitor.OnMonitoredValuesChanged += MachineStateMonitor_OnMonitoredValuesChanged; } private void MachineStateMonitor_OnMonitoredValuesChanged(List<(Node node, object newValue)> obj) { foreach (var item in obj) { if (AppSession.MachineState.PlcAddressPropertyPathsPairs.TryGetValue(item.node.Value, out List propertyPaths)) { foreach (var propertyPath in propertyPaths) { ObjectPropertySetter.SetPropertyValue(AppSession.MachineState, propertyPath, item.newValue); } } } } void AlarmMonitorInit() { AlarmMonitor = new PlcDataMonitor(); AlarmMonitor.Protocol=Protocol; AlarmMonitor.MonitorRules = Alarm.AlarmNodeWithMessage.Select( a => new MonitorRule() { Node = new Node(a.AlarmNode), Message = a.AlarmMessage, TargetValue = a.DesiredValue, CompareType= CompareType.ToTarget //MonitorType = MonitorType.RisingOrFallingEdge }). ToList(); AlarmMonitor.OnDataTriggered += AlarmMonitor_OnDataTriggered; AlarmMonitor.OnDataCleared += AlarmMonitor_OnDataCleared; } private void AlarmMonitor_OnDataTriggered(object arg1, List arg2) { AlarmService.LogBatch(arg2.Select(a => new Alarm() { AlarmMessage = a.Rule.Message, AlarmType = AlarmType.Error, Timestamp = a.Timestamp }), AppSession.CurrentUser); //AlarmService.Log(new Alarm() { AlarmMessage = arg2.Rule.Message, AlarmType = AlarmType.Error, Timestamp = arg2.Timestamp }, AppSession.CurrentUser); } private void AlarmMonitor_OnDataCleared(object arg1, List arg2) { AlarmService.LogBatch(arg2.Select(a => new Alarm() { AlarmMessage = a.Rule.Message, AlarmType = AlarmType.Cleared, Timestamp = a.Timestamp }), AppSession.CurrentUser); } void TipsMonitorInit() { TipsMonitor = new PlcDataMonitor(); TipsMonitor.Protocol = Protocol; TipsMonitor.MonitorRules = Tips.TipsNodeWithMessage.Select( t => new MonitorRule() { Node = new Node(t.Node), Message = t.Message, TargetValue = t.DesiredValue, MonitorType = MonitorType.RisingOrFallingEdge }). ToList(); TipsMonitor.OnDataTriggered += TipsMonitor_OnDataTriggered; TipsMonitor.OnDataCleared += TipsMonitor_OnDataCleared; } private void TipsMonitor_OnDataTriggered(object arg1, List arg2) { TipsService.LogBatch(arg2.Select(a => new Tips() { Message = a.Rule.Message, Type = TipsType.Common, Timestamp = a.Timestamp }), AppSession.CurrentUser); ////TipsService.Log(new Tips() { Message = arg2.Rule.Message, Type = TipsType.Common , Timestamp = arg2.Timestamp }, AppSession.CurrentUser); } private void TipsMonitor_OnDataCleared(object arg1, List arg2) { TipsService.LogBatch(arg2.Select(a => new Tips() { Message = a.Rule.Message, Type = TipsType.Cleared, Timestamp = a.Timestamp }), AppSession.CurrentUser); ////TipsService.Log(new Tips() { Message = arg2.Rule.Message, Type = TipsType.Cleared, Timestamp = arg2.Timestamp }, AppSession.CurrentUser); } Timer _timerMonitor; private void StartAlarmTipsMonitor(int dueTime,int interval) { if (_timerMonitor != null) return; // 避免重复创建 var invalidNodes= AlarmMonitor.RemoveInvalidNodes();//剔除掉地址错误的节点 foreach (var item in invalidNodes) { string err = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + $" {this.GetType().Name}->AlarmMonitor->{item},读取失败"; AppSession.ReadWriteErrors.Add(err); } invalidNodes = TipsMonitor.RemoveInvalidNodes();//剔除掉地址错误的节点 foreach (var item in invalidNodes) { string err = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + $" {this.GetType().Name}->TipsMonitor->{item},读取失败"; AppSession.ReadWriteErrors.Add(err); } invalidNodes = MachineStateMonitor.RemoveInvalidNodes();//剔除掉地址错误的节点 foreach (var item in invalidNodes) { string err = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + $" {this.GetType().Name}->TipsMonitor->{item},读取失败"; AppSession.ReadWriteErrors.Add(err); } // 创建 Timer // dueTime: intervalms 后第一次执行 // period: 之后每 intervalms 执行一次 _timerMonitor = new Timer( callback: OnTimerElapsed, state: null, dueTime: dueTime, period: interval ); } private int _isExecuting = 0; // 0 = 空闲, 1 = 正在执行 private void OnTimerElapsed(object state) { // ✅ 原子操作:尝试设置“正在执行” if (Interlocked.CompareExchange(ref _isExecuting, 1, 0) == 0) { try { // ✅ 只有获得锁的线程才能执行 // 这个方法运行在**线程池线程**上 AlarmMonitor.Read(); TipsMonitor.Read(); MachineStateMonitor.Read(); } finally { // ✅ 释放:设置为 0 Interlocked.Exchange(ref _isExecuting, 0); } } } private void StopAlarmTipsMonitor() { if (_timerMonitor != null) { _timerMonitor.Dispose(); // 或 _timerMonitor.Change(Timeout.Infinite, 0); _timerMonitor = null; } AlarmMonitor.OnDataTriggered -= AlarmMonitor_OnDataTriggered; AlarmMonitor.OnDataCleared -= AlarmMonitor_OnDataCleared; } public bool RefreshInstanceFromPLC(IPlcAddressToPropertyPaths instance) { PlcDataMonitor monitor = new PlcDataMonitor(); monitor.Protocol = Protocol; monitor.ReadNodes = instance.PlcAddressPropertyPathsPairs .Select(n => new Node(n.Key)) .ToList(); var results = monitor.Read(); if (results != null) { foreach (var kvp in monitor.NodeStringValuePairs) { //if (batch.PlcAddressPropertyPathPairs.TryGetValue(kvp.Key, out string propertyPath)) //{ // ObjectPropertySetter.SetPropertyValue(batch, propertyPath, kvp.Value); //} if (instance.PlcAddressPropertyPathsPairs.TryGetValue(kvp.Key, out List propertyPaths)) { foreach (var propertyPath in propertyPaths) { ObjectPropertySetter.SetPropertyValue(instance, propertyPath, kvp.Value); } } } return true; } else { return false; } } } }