ModbusMapper.cs 15.6 KB
Newer Older
潘栩锋's avatar
潘栩锋 committed
1 2 3 4 5 6 7 8 9 10 11
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;

namespace FLY.Modbus
{
    /// <summary>
    /// 命名数据到Modbus数据的映射
    /// </summary>
潘栩锋's avatar
潘栩锋 committed
12
    public abstract class ModbusMapper : INotifyPropertyChanged
潘栩锋's avatar
潘栩锋 committed
13 14 15 16
    {
        /// <summary>
        /// 一次读取的数据量
        /// </summary>
潘栩锋's avatar
潘栩锋 committed
17 18
        const int MaxRegsOfOneRead = 100;
        const int MaxCoilsOfOneRead = 900;
潘栩锋's avatar
潘栩锋 committed
19 20 21 22 23 24

        #region 数据变化通知
        /// <summary>
        /// 命名数据改变事件
        /// </summary>
        public event NameDataChangedEventHandler NameDataChanged;
潘栩锋's avatar
潘栩锋 committed
25
        protected void NotifyNameDataChanged(DataToRegs dr)// object dataclass, string dataname)
潘栩锋's avatar
潘栩锋 committed
26
        {
潘栩锋's avatar
潘栩锋 committed
27
            NameDataChanged?.Invoke(this, dr);// new NameDataChangedEventArgs(dataclass, dataname));
潘栩锋's avatar
潘栩锋 committed
28 29 30 31 32 33 34 35
        }
        
        public event PropertyChangedEventHandler PropertyChanged;


        #endregion

        /// <summary>
潘栩锋's avatar
潘栩锋 committed
36
        /// 数据映射表,根据这个表,以后自动更新PLC寄存器,或更新property
潘栩锋's avatar
潘栩锋 committed
37
        /// </summary>
潘栩锋's avatar
潘栩锋 committed
38
        public List<DataToRegs> DRmap = new List<DataToRegs>();
潘栩锋's avatar
潘栩锋 committed
39 40

        /// <summary>
潘栩锋's avatar
潘栩锋 committed
41
        /// 
潘栩锋's avatar
潘栩锋 committed
42
        /// </summary>
潘栩锋's avatar
潘栩锋 committed
43 44 45 46 47 48 49 50 51 52 53
        public ModbusMapper()
        {
            coilData = new CoilData(PLCAddressArea.Coil, MaxCoilsOfOneRead);
            registerData = new RegisterData(PLCAddressArea.Register, MaxRegsOfOneRead);
            mAreaManager = new List<AreaManager>
            {
                coilData,
                registerData
            };
        }
        #region 数据映射
潘栩锋's avatar
潘栩锋 committed
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93

        /// <summary>
        /// 查找数据-寄存器对应表
        /// </summary>
        public DataToRegs FindDataMap(object owner, string propertyName)
        {
            return DRmap.Find((dr) => { return (dr.propertyName == propertyName && dr.owner == owner); });
        }

        /// <summary>
        /// 数据映射到寄存器
        /// </summary>
        /// <param name="dataArea">modbus 寄存器类型</param>    
        /// <param name="addr">modbus 寄存器首地址</param> 
        /// <param name="type">plc 数据对应类型</param>
        /// <param name="scale">plc 数据放大倍数</param>
        /// <param name="obj">Property 所属对象</param>    
        /// <param name="propertyname">Property Name</param>   
        /// <returns></returns>
        public DataToRegs MapDataToRegs(PLCAddressArea dataArea, int addr, REG_TYPE type, double scale, object obj, string propertyname)
        {
            DataToRegs dr = FindDataMap(obj, propertyname);
            if (dr == null)
            {
                dr = new DataToRegs(this, dataArea, addr, type, scale, obj, propertyname);
                DRmap.Add(dr);
                DRmap.Sort(delegate (DataToRegs X, DataToRegs Y)
                {
                    int ct = X.dataArea.CompareTo(Y.dataArea);
                    if (ct != 0)
                        return ct;
                    
                    return X.addr.CompareTo(Y.addr);
                });
                return dr;
            }

            return null;
        }

潘栩锋's avatar
潘栩锋 committed
94

潘栩锋's avatar
潘栩锋 committed
95 96 97 98 99 100


        #endregion

        #region Modbus数据
        /// <summary>
潘栩锋's avatar
潘栩锋 committed
101
        /// 就是 coils 与 registers 的组合
潘栩锋's avatar
潘栩锋 committed
102
        /// </summary>
潘栩锋's avatar
潘栩锋 committed
103 104 105
        protected List<AreaManager> mAreaManager;
        protected CoilData coilData;
        protected RegisterData registerData;
潘栩锋's avatar
潘栩锋 committed
106 107 108 109 110 111 112
        #endregion

        #region 数据读写
        /// <summary>
        /// 为命名数据赋值,会改变Modbus的寄存器值,并产生通知
        /// </summary>
        /// <param name="obj">属性所属的对象, 可为null</param>
潘栩锋's avatar
潘栩锋 committed
113
        /// <param name="propertyName">属性名,不能为空</param>
潘栩锋's avatar
潘栩锋 committed
114 115 116 117 118 119 120
        /// <param name="val">数据值,不能为空</param>
        /// <returns>赋值的结果</returns>
        public virtual bool SetNameData(object obj, string propertyName, object val)
        {

            DataToRegs dr = FindDataMap(obj, propertyName);
            if (dr == null) return false;
潘栩锋's avatar
潘栩锋 committed
121 122
            SetNameData(dr, val);
            return true;
潘栩锋's avatar
潘栩锋 committed
123 124 125 126 127 128 129 130 131
        }

        /// <summary>
        /// PC想向PLC写入数据;
        /// 为命名数据赋值,会改变Modbus的寄存器值,并产生通知
        /// </summary>
        /// <param name="dr">数据映射</param>
        /// <param name="val">数据值,不能为空</param>
        /// <returns></returns>
潘栩锋's avatar
潘栩锋 committed
132 133
        public abstract void SetNameData(DataToRegs dr, object val);
        
潘栩锋's avatar
潘栩锋 committed
134 135 136 137 138 139 140 141 142 143 144 145

        /// <summary>
        /// 取命名数据的值
        /// </summary>
        /// <param name="obj">属性所属 的对象</param>
        /// <param name="propertyName">属性名</param>
        /// <returns>属性的值</returns>
        public virtual object GetNameData(object obj, string propertyName)
        {
            DataToRegs dr = FindDataMap(obj, propertyName);
            if (dr == null) return null;
            return dr.value;
潘栩锋's avatar
潘栩锋 committed
146
        }
潘栩锋's avatar
潘栩锋 committed
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164

        #region IModbusRegister实现


        /// <summary>
        /// 是从PLC读取数据后;
        /// 设置Modbus的值,会改变相应命名数据的值,并产生通知
        /// </summary>
        /// <param name="dataarea">coil or register</param>
        /// <param name="addr">地址</param>
        /// <param name="value">值</param>
        /// <returns></returns>
        public virtual void SetModbusData(PLCAddressArea dataarea, int addr, object value)
        {
            switch (dataarea)
            {
                case PLCAddressArea.Register:
                    {
潘栩锋's avatar
潘栩锋 committed
165
                        IEnumerable<UInt16> vals = (IEnumerable<UInt16>)value;
潘栩锋's avatar
潘栩锋 committed
166 167 168
                        //找寄存器
                        int endAddr = addr + vals.Count() - 1;

潘栩锋's avatar
潘栩锋 committed
169
                        RegisterData area_regs = registerData;
潘栩锋's avatar
潘栩锋 committed
170 171 172

                        for (int i = 0; i < area_regs.regs.Count(); i++)
                        {
潘栩锋's avatar
潘栩锋 committed
173 174
                            DataToRegs dr = area_regs.regs[i];
                            if (endAddr < dr.addr)
潘栩锋's avatar
潘栩锋 committed
175 176 177 178 179
                            {
                                //在前面

                                break;
                            }
潘栩锋's avatar
潘栩锋 committed
180
                            else if (addr <= dr.endAddr)//有交集
潘栩锋's avatar
潘栩锋 committed
181
                            {
潘栩锋's avatar
潘栩锋 committed
182 183 184 185
                                //计算交集
                                int addr_act = Math.Max(addr, dr.addr);
                                int endAddr_act = Math.Min(endAddr, dr.endAddr);
                                int num_act = endAddr_act - addr_act + 1;//不需要检测<=0, 当为0,下面的循环跑不动
潘栩锋's avatar
潘栩锋 committed
186 187 188

                                for (int j = 0; j < num_act; j++)
                                {
潘栩锋's avatar
潘栩锋 committed
189
                                    int idx1 = addr_act - dr.addr + j;
潘栩锋's avatar
潘栩锋 committed
190
                                    int idx2 = addr_act - addr + j;
潘栩锋's avatar
潘栩锋 committed
191 192
                                    UInt16[] plc_value = (UInt16[])dr.plcValue;
                                    if (plc_value[idx1] != vals.ElementAt(idx2))
潘栩锋's avatar
潘栩锋 committed
193
                                    {
潘栩锋's avatar
潘栩锋 committed
194 195
                                        plc_value[idx1] = vals.ElementAt(idx2);
                                        area_regs.regs[i].isPlcValueChanged = true;
潘栩锋's avatar
潘栩锋 committed
196 197
                                    }
                                }
潘栩锋's avatar
潘栩锋 committed
198
                                if (endAddr_act == dr.endAddr)
潘栩锋's avatar
潘栩锋 committed
199
                                {
潘栩锋's avatar
潘栩锋 committed
200
                                    if (dr.isPlcValueChanged)
潘栩锋's avatar
潘栩锋 committed
201
                                    {
潘栩锋's avatar
潘栩锋 committed
202 203
                                        dr.isPlcValueChanged = false;
                                        dr.ToPropertyObj();
潘栩锋's avatar
潘栩锋 committed
204
                                        //触发事件
潘栩锋's avatar
潘栩锋 committed
205
                                        NotifyNameDataChanged(dr);
潘栩锋's avatar
潘栩锋 committed
206 207 208 209 210 211 212 213 214 215 216
                                    }
                                }
                            }
                            else
                            {
                                //在后面
                            }
                        }
                    }break;
                case PLCAddressArea.Coil:
                    {
潘栩锋's avatar
潘栩锋 committed
217
                        IEnumerable<bool> vals = (IEnumerable<bool>)value;
潘栩锋's avatar
潘栩锋 committed
218 219 220
                        //找寄存器
                        int endAddr = addr + vals.Count() - 1;

潘栩锋's avatar
潘栩锋 committed
221
                        CoilData area_coils = coilData; 
潘栩锋's avatar
潘栩锋 committed
222 223 224

                        for (int i = 0; i < area_coils.regs.Count(); i++)
                        {
潘栩锋's avatar
潘栩锋 committed
225 226
                            DataToRegs dr = area_coils.regs[i];
                            if (endAddr < dr.addr)
潘栩锋's avatar
潘栩锋 committed
227 228 229 230 231
                            {
                                //在前面

                                break;
                            }
潘栩锋's avatar
潘栩锋 committed
232
                            else if (addr <= dr.addr)//有交集
潘栩锋's avatar
潘栩锋 committed
233
                            {
潘栩锋's avatar
潘栩锋 committed
234
                                int addr_act = dr.addr;
潘栩锋's avatar
潘栩锋 committed
235 236 237
                                
                                int idx2 = addr_act - addr;

潘栩锋's avatar
潘栩锋 committed
238
                                if ((bool)dr.plcValue != vals.ElementAt(idx2))
潘栩锋's avatar
潘栩锋 committed
239
                                {
潘栩锋's avatar
潘栩锋 committed
240 241
                                    dr.plcValue = vals.ElementAt(idx2);
                                    dr.ToPropertyObj();
潘栩锋's avatar
潘栩锋 committed
242
                                    //触发事件
潘栩锋's avatar
潘栩锋 committed
243
                                    NotifyNameDataChanged(dr);
潘栩锋's avatar
潘栩锋 committed
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
                                }
                            }
                            else
                            {
                                //在后面
                            }
                        }
                    }break;
            }
        }


        #endregion
        #endregion

        #region 类的静态函数
        /// <summary>
        /// C#类型,对应PLC寄存器个数
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static int RegSize(REG_TYPE type)
        {
            switch (type)
            {
                case REG_TYPE.FLOAT:
                    return sizeof(float) / sizeof(Int16);
                case REG_TYPE.UINT16:
                    return sizeof(UInt16) / sizeof(Int16);
                case REG_TYPE.INT32:
                    return sizeof(Int32) / sizeof(Int16);
                case REG_TYPE.INT16:
                    return sizeof(Int16) / sizeof(Int16);
                case REG_TYPE.UINT32:
                    return sizeof(Int32) / sizeof(Int16);
                default:
                    {
                        throw new NotImplementedException("不支持类型 " + type);
                    }
            }
        }


        /// <summary>
        /// string 转 C#类型
        /// </summary>
        /// <param name="typename"></param>
        /// <returns></returns>
        public static REG_TYPE TranslateToREG_TYPE(string typename)
        {
            typename = typename.ToLower();
            if (typename == "int32")
                return REG_TYPE.INT32;
            if (typename == "uint32")
                return REG_TYPE.UINT32;
            if (typename == "bool")
                return REG_TYPE.BOOL;
            if (typename == "int16")
                return REG_TYPE.INT16;
            if (typename == "uint16")
                return REG_TYPE.UINT16;
            if (typename == "float")
                return REG_TYPE.FLOAT;
            return REG_TYPE.Unknown;
        }
        /// <summary>
        /// string 转modbus类型
        /// </summary>
        /// <param name="dm"></param>
        /// <returns></returns>
        public static PLCAddressArea TranslateToPLCAddressArea(string dm)
        {
            if (dm.CompareTo("0") == 0) return PLCAddressArea.Coil;
            if (dm.CompareTo("1") == 0) return PLCAddressArea.Coil;
            if (dm.CompareTo("3") == 0) return PLCAddressArea.Register;
            if (dm.CompareTo("4") == 0) return PLCAddressArea.Register;
            else {
                throw new Exception("{0} 不是合法的 PLCAddressArea类型");
            }
        }
        #endregion

        #region 寄存器读取更新计划
        Dictionary<object, List<DataToRegs>> plans = new Dictionary<object, List<DataToRegs>>();
        /// <summary>
        /// 登记寄存器更新计划
        /// </summary>
        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<DataToRegs>());
                plans[key].Add(dr);
            }
        }
        /// <summary>
        /// 删除寄存器更新计划
        /// </summary>
        /// <param name="key"></param>
        public void PlanRemove(object key)
        {
            plans.Remove(key);
        }
        /// <summary>
        /// 删除全部寄存器更新计划
        /// </summary>
        public void PlanClear()
        {
            plans.Clear();
        }

        /// <summary>
        /// 需要被更新的 寄存器数量
        /// </summary>
        public int DRNeedUpdateCnt { get; private set; }
        /// <summary>
        /// 总寄存器数量
        /// </summary>
        public int DRCnt { get; private set; }
