diff --git a/Modbus.Net/Modbus.Net.Core/ConfigurationManager.cs b/Modbus.Net/Modbus.Net.Core/ConfigurationManager.cs index 55e5301..9fb6291 100644 --- a/Modbus.Net/Modbus.Net.Core/ConfigurationManager.cs +++ b/Modbus.Net/Modbus.Net.Core/ConfigurationManager.cs @@ -14,5 +14,7 @@ namespace Modbus.Net private static IConfigurationRoot Configuration => builder.Build(); public static IConfigurationSection AppSettings => Configuration.GetSection("AppSettings"); + + public static IConfigurationSection ConnectionStrings => Configuration.GetSection("ConnectionStrings"); } } \ No newline at end of file diff --git a/Modbus.Net/Modbus.Net.Core/Modbus.Net.Core.csproj b/Modbus.Net/Modbus.Net.Core/Modbus.Net.Core.csproj index 3e7c4ae..c1c1818 100644 --- a/Modbus.Net/Modbus.Net.Core/Modbus.Net.Core.csproj +++ b/Modbus.Net/Modbus.Net.Core/Modbus.Net.Core.csproj @@ -48,12 +48,14 @@ + + diff --git a/Modbus.Net/Modbus.Net.OPC/OpcUtility.cs b/Modbus.Net/Modbus.Net.OPC/OpcUtility.cs index 137637b..50ce8a5 100644 --- a/Modbus.Net/Modbus.Net.OPC/OpcUtility.cs +++ b/Modbus.Net/Modbus.Net.OPC/OpcUtility.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; namespace Modbus.Net.OPC { - public abstract class OpcUtility : BaseUtility + public abstract class OpcUtility : BaseUtility> { protected OpcUtility(string connectionString) : base(0, 0) { diff --git a/Modbus.Net/Modbus.Net/Modbus.Net.csproj b/Modbus.Net/Modbus.Net/Modbus.Net.csproj index 2d503be..00e6daa 100644 --- a/Modbus.Net/Modbus.Net/Modbus.Net.csproj +++ b/Modbus.Net/Modbus.Net/Modbus.Net.csproj @@ -47,6 +47,11 @@ + + + + + diff --git a/Modbus.Net/src/Base.Common/BaseMachine.cs b/Modbus.Net/src/Base.Common/BaseMachine.cs index 80d26c8..757df30 100644 --- a/Modbus.Net/src/Base.Common/BaseMachine.cs +++ b/Modbus.Net/src/Base.Common/BaseMachine.cs @@ -661,6 +661,11 @@ namespace Modbus.Net { return BaseUtility.Disconnect(); } + + public string GetMachineIdString() + { + return Id.ToString(); + } } internal class BaseMachineEqualityComparer : IEqualityComparer> @@ -754,7 +759,7 @@ namespace Modbus.Net /// /// 地址单元 /// - public class AddressUnit where TKey : IEquatable + public class AddressUnit : IEquatable> where TKey : IEquatable { /// /// 数据单元Id @@ -815,31 +820,15 @@ namespace Modbus.Net /// 扩展 /// public UnitExtend UnitExtend { get; set; } - } - internal struct AddressUnitEqualityComparer : IEqualityComparer> where TKey : IEquatable - { - public bool Equals(AddressUnit x, AddressUnit y) + public bool Equals(AddressUnit other) { - return (x.Area.ToUpper() == y.Area.ToUpper() && x.Address == y.Address) || x.Id.Equals(y.Id); - } - - public int GetHashCode(AddressUnit obj) - { - return obj.GetHashCode(); + return (Area.ToUpper() == other.Area.ToUpper() && Address == other.Address) || Id.Equals(other.Id); } } - /// - /// 设备的抽象 - /// - public interface IMachineProperty where TKey : IEquatable + public interface IMachinePropertyWithoutKey { - /// - /// Id - /// - TKey Id { get; set; } - /// /// 工程名 /// @@ -898,5 +887,18 @@ namespace Modbus.Net /// /// 是否断开成功 bool Disconnect(); + + string GetMachineIdString(); + } + + /// + /// 设备的抽象 + /// + public interface IMachineProperty : IMachinePropertyWithoutKey where TKey : IEquatable + { + /// + /// Id + /// + TKey Id { get; set; } } } \ No newline at end of file diff --git a/Modbus.Net/src/Base.Common/BaseProtocal.cs b/Modbus.Net/src/Base.Common/BaseProtocal.cs index c4db1f3..627be11 100644 --- a/Modbus.Net/src/Base.Common/BaseProtocal.cs +++ b/Modbus.Net/src/Base.Common/BaseProtocal.cs @@ -10,11 +10,6 @@ namespace Modbus.Net /// public abstract class BaseProtocal : BaseProtocal { - /// - /// 协议的连接器 - /// - public new ProtocalLinker ProtocalLinker { get; set; } - /// /// 构造器 /// diff --git a/Modbus.Net/src/Base.Common/BaseUtility.cs b/Modbus.Net/src/Base.Common/BaseUtility.cs index 830b401..86172fb 100644 --- a/Modbus.Net/src/Base.Common/BaseUtility.cs +++ b/Modbus.Net/src/Base.Common/BaseUtility.cs @@ -28,7 +28,7 @@ namespace Modbus.Net /// /// 基础Api入口 /// - public abstract class BaseUtility : BaseUtility + public abstract class BaseUtility : BaseUtility { /// /// 构造器 @@ -37,23 +37,17 @@ namespace Modbus.Net { } - - /// - /// 协议收发主体 - /// - protected new BaseProtocal Wrapper; - } /// /// 基础Api入口 /// - public abstract class BaseUtility : IUtilityProperty, IUtilityMethodData + public abstract class BaseUtility : IUtilityProperty, IUtilityMethodData where TProtocalUnit : ProtocalUnit { /// /// 协议收发主体 /// - protected BaseProtocal> Wrapper; + protected BaseProtocal Wrapper; /// /// 构造器 @@ -160,7 +154,7 @@ namespace Modbus.Net return null; } } - + /// /// 获取数据 /// @@ -296,7 +290,7 @@ namespace Modbus.Net if (this is TUtilityMethod) { Type t = typeof(TUtilityMethod); - object returnValue = t.GetRuntimeMethod(methodName, parameters.Select(p => p.GetType()).ToArray()) + object returnValue = t.GetRuntimeMethod(methodName, parameters.Select(p => p.GetType()).ToArray(), false) .Invoke(this, parameters); return (TReturnType)returnValue; } diff --git a/Modbus.Net/src/Base.Common/ProtocalLinker.cs b/Modbus.Net/src/Base.Common/ProtocalLinker.cs index 0d2bf45..3ec9995 100644 --- a/Modbus.Net/src/Base.Common/ProtocalLinker.cs +++ b/Modbus.Net/src/Base.Common/ProtocalLinker.cs @@ -9,11 +9,6 @@ namespace Modbus.Net /// public abstract class ProtocalLinker : ProtocalLinker { - /// - /// 传输连接器 - /// - protected new BaseConnector BaseConnector; - /// /// 发送并接收数据 /// diff --git a/Modbus.Net/src/Base.Common/TaskManager.cs b/Modbus.Net/src/Base.Common/TaskManager.cs index f6058ec..00ae740 100644 --- a/Modbus.Net/src/Base.Common/TaskManager.cs +++ b/Modbus.Net/src/Base.Common/TaskManager.cs @@ -8,34 +8,27 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Nito.AsyncEx; namespace Modbus.Net { /// /// 返回结果的定义类 /// - public class TaskReturnDef : TaskReturnDef + public class DataReturnDef : DataReturnDef { - + } /// /// 返回结果的定义类 /// - public class TaskReturnDef where TMachineKey : IEquatable + public class DataReturnDef where TMachineKey : IEquatable { public TMachineKey MachineId { get; set; } public Dictionary ReturnValues { get; set; } } - /// - /// 时间定义 - /// - public static class TimeRestore - { - public static int Restore = 0; - } - public class LimitedConcurrencyLevelTaskScheduler : TaskScheduler { /// @@ -105,7 +98,7 @@ namespace Modbus.Net /// private void NotifyThreadPoolOfPendingWork() { -#if NET40 || NET45 || NET451 || NET452 || NET46 || NET461 || NET462 +#if NET40 || NET45 || NET451 || NET452 || NET46 || NET461 || NET462 || NET47 ThreadPool.UnsafeQueueUserWorkItem(_ => #else ThreadPool.QueueUserWorkItem(_ => @@ -199,51 +192,270 @@ namespace Modbus.Net } } + public class TaskMachine where TMachineKey : IEquatable + { + private TaskFactory _tasks { get; } + + public TaskMachine(IMachineProperty machine, TaskFactory taskFactory) + { + Machine = machine; + _tasks = taskFactory; + } + + public IMachineProperty Machine { get; } + + public List TasksWithTimer { get; set; } + + public bool InvokeTimer(TaskItem task) + { + task.DetectConnected = () => Machine.IsConnected; + task.GetMachine = () => Machine; + task.GetTaskFactory = () => _tasks; + + if (!TasksWithTimer.Exists(taskCon => taskCon.Name == task.Name)) + { + TasksWithTimer.Add(task); + task.StartTimer(); + return true; + } + return false; + } + + public bool StopTimer(string taskItemName) + { + if (TasksWithTimer.Exists(taskCon => taskCon.Name == taskItemName)) + { + var task = TasksWithTimer.FirstOrDefault(taskCon => taskCon.Name == taskItemName); + task?.StopTimer(); + TasksWithTimer.Remove(task); + return true; + } + return false; + } + + public bool StopAllTimers() + { + bool ans = true; + foreach (var task in TasksWithTimer) + { + ans = ans && StopTimer(task.Name); + } + return ans; + } + + public bool PauseTimer(string taskItemName) + { + if (TasksWithTimer.Exists(taskCon => taskCon.Name == taskItemName)) + { + var task = TasksWithTimer.FirstOrDefault(taskCon => taskCon.Name == taskItemName); + task?.StopTimer(); + return true; + } + return false; + } + + public bool PauseAllTimers() + { + bool ans = true; + foreach (var task in TasksWithTimer) + { + ans = ans && PauseTimer(task.Name); + } + return ans; + } + + public bool ContinueTimer(string taskItemName) + { + if (TasksWithTimer.Exists(taskCon => taskCon.Name == taskItemName)) + { + var task = TasksWithTimer.FirstOrDefault(taskCon => taskCon.Name == taskItemName); + task?.StartTimer(); + return true; + } + return false; + } + + public bool ContinueAllTimers() + { + bool ans = true; + foreach (var task in TasksWithTimer) + { + ans = ans && ContinueTimer(task.Name); + } + return ans; + } + + public async Task InvokeOnce(TaskItem task) + { + if (Machine.IsConnected) + { + var ans = await task.Invoke(Machine, _tasks, task.Params); + task.Return(ans); + return true; + } + return false; + } + } + + internal class TaskMachineEqualityComparer : IEqualityComparer> + where TKey : IEquatable + { + public bool Equals(TaskMachine x, TaskMachine y) + { + return x.Machine.Id.Equals(y.Machine.Id); + } + + public int GetHashCode(TaskMachine obj) + { + return obj.GetHashCode(); + } + } + + public interface ITaskItem + { + string Name { get; set; } + + bool StartTimer(); + + bool StopTimer(); + } + + public class TaskItemGetData : TaskItem + { + public TaskItemGetData(Action returnFunc, int getCycle, int sleepCycle) + { + Name = "GetDatas"; + Invoke = async (machine, tasks, parameters) => + { + var cts = new CancellationTokenSource(); + cts.CancelAfter(TimeSpan.FromSeconds(100000)); + var ans = + await tasks.Run( + async () => await machine.InvokeMachineMethod>>("GetDatasAsync", + MachineGetDataType.CommunicationTag).WithCancellation(cts.Token)); + return new DataReturnDef + { + MachineId = machine.GetMachineIdString(), + ReturnValues = ans, + }; + }; + Params = null; + Return = returnFunc; + TimerDisconnectedTime = sleepCycle; + TimerTime = getCycle; + } + } + + public class TaskItemSetData : TaskItem + { + public TaskItemSetData(Dictionary values) + { + Name = "SetDatas"; + Invoke = Invoke = async (machine, tasks, parameters) => + { + var cts = new CancellationTokenSource(); + cts.CancelAfter(TimeSpan.FromSeconds(100000)); + var ans = + await tasks.Run( + async () => await machine.InvokeMachineMethod>("SetDatasAsync", parameters[0], + MachineSetDataType.CommunicationTag).WithCancellation(cts.Token)); + return ans; + }; + Params = new object[]{values}; + } + } + + public class TaskItem : ITaskItem, IEquatable> + { + public string Name { get; set; } + private Timer Timer { get; set; } + public int TimerTime { get; set; } + private Timer TimerDisconnected { get; set; } + public int TimerDisconnectedTime { get; set; } + public Func> Invoke { get; set; } + internal Func DetectConnected { get; set; } + public object[] Params { get; set; } + public Action Return { get; set; } + internal Func GetMachine { get; set; } + internal Func GetTaskFactory { get; set; } + + public bool Equals(TaskItem other) + { + return Name == other?.Name; + } + + public bool StartTimer() + { + ActivateTimerDisconnected(); + return true; + } + + private void ActivateTimer() + { + Timer = new Timer(async state => + { + if (!DetectConnected()) TimerChangeToDisconnect(); + var ans = await Invoke(GetMachine(),GetTaskFactory(),Params); + Return(ans); + }, null, 0, TimerTime); + } + + private void DeactivateTimer() + { + Timer.Dispose(); + Timer = null; + } + + private void ActivateTimerDisconnected() + { + TimerDisconnected = new Timer(async state => + { + await GetMachine().ConnectAsync(); + if (DetectConnected()) TimerChangeToConnect(); + }, null, 0, TimerDisconnectedTime); + } + + private void DeactivateTimerDisconnected() + { + TimerDisconnected.Dispose(); + TimerDisconnected = null; + } + + private bool TimerChangeToConnect() + { + DeactivateTimerDisconnected(); + ActivateTimer(); + return true; + } + + private bool TimerChangeToDisconnect() + { + DeactivateTimer(); + ActivateTimerDisconnected(); + return true; + } + + public bool StopTimer() + { + DeactivateTimer(); + DeactivateTimerDisconnected(); + return true; + } + } + /// /// 任务调度器 /// public class TaskManager : TaskManager { - public TaskManager(int maxRunningTask, int getCycle, bool keepConnect, + public TaskManager(int maxRunningTask, bool keepConnect, MachineDataType dataType = MachineDataType.CommunicationTag) - : base(maxRunningTask, getCycle, keepConnect, dataType) + : base(maxRunningTask, keepConnect, dataType) { } - public new delegate void ReturnValuesDelegate(TaskReturnDef returnValue); - - public new event ReturnValuesDelegate ReturnValues; - - /// - /// 执行对具体设备的数据更新 - /// - /// 设备的实例 - /// - protected override async Task RunTask(IMachineProperty machine) - { - try - { - var ans = await GetValue(machine); - ReturnValues?.Invoke(new TaskReturnDef - { - MachineId = machine.Id, - ReturnValues = ans - }); - } - catch (Exception e) - { - if (!machine.IsConnected) - { - MoveMachineToUnlinked(machine.Id); - } - ReturnValues?.Invoke(new TaskReturnDef - { - MachineId = machine.Id, - ReturnValues = null - }); - } - } - public void AddMachine(BaseMachine machine) { base.AddMachine(machine); @@ -271,29 +483,13 @@ namespace Modbus.Net /// public class TaskManager where TMachineKey : IEquatable { - /// - /// 返回数据代理 - /// - /// - public delegate void ReturnValuesDelegate(TaskReturnDef returnValue); - /// /// 正在运行的设备 /// - private readonly HashSet> _machines; - - /// - /// 不在运行的设备 - /// - private readonly HashSet> _unlinkedMachines; + private readonly HashSet> _machines; private CancellationTokenSource _cts; - /// - /// 获取间隔 - /// - private int _getCycle; - /// /// 保持连接 /// @@ -309,34 +505,32 @@ namespace Modbus.Net /// private TaskFactory _tasks; - /// - /// 正常读取的计时器 - /// - private Timer _timer; - - /// - /// 重连计时器 - /// - private Timer _timer2; - /// /// 构造一个TaskManager /// /// 同时可以运行的任务数 - /// 读取数据的时间间隔(毫秒) /// 读取数据后是否保持连接 /// 获取与设置数据的方式 - public TaskManager(int maxRunningTask, int getCycle, bool keepConnect, + public TaskManager(int maxRunningTask, bool keepConnect, MachineDataType dataType = MachineDataType.CommunicationTag) { _scheduler = new LimitedConcurrencyLevelTaskScheduler(maxRunningTask); _machines = - new HashSet>(new BaseMachineEqualityComparer()); - _unlinkedMachines = - new HashSet>(new BaseMachineEqualityComparer()); - _getCycle = getCycle; + new HashSet>(new TaskMachineEqualityComparer()); KeepConnect = keepConnect; MachineDataType = dataType; + _cts = new CancellationTokenSource(); + _tasks = new TaskFactory(_cts.Token, TaskCreationOptions.None, TaskContinuationOptions.None, _scheduler); + } + + /// + /// 强制停止所有正在运行的任务 + /// + public void TaskHalt() + { + _cts.Cancel(); + _cts = new CancellationTokenSource(); + _tasks = new TaskFactory(_cts.Token, TaskCreationOptions.None, TaskContinuationOptions.None, _scheduler); } /// @@ -347,61 +541,16 @@ namespace Modbus.Net get { return _keepConnect; } set { - TaskStop(); + PauseTimerAll(); _keepConnect = value; lock (_machines) { foreach (var machine in _machines) { - machine.KeepConnect = _keepConnect; + machine.Machine.KeepConnect = _keepConnect; } } - } - } - - /// - /// 获取间隔,毫秒 - /// - public int GetCycle - { - get { return _getCycle; } - set - { - if (value == _getCycle) return; - - if (value == Timeout.Infinite) - { - if (_timer != null) - { - _timer.Change(Timeout.Infinite, Timeout.Infinite); - _timer2.Change(Timeout.Infinite, Timeout.Infinite); - _timer.Dispose(); - _timer2.Dispose(); - _timer = null; - _timer2 = null; - } - } - else if (value < 0) return; - else - { - if (_timer != null) - { - _timer.Change(Timeout.Infinite, Timeout.Infinite); - _timer2.Change(Timeout.Infinite, Timeout.Infinite); - _timer.Dispose(); - _timer2.Dispose(); - _timer = null; - _timer2 = null; - } - if (value > 0) - { - _getCycle = value; - } - _timer = new Timer(MaintainTasks, null, 0, _getCycle); - _timer2 = new Timer(MaintainTasks2, null, _getCycle*2, _getCycle*2); - //调试行,调试时请注释上面两行并取消下面一行的注释,每台设备只会执行一次数据获取。 - //MaintainTasks(null); - } + ContinueTimerAll(); } } @@ -450,26 +599,23 @@ namespace Modbus.Net get { return _scheduler.MaximumConcurrencyLevel; } set { - TaskStop(); + PauseTimerAll(); _scheduler = new LimitedConcurrencyLevelTaskScheduler(value); + ContinueTimerAll(); } } - /// - /// 返回数据事件 - /// - public event ReturnValuesDelegate ReturnValues; - /// /// 添加一台设备 /// /// 设备 - public void AddMachine(BaseMachine machine) where TUnitKey : IEquatable + public void AddMachine(BaseMachine machine) + where TUnitKey : IEquatable { machine.KeepConnect = KeepConnect; lock (_machines) { - _machines.Add(machine); + _machines.Add(new TaskMachine(machine, _tasks) {TasksWithTimer = new List()}); } } @@ -477,7 +623,8 @@ namespace Modbus.Net /// 添加多台设备 /// /// 设备的列表 - public void AddMachines(IEnumerable> machines) where TUnitKey : IEquatable + public void AddMachines(IEnumerable> machines) + where TUnitKey : IEquatable { lock (_machines) { @@ -493,17 +640,10 @@ namespace Modbus.Net { try { - IMachineProperty machine; + TaskMachine machine; lock (_machines) { - machine = _machines.FirstOrDefault(p => p.Id.Equals(id)); - if (machine == null) - { - lock (_unlinkedMachines) - { - machine = _unlinkedMachines.FirstOrDefault(p => p.Id.Equals(id)); - } - } + machine = _machines.FirstOrDefault(p => p.Machine.Id.Equals(id)); } return machine as BaseMachine; } @@ -519,17 +659,10 @@ namespace Modbus.Net { try { - IMachineProperty machine; + TaskMachine machine; lock (_machines) { - machine = _machines.FirstOrDefault(p => p.ConnectionToken == connectionToken); - if (machine == null) - { - lock (_unlinkedMachines) - { - machine = _unlinkedMachines.FirstOrDefault(p => p.ConnectionToken == connectionToken); - } - } + machine = _machines.FirstOrDefault(p => p.Machine.ConnectionToken == connectionToken); } return machine as BaseMachine; } @@ -548,11 +681,7 @@ namespace Modbus.Net { lock (_machines) { - _machines.RemoveWhere(p => p.ConnectionToken == machineToken); - } - lock (_unlinkedMachines) - { - _unlinkedMachines.RemoveWhere(p => p.ConnectionToken == machineToken); + _machines.RemoveWhere(p => p.Machine.ConnectionToken == machineToken); } } @@ -564,55 +693,7 @@ namespace Modbus.Net { lock (_machines) { - _machines.RemoveWhere(p => p.Id.Equals(id)); - } - lock (_unlinkedMachines) - { - _unlinkedMachines.RemoveWhere(p => p.Id.Equals(id)); - } - } - - /// - /// 将设备指定为未连接 - /// - /// 设备的id - public void MoveMachineToUnlinked(TMachineKey id) - { - IEnumerable> machines; - lock (_machines) - { - machines = _machines.Where(c => c.Id.Equals(id)).ToList(); - if (!machines.Any()) return; - _machines.RemoveWhere(p => p.Id.Equals(id)); - } - lock (_unlinkedMachines) - { - foreach (var machine in machines) - { - _unlinkedMachines.Add(machine); - } - } - } - - /// - /// 将设备指定为已连接 - /// - /// 设备的id - public void MoveMachineToLinked(TMachineKey id) - { - IEnumerable> machines; - lock (_unlinkedMachines) - { - machines = _unlinkedMachines.Where(c => c.Id.Equals(id)).ToList(); - if (!machines.Any()) return; - _unlinkedMachines.RemoveWhere(p => p.Id.Equals(id)); - } - lock (_machines) - { - foreach (var machine in machines) - { - _machines.Add(machine); - } + _machines.RemoveWhere(p => p.Machine.Id.Equals(id)); } } @@ -624,192 +705,161 @@ namespace Modbus.Net { lock (_machines) { - _machines.Remove(machine); - } - lock (_unlinkedMachines) - { - _unlinkedMachines.Remove(machine); + _machines.RemoveWhere(p=>p.Machine.Equals(machine)); } } - /// - /// 已连接设备更新 - /// - /// - private void MaintainTasks(object sender) - { - AsyncHelper.RunSync(MaintainTasksAsync); - } - - /// - /// 未连接设备更新 - /// - /// - private void MaintainTasks2(object sender) - { - AsyncHelper.RunSync(MaintainTasks2Async); - } - - /// - /// 已连接设备更新 - /// - /// - private async Task MaintainTasksAsync() - { - try - { - var tasks = new List(); - var saveMachines = new HashSet>(); - IEnumerable> saveMachinesEnum; - lock (_machines) - { - saveMachines.UnionWith(_machines); - saveMachinesEnum = saveMachines.ToList(); - } - foreach (var machine in saveMachinesEnum) - { - var cts = new CancellationTokenSource(); - cts.CancelAfter(TimeSpan.FromSeconds(_getCycle*10)); - var task = _tasks.StartNew(() => RunTask(machine).WithCancellation(cts.Token)); - tasks.Add(task); - } - await Task.WhenAll(tasks); - } - catch (Exception) - { - //ignore - } - } - - /// - /// 未连接设备更新 - /// - /// - private async Task MaintainTasks2Async() - { - try - { - var tasks = new List(); - var saveMachines = new HashSet>(); - lock (_unlinkedMachines) - { - saveMachines.UnionWith(_unlinkedMachines); - } - foreach (var machine in saveMachines) - { - var cts = new CancellationTokenSource(); - cts.CancelAfter(TimeSpan.FromSeconds(_getCycle*10)); - var task = _tasks.StartNew(() => RunTask(machine).WithCancellation(cts.Token)); - tasks.Add(task); - } - await Task.WhenAll(tasks); - } - catch (Exception) - { - //ignore - } - } - - /// - /// 设置数据 - /// - /// 设备的连接标识 - /// 需要设置的数据 - /// 是否设置成功 - public async Task SetDatasAsync(string connectionToken, - Dictionary values) - { - IMachineProperty machine = null; - lock (_machines) - { - machine = _machines.FirstOrDefault(p => p.ConnectionToken == connectionToken); - } - if (machine == null) return false; - return await machine.InvokeMachineMethod>("SetDatasAsync", SetDataType, values); - } - - /// - /// 启动TaskManager - /// - public void TaskStart() - { - TaskStop(); - _cts = new CancellationTokenSource(); - _tasks = new TaskFactory(_cts.Token, TaskCreationOptions.None, TaskContinuationOptions.None, _scheduler); - GetCycle = TimeRestore.Restore; - } - - /// - /// 停止TaskManager - /// - public void TaskStop() + public bool InvokeTimerAll(TaskItem item) { lock (_machines) { - GetCycle = Timeout.Infinite; - _cts?.Cancel(); - if (_machines != null) + foreach (var machine in _machines) { - foreach (var machine in _machines) - { - machine.Disconnect(); - } + machine.InvokeTimer(item); } - _tasks = null; } + return true; } - /// - /// 执行对具体设备的数据更新 - /// - /// 设备的实例 - /// - protected virtual async Task RunTask(IMachineProperty machine) + public bool StopTimerAll() { - try + lock (_machines) { - var ans = await GetValue(machine); - ReturnValues?.Invoke(new TaskReturnDef + foreach (var machine in _machines) { - MachineId = machine.Id, - ReturnValues = ans - }); - } - catch (Exception e) - { - if (!machine.IsConnected) - { - MoveMachineToUnlinked(machine.Id); + machine.StopAllTimers(); } - ReturnValues?.Invoke(new TaskReturnDef - { - MachineId = machine.Id, - ReturnValues = null - }); } + return true; } - protected async Task> GetValue(IMachineProperty machine) + public bool StopTimerAll(string taskItemName) { - //调试代码,调试时取消下面一下代码的注释,会同步调用获取数据。 - //var ans = machine.GetDatas(); - //设置Cancellation Token - var cts = new CancellationTokenSource(); - //超时后取消任务 - cts.CancelAfter(TimeSpan.FromSeconds(_getCycle)); - //读取数据 - var ans = - await machine.InvokeMachineMethod>>("GetDatasAsync", - GetDataType).WithCancellation(cts.Token); - if (!machine.IsConnected) + lock (_machines) { - MoveMachineToUnlinked(machine.Id); + foreach (var machine in _machines) + { + machine.StopTimer(taskItemName); + } } - else + return true; + } + + public bool PauseTimerAll() + { + lock (_machines) { - MoveMachineToLinked(machine.Id); + foreach (var machine in _machines) + { + machine.PauseAllTimers(); + } } - return ans; + return true; + } + + public bool PauseTimerAll(string taskItemName) + { + lock (_machines) + { + foreach (var machine in _machines) + { + machine.PauseTimer(taskItemName); + } + } + return true; + } + + public bool ContinueTimerAll() + { + lock (_machines) + { + foreach (var machine in _machines) + { + machine.ContinueAllTimers(); + } + } + return true; + } + + public bool ConinueTimerAll(string taskItemName) + { + lock (_machines) + { + foreach (var machine in _machines) + { + machine.ContinueTimer(taskItemName); + } + } + return true; + } + + public async Task InvokeOnceAll(TaskItem item) + { + var tasks = new List>(); + lock (_machines) + { + foreach (var machine in _machines) + { + tasks.Add(machine.InvokeOnce(item)); + } + } + var ans = await Task.WhenAll(tasks); + return ans.All(p=>p); + } + + public async Task InvokeOnceForMachine(TMachineKey machineId, TaskItem item) + { + var machine = _machines.FirstOrDefault(p => p.Machine.Id.Equals(machineId)); + if (machine != null) + { + await machine.InvokeOnce(item); + return true; + } + return false; + } + + public bool InvokeTimerForMachine(TMachineKey machineId, TaskItem item) + { + var machine = _machines.FirstOrDefault(p => p.Machine.Id.Equals(machineId)); + if (machine != null) + { + machine.InvokeTimer(item); + return true; + } + return false; + } + + public bool StopTimerForMachine(TMachineKey machineId, string taskItemName) + { + var machine = _machines.FirstOrDefault(p => p.Machine.Id.Equals(machineId)); + if (machine != null) + { + machine.StopTimer(taskItemName); + return true; + } + return false; + } + + public bool PauseTimerForMachine(TMachineKey machineId, string taskItemName) + { + var machine = _machines.FirstOrDefault(p => p.Machine.Id.Equals(machineId)); + if (machine != null) + { + machine.PauseTimer(taskItemName); + return true; + } + return false; + } + + public bool ContinueTimerForMachine(TMachineKey machineId, string taskItemName) + { + var machine = _machines.FirstOrDefault(p => p.Machine.Id.Equals(machineId)); + if (machine != null) + { + machine.ContinueTimer(taskItemName); + return true; + } + return false; } } } \ No newline at end of file diff --git a/Modbus.Net/src/Base.Common/TypeExtensions.cs b/Modbus.Net/src/Base.Common/TypeExtensions.cs new file mode 100644 index 0000000..282f839 --- /dev/null +++ b/Modbus.Net/src/Base.Common/TypeExtensions.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace Modbus.Net +{ + public static class TypeExtensions + { + #region Public Methods + + /// + /// Looks for the method in the type matching the name and arguments. + /// + /// + /// + /// The name of the method to find. + /// + /// + /// The types of the method's arguments to match. + /// + /// + /// + /// Thrown if: + /// - The name of the method is not specified. + /// + public static MethodInfo GetRuntimeMethod(this Type type, string methodName, Type[] args, bool isGenericMethod) + { + if (ReferenceEquals(type, null)) + throw new NullReferenceException("The type has not been specified."); + + if (string.IsNullOrEmpty(methodName)) + throw new ArgumentNullException("methodName", "The name of the method has not been specified."); + + + var methods = type.GetRuntimeMethods().Where(methodInfo => string.Equals(methodInfo.Name, methodName, StringComparison.OrdinalIgnoreCase)).ToList(); + + if (!methods.Any()) + return null; // No methods have the specified name. + + if (isGenericMethod) + { + methods = methods.Where(method => method.IsGenericMethod).ToList(); + } + else + { + methods = methods.Where(method => !method.IsGenericMethod).ToList(); + } + + var ans = methods.Where(method => IsSignatureMatch(method, args)); + + if (ans.Count() <= 1) + { + return ans.Count() == 1 ? ans.Single() : null; + } + + // Oh noes, don't make me go there. + throw new NotImplementedException("Resolving overloaded methods is not implemented as of now."); + } + + #endregion + + #region Private Methods + + /// + /// Finds out if the provided arguments matches the specified method's signature. + /// + /// + /// + /// + private static bool IsSignatureMatch(MethodBase methodInfo, Type[] args) + { + Debug.Assert(!ReferenceEquals(methodInfo, null), "The methodInfo has not been specified."); + + + // Gets the parameters of the method to analyze. + ParameterInfo[] parameters = methodInfo.GetParameters(); + + int currentArgId = 0; + + foreach (ParameterInfo parameterInfo in parameters) + { + if (!ReferenceEquals(args, null) && currentArgId < args.Length) + { + // Find out if the types matchs. + if (parameterInfo.ParameterType == args[currentArgId]) + { + currentArgId++; + continue; // Yeah! Try the next one. + } + + // Is this a generic parameter? + if (parameterInfo.ParameterType.IsGenericParameter) + { + // Gets the base type of the generic parameter. + Type baseType = parameterInfo.ParameterType.GetTypeInfo().BaseType; + + + // TODO: This is not good v and works with the most simple situation. + // Does the base type match? + if (args[currentArgId].GetTypeInfo().BaseType == baseType) + { + currentArgId++; + continue; // Yeah! Go on to the next parameter. + } + } + } + + // Is this parameter optional or does it have a default value? + if (parameterInfo.IsOptional || parameterInfo.HasDefaultValue) + continue; // Uhum. So let's ignore this parameter for now. + + // No need to go further. It does not match :( + return false; + } + + // Ye! + return true; + } + + #endregion + } +} diff --git a/Samples/AnyType/Controllers/HomeController.cs b/Samples/AnyType/Controllers/HomeController.cs index 93b730f..ad7d7de 100644 --- a/Samples/AnyType/Controllers/HomeController.cs +++ b/Samples/AnyType/Controllers/HomeController.cs @@ -55,38 +55,39 @@ namespace AnyType.Controllers }; //初始化任务管理器 - task = new TaskManager(10, 300, true); + task = new TaskManager(10, true); //向任务管理器中添加设备 task.AddMachine(new ModbusMachine(ModbusType.Tcp, "192.168.3.10", addressUnits, true, 2, 0)); - //增加值返回时的处理函数 - task.ReturnValues += (returnValues) => + //启动任务 + task.InvokeTimerAll(new TaskItemGetData(returnValues => { //唯一的参数包含返回值,是一个唯一标识符(machine的第二个参数),返回值(类型ReturnUnit)的键值对。 if (returnValues.ReturnValues != null) { lock (values) { - var unitValues = from val in returnValues.ReturnValues select new Tuple(addressUnits.FirstOrDefault(p => p.CommunicationTag == val.Key), val.Value.PlcValue); + var unitValues = from val in returnValues.ReturnValues + select + new Tuple( + addressUnits.FirstOrDefault(p => p.CommunicationTag == val.Key), val.Value.PlcValue); values = from unitValue in unitValues - select - new TaskViewModel() - { - Id = unitValue.Item1.Id, - Name = unitValue.Item1.Name, - Address = unitValue.Item1.Address + "." + unitValue.Item1.SubAddress, - Value = unitValue.Item2 ?? 0, - Type = unitValue.Item1.DataType.Name - }; + select + new TaskViewModel() + { + Id = unitValue.Item1.Id, + Name = unitValue.Item1.Name, + Address = unitValue.Item1.Address + "." + unitValue.Item1.SubAddress, + Value = unitValue.Item2 ?? 0, + Type = unitValue.Item1.DataType.Name + }; } } else { Console.WriteLine($"ip {returnValues.MachineId} not return value"); } - }; - //启动任务 - task.TaskStart(); + }, 15000, 60000)); } [HttpGet] diff --git a/Samples/TaskManager/Controllers/HomeController.cs b/Samples/TaskManager/Controllers/HomeController.cs index d34abc4..484df23 100644 --- a/Samples/TaskManager/Controllers/HomeController.cs +++ b/Samples/TaskManager/Controllers/HomeController.cs @@ -47,38 +47,39 @@ namespace TaskManager.Controllers }; //初始化任务管理器 - task = new Modbus.Net.TaskManager(10, 300, true); + task = new Modbus.Net.TaskManager(10, true); //向任务管理器中添加设备 task.AddMachine(new ModbusMachine(ModbusType.Tcp, "192.168.3.10", addressUnits, true, 2, 0)); - //增加值返回时的处理函数 - task.ReturnValues += (returnValues) => + //启动任务 + task.InvokeTimerAll(new TaskItemGetData(returnValues => { //唯一的参数包含返回值,是一个唯一标识符(machine的第二个参数),返回值(类型ReturnUnit)的键值对。 if (returnValues.ReturnValues != null) - { + { lock (values) { - var unitValues = from val in returnValues.ReturnValues select new Tuple(addressUnits.FirstOrDefault(p => p.CommunicationTag == val.Key), val.Value.PlcValue); + var unitValues = from val in returnValues.ReturnValues + select + new Tuple( + addressUnits.FirstOrDefault(p => p.CommunicationTag == val.Key), val.Value.PlcValue); values = from unitValue in unitValues select - new TaskViewModel() - { - Id = unitValue.Item1.Id, - Name = unitValue.Item1.Name, - Address = unitValue.Item1.Address.ToString(), - Value = unitValue.Item2 ?? 0, - Type = unitValue.Item1.DataType.Name - }; + new TaskViewModel() + { + Id = unitValue.Item1.Id, + Name = unitValue.Item1.Name, + Address = unitValue.Item1.Address.ToString(), + Value = unitValue.Item2 ?? 0, + Type = unitValue.Item1.DataType.Name + }; } } else { Console.WriteLine($"ip {returnValues.MachineId} not return value"); } - }; - //启动任务 - task.TaskStart(); + }, 15000, 60000)); } [HttpGet] diff --git a/Tests/Modbus.Net.Tests/BaseTest.cs b/Tests/Modbus.Net.Tests/BaseTest.cs index c6b1249..46cb258 100644 --- a/Tests/Modbus.Net.Tests/BaseTest.cs +++ b/Tests/Modbus.Net.Tests/BaseTest.cs @@ -142,7 +142,7 @@ namespace Modbus.Net.Tests MachineName = "Test 2" }; - _taskManager = new TaskManager(10, 3000, true); + _taskManager = new TaskManager(10, true); _taskManager.AddMachine(_baseMachine); _taskManager.AddMachine(_baseMachine2);