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