using FLY.OBJComponents.IService; using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using FLY.Modbus; using System.Windows.Threading; namespace FLY.OBJComponents.Server { /// <summary> /// 属性更新计划 /// </summary> public class PLCProxySystem : IPLCProxySystemService, IPropertyOpt { class Plan { /// <summary> /// 唯一ID /// </summary> public long ID; /// <summary> /// 上一次喂的时间 /// </summary> public DateTime FeedTime; /// <summary> /// plc /// </summary> public List<ModbusMapper> mappers = new List<ModbusMapper>(); } List<Plan> planIDs = new List<Plan>(); public List<DataToRegs> DRMap = new List<DataToRegs>(); public List<Modbus.WithThread.ModbusMapper_Client> PLCs = new List<Modbus.WithThread.ModbusMapper_Client>(); public Dictionary<string, INotifyPropertyChanged> ObjNames { get; } = new Dictionary<string, INotifyPropertyChanged>(); /// <summary> /// 与PLC连接成功 /// </summary> public bool IsConnectedWithPLC { get; private set; } public event PropertyChangedEventHandler PropertyChanged; public PLCProxySystem() { } public void Init() { foreach (var plc in PLCs) { plc.NameDataChanged += plc_NameDataChanged; } Misc.BindingOperations.SetBinding(PLCs[0].Client, "IsConnected", () => { IsConnectedWithPLC = PLCs[0].Client.IsConnected; //if (!IsConnectedWithPLC) // ClearPlan(); }); foreach (var obj in ObjNames.Values) { obj.PropertyChanged += Obj_PropertyChanged; } FObjBase.PollModule.Current.Poll_Config(FObjBase.PollModule.POLL_CONFIG.ADD, OnPoll_plans, TimeSpan.FromSeconds(1)); //启动!!!! foreach (var plc in PLCs) plc.Start(); } private void Obj_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (isShield) return; List<DataToRegs> drs = DRMap; DataToRegs dr = drs.Find((_dr) => { return (_dr.propertyName == e.PropertyName) && (_dr.owner == sender); }); if (dr == null) return;//不会刷新PLC //向PLC写入数据,mRegs每次只能写入一个,它有列表保存功能。 //SetNameData可以执行很多次,不过它只能一个个发。 dr.mapper.SetNameData(dr, Misc.PropertiesManager.GetValue(sender, e.PropertyName)); } bool isShield = false; void plc_NameDataChanged(object sender, DataToRegs dr)// NameDataChangedEventArgs e) { //var plc = sender as Modbus.WithThread.ModbusMapper_Client; //object value = plc.GetNameData(e.Owener, e.PropertyName); //这是从PLC 更新过来的,不需要向PLC写入 FObjBase.PollModule.Current.Dispatcher.Invoke(new SetValueHandler((owner, propertyname, value) => { isShield = true; Misc.PropertiesManager.SetValue(dr.owner, dr.propertyName, dr.value); isShield = false; }), dr.owner, dr.propertyName, dr.value); } delegate void SetValueHandler(object owner, string propertyname, object value); /// <summary> /// 设置更新计划 /// </summary> /// <param name="objname">对象名称</param> /// <param name="propertynames"></param> /// <param name="planID">计划的编号,应该全局唯一,建议使用时间ticks</param> public void SetPlan(string objname, IEnumerable<string> propertynames, long planID) { Plan plan = planIDs.Find(p => p.ID == planID); if (plan == null) { plan = new Plan() { ID = planID, FeedTime = DateTime.Now }; planIDs.Add(plan); } else { plan.FeedTime = DateTime.Now; } List<DataToRegs> drs = DRMap; foreach (string propertyname in propertynames) { if (!ObjNames.ContainsKey(objname)) continue; DataToRegs dr = drs.Find((_dr) => { return (_dr.propertyName == propertyname && _dr.owner == ObjNames[objname]); }); if (dr == null) { continue; //throw new Exception($"PLCProxySystem.SetPlan 不能找到 {objname}.{propertyname}"); } dr.mapper.PlanAdd(planID, dr); if (!plan.mappers.Contains(dr.mapper)) plan.mappers.Add(dr.mapper); } foreach (var mapper in plan.mappers) { mapper.PlanMake(); } } long freeplanid = 1; long GetFreePlanID() { long planid = freeplanid; freeplanid++; if (freeplanid == 0) freeplanid = 1; return planid; } /// <summary> /// 设置更新计划 /// </summary> /// <param name="objname">对象名称</param> /// <param name="propertynames"></param> /// <param name="planID">计划的编号,应该全局唯一,建议使用时间ticks</param> public void SetPlan(string objname, IEnumerable<string> propertynames, SetPlanReponseHandler reponseHandler, object context) { long planID = GetFreePlanID(); SetPlan(objname, propertynames, planID); reponseHandler(planID, context); } /// <summary> /// 更新计划持续,如果不喂狗,20s后停止更新数据 /// </summary> /// <param name="planID">计划的编号</param> public void FeedPlan(long planID) { Plan plan = planIDs.Find(p => p.ID == planID); if (plan != null) { plan.FeedTime = DateTime.Now; } } /// <summary> /// 主动删除某个计划 /// </summary> /// <param name="planID"></param> public void RemovePlan(long planID) { ClearPlan((plan) => (plan.ID == planID)); } /// <summary> /// 清除 非0 全部计划 /// </summary> void ClearPlan() { //if (planIDs.Count() > 0) //{ // foreach (var plc in PLCs) // { // plc.PlanClear(); // plc.PlanMake(); // } // planIDs.Clear(); //} ClearPlan((plan) => (plan.ID != 0)); } void ClearPlan(Func<Plan, bool> condition) { if (planIDs.Count() > 0) { var _remove = from plan in planIDs where condition(plan) select plan; if (_remove.Count() > 0) { List<Plan> remove = new List<Plan>(_remove); List<ModbusMapper> mappers = new List<ModbusMapper>(); foreach (var plan in remove) { planIDs.Remove(plan); foreach (var mapper in plan.mappers) { mapper.PlanRemove(plan.ID); if (!mappers.Contains(mapper)) mappers.Add(mapper); } } foreach (var mapper in mappers) { mapper.PlanMake(); } } } } /// <summary> /// 1s 一次,看出哪个令牌过期 /// </summary> void OnPoll_plans() { DateTime now = DateTime.Now; ClearPlan((plan) => (plan.ID != 0) && (now - plan.FeedTime) > TimeSpan.FromMinutes(2)); } public string[] GetSyncPropNames() { return new string[] { "IsConnectedWithPLC" }; } public string[] GetNoSyncPropNames() { return null; } } }