using System ; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Net; using System.Text; namespace FLY.Modbus { /// /// 学习HMI, 根据 property 与 PLC寄存器 映射表 自动更新 /// /// 使用说明: /// 1.注册 NameDataChanged 事件, 当从PLC读取的数据更新后,通知property修改 /// NameDataChanged += ...... /// /// 2.填写 property 与 PLC寄存器 映射表 /// DRMap /// /// 3.通过FindDataMap() 找到 映射 注册需要更新的属性 /// dr = FindDataMap(obj, propertyName) /// key = 页面对应obj /// PlanAdd(key, dr) /// /// 4.创建更新计划 /// PlanMake() /// /// 5.页面关闭,不需要再更新 property, 注销更新计划 /// key = 页面对应obj /// PlanRemove(key) /// /// 6.当property 修改后,调用 SetNameData 设置PLC /// SetNameData(obj, propertyName, propertyValue) /// public class ModbusMapper_Client : ModbusMapper { /// /// 更新周期,单位ms, /// public int UpdateInterval = 200; /// /// 实际更新间隔 /// public TimeSpan ActUpdateInterval { get; protected set; } private Stopwatch mStopwatch = new Stopwatch(); class RegWrite { public DataToRegs dr; public object val; public RegWrite(DataToRegs dr, object val) { this.dr = dr; this.val = val; } } List rws = new List(); /// /// tcp client /// public ClientTCP mclient; /// /// /// /// public ModbusMapper_Client(ClientTCP clienttcp) { mclient = clienttcp; FObjBase.PollModule.Current.Poll_Config(OnPoll_update); mclient.PropertyChanged += Mclient_PropertyChanged; } public override string ToString() { return mclient.ToString(); } private void Mclient_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { if (e.PropertyName == "IsConnected") { if (!mclient.IsConnected) { foreach (var areaManager in mAreaManager) areaManager.ClearPlanState(); //复位全部状态 isInCommunication = false; p_doing = null; isUpdating = false; curr_area_idx = 0; // DRMap 的数据 与 下端 属性的数据一致 //foreach (var areaManager in mAreaManager) // areaManager.UpdatePLCObjFromProperty(); //rws.Clear(); mStopwatch.Stop(); } else { foreach (var areaManager in mAreaManager) areaManager.UpdatePLCObjFromProperty(); rws.Clear(); } } } void OnPoll_update() { if (!mclient.IsConnected) return; if (!mStopwatch.IsRunning) mStopwatch.Restart(); if (mStopwatch.ElapsedMilliseconds >= UpdateInterval) { //检查上次更新周期是否完成 if (!isUpdating) { isUpdating = true; ActUpdateInterval = mStopwatch.Elapsed; mStopwatch.Restart(); foreach (var areaManager in mAreaManager) { areaManager.MakePlanReal(); } } } NextPlan(); } int curr_area_idx = 0; void NextPlan() { if (isInCommunication)//已经在通讯中 return; if (rws.Count() > 0) UpdateWriteData(); if (isUpdating) { p_doing = mAreaManager[curr_area_idx].GetNextPlan(); if (p_doing != null) { p_doing.isDoing = false; UpdateReadData(); } else { //读完 curr_area_idx++; if (curr_area_idx >= mAreaManager.Count()) { isUpdating = false; //读完一轮了 curr_area_idx = 0; } else { NextPlan(); } } } } /// /// 读 多个线圈回调 /// /// /// void Do_01_callback(bool[] val, object obj) { Plan p = obj as Plan; p.isDoing = false; SetModbusData(PLCAddressArea.Coil, p.addr, val); isInCommunication = false; NextPlan(); } /// /// 读多个寄存器回调 /// /// /// void Do_03_callback(UInt16[] val, object obj) { Plan p = obj as Plan; p.isDoing = false; SetModbusData(PLCAddressArea.Register, p.addr, val); isInCommunication = false; NextPlan(); } void Do_0F_callback(object obj) { isInCommunication = false; NextPlan(); } void Do_10_callback(object obj) { isInCommunication = false; NextPlan(); } /// /// 正在通讯中 /// bool isInCommunication = false; /// /// 读更新流程中 /// bool isUpdating = false; /// /// 读更新 的计划 /// Plan p_doing = null; ///// ///// ///// ///// //protected void NotifyPropertyChanged(string propertyname) //{ // PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyname)); //} ///// ///// ///// //public event PropertyChangedEventHandler PropertyChanged; /// /// PC -> PLC /// 为命名数据赋值,会改变Modbus的寄存器值,并产生通知 /// /// 数据映射 /// 数据值,不能为空 /// public override void SetNameData(DataToRegs dr, object val) { RegWrite rw; rw = rws.Find((c) => { return (c.dr == dr); }); if (rw != null) rws.Remove(rw); rw = new RegWrite(dr, val); rws.Add(rw); } /// /// 更新写命令缓冲区 /// void UpdateWriteData() { RegWrite rw = rws.First(); rws.Remove(rw); //更新 本地的 PLC 数据 rw.dr.value = rw.val; rw.dr.ToPLCObj(); switch (rw.dr.dataArea) { case PLCAddressArea.Coil: if (mclient.Do_0F((UInt16)rw.dr.addr, new bool[] { (bool)rw.dr.plc_value }, Do_0F_callback, null) != ModbusClient_Errno.OK) return; break; case PLCAddressArea.Register: if (mclient.Do_10((UInt16)rw.dr.addr, (UInt16[])rw.dr.plc_value, Do_10_callback, null) != ModbusClient_Errno.OK) return; break; } isInCommunication = true; } void UpdateReadData() { switch (p_doing.area) { case PLCAddressArea.Coil: mclient.Do_01((UInt16)p_doing.addr, (UInt16)p_doing.num, Do_01_callback, p_doing); break; case PLCAddressArea.Register: mclient.Do_03((UInt16)p_doing.addr, (UInt16)p_doing.num, Do_03_callback, p_doing); break; } p_doing.isDoing = true; isInCommunication = true; } #region 寄存器读取更新计划 Dictionary> plans = new Dictionary>(); /// /// 登记寄存器更新计划 /// /// /// public void PlanAdd(object key, DataToRegs dr) { if (plans.ContainsKey(key)) { if (!plans[key].Contains(dr)) plans[key].Add(dr); } else { plans.Add(key, new List()); plans[key].Add(dr); } } /// /// 删除寄存器更新计划 /// /// public void PlanRemove(object key) { plans.Remove(key); } /// /// 删除全部寄存器更新计划 /// public void PlanClear() { plans.Clear(); } /// /// 需要被更新的 寄存器数量 /// public int DRNeedUpdateCnt { get; private set; } /// /// 总寄存器数量 /// public int DRCnt { get; private set; } /// /// DRmap分配到 每个寄存器区 /// public void Build() { DRCnt = DRmap.Count; //TODO, 应该提前做!!!! foreach (var areaManager in mAreaManager) areaManager.BuildArray(DRmap); } /// /// 创建寄存器更新计划 /// public void PlanMake() { foreach (DataToRegs dr in DRmap) { dr.isNeedUpdate = false; } foreach (KeyValuePair> kv in plans) { foreach (DataToRegs dr in kv.Value) { dr.isNeedUpdate = true; } } DRNeedUpdateCnt = DRmap.Count((dr) => dr.isNeedUpdate); foreach (var areaManager in mAreaManager) areaManager.MakePlan(); } #endregion } }