2017-05-15 Update 1 TaskManager Remake. Bug Fix.(Not Test)
This commit is contained in:
@@ -14,5 +14,7 @@ namespace Modbus.Net
|
|||||||
private static IConfigurationRoot Configuration => builder.Build();
|
private static IConfigurationRoot Configuration => builder.Build();
|
||||||
|
|
||||||
public static IConfigurationSection AppSettings => Configuration.GetSection("AppSettings");
|
public static IConfigurationSection AppSettings => Configuration.GetSection("AppSettings");
|
||||||
|
|
||||||
|
public static IConfigurationSection ConnectionStrings => Configuration.GetSection("ConnectionStrings");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -48,12 +48,14 @@
|
|||||||
<Compile Include="..\src\Base.Common\ValueHelper.cs" Link="ValueHelper.cs" />
|
<Compile Include="..\src\Base.Common\ValueHelper.cs" Link="ValueHelper.cs" />
|
||||||
<Compile Include="..\src\Base.Common\IUtilityMethod.cs" Link="IUtilityMethod.cs" />
|
<Compile Include="..\src\Base.Common\IUtilityMethod.cs" Link="IUtilityMethod.cs" />
|
||||||
<Compile Include="..\src\Base.Common\IMachineMethod.cs" Link="IMachineMethod.cs" />
|
<Compile Include="..\src\Base.Common\IMachineMethod.cs" Link="IMachineMethod.cs" />
|
||||||
|
<Compile Include="..\src\Base.Common\TypeExtensions.cs" Link="TypeExtensions.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="1.1.2" />
|
<PackageReference Include="Microsoft.Extensions.Configuration" Version="1.1.2" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.1.2" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.1.2" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Xml" Version="1.1.2" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Xml" Version="1.1.2" />
|
||||||
|
<PackageReference Include="Nito.AsyncEx.Tasks" Version="1.1.0" />
|
||||||
<PackageReference Include="System.Collections.NonGeneric" Version="4.3.0" />
|
<PackageReference Include="System.Collections.NonGeneric" Version="4.3.0" />
|
||||||
<PackageReference Include="System.Threading.ThreadPool" Version="4.3.0" />
|
<PackageReference Include="System.Threading.ThreadPool" Version="4.3.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace Modbus.Net.OPC
|
namespace Modbus.Net.OPC
|
||||||
{
|
{
|
||||||
public abstract class OpcUtility : BaseUtility<OpcParamIn, OpcParamOut>
|
public abstract class OpcUtility : BaseUtility<OpcParamIn, OpcParamOut, ProtocalUnit<OpcParamIn, OpcParamOut>>
|
||||||
{
|
{
|
||||||
protected OpcUtility(string connectionString) : base(0, 0)
|
protected OpcUtility(string connectionString) : base(0, 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -47,6 +47,11 @@
|
|||||||
<Compile Include="..\src\Base.Common\ValueHelper.cs" Link="ValueHelper.cs" />
|
<Compile Include="..\src\Base.Common\ValueHelper.cs" Link="ValueHelper.cs" />
|
||||||
<Compile Include="..\src\Base.Common\IUtilityMethod.cs" Link="IUtilityMethod.cs" />
|
<Compile Include="..\src\Base.Common\IUtilityMethod.cs" Link="IUtilityMethod.cs" />
|
||||||
<Compile Include="..\src\Base.Common\IMachineMethod.cs" Link="IMachineMethod.cs" />
|
<Compile Include="..\src\Base.Common\IMachineMethod.cs" Link="IMachineMethod.cs" />
|
||||||
|
<Compile Include="..\src\Base.Common\TypeExtensions.cs" Link="TypeExtensions.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Nito.AsyncEx" Version="4.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -661,6 +661,11 @@ namespace Modbus.Net
|
|||||||
{
|
{
|
||||||
return BaseUtility.Disconnect();
|
return BaseUtility.Disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string GetMachineIdString()
|
||||||
|
{
|
||||||
|
return Id.ToString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class BaseMachineEqualityComparer<TKey> : IEqualityComparer<IMachineProperty<TKey>>
|
internal class BaseMachineEqualityComparer<TKey> : IEqualityComparer<IMachineProperty<TKey>>
|
||||||
@@ -754,7 +759,7 @@ namespace Modbus.Net
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 地址单元
|
/// 地址单元
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AddressUnit<TKey> where TKey : IEquatable<TKey>
|
public class AddressUnit<TKey> : IEquatable<AddressUnit<TKey>> where TKey : IEquatable<TKey>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 数据单元Id
|
/// 数据单元Id
|
||||||
@@ -815,31 +820,15 @@ namespace Modbus.Net
|
|||||||
/// 扩展
|
/// 扩展
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public UnitExtend UnitExtend { get; set; }
|
public UnitExtend UnitExtend { get; set; }
|
||||||
}
|
|
||||||
|
|
||||||
internal struct AddressUnitEqualityComparer<TKey> : IEqualityComparer<AddressUnit<TKey>> where TKey : IEquatable<TKey>
|
public bool Equals(AddressUnit<TKey> other)
|
||||||
{
|
|
||||||
public bool Equals(AddressUnit<TKey> x, AddressUnit<TKey> y)
|
|
||||||
{
|
{
|
||||||
return (x.Area.ToUpper() == y.Area.ToUpper() && x.Address == y.Address) || x.Id.Equals(y.Id);
|
return (Area.ToUpper() == other.Area.ToUpper() && Address == other.Address) || Id.Equals(other.Id);
|
||||||
}
|
|
||||||
|
|
||||||
public int GetHashCode(AddressUnit<TKey> obj)
|
|
||||||
{
|
|
||||||
return obj.GetHashCode();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public interface IMachinePropertyWithoutKey
|
||||||
/// 设备的抽象
|
|
||||||
/// </summary>
|
|
||||||
public interface IMachineProperty<TKey> where TKey : IEquatable<TKey>
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Id
|
|
||||||
/// </summary>
|
|
||||||
TKey Id { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 工程名
|
/// 工程名
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -898,5 +887,18 @@ namespace Modbus.Net
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>是否断开成功</returns>
|
/// <returns>是否断开成功</returns>
|
||||||
bool Disconnect();
|
bool Disconnect();
|
||||||
|
|
||||||
|
string GetMachineIdString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设备的抽象
|
||||||
|
/// </summary>
|
||||||
|
public interface IMachineProperty<TKey> : IMachinePropertyWithoutKey where TKey : IEquatable<TKey>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Id
|
||||||
|
/// </summary>
|
||||||
|
TKey Id { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,11 +10,6 @@ namespace Modbus.Net
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class BaseProtocal : BaseProtocal<byte[], byte[], ProtocalUnit>
|
public abstract class BaseProtocal : BaseProtocal<byte[], byte[], ProtocalUnit>
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// 协议的连接器
|
|
||||||
/// </summary>
|
|
||||||
public new ProtocalLinker ProtocalLinker { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造器
|
/// 构造器
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ namespace Modbus.Net
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 基础Api入口
|
/// 基础Api入口
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class BaseUtility : BaseUtility<byte[], byte[]>
|
public abstract class BaseUtility : BaseUtility<byte[], byte[], ProtocalUnit>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造器
|
/// 构造器
|
||||||
@@ -37,23 +37,17 @@ namespace Modbus.Net
|
|||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 协议收发主体
|
|
||||||
/// </summary>
|
|
||||||
protected new BaseProtocal Wrapper;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 基础Api入口
|
/// 基础Api入口
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class BaseUtility<TParamIn, TParamOut> : IUtilityProperty, IUtilityMethodData
|
public abstract class BaseUtility<TParamIn, TParamOut, TProtocalUnit> : IUtilityProperty, IUtilityMethodData where TProtocalUnit : ProtocalUnit<TParamIn, TParamOut>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 协议收发主体
|
/// 协议收发主体
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected BaseProtocal<TParamIn, TParamOut, ProtocalUnit<TParamIn, TParamOut>> Wrapper;
|
protected BaseProtocal<TParamIn, TParamOut, TProtocalUnit> Wrapper;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造器
|
/// 构造器
|
||||||
@@ -160,7 +154,7 @@ namespace Modbus.Net
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取数据
|
/// 获取数据
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -296,7 +290,7 @@ namespace Modbus.Net
|
|||||||
if (this is TUtilityMethod)
|
if (this is TUtilityMethod)
|
||||||
{
|
{
|
||||||
Type t = typeof(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);
|
.Invoke(this, parameters);
|
||||||
return (TReturnType)returnValue;
|
return (TReturnType)returnValue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,11 +9,6 @@ namespace Modbus.Net
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class ProtocalLinker : ProtocalLinker<byte[], byte[]>
|
public abstract class ProtocalLinker : ProtocalLinker<byte[], byte[]>
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// 传输连接器
|
|
||||||
/// </summary>
|
|
||||||
protected new BaseConnector BaseConnector;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 发送并接收数据
|
/// 发送并接收数据
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -8,34 +8,27 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Nito.AsyncEx;
|
||||||
|
|
||||||
namespace Modbus.Net
|
namespace Modbus.Net
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 返回结果的定义类
|
/// 返回结果的定义类
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class TaskReturnDef : TaskReturnDef<string>
|
public class DataReturnDef : DataReturnDef<string>
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 返回结果的定义类
|
/// 返回结果的定义类
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class TaskReturnDef<TMachineKey> where TMachineKey : IEquatable<TMachineKey>
|
public class DataReturnDef<TMachineKey> where TMachineKey : IEquatable<TMachineKey>
|
||||||
{
|
{
|
||||||
public TMachineKey MachineId { get; set; }
|
public TMachineKey MachineId { get; set; }
|
||||||
public Dictionary<string, ReturnUnit> ReturnValues { get; set; }
|
public Dictionary<string, ReturnUnit> ReturnValues { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 时间定义
|
|
||||||
/// </summary>
|
|
||||||
public static class TimeRestore
|
|
||||||
{
|
|
||||||
public static int Restore = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class LimitedConcurrencyLevelTaskScheduler : TaskScheduler
|
public class LimitedConcurrencyLevelTaskScheduler : TaskScheduler
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -105,7 +98,7 @@ namespace Modbus.Net
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void NotifyThreadPoolOfPendingWork()
|
private void NotifyThreadPoolOfPendingWork()
|
||||||
{
|
{
|
||||||
#if NET40 || NET45 || NET451 || NET452 || NET46 || NET461 || NET462
|
#if NET40 || NET45 || NET451 || NET452 || NET46 || NET461 || NET462 || NET47
|
||||||
ThreadPool.UnsafeQueueUserWorkItem(_ =>
|
ThreadPool.UnsafeQueueUserWorkItem(_ =>
|
||||||
#else
|
#else
|
||||||
ThreadPool.QueueUserWorkItem(_ =>
|
ThreadPool.QueueUserWorkItem(_ =>
|
||||||
@@ -199,51 +192,270 @@ namespace Modbus.Net
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class TaskMachine<TMachineKey> where TMachineKey : IEquatable<TMachineKey>
|
||||||
|
{
|
||||||
|
private TaskFactory _tasks { get; }
|
||||||
|
|
||||||
|
public TaskMachine(IMachineProperty<TMachineKey> machine, TaskFactory taskFactory)
|
||||||
|
{
|
||||||
|
Machine = machine;
|
||||||
|
_tasks = taskFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IMachineProperty<TMachineKey> Machine { get; }
|
||||||
|
|
||||||
|
public List<ITaskItem> TasksWithTimer { get; set; }
|
||||||
|
|
||||||
|
public bool InvokeTimer<TInterType>(TaskItem<TInterType> 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<bool> InvokeOnce<TInterType>(TaskItem<TInterType> task)
|
||||||
|
{
|
||||||
|
if (Machine.IsConnected)
|
||||||
|
{
|
||||||
|
var ans = await task.Invoke(Machine, _tasks, task.Params);
|
||||||
|
task.Return(ans);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class TaskMachineEqualityComparer<TKey> : IEqualityComparer<TaskMachine<TKey>>
|
||||||
|
where TKey : IEquatable<TKey>
|
||||||
|
{
|
||||||
|
public bool Equals(TaskMachine<TKey> x, TaskMachine<TKey> y)
|
||||||
|
{
|
||||||
|
return x.Machine.Id.Equals(y.Machine.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetHashCode(TaskMachine<TKey> obj)
|
||||||
|
{
|
||||||
|
return obj.GetHashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ITaskItem
|
||||||
|
{
|
||||||
|
string Name { get; set; }
|
||||||
|
|
||||||
|
bool StartTimer();
|
||||||
|
|
||||||
|
bool StopTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TaskItemGetData : TaskItem<DataReturnDef>
|
||||||
|
{
|
||||||
|
public TaskItemGetData(Action<DataReturnDef> 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<IMachineMethodData,
|
||||||
|
Task<Dictionary<string, ReturnUnit>>>("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<bool>
|
||||||
|
{
|
||||||
|
public TaskItemSetData(Dictionary<string, double> 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<IMachineMethodData,
|
||||||
|
Task<bool>>("SetDatasAsync", parameters[0],
|
||||||
|
MachineSetDataType.CommunicationTag).WithCancellation(cts.Token));
|
||||||
|
return ans;
|
||||||
|
};
|
||||||
|
Params = new object[]{values};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TaskItem<TInterType> : ITaskItem, IEquatable<TaskItem<TInterType>>
|
||||||
|
{
|
||||||
|
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<IMachinePropertyWithoutKey, TaskFactory, object[], Task<TInterType>> Invoke { get; set; }
|
||||||
|
internal Func<bool> DetectConnected { get; set; }
|
||||||
|
public object[] Params { get; set; }
|
||||||
|
public Action<TInterType> Return { get; set; }
|
||||||
|
internal Func<IMachinePropertyWithoutKey> GetMachine { get; set; }
|
||||||
|
internal Func<TaskFactory> GetTaskFactory { get; set; }
|
||||||
|
|
||||||
|
public bool Equals(TaskItem<TInterType> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 任务调度器
|
/// 任务调度器
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class TaskManager : TaskManager<string>
|
public class TaskManager : TaskManager<string>
|
||||||
{
|
{
|
||||||
public TaskManager(int maxRunningTask, int getCycle, bool keepConnect,
|
public TaskManager(int maxRunningTask, bool keepConnect,
|
||||||
MachineDataType dataType = MachineDataType.CommunicationTag)
|
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;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 执行对具体设备的数据更新
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="machine">设备的实例</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
protected override async Task RunTask(IMachineProperty<string> 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)
|
public void AddMachine(BaseMachine machine)
|
||||||
{
|
{
|
||||||
base.AddMachine(machine);
|
base.AddMachine(machine);
|
||||||
@@ -271,29 +483,13 @@ namespace Modbus.Net
|
|||||||
/// <typeparam name="TMachineKey"></typeparam>
|
/// <typeparam name="TMachineKey"></typeparam>
|
||||||
public class TaskManager<TMachineKey> where TMachineKey : IEquatable<TMachineKey>
|
public class TaskManager<TMachineKey> where TMachineKey : IEquatable<TMachineKey>
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// 返回数据代理
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="returnValue"></param>
|
|
||||||
public delegate void ReturnValuesDelegate(TaskReturnDef<TMachineKey> returnValue);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 正在运行的设备
|
/// 正在运行的设备
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly HashSet<IMachineProperty<TMachineKey>> _machines;
|
private readonly HashSet<TaskMachine<TMachineKey>> _machines;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 不在运行的设备
|
|
||||||
/// </summary>
|
|
||||||
private readonly HashSet<IMachineProperty<TMachineKey>> _unlinkedMachines;
|
|
||||||
|
|
||||||
private CancellationTokenSource _cts;
|
private CancellationTokenSource _cts;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取间隔
|
|
||||||
/// </summary>
|
|
||||||
private int _getCycle;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 保持连接
|
/// 保持连接
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -309,34 +505,32 @@ namespace Modbus.Net
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private TaskFactory _tasks;
|
private TaskFactory _tasks;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 正常读取的计时器
|
|
||||||
/// </summary>
|
|
||||||
private Timer _timer;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 重连计时器
|
|
||||||
/// </summary>
|
|
||||||
private Timer _timer2;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造一个TaskManager
|
/// 构造一个TaskManager
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="maxRunningTask">同时可以运行的任务数</param>
|
/// <param name="maxRunningTask">同时可以运行的任务数</param>
|
||||||
/// <param name="getCycle">读取数据的时间间隔(毫秒)</param>
|
|
||||||
/// <param name="keepConnect">读取数据后是否保持连接</param>
|
/// <param name="keepConnect">读取数据后是否保持连接</param>
|
||||||
/// <param name="dataType">获取与设置数据的方式</param>
|
/// <param name="dataType">获取与设置数据的方式</param>
|
||||||
public TaskManager(int maxRunningTask, int getCycle, bool keepConnect,
|
public TaskManager(int maxRunningTask, bool keepConnect,
|
||||||
MachineDataType dataType = MachineDataType.CommunicationTag)
|
MachineDataType dataType = MachineDataType.CommunicationTag)
|
||||||
{
|
{
|
||||||
_scheduler = new LimitedConcurrencyLevelTaskScheduler(maxRunningTask);
|
_scheduler = new LimitedConcurrencyLevelTaskScheduler(maxRunningTask);
|
||||||
_machines =
|
_machines =
|
||||||
new HashSet<IMachineProperty<TMachineKey>>(new BaseMachineEqualityComparer<TMachineKey>());
|
new HashSet<TaskMachine<TMachineKey>>(new TaskMachineEqualityComparer<TMachineKey>());
|
||||||
_unlinkedMachines =
|
|
||||||
new HashSet<IMachineProperty<TMachineKey>>(new BaseMachineEqualityComparer<TMachineKey>());
|
|
||||||
_getCycle = getCycle;
|
|
||||||
KeepConnect = keepConnect;
|
KeepConnect = keepConnect;
|
||||||
MachineDataType = dataType;
|
MachineDataType = dataType;
|
||||||
|
_cts = new CancellationTokenSource();
|
||||||
|
_tasks = new TaskFactory(_cts.Token, TaskCreationOptions.None, TaskContinuationOptions.None, _scheduler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 强制停止所有正在运行的任务
|
||||||
|
/// </summary>
|
||||||
|
public void TaskHalt()
|
||||||
|
{
|
||||||
|
_cts.Cancel();
|
||||||
|
_cts = new CancellationTokenSource();
|
||||||
|
_tasks = new TaskFactory(_cts.Token, TaskCreationOptions.None, TaskContinuationOptions.None, _scheduler);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -347,61 +541,16 @@ namespace Modbus.Net
|
|||||||
get { return _keepConnect; }
|
get { return _keepConnect; }
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
TaskStop();
|
PauseTimerAll();
|
||||||
_keepConnect = value;
|
_keepConnect = value;
|
||||||
lock (_machines)
|
lock (_machines)
|
||||||
{
|
{
|
||||||
foreach (var machine in _machines)
|
foreach (var machine in _machines)
|
||||||
{
|
{
|
||||||
machine.KeepConnect = _keepConnect;
|
machine.Machine.KeepConnect = _keepConnect;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
ContinueTimerAll();
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取间隔,毫秒
|
|
||||||
/// </summary>
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -450,26 +599,23 @@ namespace Modbus.Net
|
|||||||
get { return _scheduler.MaximumConcurrencyLevel; }
|
get { return _scheduler.MaximumConcurrencyLevel; }
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
TaskStop();
|
PauseTimerAll();
|
||||||
_scheduler = new LimitedConcurrencyLevelTaskScheduler(value);
|
_scheduler = new LimitedConcurrencyLevelTaskScheduler(value);
|
||||||
|
ContinueTimerAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 返回数据事件
|
|
||||||
/// </summary>
|
|
||||||
public event ReturnValuesDelegate ReturnValues;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 添加一台设备
|
/// 添加一台设备
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="machine">设备</param>
|
/// <param name="machine">设备</param>
|
||||||
public void AddMachine<TUnitKey>(BaseMachine<TMachineKey, TUnitKey> machine) where TUnitKey : IEquatable<TUnitKey>
|
public void AddMachine<TUnitKey>(BaseMachine<TMachineKey, TUnitKey> machine)
|
||||||
|
where TUnitKey : IEquatable<TUnitKey>
|
||||||
{
|
{
|
||||||
machine.KeepConnect = KeepConnect;
|
machine.KeepConnect = KeepConnect;
|
||||||
lock (_machines)
|
lock (_machines)
|
||||||
{
|
{
|
||||||
_machines.Add(machine);
|
_machines.Add(new TaskMachine<TMachineKey>(machine, _tasks) {TasksWithTimer = new List<ITaskItem>()});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -477,7 +623,8 @@ namespace Modbus.Net
|
|||||||
/// 添加多台设备
|
/// 添加多台设备
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="machines">设备的列表</param>
|
/// <param name="machines">设备的列表</param>
|
||||||
public void AddMachines<TUnitKey>(IEnumerable<BaseMachine<TMachineKey, TUnitKey>> machines) where TUnitKey : IEquatable<TUnitKey>
|
public void AddMachines<TUnitKey>(IEnumerable<BaseMachine<TMachineKey, TUnitKey>> machines)
|
||||||
|
where TUnitKey : IEquatable<TUnitKey>
|
||||||
{
|
{
|
||||||
lock (_machines)
|
lock (_machines)
|
||||||
{
|
{
|
||||||
@@ -493,17 +640,10 @@ namespace Modbus.Net
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
IMachineProperty<TMachineKey> machine;
|
TaskMachine<TMachineKey> machine;
|
||||||
lock (_machines)
|
lock (_machines)
|
||||||
{
|
{
|
||||||
machine = _machines.FirstOrDefault(p => p.Id.Equals(id));
|
machine = _machines.FirstOrDefault(p => p.Machine.Id.Equals(id));
|
||||||
if (machine == null)
|
|
||||||
{
|
|
||||||
lock (_unlinkedMachines)
|
|
||||||
{
|
|
||||||
machine = _unlinkedMachines.FirstOrDefault(p => p.Id.Equals(id));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return machine as BaseMachine<TMachineKey, TUnitKey>;
|
return machine as BaseMachine<TMachineKey, TUnitKey>;
|
||||||
}
|
}
|
||||||
@@ -519,17 +659,10 @@ namespace Modbus.Net
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
IMachineProperty<TMachineKey> machine;
|
TaskMachine<TMachineKey> machine;
|
||||||
lock (_machines)
|
lock (_machines)
|
||||||
{
|
{
|
||||||
machine = _machines.FirstOrDefault(p => p.ConnectionToken == connectionToken);
|
machine = _machines.FirstOrDefault(p => p.Machine.ConnectionToken == connectionToken);
|
||||||
if (machine == null)
|
|
||||||
{
|
|
||||||
lock (_unlinkedMachines)
|
|
||||||
{
|
|
||||||
machine = _unlinkedMachines.FirstOrDefault(p => p.ConnectionToken == connectionToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return machine as BaseMachine<TMachineKey, TUnitKey>;
|
return machine as BaseMachine<TMachineKey, TUnitKey>;
|
||||||
}
|
}
|
||||||
@@ -548,11 +681,7 @@ namespace Modbus.Net
|
|||||||
{
|
{
|
||||||
lock (_machines)
|
lock (_machines)
|
||||||
{
|
{
|
||||||
_machines.RemoveWhere(p => p.ConnectionToken == machineToken);
|
_machines.RemoveWhere(p => p.Machine.ConnectionToken == machineToken);
|
||||||
}
|
|
||||||
lock (_unlinkedMachines)
|
|
||||||
{
|
|
||||||
_unlinkedMachines.RemoveWhere(p => p.ConnectionToken == machineToken);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -564,55 +693,7 @@ namespace Modbus.Net
|
|||||||
{
|
{
|
||||||
lock (_machines)
|
lock (_machines)
|
||||||
{
|
{
|
||||||
_machines.RemoveWhere(p => p.Id.Equals(id));
|
_machines.RemoveWhere(p => p.Machine.Id.Equals(id));
|
||||||
}
|
|
||||||
lock (_unlinkedMachines)
|
|
||||||
{
|
|
||||||
_unlinkedMachines.RemoveWhere(p => p.Id.Equals(id));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 将设备指定为未连接
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="id">设备的id</param>
|
|
||||||
public void MoveMachineToUnlinked(TMachineKey id)
|
|
||||||
{
|
|
||||||
IEnumerable<IMachineProperty<TMachineKey>> 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 将设备指定为已连接
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="id">设备的id</param>
|
|
||||||
public void MoveMachineToLinked(TMachineKey id)
|
|
||||||
{
|
|
||||||
IEnumerable<IMachineProperty<TMachineKey>> 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -624,192 +705,161 @@ namespace Modbus.Net
|
|||||||
{
|
{
|
||||||
lock (_machines)
|
lock (_machines)
|
||||||
{
|
{
|
||||||
_machines.Remove(machine);
|
_machines.RemoveWhere(p=>p.Machine.Equals(machine));
|
||||||
}
|
|
||||||
lock (_unlinkedMachines)
|
|
||||||
{
|
|
||||||
_unlinkedMachines.Remove(machine);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public bool InvokeTimerAll<TInterType>(TaskItem<TInterType> item)
|
||||||
/// 已连接设备更新
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="sender"></param>
|
|
||||||
private void MaintainTasks(object sender)
|
|
||||||
{
|
|
||||||
AsyncHelper.RunSync(MaintainTasksAsync);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 未连接设备更新
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="sender"></param>
|
|
||||||
private void MaintainTasks2(object sender)
|
|
||||||
{
|
|
||||||
AsyncHelper.RunSync(MaintainTasks2Async);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 已连接设备更新
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
private async Task MaintainTasksAsync()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var tasks = new List<Task>();
|
|
||||||
var saveMachines = new HashSet<IMachineProperty<TMachineKey>>();
|
|
||||||
IEnumerable<IMachineProperty<TMachineKey>> 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 未连接设备更新
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
private async Task MaintainTasks2Async()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var tasks = new List<Task>();
|
|
||||||
var saveMachines = new HashSet<IMachineProperty<TMachineKey>>();
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 设置数据
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="connectionToken">设备的连接标识</param>
|
|
||||||
/// <param name="values">需要设置的数据</param>
|
|
||||||
/// <returns>是否设置成功</returns>
|
|
||||||
public async Task<bool> SetDatasAsync(string connectionToken,
|
|
||||||
Dictionary<string, double> values)
|
|
||||||
{
|
|
||||||
IMachineProperty<TMachineKey> machine = null;
|
|
||||||
lock (_machines)
|
|
||||||
{
|
|
||||||
machine = _machines.FirstOrDefault(p => p.ConnectionToken == connectionToken);
|
|
||||||
}
|
|
||||||
if (machine == null) return false;
|
|
||||||
return await machine.InvokeMachineMethod<IMachineMethodData, Task<bool>>("SetDatasAsync", SetDataType, values);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 启动TaskManager
|
|
||||||
/// </summary>
|
|
||||||
public void TaskStart()
|
|
||||||
{
|
|
||||||
TaskStop();
|
|
||||||
_cts = new CancellationTokenSource();
|
|
||||||
_tasks = new TaskFactory(_cts.Token, TaskCreationOptions.None, TaskContinuationOptions.None, _scheduler);
|
|
||||||
GetCycle = TimeRestore.Restore;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 停止TaskManager
|
|
||||||
/// </summary>
|
|
||||||
public void TaskStop()
|
|
||||||
{
|
{
|
||||||
lock (_machines)
|
lock (_machines)
|
||||||
{
|
{
|
||||||
GetCycle = Timeout.Infinite;
|
foreach (var machine in _machines)
|
||||||
_cts?.Cancel();
|
|
||||||
if (_machines != null)
|
|
||||||
{
|
{
|
||||||
foreach (var machine in _machines)
|
machine.InvokeTimer(item);
|
||||||
{
|
|
||||||
machine.Disconnect();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_tasks = null;
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public bool StopTimerAll()
|
||||||
/// 执行对具体设备的数据更新
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="machine">设备的实例</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
protected virtual async Task RunTask(IMachineProperty<TMachineKey> machine)
|
|
||||||
{
|
{
|
||||||
try
|
lock (_machines)
|
||||||
{
|
{
|
||||||
var ans = await GetValue(machine);
|
foreach (var machine in _machines)
|
||||||
ReturnValues?.Invoke(new TaskReturnDef<TMachineKey>
|
|
||||||
{
|
{
|
||||||
MachineId = machine.Id,
|
machine.StopAllTimers();
|
||||||
ReturnValues = ans
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
if (!machine.IsConnected)
|
|
||||||
{
|
|
||||||
MoveMachineToUnlinked(machine.Id);
|
|
||||||
}
|
}
|
||||||
ReturnValues?.Invoke(new TaskReturnDef<TMachineKey>
|
|
||||||
{
|
|
||||||
MachineId = machine.Id,
|
|
||||||
ReturnValues = null
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async Task<Dictionary<string, ReturnUnit>> GetValue(IMachineProperty<TMachineKey> machine)
|
public bool StopTimerAll(string taskItemName)
|
||||||
{
|
{
|
||||||
//调试代码,调试时取消下面一下代码的注释,会同步调用获取数据。
|
lock (_machines)
|
||||||
//var ans = machine.GetDatas();
|
|
||||||
//设置Cancellation Token
|
|
||||||
var cts = new CancellationTokenSource();
|
|
||||||
//超时后取消任务
|
|
||||||
cts.CancelAfter(TimeSpan.FromSeconds(_getCycle));
|
|
||||||
//读取数据
|
|
||||||
var ans =
|
|
||||||
await machine.InvokeMachineMethod<IMachineMethodData, Task<Dictionary<string, ReturnUnit>>>("GetDatasAsync",
|
|
||||||
GetDataType).WithCancellation(cts.Token);
|
|
||||||
if (!machine.IsConnected)
|
|
||||||
{
|
{
|
||||||
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<bool> InvokeOnceAll<TInterType>(TaskItem<TInterType> item)
|
||||||
|
{
|
||||||
|
var tasks = new List<Task<bool>>();
|
||||||
|
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<bool> InvokeOnceForMachine<TInterType>(TMachineKey machineId, TaskItem<TInterType> 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<TInterType>(TMachineKey machineId, TaskItem<TInterType> 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
126
Modbus.Net/src/Base.Common/TypeExtensions.cs
Normal file
126
Modbus.Net/src/Base.Common/TypeExtensions.cs
Normal file
@@ -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
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks for the method in the type matching the name and arguments.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type"></param>
|
||||||
|
/// <param name="methodName">
|
||||||
|
/// The name of the method to find.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="args">
|
||||||
|
/// The types of the method's arguments to match.
|
||||||
|
/// </param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="ArgumentNullException">
|
||||||
|
/// Thrown if:
|
||||||
|
/// - The name of the method is not specified.
|
||||||
|
/// </exception>
|
||||||
|
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
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds out if the provided arguments matches the specified method's signature.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="methodInfo"></param>
|
||||||
|
/// <param name="args"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,
|
task.AddMachine(new ModbusMachine(ModbusType.Tcp, "192.168.3.10", addressUnits,
|
||||||
true, 2, 0));
|
true, 2, 0));
|
||||||
//增加值返回时的处理函数
|
//启动任务
|
||||||
task.ReturnValues += (returnValues) =>
|
task.InvokeTimerAll(new TaskItemGetData(returnValues =>
|
||||||
{
|
{
|
||||||
//唯一的参数包含返回值,是一个唯一标识符(machine的第二个参数),返回值(类型ReturnUnit)的键值对。
|
//唯一的参数包含返回值,是一个唯一标识符(machine的第二个参数),返回值(类型ReturnUnit)的键值对。
|
||||||
if (returnValues.ReturnValues != null)
|
if (returnValues.ReturnValues != null)
|
||||||
{
|
{
|
||||||
lock (values)
|
lock (values)
|
||||||
{
|
{
|
||||||
var unitValues = from val in returnValues.ReturnValues select new Tuple<AddressUnit, double?>(addressUnits.FirstOrDefault(p => p.CommunicationTag == val.Key), val.Value.PlcValue);
|
var unitValues = from val in returnValues.ReturnValues
|
||||||
|
select
|
||||||
|
new Tuple<AddressUnit, double?>(
|
||||||
|
addressUnits.FirstOrDefault(p => p.CommunicationTag == val.Key), val.Value.PlcValue);
|
||||||
values = from unitValue in unitValues
|
values = from unitValue in unitValues
|
||||||
select
|
select
|
||||||
new TaskViewModel()
|
new TaskViewModel()
|
||||||
{
|
{
|
||||||
Id = unitValue.Item1.Id,
|
Id = unitValue.Item1.Id,
|
||||||
Name = unitValue.Item1.Name,
|
Name = unitValue.Item1.Name,
|
||||||
Address = unitValue.Item1.Address + "." + unitValue.Item1.SubAddress,
|
Address = unitValue.Item1.Address + "." + unitValue.Item1.SubAddress,
|
||||||
Value = unitValue.Item2 ?? 0,
|
Value = unitValue.Item2 ?? 0,
|
||||||
Type = unitValue.Item1.DataType.Name
|
Type = unitValue.Item1.DataType.Name
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Console.WriteLine($"ip {returnValues.MachineId} not return value");
|
Console.WriteLine($"ip {returnValues.MachineId} not return value");
|
||||||
}
|
}
|
||||||
};
|
}, 15000, 60000));
|
||||||
//启动任务
|
|
||||||
task.TaskStart();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
|
|||||||
@@ -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,
|
task.AddMachine(new ModbusMachine(ModbusType.Tcp, "192.168.3.10", addressUnits,
|
||||||
true, 2, 0));
|
true, 2, 0));
|
||||||
//增加值返回时的处理函数
|
//启动任务
|
||||||
task.ReturnValues += (returnValues) =>
|
task.InvokeTimerAll(new TaskItemGetData(returnValues =>
|
||||||
{
|
{
|
||||||
//唯一的参数包含返回值,是一个唯一标识符(machine的第二个参数),返回值(类型ReturnUnit)的键值对。
|
//唯一的参数包含返回值,是一个唯一标识符(machine的第二个参数),返回值(类型ReturnUnit)的键值对。
|
||||||
if (returnValues.ReturnValues != null)
|
if (returnValues.ReturnValues != null)
|
||||||
{
|
{
|
||||||
lock (values)
|
lock (values)
|
||||||
{
|
{
|
||||||
var unitValues = from val in returnValues.ReturnValues select new Tuple<AddressUnit, double?>(addressUnits.FirstOrDefault(p => p.CommunicationTag == val.Key), val.Value.PlcValue);
|
var unitValues = from val in returnValues.ReturnValues
|
||||||
|
select
|
||||||
|
new Tuple<AddressUnit, double?>(
|
||||||
|
addressUnits.FirstOrDefault(p => p.CommunicationTag == val.Key), val.Value.PlcValue);
|
||||||
values = from unitValue in unitValues
|
values = from unitValue in unitValues
|
||||||
select
|
select
|
||||||
new TaskViewModel()
|
new TaskViewModel()
|
||||||
{
|
{
|
||||||
Id = unitValue.Item1.Id,
|
Id = unitValue.Item1.Id,
|
||||||
Name = unitValue.Item1.Name,
|
Name = unitValue.Item1.Name,
|
||||||
Address = unitValue.Item1.Address.ToString(),
|
Address = unitValue.Item1.Address.ToString(),
|
||||||
Value = unitValue.Item2 ?? 0,
|
Value = unitValue.Item2 ?? 0,
|
||||||
Type = unitValue.Item1.DataType.Name
|
Type = unitValue.Item1.DataType.Name
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Console.WriteLine($"ip {returnValues.MachineId} not return value");
|
Console.WriteLine($"ip {returnValues.MachineId} not return value");
|
||||||
}
|
}
|
||||||
};
|
}, 15000, 60000));
|
||||||
//启动任务
|
|
||||||
task.TaskStart();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ namespace Modbus.Net.Tests
|
|||||||
MachineName = "Test 2"
|
MachineName = "Test 2"
|
||||||
};
|
};
|
||||||
|
|
||||||
_taskManager = new TaskManager<int>(10, 3000, true);
|
_taskManager = new TaskManager<int>(10, true);
|
||||||
|
|
||||||
_taskManager.AddMachine(_baseMachine);
|
_taskManager.AddMachine(_baseMachine);
|
||||||
_taskManager.AddMachine(_baseMachine2);
|
_taskManager.AddMachine(_baseMachine2);
|
||||||
|
|||||||
Reference in New Issue
Block a user