潘栩锋's avatar
潘栩锋 committed
368 369 370 371 372 373 374 375 376 377 378 379

        /// <summary>
        /// DRmap分配到 每个寄存器区
        /// </summary>
        public void Build()
        {
            DRCnt = DRmap.Count;

            //TODO, 应该提前做!!!!
            foreach (var areaManager in mAreaManager)
                areaManager.BuildArray(DRmap);
        }
潘栩锋's avatar
潘栩锋 committed
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396
        /// <summary>
        /// 创建寄存器更新计划
        /// </summary>
        public void PlanMake()
        {
            foreach (DataToRegs dr in DRmap)
            {
                dr.isNeedUpdate = false;
            }

            foreach (KeyValuePair<object, List<DataToRegs>> kv in plans)
            {
                foreach (DataToRegs dr in kv.Value)
                {
                    dr.isNeedUpdate = true;
                }
            }
潘栩锋's avatar
潘栩锋 committed
397 398 399 400
            //foreach (DataToRegs dr in DRmap)
            //{
            //    dr.isNeedUpdate = true;
            //}
潘栩锋's avatar
潘栩锋 committed
401 402
            DRNeedUpdateCnt = DRmap.Count((dr) => dr.isNeedUpdate);

潘栩锋's avatar
潘栩锋 committed
403 404
            foreach (var areaManager in mAreaManager)
                areaManager.MakePlan();
潘栩锋's avatar
潘栩锋 committed
405 406 407 408
        }
        #endregion
    }

