using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
namespace FLY.Modbus
{
///
/// 命名数据到Modbus数据的映射
///
public abstract class ModbusMapper : INotifyPropertyChanged
{
///
/// 一次读取的数据量
///
const int MaxRegsOfOneRead = 100;
const int MaxCoilsOfOneRead = 900;
#region 数据变化通知
///
/// 命名数据改变事件
///
public event NameDataChangedEventHandler NameDataChanged;
protected void NotifyNameDataChanged(DataToRegs dr)// object dataclass, string dataname)
{
NameDataChanged?.Invoke(this, dr);// new NameDataChangedEventArgs(dataclass, dataname));
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion
///
/// 数据映射表,根据这个表,以后自动更新PLC寄存器,或更新property
///
public List DRmap = new List();
///
///
///
public ModbusMapper()
{
coilData = new CoilData(PLCAddressArea.Coil, MaxCoilsOfOneRead);
registerData = new RegisterData(PLCAddressArea.Register, MaxRegsOfOneRead);
mAreaManager = new List
{
coilData,
registerData
};
}
#region 数据映射
///
/// 查找数据-寄存器对应表
///
public DataToRegs FindDataMap(object owner, string propertyName)
{
return DRmap.Find((dr) => { return (dr.propertyName == propertyName && dr.owner == owner); });
}
///
/// 数据映射到寄存器
///
/// modbus 寄存器类型
/// modbus 寄存器首地址
/// plc 数据对应类型
/// plc 数据放大倍数
/// Property 所属对象
/// Property Name
///
public DataToRegs MapDataToRegs(PLCAddressArea dataArea, int addr, REG_TYPE type, double scale, object obj, string propertyname, string regAddr, string ownerName)
{
DataToRegs dr = FindDataMap(obj, propertyname);
if (dr == null)
{
dr = new DataToRegs(this, dataArea, addr, type, scale, obj, propertyname, regAddr, ownerName);
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;
}
#endregion
#region Modbus数据
///
/// 就是 coils 与 registers 的组合
///
protected List mAreaManager;
protected CoilData coilData;
protected RegisterData registerData;
#endregion
#region 数据读写
///
/// 为命名数据赋值,会改变Modbus的寄存器值,并产生通知
///
/// 属性所属的对象, 可为null
/// 属性名,不能为空
/// 数据值,不能为空
/// 赋值的结果
public virtual bool SetNameData(object obj, string propertyName, object val)
{
DataToRegs dr = FindDataMap(obj, propertyName);
if (dr == null) return false;
SetNameData(dr, val);
return true;
}
///
/// PC想向PLC写入数据;
/// 为命名数据赋值,会改变Modbus的寄存器值,并产生通知
///
/// 数据映射
/// 数据值,不能为空
///
public abstract void SetNameData(DataToRegs dr, object val);
///
/// 取命名数据的值
///
/// 属性所属 的对象
/// 属性名
/// 属性的值
public virtual object GetNameData(object obj, string propertyName)
{
DataToRegs dr = FindDataMap(obj, propertyName);
if (dr == null) return null;
return dr.value;
}
#region IModbusRegister实现
///
/// 是从PLC读取数据后;
/// 设置Modbus的值,会改变相应命名数据的值,并产生通知
///
/// coil or register
/// 地址
/// 值
///
public virtual void SetModbusData(PLCAddressArea dataarea, int addr, object value)
{
switch (dataarea)
{
case PLCAddressArea.Register:
{
IEnumerable vals = (IEnumerable)value;
//找寄存器
int endAddr = addr + vals.Count() - 1;
RegisterData area_regs = registerData;
for (int i = 0; i < area_regs.regs.Count(); i++)
{
DataToRegs dr = area_regs.regs[i];
if (endAddr < dr.addr)
{
//当前输入的全部PLC寄存器 在 dr的前面
break;
}
else if (addr <= dr.endAddr)//有交集
{
//计算交集
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,下面的循环跑不动
for (int j = 0; j < num_act; j++)
{
int idx1 = addr_act - dr.addr + j;
int idx2 = addr_act - addr + j;
UInt16[] plc_value = (UInt16[])dr.plcValue;
if (plc_value[idx1] != vals.ElementAt(idx2))
{
plc_value[idx1] = vals.ElementAt(idx2);
dr.isPlcValueChanged = true;//其中 一个PLC寄存器 被改变了
}
}
if (endAddr_act == dr.endAddr)//枚举到 dr 的最后一个寄存器了,触发动作
{
if (dr.isPlcValueChanged)
{
dr.isPlcValueChanged = false;
dr.ToPropertyObj();
//触发事件
NotifyNameDataChanged(dr);
}
}
}
else
{
//在后面
}
}
}break;
case PLCAddressArea.Coil:
{
IEnumerable vals = (IEnumerable)value;
//找寄存器
int endAddr = addr + vals.Count() - 1;
CoilData area_coils = coilData;
for (int i = 0; i < area_coils.regs.Count(); i++)
{
DataToRegs dr = area_coils.regs[i];
if (endAddr < dr.addr)
{
//在前面
break;
}
else if (addr <= dr.addr)//有交集
{
int addr_act = dr.addr;
int idx2 = addr_act - addr;
if ((bool)dr.plcValue != vals.ElementAt(idx2))
{
dr.plcValue = vals.ElementAt(idx2);
dr.isPlcValueChanged = true;
}
//isPlcValueChanged 可能是上面值不一样 被设为true, 也可能是 客户端 发送数据到 PLC ,所以被强制设置为 true
if (dr.isPlcValueChanged) {
dr.ToPropertyObj();
//触发事件
NotifyNameDataChanged(dr);
}
}
else
{
//在后面
}
}
}break;
}
}
#endregion
#endregion
#region 类的静态函数
///
/// C#类型,对应PLC寄存器个数
///
///
///
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);
}
}
}
///
/// string 转 C#类型
///
///
///
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;
}
///
/// string 转modbus类型
///
///
///
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