using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace FLY.Modbus.WithThread
{
///
/// 使用线程版本
/// 支持自定义逻辑
///
public class ModbusMapper_Client : ModbusMapper
{
///
/// 更新周期,单位ms,
///
public int UpdateInterval = 100;
///
/// 实际更新间隔
///
public TimeSpan ActUpdateInterval { get; private set; }
///
/// 工作中
///
public bool IsRunning { get; private set; }
///
/// 异常次数
///
public int ErrorCnt { get; private set; }
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 Client { get; set; }
CancellationTokenSource cts;
///
///
///
/// 服务器ep
public ModbusMapper_Client(IPEndPoint ep)
{
Client = new ClientTCP(ep);
}
///
///
///
///
public override string ToString()
{
return Client.ToString();
}
///
/// 启动
///
public void Start()
{
lock (this)
{
if (IsRunning)
return;
IsRunning = true;
cts = new CancellationTokenSource();
Task.Factory.StartNew(OnPoll_update, cts.Token);
}
}
///
/// 停止
///
public void Stop()
{
lock (this)
{
if (!IsRunning)
return;
cts.Cancel();
Client.Close();
}
}
void OnPoll_update()
{
Stopwatch stopwatch = new Stopwatch();
Stopwatch stopwatch_act = new Stopwatch();
bool isTimeForRead = true;
_connect:
while (true)
{
if (cts.IsCancellationRequested)
goto _end;
bool isconnected = Client.Connect();
if (cts.IsCancellationRequested)
goto _end;
if (isconnected)
break;
else
Thread.Sleep(1000);
}
//刚连接成功。
//从本地PC 属性获取value 更新到 本地PLC 寄存器,
//目的: 当从PLC读取寄存器值时,能比较不同,产生变化事件
foreach (var areaManager in mAreaManager)
areaManager.SetAllIsPlcValueChanged();
//清空 写缓存
lock (rws)
{
rws.Clear();
}
stopwatch.Restart();
isTimeForRead = true;
while (true)
{
if (cts.IsCancellationRequested)
goto _end;
if (stopwatch.ElapsedMilliseconds >= UpdateInterval)
{
isTimeForRead = true;
}
if (isTimeForRead)
{
isTimeForRead = false;
stopwatch.Restart();
if (!stopwatch_act.IsRunning)
{
stopwatch_act.Restart();
}
else {
ActUpdateInterval = stopwatch_act.Elapsed;
stopwatch_act.Restart();
}
if (!UpdateReadData())
{
if (cts.IsCancellationRequested)//可能是外部强制关闭
goto _end;
goto _error;//连接断开,终止更新线程
}
}
//输出写入数据
if (!UpdateWriteData())
{
if (cts.IsCancellationRequested)//可能是外部强制关闭
goto _end;
goto _error;//连接断开,终止更新线程
}
Thread.Sleep(30);
}
_error:
ErrorCnt++;
goto _connect;
_end:
Client.Close();
IsRunning = false;
}
///
/// 更新一次读全部数据
///
///
bool UpdateReadData()
{
foreach (var areaManager in mAreaManager)
{
areaManager.MakePlanReal();
}
//int curr_area_idx = 0;
foreach (var areaManager in mAreaManager)
{
foreach(Plan p_doing in areaManager.plan)
{
switch (p_doing.area)
{
case PLCAddressArea.Coil:
{
if (!Client.Do_01(p_doing.addr, p_doing.num, out IEnumerable values))
return false;
SetModbusData(PLCAddressArea.Coil, p_doing.addr, values);
}
break;
case PLCAddressArea.Register:
{
if (!Client.Do_03(p_doing.addr, p_doing.num, out IEnumerable values))
return false;
SetModbusData(PLCAddressArea.Register, p_doing.addr, values);
}
break;
}
}
}
return true;
}
///
/// PC -> PLC
/// 为命名数据赋值,会改变Modbus的寄存器值,并产生通知
///
/// 数据映射
/// 数据值,不能为空
///
public override void SetNameData(DataToRegs dr, object val)
{
RegWrite rw = new RegWrite(dr, val);
lock (rws)
{
rws.Add(rw);
}
}
///
/// 提取全部写命令缓冲区 输出
///
bool UpdateWriteData()
{
while (true)
{
RegWrite rw;
lock (rws)
{
if (rws.Count() == 0)
break;
rw = rws.First();
rws.RemoveAt(0);
}
//强制令读时,推送数据
rw.dr.isPlcValueChanged = true;
object plc_value = rw.dr.ToPLCObj(rw.val);
switch (rw.dr.dataArea)
{
case PLCAddressArea.Coil:
if (!Client.Do_0F((UInt16)rw.dr.addr, new bool[] {(bool)plc_value }))
return false;
break;
default:
//case PLCAddressArea.Register:
if (!Client.Do_10((UInt16)rw.dr.addr, (UInt16[])plc_value))
return false;
break;
}
}
return true;
}
}
}