潘栩锋's avatar
潘栩锋 committed
409

潘栩锋's avatar
潘栩锋 committed
410
    /// <summary>
潘栩锋's avatar
潘栩锋 committed
411
    /// PLC 地址空间, 线圈 or 寄存器
潘栩锋's avatar
潘栩锋 committed
412
    /// </summary>
潘栩锋's avatar
潘栩锋 committed
413
    public enum PLCAddressArea
潘栩锋's avatar
潘栩锋 committed
414 415
    {
        /// <summary>
潘栩锋's avatar
潘栩锋 committed
416
        /// 线圈
潘栩锋's avatar
潘栩锋 committed
417
        /// </summary>
潘栩锋's avatar
潘栩锋 committed
418
        Coil,
潘栩锋's avatar
潘栩锋 committed
419
        /// <summary>
潘栩锋's avatar
潘栩锋 committed
420
        /// 寄存器
潘栩锋's avatar
潘栩锋 committed
421
        /// </summary>
潘栩锋's avatar
潘栩锋 committed
422
        Register
潘栩锋's avatar
潘栩锋 committed
423 424
    }
    /// <summary>
潘栩锋's avatar
潘栩锋 committed
425
    /// 通知命名数据改变是的参数
潘栩锋's avatar
潘栩锋 committed
426
    /// </summary>
潘栩锋's avatar
潘栩锋 committed
427
    public class NameDataChangedEventArgs : EventArgs
潘栩锋's avatar
潘栩锋 committed
428 429 430
    {

        /// <summary>
潘栩锋's avatar
潘栩锋 committed
431
        /// 初始化 NameDataChangedEventArgs 类的新实例。
潘栩锋's avatar
潘栩锋 committed
432
        /// </summary>
潘栩锋's avatar
潘栩锋 committed
433 434 435
        /// <param name="owner">数据类</param>
        /// <param name="propertyName">已更改的数据名。</param>
        public NameDataChangedEventArgs(object owner, string propertyName)
潘栩锋's avatar
潘栩锋 committed
436
        {
潘栩锋's avatar
潘栩锋 committed
437 438 439
            this.owner = owner;
            this.propertyName = propertyName;
        }
潘栩锋's avatar
潘栩锋 committed
440

潘栩锋's avatar
潘栩锋 committed
441 442
        object owner;
        string propertyName;
潘栩锋's avatar
潘栩锋 committed
443

潘栩锋's avatar
潘栩锋 committed
444 445 446 447 448 449 450 451
        /// <summary>
        ///  获取数据所属类
        /// </summary>
        public object Owener
        {
            get
            {
                return owner;
潘栩锋's avatar
潘栩锋 committed
452 453 454 455
            }
        }

        /// <summary>
潘栩锋's avatar
潘栩锋 committed
456
        /// 获取已更改的数据名名。
潘栩锋's avatar
潘栩锋 committed
457
        /// </summary>
潘栩锋's avatar
潘栩锋 committed
458
        public string PropertyName
潘栩锋's avatar
潘栩锋 committed
459
        {
潘栩锋's avatar
潘栩锋 committed
460
            get
潘栩锋's avatar
潘栩锋 committed
461
            {
潘栩锋's avatar
潘栩锋 committed
462
                return propertyName;
潘栩锋's avatar
潘栩锋 committed
463 464 465 466
            }
        }
    }

潘栩锋's avatar
潘栩锋 committed
467 468 469 470 471 472
    /// <summary>
    /// NameData 改变事件
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    public delegate void NameDataChangedEventHandler(object sender, DataToRegs e);// NameDataChangedEventArgs e);
潘栩锋's avatar
潘栩锋 committed
473 474

}