Merge branch 'dev'
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0</TargetFrameworks>
|
||||
<LangVersion>10.0</LangVersion>
|
||||
<AssemblyName>Modbus.Net.BigEndian3412</AssemblyName>
|
||||
<RootNamespace>Modbus.Net.BigEndian3412</RootNamespace>
|
||||
<PackageId>Modbus.Net.BigEndian3412</PackageId>
|
||||
<Version>1.4.2</Version>
|
||||
<Authors>Chris L.(Luo Sheng)</Authors>
|
||||
<Company>Hangzhou Delian Science Technology Co.,Ltd.</Company>
|
||||
<Product>Modbus.Net.Modbus</Product>
|
||||
<Description>Modbus.Net Modbus Implementation</Description>
|
||||
<Copyright>Copyright 2023 Hangzhou Delian Science Technology Co.,Ltd.</Copyright>
|
||||
<PackageProjectUrl>https://github.com/parallelbgls/Modbus.Net/tree/master/Modbus.Net/Modbus.Net.BigEndian3412</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/parallelbgls/Modbus.Net</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageTags>hardware communicate protocol modbus Delian</PackageTags>
|
||||
<PackageRequireLicenseAcceptance>False</PackageRequireLicenseAcceptance>
|
||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||
<IncludeSymbols>True</IncludeSymbols>
|
||||
<IncludeSource>True</IncludeSource>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Modbus.Net\Modbus.Net.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="README.md" Pack="true" PackagePath=""/>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
|
||||
namespace Modbus.Net
|
||||
{
|
||||
public partial class Endian
|
||||
{
|
||||
public const int BigEndian3412 = 10;
|
||||
}
|
||||
|
||||
public class BigEndian3412ValueHelper : BigEndianLsbValueHelper
|
||||
{
|
||||
private static BigEndian3412ValueHelper _bigEndian3412Instance;
|
||||
|
||||
/// <summary>
|
||||
/// 构造器
|
||||
/// </summary>
|
||||
protected BigEndian3412ValueHelper()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 覆写的实例获取
|
||||
/// </summary>
|
||||
protected override ValueHelper _Instance => _bigEndian3412Instance;
|
||||
|
||||
/// <summary>
|
||||
/// 是否为大端
|
||||
/// </summary>
|
||||
protected new bool LittleEndian => false;
|
||||
|
||||
protected new bool LittleEndianBit => false;
|
||||
|
||||
/// <summary>
|
||||
/// 覆盖的获取实例的方法
|
||||
/// </summary>
|
||||
public new static BigEndian3412ValueHelper Instance
|
||||
=> _bigEndian3412Instance ?? (_bigEndian3412Instance = new BigEndian3412ValueHelper());
|
||||
|
||||
public override float GetFloat(byte[] data, ref int pos)
|
||||
{
|
||||
Array.Reverse(data, pos, 4);
|
||||
byte temp;
|
||||
temp = data[pos]; data[pos] = data[pos + 2]; data[pos + 2] = temp;
|
||||
temp = data[pos + 1]; data[pos + 1] = data[pos + 3]; data[pos + 3] = temp;
|
||||
var t = BitConverter.ToSingle(data, pos);
|
||||
temp = data[pos]; data[pos] = data[pos + 2]; data[pos + 2] = temp;
|
||||
temp = data[pos + 1]; data[pos + 1] = data[pos + 3]; data[pos + 3] = temp;
|
||||
Array.Reverse(data, pos, 4);
|
||||
pos += 4;
|
||||
return t;
|
||||
}
|
||||
}
|
||||
}
|
||||
5
Modbus.Net/Modbus.Net.BigEndian3412/README.md
Normal file
5
Modbus.Net/Modbus.Net.BigEndian3412/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
Modbus.Net.BigEndian3412
|
||||
===================
|
||||
Self defined EndianHelper implementation of Modbus.Net
|
||||
|
||||
Doc has been moved to wiki.
|
||||
@@ -3,7 +3,7 @@
|
||||
/// <summary>
|
||||
/// 南大奥拓NA200H专用AddressFormater
|
||||
/// </summary>
|
||||
public class AddressFormaterNA200H : AddressFormater
|
||||
public class AddressFormaterNA200H : AddressFormater<int, int>
|
||||
{
|
||||
/// <summary>
|
||||
/// 格式化地址
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0;net462</TargetFrameworks>
|
||||
<TargetFrameworks>net6.0</TargetFrameworks>
|
||||
<LangVersion>10.0</LangVersion>
|
||||
<AssemblyName>Modbus.Net.Modbus.NA200H</AssemblyName>
|
||||
<RootNamespace>Modbus.Net.Modbus.NA200H</RootNamespace>
|
||||
<PackageId>Modbus.Net.Modbus.NA200H</PackageId>
|
||||
<Version>1.4.1</Version>
|
||||
<Version>1.4.2</Version>
|
||||
<Authors>Chris L.(Luo Sheng)</Authors>
|
||||
<Company>Hangzhou Delian Science Technology Co.,Ltd.</Company>
|
||||
<Product>Modbus.Net.Modbus</Product>
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
Modbus.Net.Modbus
|
||||
===================
|
||||
[](https://www.nuget.org/packages/Modbus.Net.Modbus.NA200H/)
|
||||
|
||||
NA200H Implementation of Modbus.Net
|
||||
|
||||
Doc has been moved to wiki.
|
||||
@@ -1,18 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0;net462</TargetFrameworks>
|
||||
<TargetFrameworks>net6.0</TargetFrameworks>
|
||||
<LangVersion>10.0</LangVersion>
|
||||
<AssemblyName>Modbus.Net.Modbus.SelfDefinedSample</AssemblyName>
|
||||
<RootNamespace>Modbus.Net.Modbus.SelfDefinedSample</RootNamespace>
|
||||
<PackageId>Modbus.Net.Modbus.SelfDefinedSample</PackageId>
|
||||
<Version>1.4.1</Version>
|
||||
<Version>1.4.2</Version>
|
||||
<Authors>Chris L.(Luo Sheng)</Authors>
|
||||
<Company>Hangzhou Delian Science Technology Co.,Ltd.</Company>
|
||||
<Product>Modbus.Net.Modbus</Product>
|
||||
<Description>Modbus.Net Modbus Implementation</Description>
|
||||
<Copyright>Copyright 2023 Hangzhou Delian Science Technology Co.,Ltd.</Copyright>
|
||||
<PackageProjectUrl>https://github.com/parallelbgls/Modbus.Net/tree/master/Modbus.Net/Modbus.Net.Modbus.NA200H</PackageProjectUrl>
|
||||
<PackageProjectUrl>https://github.com/parallelbgls/Modbus.Net/tree/master/Modbus.Net/Modbus.Net.Modbus.SelfDefinedSample</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/parallelbgls/Modbus.Net</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageTags>hardware communicate protocol modbus Delian</PackageTags>
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Modbus.Net.Modbus.SelfDefinedSample
|
||||
/// <param name="masterAddress">主站号</param>
|
||||
/// <param name="endian">端格式</param>
|
||||
public ModbusUtilityTime(int connectionType, byte slaveAddress, byte masterAddress,
|
||||
Endian endian = Endian.BigEndianLsb)
|
||||
Endian endian)
|
||||
: base(connectionType, slaveAddress, masterAddress, endian)
|
||||
{
|
||||
}
|
||||
@@ -30,7 +30,7 @@ namespace Modbus.Net.Modbus.SelfDefinedSample
|
||||
/// <param name="masterAddress">主站号</param>
|
||||
/// <param name="endian">端格式</param>
|
||||
public ModbusUtilityTime(ModbusType connectionType, string connectionString, byte slaveAddress, byte masterAddress,
|
||||
Endian endian = Endian.BigEndianLsb)
|
||||
Endian endian)
|
||||
: base(connectionType, connectionString, slaveAddress, masterAddress, endian)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
Modbus.Net.Modbus
|
||||
===================
|
||||
[](https://www.nuget.org/packages/Modbus.Net.Modbus.SelfDefinedSample/)
|
||||
|
||||
Self defined function get time and set time implementation of Modbus.Net
|
||||
|
||||
Doc has been moved to wiki.
|
||||
@@ -3,7 +3,7 @@
|
||||
/// <summary>
|
||||
/// Modbus标准AddressFormater
|
||||
/// </summary>
|
||||
public class AddressFormaterModbus : AddressFormater
|
||||
public class AddressFormaterModbus : AddressFormater<int, int>
|
||||
{
|
||||
/// <summary>
|
||||
/// 格式化地址
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0;net462</TargetFrameworks>
|
||||
<TargetFrameworks>net6.0</TargetFrameworks>
|
||||
<LangVersion>10.0</LangVersion>
|
||||
<AssemblyName>Modbus.Net.Modbus</AssemblyName>
|
||||
<RootNamespace>Modbus.Net.Modbus</RootNamespace>
|
||||
<PackageId>Modbus.Net.Modbus</PackageId>
|
||||
<Version>1.4.1</Version>
|
||||
<Version>1.4.2</Version>
|
||||
<Authors>Chris L.(Luo Sheng)</Authors>
|
||||
<Company>Hangzhou Delian Science Technology Co.,Ltd.</Company>
|
||||
<Product>Modbus.Net.Modbus</Product>
|
||||
|
||||
@@ -21,8 +21,8 @@ namespace Modbus.Net.Modbus
|
||||
/// <param name="masterAddress">主站号</param>
|
||||
/// <param name="endian">端格式</param>
|
||||
public ModbusMachine(TKey id, ModbusType connectionType, string connectionString,
|
||||
IEnumerable<AddressUnit<TUnitKey>> getAddresses, bool keepConnect, byte slaveAddress, byte masterAddress,
|
||||
Endian endian = Endian.BigEndianLsb)
|
||||
IEnumerable<AddressUnit<TUnitKey, int, int>> getAddresses, bool keepConnect, byte slaveAddress, byte masterAddress,
|
||||
Endian endian)
|
||||
: base(id, getAddresses, keepConnect, slaveAddress, masterAddress)
|
||||
{
|
||||
BaseUtility = new ModbusUtility(connectionType, connectionString, slaveAddress, masterAddress, endian);
|
||||
@@ -42,8 +42,8 @@ namespace Modbus.Net.Modbus
|
||||
/// <param name="masterAddress">主站号</param>
|
||||
/// <param name="endian">端格式</param>
|
||||
public ModbusMachine(TKey id, ModbusType connectionType, string connectionString,
|
||||
IEnumerable<AddressUnit<TUnitKey>> getAddresses, byte slaveAddress, byte masterAddress,
|
||||
Endian endian = Endian.BigEndianLsb)
|
||||
IEnumerable<AddressUnit<TUnitKey, int, int>> getAddresses, byte slaveAddress, byte masterAddress,
|
||||
Endian endian)
|
||||
: this(id, connectionType, connectionString, getAddresses, true, slaveAddress, masterAddress, endian)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -67,9 +67,9 @@ namespace Modbus.Net.Modbus
|
||||
var transaction = (ushort)(_sendCount % 65536 + 1);
|
||||
var tag = (ushort)0;
|
||||
var leng = (ushort)content.Length;
|
||||
Array.Copy(BigEndianValueHelper.Instance.GetBytes(transaction), 0, newFormat, 0, 2);
|
||||
Array.Copy(BigEndianValueHelper.Instance.GetBytes(tag), 0, newFormat, 2, 2);
|
||||
Array.Copy(BigEndianValueHelper.Instance.GetBytes(leng), 0, newFormat, 4, 2);
|
||||
Array.Copy(BigEndianLsbValueHelper.Instance.GetBytes(transaction), 0, newFormat, 0, 2);
|
||||
Array.Copy(BigEndianLsbValueHelper.Instance.GetBytes(tag), 0, newFormat, 2, 2);
|
||||
Array.Copy(BigEndianLsbValueHelper.Instance.GetBytes(leng), 0, newFormat, 4, 2);
|
||||
Array.Copy(content, 0, newFormat, 6, content.Length);
|
||||
_sendCount++;
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ namespace Modbus.Net.Modbus
|
||||
/// <param name="masterAddress">主站号</param>
|
||||
/// <param name="endian">端格式</param>
|
||||
public ModbusUtility(int connectionType, byte slaveAddress, byte masterAddress,
|
||||
Endian endian = Endian.BigEndianLsb)
|
||||
Endian endian)
|
||||
: base(slaveAddress, masterAddress)
|
||||
{
|
||||
Endian = endian;
|
||||
@@ -97,7 +97,7 @@ namespace Modbus.Net.Modbus
|
||||
/// <param name="masterAddress">主站号</param>
|
||||
/// <param name="endian">端格式</param>
|
||||
public ModbusUtility(ModbusType connectionType, string connectionString, byte slaveAddress, byte masterAddress,
|
||||
Endian endian = Endian.BigEndianLsb)
|
||||
Endian endian)
|
||||
: base(slaveAddress, masterAddress)
|
||||
{
|
||||
Endian = endian;
|
||||
|
||||
@@ -6,16 +6,16 @@ namespace Modbus.Net.Opc
|
||||
/// <summary>
|
||||
/// Opc地址编码器
|
||||
/// </summary>
|
||||
public class AddressFormaterOpc<TMachineKey, TUnitKey> : AddressFormater where TMachineKey : IEquatable<TMachineKey>
|
||||
where TUnitKey : IEquatable<TUnitKey>
|
||||
public class AddressFormaterOpc<TMachineKey, TUnitKey, TAddressKey, TSubAddressKey> : AddressFormater<TAddressKey, TSubAddressKey> where TMachineKey : IEquatable<TMachineKey>
|
||||
where TUnitKey : IEquatable<TUnitKey> where TAddressKey : IEquatable<TAddressKey> where TSubAddressKey : IEquatable<TSubAddressKey>
|
||||
{
|
||||
/// <summary>
|
||||
/// 协议构造器
|
||||
/// </summary>
|
||||
/// <param name="tagGeter">如何通过BaseMachine和AddressUnit构造Opc的标签</param>
|
||||
/// <param name="machine">调用这个编码器的设备</param>
|
||||
public AddressFormaterOpc(Func<BaseMachine<TMachineKey, TUnitKey>, AddressUnit<TUnitKey>, string[]> tagGeter,
|
||||
BaseMachine<TMachineKey, TUnitKey> machine)
|
||||
public AddressFormaterOpc(Func<BaseMachine<TMachineKey, TUnitKey, TAddressKey, TSubAddressKey>, AddressUnit<TUnitKey, TAddressKey, TSubAddressKey>, string> tagGeter,
|
||||
BaseMachine<TMachineKey, TUnitKey, TAddressKey, TSubAddressKey> machine)
|
||||
{
|
||||
Machine = machine;
|
||||
TagGeter = tagGeter;
|
||||
@@ -24,13 +24,13 @@ namespace Modbus.Net.Opc
|
||||
/// <summary>
|
||||
/// 设备
|
||||
/// </summary>
|
||||
public BaseMachine<TMachineKey, TUnitKey> Machine { get; set; }
|
||||
public BaseMachine<TMachineKey, TUnitKey, TAddressKey, TSubAddressKey> Machine { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 标签构造器
|
||||
/// (设备,地址)->不具备分隔符的标签数组
|
||||
/// </summary>
|
||||
protected Func<BaseMachine<TMachineKey, TUnitKey>, AddressUnit<TUnitKey>, string[]> TagGeter { get; set; }
|
||||
protected Func<BaseMachine<TMachineKey, TUnitKey, TAddressKey, TSubAddressKey>, AddressUnit<TUnitKey, TAddressKey, TSubAddressKey>, string> TagGeter { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 编码地址
|
||||
@@ -38,15 +38,12 @@ namespace Modbus.Net.Opc
|
||||
/// <param name="area">地址所在的数据区域</param>
|
||||
/// <param name="address">地址</param>
|
||||
/// <returns>编码后的地址</returns>
|
||||
public override string FormatAddress(string area, int address)
|
||||
public override string FormatAddress(string area, TAddressKey address)
|
||||
{
|
||||
var findAddress = Machine?.GetAddresses.FirstOrDefault(p => p.Area == area && p.Address == address);
|
||||
var findAddress = Machine?.GetAddresses.FirstOrDefault(p => p.Area == area && p.Address.Equals(address));
|
||||
if (findAddress == null) return null;
|
||||
var strings = TagGeter(Machine, findAddress);
|
||||
var ans = "";
|
||||
for (var i = 0; i < strings.Length; i++)
|
||||
ans += strings[i].Trim().Replace(" ", "") + '\r';
|
||||
ans = ans.Substring(0, ans.Length - 1);
|
||||
var ans = TagGeter(Machine, findAddress);
|
||||
ans = ans.Trim().Replace(" ", "");
|
||||
return ans;
|
||||
}
|
||||
|
||||
@@ -57,7 +54,7 @@ namespace Modbus.Net.Opc
|
||||
/// <param name="address">地址</param>
|
||||
/// <param name="subAddress">子地址(忽略)</param>
|
||||
/// <returns>编码后的地址</returns>
|
||||
public override string FormatAddress(string area, int address, int subAddress)
|
||||
public override string FormatAddress(string area, TAddressKey address, TSubAddressKey subAddress)
|
||||
{
|
||||
return FormatAddress(area, address);
|
||||
}
|
||||
|
||||
@@ -3,9 +3,7 @@ using Hylasoft.Opc.Da;
|
||||
using Hylasoft.Opc.Ua;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using URL = Opc.URL;
|
||||
|
||||
namespace Modbus.Net.Opc
|
||||
{
|
||||
@@ -73,18 +71,11 @@ namespace Modbus.Net.Opc
|
||||
public class MyDaClient : DaClient, IClientExtend
|
||||
{
|
||||
/// <summary>
|
||||
/// UaClient Extend
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="serverUrl">Url address of Opc UA server</param>
|
||||
/// <param name="serverUrl">OpcDa服务端Url</param>
|
||||
public MyDaClient(Uri serverUrl) : base(serverUrl)
|
||||
{
|
||||
var url = new URL(serverUrl.OriginalString)
|
||||
{
|
||||
Scheme = serverUrl.Scheme,
|
||||
HostName = serverUrl.Host
|
||||
};
|
||||
|
||||
typeof(DaClient).GetField("_url", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(this, url);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net462</TargetFramework>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<LangVersion>10.0</LangVersion>
|
||||
<AssemblyName>Modbus.Net.Opc</AssemblyName>
|
||||
<RootNamespace>Modbus.Net.Opc</RootNamespace>
|
||||
<PackageId>Modbus.Net.Opc</PackageId>
|
||||
<Version>1.4.1</Version>
|
||||
<Version>1.4.2</Version>
|
||||
<Authors>Chris L.(Luo Sheng)</Authors>
|
||||
<Company>Hangzhou Delian Science Technology Co.,Ltd.</Company>
|
||||
<Product>Modbus.Net.Opc</Product>
|
||||
@@ -20,25 +20,18 @@
|
||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||
<IncludeSymbols>True</IncludeSymbols>
|
||||
<IncludeSource>True</IncludeSource>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<Platforms>AnyCPU;x86</Platforms>
|
||||
<Platforms>AnyCPU</Platforms>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DocumentationFile>bin\Debug\Modbus.Net.Opc.xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'">
|
||||
<DocumentationFile>bin\Debug\Modbus.Net.Opc.xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="H.Opc" Version="0.9.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\h-opc\h-opc\h-opc.csproj" />
|
||||
<ProjectReference Include="..\Modbus.Net\Modbus.Net.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -96,13 +96,36 @@ namespace Modbus.Net.Opc
|
||||
if (tag != null)
|
||||
{
|
||||
var result = await Client.ReadAsync<object>(tag);
|
||||
logger.LogInformation($"Opc Machine {ConnectionToken} Read Opc tag {tag} for value {result.Value}");
|
||||
object resultTrans;
|
||||
if (result.Value?.ToString() == "False")
|
||||
{
|
||||
resultTrans = (byte)0;
|
||||
}
|
||||
else if (result.Value?.ToString() == "True")
|
||||
{
|
||||
resultTrans = (byte)1;
|
||||
}
|
||||
else if (result.Value != null)
|
||||
{
|
||||
resultTrans = result.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogError($"Opc Machine {ConnectionToken} Read Opc tag {tag} for value null");
|
||||
return new OpcParamOut
|
||||
{
|
||||
Success = false,
|
||||
Value = Encoding.ASCII.GetBytes("NoData")
|
||||
};
|
||||
}
|
||||
logger.LogInformation($"Opc Machine {ConnectionToken} Read Opc tag {tag} for value {result.Value} {result.Value.GetType().FullName}");
|
||||
return new OpcParamOut
|
||||
{
|
||||
Success = true,
|
||||
Value = BigEndianValueHelper.Instance.GetBytes(result.Value, result.Value.GetType())
|
||||
Value = BigEndianLsbValueHelper.Instance.GetBytes(resultTrans, resultTrans.GetType())
|
||||
};
|
||||
}
|
||||
logger.LogError($"Opc Machine {ConnectionToken} Read Opc tag null");
|
||||
return new OpcParamOut
|
||||
{
|
||||
Success = false,
|
||||
@@ -143,6 +166,7 @@ namespace Modbus.Net.Opc
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogError(e, "Opc client {ConnectionToken} read exception", ConnectionToken);
|
||||
Disconnect();
|
||||
return new OpcParamOut
|
||||
{
|
||||
Success = false,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Modbus.Net.Opc
|
||||
{
|
||||
@@ -19,7 +20,6 @@ namespace Modbus.Net.Opc
|
||||
/// <param name="host">Opc DA 服务地址</param>
|
||||
protected OpcDaConnector(string host) : base(host)
|
||||
{
|
||||
Client = new MyDaClient(new Uri(ConnectionToken));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -36,5 +36,12 @@ namespace Modbus.Net.Opc
|
||||
}
|
||||
return _instances[host];
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task<bool> ConnectAsync()
|
||||
{
|
||||
if (Client == null) Client = new MyDaClient(new Uri(ConnectionToken));
|
||||
return base.ConnectAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ namespace Modbus.Net.Opc
|
||||
/// <summary>
|
||||
/// Opc设备
|
||||
/// </summary>
|
||||
public class OpcMachine<TKey, TUnitKey> : BaseMachine<TKey, TUnitKey> where TKey : IEquatable<TKey>
|
||||
public class OpcMachine<TKey, TUnitKey> : BaseMachine<TKey, TUnitKey, string, string> where TKey : IEquatable<TKey>
|
||||
where TUnitKey : IEquatable<TUnitKey>
|
||||
{
|
||||
/// <summary>
|
||||
@@ -16,13 +16,12 @@ namespace Modbus.Net.Opc
|
||||
/// <param name="connectionType">连接类型</param>
|
||||
/// <param name="connectionString">连接地址</param>
|
||||
/// <param name="getAddresses">需要读写的地址</param>
|
||||
public OpcMachine(TKey id, OpcType connectionType, string connectionString, IEnumerable<AddressUnit<TUnitKey>> getAddresses)
|
||||
/// <param name="tagSpliter">连接各个字段用的符号</param>
|
||||
public OpcMachine(TKey id, OpcType connectionType, string connectionString, IEnumerable<AddressUnit<TUnitKey, string, string>> getAddresses, string tagSpliter)
|
||||
: base(id, getAddresses, true)
|
||||
{
|
||||
BaseUtility = new OpcUtility(connectionType, connectionString);
|
||||
AddressFormater = new AddressFormaterOpc<TKey, TUnitKey>((machine, unit) => { return new string[] { unit.Area }; }, this);
|
||||
AddressCombiner = new AddressCombinerSingle<TUnitKey>();
|
||||
AddressCombinerSet = new AddressCombinerSingle<TUnitKey>();
|
||||
AddressFormater = new AddressFormaterOpc<TKey, TUnitKey, string, string>((machine, unit) => { var ans = unit.Area; if (unit.Address != null) ans += tagSpliter + unit.Address; if (unit.SubAddress != null) ans += tagSpliter + unit.SubAddress; return ans; }, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -171,7 +171,7 @@
|
||||
/// <returns>结构化的输出数据</returns>
|
||||
public override IOutputStruct Unformat(OpcParamOut messageBytes, ref int pos)
|
||||
{
|
||||
var ansByte = BigEndianValueHelper.Instance.GetByte(messageBytes.Value, ref pos);
|
||||
var ansByte = BigEndianLsbValueHelper.Instance.GetByte(messageBytes.Value, ref pos);
|
||||
var ans = ansByte != 0;
|
||||
return new WriteRequestOpcOutputStruct(ans);
|
||||
}
|
||||
|
||||
@@ -8,20 +8,6 @@ namespace Modbus.Net.Opc
|
||||
/// </summary>
|
||||
public abstract class OpcProtocolLinker : ProtocolLinker<OpcParamIn, OpcParamOut>
|
||||
{
|
||||
/// <summary>
|
||||
/// 发送并接收数据
|
||||
/// </summary>
|
||||
/// <param name="content">发送协议的内容</param>
|
||||
/// <returns>接收协议的内容</returns>
|
||||
public override async Task<OpcParamOut> SendReceiveAsync(OpcParamIn content)
|
||||
{
|
||||
var extBytes = BytesExtend(content);
|
||||
var receiveBytes = await SendReceiveWithoutExtAndDecAsync(extBytes);
|
||||
return receiveBytes == null
|
||||
? null
|
||||
: receiveBytes.Value.Length == 0 ? receiveBytes : BytesDecact(receiveBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送并接收数据,不进行协议扩展和收缩,用于特殊协议
|
||||
/// </summary>
|
||||
@@ -47,6 +33,7 @@ namespace Modbus.Net.Opc
|
||||
public override bool? CheckRight(OpcParamOut content)
|
||||
{
|
||||
if (content == null || !content.Success) return false;
|
||||
if (content.Success && content.Value == null) { content.Value = Encoding.ASCII.GetBytes("Success"); return true; }
|
||||
if (content.Value.Length == 6 && Encoding.ASCII.GetString(content.Value) == "NoData")
|
||||
return null;
|
||||
return base.CheckRight(content);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Modbus.Net.Opc
|
||||
{
|
||||
@@ -19,7 +20,6 @@ namespace Modbus.Net.Opc
|
||||
/// <param name="host">Opc UA 服务地址</param>
|
||||
protected OpcUaConnector(string host) : base(host)
|
||||
{
|
||||
Client = new MyUaClient(new Uri(ConnectionToken));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -36,5 +36,12 @@ namespace Modbus.Net.Opc
|
||||
}
|
||||
return _instances[host];
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task<bool> ConnectAsync()
|
||||
{
|
||||
if (Client == null) Client = new MyUaClient(new Uri(ConnectionToken));
|
||||
return base.ConnectAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,4 +4,9 @@
|
||||
|
||||
OPC Implementation of Modbus.Net
|
||||
|
||||
Doc has been moved to wiki.
|
||||
Doc has been moved to wiki.
|
||||
|
||||
## Caution
|
||||
|
||||
Do not use this module in commercial environment.<br>
|
||||
Altered h-opc uses Technosoftware.DaAeHdaSolution that will not abey for non paid commercial use.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/// <summary>
|
||||
/// Siemens地址格式化(Modbus.Net专用格式)
|
||||
/// </summary>
|
||||
public class AddressFormaterSiemens : AddressFormater
|
||||
public class AddressFormaterSiemens : AddressFormater<int, int>
|
||||
{
|
||||
/// <summary>
|
||||
/// 编码地址
|
||||
@@ -32,7 +32,7 @@
|
||||
/// <summary>
|
||||
/// Siemens地址格式化(Siemens格式)
|
||||
/// </summary>
|
||||
public class AddressFormaterSimenseStandard : AddressFormater
|
||||
public class AddressFormaterSimenseStandard : AddressFormater<int, int>
|
||||
{
|
||||
/// <summary>
|
||||
/// 编码地址
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0;net462</TargetFrameworks>
|
||||
<TargetFrameworks>net6.0</TargetFrameworks>
|
||||
<LangVersion>10.0</LangVersion>
|
||||
<AssemblyName>Modbus.Net.Siemens</AssemblyName>
|
||||
<RootNamespace>Modbus.Net.Siemens</RootNamespace>
|
||||
<PackageId>Modbus.Net.Siemens</PackageId>
|
||||
<Version>1.4.1</Version>
|
||||
<Version>1.4.2</Version>
|
||||
<Authors>Chris L.(Luo Sheng)</Authors>
|
||||
<Company>Hangzhou Delian Science Technology Co.,Ltd.</Company>
|
||||
<Description>Modbus.Net Siemens Profinet Implementation</Description>
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Modbus.Net.Siemens
|
||||
/// <param name="src">本机模块位,0到7,仅200使用,其它型号不要填写</param>
|
||||
/// <param name="dst">PLC模块位,0到7,仅200使用,其它型号不要填写</param>
|
||||
public SiemensMachine(TKey id, SiemensType connectionType, string connectionString, SiemensMachineModel model,
|
||||
IEnumerable<AddressUnit<TUnitKey>> getAddresses, bool keepConnect, byte slaveAddress, byte masterAddress, byte src = 1, byte dst = 0)
|
||||
IEnumerable<AddressUnit<TUnitKey, int, int>> getAddresses, bool keepConnect, byte slaveAddress, byte masterAddress, byte src = 1, byte dst = 0)
|
||||
: base(id, getAddresses, keepConnect, slaveAddress, masterAddress)
|
||||
{
|
||||
BaseUtility = new SiemensUtility(connectionType, connectionString, model, slaveAddress, masterAddress, src, dst);
|
||||
@@ -45,7 +45,7 @@ namespace Modbus.Net.Siemens
|
||||
/// <param name="src">本机模块位,0到7,仅200使用,其它型号不要填写</param>
|
||||
/// <param name="dst">PLC模块位,0到7,仅200使用,其它型号不要填写</param>
|
||||
public SiemensMachine(TKey id, SiemensType connectionType, string connectionString, SiemensMachineModel model,
|
||||
IEnumerable<AddressUnit<TUnitKey>> getAddresses, byte slaveAddress, byte masterAddress, byte src = 1, byte dst = 0)
|
||||
IEnumerable<AddressUnit<TUnitKey, int, int>> getAddresses, byte slaveAddress, byte masterAddress, byte src = 1, byte dst = 0)
|
||||
: this(id, connectionType, connectionString, model, getAddresses, true, slaveAddress, masterAddress, src, dst)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -34,16 +34,6 @@ namespace Modbus.Net.Siemens
|
||||
ProtocolLinker = new SiemensPpiProtocolLinker(_com, SlaveAddress);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送协议内容并接收,一般方法
|
||||
/// </summary>
|
||||
/// <param name="content">写入的内容,使用对象数组描述</param>
|
||||
/// <returns>从设备获取的字节流</returns>
|
||||
public override PipeUnit SendReceive(params object[] content)
|
||||
{
|
||||
return AsyncHelper.RunSync(() => SendReceiveAsync(Endian, content));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送协议内容并接收,一般方法
|
||||
/// </summary>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using FastEnumUtility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO.Ports;
|
||||
using System.Threading;
|
||||
@@ -19,7 +19,7 @@ namespace Modbus.Net.Siemens
|
||||
public SiemensPpiProtocolLinker(string com, int slaveAddress)
|
||||
: base(com, slaveAddress, parity:
|
||||
ConfigurationReader.GetValue("COM:Siemens", "Parity") != null
|
||||
? FastEnum.Parse<Parity>(ConfigurationReader.GetValue("COM:Siemens", "Parity"))
|
||||
? Enum.Parse<Parity>(ConfigurationReader.GetValue("COM:Siemens", "Parity"))
|
||||
: null
|
||||
)
|
||||
{
|
||||
|
||||
@@ -162,9 +162,9 @@ namespace Modbus.Net.Siemens
|
||||
public override IOutputStruct Unformat(byte[] messageBytes, ref int pos)
|
||||
{
|
||||
pos = 1;
|
||||
var masterAddress = BigEndianValueHelper.Instance.GetByte(messageBytes, ref pos);
|
||||
var slaveAddress = BigEndianValueHelper.Instance.GetByte(messageBytes, ref pos);
|
||||
var confirmMessage = BigEndianValueHelper.Instance.GetByte(messageBytes, ref pos);
|
||||
var masterAddress = BigEndianLsbValueHelper.Instance.GetByte(messageBytes, ref pos);
|
||||
var slaveAddress = BigEndianLsbValueHelper.Instance.GetByte(messageBytes, ref pos);
|
||||
var confirmMessage = BigEndianLsbValueHelper.Instance.GetByte(messageBytes, ref pos);
|
||||
return new ComCreateReferenceSiemensOutputStruct(slaveAddress, masterAddress, confirmMessage);
|
||||
}
|
||||
}
|
||||
@@ -236,19 +236,19 @@ namespace Modbus.Net.Siemens
|
||||
case 0xc0:
|
||||
{
|
||||
pos += 2;
|
||||
tdpuSize = BigEndianValueHelper.Instance.GetByte(messageBytes, ref pos);
|
||||
tdpuSize = BigEndianLsbValueHelper.Instance.GetByte(messageBytes, ref pos);
|
||||
break;
|
||||
}
|
||||
case 0xc1:
|
||||
{
|
||||
pos += 2;
|
||||
srcTsap = BigEndianValueHelper.Instance.GetUShort(messageBytes, ref pos);
|
||||
srcTsap = BigEndianLsbValueHelper.Instance.GetUShort(messageBytes, ref pos);
|
||||
break;
|
||||
}
|
||||
case 0xc2:
|
||||
{
|
||||
pos += 2;
|
||||
dstTsap = BigEndianValueHelper.Instance.GetUShort(messageBytes, ref pos);
|
||||
dstTsap = BigEndianLsbValueHelper.Instance.GetUShort(messageBytes, ref pos);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -334,7 +334,7 @@ namespace Modbus.Net.Siemens
|
||||
/// <returns>输出数据</returns>
|
||||
public override IOutputStruct Unformat(byte[] messageBytes, ref int pos)
|
||||
{
|
||||
var confirmByte = BigEndianValueHelper.Instance.GetByte(messageBytes, ref pos);
|
||||
var confirmByte = BigEndianLsbValueHelper.Instance.GetByte(messageBytes, ref pos);
|
||||
return new ComConfirmMessageSiemensOutputStruct(confirmByte);
|
||||
}
|
||||
}
|
||||
@@ -399,11 +399,11 @@ namespace Modbus.Net.Siemens
|
||||
public override IOutputStruct Unformat(byte[] messageBytes, ref int pos)
|
||||
{
|
||||
pos = 4;
|
||||
var pduRef = BigEndianValueHelper.Instance.GetUShort(messageBytes, ref pos);
|
||||
var pduRef = BigEndianLsbValueHelper.Instance.GetUShort(messageBytes, ref pos);
|
||||
pos = 14;
|
||||
var maxCalling = BigEndianValueHelper.Instance.GetUShort(messageBytes, ref pos);
|
||||
var maxCalled = BigEndianValueHelper.Instance.GetUShort(messageBytes, ref pos);
|
||||
var maxPdu = BigEndianValueHelper.Instance.GetUShort(messageBytes, ref pos);
|
||||
var maxCalling = BigEndianLsbValueHelper.Instance.GetUShort(messageBytes, ref pos);
|
||||
var maxCalled = BigEndianLsbValueHelper.Instance.GetUShort(messageBytes, ref pos);
|
||||
var maxPdu = BigEndianLsbValueHelper.Instance.GetUShort(messageBytes, ref pos);
|
||||
return new EstablishAssociationSiemensOutputStruct(pduRef, maxCalling, maxCalled, maxPdu);
|
||||
}
|
||||
}
|
||||
@@ -563,7 +563,7 @@ namespace Modbus.Net.Siemens
|
||||
var dbBlock = r_message.DbBlock;
|
||||
var area = r_message.Area;
|
||||
var offsetBit = r_message.Offset * 8;
|
||||
var offsetBitBytes = BigEndianValueHelper.Instance.GetBytes(offsetBit);
|
||||
var offsetBitBytes = BigEndianLsbValueHelper.Instance.GetBytes(offsetBit);
|
||||
return Format(new byte[4], slaveAddress, masterAddress, (byte)0x6c, protoId, rosctr, redId, pduRef, parLg,
|
||||
datLg, serviceId, numberOfVariables
|
||||
, variableSpec, vAddrLg, syntaxId, type, numberOfElements, dbBlock, area,
|
||||
@@ -579,11 +579,11 @@ namespace Modbus.Net.Siemens
|
||||
public override IOutputStruct Unformat(byte[] messageBytes, ref int pos)
|
||||
{
|
||||
pos = 4;
|
||||
var pduRef = BigEndianValueHelper.Instance.GetUShort(messageBytes, ref pos);
|
||||
var pduRef = BigEndianLsbValueHelper.Instance.GetUShort(messageBytes, ref pos);
|
||||
pos = 14;
|
||||
var accessResult = BigEndianValueHelper.Instance.GetByte(messageBytes, ref pos);
|
||||
var dataType = BigEndianValueHelper.Instance.GetByte(messageBytes, ref pos);
|
||||
var length = BigEndianValueHelper.Instance.GetUShort(messageBytes, ref pos);
|
||||
var accessResult = BigEndianLsbValueHelper.Instance.GetByte(messageBytes, ref pos);
|
||||
var dataType = BigEndianLsbValueHelper.Instance.GetByte(messageBytes, ref pos);
|
||||
var length = BigEndianLsbValueHelper.Instance.GetUShort(messageBytes, ref pos);
|
||||
var byteLength = length / 8;
|
||||
var values = new byte[byteLength];
|
||||
Array.Copy(messageBytes, pos, values, 0, byteLength);
|
||||
@@ -700,7 +700,7 @@ namespace Modbus.Net.Siemens
|
||||
public override byte[] Format(IInputStruct message)
|
||||
{
|
||||
var r_message = (WriteRequestSiemensInputStruct)message;
|
||||
var valueBytes = BigEndianValueHelper.Instance.ObjectArrayToByteArray(r_message.WriteValue);
|
||||
var valueBytes = BigEndianLsbValueHelper.Instance.ObjectArrayToByteArray(r_message.WriteValue);
|
||||
var slaveAddress = r_message.SlaveAddress;
|
||||
var masterAddress = r_message.MasterAddress;
|
||||
const byte protoId = 0x32;
|
||||
@@ -719,7 +719,7 @@ namespace Modbus.Net.Siemens
|
||||
var dbBlock = r_message.DbBlock;
|
||||
var area = r_message.Area;
|
||||
var offsetBit = r_message.Offset * 8;
|
||||
var offsetBitBytes = BigEndianValueHelper.Instance.GetBytes(offsetBit);
|
||||
var offsetBitBytes = BigEndianLsbValueHelper.Instance.GetBytes(offsetBit);
|
||||
const byte reserved = 0x00;
|
||||
const byte type = (byte)SiemensDataType.OtherAccess;
|
||||
var numberOfWriteBits = (ushort)(valueBytes.Length * 8);
|
||||
@@ -739,16 +739,16 @@ namespace Modbus.Net.Siemens
|
||||
{
|
||||
if (messageBytes.Length == 1)
|
||||
{
|
||||
var accessResult = BigEndianValueHelper.Instance.GetByte(messageBytes, ref pos);
|
||||
var accessResult = BigEndianLsbValueHelper.Instance.GetByte(messageBytes, ref pos);
|
||||
return new WriteRequestSiemensOutputStruct(0,
|
||||
accessResult == 0xe5 ? SiemensAccessResult.NoError : SiemensAccessResult.InvalidAddress);
|
||||
}
|
||||
else
|
||||
{
|
||||
pos = 4;
|
||||
var pduRef = BigEndianValueHelper.Instance.GetUShort(messageBytes, ref pos);
|
||||
var pduRef = BigEndianLsbValueHelper.Instance.GetUShort(messageBytes, ref pos);
|
||||
pos = 14;
|
||||
var accessResult = BigEndianValueHelper.Instance.GetByte(messageBytes, ref pos);
|
||||
var accessResult = BigEndianLsbValueHelper.Instance.GetByte(messageBytes, ref pos);
|
||||
return new WriteRequestSiemensOutputStruct(pduRef, (SiemensAccessResult)accessResult);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Modbus.Net.Siemens
|
||||
public byte[] BytesExtend(byte[] content)
|
||||
{
|
||||
Array.Copy(new byte[] { 0x03, 0x00, 0x00, 0x00, 0x02, 0xf0, 0x80 }, 0, content, 0, 7);
|
||||
Array.Copy(BigEndianValueHelper.Instance.GetBytes((ushort)content.Length), 0, content, 2, 2);
|
||||
Array.Copy(BigEndianLsbValueHelper.Instance.GetBytes((ushort)content.Length), 0, content, 2, 2);
|
||||
return content;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
get
|
||||
{
|
||||
var pos = 15;
|
||||
return BigEndianValueHelper.Instance.GetBit(BigEndianValueHelper.Instance.GetBytes(TodValue), ref pos);
|
||||
return BigEndianLsbValueHelper.Instance.GetBit(BigEndianLsbValueHelper.Instance.GetBytes(TodValue), ref pos);
|
||||
}
|
||||
set { TodValue = BigEndianValueHelper.Instance.SetBit(BigEndianValueHelper.Instance.GetBytes(TodValue), 15, value); }
|
||||
set { TodValue = BigEndianLsbValueHelper.Instance.SetBit(BigEndianLsbValueHelper.Instance.GetBytes(TodValue), 15, value); }
|
||||
}
|
||||
|
||||
public byte K0_4
|
||||
@@ -17,13 +17,13 @@
|
||||
get
|
||||
{
|
||||
var pos = 0;
|
||||
var byteValue = BigEndianValueHelper.Instance.GetByte(BigEndianValueHelper.Instance.GetBytes(TodValue), ref pos);
|
||||
var byteValue = BigEndianLsbValueHelper.Instance.GetByte(BigEndianLsbValueHelper.Instance.GetBytes(TodValue), ref pos);
|
||||
return (byte)(byteValue%64/4);
|
||||
}
|
||||
set
|
||||
{
|
||||
var pos = 0;
|
||||
var byteValue = BigEndianValueHelper.Instance.GetByte(BigEndianValueHelper.Instance.GetBytes(TodValue), ref pos);
|
||||
var byteValue = BigEndianLsbValueHelper.Instance.GetByte(BigEndianLsbValueHelper.Instance.GetBytes(TodValue), ref pos);
|
||||
byteValue = (byte)(byteValue - (byteValue%128/4) + value);
|
||||
TodValue = (ushort)(TodValue%128 + byteValue*128);
|
||||
}
|
||||
@@ -34,9 +34,9 @@
|
||||
get
|
||||
{
|
||||
var pos = 5;
|
||||
return BigEndianValueHelper.Instance.GetBit(BigEndianValueHelper.Instance.GetBytes(TodValue), ref pos);
|
||||
return BigEndianLsbValueHelper.Instance.GetBit(BigEndianLsbValueHelper.Instance.GetBytes(TodValue), ref pos);
|
||||
}
|
||||
set { TodValue = BigEndianValueHelper.Instance.SetBit(BigEndianValueHelper.Instance.GetBytes(TodValue), 5, value); }
|
||||
set { TodValue = BigEndianLsbValueHelper.Instance.SetBit(BigEndianLsbValueHelper.Instance.GetBytes(TodValue), 5, value); }
|
||||
}
|
||||
|
||||
public byte UA
|
||||
@@ -44,15 +44,15 @@
|
||||
get
|
||||
{
|
||||
var pos = 3;
|
||||
var low = BigEndianValueHelper.Instance.GetBit(BigEndianValueHelper.Instance.GetBytes(TodValue), ref pos) ? 1 : 0;
|
||||
var high = BigEndianValueHelper.Instance.GetBit(BigEndianValueHelper.Instance.GetBytes(TodValue), ref pos) ? 1 : 0;
|
||||
var low = BigEndianLsbValueHelper.Instance.GetBit(BigEndianLsbValueHelper.Instance.GetBytes(TodValue), ref pos) ? 1 : 0;
|
||||
var high = BigEndianLsbValueHelper.Instance.GetBit(BigEndianLsbValueHelper.Instance.GetBytes(TodValue), ref pos) ? 1 : 0;
|
||||
high *= 2;
|
||||
return (byte) (high + low);
|
||||
}
|
||||
set
|
||||
{
|
||||
TodValue = BigEndianValueHelper.Instance.SetBit(BigEndianValueHelper.Instance.GetBytes(TodValue), 3, value % 2 >= 1);
|
||||
TodValue = BigEndianValueHelper.Instance.SetBit(BigEndianValueHelper.Instance.GetBytes(TodValue), 4, value / 2 >= 1);
|
||||
TodValue = BigEndianLsbValueHelper.Instance.SetBit(BigEndianLsbValueHelper.Instance.GetBytes(TodValue), 3, value % 2 >= 1);
|
||||
TodValue = BigEndianLsbValueHelper.Instance.SetBit(BigEndianLsbValueHelper.Instance.GetBytes(TodValue), 4, value / 2 >= 1);
|
||||
}
|
||||
}
|
||||
public bool UZS
|
||||
@@ -60,9 +60,9 @@
|
||||
get
|
||||
{
|
||||
var pos = 2;
|
||||
return BigEndianValueHelper.Instance.GetBit(BigEndianValueHelper.Instance.GetBytes(TodValue), ref pos);
|
||||
return BigEndianLsbValueHelper.Instance.GetBit(BigEndianLsbValueHelper.Instance.GetBytes(TodValue), ref pos);
|
||||
}
|
||||
set { TodValue = BigEndianValueHelper.Instance.SetBit(BigEndianValueHelper.Instance.GetBytes(TodValue), 2, value); }
|
||||
set { TodValue = BigEndianLsbValueHelper.Instance.SetBit(BigEndianLsbValueHelper.Instance.GetBytes(TodValue), 2, value); }
|
||||
}
|
||||
|
||||
public bool ESY
|
||||
@@ -70,9 +70,9 @@
|
||||
get
|
||||
{
|
||||
var pos = 1;
|
||||
return BigEndianValueHelper.Instance.GetBit(BigEndianValueHelper.Instance.GetBytes(TodValue), ref pos);
|
||||
return BigEndianLsbValueHelper.Instance.GetBit(BigEndianLsbValueHelper.Instance.GetBytes(TodValue), ref pos);
|
||||
}
|
||||
set { TodValue = BigEndianValueHelper.Instance.SetBit(BigEndianValueHelper.Instance.GetBytes(TodValue), 1, value); }
|
||||
set { TodValue = BigEndianLsbValueHelper.Instance.SetBit(BigEndianLsbValueHelper.Instance.GetBytes(TodValue), 1, value); }
|
||||
}
|
||||
|
||||
public bool SYA
|
||||
@@ -80,9 +80,9 @@
|
||||
get
|
||||
{
|
||||
var pos = 0;
|
||||
return BigEndianValueHelper.Instance.GetBit(BigEndianValueHelper.Instance.GetBytes(TodValue), ref pos);
|
||||
return BigEndianLsbValueHelper.Instance.GetBit(BigEndianLsbValueHelper.Instance.GetBytes(TodValue), ref pos);
|
||||
}
|
||||
set { TodValue = BigEndianValueHelper.Instance.SetBit(BigEndianValueHelper.Instance.GetBytes(TodValue), 0, value); }
|
||||
set { TodValue = BigEndianLsbValueHelper.Instance.SetBit(BigEndianLsbValueHelper.Instance.GetBytes(TodValue), 0, value); }
|
||||
}
|
||||
|
||||
public ushort TodValue { get; set; }
|
||||
|
||||
@@ -78,16 +78,6 @@ namespace Modbus.Net.Siemens
|
||||
ProtocolLinker = new SiemensTcpProtocolLinker(_ip, _port);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送数据并接收
|
||||
/// </summary>
|
||||
/// <param name="content">发送的数据</param>
|
||||
/// <returns>返回的数据</returns>
|
||||
public override PipeUnit SendReceive(params object[] content)
|
||||
{
|
||||
return AsyncHelper.RunSync(() => SendReceiveAsync(Endian, content));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送数据并接收
|
||||
/// </summary>
|
||||
@@ -100,17 +90,6 @@ namespace Modbus.Net.Siemens
|
||||
return await base.SendReceiveAsync(Endian, content);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送数据并接收
|
||||
/// </summary>
|
||||
/// <param name="unit">协议的核心</param>
|
||||
/// <param name="content">协议的参数</param>
|
||||
/// <returns>返回的数据</returns>
|
||||
public override PipeUnit SendReceive(ProtocolUnit<byte[], byte[]> unit, IInputStruct content)
|
||||
{
|
||||
return AsyncHelper.RunSync(() => SendReceiveAsync(unit, content));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送数据并接收
|
||||
/// </summary>
|
||||
|
||||
@@ -7,6 +7,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Modbus.Net", "Modbus.Net\Mo
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{64472271-8B95-4036-ACF9-C10941C1DB0A}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
..\LICENSE = ..\LICENSE
|
||||
..\README.md = ..\README.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
@@ -34,56 +35,152 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TripleAdd", "..\Samples\Tri
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Modbus.Net.Modbus.SelfDefinedSample", "Modbus.Net.Modbus.SelfDefinedSample\Modbus.Net.Modbus.SelfDefinedSample.csproj", "{C4FA55AF-80ED-4467-948F-8EF865C8A5A5}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "h-opc", "..\h-opc\h-opc\h-opc.csproj", "{DC6425E4-1409-488D-A014-4DCC909CF542}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Modbus.Net.BigEndian3412", "Modbus.Net.BigEndian3412\Modbus.Net.BigEndian3412.csproj", "{D48D4F79-1DA2-4C91-A9EE-FDCAEC09E808}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Technosoftware.OpcRcw", "..\Technosoftware\OpcRcw\Technosoftware.OpcRcw.csproj", "{7689CBF8-1992-467D-AD45-E1464F705220}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Technosoftware.DaAeHdaClient", "..\Technosoftware\DaAeHdaClient\Technosoftware.DaAeHdaClient.csproj", "{116160B2-7D6D-40A2-839C-7997BC0E1A0C}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Technosoftware.DaAeHdaClient.Com", "..\Technosoftware\DaAeHdaClient.Com\Technosoftware.DaAeHdaClient.Com.csproj", "{ACAF0A16-FC51-4369-BFA8-484FF20707D7}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|x64 = Debug|x64
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|x64 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{124EBEF2-8960-4447-84CF-1D683B1EF7CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{124EBEF2-8960-4447-84CF-1D683B1EF7CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{124EBEF2-8960-4447-84CF-1D683B1EF7CC}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{124EBEF2-8960-4447-84CF-1D683B1EF7CC}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{124EBEF2-8960-4447-84CF-1D683B1EF7CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{124EBEF2-8960-4447-84CF-1D683B1EF7CC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{124EBEF2-8960-4447-84CF-1D683B1EF7CC}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{124EBEF2-8960-4447-84CF-1D683B1EF7CC}.Release|x64.Build.0 = Release|Any CPU
|
||||
{FDCA72BA-6D06-4DE0-B873-C11C4AC853AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FDCA72BA-6D06-4DE0-B873-C11C4AC853AD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FDCA72BA-6D06-4DE0-B873-C11C4AC853AD}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{FDCA72BA-6D06-4DE0-B873-C11C4AC853AD}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{FDCA72BA-6D06-4DE0-B873-C11C4AC853AD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FDCA72BA-6D06-4DE0-B873-C11C4AC853AD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{FDCA72BA-6D06-4DE0-B873-C11C4AC853AD}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{FDCA72BA-6D06-4DE0-B873-C11C4AC853AD}.Release|x64.Build.0 = Release|Any CPU
|
||||
{6258F9D9-0DF4-497F-9F3B-6D2F6F752A21}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6258F9D9-0DF4-497F-9F3B-6D2F6F752A21}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6258F9D9-0DF4-497F-9F3B-6D2F6F752A21}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{6258F9D9-0DF4-497F-9F3B-6D2F6F752A21}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{6258F9D9-0DF4-497F-9F3B-6D2F6F752A21}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6258F9D9-0DF4-497F-9F3B-6D2F6F752A21}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{6258F9D9-0DF4-497F-9F3B-6D2F6F752A21}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{6258F9D9-0DF4-497F-9F3B-6D2F6F752A21}.Release|x64.Build.0 = Release|Any CPU
|
||||
{3BB01E98-3D45-454A-A1BD-49D7B2C83B74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3BB01E98-3D45-454A-A1BD-49D7B2C83B74}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3BB01E98-3D45-454A-A1BD-49D7B2C83B74}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{3BB01E98-3D45-454A-A1BD-49D7B2C83B74}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{3BB01E98-3D45-454A-A1BD-49D7B2C83B74}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3BB01E98-3D45-454A-A1BD-49D7B2C83B74}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3BB01E98-3D45-454A-A1BD-49D7B2C83B74}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{3BB01E98-3D45-454A-A1BD-49D7B2C83B74}.Release|x64.Build.0 = Release|Any CPU
|
||||
{22A35CA8-CDCF-416D-BA84-08C933B4A3DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{22A35CA8-CDCF-416D-BA84-08C933B4A3DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{22A35CA8-CDCF-416D-BA84-08C933B4A3DE}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{22A35CA8-CDCF-416D-BA84-08C933B4A3DE}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{22A35CA8-CDCF-416D-BA84-08C933B4A3DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{22A35CA8-CDCF-416D-BA84-08C933B4A3DE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{22A35CA8-CDCF-416D-BA84-08C933B4A3DE}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{22A35CA8-CDCF-416D-BA84-08C933B4A3DE}.Release|x64.Build.0 = Release|Any CPU
|
||||
{D4AF0E1E-676E-43B6-BAA3-BFC329D68C80}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D4AF0E1E-676E-43B6-BAA3-BFC329D68C80}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D4AF0E1E-676E-43B6-BAA3-BFC329D68C80}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{D4AF0E1E-676E-43B6-BAA3-BFC329D68C80}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{D4AF0E1E-676E-43B6-BAA3-BFC329D68C80}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D4AF0E1E-676E-43B6-BAA3-BFC329D68C80}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D4AF0E1E-676E-43B6-BAA3-BFC329D68C80}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{D4AF0E1E-676E-43B6-BAA3-BFC329D68C80}.Release|x64.Build.0 = Release|Any CPU
|
||||
{1857DA63-3335-428F-84D8-1FA4F8178643}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1857DA63-3335-428F-84D8-1FA4F8178643}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1857DA63-3335-428F-84D8-1FA4F8178643}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{1857DA63-3335-428F-84D8-1FA4F8178643}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{1857DA63-3335-428F-84D8-1FA4F8178643}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1857DA63-3335-428F-84D8-1FA4F8178643}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1857DA63-3335-428F-84D8-1FA4F8178643}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{1857DA63-3335-428F-84D8-1FA4F8178643}.Release|x64.Build.0 = Release|Any CPU
|
||||
{C854A379-C5EA-4CAC-9C5F-7291372D1D3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C854A379-C5EA-4CAC-9C5F-7291372D1D3F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C854A379-C5EA-4CAC-9C5F-7291372D1D3F}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{C854A379-C5EA-4CAC-9C5F-7291372D1D3F}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{C854A379-C5EA-4CAC-9C5F-7291372D1D3F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C854A379-C5EA-4CAC-9C5F-7291372D1D3F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C854A379-C5EA-4CAC-9C5F-7291372D1D3F}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{C854A379-C5EA-4CAC-9C5F-7291372D1D3F}.Release|x64.Build.0 = Release|Any CPU
|
||||
{AA3A42D2-0502-41D3-929A-BAB729DF07D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{AA3A42D2-0502-41D3-929A-BAB729DF07D6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{AA3A42D2-0502-41D3-929A-BAB729DF07D6}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{AA3A42D2-0502-41D3-929A-BAB729DF07D6}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{AA3A42D2-0502-41D3-929A-BAB729DF07D6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{AA3A42D2-0502-41D3-929A-BAB729DF07D6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{AA3A42D2-0502-41D3-929A-BAB729DF07D6}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{AA3A42D2-0502-41D3-929A-BAB729DF07D6}.Release|x64.Build.0 = Release|Any CPU
|
||||
{414956B8-DBD4-414C-ABD3-565580739646}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{414956B8-DBD4-414C-ABD3-565580739646}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{414956B8-DBD4-414C-ABD3-565580739646}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{414956B8-DBD4-414C-ABD3-565580739646}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{414956B8-DBD4-414C-ABD3-565580739646}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{414956B8-DBD4-414C-ABD3-565580739646}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{414956B8-DBD4-414C-ABD3-565580739646}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{414956B8-DBD4-414C-ABD3-565580739646}.Release|x64.Build.0 = Release|Any CPU
|
||||
{C4FA55AF-80ED-4467-948F-8EF865C8A5A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C4FA55AF-80ED-4467-948F-8EF865C8A5A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C4FA55AF-80ED-4467-948F-8EF865C8A5A5}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{C4FA55AF-80ED-4467-948F-8EF865C8A5A5}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{C4FA55AF-80ED-4467-948F-8EF865C8A5A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C4FA55AF-80ED-4467-948F-8EF865C8A5A5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C4FA55AF-80ED-4467-948F-8EF865C8A5A5}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{C4FA55AF-80ED-4467-948F-8EF865C8A5A5}.Release|x64.Build.0 = Release|Any CPU
|
||||
{DC6425E4-1409-488D-A014-4DCC909CF542}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{DC6425E4-1409-488D-A014-4DCC909CF542}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DC6425E4-1409-488D-A014-4DCC909CF542}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{DC6425E4-1409-488D-A014-4DCC909CF542}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{DC6425E4-1409-488D-A014-4DCC909CF542}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DC6425E4-1409-488D-A014-4DCC909CF542}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{DC6425E4-1409-488D-A014-4DCC909CF542}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{DC6425E4-1409-488D-A014-4DCC909CF542}.Release|x64.Build.0 = Release|Any CPU
|
||||
{D48D4F79-1DA2-4C91-A9EE-FDCAEC09E808}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D48D4F79-1DA2-4C91-A9EE-FDCAEC09E808}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D48D4F79-1DA2-4C91-A9EE-FDCAEC09E808}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{D48D4F79-1DA2-4C91-A9EE-FDCAEC09E808}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{D48D4F79-1DA2-4C91-A9EE-FDCAEC09E808}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D48D4F79-1DA2-4C91-A9EE-FDCAEC09E808}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D48D4F79-1DA2-4C91-A9EE-FDCAEC09E808}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{D48D4F79-1DA2-4C91-A9EE-FDCAEC09E808}.Release|x64.Build.0 = Release|Any CPU
|
||||
{7689CBF8-1992-467D-AD45-E1464F705220}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7689CBF8-1992-467D-AD45-E1464F705220}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7689CBF8-1992-467D-AD45-E1464F705220}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{7689CBF8-1992-467D-AD45-E1464F705220}.Debug|x64.Build.0 = Debug|x64
|
||||
{7689CBF8-1992-467D-AD45-E1464F705220}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7689CBF8-1992-467D-AD45-E1464F705220}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7689CBF8-1992-467D-AD45-E1464F705220}.Release|x64.ActiveCfg = Release|x64
|
||||
{7689CBF8-1992-467D-AD45-E1464F705220}.Release|x64.Build.0 = Release|x64
|
||||
{116160B2-7D6D-40A2-839C-7997BC0E1A0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{116160B2-7D6D-40A2-839C-7997BC0E1A0C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{116160B2-7D6D-40A2-839C-7997BC0E1A0C}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{116160B2-7D6D-40A2-839C-7997BC0E1A0C}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{116160B2-7D6D-40A2-839C-7997BC0E1A0C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{116160B2-7D6D-40A2-839C-7997BC0E1A0C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{116160B2-7D6D-40A2-839C-7997BC0E1A0C}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{116160B2-7D6D-40A2-839C-7997BC0E1A0C}.Release|x64.Build.0 = Release|Any CPU
|
||||
{ACAF0A16-FC51-4369-BFA8-484FF20707D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{ACAF0A16-FC51-4369-BFA8-484FF20707D7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{ACAF0A16-FC51-4369-BFA8-484FF20707D7}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{ACAF0A16-FC51-4369-BFA8-484FF20707D7}.Debug|x64.Build.0 = Debug|x64
|
||||
{ACAF0A16-FC51-4369-BFA8-484FF20707D7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{ACAF0A16-FC51-4369-BFA8-484FF20707D7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{ACAF0A16-FC51-4369-BFA8-484FF20707D7}.Release|x64.ActiveCfg = Release|x64
|
||||
{ACAF0A16-FC51-4369-BFA8-484FF20707D7}.Release|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using FastEnumUtility;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@@ -62,12 +61,23 @@ namespace Modbus.Net
|
||||
}
|
||||
case "addressMap":
|
||||
{
|
||||
paramsSet.Add(AddressReader.ReadAddresses(dic["addressMap"]));
|
||||
break;
|
||||
var machineTypeTemp = Assembly.Load("Modbus.Net." + dic["protocol"]).GetType("Modbus.Net." + dic["protocol"] + "." + dic["protocol"] + "Machine`2");
|
||||
var addressTypes = machineTypeTemp.GetProperty("GetAddresses").PropertyType.GenericTypeArguments[0].GenericTypeArguments;
|
||||
if (addressTypes[1] == typeof(int) && addressTypes[2] == typeof(int))
|
||||
{
|
||||
paramsSet.Add(AddressReader<string, int, int>.ReadAddresses(dic["addressMap"]));
|
||||
break;
|
||||
}
|
||||
else if (addressTypes[1] == typeof(string) && addressTypes[2] == typeof(string))
|
||||
{
|
||||
paramsSet.Add(AddressReader<string, string, string>.ReadAddresses(dic["addressMap"]));
|
||||
break;
|
||||
}
|
||||
throw new NotSupportedException("AddressUnit type not supported for AddressReader");
|
||||
}
|
||||
case "endian":
|
||||
{
|
||||
paramsSet.Add(FastEnum.Parse<Endian>(dic["endian"]));
|
||||
paramsSet.Add(Endian.Parse(dic["endian"]));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -105,7 +115,7 @@ namespace Modbus.Net
|
||||
}
|
||||
}
|
||||
|
||||
class AddressReader
|
||||
internal class AddressReader<TUnitKey, TAddressKey, TSubAddressKey> where TUnitKey : IEquatable<TUnitKey> where TAddressKey : IEquatable<TAddressKey> where TSubAddressKey : IEquatable<TSubAddressKey>
|
||||
{
|
||||
private static readonly IConfigurationRoot configuration = new ConfigurationBuilder()
|
||||
.SetBasePath(Directory.GetCurrentDirectory())
|
||||
@@ -114,14 +124,14 @@ namespace Modbus.Net
|
||||
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") ?? Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", optional: true, reloadOnChange: true)
|
||||
.Build();
|
||||
|
||||
public static IEnumerable<AddressUnit<string>> ReadAddresses(string addressMapName)
|
||||
public static IEnumerable<AddressUnit<TUnitKey, TAddressKey, TSubAddressKey>> ReadAddresses(string addressMapName)
|
||||
{
|
||||
var ans = new List<AddressUnit<string>>();
|
||||
var ans = new List<AddressUnit<TUnitKey, TAddressKey, TSubAddressKey>>();
|
||||
var addressesRoot = configuration.GetSection("Modbus.Net").GetSection("AddressMap").GetSection(addressMapName).GetChildren();
|
||||
foreach (var address in addressesRoot)
|
||||
{
|
||||
|
||||
var addressNew = address.Get<AddressUnit<string>>();
|
||||
var addressNew = address.Get<AddressUnit<TUnitKey, TAddressKey, TSubAddressKey>>();
|
||||
addressNew.DataType = "System." + address["DataType"] != null ? Type.GetType("System." + address["DataType"]) : throw new ArgumentNullException("DataType is null");
|
||||
if (addressNew.DataType == null) throw new ArgumentNullException(string.Format("DataType define error {0} {1} {2}", addressMapName, addressNew.Id, address["DataType"]));
|
||||
ans.Add(addressNew);
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace Modbus.Net
|
||||
/// </summary>
|
||||
/// <param name="timeoutTime">发送超时时间</param>
|
||||
/// <param name="isFullDuplex">是否为全双工</param>
|
||||
protected BaseConnector(int timeoutTime = 10000, bool isFullDuplex = true)
|
||||
protected BaseConnector(int timeoutTime = 10000, bool isFullDuplex = false)
|
||||
{
|
||||
IsFullDuplex = isFullDuplex;
|
||||
if (timeoutTime < -1) timeoutTime = -1;
|
||||
@@ -49,12 +49,17 @@ namespace Modbus.Net
|
||||
/// 发送内部
|
||||
/// </summary>
|
||||
/// <param name="message">发送的信息</param>
|
||||
/// <param name="repeat">是否为重发消息</param>
|
||||
/// <returns>发送信息的定义</returns>
|
||||
protected async Task<MessageWaitingDef> SendMsgInner(byte[] message)
|
||||
protected async Task<MessageWaitingDef> SendMsgInner(byte[] message, bool repeat = false)
|
||||
{
|
||||
IDisposable asyncLock = null;
|
||||
try
|
||||
{
|
||||
if (!Controller.IsSending)
|
||||
{
|
||||
Controller.SendStart();
|
||||
}
|
||||
var messageSendingdef = Controller.AddMessage(message);
|
||||
if (messageSendingdef != null)
|
||||
{
|
||||
@@ -69,6 +74,11 @@ namespace Modbus.Net
|
||||
success = messageSendingdef.ReceiveMutex.WaitOne(TimeoutTime);
|
||||
if (success)
|
||||
{
|
||||
if (!repeat && messageSendingdef.ReceiveMessage == null)
|
||||
{
|
||||
asyncLock?.Dispose();
|
||||
return await SendMsgInner(message, true);
|
||||
}
|
||||
return messageSendingdef;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -506,7 +506,7 @@ namespace Modbus.Net
|
||||
_taskCancel = true;
|
||||
}
|
||||
|
||||
private void ReceiveMessage()
|
||||
private async Task ReceiveMessage()
|
||||
{
|
||||
while (!_taskCancel)
|
||||
{
|
||||
@@ -549,9 +549,19 @@ namespace Modbus.Net
|
||||
CacheBytes.RemoveRange(0, confirmed.Item1.Length);
|
||||
}
|
||||
}
|
||||
else if (confirmed.Item2 == false)
|
||||
else
|
||||
{
|
||||
lock (CacheBytes)
|
||||
{
|
||||
CacheBytes.RemoveRange(0, confirmed.Item1.Length);
|
||||
}
|
||||
|
||||
var sendMessage = InvokeReturnMessage(confirmed.Item1);
|
||||
//主动传输事件
|
||||
if (sendMessage != null)
|
||||
{
|
||||
await SendMsgWithoutConfirm(sendMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,12 +53,17 @@ namespace Modbus.Net
|
||||
/// 发送内部
|
||||
/// </summary>
|
||||
/// <param name="message">发送的信息</param>
|
||||
/// <param name="repeat">是否为重发消息</param>
|
||||
/// <returns>发送信息的定义</returns>
|
||||
protected async Task<MessageWaitingDef> SendMsgInner(byte[] message)
|
||||
protected async Task<MessageWaitingDef> SendMsgInner(byte[] message, bool repeat = false)
|
||||
{
|
||||
IDisposable asyncLock = null;
|
||||
try
|
||||
{
|
||||
if (!Controller.IsSending)
|
||||
{
|
||||
Controller.SendStart();
|
||||
}
|
||||
var messageSendingdef = Controller.AddMessage(message);
|
||||
if (messageSendingdef != null)
|
||||
{
|
||||
@@ -73,6 +78,11 @@ namespace Modbus.Net
|
||||
success = messageSendingdef.ReceiveMutex.WaitOne(TimeoutTime);
|
||||
if (success)
|
||||
{
|
||||
if (!repeat && messageSendingdef.ReceiveMessage == null)
|
||||
{
|
||||
asyncLock?.Dispose();
|
||||
return await SendMsgInner(message, true);
|
||||
}
|
||||
return messageSendingdef;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,11 @@ namespace Modbus.Net
|
||||
/// </summary>
|
||||
protected Task SendingThread { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 消息维护线程是否在运行
|
||||
/// </summary>
|
||||
public virtual bool IsSending => SendingThread.Status.Equals(TaskStatus.Running);
|
||||
|
||||
/// <summary>
|
||||
/// 包切分位置函数
|
||||
/// </summary>
|
||||
@@ -92,15 +97,16 @@ namespace Modbus.Net
|
||||
/// <param name="def">需要添加的信息信息</param>
|
||||
protected virtual bool AddMessageToList(MessageWaitingDef def)
|
||||
{
|
||||
var ans = false;
|
||||
lock (WaitingMessages)
|
||||
{
|
||||
if (WaitingMessages.FirstOrDefault(p => p.Key == def.Key) == null || def.Key == null)
|
||||
{
|
||||
WaitingMessages.Add(def);
|
||||
return true;
|
||||
ans = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -116,45 +122,89 @@ namespace Modbus.Net
|
||||
var ans = new List<(byte[], bool)>();
|
||||
byte[] receiveMessageCopy = new byte[receiveMessage.Length];
|
||||
Array.Copy(receiveMessage, receiveMessageCopy, receiveMessage.Length);
|
||||
var length = LengthCalc?.Invoke(receiveMessageCopy);
|
||||
List<byte[]> duplicatedMessages;
|
||||
int? length = -1;
|
||||
try
|
||||
{
|
||||
length = LengthCalc?.Invoke(receiveMessageCopy);
|
||||
}
|
||||
catch
|
||||
{
|
||||
//ignore
|
||||
}
|
||||
List<(byte[], bool)> duplicatedMessages;
|
||||
if (length == null || length == -1) return ans;
|
||||
if (length == 0) return null;
|
||||
else if (length == 0) return null;
|
||||
else
|
||||
{
|
||||
duplicatedMessages = new List<byte[]>();
|
||||
duplicatedMessages = new List<(byte[], bool)>();
|
||||
var skipLength = 0;
|
||||
while (receiveMessageCopy.Length >= length)
|
||||
{
|
||||
var duplicateMessage = receiveMessageCopy.Take(length.Value).ToArray();
|
||||
if (CheckRightFunc != null && CheckRightFunc(duplicateMessage) == false)
|
||||
{
|
||||
receiveMessageCopy = receiveMessageCopy.TakeLast(receiveMessage.Length - 1).ToArray();
|
||||
receiveMessageCopy = receiveMessageCopy.TakeLast(receiveMessageCopy.Length - 1).ToArray();
|
||||
skipLength++;
|
||||
continue;
|
||||
}
|
||||
duplicatedMessages.Add(duplicateMessage);
|
||||
receiveMessageCopy = receiveMessageCopy.TakeLast(receiveMessage.Length - length.Value).ToArray();
|
||||
if (skipLength > 0)
|
||||
{
|
||||
duplicatedMessages.Add((new byte[skipLength], false));
|
||||
}
|
||||
skipLength = 0;
|
||||
duplicatedMessages.Add((duplicateMessage, true));
|
||||
receiveMessageCopy = receiveMessageCopy.TakeLast(receiveMessageCopy.Length - length.Value).ToArray();
|
||||
if (receiveMessageCopy.Length == 0) break;
|
||||
length = LengthCalc?.Invoke(receiveMessageCopy);
|
||||
if (length == -1) break;
|
||||
if (length == 0) return null;
|
||||
}
|
||||
if (skipLength > 0)
|
||||
{
|
||||
lock (WaitingMessages)
|
||||
{
|
||||
var def = GetMessageFromWaitingList(null);
|
||||
if (def != null)
|
||||
{
|
||||
lock (WaitingMessages)
|
||||
{
|
||||
if (WaitingMessages.IndexOf(def) >= 0)
|
||||
{
|
||||
WaitingMessages.Remove(def);
|
||||
}
|
||||
}
|
||||
def.ReceiveMutex.Set();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
foreach (var message in duplicatedMessages)
|
||||
{
|
||||
var def = GetMessageFromWaitingList(message);
|
||||
if (def != null)
|
||||
if (!message.Item2)
|
||||
{
|
||||
def.ReceiveMessage = receiveMessage;
|
||||
lock (WaitingMessages)
|
||||
{
|
||||
WaitingMessages.Remove(def);
|
||||
}
|
||||
def.ReceiveMutex.Set();
|
||||
ans.Add((message, true));
|
||||
ans.Add((message.Item1, true));
|
||||
}
|
||||
else
|
||||
{
|
||||
ans.Add((message, false));
|
||||
var def = GetMessageFromWaitingList(message.Item1);
|
||||
if (def != null)
|
||||
{
|
||||
def.ReceiveMessage = message.Item1;
|
||||
lock (WaitingMessages)
|
||||
{
|
||||
if (WaitingMessages.IndexOf(def) >= 0)
|
||||
{
|
||||
WaitingMessages.Remove(def);
|
||||
}
|
||||
}
|
||||
def.ReceiveMutex.Set();
|
||||
ans.Add((message.Item1, true));
|
||||
}
|
||||
else
|
||||
{
|
||||
ans.Add((message.Item1, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
return ans;
|
||||
@@ -172,7 +222,10 @@ namespace Modbus.Net
|
||||
{
|
||||
lock (WaitingMessages)
|
||||
{
|
||||
WaitingMessages.Remove(def);
|
||||
if (WaitingMessages.IndexOf(def) >= 0)
|
||||
{
|
||||
WaitingMessages.Remove(def);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,9 +20,10 @@ namespace Modbus.Net
|
||||
var ans = 0;
|
||||
foreach (var position in packageCountPositions)
|
||||
{
|
||||
if (position >= receiveMessage.Length) return -1;
|
||||
if (position > receiveMessage.Length - 1) return -1;
|
||||
ans = ans * 256 + receiveMessage[position];
|
||||
}
|
||||
if (ans + otherCount > receiveMessage.Length) return -1;
|
||||
return ans + otherCount;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,8 +18,6 @@ namespace Modbus.Net
|
||||
|
||||
private int _waitingListMaxCount;
|
||||
|
||||
private readonly Semaphore _taskCycleSema;
|
||||
|
||||
/// <summary>
|
||||
/// 间隔时间
|
||||
/// </summary>
|
||||
@@ -29,35 +27,28 @@ namespace Modbus.Net
|
||||
/// 构造器
|
||||
/// </summary>
|
||||
/// <param name="acquireTime">间隔时间</param>
|
||||
/// <param name="activateSema">是否开启信号量</param>
|
||||
/// <param name="lengthCalc">包切分长度函数</param>
|
||||
/// <param name="checkRightFunc">包校验函数</param>
|
||||
/// <param name="waitingListMaxCount">包等待队列长度</param>
|
||||
public FifoController(int acquireTime, bool activateSema = true, Func<byte[], int> lengthCalc = null, Func<byte[], bool?> checkRightFunc = null, int? waitingListMaxCount = null)
|
||||
public FifoController(int acquireTime, Func<byte[], int> lengthCalc = null, Func<byte[], bool?> checkRightFunc = null, int? waitingListMaxCount = null)
|
||||
: base(lengthCalc, checkRightFunc)
|
||||
{
|
||||
_waitingListMaxCount = int.Parse(waitingListMaxCount != null ? waitingListMaxCount.ToString() : null ?? ConfigurationReader.GetValueDirect("Controller", "WaitingListCount"));
|
||||
if (activateSema)
|
||||
{
|
||||
_taskCycleSema = new Semaphore(0, _waitingListMaxCount);
|
||||
}
|
||||
AcquireTime = acquireTime;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void SendingMessageControlInner()
|
||||
{
|
||||
try
|
||||
while (!_taskCancel)
|
||||
{
|
||||
_taskCycleSema?.WaitOne();
|
||||
while (!_taskCancel)
|
||||
if (AcquireTime > 0)
|
||||
{
|
||||
if (AcquireTime > 0)
|
||||
{
|
||||
Thread.Sleep(AcquireTime);
|
||||
}
|
||||
bool sendSuccess = false;
|
||||
lock (WaitingMessages)
|
||||
Thread.Sleep(AcquireTime);
|
||||
}
|
||||
lock (WaitingMessages)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_currentSendingPos == null)
|
||||
{
|
||||
@@ -65,7 +56,6 @@ namespace Modbus.Net
|
||||
{
|
||||
_currentSendingPos = WaitingMessages.First();
|
||||
_currentSendingPos.SendMutex.Set();
|
||||
sendSuccess = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -73,32 +63,27 @@ namespace Modbus.Net
|
||||
if (WaitingMessages.Count <= 0)
|
||||
{
|
||||
_currentSendingPos = null;
|
||||
_taskCycleSema?.Close();
|
||||
sendSuccess = true;
|
||||
}
|
||||
else if (WaitingMessages.Count > WaitingMessages.IndexOf(_currentSendingPos) + 1)
|
||||
else if (WaitingMessages.IndexOf(_currentSendingPos) == -1)
|
||||
{
|
||||
_currentSendingPos = WaitingMessages[WaitingMessages.IndexOf(_currentSendingPos) + 1];
|
||||
_currentSendingPos = WaitingMessages.First();
|
||||
_currentSendingPos.SendMutex.Set();
|
||||
sendSuccess = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (sendSuccess)
|
||||
catch (ObjectDisposedException e)
|
||||
{
|
||||
_taskCycleSema?.WaitOne();
|
||||
logger.LogError(e, "Controller _currentSendingPos disposed");
|
||||
_currentSendingPos = null;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogError(e, "Controller throws exception");
|
||||
_taskCancel = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
//ignore
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogError(e, "Controller throws exception");
|
||||
}
|
||||
|
||||
Clear();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -138,11 +123,11 @@ namespace Modbus.Net
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var success = base.AddMessageToList(def);
|
||||
if (success)
|
||||
if (_taskCancel)
|
||||
{
|
||||
_taskCycleSema?.Release();
|
||||
return false;
|
||||
}
|
||||
var success = base.AddMessageToList(def);
|
||||
return success;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,12 +19,11 @@ namespace Modbus.Net
|
||||
/// </summary>
|
||||
/// <param name="keyMatches">匹配字典,每个Collection代表一个匹配集合,每一个匹配集合中的数字代表需要匹配的位置,最后计算出来的数字是所有位置数字按照集合排序后叠放在一起</param>
|
||||
/// <param name="acquireTime">获取间隔</param>
|
||||
/// <param name="activateSema">是否开启信号量</param>
|
||||
/// <param name="lengthCalc">包长度计算</param>
|
||||
/// <param name="checkRightFunc">包校验函数</param>
|
||||
/// <param name="waitingListMaxCount">包等待队列长度</param>
|
||||
public MatchController(ICollection<(int, int)>[] keyMatches, int acquireTime, bool activateSema = true,
|
||||
Func<byte[], int> lengthCalc = null, Func<byte[], bool?> checkRightFunc = null, int? waitingListMaxCount = null) : base(acquireTime, activateSema, lengthCalc, checkRightFunc, waitingListMaxCount)
|
||||
public MatchController(ICollection<(int, int)>[] keyMatches, int acquireTime,
|
||||
Func<byte[], int> lengthCalc = null, Func<byte[], bool?> checkRightFunc = null, int? waitingListMaxCount = null) : base(acquireTime, lengthCalc, checkRightFunc, waitingListMaxCount)
|
||||
{
|
||||
KeyMatches = keyMatches;
|
||||
}
|
||||
@@ -39,7 +38,7 @@ namespace Modbus.Net
|
||||
int tmpCount = 0, tmpCount2 = 0;
|
||||
foreach (var matchPos in matchPoses)
|
||||
{
|
||||
if (matchPos.Item1 >= message.Length || matchPos.Item2 >= message.Length) return null;
|
||||
if (matchPos.Item1 > message.Length - 1 || matchPos.Item2 > message.Length - 1) return null;
|
||||
tmpCount = tmpCount * 256 + message[matchPos.Item1];
|
||||
tmpCount2 = tmpCount2 * 256 + message[matchPos.Item2];
|
||||
}
|
||||
@@ -52,6 +51,7 @@ namespace Modbus.Net
|
||||
/// <inheritdoc />
|
||||
protected override MessageWaitingDef GetMessageFromWaitingList(byte[] receiveMessage)
|
||||
{
|
||||
if (receiveMessage == null) return null;
|
||||
var returnKey = GetKeyFromMessage(receiveMessage);
|
||||
MessageWaitingDef ans;
|
||||
lock (WaitingMessages)
|
||||
|
||||
@@ -8,10 +8,15 @@ namespace Modbus.Net
|
||||
/// </summary>
|
||||
public class MatchDirectlySendController : MatchController
|
||||
{
|
||||
/// <summary>
|
||||
/// 消息维护线程是否在运行
|
||||
/// </summary>
|
||||
public override bool IsSending => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchDirectlySendController(ICollection<(int, int)>[] keyMatches,
|
||||
Func<byte[], int> lengthCalc = null, Func<byte[], bool?> checkRightFunc = null, int? waitingListMaxCount = null) : base(keyMatches,
|
||||
0, false, lengthCalc, checkRightFunc, waitingListMaxCount)
|
||||
0, lengthCalc, checkRightFunc, waitingListMaxCount)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
152
Modbus.Net/Modbus.Net/Enum/Enum.cs
Normal file
152
Modbus.Net/Modbus.Net/Enum/Enum.cs
Normal file
@@ -0,0 +1,152 @@
|
||||
using System;
|
||||
|
||||
namespace Modbus.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// 端格式
|
||||
/// </summary>
|
||||
public partial class Endian
|
||||
{
|
||||
/// <summary>
|
||||
/// 小端
|
||||
/// </summary>
|
||||
public const int LittleEndianLsb = 1;
|
||||
/// <summary>
|
||||
/// 大端-大端位
|
||||
/// </summary>
|
||||
public const int BigEndianLsb = 2;
|
||||
/// <summary>
|
||||
/// 大端-大端位
|
||||
/// </summary>
|
||||
public const int BigEndianMsb = 3;
|
||||
|
||||
#pragma warning disable
|
||||
protected int Value { get; set; }
|
||||
|
||||
protected Endian(int value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public static implicit operator int(Endian endian)
|
||||
{
|
||||
return endian.Value;
|
||||
}
|
||||
|
||||
public static implicit operator Endian(int value)
|
||||
{
|
||||
return new Endian(value);
|
||||
}
|
||||
|
||||
public static implicit operator Endian(string value)
|
||||
{
|
||||
return Endian.Parse(value);
|
||||
}
|
||||
|
||||
public static implicit operator string(Endian endian)
|
||||
{
|
||||
return endian.ToString();
|
||||
}
|
||||
|
||||
public static Endian Parse(string value)
|
||||
{
|
||||
var Assemblies = AssemblyHelper.GetAllLibraryAssemblies();
|
||||
foreach (var assembly in Assemblies)
|
||||
{
|
||||
if (assembly.GetType("Modbus.Net.Endian")?.GetField(value) != null)
|
||||
{
|
||||
return (int)assembly.GetType("Modbus.Net.Endian").GetField(value).GetValue(null);
|
||||
}
|
||||
}
|
||||
throw new NotSupportedException("Endian name " + value + " is not supported.");
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var Assemblies = AssemblyHelper.GetAllLibraryAssemblies();
|
||||
foreach (var assembly in Assemblies)
|
||||
{
|
||||
var endianType = assembly.GetType("Modbus.Net.Endian");
|
||||
if (endianType != null)
|
||||
{
|
||||
foreach (var field in endianType.GetFields())
|
||||
{
|
||||
if ((int)field.GetValue(null) == Value)
|
||||
{
|
||||
return field.Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
#pragma warning restore
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读写设备值的方式
|
||||
/// </summary>
|
||||
public enum MachineDataType
|
||||
{
|
||||
/// <summary>
|
||||
/// 地址
|
||||
/// </summary>
|
||||
Address,
|
||||
|
||||
/// <summary>
|
||||
/// 通讯标识
|
||||
/// </summary>
|
||||
CommunicationTag,
|
||||
|
||||
/// <summary>
|
||||
/// 名称
|
||||
/// </summary>
|
||||
Name,
|
||||
|
||||
/// <summary>
|
||||
/// Id
|
||||
/// </summary>
|
||||
Id
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 波特率
|
||||
/// </summary>
|
||||
public enum BaudRate
|
||||
{
|
||||
#pragma warning disable
|
||||
BaudRate75 = 75,
|
||||
BaudRate110 = 110,
|
||||
BaudRate134 = 134,
|
||||
BaudRate150 = 150,
|
||||
BaudRate300 = 300,
|
||||
BaudRate600 = 600,
|
||||
BaudRate1200 = 1200,
|
||||
BaudRate1800 = 1800,
|
||||
BaudRate2400 = 2400,
|
||||
BaudRate4800 = 4800,
|
||||
BaudRate9600 = 9600,
|
||||
BaudRate14400 = 14400,
|
||||
BaudRate19200 = 19200,
|
||||
BaudRate38400 = 38400,
|
||||
BaudRate57600 = 57600,
|
||||
BaudRate115200 = 115200,
|
||||
BaudRate128000 = 128000,
|
||||
BaudRate230400 = 230400,
|
||||
BaudRate460800 = 460800,
|
||||
BaudRate921600 = 921600,
|
||||
#pragma warning restore
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 数据位
|
||||
/// </summary>
|
||||
public enum DataBits
|
||||
{
|
||||
#pragma warning disable
|
||||
Seven = 7,
|
||||
Eight = 8,
|
||||
#pragma warning restore
|
||||
}
|
||||
|
||||
}
|
||||
29
Modbus.Net/Modbus.Net/Helper/AssemblyHelper.cs
Normal file
29
Modbus.Net/Modbus.Net/Helper/AssemblyHelper.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Modbus.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// 程序集辅助类
|
||||
/// </summary>
|
||||
public static class AssemblyHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取与Modbus.Net相关的所有程序集
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static List<Assembly> GetAllLibraryAssemblies()
|
||||
{
|
||||
List<Assembly> allAssemblies = new List<Assembly>();
|
||||
string path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
|
||||
allAssemblies.Add(Assembly.Load("Modbus.Net"));
|
||||
|
||||
foreach (string dll in Directory.GetFiles(path, "Modbus.Net.*.dll"))
|
||||
allAssemblies.Add(Assembly.LoadFile(dll));
|
||||
|
||||
return allAssemblies;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Modbus.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// AsyncHelper Class
|
||||
/// </summary>
|
||||
public static class AsyncHelper
|
||||
{
|
||||
private static readonly TaskFactory _myTaskFactory = new
|
||||
TaskFactory(CancellationToken.None,
|
||||
TaskCreationOptions.None,
|
||||
TaskContinuationOptions.None,
|
||||
TaskScheduler.Default);
|
||||
|
||||
/// <summary>
|
||||
/// Run async method syncronized
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult">Return type</typeparam>
|
||||
/// <param name="func">Async method with return</param>
|
||||
/// <returns>Return value</returns>
|
||||
public static TResult RunSync<TResult>(Func<Task<TResult>> func)
|
||||
{
|
||||
return _myTaskFactory
|
||||
.StartNew(func)
|
||||
.Unwrap()
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Run async method syncronized.
|
||||
/// </summary>
|
||||
/// <param name="func">Async method</param>
|
||||
public static void RunSync(Func<Task> func)
|
||||
{
|
||||
_myTaskFactory
|
||||
.StartNew(func)
|
||||
.Unwrap()
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Change async task to async task with cancellation token
|
||||
/// </summary>
|
||||
/// <param name="task">Async task</param>
|
||||
/// <param name="token">Cancellation Token</param>
|
||||
/// <returns>Task with Cancellation token</returns>
|
||||
public static Task WithCancellation(this Task task,
|
||||
CancellationToken token)
|
||||
{
|
||||
return task.ContinueWith(t => t.GetAwaiter().GetResult(), token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a CancellationToken to async task
|
||||
/// </summary>
|
||||
/// <typeparam name="T">type of a task</typeparam>
|
||||
/// <param name="task">Task</param>
|
||||
/// <param name="token">Cancellation token</param>
|
||||
/// <returns>Task with cancellation token</returns>
|
||||
public static Task<T> WithCancellation<T>(this Task<T> task,
|
||||
CancellationToken token)
|
||||
{
|
||||
return task.ContinueWith(t => t.GetAwaiter().GetResult(), token);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Modbus.Net
|
||||
namespace Modbus.Net
|
||||
{
|
||||
#if NET462
|
||||
#pragma warning disable 1591
|
||||
|
||||
@@ -8,16 +8,17 @@ using System.Text;
|
||||
namespace Modbus.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// 值与字节数组之间转换的辅助类(小端格式),这是一个Singleton类
|
||||
/// 值与字节数组之间转换的辅助类
|
||||
/// </summary>
|
||||
public class ValueHelper
|
||||
public abstract class ValueHelper
|
||||
{
|
||||
private static readonly ILogger<ValueHelper> logger = LogProvider.CreateLogger<ValueHelper>();
|
||||
|
||||
/// <summary>
|
||||
/// 兼容数据类型对应的字节长度
|
||||
/// </summary>
|
||||
public Dictionary<string, double> ByteLength = new Dictionary<string, double>
|
||||
/// <summary>
|
||||
/// 兼容数据类型对应的字节长度
|
||||
/// </summary>
|
||||
public static Dictionary<string, double> ByteLength => new Dictionary<string, double>
|
||||
{
|
||||
{"System.Boolean", 0.125},
|
||||
{"System.Byte", 1},
|
||||
@@ -31,10 +32,315 @@ namespace Modbus.Net
|
||||
{"System.Double", 8}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// 将一个byte数字转换为一个byte元素的数组。
|
||||
/// </summary>
|
||||
/// <param name="value">byte数字</param>
|
||||
/// <returns>byte数组</returns>
|
||||
public abstract byte[] GetBytes(byte value);
|
||||
|
||||
/// <summary>
|
||||
/// 将short数字转换为byte数组
|
||||
/// </summary>
|
||||
/// <param name="value">待转换的值</param>
|
||||
/// <returns>转换后的byte数组</returns>
|
||||
public abstract byte[] GetBytes(short value);
|
||||
|
||||
/// <summary>
|
||||
/// 将int数字转换为byte数组
|
||||
/// </summary>
|
||||
/// <param name="value">待转换的值</param>
|
||||
/// <returns>转换后的byte数组</returns>
|
||||
public abstract byte[] GetBytes(int value);
|
||||
|
||||
/// <summary>
|
||||
/// 将long数字转换为byte数组
|
||||
/// </summary>
|
||||
/// <param name="value">待转换的值</param>
|
||||
/// <returns>转换后的byte数组</returns>
|
||||
public abstract byte[] GetBytes(long value);
|
||||
|
||||
/// <summary>
|
||||
/// 将ushort数字转换为byte数组
|
||||
/// </summary>
|
||||
/// <param name="value">待转换的值</param>
|
||||
/// <returns>转换后的byte数组</returns>
|
||||
public abstract byte[] GetBytes(ushort value);
|
||||
|
||||
/// <summary>
|
||||
/// 将uint数字转换为byte数组
|
||||
/// </summary>
|
||||
/// <param name="value">待转换的值</param>
|
||||
/// <returns>转换后的byte数组</returns>
|
||||
public abstract byte[] GetBytes(uint value);
|
||||
|
||||
/// <summary>
|
||||
/// 将ulong数字转换为byte数组
|
||||
/// </summary>
|
||||
/// <param name="value">待转换的值</param>
|
||||
/// <returns>转换后的byte数组</returns>
|
||||
public abstract byte[] GetBytes(ulong value);
|
||||
|
||||
/// <summary>
|
||||
/// 将float数字转换为byte数组
|
||||
/// </summary>
|
||||
/// <param name="value">待转换的值</param>
|
||||
/// <returns>转换后的byte数组</returns>
|
||||
public abstract byte[] GetBytes(float value);
|
||||
|
||||
/// <summary>
|
||||
/// 将double数字转换为byte数组
|
||||
/// </summary>
|
||||
/// <param name="value">待转换的值</param>
|
||||
/// <returns>转换后的byte数组</returns>
|
||||
public abstract byte[] GetBytes(double value);
|
||||
|
||||
/// <summary>
|
||||
/// 将object数字转换为byte数组
|
||||
/// </summary>
|
||||
/// <param name="value">待转换的值</param>
|
||||
/// <param name="type">待转换的值的类型</param>
|
||||
/// <returns>转换后的byte数组</returns>
|
||||
public abstract byte[] GetBytes(object value, Type type);
|
||||
|
||||
/// <summary>
|
||||
/// 将byte数组中相应的位置转换为对应类型的数字
|
||||
/// </summary>
|
||||
/// <param name="data">待转换的字节数组</param>
|
||||
/// <param name="pos">转换数字的位置</param>
|
||||
/// <param name="subPos">转换数字的比特位置(仅Type为bool的时候有效)</param>
|
||||
/// <param name="t">转换的类型</param>
|
||||
/// <returns>转换出的数字</returns>
|
||||
public abstract object GetValue(byte[] data, ref int pos, ref int subPos, Type t);
|
||||
|
||||
/// <summary>
|
||||
/// 将byte数组中相应的位置转换为byte数字
|
||||
/// </summary>
|
||||
/// <param name="data">待转换的数组</param>
|
||||
/// <param name="pos">转换数字的位置</param>
|
||||
/// <returns>转换出的数字</returns>
|
||||
public abstract byte GetByte(byte[] data, ref int pos);
|
||||
|
||||
/// <summary>
|
||||
/// 将byte数组中相应的位置转换为short数字
|
||||
/// </summary>
|
||||
/// <param name="data">待转换的数组</param>
|
||||
/// <param name="pos">转换数字的位置</param>
|
||||
/// <returns>转换出的数字</returns>
|
||||
public abstract short GetShort(byte[] data, ref int pos);
|
||||
|
||||
/// <summary>
|
||||
/// 将byte数组中相应的位置转换为int数字
|
||||
/// </summary>
|
||||
/// <param name="data">待转换的数组</param>
|
||||
/// <param name="pos">转换数字的位置</param>
|
||||
/// <returns>转换出的数字</returns>
|
||||
public abstract int GetInt(byte[] data, ref int pos);
|
||||
|
||||
/// <summary>
|
||||
/// 将byte数组中相应的位置转换为long数字
|
||||
/// </summary>
|
||||
/// <param name="data">待转换的数组</param>
|
||||
/// <param name="pos">转换数字的位置</param>
|
||||
/// <returns>转换出的数字</returns>
|
||||
public abstract long GetLong(byte[] data, ref int pos);
|
||||
|
||||
/// <summary>
|
||||
/// 将byte数组中相应的位置转换为ushort数字
|
||||
/// </summary>
|
||||
/// <param name="data">待转换的数组</param>
|
||||
/// <param name="pos">转换数字的位置</param>
|
||||
/// <returns>转换出的数字</returns>
|
||||
public abstract ushort GetUShort(byte[] data, ref int pos);
|
||||
|
||||
/// <summary>
|
||||
/// 将byte数组中相应的位置转换为uint数字
|
||||
/// </summary>
|
||||
/// <param name="data">待转换的数组</param>
|
||||
/// <param name="pos">转换数字的位置</param>
|
||||
/// <returns>转换出的数字</returns>
|
||||
public abstract uint GetUInt(byte[] data, ref int pos);
|
||||
|
||||
/// <summary>
|
||||
/// 将byte数组中相应的位置转换为ulong数字
|
||||
/// </summary>
|
||||
/// <param name="data">待转换的数组</param>
|
||||
/// <param name="pos">转换数字的位置</param>
|
||||
/// <returns>转换出的数字</returns>
|
||||
public abstract ulong GetULong(byte[] data, ref int pos);
|
||||
|
||||
/// <summary>
|
||||
/// 将byte数组中相应的位置转换为float数字
|
||||
/// </summary>
|
||||
/// <param name="data">待转换的数组</param>
|
||||
/// <param name="pos">转换数字的位置</param>
|
||||
/// <returns>转换出的数字</returns>
|
||||
public abstract float GetFloat(byte[] data, ref int pos);
|
||||
|
||||
/// <summary>
|
||||
/// 将byte数组中相应的位置转换为double数字
|
||||
/// </summary>
|
||||
/// <param name="data">待转换的数组</param>
|
||||
/// <param name="pos">转换数字的位置</param>
|
||||
/// <returns>转换出的数字</returns>
|
||||
public abstract double GetDouble(byte[] data, ref int pos);
|
||||
|
||||
/// <summary>
|
||||
/// 将byte数组中相应的位置转换为字符串
|
||||
/// </summary>
|
||||
/// <param name="data">待转换的数组</param>
|
||||
/// <param name="count">转换的个数</param>
|
||||
/// <param name="pos">转换数字的位置</param>
|
||||
/// <param name="encoding">编码方法</param>
|
||||
/// <returns>转换出的字符串</returns>
|
||||
public abstract string GetString(byte[] data, int count, ref int pos, Encoding encoding);
|
||||
|
||||
/// <summary>
|
||||
/// 将byte数组中相应的位置转换为8个bit数字
|
||||
/// </summary>
|
||||
/// <param name="data">待转换的数组</param>
|
||||
/// <param name="pos">转换数字的位置</param>
|
||||
/// <returns>转换出的位数组</returns>
|
||||
public abstract bool[] GetBits(byte[] data, ref int pos);
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个byte中相对应的bit数组展开中第n个位置中的bit元素。
|
||||
/// </summary>
|
||||
/// <param name="number">byte数字</param>
|
||||
/// <param name="pos">bit数组中的对应位置</param>
|
||||
/// <param name="subPos">小数位</param>
|
||||
/// <returns>对应位置的bit元素</returns>
|
||||
public abstract bool GetBit(byte number, ref int pos, ref int subPos);
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个字节数组中某个Bit位的数据
|
||||
/// </summary>
|
||||
/// <param name="number">byte数字</param>
|
||||
/// <param name="pos">bit数组中的对应位置</param>
|
||||
/// <param name="subPos">小数位</param>
|
||||
/// <returns>对应位置的bit元素</returns>
|
||||
public abstract bool GetBit(byte[] number, ref int pos, ref int subPos);
|
||||
|
||||
/// <summary>
|
||||
/// 反转一个字节的8个Bit位
|
||||
/// </summary>
|
||||
/// <param name="originalByte">原始bit数组</param>
|
||||
/// <returns>反转的bit数组</returns>
|
||||
public abstract byte ReverseByte(byte originalByte);
|
||||
|
||||
/// <summary>
|
||||
/// 将待转换的对象数组转换为需要发送的byte数组
|
||||
/// </summary>
|
||||
/// <param name="contents">object数组</param>
|
||||
/// <returns>byte数组</returns>
|
||||
public abstract byte[] ObjectArrayToByteArray(object[] contents);
|
||||
|
||||
/// <summary>
|
||||
/// 将byte数组转换为用户指定类型的数组,通过object数组的方式返回,用户需要再把object转换为自己需要的类型,或调用ObjectArrayToDestinationArray返回单一类型的目标数组。
|
||||
/// </summary>
|
||||
/// <param name="contents">byte数组</param>
|
||||
/// <param name="translateTypeAndCount">单一的类型和需要转换的个数的键值对</param>
|
||||
/// <returns>object数组</returns>
|
||||
public abstract object[] ByteArrayToObjectArray(byte[] contents, KeyValuePair<Type, int> translateTypeAndCount);
|
||||
|
||||
/// <summary>
|
||||
/// 将一个byte数组转换成用户指定类型的数组,使用模板参数确定需要转换的类型
|
||||
/// </summary>
|
||||
/// <typeparam name="T">目标数组类型</typeparam>
|
||||
/// <param name="contents">待转换的数组</param>
|
||||
/// <param name="getCount">转换的个数</param>
|
||||
/// <returns>以T为类型的数组</returns>
|
||||
public abstract T[] ByteArrayToDestinationArray<T>(byte[] contents, int getCount);
|
||||
|
||||
/// <summary>
|
||||
/// 将byte数组转换为用户指定类型的数组,通过object数组的方式返回,用户需要再把object转换为自己需要的类型,或调用ObjectArrayToDestinationArray返回单一类型的目标数组。
|
||||
/// </summary>
|
||||
/// <param name="contents">byte数组</param>
|
||||
/// <param name="translateTypeAndCount">
|
||||
/// 一连串类型和需要转换的个数的键值对,该方法会依次转换每一个需要转的目标数据类型。比如:typeof(int),5; typeof(short),3
|
||||
/// 会转换出8个元素(当然前提是byte数组足够长的时候),5个int和3个short,然后全部变为object类型返回。
|
||||
/// </param>
|
||||
/// <returns>object数组</returns>
|
||||
public abstract object[] ByteArrayToObjectArray(byte[] contents, IEnumerable<KeyValuePair<Type, int>> translateTypeAndCount);
|
||||
|
||||
/// <summary>
|
||||
/// 将object数组转换为目标类型的单一数组
|
||||
/// </summary>
|
||||
/// <typeparam name="T">需要转换的目标类型</typeparam>
|
||||
/// <param name="contents">object数组</param>
|
||||
/// <returns>目标数组</returns>
|
||||
public abstract T[] ObjectArrayToDestinationArray<T>(object[] contents);
|
||||
|
||||
/// <summary>
|
||||
/// 在一个数组中写一个值
|
||||
/// </summary>
|
||||
/// <param name="contents">待写入的字节数组</param>
|
||||
/// <param name="setPos">设置的位置</param>
|
||||
/// <param name="subPos">设置的比特位位置(仅setValue为bit的时候有效)</param>
|
||||
/// <param name="setValue">写入的值</param>
|
||||
/// <returns>写入是否成功</returns>
|
||||
public abstract bool SetValue(byte[] contents, int setPos, int subPos, object setValue);
|
||||
|
||||
/// <summary>
|
||||
/// 将short值设置到byte数组中
|
||||
/// </summary>
|
||||
/// <param name="contents">待设置的byte数组</param>
|
||||
/// <param name="pos">设置的位置</param>
|
||||
/// <param name="setValue">要设置的值</param>
|
||||
/// <returns>设置是否成功</returns>
|
||||
public abstract bool SetValue(byte[] contents, int pos, object setValue);
|
||||
|
||||
/// <summary>
|
||||
/// 设置一组数据中的一个bit
|
||||
/// </summary>
|
||||
/// <param name="contents">待设置的字节数组</param>
|
||||
/// <param name="pos">位置</param>
|
||||
/// <param name="subPos">bit位置</param>
|
||||
/// <param name="setValue">bit数</param>
|
||||
/// <returns>设置是否成功</returns>
|
||||
public abstract bool SetBit(byte[] contents, int pos, int subPos, bool setValue);
|
||||
|
||||
/// <summary>
|
||||
/// 根据端格式获取ValueHelper实例
|
||||
/// </summary>
|
||||
/// <param name="endian">端格式</param>
|
||||
/// <returns>对应的ValueHelper实例</returns>
|
||||
public static ValueHelper GetInstance(Endian endian)
|
||||
{
|
||||
if (EndianInstanceCache.ContainsKey(endian.ToString()))
|
||||
{
|
||||
return EndianInstanceCache[endian.ToString()];
|
||||
}
|
||||
foreach (var assembly in AssemblyHelper.GetAllLibraryAssemblies())
|
||||
{
|
||||
var valueHelper = assembly.GetType("Modbus.Net." + endian.ToString() + "ValueHelper")?.GetProperty("Instance")?.GetValue(null, null) as ValueHelper;
|
||||
if (valueHelper != null)
|
||||
{
|
||||
EndianInstanceCache[endian.ToString()] = valueHelper;
|
||||
return valueHelper;
|
||||
}
|
||||
|
||||
}
|
||||
throw new NotImplementedException("ValueHelper " + endian.ToString() + " doesn't exist.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ValueHelper实例的缓存
|
||||
/// </summary>
|
||||
protected static Dictionary<string, ValueHelper> EndianInstanceCache = new Dictionary<string, ValueHelper>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 值与字节数组之间转换的辅助类(小端格式),这是一个Singleton类
|
||||
/// </summary>
|
||||
public class LittleEndianLsbValueHelper : ValueHelper
|
||||
{
|
||||
private static readonly ILogger<LittleEndianLsbValueHelper> logger = LogProvider.CreateLogger<LittleEndianLsbValueHelper>();
|
||||
|
||||
/// <summary>
|
||||
/// 构造器
|
||||
/// </summary>
|
||||
protected ValueHelper()
|
||||
protected LittleEndianLsbValueHelper()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -53,7 +359,7 @@ namespace Modbus.Net
|
||||
/// </summary>
|
||||
/// <param name="value">byte数字</param>
|
||||
/// <returns>byte数组</returns>
|
||||
public byte[] GetBytes(byte value)
|
||||
public override byte[] GetBytes(byte value)
|
||||
{
|
||||
return new[] { value };
|
||||
}
|
||||
@@ -63,7 +369,7 @@ namespace Modbus.Net
|
||||
/// </summary>
|
||||
/// <param name="value">待转换的值</param>
|
||||
/// <returns>转换后的byte数组</returns>
|
||||
public virtual byte[] GetBytes(short value)
|
||||
public override byte[] GetBytes(short value)
|
||||
{
|
||||
return BitConverter.GetBytes(value);
|
||||
}
|
||||
@@ -73,7 +379,7 @@ namespace Modbus.Net
|
||||
/// </summary>
|
||||
/// <param name="value">待转换的值</param>
|
||||
/// <returns>转换后的byte数组</returns>
|
||||
public virtual byte[] GetBytes(int value)
|
||||
public override byte[] GetBytes(int value)
|
||||
{
|
||||
return BitConverter.GetBytes(value);
|
||||
}
|
||||
@@ -83,7 +389,7 @@ namespace Modbus.Net
|
||||
/// </summary>
|
||||
/// <param name="value">待转换的值</param>
|
||||
/// <returns>转换后的byte数组</returns>
|
||||
public virtual byte[] GetBytes(long value)
|
||||
public override byte[] GetBytes(long value)
|
||||
{
|
||||
return BitConverter.GetBytes(value);
|
||||
}
|
||||
@@ -93,7 +399,7 @@ namespace Modbus.Net
|
||||
/// </summary>
|
||||
/// <param name="value">待转换的值</param>
|
||||
/// <returns>转换后的byte数组</returns>
|
||||
public virtual byte[] GetBytes(ushort value)
|
||||
public override byte[] GetBytes(ushort value)
|
||||
{
|
||||
return BitConverter.GetBytes(value);
|
||||
}
|
||||
@@ -103,7 +409,7 @@ namespace Modbus.Net
|
||||
/// </summary>
|
||||
/// <param name="value">待转换的值</param>
|
||||
/// <returns>转换后的byte数组</returns>
|
||||
public virtual byte[] GetBytes(uint value)
|
||||
public override byte[] GetBytes(uint value)
|
||||
{
|
||||
return BitConverter.GetBytes(value);
|
||||
}
|
||||
@@ -113,7 +419,7 @@ namespace Modbus.Net
|
||||
/// </summary>
|
||||
/// <param name="value">待转换的值</param>
|
||||
/// <returns>转换后的byte数组</returns>
|
||||
public virtual byte[] GetBytes(ulong value)
|
||||
public override byte[] GetBytes(ulong value)
|
||||
{
|
||||
return BitConverter.GetBytes(value);
|
||||
}
|
||||
@@ -123,7 +429,7 @@ namespace Modbus.Net
|
||||
/// </summary>
|
||||
/// <param name="value">待转换的值</param>
|
||||
/// <returns>转换后的byte数组</returns>
|
||||
public virtual byte[] GetBytes(float value)
|
||||
public override byte[] GetBytes(float value)
|
||||
{
|
||||
return BitConverter.GetBytes(value);
|
||||
}
|
||||
@@ -133,7 +439,7 @@ namespace Modbus.Net
|
||||
/// </summary>
|
||||
/// <param name="value">待转换的值</param>
|
||||
/// <returns>转换后的byte数组</returns>
|
||||
public virtual byte[] GetBytes(double value)
|
||||
public override byte[] GetBytes(double value)
|
||||
{
|
||||
return BitConverter.GetBytes(value);
|
||||
}
|
||||
@@ -144,7 +450,7 @@ namespace Modbus.Net
|
||||
/// <param name="value">待转换的值</param>
|
||||
/// <param name="type">待转换的值的类型</param>
|
||||
/// <returns>转换后的byte数组</returns>
|
||||
public virtual byte[] GetBytes(object value, Type type)
|
||||
public override byte[] GetBytes(object value, Type type)
|
||||
{
|
||||
switch (type.FullName)
|
||||
{
|
||||
@@ -193,6 +499,11 @@ namespace Modbus.Net
|
||||
var bytes = _Instance.GetBytes((byte)value);
|
||||
return bytes;
|
||||
}
|
||||
case "System.Boolean":
|
||||
{
|
||||
var bytes = _Instance.GetBytes((bool)value ? (byte)1 : (byte)0);
|
||||
return bytes;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new NotImplementedException("没有实现除整数以外的其它转换方式");
|
||||
@@ -208,7 +519,7 @@ namespace Modbus.Net
|
||||
/// <param name="subPos">转换数字的比特位置(仅Type为bool的时候有效)</param>
|
||||
/// <param name="t">转换的类型</param>
|
||||
/// <returns>转换出的数字</returns>
|
||||
public virtual object GetValue(byte[] data, ref int pos, ref int subPos, Type t)
|
||||
public override object GetValue(byte[] data, ref int pos, ref int subPos, Type t)
|
||||
{
|
||||
switch (t.FullName)
|
||||
{
|
||||
@@ -275,7 +586,7 @@ namespace Modbus.Net
|
||||
/// <param name="data">待转换的数组</param>
|
||||
/// <param name="pos">转换数字的位置</param>
|
||||
/// <returns>转换出的数字</returns>
|
||||
public virtual byte GetByte(byte[] data, ref int pos)
|
||||
public override byte GetByte(byte[] data, ref int pos)
|
||||
{
|
||||
var t = data[pos];
|
||||
pos += 1;
|
||||
@@ -288,7 +599,7 @@ namespace Modbus.Net
|
||||
/// <param name="data">待转换的数组</param>
|
||||
/// <param name="pos">转换数字的位置</param>
|
||||
/// <returns>转换出的数字</returns>
|
||||
public virtual short GetShort(byte[] data, ref int pos)
|
||||
public override short GetShort(byte[] data, ref int pos)
|
||||
{
|
||||
var t = BitConverter.ToInt16(data, pos);
|
||||
pos += 2;
|
||||
@@ -301,7 +612,7 @@ namespace Modbus.Net
|
||||
/// <param name="data">待转换的数组</param>
|
||||
/// <param name="pos">转换数字的位置</param>
|
||||
/// <returns>转换出的数字</returns>
|
||||
public virtual int GetInt(byte[] data, ref int pos)
|
||||
public override int GetInt(byte[] data, ref int pos)
|
||||
{
|
||||
var t = BitConverter.ToInt32(data, pos);
|
||||
pos += 4;
|
||||
@@ -314,7 +625,7 @@ namespace Modbus.Net
|
||||
/// <param name="data">待转换的数组</param>
|
||||
/// <param name="pos">转换数字的位置</param>
|
||||
/// <returns>转换出的数字</returns>
|
||||
public virtual long GetLong(byte[] data, ref int pos)
|
||||
public override long GetLong(byte[] data, ref int pos)
|
||||
{
|
||||
var t = BitConverter.ToInt64(data, pos);
|
||||
pos += 8;
|
||||
@@ -327,7 +638,7 @@ namespace Modbus.Net
|
||||
/// <param name="data">待转换的数组</param>
|
||||
/// <param name="pos">转换数字的位置</param>
|
||||
/// <returns>转换出的数字</returns>
|
||||
public virtual ushort GetUShort(byte[] data, ref int pos)
|
||||
public override ushort GetUShort(byte[] data, ref int pos)
|
||||
{
|
||||
var t = BitConverter.ToUInt16(data, pos);
|
||||
pos += 2;
|
||||
@@ -340,7 +651,7 @@ namespace Modbus.Net
|
||||
/// <param name="data">待转换的数组</param>
|
||||
/// <param name="pos">转换数字的位置</param>
|
||||
/// <returns>转换出的数字</returns>
|
||||
public virtual uint GetUInt(byte[] data, ref int pos)
|
||||
public override uint GetUInt(byte[] data, ref int pos)
|
||||
{
|
||||
var t = BitConverter.ToUInt32(data, pos);
|
||||
pos += 4;
|
||||
@@ -353,7 +664,7 @@ namespace Modbus.Net
|
||||
/// <param name="data">待转换的数组</param>
|
||||
/// <param name="pos">转换数字的位置</param>
|
||||
/// <returns>转换出的数字</returns>
|
||||
public virtual ulong GetULong(byte[] data, ref int pos)
|
||||
public override ulong GetULong(byte[] data, ref int pos)
|
||||
{
|
||||
var t = BitConverter.ToUInt64(data, pos);
|
||||
pos += 8;
|
||||
@@ -366,7 +677,7 @@ namespace Modbus.Net
|
||||
/// <param name="data">待转换的数组</param>
|
||||
/// <param name="pos">转换数字的位置</param>
|
||||
/// <returns>转换出的数字</returns>
|
||||
public virtual float GetFloat(byte[] data, ref int pos)
|
||||
public override float GetFloat(byte[] data, ref int pos)
|
||||
{
|
||||
var t = BitConverter.ToSingle(data, pos);
|
||||
pos += 4;
|
||||
@@ -379,7 +690,7 @@ namespace Modbus.Net
|
||||
/// <param name="data">待转换的数组</param>
|
||||
/// <param name="pos">转换数字的位置</param>
|
||||
/// <returns>转换出的数字</returns>
|
||||
public virtual double GetDouble(byte[] data, ref int pos)
|
||||
public override double GetDouble(byte[] data, ref int pos)
|
||||
{
|
||||
var t = BitConverter.ToDouble(data, pos);
|
||||
pos += 8;
|
||||
@@ -394,7 +705,7 @@ namespace Modbus.Net
|
||||
/// <param name="pos">转换数字的位置</param>
|
||||
/// <param name="encoding">编码方法</param>
|
||||
/// <returns>转换出的字符串</returns>
|
||||
public virtual string GetString(byte[] data, int count, ref int pos, Encoding encoding)
|
||||
public override string GetString(byte[] data, int count, ref int pos, Encoding encoding)
|
||||
{
|
||||
var t = encoding.GetString(data, pos, count);
|
||||
pos += count;
|
||||
@@ -407,7 +718,7 @@ namespace Modbus.Net
|
||||
/// <param name="data">待转换的数组</param>
|
||||
/// <param name="pos">转换数字的位置</param>
|
||||
/// <returns>转换出的位数组</returns>
|
||||
public virtual bool[] GetBits(byte[] data, ref int pos)
|
||||
public override bool[] GetBits(byte[] data, ref int pos)
|
||||
{
|
||||
var t = new bool[8];
|
||||
var temp = data[pos];
|
||||
@@ -427,7 +738,7 @@ namespace Modbus.Net
|
||||
/// <param name="pos">bit数组中的对应位置</param>
|
||||
/// <param name="subPos">小数位</param>
|
||||
/// <returns>对应位置的bit元素</returns>
|
||||
public bool GetBit(byte number, ref int pos, ref int subPos)
|
||||
public override bool GetBit(byte number, ref int pos, ref int subPos)
|
||||
{
|
||||
if (subPos < 0 && subPos >= 8) throw new IndexOutOfRangeException();
|
||||
var ans = number % 2;
|
||||
@@ -454,7 +765,7 @@ namespace Modbus.Net
|
||||
/// <param name="pos">bit数组中的对应位置</param>
|
||||
/// <param name="subPos">小数位</param>
|
||||
/// <returns>对应位置的bit元素</returns>
|
||||
public virtual bool GetBit(byte[] number, ref int pos, ref int subPos)
|
||||
public override bool GetBit(byte[] number, ref int pos, ref int subPos)
|
||||
{
|
||||
return GetBit(number[pos], ref pos, ref subPos);
|
||||
}
|
||||
@@ -464,7 +775,7 @@ namespace Modbus.Net
|
||||
/// </summary>
|
||||
/// <param name="originalByte">原始bit数组</param>
|
||||
/// <returns>反转的bit数组</returns>
|
||||
public virtual byte ReverseByte(byte originalByte)
|
||||
public override byte ReverseByte(byte originalByte)
|
||||
{
|
||||
byte result = 0;
|
||||
for (var i = 0; i < 8; i++)
|
||||
@@ -481,7 +792,7 @@ namespace Modbus.Net
|
||||
/// </summary>
|
||||
/// <param name="contents">object数组</param>
|
||||
/// <returns>byte数组</returns>
|
||||
public virtual byte[] ObjectArrayToByteArray(object[] contents)
|
||||
public override byte[] ObjectArrayToByteArray(object[] contents)
|
||||
{
|
||||
var b = false;
|
||||
//先查找传入的结构中有没有数组,有的话将其打开
|
||||
@@ -604,7 +915,7 @@ namespace Modbus.Net
|
||||
/// <param name="contents">byte数组</param>
|
||||
/// <param name="translateTypeAndCount">单一的类型和需要转换的个数的键值对</param>
|
||||
/// <returns>object数组</returns>
|
||||
public virtual object[] ByteArrayToObjectArray(byte[] contents, KeyValuePair<Type, int> translateTypeAndCount)
|
||||
public override object[] ByteArrayToObjectArray(byte[] contents, KeyValuePair<Type, int> translateTypeAndCount)
|
||||
{
|
||||
return ByteArrayToObjectArray(contents, new List<KeyValuePair<Type, int>> { translateTypeAndCount });
|
||||
}
|
||||
@@ -616,7 +927,7 @@ namespace Modbus.Net
|
||||
/// <param name="contents">待转换的数组</param>
|
||||
/// <param name="getCount">转换的个数</param>
|
||||
/// <returns>以T为类型的数组</returns>
|
||||
public virtual T[] ByteArrayToDestinationArray<T>(byte[] contents, int getCount)
|
||||
public override T[] ByteArrayToDestinationArray<T>(byte[] contents, int getCount)
|
||||
{
|
||||
var objectArray = _Instance.ByteArrayToObjectArray(contents,
|
||||
new KeyValuePair<Type, int>(typeof(T), getCount));
|
||||
@@ -632,7 +943,7 @@ namespace Modbus.Net
|
||||
/// 会转换出8个元素(当然前提是byte数组足够长的时候),5个int和3个short,然后全部变为object类型返回。
|
||||
/// </param>
|
||||
/// <returns>object数组</returns>
|
||||
public virtual object[] ByteArrayToObjectArray(byte[] contents,
|
||||
public override object[] ByteArrayToObjectArray(byte[] contents,
|
||||
IEnumerable<KeyValuePair<Type, int>> translateTypeAndCount)
|
||||
{
|
||||
var translation = new List<object>();
|
||||
@@ -729,7 +1040,7 @@ namespace Modbus.Net
|
||||
/// <typeparam name="T">需要转换的目标类型</typeparam>
|
||||
/// <param name="contents">object数组</param>
|
||||
/// <returns>目标数组</returns>
|
||||
public T[] ObjectArrayToDestinationArray<T>(object[] contents)
|
||||
public override T[] ObjectArrayToDestinationArray<T>(object[] contents)
|
||||
{
|
||||
var array = new T[contents.Length];
|
||||
Array.Copy(contents, array, contents.Length);
|
||||
@@ -744,7 +1055,7 @@ namespace Modbus.Net
|
||||
/// <param name="subPos">设置的比特位位置(仅setValue为bit的时候有效)</param>
|
||||
/// <param name="setValue">写入的值</param>
|
||||
/// <returns>写入是否成功</returns>
|
||||
public bool SetValue(byte[] contents, int setPos, int subPos, object setValue)
|
||||
public override bool SetValue(byte[] contents, int setPos, int subPos, object setValue)
|
||||
{
|
||||
var type = setValue.GetType();
|
||||
|
||||
@@ -814,7 +1125,7 @@ namespace Modbus.Net
|
||||
/// <param name="pos">设置的位置</param>
|
||||
/// <param name="setValue">要设置的值</param>
|
||||
/// <returns>设置是否成功</returns>
|
||||
public virtual bool SetValue(byte[] contents, int pos, object setValue)
|
||||
public override bool SetValue(byte[] contents, int pos, object setValue)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -864,7 +1175,7 @@ namespace Modbus.Net
|
||||
/// <param name="subPos">bit位置</param>
|
||||
/// <param name="setValue">bit数</param>
|
||||
/// <returns>设置是否成功</returns>
|
||||
public virtual bool SetBit(byte[] contents, int pos, int subPos, bool setValue)
|
||||
public override bool SetBit(byte[] contents, int pos, int subPos, bool setValue)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -890,35 +1201,7 @@ namespace Modbus.Net
|
||||
/// <summary>
|
||||
/// ValueHelper单例的实例
|
||||
/// </summary>
|
||||
public static ValueHelper Instance => _instance ?? (_instance = new ValueHelper());
|
||||
|
||||
/// <summary>
|
||||
/// 根据端格式获取ValueHelper实例
|
||||
/// </summary>
|
||||
/// <param name="endian">端格式</param>
|
||||
/// <returns>对应的ValueHelper实例</returns>
|
||||
public static ValueHelper GetInstance(Endian endian)
|
||||
{
|
||||
switch (endian)
|
||||
{
|
||||
case Endian.LittleEndianLsb:
|
||||
{
|
||||
return Instance;
|
||||
}
|
||||
case Endian.BigEndianLsb:
|
||||
{
|
||||
return BigEndianValueHelper.Instance;
|
||||
}
|
||||
case Endian.BigEndianMsb:
|
||||
{
|
||||
return BigEndianMsbValueHelper.Instance;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return Instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
public static ValueHelper Instance => _instance ?? (_instance = new LittleEndianLsbValueHelper());
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -926,14 +1209,14 @@ namespace Modbus.Net
|
||||
/// <summary>
|
||||
/// 值与字节数组之间转换的辅助类(大端格式),这是一个Singleton类
|
||||
/// </summary>
|
||||
public class BigEndianValueHelper : ValueHelper
|
||||
public class BigEndianLsbValueHelper : LittleEndianLsbValueHelper
|
||||
{
|
||||
private static BigEndianValueHelper _bigEndianInstance;
|
||||
private static BigEndianLsbValueHelper _bigEndianInstance;
|
||||
|
||||
/// <summary>
|
||||
/// 构造器
|
||||
/// </summary>
|
||||
protected BigEndianValueHelper()
|
||||
protected BigEndianLsbValueHelper()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -950,8 +1233,8 @@ namespace Modbus.Net
|
||||
/// <summary>
|
||||
/// 覆盖的获取实例的方法
|
||||
/// </summary>
|
||||
public new static BigEndianValueHelper Instance
|
||||
=> _bigEndianInstance ?? (_bigEndianInstance = new BigEndianValueHelper());
|
||||
public new static BigEndianLsbValueHelper Instance
|
||||
=> _bigEndianInstance ?? (_bigEndianInstance = new BigEndianLsbValueHelper());
|
||||
|
||||
/// <summary>
|
||||
/// 将short数字转换为byte数组
|
||||
@@ -1169,9 +1452,9 @@ namespace Modbus.Net
|
||||
/// <summary>
|
||||
/// 值与字节数组之间转换的辅助类(大端-大端位格式),这是一个Singleton类
|
||||
/// </summary>
|
||||
public class BigEndianMsbValueHelper : BigEndianValueHelper
|
||||
public class BigEndianMsbValueHelper : BigEndianLsbValueHelper
|
||||
{
|
||||
private static BigEndianValueHelper _bigEndianInstance;
|
||||
private static BigEndianMsbValueHelper _bigEndianInstance;
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
@@ -1198,7 +1481,7 @@ namespace Modbus.Net
|
||||
/// <summary>
|
||||
/// 覆盖的实例获取方法
|
||||
/// </summary>
|
||||
public new static BigEndianValueHelper Instance
|
||||
public new static BigEndianMsbValueHelper Instance
|
||||
=> _bigEndianInstance ?? (_bigEndianInstance = new BigEndianMsbValueHelper());
|
||||
|
||||
/// <summary>
|
||||
@@ -7,6 +7,11 @@ namespace Modbus.Net
|
||||
/// </summary>
|
||||
public interface IController
|
||||
{
|
||||
/// <summary>
|
||||
/// 消息维护线程是否在运行
|
||||
/// </summary>
|
||||
bool IsSending { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 增加信息
|
||||
/// </summary>
|
||||
|
||||
@@ -15,26 +15,12 @@ namespace Modbus.Net
|
||||
/// </summary>
|
||||
public interface IMachineMethodDatas : IMachineMethod
|
||||
{
|
||||
/// <summary>
|
||||
/// 读取数据
|
||||
/// </summary>
|
||||
/// <returns>从设备读取的数据</returns>
|
||||
ReturnStruct<Dictionary<string, ReturnUnit<double>>> GetDatas(MachineDataType getDataType);
|
||||
|
||||
/// <summary>
|
||||
/// 读取数据
|
||||
/// </summary>
|
||||
/// <returns>从设备读取的数据</returns>
|
||||
Task<ReturnStruct<Dictionary<string, ReturnUnit<double>>>> GetDatasAsync(MachineDataType getDataType);
|
||||
|
||||
/// <summary>
|
||||
/// 写入数据
|
||||
/// </summary>
|
||||
/// <param name="setDataType">写入类型</param>
|
||||
/// <param name="values">需要写入的数据字典,当写入类型为Address时,键为需要写入的地址,当写入类型为CommunicationTag时,键为需要写入的单元的描述</param>
|
||||
/// <returns>是否写入成功</returns>
|
||||
ReturnStruct<bool> SetDatas(MachineDataType setDataType, Dictionary<string, double> values);
|
||||
|
||||
/// <summary>
|
||||
/// 写入数据
|
||||
/// </summary>
|
||||
|
||||
@@ -39,13 +39,6 @@ namespace Modbus.Net
|
||||
/// <returns></returns>
|
||||
bool Disconnect();
|
||||
|
||||
/// <summary>
|
||||
/// 发送协议内容并接收,一般方法
|
||||
/// </summary>
|
||||
/// <param name="content">写入的内容,使用对象数组描述</param>
|
||||
/// <returns>从设备获取的字节流</returns>
|
||||
TPipeUnit SendReceive(params object[] content);
|
||||
|
||||
/// <summary>
|
||||
/// 发送协议内容并接收,一般方法
|
||||
/// </summary>
|
||||
@@ -53,14 +46,6 @@ namespace Modbus.Net
|
||||
/// <returns>从设备获取的字节流</returns>
|
||||
Task<TPipeUnit> SendReceiveAsync(params object[] content);
|
||||
|
||||
/// <summary>
|
||||
/// 发送协议,通过传入需要使用的协议内容和输入结构
|
||||
/// </summary>
|
||||
/// <param name="unit">协议的实例</param>
|
||||
/// <param name="content">输入信息的结构化描述</param>
|
||||
/// <returns>输出信息的结构化描述</returns>
|
||||
TPipeUnit SendReceive(TProtocolUnit unit, IInputStruct content);
|
||||
|
||||
/// <summary>
|
||||
/// 发送协议,通过传入需要使用的协议内容和输入结构
|
||||
/// </summary>
|
||||
@@ -69,16 +54,6 @@ namespace Modbus.Net
|
||||
/// <returns>输出信息的结构化描述</returns>
|
||||
Task<TPipeUnit> SendReceiveAsync(TProtocolUnit unit, IInputStruct content);
|
||||
|
||||
/// <summary>
|
||||
/// 发送协议,通过传入需要使用的协议内容和输入结构
|
||||
/// </summary>
|
||||
/// <param name="unit">协议的实例</param>
|
||||
/// <param name="content">输入信息的结构化描述</param>
|
||||
/// <returns>输出信息的结构化描述</returns>
|
||||
/// <typeparam name="T">IOutputStruct的具体类型</typeparam>
|
||||
T SendReceive<T>(
|
||||
TProtocolUnit unit, IInputStruct content) where T : class, IOutputStruct;
|
||||
|
||||
/// <summary>
|
||||
/// 发送协议,通过传入需要使用的协议内容和输入结构
|
||||
/// </summary>
|
||||
|
||||
@@ -31,13 +31,6 @@ namespace Modbus.Net
|
||||
/// <returns>设备是否断开成功</returns>
|
||||
bool Disconnect();
|
||||
|
||||
/// <summary>
|
||||
/// 发送并接收数据
|
||||
/// </summary>
|
||||
/// <param name="content">发送协议的内容</param>
|
||||
/// <returns>接收协议的内容</returns>
|
||||
TParamOut SendReceive(TParamIn content);
|
||||
|
||||
/// <summary>
|
||||
/// 发送并接收数据
|
||||
/// </summary>
|
||||
@@ -45,13 +38,6 @@ namespace Modbus.Net
|
||||
/// <returns>接收协议的内容</returns>
|
||||
Task<TParamOut> SendReceiveAsync(TParamIn content);
|
||||
|
||||
/// <summary>
|
||||
/// 发送并接收数据,不进行协议扩展和收缩,用于特殊协议
|
||||
/// </summary>
|
||||
/// <param name="content">发送协议的内容</param>
|
||||
/// <returns>接收协议的内容</returns>
|
||||
TParamOut SendReceiveWithoutExtAndDec(TParamIn content);
|
||||
|
||||
/// <summary>
|
||||
/// 发送并接收数据,不进行协议扩展和收缩,用于特殊协议
|
||||
/// </summary>
|
||||
@@ -65,19 +51,5 @@ namespace Modbus.Net
|
||||
/// <param name="content">接收协议的内容</param>
|
||||
/// <returns>协议是否是正确的</returns>
|
||||
bool? CheckRight(TParamOut content);
|
||||
|
||||
/// <summary>
|
||||
/// 协议内容扩展,发送时根据需要扩展
|
||||
/// </summary>
|
||||
/// <param name="content">扩展前的基本协议内容</param>
|
||||
/// <returns>扩展后的协议内容</returns>
|
||||
TParamIn BytesExtend(TParamIn content);
|
||||
|
||||
/// <summary>
|
||||
/// 协议内容缩减,接收时根据需要缩减
|
||||
/// </summary>
|
||||
/// <param name="content">缩减前的完整协议内容</param>
|
||||
/// <returns>缩减后的协议内容</returns>
|
||||
TParamOut BytesDecact(TParamOut content);
|
||||
}
|
||||
}
|
||||
@@ -16,14 +16,6 @@ namespace Modbus.Net
|
||||
/// </summary>
|
||||
public interface IUtilityMethodDatas : IUtilityMethod
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取数据
|
||||
/// </summary>
|
||||
/// <param name="startAddress">开始地址</param>
|
||||
/// <param name="getByteCount">获取字节数个数</param>
|
||||
/// <returns>接收到的byte数据</returns>
|
||||
ReturnStruct<byte[]> GetDatas(string startAddress, int getByteCount);
|
||||
|
||||
/// <summary>
|
||||
/// 获取数据
|
||||
/// </summary>
|
||||
@@ -32,14 +24,6 @@ namespace Modbus.Net
|
||||
/// <returns>接收到的byte数据</returns>
|
||||
Task<ReturnStruct<byte[]>> GetDatasAsync(string startAddress, int getByteCount);
|
||||
|
||||
/// <summary>
|
||||
/// 获取数据
|
||||
/// </summary>
|
||||
/// <param name="startAddress">开始地址</param>
|
||||
/// <param name="getTypeAndCount">获取类型和个数</param>
|
||||
/// <returns>接收到的对应的类型和数据</returns>
|
||||
ReturnStruct<object[]> GetDatas(string startAddress, KeyValuePair<Type, int> getTypeAndCount);
|
||||
|
||||
/// <summary>
|
||||
/// 获取数据
|
||||
/// </summary>
|
||||
@@ -48,15 +32,6 @@ namespace Modbus.Net
|
||||
/// <returns>接收到的对应的类型和数据</returns>
|
||||
Task<ReturnStruct<object[]>> GetDatasAsync(string startAddress, KeyValuePair<Type, int> getTypeAndCount);
|
||||
|
||||
/// <summary>
|
||||
/// 获取数据
|
||||
/// </summary>
|
||||
/// <typeparam name="T">需要接收的类型</typeparam>
|
||||
/// <param name="startAddress">开始地址</param>
|
||||
/// <param name="getByteCount">获取字节数个数</param>
|
||||
/// <returns>接收到的对应的类型和数据</returns>
|
||||
ReturnStruct<T[]> GetDatas<T>(string startAddress, int getByteCount);
|
||||
|
||||
/// <summary>
|
||||
/// 获取数据
|
||||
/// </summary>
|
||||
@@ -66,14 +41,6 @@ namespace Modbus.Net
|
||||
/// <returns>接收到的对应的类型和数据</returns>
|
||||
Task<ReturnStruct<T[]>> GetDatasAsync<T>(string startAddress, int getByteCount);
|
||||
|
||||
/// <summary>
|
||||
/// 获取数据
|
||||
/// </summary>
|
||||
/// <param name="startAddress">开始地址</param>
|
||||
/// <param name="getTypeAndCountList">获取类型和个数的队列</param>
|
||||
/// <returns>获取数据的对象数组,请强制转换成相应类型</returns>
|
||||
ReturnStruct<object[]> GetDatas(string startAddress, IEnumerable<KeyValuePair<Type, int>> getTypeAndCountList);
|
||||
|
||||
/// <summary>
|
||||
/// 获取数据
|
||||
/// </summary>
|
||||
@@ -81,14 +48,6 @@ namespace Modbus.Net
|
||||
/// <param name="getTypeAndCountList">获取类型和个数的队列</param>
|
||||
Task<ReturnStruct<object[]>> GetDatasAsync(string startAddress, IEnumerable<KeyValuePair<Type, int>> getTypeAndCountList);
|
||||
|
||||
/// <summary>
|
||||
/// 设置数据
|
||||
/// </summary>
|
||||
/// <param name="startAddress">开始地址</param>
|
||||
/// <param name="setContents">设置数据</param>
|
||||
/// <returns>是否设置成功</returns>
|
||||
ReturnStruct<bool> SetDatas(string startAddress, object[] setContents);
|
||||
|
||||
/// <summary>
|
||||
/// 设置数据
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
using Quartz;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Modbus.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// Repeated JobChaningJobListener
|
||||
/// </summary>
|
||||
public class JobChainingJobLIstenerWithDataMapRepeated : JobChainingJobListenerWithDataMap
|
||||
{
|
||||
/// <summary>
|
||||
/// Job repeat count, -1 means infinity, 0 means 1 time.
|
||||
/// </summary>
|
||||
protected int RepeatCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// JobChaningJobListener with DataMap passing from parent job to next job
|
||||
/// </summary>
|
||||
/// <param name="name">Job name</param>
|
||||
/// <param name="overwriteKeys">If key is overwritable, parent job will pass the value to next job event next job contains that key</param>
|
||||
/// <param name="repeatCount">Repeatation count for job chain</param>
|
||||
public JobChainingJobLIstenerWithDataMapRepeated(string name, ICollection<string> overwriteKeys, int repeatCount) : base(name, overwriteKeys)
|
||||
{
|
||||
RepeatCount = repeatCount;
|
||||
}
|
||||
|
||||
#nullable enable
|
||||
/// <inheritdoc />
|
||||
public override async Task JobWasExecuted(IJobExecutionContext context,
|
||||
JobExecutionException? jobException,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
await base.JobWasExecuted(context, jobException, cancellationToken);
|
||||
if (RepeatCount == 0) return;
|
||||
ChainLinks.TryGetValue(context.JobDetail.Key, out var sj);
|
||||
if (sj == null)
|
||||
{
|
||||
var chainRoot = context.JobDetail.Key;
|
||||
var chainParent = ChainLinks.FirstOrDefault(p => p.Value == context.JobDetail.Key).Key;
|
||||
while (chainParent != null)
|
||||
{
|
||||
chainRoot = chainParent;
|
||||
chainParent = ChainLinks.FirstOrDefault(p => p.Value == chainParent).Key;
|
||||
}
|
||||
if (RepeatCount > 0)
|
||||
{
|
||||
RepeatCount--;
|
||||
}
|
||||
var sjJobDetail = await context.Scheduler.GetJobDetail(chainRoot);
|
||||
await context.Scheduler.AddJob(sjJobDetail!, true, false);
|
||||
await context.Scheduler.TriggerJob(chainRoot, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
#nullable disable
|
||||
}
|
||||
}
|
||||
@@ -23,10 +23,13 @@ namespace Modbus.Net
|
||||
{
|
||||
Name = name;
|
||||
OverWriteKeys = overwriteKeys;
|
||||
chainLinks = new Dictionary<JobKey, JobKey>();
|
||||
ChainLinks = new Dictionary<JobKey, JobKey>();
|
||||
}
|
||||
|
||||
private readonly Dictionary<JobKey, JobKey> chainLinks;
|
||||
/// <summary>
|
||||
/// Job chain links
|
||||
/// </summary>
|
||||
protected readonly Dictionary<JobKey, JobKey> ChainLinks;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name { get; }
|
||||
@@ -44,7 +47,7 @@ namespace Modbus.Net
|
||||
/// <param name="secondJob">a JobKey with the name and group of the follow-up job</param>
|
||||
public void AddJobChainLink(JobKey firstJob, JobKey secondJob)
|
||||
{
|
||||
chainLinks.Add(firstJob, secondJob);
|
||||
ChainLinks.Add(firstJob, secondJob);
|
||||
}
|
||||
|
||||
#nullable enable
|
||||
@@ -53,7 +56,7 @@ namespace Modbus.Net
|
||||
JobExecutionException? jobException,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
chainLinks.TryGetValue(context.JobDetail.Key, out var sj);
|
||||
ChainLinks.TryGetValue(context.JobDetail.Key, out var sj);
|
||||
|
||||
if (sj == null)
|
||||
{
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace Modbus.Net
|
||||
/// <returns></returns>
|
||||
public static async Task<MachineGetJobScheduler<TMachineMethod, TMachineKey, TReturnUnit>> CreateScheduler(string triggerKey, int count = 0, int intervalSecond = 1)
|
||||
{
|
||||
return await CreateScheduler(triggerKey, count, (double)intervalSecond);
|
||||
return await CreateSchedulerMillisecond(triggerKey, count, intervalSecond * 1000);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -48,29 +48,39 @@ namespace Modbus.Net
|
||||
/// <param name="intervalMilliSecond">间隔毫秒数</param>
|
||||
/// <returns></returns>
|
||||
public static async Task<MachineGetJobScheduler<TMachineMethod, TMachineKey, TReturnUnit>> CreateSchedulerMillisecond(string triggerKey, int count = 0, int intervalMilliSecond = 1000)
|
||||
{
|
||||
return await CreateScheduler(triggerKey, count, intervalMilliSecond / 1000.0);
|
||||
}
|
||||
|
||||
private static async Task<MachineGetJobScheduler<TMachineMethod, TMachineKey, TReturnUnit>> CreateScheduler(string triggerKey, int count = 0, double interval = 1)
|
||||
{
|
||||
IScheduler scheduler = await StdSchedulerFactory.GetDefaultScheduler();
|
||||
|
||||
ITrigger trigger;
|
||||
if (count >= 0)
|
||||
if (intervalMilliSecond <= 0)
|
||||
{
|
||||
trigger = TriggerBuilder.Create()
|
||||
.WithIdentity(triggerKey, "Modbus.Net.DataQuery.Group." + triggerKey)
|
||||
.StartNow()
|
||||
.WithSimpleSchedule(b => b.WithInterval(TimeSpan.FromSeconds(interval)).WithRepeatCount(count))
|
||||
.Build();
|
||||
}
|
||||
else if (count >= 0)
|
||||
trigger = TriggerBuilder.Create()
|
||||
.WithIdentity(triggerKey, "Modbus.Net.DataQuery.Group." + triggerKey)
|
||||
.StartNow()
|
||||
.WithSimpleSchedule(b => b.WithInterval(TimeSpan.FromMilliseconds(intervalMilliSecond)).WithRepeatCount(count))
|
||||
.Build();
|
||||
else
|
||||
trigger = TriggerBuilder.Create()
|
||||
.WithIdentity(triggerKey, "Modbus.Net.DataQuery.Group." + triggerKey)
|
||||
.StartNow()
|
||||
.WithSimpleSchedule(b => b.WithInterval(TimeSpan.FromSeconds(interval)).RepeatForever())
|
||||
.WithSimpleSchedule(b => b.WithInterval(TimeSpan.FromMilliseconds(intervalMilliSecond)).RepeatForever())
|
||||
.Build();
|
||||
|
||||
var listener = new JobChainingJobListenerWithDataMap("Modbus.Net.DataQuery.Chain." + triggerKey, new string[2] { "Value", "SetValue" });
|
||||
IJobListener listener;
|
||||
if (intervalMilliSecond <= 0)
|
||||
{
|
||||
listener = new JobChainingJobLIstenerWithDataMapRepeated("Modbus.Net.DataQuery.Chain." + triggerKey, new string[2] { "Value", "SetValue" }, count);
|
||||
}
|
||||
else
|
||||
{
|
||||
listener = new JobChainingJobListenerWithDataMap("Modbus.Net.DataQuery.Chain." + triggerKey, new string[2] { "Value", "SetValue" });
|
||||
}
|
||||
scheduler.ListenerManager.AddJobListener(listener, GroupMatcher<JobKey>.GroupEquals("Modbus.Net.DataQuery.Group." + triggerKey));
|
||||
|
||||
if (await scheduler.GetTrigger(new TriggerKey(triggerKey)) != null)
|
||||
@@ -344,9 +354,9 @@ namespace Modbus.Net
|
||||
/// 执行任务
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task Run()
|
||||
public Task Run()
|
||||
{
|
||||
await _scheduler.Start();
|
||||
return _scheduler.Start();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,10 @@ namespace Modbus.Net
|
||||
{
|
||||
Task.Factory.StartNew(async () =>
|
||||
{
|
||||
Thread.Sleep((int)(intervalSecond * 1000.0 / _machineCount * index));
|
||||
if (intervalSecond > 0)
|
||||
{
|
||||
Thread.Sleep((int)(intervalSecond * 1000.0 / _machineCount * index));
|
||||
}
|
||||
var getJobScheduler = await MachineJobSchedulerCreator<TMachineMethod, TMachineKey, TReturnUnit>.CreateScheduler("Trigger" + index, count, intervalSecond);
|
||||
await machineJobTemplate(machine, getJobScheduler);
|
||||
});
|
||||
|
||||
@@ -1,48 +1,8 @@
|
||||
using FastEnumUtility;
|
||||
using System;
|
||||
using System.IO.Ports;
|
||||
|
||||
namespace Modbus.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// 波特率
|
||||
/// </summary>
|
||||
public enum BaudRate
|
||||
{
|
||||
#pragma warning disable
|
||||
BaudRate75 = 75,
|
||||
BaudRate110 = 110,
|
||||
BaudRate134 = 134,
|
||||
BaudRate150 = 150,
|
||||
BaudRate300 = 300,
|
||||
BaudRate600 = 600,
|
||||
BaudRate1200 = 1200,
|
||||
BaudRate1800 = 1800,
|
||||
BaudRate2400 = 2400,
|
||||
BaudRate4800 = 4800,
|
||||
BaudRate9600 = 9600,
|
||||
BaudRate14400 = 14400,
|
||||
BaudRate19200 = 19200,
|
||||
BaudRate38400 = 38400,
|
||||
BaudRate57600 = 57600,
|
||||
BaudRate115200 = 115200,
|
||||
BaudRate128000 = 128000,
|
||||
BaudRate230400 = 230400,
|
||||
BaudRate460800 = 460800,
|
||||
BaudRate921600 = 921600,
|
||||
#pragma warning restore
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 数据位
|
||||
/// </summary>
|
||||
public enum DataBits
|
||||
{
|
||||
#pragma warning disable
|
||||
Seven = 7,
|
||||
Eight = 8,
|
||||
#pragma warning restore
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 串口连接对象
|
||||
/// </summary>
|
||||
@@ -63,11 +23,11 @@ namespace Modbus.Net
|
||||
protected ComProtocolLinker(string com, int slaveAddress, BaudRate? baudRate = null, Parity? parity = null, StopBits? stopBits = null, DataBits? dataBits = null, Handshake? handshake = null,
|
||||
int? connectionTimeout = null, bool? isFullDuplex = null)
|
||||
{
|
||||
baudRate = FastEnum.Parse<BaudRate>(baudRate != null ? baudRate.ToString() : null ?? ConfigurationReader.GetValue("COM:" + com, "BaudRate"));
|
||||
parity = FastEnum.Parse<Parity>(parity != null ? parity.ToString() : null ?? ConfigurationReader.GetValue("COM:" + com, "Parity"));
|
||||
stopBits = FastEnum.Parse<StopBits>(stopBits != null ? stopBits.ToString() : null ?? ConfigurationReader.GetValue("COM:" + com, "StopBits"));
|
||||
dataBits = FastEnum.Parse<DataBits>(dataBits != null ? dataBits.ToString() : null ?? ConfigurationReader.GetValue("COM:" + com, "DataBits"));
|
||||
handshake = FastEnum.Parse<Handshake>(handshake != null ? handshake.ToString() : null ?? ConfigurationReader.GetValue("COM:" + com, "Handshake"));
|
||||
baudRate = Enum.Parse<BaudRate>(baudRate != null ? baudRate.ToString() : null ?? ConfigurationReader.GetValue("COM:" + com, "BaudRate"));
|
||||
parity = Enum.Parse<Parity>(parity != null ? parity.ToString() : null ?? ConfigurationReader.GetValue("COM:" + com, "Parity"));
|
||||
stopBits = Enum.Parse<StopBits>(stopBits != null ? stopBits.ToString() : null ?? ConfigurationReader.GetValue("COM:" + com, "StopBits"));
|
||||
dataBits = Enum.Parse<DataBits>(dataBits != null ? dataBits.ToString() : null ?? ConfigurationReader.GetValue("COM:" + com, "DataBits"));
|
||||
handshake = Enum.Parse<Handshake>(handshake != null ? handshake.ToString() : null ?? ConfigurationReader.GetValue("COM:" + com, "Handshake"));
|
||||
connectionTimeout = int.Parse(connectionTimeout != null ? connectionTimeout.ToString() : null ?? ConfigurationReader.GetValue("COM:" + com, "ConnectionTimeout"));
|
||||
isFullDuplex = bool.Parse(isFullDuplex != null ? isFullDuplex.ToString() : null ?? ConfigurationReader.GetValue("COM:" + com, "FullDuplex"));
|
||||
BaseConnector = new ComConnector(com + ":" + slaveAddress, baudRate.Value, parity.Value, stopBits.Value, dataBits.Value, handshake.Value, connectionTimeout.Value, isFullDuplex.Value);
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace Modbus.Net
|
||||
/// </summary>
|
||||
/// <param name="content">扩展前的基本协议内容</param>
|
||||
/// <returns>扩展后的协议内容</returns>
|
||||
public override byte[] BytesExtend(byte[] content)
|
||||
public virtual byte[] BytesExtend(byte[] content)
|
||||
{
|
||||
//自动查找相应的协议放缩类,命令规则为——当前的实际类名(注意是继承后的)+"BytesExtend"。
|
||||
var bytesExtend =
|
||||
@@ -56,7 +56,7 @@ namespace Modbus.Net
|
||||
/// </summary>
|
||||
/// <param name="content">缩减前的完整协议内容</param>
|
||||
/// <returns>缩减后的协议内容</returns>
|
||||
public override byte[] BytesDecact(byte[] content)
|
||||
public virtual byte[] BytesDecact(byte[] content)
|
||||
{
|
||||
//自动查找相应的协议放缩类,命令规则为——当前的实际类名(注意是继承后的)+"BytesExtend"。
|
||||
var bytesExtend =
|
||||
@@ -122,16 +122,6 @@ namespace Modbus.Net
|
||||
/// </summary>
|
||||
public bool IsConnected => BaseConnector != null && BaseConnector.IsConnected;
|
||||
|
||||
/// <summary>
|
||||
/// 发送并接收数据
|
||||
/// </summary>
|
||||
/// <param name="content">发送协议的内容</param>
|
||||
/// <returns>接收协议的内容</returns>
|
||||
public virtual TParamOut SendReceive(TParamIn content)
|
||||
{
|
||||
return AsyncHelper.RunSync(() => SendReceiveAsync(content));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送并接收数据
|
||||
/// </summary>
|
||||
@@ -139,19 +129,8 @@ namespace Modbus.Net
|
||||
/// <returns>接收协议的内容</returns>
|
||||
public virtual async Task<TParamOut> SendReceiveAsync(TParamIn content)
|
||||
{
|
||||
var extBytes = BytesExtend(content);
|
||||
var receiveBytes = await SendReceiveWithoutExtAndDecAsync(extBytes);
|
||||
return BytesDecact(receiveBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送并接收数据,不进行协议扩展和收缩,用于特殊协议
|
||||
/// </summary>
|
||||
/// <param name="content">发送协议的内容</param>
|
||||
/// <returns>接收协议的内容</returns>
|
||||
public virtual TParamOut SendReceiveWithoutExtAndDec(TParamIn content)
|
||||
{
|
||||
return AsyncHelper.RunSync(() => SendReceiveWithoutExtAndDecAsync(content));
|
||||
var receiveBytes = await SendReceiveWithoutExtAndDecAsync(content);
|
||||
return receiveBytes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -180,25 +159,5 @@ namespace Modbus.Net
|
||||
Disconnect();
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 协议内容扩展,发送时根据需要扩展
|
||||
/// </summary>
|
||||
/// <param name="content">扩展前的基本协议内容</param>
|
||||
/// <returns>扩展后的协议内容</returns>
|
||||
public virtual TParamIn BytesExtend(TParamIn content)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 协议内容缩减,接收时根据需要缩减
|
||||
/// </summary>
|
||||
/// <param name="content">缩减前的完整协议内容</param>
|
||||
/// <returns>缩减后的协议内容</returns>
|
||||
public virtual TParamOut BytesDecact(TParamOut content)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,20 +7,20 @@ namespace Modbus.Net
|
||||
/// <summary>
|
||||
/// 地址组合器,组合后的每一组地址将只需一次向设备进行通讯
|
||||
/// </summary>
|
||||
public abstract class AddressCombiner<TKey> where TKey : IEquatable<TKey>
|
||||
public abstract class AddressCombiner<TKey, TAddressKey, TSubAddressKey> where TKey : IEquatable<TKey> where TAddressKey : IEquatable<TAddressKey> where TSubAddressKey : IEquatable<TSubAddressKey>
|
||||
{
|
||||
/// <summary>
|
||||
/// 组合地址
|
||||
/// </summary>
|
||||
/// <param name="addresses">需要进行组合的地址</param>
|
||||
/// <returns>组合完成后与设备通讯的地址</returns>
|
||||
public abstract IEnumerable<CommunicationUnit<TKey>> Combine(IEnumerable<AddressUnit<TKey>> addresses);
|
||||
public abstract IEnumerable<CommunicationUnit<TKey, TAddressKey, TSubAddressKey>> Combine(IEnumerable<AddressUnit<TKey, TAddressKey, TSubAddressKey>> addresses);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 连续的地址将组合成一组,向设备进行通讯
|
||||
/// </summary>
|
||||
public class AddressCombinerContinus<TKey> : AddressCombiner<TKey> where TKey : IEquatable<TKey>
|
||||
public class AddressCombinerContinus<TKey> : AddressCombiner<TKey, int, int> where TKey : IEquatable<TKey>
|
||||
{
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
@@ -48,7 +48,7 @@ namespace Modbus.Net
|
||||
/// </summary>
|
||||
/// <param name="addresses">需要组合的地址</param>
|
||||
/// <returns>组合后的地址</returns>
|
||||
public override IEnumerable<CommunicationUnit<TKey>> Combine(IEnumerable<AddressUnit<TKey>> addresses)
|
||||
public override IEnumerable<CommunicationUnit<TKey, int, int>> Combine(IEnumerable<AddressUnit<TKey, int, int>> addresses)
|
||||
{
|
||||
//按从小到大的顺序对地址进行排序
|
||||
var groupedAddresses = from address in addresses
|
||||
@@ -58,7 +58,7 @@ namespace Modbus.Net
|
||||
group address by address.Area
|
||||
into grouped
|
||||
select grouped;
|
||||
var ans = new List<CommunicationUnit<TKey>>();
|
||||
var ans = new List<CommunicationUnit<TKey, int, int>>();
|
||||
foreach (var groupedAddress in groupedAddresses)
|
||||
{
|
||||
var area = groupedAddress.Key;
|
||||
@@ -69,7 +69,7 @@ namespace Modbus.Net
|
||||
//上一个地址类型
|
||||
Type preType = null;
|
||||
//记录一个地址组合当中的所有原始地址
|
||||
var originalAddresses = new List<AddressUnit<TKey>>();
|
||||
var originalAddresses = new List<AddressUnit<TKey, int, int>>();
|
||||
//对组合内地址从小到大进行排序
|
||||
var orderedAddresses =
|
||||
groupedAddress.OrderBy(
|
||||
@@ -114,7 +114,7 @@ namespace Modbus.Net
|
||||
AddressTranslator.GetAreaByteLength(address.Area)))
|
||||
{
|
||||
//上一个地址域压入返回结果,并把当前记录的结果清空。
|
||||
ans.Add(new CommunicationUnit<TKey>
|
||||
ans.Add(new CommunicationUnit<TKey, int, int>
|
||||
{
|
||||
Area = area,
|
||||
Address = (int)Math.Floor(initNum),
|
||||
@@ -124,7 +124,7 @@ namespace Modbus.Net
|
||||
AddressHelper.MapProtocolGetCountToAbstractByteCount(
|
||||
preNum - (int)Math.Floor(initNum),
|
||||
AddressTranslator.GetAreaByteLength(address.Area),
|
||||
BigEndianValueHelper.Instance.ByteLength[preType.FullName])),
|
||||
ValueHelper.ByteLength[preType.FullName])),
|
||||
DataType = typeof(byte),
|
||||
OriginalAddresses = originalAddresses.ToList()
|
||||
});
|
||||
@@ -144,7 +144,7 @@ namespace Modbus.Net
|
||||
preType = address.DataType;
|
||||
}
|
||||
//最后一个地址域压入返回结果
|
||||
ans.Add(new CommunicationUnit<TKey>
|
||||
ans.Add(new CommunicationUnit<TKey, int, int>
|
||||
{
|
||||
Area = area,
|
||||
Address = (int)Math.Floor(initNum),
|
||||
@@ -153,7 +153,7 @@ namespace Modbus.Net
|
||||
Math.Ceiling(
|
||||
AddressHelper.MapProtocolGetCountToAbstractByteCount(
|
||||
preNum - (int)Math.Floor(initNum), AddressTranslator.GetAreaByteLength(area),
|
||||
BigEndianValueHelper.Instance.ByteLength[preType.FullName])),
|
||||
ValueHelper.ByteLength[preType.FullName])),
|
||||
DataType = typeof(byte),
|
||||
OriginalAddresses = originalAddresses.ToList()
|
||||
});
|
||||
@@ -167,30 +167,30 @@ namespace Modbus.Net
|
||||
/// </summary>
|
||||
/// <param name="ans">拆分前的连续地址池</param>
|
||||
/// <returns>拆分后的连续地址池</returns>
|
||||
protected List<CommunicationUnit<TKey>> MaxExclude(List<CommunicationUnit<TKey>> ans)
|
||||
protected List<CommunicationUnit<TKey, int, int>> MaxExclude(List<CommunicationUnit<TKey, int, int>> ans)
|
||||
{
|
||||
var newAns = new List<CommunicationUnit<TKey>>();
|
||||
var newAns = new List<CommunicationUnit<TKey, int, int>>();
|
||||
foreach (var communicationUnit in ans)
|
||||
{
|
||||
var oldByteCount = communicationUnit.GetCount *
|
||||
BigEndianValueHelper.Instance.ByteLength[communicationUnit.DataType.FullName];
|
||||
ValueHelper.ByteLength[communicationUnit.DataType.FullName];
|
||||
var oldOriginalAddresses = communicationUnit.OriginalAddresses.ToList();
|
||||
while (oldByteCount * BigEndianValueHelper.Instance.ByteLength[communicationUnit.DataType.FullName] >
|
||||
while (oldByteCount * ValueHelper.ByteLength[communicationUnit.DataType.FullName] >
|
||||
MaxLength)
|
||||
{
|
||||
var newOriginalAddresses = new List<AddressUnit<TKey>>();
|
||||
var newOriginalAddresses = new List<AddressUnit<TKey, int, int>>();
|
||||
var newByteCount = 0.0;
|
||||
var newAddressUnitStart = oldOriginalAddresses.First();
|
||||
do
|
||||
{
|
||||
var currentAddressUnit = oldOriginalAddresses.First();
|
||||
newByteCount += BigEndianValueHelper.Instance.ByteLength[currentAddressUnit.DataType.FullName];
|
||||
newByteCount += ValueHelper.ByteLength[currentAddressUnit.DataType.FullName];
|
||||
if (newByteCount > MaxLength) break;
|
||||
oldByteCount -= BigEndianValueHelper.Instance.ByteLength[currentAddressUnit.DataType.FullName];
|
||||
oldByteCount -= ValueHelper.ByteLength[currentAddressUnit.DataType.FullName];
|
||||
newOriginalAddresses.Add(currentAddressUnit);
|
||||
oldOriginalAddresses.RemoveAt(0);
|
||||
} while (newByteCount < MaxLength);
|
||||
var newCommunicationUnit = new CommunicationUnit<TKey>
|
||||
var newCommunicationUnit = new CommunicationUnit<TKey, int, int>
|
||||
{
|
||||
Area = newAddressUnitStart.Area,
|
||||
Address = newAddressUnitStart.Address,
|
||||
@@ -199,7 +199,7 @@ namespace Modbus.Net
|
||||
GetCount =
|
||||
(int)
|
||||
Math.Ceiling(newByteCount /
|
||||
BigEndianValueHelper.Instance.ByteLength[communicationUnit.DataType.FullName]),
|
||||
ValueHelper.ByteLength[communicationUnit.DataType.FullName]),
|
||||
OriginalAddresses = newOriginalAddresses
|
||||
};
|
||||
|
||||
@@ -214,7 +214,7 @@ namespace Modbus.Net
|
||||
communicationUnit.GetCount =
|
||||
(int)
|
||||
Math.Ceiling(oldByteCount /
|
||||
BigEndianValueHelper.Instance.ByteLength[communicationUnit.DataType.FullName]);
|
||||
ValueHelper.ByteLength[communicationUnit.DataType.FullName]);
|
||||
communicationUnit.OriginalAddresses = oldOriginalAddresses;
|
||||
newAns.Add(communicationUnit);
|
||||
}
|
||||
@@ -225,26 +225,26 @@ namespace Modbus.Net
|
||||
/// <summary>
|
||||
/// 单个地址变为一组,每一个地址都进行一次查询
|
||||
/// </summary>
|
||||
public class AddressCombinerSingle<TKey> : AddressCombiner<TKey> where TKey : IEquatable<TKey>
|
||||
public class AddressCombinerSingle<TKey, TAddressKey, TSubAddressKey> : AddressCombiner<TKey, TAddressKey, TSubAddressKey> where TKey : IEquatable<TKey> where TAddressKey : IEquatable<TAddressKey> where TSubAddressKey : IEquatable<TSubAddressKey>
|
||||
{
|
||||
/// <summary>
|
||||
/// 组合地址
|
||||
/// </summary>
|
||||
/// <param name="addresses">需要组合的地址</param>
|
||||
/// <returns>组合后的地址</returns>
|
||||
public override IEnumerable<CommunicationUnit<TKey>> Combine(IEnumerable<AddressUnit<TKey>> addresses)
|
||||
public override IEnumerable<CommunicationUnit<TKey, TAddressKey, TSubAddressKey>> Combine(IEnumerable<AddressUnit<TKey, TAddressKey, TSubAddressKey>> addresses)
|
||||
{
|
||||
return
|
||||
addresses.Select(
|
||||
address =>
|
||||
new CommunicationUnit<TKey>
|
||||
new CommunicationUnit<TKey, TAddressKey, TSubAddressKey>
|
||||
{
|
||||
Area = address.Area,
|
||||
Address = address.Address,
|
||||
SubAddress = address.SubAddress,
|
||||
DataType = address.DataType,
|
||||
GetCount = 1,
|
||||
OriginalAddresses = new List<AddressUnit<TKey>> { address }
|
||||
OriginalAddresses = new List<AddressUnit<TKey, TAddressKey, TSubAddressKey>> { address }
|
||||
}).ToList();
|
||||
}
|
||||
}
|
||||
@@ -254,7 +254,7 @@ namespace Modbus.Net
|
||||
/// </summary>
|
||||
internal class CommunicationUnitGap<TKey> where TKey : IEquatable<TKey>
|
||||
{
|
||||
public CommunicationUnit<TKey> EndUnit { get; set; }
|
||||
public CommunicationUnit<TKey, int, int> EndUnit { get; set; }
|
||||
public int GapNumber { get; set; }
|
||||
}
|
||||
|
||||
@@ -285,11 +285,11 @@ namespace Modbus.Net
|
||||
/// </summary>
|
||||
/// <param name="addresses">需要组合的地址</param>
|
||||
/// <returns>组合后的地址</returns>
|
||||
public override IEnumerable<CommunicationUnit<TKey>> Combine(IEnumerable<AddressUnit<TKey>> addresses)
|
||||
public override IEnumerable<CommunicationUnit<TKey, int, int>> Combine(IEnumerable<AddressUnit<TKey, int, int>> addresses)
|
||||
{
|
||||
var continusAddresses = base.Combine(addresses).ToList();
|
||||
var addressesGaps = new List<CommunicationUnitGap<TKey>>();
|
||||
CommunicationUnit<TKey> preCommunicationUnit = null;
|
||||
CommunicationUnit<TKey, int, int> preCommunicationUnit = null;
|
||||
foreach (var continusAddress in continusAddresses)
|
||||
{
|
||||
if (preCommunicationUnit == null)
|
||||
@@ -309,7 +309,7 @@ namespace Modbus.Net
|
||||
continusAddress.Address, preCommunicationUnit.Address,
|
||||
AddressTranslator.GetAreaByteLength(continusAddress.Area)) -
|
||||
preCommunicationUnit.GetCount *
|
||||
BigEndianValueHelper.Instance.ByteLength[
|
||||
ValueHelper.ByteLength[
|
||||
preCommunicationUnit.DataType.FullName])
|
||||
};
|
||||
addressesGaps.Add(gap);
|
||||
@@ -327,24 +327,24 @@ namespace Modbus.Net
|
||||
nowAddress = continusAddresses[index];
|
||||
index--;
|
||||
var preAddress = continusAddresses[index];
|
||||
if (nowAddress.GetCount * BigEndianValueHelper.Instance.ByteLength[nowAddress.DataType.FullName] +
|
||||
preAddress.GetCount * BigEndianValueHelper.Instance.ByteLength[preAddress.DataType.FullName] +
|
||||
if (nowAddress.GetCount * ValueHelper.ByteLength[nowAddress.DataType.FullName] +
|
||||
preAddress.GetCount * ValueHelper.ByteLength[preAddress.DataType.FullName] +
|
||||
orderedGap.GapNumber > MaxLength) continue;
|
||||
jumpNumberInner -= orderedGap.GapNumber;
|
||||
if (jumpNumberInner < 0) break;
|
||||
continusAddresses.RemoveAt(index);
|
||||
continusAddresses.RemoveAt(index);
|
||||
//合并两个已有的地址段,变为一个新的地址段
|
||||
var newAddress = new CommunicationUnit<TKey>
|
||||
var newAddress = new CommunicationUnit<TKey, int, int>
|
||||
{
|
||||
Area = nowAddress.Area,
|
||||
Address = preAddress.Address,
|
||||
GetCount =
|
||||
(int)
|
||||
(preAddress.GetCount * BigEndianValueHelper.Instance.ByteLength[preAddress.DataType.FullName]) +
|
||||
(preAddress.GetCount * ValueHelper.ByteLength[preAddress.DataType.FullName]) +
|
||||
orderedGap.GapNumber +
|
||||
(int)
|
||||
(nowAddress.GetCount * BigEndianValueHelper.Instance.ByteLength[nowAddress.DataType.FullName]),
|
||||
(nowAddress.GetCount * ValueHelper.ByteLength[nowAddress.DataType.FullName]),
|
||||
DataType = typeof(byte),
|
||||
OriginalAddresses = preAddress.OriginalAddresses.ToList().Union(nowAddress.OriginalAddresses)
|
||||
};
|
||||
@@ -382,10 +382,10 @@ namespace Modbus.Net
|
||||
/// </summary>
|
||||
/// <param name="addresses">需要组合的地址</param>
|
||||
/// <returns>组合后的地址</returns>
|
||||
public override IEnumerable<CommunicationUnit<TKey>> Combine(IEnumerable<AddressUnit<TKey>> addresses)
|
||||
public override IEnumerable<CommunicationUnit<TKey, int, int>> Combine(IEnumerable<AddressUnit<TKey, int, int>> addresses)
|
||||
{
|
||||
var addressUnits = addresses as IList<AddressUnit<TKey>> ?? addresses.ToList();
|
||||
var count = addressUnits.Sum(address => BigEndianValueHelper.Instance.ByteLength[address.DataType.FullName]);
|
||||
var addressUnits = addresses as IList<AddressUnit<TKey, int, int>> ?? addresses.ToList();
|
||||
var count = addressUnits.Sum(address => ValueHelper.ByteLength[address.DataType.FullName]);
|
||||
return
|
||||
new AddressCombinerNumericJump<TKey>((int)(count * Percentage / 100.0), MaxLength, AddressTranslator)
|
||||
.Combine(
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
namespace Modbus.Net
|
||||
using System;
|
||||
|
||||
namespace Modbus.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// 地址编码器
|
||||
/// </summary>
|
||||
public abstract class AddressFormater
|
||||
public abstract class AddressFormater<TAddressKey, TSubAddressKey> where TAddressKey : IEquatable<TAddressKey> where TSubAddressKey : IEquatable<TSubAddressKey>
|
||||
{
|
||||
/// <summary>
|
||||
/// 编码地址
|
||||
@@ -11,7 +13,7 @@
|
||||
/// <param name="area">地址所在的数据区域</param>
|
||||
/// <param name="address">地址</param>
|
||||
/// <returns>编码后的地址</returns>
|
||||
public abstract string FormatAddress(string area, int address);
|
||||
public abstract string FormatAddress(string area, TAddressKey address);
|
||||
|
||||
/// <summary>
|
||||
/// 编码地址
|
||||
@@ -20,13 +22,13 @@
|
||||
/// <param name="address">地址</param>
|
||||
/// <param name="subAddress">子地址</param>
|
||||
/// <returns>编码后的地址</returns>
|
||||
public abstract string FormatAddress(string area, int address, int subAddress);
|
||||
public abstract string FormatAddress(string area, TAddressKey address, TSubAddressKey subAddress);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 基本的地址编码器
|
||||
/// </summary>
|
||||
public class AddressFormaterBase : AddressFormater
|
||||
public class AddressFormaterBase : AddressFormater<int, int>
|
||||
{
|
||||
/// <summary>
|
||||
/// 编码地址
|
||||
|
||||
@@ -80,7 +80,7 @@ namespace Modbus.Net
|
||||
double byteLength)
|
||||
{
|
||||
return protocolAddress +
|
||||
BigEndianValueHelper.Instance.ByteLength[nextPositionBetweenType.FullName] / byteLength;
|
||||
ValueHelper.ByteLength[nextPositionBetweenType.FullName] / byteLength;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -92,7 +92,7 @@ namespace Modbus.Net
|
||||
public static double GetAbstractCoordinateNextPosition(double abstractAddress, Type nextPositionBetweenType)
|
||||
{
|
||||
return abstractAddress +
|
||||
BigEndianValueHelper.Instance.ByteLength[nextPositionBetweenType.FullName];
|
||||
ValueHelper.ByteLength[nextPositionBetweenType.FullName];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,68 +6,17 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Modbus.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// 读写设备值的方式
|
||||
/// </summary>
|
||||
public enum MachineDataType
|
||||
{
|
||||
/// <summary>
|
||||
/// 地址
|
||||
/// </summary>
|
||||
Address,
|
||||
|
||||
/// <summary>
|
||||
/// 通讯标识
|
||||
/// </summary>
|
||||
CommunicationTag,
|
||||
|
||||
/// <summary>
|
||||
/// 名称
|
||||
/// </summary>
|
||||
Name,
|
||||
|
||||
/// <summary>
|
||||
/// Id
|
||||
/// </summary>
|
||||
Id
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设备
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">设备的Id类型</typeparam>
|
||||
/// <typeparam name="TUnitKey">设备中使用的AddressUnit的Id类型</typeparam>
|
||||
public abstract class BaseMachine<TKey, TUnitKey> : IMachine<TKey>
|
||||
public abstract class BaseMachine<TKey, TUnitKey> : BaseMachine<TKey, TUnitKey, int, int>
|
||||
where TKey : IEquatable<TKey>
|
||||
where TUnitKey : IEquatable<TUnitKey>
|
||||
{
|
||||
private static readonly ILogger<BaseMachine<TKey, TUnitKey>> logger = LogProvider.CreateLogger<BaseMachine<TKey, TUnitKey>>();
|
||||
|
||||
private readonly int _maxErrorCount = 3;
|
||||
|
||||
/// <summary>
|
||||
/// 构造器
|
||||
/// </summary>
|
||||
/// <param name="id">设备的ID号</param>
|
||||
/// <param name="getAddresses">需要与设备通讯的地址</param>
|
||||
protected BaseMachine(TKey id, IEnumerable<AddressUnit<TUnitKey>> getAddresses)
|
||||
: this(id, getAddresses, false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构造器
|
||||
/// </summary>
|
||||
/// <param name="id">设备的ID号</param>
|
||||
/// <param name="getAddresses">需要与设备通讯的地址</param>
|
||||
/// <param name="keepConnect">是否保持连接</param>
|
||||
protected BaseMachine(TKey id, IEnumerable<AddressUnit<TUnitKey>> getAddresses, bool keepConnect)
|
||||
{
|
||||
Id = id;
|
||||
GetAddresses = getAddresses;
|
||||
KeepConnect = keepConnect;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构造器
|
||||
/// </summary>
|
||||
@@ -76,70 +25,18 @@ namespace Modbus.Net
|
||||
/// <param name="keepConnect">是否保持连接</param>
|
||||
/// <param name="slaveAddress">从站地址</param>
|
||||
/// <param name="masterAddress">主站地址</param>
|
||||
protected BaseMachine(TKey id, IEnumerable<AddressUnit<TUnitKey>> getAddresses, bool keepConnect, byte slaveAddress,
|
||||
byte masterAddress) : this(id, getAddresses, keepConnect)
|
||||
protected BaseMachine(TKey id, IEnumerable<AddressUnit<TUnitKey, int, int>> getAddresses, bool keepConnect, byte slaveAddress,
|
||||
byte masterAddress) : base(id, getAddresses, keepConnect)
|
||||
{
|
||||
SlaveAddress = slaveAddress;
|
||||
MasterAddress = masterAddress;
|
||||
}
|
||||
|
||||
|
||||
private readonly int _maxErrorCount = 3;
|
||||
|
||||
private int ErrorCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 地址编码器
|
||||
/// </summary>
|
||||
public AddressFormater AddressFormater { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取地址组合器
|
||||
/// </summary>
|
||||
public AddressCombiner<TUnitKey> AddressCombiner { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 写入地址组合器
|
||||
/// </summary>
|
||||
public AddressCombiner<TUnitKey> AddressCombinerSet { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 地址转换器
|
||||
/// </summary>
|
||||
public AddressTranslator AddressTranslator
|
||||
{
|
||||
get => BaseUtility.AddressTranslator;
|
||||
set => BaseUtility.AddressTranslator = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 与设备实际通讯的连续地址
|
||||
/// </summary>
|
||||
protected IEnumerable<CommunicationUnit<TUnitKey>> CommunicateAddresses
|
||||
=> GetAddresses != null ? AddressCombiner.Combine(GetAddresses) : null;
|
||||
|
||||
/// <summary>
|
||||
/// 描述需要与设备通讯的地址
|
||||
/// </summary>
|
||||
private IEnumerable<AddressUnit<TUnitKey>> getAddresses;
|
||||
|
||||
private object getAddressesLock = new object();
|
||||
|
||||
/// <summary>
|
||||
/// 描述需要与设备通讯的地址
|
||||
/// </summary>
|
||||
public IEnumerable<AddressUnit<TUnitKey>> GetAddresses
|
||||
{
|
||||
get
|
||||
{
|
||||
return getAddresses;
|
||||
}
|
||||
set
|
||||
{
|
||||
lock (getAddressesLock)
|
||||
{
|
||||
getAddresses = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从站号
|
||||
/// </summary>
|
||||
@@ -151,20 +48,26 @@ namespace Modbus.Net
|
||||
public byte MasterAddress { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 读取数据
|
||||
/// 与设备实际通讯的连续地址
|
||||
/// </summary>
|
||||
/// <returns>从设备读取的数据</returns>
|
||||
public ReturnStruct<Dictionary<string, ReturnUnit<double>>> GetDatas(MachineDataType getDataType)
|
||||
{
|
||||
return AsyncHelper.RunSync(() => GetDatasAsync(getDataType));
|
||||
}
|
||||
protected IEnumerable<CommunicationUnit<TUnitKey, int, int>> CommunicateAddresses
|
||||
=> GetAddresses != null ? AddressCombiner.Combine(GetAddresses) : null;
|
||||
|
||||
/// <summary>
|
||||
/// 获取地址组合器
|
||||
/// </summary>
|
||||
public AddressCombiner<TUnitKey, int, int> AddressCombiner { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 写入地址组合器
|
||||
/// </summary>
|
||||
public AddressCombiner<TUnitKey, int, int> AddressCombinerSet { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 读取数据
|
||||
/// </summary>
|
||||
/// <returns>从设备读取的数据</returns>
|
||||
public async Task<ReturnStruct<Dictionary<string, ReturnUnit<double>>>> GetDatasAsync(MachineDataType getDataType)
|
||||
public async override Task<ReturnStruct<Dictionary<string, ReturnUnit<double>>>> GetDatasAsync(MachineDataType getDataType)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -192,7 +95,7 @@ namespace Modbus.Net
|
||||
communicateAddress.SubAddress),
|
||||
(int)
|
||||
Math.Ceiling(communicateAddress.GetCount *
|
||||
BigEndianValueHelper.Instance.ByteLength[
|
||||
ValueHelper.ByteLength[
|
||||
communicateAddress.DataType.FullName]));
|
||||
|
||||
|
||||
@@ -210,7 +113,7 @@ namespace Modbus.Net
|
||||
else if (datas.Datas.Length != 0 && datas.Datas.Length <
|
||||
(int)
|
||||
Math.Ceiling(communicateAddress.GetCount *
|
||||
BigEndianValueHelper.Instance.ByteLength[
|
||||
ValueHelper.ByteLength[
|
||||
communicateAddress.DataType.FullName]))
|
||||
{
|
||||
return new ReturnStruct<Dictionary<string, ReturnUnit<double>>>()
|
||||
@@ -342,18 +245,7 @@ namespace Modbus.Net
|
||||
/// <param name="setDataType">写入类型</param>
|
||||
/// <param name="values">需要写入的数据字典,当写入类型为Address时,键为需要写入的地址,当写入类型为CommunicationTag时,键为需要写入的单元的描述</param>
|
||||
/// <returns>是否写入成功</returns>
|
||||
public ReturnStruct<bool> SetDatas(MachineDataType setDataType, Dictionary<string, double> values)
|
||||
{
|
||||
return AsyncHelper.RunSync(() => SetDatasAsync(setDataType, values));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写入数据
|
||||
/// </summary>
|
||||
/// <param name="setDataType">写入类型</param>
|
||||
/// <param name="values">需要写入的数据字典,当写入类型为Address时,键为需要写入的地址,当写入类型为CommunicationTag时,键为需要写入的单元的描述</param>
|
||||
/// <returns>是否写入成功</returns>
|
||||
public async Task<ReturnStruct<bool>> SetDatasAsync(MachineDataType setDataType, Dictionary<string, double> values)
|
||||
public async override Task<ReturnStruct<bool>> SetDatasAsync(MachineDataType setDataType, Dictionary<string, double> values)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -368,12 +260,12 @@ namespace Modbus.Net
|
||||
ErrorCode = -1,
|
||||
ErrorMsg = "Connection Error"
|
||||
};
|
||||
var addresses = new List<AddressUnit<TUnitKey>>();
|
||||
var addresses = new List<AddressUnit<TUnitKey, int, int>>();
|
||||
//遍历每个要设置的值
|
||||
foreach (var value in values)
|
||||
{
|
||||
//根据设置类型找到对应的地址描述
|
||||
AddressUnit<TUnitKey> address = null;
|
||||
AddressUnit<TUnitKey, int, int> address = null;
|
||||
switch (setDataType)
|
||||
{
|
||||
case MachineDataType.Address:
|
||||
@@ -437,7 +329,7 @@ namespace Modbus.Net
|
||||
AddressFormater.FormatAddress(communicateAddress.Area, communicateAddress.Address, 0),
|
||||
(int)
|
||||
Math.Ceiling(communicateAddress.GetCount *
|
||||
BigEndianValueHelper.Instance.ByteLength[
|
||||
ValueHelper.ByteLength[
|
||||
communicateAddress.DataType.FullName]));
|
||||
|
||||
var valueHelper = ValueHelper.GetInstance(BaseUtility.Endian);
|
||||
@@ -458,7 +350,7 @@ namespace Modbus.Net
|
||||
else if (datas.Datas.Length <
|
||||
(int)
|
||||
Math.Ceiling(communicateAddress.GetCount *
|
||||
BigEndianValueHelper.Instance.ByteLength[
|
||||
ValueHelper.ByteLength[
|
||||
communicateAddress.DataType.FullName]))
|
||||
return new ReturnStruct<bool>()
|
||||
{
|
||||
@@ -576,6 +468,49 @@ namespace Modbus.Net
|
||||
ErrorMsg = ""
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设备
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">设备的Id类型</typeparam>
|
||||
/// <typeparam name="TUnitKey">设备中使用的AddressUnit的Id类型</typeparam>
|
||||
/// <typeparam name="TAddressKey">设备中使用的AddressUnit的Address类型</typeparam>
|
||||
/// <typeparam name="TSubAddressKey">设备中使用的AddressUnit的SubAddress类型</typeparam>
|
||||
public abstract class BaseMachine<TKey, TUnitKey, TAddressKey, TSubAddressKey> : IMachine<TKey>
|
||||
where TKey : IEquatable<TKey>
|
||||
where TUnitKey : IEquatable<TUnitKey>
|
||||
where TAddressKey : IEquatable<TAddressKey>
|
||||
where TSubAddressKey : IEquatable<TSubAddressKey>
|
||||
{
|
||||
private static readonly ILogger<BaseMachine<TKey, TUnitKey, TAddressKey, TSubAddressKey>> logger = LogProvider.CreateLogger<BaseMachine<TKey, TUnitKey, TAddressKey, TSubAddressKey>>();
|
||||
|
||||
/// <summary>
|
||||
/// 构造器
|
||||
/// </summary>
|
||||
/// <param name="id">设备的ID号</param>
|
||||
/// <param name="getAddresses">需要与设备通讯的地址</param>
|
||||
protected BaseMachine(TKey id, IEnumerable<AddressUnit<TUnitKey, TAddressKey, TSubAddressKey>> getAddresses)
|
||||
: this(id, getAddresses, false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构造器
|
||||
/// </summary>
|
||||
/// <param name="id">设备的ID号</param>
|
||||
/// <param name="getAddresses">需要与设备通讯的地址</param>
|
||||
/// <param name="keepConnect">是否保持连接</param>
|
||||
protected BaseMachine(TKey id, IEnumerable<AddressUnit<TUnitKey, TAddressKey, TSubAddressKey>> getAddresses, bool keepConnect)
|
||||
{
|
||||
Id = id;
|
||||
GetAddresses = getAddresses;
|
||||
KeepConnect = keepConnect;
|
||||
}
|
||||
|
||||
private readonly int _maxErrorCount = 3;
|
||||
|
||||
private int ErrorCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否处于连接状态
|
||||
@@ -612,6 +547,383 @@ namespace Modbus.Net
|
||||
/// </summary>
|
||||
public string ConnectionToken => BaseUtility.ConnectionToken;
|
||||
|
||||
/// <summary>
|
||||
/// 描述需要与设备通讯的地址
|
||||
/// </summary>
|
||||
private IEnumerable<AddressUnit<TUnitKey, TAddressKey, TSubAddressKey>> getAddresses;
|
||||
|
||||
private object getAddressesLock = new object();
|
||||
|
||||
/// <summary>
|
||||
/// 描述需要与设备通讯的地址
|
||||
/// </summary>
|
||||
public IEnumerable<AddressUnit<TUnitKey, TAddressKey, TSubAddressKey>> GetAddresses
|
||||
{
|
||||
get
|
||||
{
|
||||
return getAddresses;
|
||||
}
|
||||
set
|
||||
{
|
||||
lock (getAddressesLock)
|
||||
{
|
||||
getAddresses = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 地址编码器
|
||||
/// </summary>
|
||||
public AddressFormater<TAddressKey, TSubAddressKey> AddressFormater { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 地址转换器
|
||||
/// </summary>
|
||||
public AddressTranslator AddressTranslator
|
||||
{
|
||||
get => BaseUtility.AddressTranslator;
|
||||
set => BaseUtility.AddressTranslator = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取数据
|
||||
/// </summary>
|
||||
/// <returns>从设备读取的数据</returns>
|
||||
public async virtual Task<ReturnStruct<Dictionary<string, ReturnUnit<double>>>> GetDatasAsync(MachineDataType getDataType)
|
||||
{
|
||||
try
|
||||
{
|
||||
var ans = new Dictionary<string, ReturnUnit<double>>();
|
||||
//检测并连接设备
|
||||
if (!BaseUtility.IsConnected)
|
||||
await BaseUtility.ConnectAsync();
|
||||
//如果无法连接,终止
|
||||
if (!BaseUtility.IsConnected) return
|
||||
new ReturnStruct<Dictionary<string, ReturnUnit<double>>>()
|
||||
{
|
||||
Datas = null,
|
||||
IsSuccess = false,
|
||||
ErrorCode = -1,
|
||||
ErrorMsg = "Connection Error"
|
||||
};
|
||||
//遍历每一个实际向设备获取数据的连续地址
|
||||
foreach (var address in GetAddresses)
|
||||
{
|
||||
//获取数据
|
||||
var datas =
|
||||
await
|
||||
BaseUtility.GetUtilityMethods<IUtilityMethodDatas>().GetDatasAsync(
|
||||
AddressFormater.FormatAddress(address.Area, address.Address,
|
||||
address.SubAddress),
|
||||
(int)
|
||||
Math.Ceiling(ValueHelper.ByteLength[
|
||||
address.DataType.FullName]));
|
||||
|
||||
|
||||
//如果没有数据,终止
|
||||
if (datas.IsSuccess == false || datas.Datas == null)
|
||||
{
|
||||
return new ReturnStruct<Dictionary<string, ReturnUnit<double>>>()
|
||||
{
|
||||
Datas = null,
|
||||
IsSuccess = false,
|
||||
ErrorCode = datas.ErrorCode,
|
||||
ErrorMsg = datas.ErrorMsg
|
||||
};
|
||||
}
|
||||
else if (datas.Datas.Length != 0 && datas.Datas.Length <
|
||||
(int)
|
||||
Math.Ceiling(ValueHelper.ByteLength[
|
||||
address.DataType.FullName]))
|
||||
{
|
||||
return new ReturnStruct<Dictionary<string, ReturnUnit<double>>>()
|
||||
{
|
||||
Datas = null,
|
||||
IsSuccess = false,
|
||||
ErrorCode = -2,
|
||||
ErrorMsg = "Data length mismatch"
|
||||
};
|
||||
}
|
||||
|
||||
//字节坐标的主地址位置
|
||||
var localMainPos = 0;
|
||||
//字节坐标的子地址位置
|
||||
var localSubPos = 0;
|
||||
|
||||
//根据类型选择返回结果的键是通讯标识还是地址
|
||||
string key;
|
||||
switch (getDataType)
|
||||
{
|
||||
case MachineDataType.CommunicationTag:
|
||||
{
|
||||
key = address.CommunicationTag;
|
||||
break;
|
||||
}
|
||||
case MachineDataType.Address:
|
||||
{
|
||||
key = AddressFormater.FormatAddress(address.Area, address.Address, address.SubAddress);
|
||||
break;
|
||||
}
|
||||
case MachineDataType.Name:
|
||||
{
|
||||
key = address.Name;
|
||||
break;
|
||||
}
|
||||
case MachineDataType.Id:
|
||||
{
|
||||
key = address.Id.ToString();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
key = address.CommunicationTag;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
//如果没有数据返回空
|
||||
if (datas.Datas.Length == 0)
|
||||
ans.Add(key, new ReturnUnit<double>
|
||||
{
|
||||
DeviceValue = null,
|
||||
AddressUnit = address.MapAddressUnitTUnitKeyToAddressUnit(),
|
||||
});
|
||||
else
|
||||
ans.Add(key,
|
||||
new ReturnUnit<double>
|
||||
{
|
||||
DeviceValue =
|
||||
Math.Round(Convert.ToDouble(
|
||||
ValueHelper.GetInstance(BaseUtility.Endian)
|
||||
.GetValue(datas.Datas, ref localMainPos, ref localSubPos,
|
||||
address.DataType)) * address.Zoom, address.DecimalPos),
|
||||
AddressUnit = address.MapAddressUnitTUnitKeyToAddressUnit(),
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ErrorCount++;
|
||||
logger.LogError(e, $"BaseMachine -> GetDatas, Id:{Id} Connection:{ConnectionToken} key {key} existing. ErrorCount {ErrorCount}.");
|
||||
|
||||
if (ErrorCount >= _maxErrorCount)
|
||||
Disconnect();
|
||||
return new ReturnStruct<Dictionary<string, ReturnUnit<double>>>()
|
||||
{
|
||||
Datas = null,
|
||||
IsSuccess = false,
|
||||
ErrorCode = -3,
|
||||
ErrorMsg = "Data translation mismatch"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
//如果不保持连接,断开连接
|
||||
if (!KeepConnect)
|
||||
BaseUtility.Disconnect();
|
||||
//返回数据
|
||||
if (ans.All(p => p.Value.DeviceValue == null)) ans = null;
|
||||
ErrorCount = 0;
|
||||
return new ReturnStruct<Dictionary<string, ReturnUnit<double>>>
|
||||
{
|
||||
Datas = ans,
|
||||
IsSuccess = true,
|
||||
ErrorCode = 0,
|
||||
ErrorMsg = ""
|
||||
};
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ErrorCount++;
|
||||
logger.LogError(e, $"BaseMachine -> GetDatas, Id:{Id} Connection:{ConnectionToken} error. ErrorCount {ErrorCount}.");
|
||||
|
||||
if (ErrorCount >= _maxErrorCount)
|
||||
Disconnect();
|
||||
return new ReturnStruct<Dictionary<string, ReturnUnit<double>>>()
|
||||
{
|
||||
Datas = null,
|
||||
IsSuccess = false,
|
||||
ErrorCode = -100,
|
||||
ErrorMsg = "Unknown Exception"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写入数据
|
||||
/// </summary>
|
||||
/// <param name="setDataType">写入类型</param>
|
||||
/// <param name="values">需要写入的数据字典,当写入类型为Address时,键为需要写入的地址,当写入类型为CommunicationTag时,键为需要写入的单元的描述</param>
|
||||
/// <returns>是否写入成功</returns>
|
||||
public async virtual Task<ReturnStruct<bool>> SetDatasAsync(MachineDataType setDataType, Dictionary<string, double> values)
|
||||
{
|
||||
try
|
||||
{
|
||||
//检测并连接设备
|
||||
if (!BaseUtility.IsConnected)
|
||||
await BaseUtility.ConnectAsync();
|
||||
//如果设备无法连接,终止
|
||||
if (!BaseUtility.IsConnected) return new ReturnStruct<bool>()
|
||||
{
|
||||
Datas = false,
|
||||
IsSuccess = false,
|
||||
ErrorCode = -1,
|
||||
ErrorMsg = "Connection Error"
|
||||
};
|
||||
var addresses = new List<AddressUnit<TUnitKey, TAddressKey, TSubAddressKey>>();
|
||||
//遍历每个要设置的值
|
||||
foreach (var value in values)
|
||||
{
|
||||
//根据设置类型找到对应的地址描述
|
||||
AddressUnit<TUnitKey, TAddressKey, TSubAddressKey> address = null;
|
||||
switch (setDataType)
|
||||
{
|
||||
case MachineDataType.Address:
|
||||
{
|
||||
address =
|
||||
GetAddresses.SingleOrDefault(
|
||||
p =>
|
||||
AddressFormater.FormatAddress(p.Area, p.Address, p.SubAddress) == value.Key ||
|
||||
p.DataType != typeof(bool) &&
|
||||
AddressFormater.FormatAddress(p.Area, p.Address) == value.Key);
|
||||
break;
|
||||
}
|
||||
case MachineDataType.CommunicationTag:
|
||||
{
|
||||
address =
|
||||
GetAddresses.SingleOrDefault(p => p.CommunicationTag == value.Key);
|
||||
break;
|
||||
}
|
||||
case MachineDataType.Name:
|
||||
{
|
||||
address = GetAddresses.SingleOrDefault(p => p.Name == value.Key);
|
||||
break;
|
||||
}
|
||||
case MachineDataType.Id:
|
||||
{
|
||||
address = GetAddresses.SingleOrDefault(p => p.Id.ToString() == value.Key);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
address =
|
||||
GetAddresses.SingleOrDefault(p => p.CommunicationTag == value.Key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
//地址为空报错
|
||||
if (address == null)
|
||||
{
|
||||
logger.LogError($"Machine {ConnectionToken} Address {value.Key} doesn't exist.");
|
||||
continue;
|
||||
}
|
||||
//不能写报错
|
||||
if (!address.CanWrite)
|
||||
{
|
||||
logger.LogError($"Machine {ConnectionToken} Address {value.Key} cannot write.");
|
||||
continue;
|
||||
}
|
||||
addresses.Add(address);
|
||||
}
|
||||
|
||||
//遍历每条通讯的地址
|
||||
|
||||
|
||||
var valueHelper = ValueHelper.GetInstance(BaseUtility.Endian);
|
||||
|
||||
foreach (var addressUnit in addresses)
|
||||
{
|
||||
//协议主地址字符串
|
||||
var address = AddressFormater.FormatAddress(addressUnit.Area,
|
||||
addressUnit.Address, addressUnit.SubAddress);
|
||||
//获取写入类型
|
||||
var dataType = addressUnit.DataType;
|
||||
KeyValuePair<string, double> value;
|
||||
switch (setDataType)
|
||||
{
|
||||
case MachineDataType.Address:
|
||||
{
|
||||
//获取要写入的值
|
||||
value = values.SingleOrDefault(p => p.Key == address);
|
||||
break;
|
||||
}
|
||||
case MachineDataType.CommunicationTag:
|
||||
{
|
||||
value = values.SingleOrDefault(p => p.Key == addressUnit.CommunicationTag);
|
||||
break;
|
||||
}
|
||||
case MachineDataType.Name:
|
||||
{
|
||||
value = values.SingleOrDefault(p => p.Key == addressUnit.Name);
|
||||
break;
|
||||
}
|
||||
case MachineDataType.Id:
|
||||
{
|
||||
value = values.SingleOrDefault(p => p.Key == addressUnit.Id.ToString());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
value = values.SingleOrDefault(p => p.Key == addressUnit.CommunicationTag);
|
||||
break;
|
||||
}
|
||||
}
|
||||
//将要写入的值加入队列
|
||||
var data = Convert.ChangeType(value.Value / addressUnit.Zoom, dataType);
|
||||
|
||||
//写入数据
|
||||
await
|
||||
BaseUtility.GetUtilityMethods<IUtilityMethodDatas>().SetDatasAsync(address,
|
||||
new object[] { data });
|
||||
}
|
||||
//如果不保持连接,断开连接
|
||||
if (!KeepConnect)
|
||||
BaseUtility.Disconnect();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ErrorCount++;
|
||||
logger.LogError(e, $"BaseMachine -> SetDatas, Id:{Id} Connection:{ConnectionToken} error. ErrorCount {ErrorCount}.");
|
||||
|
||||
if (ErrorCount >= _maxErrorCount)
|
||||
Disconnect();
|
||||
return new ReturnStruct<bool>()
|
||||
{
|
||||
Datas = false,
|
||||
IsSuccess = false,
|
||||
ErrorCode = -100,
|
||||
ErrorMsg = "Unknown Exception"
|
||||
};
|
||||
}
|
||||
return new ReturnStruct<bool>()
|
||||
{
|
||||
Datas = true,
|
||||
IsSuccess = true,
|
||||
ErrorCode = 0,
|
||||
ErrorMsg = ""
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过Id获取数据字段定义
|
||||
/// </summary>
|
||||
/// <param name="addressUnitId">数据字段Id</param>
|
||||
/// <returns>数据字段</returns>
|
||||
public AddressUnit<TUnitKey, TAddressKey, TSubAddressKey> GetAddressUnitById(TUnitKey addressUnitId)
|
||||
{
|
||||
try
|
||||
{
|
||||
return GetAddresses.SingleOrDefault(p => p.Id.Equals(addressUnitId));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogError(e, $"BaseMachine -> GetAddressUnitById Id:{Id} ConnectionToken:{ConnectionToken} addressUnitId:{addressUnitId} Repeated");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取设备的方法集合
|
||||
/// </summary>
|
||||
@@ -653,24 +965,6 @@ namespace Modbus.Net
|
||||
{
|
||||
return BaseUtility.Disconnect();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过Id获取数据字段定义
|
||||
/// </summary>
|
||||
/// <param name="addressUnitId">数据字段Id</param>
|
||||
/// <returns>数据字段</returns>
|
||||
public AddressUnit<TUnitKey> GetAddressUnitById(TUnitKey addressUnitId)
|
||||
{
|
||||
try
|
||||
{
|
||||
return GetAddresses.SingleOrDefault(p => p.Id.Equals(addressUnitId));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogError(e, $"BaseMachine -> GetAddressUnitById Id:{Id} ConnectionToken:{ConnectionToken} addressUnitId:{addressUnitId} Repeated");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class BaseMachineEqualityComparer<TKey> : IEqualityComparer<IMachineProperty<TKey>>
|
||||
@@ -690,7 +984,7 @@ namespace Modbus.Net
|
||||
/// <summary>
|
||||
/// 通讯单元
|
||||
/// </summary>
|
||||
public class CommunicationUnit<TKey> where TKey : IEquatable<TKey>
|
||||
public class CommunicationUnit<TKey, TAddressKey, TSubAddressKey> where TKey : IEquatable<TKey> where TAddressKey : IEquatable<TAddressKey> where TSubAddressKey : IEquatable<TSubAddressKey>
|
||||
{
|
||||
/// <summary>
|
||||
/// 区域
|
||||
@@ -700,12 +994,12 @@ namespace Modbus.Net
|
||||
/// <summary>
|
||||
/// 地址
|
||||
/// </summary>
|
||||
public int Address { get; set; }
|
||||
public TAddressKey Address { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 子地址
|
||||
/// </summary>
|
||||
public int SubAddress { get; set; } = 0;
|
||||
public TSubAddressKey SubAddress { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取个数
|
||||
@@ -720,7 +1014,7 @@ namespace Modbus.Net
|
||||
/// <summary>
|
||||
/// 原始的地址
|
||||
/// </summary>
|
||||
public IEnumerable<AddressUnit<TKey>> OriginalAddresses { get; set; }
|
||||
public IEnumerable<AddressUnit<TKey, TAddressKey, TSubAddressKey>> OriginalAddresses { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -736,20 +1030,13 @@ namespace Modbus.Net
|
||||
/// <summary>
|
||||
/// 数据定义
|
||||
/// </summary>
|
||||
public AddressUnit AddressUnit { get; set; }
|
||||
public AddressUnit<string, string, string> AddressUnit { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 地址单元
|
||||
/// </summary>
|
||||
public class AddressUnit : AddressUnit<string>
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 地址单元
|
||||
/// </summary>
|
||||
public class AddressUnit<TKey> : IEquatable<AddressUnit<TKey>> where TKey : IEquatable<TKey>
|
||||
public class AddressUnit<TKey, TAddressKey, TSubAddressKey> : IEquatable<AddressUnit<TKey, TAddressKey, TSubAddressKey>> where TKey : IEquatable<TKey> where TAddressKey : IEquatable<TAddressKey> where TSubAddressKey : IEquatable<TSubAddressKey>
|
||||
{
|
||||
/// <summary>
|
||||
/// 数据单元Id
|
||||
@@ -764,12 +1051,12 @@ namespace Modbus.Net
|
||||
/// <summary>
|
||||
/// 地址
|
||||
/// </summary>
|
||||
public int Address { get; set; }
|
||||
public TAddressKey Address { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// bit位地址
|
||||
/// </summary>
|
||||
public int SubAddress { get; set; } = 0;
|
||||
public TSubAddressKey SubAddress { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 数据类型
|
||||
@@ -811,9 +1098,9 @@ namespace Modbus.Net
|
||||
/// </summary>
|
||||
/// <param name="other">另一个地址</param>
|
||||
/// <returns>是否一致</returns>
|
||||
public bool Equals(AddressUnit<TKey> other)
|
||||
public bool Equals(AddressUnit<TKey, TAddressKey, TSubAddressKey> other)
|
||||
{
|
||||
return Area.ToUpper() == other.Area.ToUpper() && Address == other.Address || Id.Equals(other.Id);
|
||||
return Area.ToUpper() == other.Area.ToUpper() && Address.Equals(other.Address) || Id.Equals(other.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,14 +35,14 @@ namespace Modbus.Net
|
||||
/// </summary>
|
||||
/// <param name="addressUnit"></param>
|
||||
/// <returns></returns>
|
||||
public static AddressUnit MapAddressUnitTUnitKeyToAddressUnit<TUnitKey>(this AddressUnit<TUnitKey> addressUnit) where TUnitKey : IEquatable<TUnitKey>
|
||||
public static AddressUnit<string, string, string> MapAddressUnitTUnitKeyToAddressUnit<TUnitKey, TAddressKey, TSubAddressKey>(this AddressUnit<TUnitKey, TAddressKey, TSubAddressKey> addressUnit) where TUnitKey : IEquatable<TUnitKey> where TAddressKey : IEquatable<TAddressKey> where TSubAddressKey : IEquatable<TSubAddressKey>
|
||||
{
|
||||
return new AddressUnit()
|
||||
return new AddressUnit<string, string, string>()
|
||||
{
|
||||
Id = addressUnit.Id.ToString(),
|
||||
Area = addressUnit.Area,
|
||||
Address = addressUnit.Address,
|
||||
SubAddress = addressUnit.SubAddress,
|
||||
Address = addressUnit.Address?.ToString(),
|
||||
SubAddress = addressUnit.SubAddress?.ToString(),
|
||||
DataType = addressUnit.DataType,
|
||||
Zoom = addressUnit.Zoom,
|
||||
DecimalPos = addressUnit.DecimalPos,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0;net462</TargetFrameworks>
|
||||
<TargetFrameworks>net6.0</TargetFrameworks>
|
||||
<LangVersion>10.0</LangVersion>
|
||||
<AssemblyName>Modbus.Net</AssemblyName>
|
||||
<RootNamespace>Modbus.Net</RootNamespace>
|
||||
<PackageId>Modbus.Net</PackageId>
|
||||
<Version>1.4.1</Version>
|
||||
<Version>1.4.2</Version>
|
||||
<Product>Modbus.Net</Product>
|
||||
<Authors>Chris L.(Luo Sheng)</Authors>
|
||||
<Company>Hangzhou Delian Science Technology Co.,Ltd.</Company>
|
||||
@@ -37,7 +37,6 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DotNetty.Handlers" Version="0.7.5" />
|
||||
<PackageReference Include="FastEnum" Version="1.8.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
|
||||
<PackageReference Include="Nito.AsyncEx" Version="5.1.2" />
|
||||
|
||||
@@ -146,18 +146,6 @@ namespace Modbus.Net
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送协议,通过传入需要使用的协议内容和输入结构
|
||||
/// </summary>
|
||||
/// <param name="unit">协议的实例</param>
|
||||
/// <param name="content">输入信息的结构化描述</param>
|
||||
/// <returns>输出信息的结构化描述</returns>
|
||||
public virtual TPipeUnit SendReceive(
|
||||
TProtocolUnit unit, IInputStruct content)
|
||||
{
|
||||
return AsyncHelper.RunSync(() => SendReceiveAsync(unit, content));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送协议,通过传入需要使用的协议内容和输入结构
|
||||
/// </summary>
|
||||
@@ -177,16 +165,6 @@ namespace Modbus.Net
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送协议内容并接收,一般方法
|
||||
/// </summary>
|
||||
/// <param name="content">写入的内容,使用对象数组描述</param>
|
||||
/// <returns>从设备获取的字节流</returns>
|
||||
public virtual TPipeUnit SendReceive(params object[] content)
|
||||
{
|
||||
return AsyncHelper.RunSync(() => SendReceiveAsync(content));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送协议内容并接收,一般方法(不能使用,如需使用请继承)
|
||||
/// </summary>
|
||||
@@ -197,18 +175,6 @@ namespace Modbus.Net
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送协议,通过传入需要使用的协议内容和输入结构
|
||||
/// </summary>
|
||||
/// <param name="unit">协议的实例</param>
|
||||
/// <param name="content">输入信息的结构化描述</param>
|
||||
/// <returns>输出信息的结构化描述</returns>
|
||||
/// <typeparam name="T">IOutputStruct的具体类型</typeparam>
|
||||
public virtual T SendReceive<T>(TProtocolUnit unit, IInputStruct content) where T : class, IOutputStruct
|
||||
{
|
||||
return AsyncHelper.RunSync(() => SendReceiveAsync<T>(unit, content));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送协议,通过传入需要使用的协议内容和输入结构
|
||||
/// </summary>
|
||||
|
||||
@@ -288,7 +288,8 @@ But after ":", property should match constructor except protocol, which refer to
|
||||
|
||||
The main target of Modbus.Net is building a high extensable hardware communication protocol, so we allow everyone to extend the protocol.
|
||||
|
||||
To extend Modbus.Net, first of all ValueHelper.cs in Modbus.Net is a really powerful tool that you can use to modify values in byte array.There are two ValueHelpers: ValueHelper(Little Endian) and BigEndianValueHelper(Big Endian). Remember using the correct one.
|
||||
To extend Modbus.Net, first of all ValueHelper.cs in Modbus.Net is a really powerful tool that you can use to modify values in byte array.There are three ValueHelpers: LittleEndianLsbValueHelper(Little Endian), BigEndianLsbValueHelper(Big Endian) and BigEndianMsbValueHelper(Big Endian with bit reverse). Remember using the correct one.<br>
|
||||
If you want to write a new one, just write in namespace "Modbus.Net" in assmenly start with "Modbus.Net." and Modbus.Net will automatically load it.
|
||||
|
||||
In this tutorial I will use Modbus.Net.Modbus to tell you how to implement your own protocol.
|
||||
|
||||
@@ -314,9 +315,9 @@ public class ReadDataModbusProtocol : ProtocolUnit
|
||||
|
||||
public override IOutputStruct Unformat(byte[] messageBytes, ref int pos)
|
||||
{
|
||||
byte slaveAddress = BigEndianValueHelper.Instance.GetByte(messageBytes, ref pos);
|
||||
byte functionCode = BigEndianValueHelper.Instance.GetByte(messageBytes, ref pos);
|
||||
byte dataCount = BigEndianValueHelper.Instance.GetByte(messageBytes, ref pos);
|
||||
byte slaveAddress = BigEndianLsbValueHelper.Instance.GetByte(messageBytes, ref pos);
|
||||
byte functionCode = BigEndianLsbValueHelper.Instance.GetByte(messageBytes, ref pos);
|
||||
byte dataCount = BigEndianLsbValueHelper.Instance.GetByte(messageBytes, ref pos);
|
||||
byte[] dataValue = new byte[dataCount];
|
||||
Array.Copy(messageBytes, 3, dataValue, 0, dataCount);
|
||||
return new ReadDataModbusOutputStruct(slaveAddress, functionCode, dataCount, dataValue);
|
||||
@@ -368,8 +369,8 @@ public class ModbusTcpProtocolLinkerBytesExtend : ProtocolLinkerBytesExtend
|
||||
byte[] newFormat = new byte[6 + content.Length];
|
||||
int tag = 0;
|
||||
ushort leng = (ushort)content.Length;
|
||||
Array.Copy(BigEndianValueHelper.Instance.GetBytes(tag), 0, newFormat, 0, 4);
|
||||
Array.Copy(BigEndianValueHelper.Instance.GetBytes(leng), 0, newFormat, 4, 2);
|
||||
Array.Copy(BigEndianLsbValueHelper.Instance.GetBytes(tag), 0, newFormat, 0, 4);
|
||||
Array.Copy(BigEndianLsbValueHelper.Instance.GetBytes(leng), 0, newFormat, 4, 2);
|
||||
Array.Copy(content, 0, newFormat, 6, content.Length);
|
||||
return newFormat;
|
||||
}
|
||||
|
||||
@@ -4,27 +4,6 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
/// <summary>
|
||||
/// 端格式
|
||||
/// </summary>
|
||||
public enum Endian
|
||||
{
|
||||
/// <summary>
|
||||
/// 小端
|
||||
/// </summary>
|
||||
LittleEndianLsb,
|
||||
|
||||
/// <summary>
|
||||
/// 大端-小端位
|
||||
/// </summary>
|
||||
BigEndianLsb,
|
||||
|
||||
/// <summary>
|
||||
/// 大端-大端位
|
||||
/// </summary>
|
||||
BigEndianMsb
|
||||
}
|
||||
|
||||
namespace Modbus.Net
|
||||
{
|
||||
/// <summary>
|
||||
@@ -66,17 +45,6 @@ namespace Modbus.Net
|
||||
/// </summary>
|
||||
public byte MasterAddress { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取数据
|
||||
/// </summary>
|
||||
/// <param name="startAddress">开始地址</param>
|
||||
/// <param name="getByteCount">获取字节数个数</param>
|
||||
/// <returns>接收到的byte数据</returns>
|
||||
public virtual ReturnStruct<byte[]> GetDatas(string startAddress, int getByteCount)
|
||||
{
|
||||
return AsyncHelper.RunSync(() => GetDatasAsync(startAddress, getByteCount));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取数据
|
||||
/// </summary>
|
||||
@@ -85,18 +53,6 @@ namespace Modbus.Net
|
||||
/// <returns>接收到的byte数据</returns>
|
||||
public abstract Task<ReturnStruct<byte[]>> GetDatasAsync(string startAddress, int getByteCount);
|
||||
|
||||
/// <summary>
|
||||
/// 获取数据
|
||||
/// </summary>
|
||||
/// <param name="startAddress">开始地址</param>
|
||||
/// <param name="getTypeAndCount">获取类型和个数</param>
|
||||
/// <returns>接收到的对应的类型和数据</returns>
|
||||
public virtual ReturnStruct<object[]> GetDatas(string startAddress,
|
||||
KeyValuePair<Type, int> getTypeAndCount)
|
||||
{
|
||||
return AsyncHelper.RunSync(() => GetDatasAsync(startAddress, getTypeAndCount));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取数据
|
||||
/// </summary>
|
||||
@@ -109,7 +65,7 @@ namespace Modbus.Net
|
||||
try
|
||||
{
|
||||
var typeName = getTypeAndCount.Key.FullName;
|
||||
var bCount = BigEndianValueHelper.Instance.ByteLength[typeName];
|
||||
var bCount = ValueHelper.ByteLength[typeName];
|
||||
var getReturnValue = await GetDatasAsync(startAddress,
|
||||
(int)Math.Ceiling(bCount * getTypeAndCount.Value));
|
||||
var getBytes = getReturnValue;
|
||||
@@ -144,19 +100,6 @@ namespace Modbus.Net
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取数据
|
||||
/// </summary>
|
||||
/// <typeparam name="T">需要接收的类型</typeparam>
|
||||
/// <param name="startAddress">开始地址</param>
|
||||
/// <param name="getByteCount">获取字节数个数</param>
|
||||
/// <returns>接收到的对应的类型和数据</returns>
|
||||
public virtual ReturnStruct<T[]> GetDatas<T>(string startAddress,
|
||||
int getByteCount)
|
||||
{
|
||||
return AsyncHelper.RunSync(() => GetDatasAsync<T>(startAddress, getByteCount));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取数据
|
||||
/// </summary>
|
||||
@@ -202,19 +145,6 @@ namespace Modbus.Net
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取数据
|
||||
/// </summary>
|
||||
/// <param name="startAddress">开始地址</param>
|
||||
/// <param name="getTypeAndCountList">获取类型和个数的队列</param>
|
||||
/// <returns>获取数据的对象数组,请强制转换成相应类型</returns>
|
||||
public virtual ReturnStruct<object[]> GetDatas(string startAddress,
|
||||
IEnumerable<KeyValuePair<Type, int>> getTypeAndCountList)
|
||||
{
|
||||
return
|
||||
AsyncHelper.RunSync(() => GetDatasAsync(startAddress, getTypeAndCountList));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取数据
|
||||
/// </summary>
|
||||
@@ -230,7 +160,7 @@ namespace Modbus.Net
|
||||
var bAllCount = (
|
||||
from getTypeAndCount in translateTypeAndCount
|
||||
let typeName = getTypeAndCount.Key.FullName
|
||||
let bCount = BigEndianValueHelper.Instance.ByteLength[typeName]
|
||||
let bCount = ValueHelper.ByteLength[typeName]
|
||||
select (int)Math.Ceiling(bCount * getTypeAndCount.Value)).Sum();
|
||||
var getReturnValue = await GetDatasAsync(startAddress, bAllCount);
|
||||
var getBytes = getReturnValue;
|
||||
@@ -265,17 +195,6 @@ namespace Modbus.Net
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置数据
|
||||
/// </summary>
|
||||
/// <param name="startAddress">开始地址</param>
|
||||
/// <param name="setContents">设置数据</param>
|
||||
/// <returns>是否设置成功</returns>
|
||||
public virtual ReturnStruct<bool> SetDatas(string startAddress, object[] setContents)
|
||||
{
|
||||
return AsyncHelper.RunSync(() => SetDatasAsync(startAddress, setContents));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置数据
|
||||
/// </summary>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"TCP": {
|
||||
"ConnectionTimeout": "5000",
|
||||
"FetchSleepTime": "100",
|
||||
"FullDuplex": "False",
|
||||
"FullDuplex": "True",
|
||||
"Modbus": {
|
||||
"ModbusPort": "502",
|
||||
"IP": "192.168.1.1"
|
||||
@@ -16,7 +16,7 @@
|
||||
"UDP": {
|
||||
"ConnectionTimeout": "5000",
|
||||
"FetchSleepTime": "100",
|
||||
"FullDuplex": "False",
|
||||
"FullDuplex": "True",
|
||||
"Modbus": {
|
||||
"ModbusPort": "502",
|
||||
"IP": "192.168.1.1"
|
||||
|
||||
@@ -31,3 +31,4 @@ Thanks
|
||||
* Quartz - Job Scheduler
|
||||
* Serilog - Logging
|
||||
* DotNetty - Network Transporting
|
||||
* h-opc & Technosoftware.DaAeHdaSolution & OPCFoundation.NetStandard - OPC Trasporting
|
||||
|
||||
@@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using Modbus.Net;
|
||||
using Modbus.Net.Modbus;
|
||||
using System.Diagnostics;
|
||||
using AddressUnit = Modbus.Net.AddressUnit<string, int, int>;
|
||||
using MachineJobSchedulerCreator = Modbus.Net.MachineJobSchedulerCreator<Modbus.Net.IMachineMethodDatas, string, double>;
|
||||
using ModbusMachine = Modbus.Net.Modbus.ModbusMachine<string, string>;
|
||||
|
||||
@@ -55,7 +56,7 @@ namespace AnyType.Controllers
|
||||
Value = 0,
|
||||
Type = unitValue.DataType.Name
|
||||
};
|
||||
var machine = new ModbusMachine("1", ModbusType.Tcp, "10.10.18.251:502", addressUnits, true, 2, 0);
|
||||
var machine = new ModbusMachine("1", ModbusType.Tcp, "10.10.18.251:502", addressUnits, true, 2, 0, Endian.BigEndianLsb);
|
||||
//启动任务
|
||||
await MachineJobSchedulerCreator.CreateScheduler("Trigger1", -1, 1).Result.From(machine.Id, machine, MachineDataType.CommunicationTag).Result.Query("Query1",
|
||||
returnValues =>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"TCP": {
|
||||
"ConnectionTimeout": "5000",
|
||||
"FetchSleepTime": "100",
|
||||
"FullDuplex": "False",
|
||||
"FullDuplex": "True",
|
||||
"Modbus": {
|
||||
"ModbusPort": "502",
|
||||
"IP": "192.168.1.1"
|
||||
@@ -16,7 +16,7 @@
|
||||
"UDP": {
|
||||
"ConnectionTimeout": "5000",
|
||||
"FetchSleepTime": "100",
|
||||
"FullDuplex": "False",
|
||||
"FullDuplex": "True",
|
||||
"Modbus": {
|
||||
"ModbusPort": "502",
|
||||
"IP": "192.168.1.1"
|
||||
|
||||
@@ -30,12 +30,12 @@ namespace CrossLamp.Controllers
|
||||
{
|
||||
if (_utility == null)
|
||||
{
|
||||
_utility = new ModbusUtility(ModbusType.Tcp, "10.10.18.251", 2, 0);
|
||||
_utility = new ModbusUtility(ModbusType.Tcp, "10.10.18.251", 2, 0, Endian.BigEndianLsb);
|
||||
await _utility.ConnectAsync();
|
||||
}
|
||||
Lamp light = new Lamp();
|
||||
object[] lampsbyte = (await _utility.GetDatasAsync("0X 1", new KeyValuePair<Type, int>(typeof(bool), 7))).Datas;
|
||||
bool[] lamps = BigEndianValueHelper.Instance.ObjectArrayToDestinationArray<bool>(lampsbyte);
|
||||
bool[] lamps = BigEndianLsbValueHelper.Instance.ObjectArrayToDestinationArray<bool>(lampsbyte);
|
||||
if (lamps[0])
|
||||
{
|
||||
light.MainLamp = LightLamp.Red.ToString();
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"TCP": {
|
||||
"ConnectionTimeout": "5000",
|
||||
"FetchSleepTime": "100",
|
||||
"FullDuplex": "False",
|
||||
"FullDuplex": "True",
|
||||
"Modbus": {
|
||||
"ModbusPort": "502",
|
||||
"IP": "192.168.1.1"
|
||||
@@ -16,7 +16,7 @@
|
||||
"UDP": {
|
||||
"ConnectionTimeout": "5000",
|
||||
"FetchSleepTime": "100",
|
||||
"FullDuplex": "False",
|
||||
"FullDuplex": "True",
|
||||
"Modbus": {
|
||||
"ModbusPort": "502",
|
||||
"IP": "192.168.1.1"
|
||||
|
||||
@@ -5,11 +5,17 @@ namespace MachineJob
|
||||
// simple log provider to get something to the console
|
||||
public class ConsoleLogProvider : ILogProvider
|
||||
{
|
||||
private readonly IConfigurationRoot configuration = new ConfigurationBuilder()
|
||||
.SetBasePath(Directory.GetCurrentDirectory())
|
||||
.AddJsonFile("appsettings.json")
|
||||
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") ?? "Production"}.json", true)
|
||||
.Build();
|
||||
|
||||
public Logger GetLogger(string name)
|
||||
{
|
||||
return (level, func, exception, parameters) =>
|
||||
{
|
||||
if (func != null)
|
||||
if (level >= configuration.GetSection("Quartz").GetValue<Quartz.Logging.LogLevel>("LogLevel") && func != null)
|
||||
{
|
||||
Console.WriteLine("[" + DateTime.Now.ToLongTimeString() + "] [" + level + "] " + func(), parameters);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace MachineJob
|
||||
{
|
||||
@@ -15,7 +15,7 @@ namespace MachineJob
|
||||
|
||||
private static readonly string connectionString = configuration.GetConnectionString("DatabaseWriteConnectionString")!;
|
||||
|
||||
public DbSet<DatabaseWriteEntity> DatabaseWrites { get; set; }
|
||||
public DbSet<DatabaseWriteEntity>? DatabaseWrites { get; set; }
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
@@ -23,6 +23,7 @@ namespace MachineJob
|
||||
}
|
||||
}
|
||||
|
||||
[Table(name: "databasewrites")]
|
||||
public class DatabaseWriteEntity
|
||||
{
|
||||
[Key]
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Modbus.Net\Modbus.Net.Modbus\Modbus.Net.Modbus.csproj" />
|
||||
<ProjectReference Include="..\..\Modbus.Net\Modbus.Net.OPC\Modbus.Net.Opc.csproj" />
|
||||
<ProjectReference Include="..\..\Modbus.Net\Modbus.Net.Siemens\Modbus.Net.Siemens.csproj" />
|
||||
<ProjectReference Include="..\..\Modbus.Net\Modbus.Net\Modbus.Net.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -61,10 +61,16 @@ namespace MachineJob.Service
|
||||
//}
|
||||
|
||||
//4. 使用MultipleMachinesJobScheduler
|
||||
//return Task.Run(() => MultipleMachinesJobScheduler.RunScheduler(machines, async (machine, scheduler) =>
|
||||
//{
|
||||
// /await scheduler.From(machine.Id + ".From", machine, MachineDataType.Name).Result.Query(machine.Id + ".ConsoleQuery", QueryConsole).Result.To(machine.Id + ".To", machine).Result.Deal(machine.Id + ".Deal", OnSuccess, OnFailure).Result.Run();
|
||||
//}, -1, 10));
|
||||
|
||||
//5. 不设置固定时间,连续触发Job
|
||||
return Task.Run(() => MultipleMachinesJobScheduler.RunScheduler(machines, async (machine, scheduler) =>
|
||||
{
|
||||
await scheduler.From(machine.Id + ".From", machine, MachineDataType.Name).Result.Query(machine.Id + ".ConsoleQuery", QueryConsole).Result.To(machine.Id + ".To", machine).Result.Deal(machine.Id + ".Deal", OnSuccess, OnFailure).Result.Run();
|
||||
}, -1, 10));
|
||||
}, -1, 0));
|
||||
}
|
||||
|
||||
public override Task StopAsync(CancellationToken cancellationToken)
|
||||
@@ -94,11 +100,12 @@ namespace MachineJob.Service
|
||||
_logger.LogInformation(dataReturnDef.MachineId + " " + value.Key + " " + value.Value.DeviceValue);
|
||||
}
|
||||
|
||||
/*
|
||||
try
|
||||
{
|
||||
using (var context = new DatabaseWriteContext())
|
||||
{
|
||||
context.DatabaseWrites.Add(new DatabaseWriteEntity
|
||||
context.DatabaseWrites?.Add(new DatabaseWriteEntity
|
||||
{
|
||||
Value1 = values["Test1"].DeviceValue,
|
||||
Value2 = values["Test2"].DeviceValue,
|
||||
@@ -119,7 +126,7 @@ namespace MachineJob.Service
|
||||
{
|
||||
//ignore
|
||||
}
|
||||
|
||||
*/
|
||||
Random r = new Random();
|
||||
foreach (var value in values)
|
||||
{
|
||||
|
||||
@@ -1,7 +1,21 @@
|
||||
{
|
||||
"Serilog": {
|
||||
"MinimumLevel": {
|
||||
"Default": "Debug"
|
||||
"Default": "Debug",
|
||||
"Override": {
|
||||
"Microsoft": "Debug",
|
||||
"Microsoft.Hosting.Lifetime": "Debug"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Debug",
|
||||
"Microsoft": "Debug",
|
||||
"Microsoft.Hosting.Lifetime": "Debug"
|
||||
}
|
||||
},
|
||||
"Quartz": {
|
||||
"LogLevel": "Debug"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,21 @@
|
||||
{
|
||||
"Serilog": {
|
||||
"MinimumLevel": {
|
||||
"Default": "Error"
|
||||
"Default": "Error",
|
||||
"Override": {
|
||||
"Microsoft": "Error",
|
||||
"Microsoft.Hosting.Lifetime": "Error"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Error",
|
||||
"Microsoft": "Error",
|
||||
"Microsoft.Hosting.Lifetime": "Error"
|
||||
}
|
||||
},
|
||||
"Quartz": {
|
||||
"LogLevel": "Error"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"TCP": {
|
||||
"ConnectionTimeout": "5000",
|
||||
"FetchSleepTime": "100",
|
||||
"FullDuplex": "False",
|
||||
"FullDuplex": "True",
|
||||
"Modbus": {
|
||||
"ModbusPort": "502",
|
||||
"IP": "192.168.1.1"
|
||||
@@ -16,7 +16,7 @@
|
||||
"UDP": {
|
||||
"ConnectionTimeout": "5000",
|
||||
"FetchSleepTime": "100",
|
||||
"FullDuplex": "False",
|
||||
"FullDuplex": "True",
|
||||
"Modbus": {
|
||||
"ModbusPort": "502",
|
||||
"IP": "192.168.1.1"
|
||||
|
||||
@@ -8,8 +8,18 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Information",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"Quartz": {
|
||||
"LogLevel": "Info"
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"DatabaseWriteConnectionString": "Server=10.10.18.245; User ID=root; Password=123456; Database=modbusnettest;"
|
||||
"DatabaseWriteConnectionString": "Server=127.0.0.1; User ID=root; Password=123456; Database=modbusnettest;"
|
||||
},
|
||||
|
||||
"Modbus.Net": {
|
||||
@@ -18,7 +28,7 @@
|
||||
"a:id": "ModbusMachine1",
|
||||
"b:protocol": "Modbus",
|
||||
"c:type": "Tcp",
|
||||
"d:connectionString": "10.10.18.251",
|
||||
"d:connectionString": "127.0.0.1",
|
||||
"e:addressMap": "AddressMapModbus",
|
||||
"f:keepConnect": true,
|
||||
"g:slaveAddress": 1,
|
||||
@@ -29,7 +39,7 @@
|
||||
"a:id": "SiemensMachine1",
|
||||
"b:protocol": "Siemens",
|
||||
"c:type": "Tcp",
|
||||
"d:connectionString": "10.10.18.251",
|
||||
"d:connectionString": "127.0.0.1",
|
||||
"e:model": "S7_1200",
|
||||
"f:addressMap": "AddressMapSiemens",
|
||||
"g:keepConnect": true,
|
||||
@@ -45,7 +55,7 @@
|
||||
"d:connectionString": "COM1",
|
||||
"e:addressMap": "AddressMapModbus",
|
||||
"f:keepConnect": true,
|
||||
"g:slaveAddress": 3,
|
||||
"g:slaveAddress": 1,
|
||||
"h:masterAddress": 2,
|
||||
"i:endian": "BigEndianLsb"
|
||||
},
|
||||
@@ -53,7 +63,7 @@
|
||||
"a:id": "SiemensMachine2",
|
||||
"b:protocol": "Siemens",
|
||||
"c:type": "Ppi",
|
||||
"d:connectionString": "COM11",
|
||||
"d:connectionString": "COM2",
|
||||
"e:model": "S7_200",
|
||||
"f:addressMap": "AddressMapSiemens",
|
||||
"g:keepConnect": true,
|
||||
@@ -61,6 +71,14 @@
|
||||
"i:masterAddress": 0,
|
||||
"j:src": 1,
|
||||
"k:dst": 0
|
||||
},
|
||||
{
|
||||
"a:id": "OpcMachine1",
|
||||
"b:protocol": "Opc",
|
||||
"c:type": "Da",
|
||||
"d:connectionString": "opcda://localhost/Matrikon.OPC.Simulation.1",
|
||||
"e:addressMap": "AddressMapOpc",
|
||||
"f:tagSpliter": "."
|
||||
}
|
||||
],
|
||||
"addressMap": {
|
||||
@@ -207,6 +225,24 @@
|
||||
"Id": "10",
|
||||
"Name": "Test10"
|
||||
}
|
||||
],
|
||||
"AddressMapOpc": [
|
||||
{
|
||||
"Area": "Random",
|
||||
"Address": "Real4",
|
||||
"DataType": "Single",
|
||||
"Id": "1",
|
||||
"Name": "Test1",
|
||||
"DecimalPos": 2
|
||||
},
|
||||
{
|
||||
"Area": "Random",
|
||||
"Address": "Real8",
|
||||
"DataType": "Double",
|
||||
"Id": "2",
|
||||
"Name": "Test2",
|
||||
"DecimalPos": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using Modbus.Net;
|
||||
using Modbus.Net.Modbus;
|
||||
using System.Diagnostics;
|
||||
using TripleAdd.Models;
|
||||
using AddressUnit = Modbus.Net.AddressUnit<string, int, int>;
|
||||
|
||||
namespace TripleAdd.Controllers
|
||||
{
|
||||
@@ -41,12 +42,12 @@ namespace TripleAdd.Controllers
|
||||
{
|
||||
if (utility == null)
|
||||
{
|
||||
utility = new ModbusUtility(ModbusType.Tcp, "10.10.18.251", 2, 0);
|
||||
utility = new ModbusUtility(ModbusType.Tcp, "10.10.18.251", 2, 0, Endian.BigEndianLsb);
|
||||
utility.AddressTranslator = new AddressTranslatorModbus();
|
||||
await utility.ConnectAsync();
|
||||
}
|
||||
object[] getNum = (await utility.GetDatasAsync("4X 1", new KeyValuePair<Type, int>(typeof(ushort), 4))).Datas;
|
||||
ushort[] getNumUshorts = BigEndianValueHelper.Instance.ObjectArrayToDestinationArray<ushort>(getNum);
|
||||
ushort[] getNumUshorts = BigEndianLsbValueHelper.Instance.ObjectArrayToDestinationArray<ushort>(getNum);
|
||||
return SetValue(getNumUshorts);
|
||||
}
|
||||
|
||||
@@ -60,7 +61,7 @@ namespace TripleAdd.Controllers
|
||||
new AddressUnit() {Id = "2", Area = "4X", Address = 2, CommunicationTag = "Add2", DataType = typeof(ushort), Zoom = 1, DecimalPos = 0},
|
||||
new AddressUnit() {Id = "3", Area = "4X", Address = 3, CommunicationTag = "Add3", DataType = typeof(ushort), Zoom = 1, DecimalPos = 0},
|
||||
new AddressUnit() {Id = "4", Area = "4X", Address = 4, CommunicationTag = "Ans", DataType = typeof(ushort), Zoom = 1, DecimalPos = 0},
|
||||
}, 2, 0);
|
||||
}, 2, 0, Endian.BigEndianLsb);
|
||||
machine.AddressCombiner = new AddressCombinerContinus<string>(machine.AddressTranslator, 100000);
|
||||
machine.AddressCombinerSet = new AddressCombinerContinus<string>(machine.AddressTranslator, 100000);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"TCP": {
|
||||
"ConnectionTimeout": "5000",
|
||||
"FetchSleepTime": "100",
|
||||
"FullDuplex": "False",
|
||||
"FullDuplex": "True",
|
||||
"Modbus": {
|
||||
"ModbusPort": "502",
|
||||
"IP": "192.168.1.1"
|
||||
@@ -16,7 +16,7 @@
|
||||
"UDP": {
|
||||
"ConnectionTimeout": "5000",
|
||||
"FetchSleepTime": "100",
|
||||
"FullDuplex": "False",
|
||||
"FullDuplex": "True",
|
||||
"Modbus": {
|
||||
"ModbusPort": "502",
|
||||
"IP": "192.168.1.1"
|
||||
|
||||
876
Technosoftware/DaAeHdaClient.Com/Ae/Interop.cs
Normal file
876
Technosoftware/DaAeHdaClient.Com/Ae/Interop.cs
Normal file
@@ -0,0 +1,876 @@
|
||||
#region Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
|
||||
// Web: https://www.technosoftware.com
|
||||
//
|
||||
// The source code in this file is covered under a dual-license scenario:
|
||||
// - Owner of a purchased license: SCLA 1.0
|
||||
// - GPL V3: everybody else
|
||||
//
|
||||
// SCLA license terms accompanied with this source code.
|
||||
// See SCLA 1.0: https://technosoftware.com/license/Source_Code_License_Agreement.pdf
|
||||
//
|
||||
// GNU General Public License as published by the Free Software Foundation;
|
||||
// version 3 of the License are accompanied with this source code.
|
||||
// See https://technosoftware.com/license/GPLv3License.txt
|
||||
//
|
||||
// This source code is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//-----------------------------------------------------------------------------
|
||||
#endregion Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
|
||||
|
||||
#region Using Directives
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using Technosoftware.DaAeHdaClient.Ae;
|
||||
using Technosoftware.DaAeHdaClient.Da;
|
||||
#endregion
|
||||
|
||||
#pragma warning disable 0618
|
||||
|
||||
namespace Technosoftware.DaAeHdaClient.Com.Ae
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines COM marshalling/unmarshalling functions for AE.
|
||||
/// </summary>
|
||||
internal class Interop
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a standard FILETIME to an OpcRcw.Ae.FILETIME structure.
|
||||
/// </summary>
|
||||
internal static OpcRcw.Ae.FILETIME Convert(FILETIME input)
|
||||
{
|
||||
var output = new OpcRcw.Ae.FILETIME();
|
||||
output.dwLowDateTime = input.dwLowDateTime;
|
||||
output.dwHighDateTime = input.dwHighDateTime;
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an OpcRcw.Ae.FILETIME to a standard FILETIME structure.
|
||||
/// </summary>
|
||||
internal static FILETIME Convert(OpcRcw.Ae.FILETIME input)
|
||||
{
|
||||
var output = new FILETIME();
|
||||
output.dwLowDateTime = input.dwLowDateTime;
|
||||
output.dwHighDateTime = input.dwHighDateTime;
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the HRESULT to a system type.
|
||||
/// </summary>
|
||||
internal static OpcResult GetResultID(int input)
|
||||
{
|
||||
// must check for this error because of a code collision with a DA code.
|
||||
if (input == Result.E_INVALIDBRANCHNAME)
|
||||
{
|
||||
return OpcResult.Ae.E_INVALIDBRANCHNAME;
|
||||
}
|
||||
|
||||
return Technosoftware.DaAeHdaClient.Com.Interop.GetResultID(input);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unmarshals and deallocates a OPCEVENTSERVERSTATUS structure.
|
||||
/// </summary>
|
||||
internal static OpcServerStatus GetServerStatus(ref IntPtr pInput, bool deallocate)
|
||||
{
|
||||
OpcServerStatus output = null;
|
||||
|
||||
if (pInput != IntPtr.Zero)
|
||||
{
|
||||
var status = (OpcRcw.Ae.OPCEVENTSERVERSTATUS)Marshal.PtrToStructure(pInput, typeof(OpcRcw.Ae.OPCEVENTSERVERSTATUS));
|
||||
|
||||
output = new OpcServerStatus();
|
||||
|
||||
output.VendorInfo = status.szVendorInfo;
|
||||
output.ProductVersion = string.Format("{0}.{1}.{2}", status.wMajorVersion, status.wMinorVersion, status.wBuildNumber);
|
||||
output.MajorVersion = status.wMajorVersion;
|
||||
output.MinorVersion = status.wMinorVersion;
|
||||
output.BuildNumber = status.wBuildNumber;
|
||||
|
||||
output.ServerState = (OpcServerState)status.dwServerState;
|
||||
output.StatusInfo = null;
|
||||
output.StartTime = Technosoftware.DaAeHdaClient.Com.Interop.GetFILETIME(Convert(status.ftStartTime));
|
||||
output.CurrentTime = Technosoftware.DaAeHdaClient.Com.Interop.GetFILETIME(Convert(status.ftCurrentTime));
|
||||
output.LastUpdateTime = Technosoftware.DaAeHdaClient.Com.Interop.GetFILETIME(Convert(status.ftLastUpdateTime));
|
||||
|
||||
if (deallocate)
|
||||
{
|
||||
Marshal.DestroyStructure(pInput, typeof(OpcRcw.Ae.OPCEVENTSERVERSTATUS));
|
||||
Marshal.FreeCoTaskMem(pInput);
|
||||
pInput = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a NodeType value to the OPCAEBROWSETYPE equivalent.
|
||||
/// </summary>
|
||||
internal static OpcRcw.Ae.OPCAEBROWSETYPE GetBrowseType(TsCAeBrowseType input)
|
||||
{
|
||||
switch (input)
|
||||
{
|
||||
case TsCAeBrowseType.Area: return OpcRcw.Ae.OPCAEBROWSETYPE.OPC_AREA;
|
||||
case TsCAeBrowseType.Source: return OpcRcw.Ae.OPCAEBROWSETYPE.OPC_SOURCE;
|
||||
}
|
||||
|
||||
return OpcRcw.Ae.OPCAEBROWSETYPE.OPC_AREA;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an array of ONEVENTSTRUCT structs to an array of EventNotification objects.
|
||||
/// </summary>
|
||||
internal static TsCAeEventNotification[] GetEventNotifications(OpcRcw.Ae.ONEVENTSTRUCT[] input)
|
||||
{
|
||||
TsCAeEventNotification[] output = null;
|
||||
|
||||
if (input != null && input.Length > 0)
|
||||
{
|
||||
output = new TsCAeEventNotification[input.Length];
|
||||
|
||||
for (var ii = 0; ii < input.Length; ii++)
|
||||
{
|
||||
output[ii] = GetEventNotification(input[ii]);
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a ONEVENTSTRUCT struct to a EventNotification object.
|
||||
/// </summary>
|
||||
internal static TsCAeEventNotification GetEventNotification(OpcRcw.Ae.ONEVENTSTRUCT input)
|
||||
{
|
||||
var output = new TsCAeEventNotification();
|
||||
|
||||
output.SourceID = input.szSource;
|
||||
output.Time = Technosoftware.DaAeHdaClient.Com.Interop.GetFILETIME(Convert(input.ftTime));
|
||||
output.Severity = input.dwSeverity;
|
||||
output.Message = input.szMessage;
|
||||
output.EventType = (TsCAeEventType)input.dwEventType;
|
||||
output.EventCategory = input.dwEventCategory;
|
||||
output.ChangeMask = input.wChangeMask;
|
||||
output.NewState = input.wNewState;
|
||||
output.Quality = new TsCDaQuality(input.wQuality);
|
||||
output.ConditionName = input.szConditionName;
|
||||
output.SubConditionName = input.szSubconditionName;
|
||||
output.AckRequired = input.bAckRequired != 0;
|
||||
output.ActiveTime = Technosoftware.DaAeHdaClient.Com.Interop.GetFILETIME(Convert(input.ftActiveTime));
|
||||
output.Cookie = input.dwCookie;
|
||||
output.ActorID = input.szActorID;
|
||||
|
||||
var attributes = Technosoftware.DaAeHdaClient.Com.Interop.GetVARIANTs(ref input.pEventAttributes, input.dwNumEventAttrs, false);
|
||||
|
||||
output.SetAttributes(attributes);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an array of OPCCONDITIONSTATE structs to an array of Condition objects.
|
||||
/// </summary>
|
||||
internal static TsCAeCondition[] GetConditions(ref IntPtr pInput, int count, bool deallocate)
|
||||
{
|
||||
TsCAeCondition[] output = null;
|
||||
|
||||
if (pInput != IntPtr.Zero && count > 0)
|
||||
{
|
||||
output = new TsCAeCondition[count];
|
||||
|
||||
var pos = pInput;
|
||||
|
||||
for (var ii = 0; ii < count; ii++)
|
||||
{
|
||||
var condition = (OpcRcw.Ae.OPCCONDITIONSTATE)Marshal.PtrToStructure(pos, typeof(OpcRcw.Ae.OPCCONDITIONSTATE));
|
||||
|
||||
output[ii] = new TsCAeCondition();
|
||||
|
||||
output[ii].State = condition.wState;
|
||||
output[ii].Quality = new TsCDaQuality(condition.wQuality);
|
||||
output[ii].Comment = condition.szComment;
|
||||
output[ii].AcknowledgerID = condition.szAcknowledgerID;
|
||||
output[ii].CondLastActive = Technosoftware.DaAeHdaClient.Com.Interop.GetFILETIME(Convert(condition.ftCondLastActive));
|
||||
output[ii].CondLastInactive = Technosoftware.DaAeHdaClient.Com.Interop.GetFILETIME(Convert(condition.ftCondLastInactive));
|
||||
output[ii].SubCondLastActive = Technosoftware.DaAeHdaClient.Com.Interop.GetFILETIME(Convert(condition.ftSubCondLastActive));
|
||||
output[ii].LastAckTime = Technosoftware.DaAeHdaClient.Com.Interop.GetFILETIME(Convert(condition.ftLastAckTime));
|
||||
|
||||
output[ii].ActiveSubCondition.Name = condition.szActiveSubCondition;
|
||||
output[ii].ActiveSubCondition.Definition = condition.szASCDefinition;
|
||||
output[ii].ActiveSubCondition.Severity = condition.dwASCSeverity;
|
||||
output[ii].ActiveSubCondition.Description = condition.szASCDescription;
|
||||
|
||||
// unmarshal sub-conditions.
|
||||
var names = Technosoftware.DaAeHdaClient.Com.Interop.GetUnicodeStrings(ref condition.pszSCNames, condition.dwNumSCs, deallocate);
|
||||
var severities = Technosoftware.DaAeHdaClient.Com.Interop.GetInt32s(ref condition.pdwSCSeverities, condition.dwNumSCs, deallocate);
|
||||
var definitions = Technosoftware.DaAeHdaClient.Com.Interop.GetUnicodeStrings(ref condition.pszSCDefinitions, condition.dwNumSCs, deallocate);
|
||||
var descriptions = Technosoftware.DaAeHdaClient.Com.Interop.GetUnicodeStrings(ref condition.pszSCDescriptions, condition.dwNumSCs, deallocate);
|
||||
|
||||
output[ii].SubConditions.Clear();
|
||||
|
||||
if (condition.dwNumSCs > 0)
|
||||
{
|
||||
for (var jj = 0; jj < names.Length; jj++)
|
||||
{
|
||||
var subcondition = new TsCAeSubCondition();
|
||||
|
||||
subcondition.Name = names[jj];
|
||||
subcondition.Severity = severities[jj];
|
||||
subcondition.Definition = definitions[jj];
|
||||
subcondition.Description = descriptions[jj];
|
||||
|
||||
output[ii].SubConditions.Add(subcondition);
|
||||
}
|
||||
}
|
||||
|
||||
// unmarshal attributes.
|
||||
var values = Technosoftware.DaAeHdaClient.Com.Interop.GetVARIANTs(ref condition.pEventAttributes, condition.dwNumEventAttrs, deallocate);
|
||||
var errors = Technosoftware.DaAeHdaClient.Com.Interop.GetInt32s(ref condition.pErrors, condition.dwNumEventAttrs, deallocate);
|
||||
|
||||
output[ii].Attributes.Clear();
|
||||
|
||||
if (condition.dwNumEventAttrs > 0)
|
||||
{
|
||||
for (var jj = 0; jj < values.Length; jj++)
|
||||
{
|
||||
var attribute = new TsCAeAttributeValue();
|
||||
|
||||
attribute.ID = 0;
|
||||
attribute.Value = values[jj];
|
||||
attribute.Result = GetResultID(errors[jj]);
|
||||
|
||||
output[ii].Attributes.Add(attribute);
|
||||
}
|
||||
}
|
||||
|
||||
// deallocate structure.
|
||||
if (deallocate)
|
||||
{
|
||||
Marshal.DestroyStructure(pos, typeof(OpcRcw.Ae.OPCCONDITIONSTATE));
|
||||
}
|
||||
|
||||
pos = (IntPtr)(pos.ToInt64() + Marshal.SizeOf(typeof(OpcRcw.Ae.OPCCONDITIONSTATE)));
|
||||
}
|
||||
|
||||
// deallocate array.
|
||||
if (deallocate)
|
||||
{
|
||||
Marshal.FreeCoTaskMem(pInput);
|
||||
pInput = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/*
|
||||
/// <summary>
|
||||
/// Converts an array of COM HRESULTs structures to .NET ResultID objects.
|
||||
/// </summary>
|
||||
internal static ResultID[] GetResultIDs(ref IntPtr pInput, int count, bool deallocate)
|
||||
{
|
||||
ResultID[] output = null;
|
||||
|
||||
if (pInput != IntPtr.Zero && count > 0)
|
||||
{
|
||||
output = new ResultID[count];
|
||||
|
||||
int[] errors = OpcCom.Interop.GetInt32s(ref pInput, count, deallocate);
|
||||
|
||||
for (int ii = 0; ii < count; ii++)
|
||||
{
|
||||
output[ii] = OpcCom.Interop.GetResultID(errors[ii]);
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an array of COM SourceServer structures to .NET SourceServer objects.
|
||||
/// </summary>
|
||||
internal static SourceServer[] GetSourceServers(ref IntPtr pInput, int count, bool deallocate)
|
||||
{
|
||||
SourceServer[] output = null;
|
||||
|
||||
if (pInput != IntPtr.Zero && count > 0)
|
||||
{
|
||||
output = new SourceServer[count];
|
||||
|
||||
IntPtr pos = pInput;
|
||||
|
||||
for (int ii = 0; ii < count; ii++)
|
||||
{
|
||||
OpcRcw.Dx.SourceServer server = (OpcRcw.Dx.SourceServer)Marshal.PtrToStructure(pos, typeof(OpcRcw.Dx.SourceServer));
|
||||
|
||||
output[ii] = new SourceServer();
|
||||
|
||||
output[ii].ItemName = server.szItemName;
|
||||
output[ii].ItemPath = server.szItemPath;
|
||||
output[ii].Version = server.szVersion;
|
||||
output[ii].Name = server.szName;
|
||||
output[ii].Description = server.szDescription;
|
||||
output[ii].ServerType = server.szServerType;
|
||||
output[ii].ServerURL = server.szServerURL;
|
||||
output[ii].DefaultConnected = server.bDefaultSourceServerConnected != 0;
|
||||
|
||||
pos = (IntPtr)(pos.ToInt64() + Marshal.SizeOf(typeof(OpcRcw.Dx.SourceServer)));
|
||||
}
|
||||
|
||||
if (deallocate)
|
||||
{
|
||||
Marshal.FreeCoTaskMem(pInput);
|
||||
pInput = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an array of .NET SourceServer objects to COM SourceServer structures.
|
||||
/// </summary>
|
||||
internal static OpcRcw.Dx.SourceServer[] GetSourceServers(SourceServer[] input)
|
||||
{
|
||||
OpcRcw.Dx.SourceServer[] output = null;
|
||||
|
||||
if (input != null && input.Length > 0)
|
||||
{
|
||||
output = new OpcRcw.Dx.SourceServer[input.Length];
|
||||
|
||||
for (int ii = 0; ii < input.Length; ii++)
|
||||
{
|
||||
output[ii] = new OpcRcw.Dx.SourceServer();
|
||||
|
||||
output[ii].dwMask = (uint)OpcRcw.Dx.Mask.All;
|
||||
output[ii].szItemName = input[ii].ItemName;
|
||||
output[ii].szItemPath = input[ii].ItemPath;
|
||||
output[ii].szVersion = input[ii].Version;
|
||||
output[ii].szName = input[ii].Name;
|
||||
output[ii].szDescription = input[ii].Description;
|
||||
output[ii].szServerType = input[ii].ServerType;
|
||||
output[ii].szServerURL = input[ii].ServerURL;
|
||||
output[ii].bDefaultSourceServerConnected = (input[ii].DefaultConnected)?1:0;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an array of COM DXGeneralResponse structure to a .NET GeneralResponse object.
|
||||
/// </summary>
|
||||
internal static GeneralResponse GetGeneralResponse(OpcRcw.Dx.DXGeneralResponse input, bool deallocate)
|
||||
{
|
||||
Opc.Dx.IdentifiedResult[] results = Interop.GetIdentifiedResults(ref input.pIdentifiedResults, input.dwCount, deallocate);
|
||||
|
||||
return new GeneralResponse(input.szConfigurationVersion, results);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an array of COM IdentifiedResult structures to .NET IdentifiedResult objects.
|
||||
/// </summary>
|
||||
internal static Opc.Dx.IdentifiedResult[] GetIdentifiedResults(ref IntPtr pInput, int count, bool deallocate)
|
||||
{
|
||||
Opc.Dx.IdentifiedResult[] output = null;
|
||||
|
||||
if (pInput != IntPtr.Zero && count > 0)
|
||||
{
|
||||
output = new Opc.Dx.IdentifiedResult[count];
|
||||
|
||||
IntPtr pos = pInput;
|
||||
|
||||
for (int ii = 0; ii < count; ii++)
|
||||
{
|
||||
OpcRcw.Dx.IdentifiedResult result = (OpcRcw.Dx.IdentifiedResult)Marshal.PtrToStructure(pos, typeof(OpcRcw.Dx.IdentifiedResult));
|
||||
|
||||
output[ii] = new Opc.Dx.IdentifiedResult();
|
||||
|
||||
output[ii].ItemName = result.szItemName;
|
||||
output[ii].ItemPath = result.szItemPath;
|
||||
output[ii].Version = result.szVersion;
|
||||
output[ii].ResultID = OpcCom.Interop.GetResultID(result.hResultCode);
|
||||
|
||||
if (deallocate)
|
||||
{
|
||||
Marshal.DestroyStructure(pos, typeof(OpcRcw.Dx.IdentifiedResult));
|
||||
}
|
||||
|
||||
pos = (IntPtr)(pos.ToInt64() + Marshal.SizeOf(typeof(OpcRcw.Dx.IdentifiedResult)));
|
||||
}
|
||||
|
||||
if (deallocate)
|
||||
{
|
||||
Marshal.FreeCoTaskMem(pInput);
|
||||
pInput = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an array of COM DXConnection structures to .NET DXConnection objects.
|
||||
/// </summary>
|
||||
internal static DXConnection[] GetDXConnections(ref IntPtr pInput, int count, bool deallocate)
|
||||
{
|
||||
DXConnection[] output = null;
|
||||
|
||||
if (pInput != IntPtr.Zero && count > 0)
|
||||
{
|
||||
output = new DXConnection[count];
|
||||
|
||||
IntPtr pos = pInput;
|
||||
|
||||
for (int ii = 0; ii < count; ii++)
|
||||
{
|
||||
OpcRcw.Dx.DXConnection connection = (OpcRcw.Dx.DXConnection)Marshal.PtrToStructure(pos, typeof(OpcRcw.Dx.DXConnection));
|
||||
|
||||
output[ii] = GetDXConnection(connection, deallocate);
|
||||
|
||||
if (deallocate)
|
||||
{
|
||||
Marshal.DestroyStructure(pos, typeof(OpcRcw.Dx.DXConnection));
|
||||
}
|
||||
|
||||
pos = (IntPtr)(pos.ToInt64() + Marshal.SizeOf(typeof(OpcRcw.Dx.DXConnection)));
|
||||
}
|
||||
|
||||
if (deallocate)
|
||||
{
|
||||
Marshal.FreeCoTaskMem(pInput);
|
||||
pInput = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an array of .NET DXConnection objects to COM DXConnection structures.
|
||||
/// </summary>
|
||||
internal static OpcRcw.Dx.DXConnection[] GetDXConnections(DXConnection[] input)
|
||||
{
|
||||
OpcRcw.Dx.DXConnection[] output = null;
|
||||
|
||||
if (input != null && input.Length > 0)
|
||||
{
|
||||
output = new OpcRcw.Dx.DXConnection[input.Length];
|
||||
|
||||
for (int ii = 0; ii < input.Length; ii++)
|
||||
{
|
||||
output[ii] = GetDXConnection(input[ii]);
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a .NET DXConnection object to COM DXConnection structure.
|
||||
/// </summary>
|
||||
internal static OpcRcw.Dx.DXConnection GetDXConnection(DXConnection input)
|
||||
{
|
||||
OpcRcw.Dx.DXConnection output = new OpcRcw.Dx.DXConnection();
|
||||
|
||||
// set output default values.
|
||||
output.dwMask = 0;
|
||||
output.szItemPath = null;
|
||||
output.szItemName = null;
|
||||
output.szVersion = null;
|
||||
output.dwBrowsePathCount = 0;
|
||||
output.pszBrowsePaths = IntPtr.Zero;
|
||||
output.szName = null;
|
||||
output.szDescription = null;
|
||||
output.szKeyword = null;
|
||||
output.bDefaultSourceItemConnected = 0;
|
||||
output.bDefaultTargetItemConnected = 0;
|
||||
output.bDefaultOverridden = 0;
|
||||
output.vDefaultOverrideValue = null;
|
||||
output.vSubstituteValue = null;
|
||||
output.bEnableSubstituteValue = 0;
|
||||
output.szTargetItemPath = null;
|
||||
output.szTargetItemName = null;
|
||||
output.szSourceServerName = null;
|
||||
output.szSourceItemPath = null;
|
||||
output.szSourceItemName = null;
|
||||
output.dwSourceItemQueueSize = 0;
|
||||
output.dwUpdateRate = 0;
|
||||
output.fltDeadBand = 0;
|
||||
output.szVendorData = null;
|
||||
|
||||
// item name
|
||||
if (input.ItemName != null)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.ItemName;
|
||||
output.szItemName = input.ItemName;
|
||||
}
|
||||
|
||||
// item path
|
||||
if (input.ItemPath != null)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.ItemPath;
|
||||
output.szItemPath = input.ItemPath;
|
||||
}
|
||||
|
||||
// version
|
||||
if (input.Version != null)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.Version;
|
||||
output.szVersion = input.Version;
|
||||
}
|
||||
|
||||
// browse paths
|
||||
if (input.BrowsePaths.Count > 0)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.BrowsePaths;
|
||||
output.dwBrowsePathCount = input.BrowsePaths.Count;
|
||||
output.pszBrowsePaths = OpcCom.Interop.GetUnicodeStrings(input.BrowsePaths.ToArray());
|
||||
}
|
||||
|
||||
// name
|
||||
if (input.Name != null)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.Name;
|
||||
output.szName = input.Name;
|
||||
}
|
||||
|
||||
// description
|
||||
if (input.Description != null)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.Description;
|
||||
output.szDescription = input.Description;
|
||||
}
|
||||
|
||||
// keyword
|
||||
if (input.Keyword != null)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.Keyword;
|
||||
output.szKeyword = input.Keyword;
|
||||
}
|
||||
|
||||
// default source item connected
|
||||
if (input.DefaultSourceItemConnectedSpecified)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.DefaultSourceItemConnected;
|
||||
output.bDefaultSourceItemConnected = (input.DefaultSourceItemConnected)?1:0;
|
||||
}
|
||||
|
||||
// default target item connected
|
||||
if (input.DefaultTargetItemConnectedSpecified)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.DefaultTargetItemConnected;
|
||||
output.bDefaultTargetItemConnected = (input.DefaultTargetItemConnected)?1:0;
|
||||
}
|
||||
|
||||
// default overridden
|
||||
if (input.DefaultOverriddenSpecified)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.DefaultOverridden;
|
||||
output.bDefaultOverridden = (input.DefaultOverridden)?1:0;
|
||||
}
|
||||
|
||||
// default override value
|
||||
if (input.DefaultOverrideValue != null)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.DefaultOverrideValue;
|
||||
output.vDefaultOverrideValue = input.DefaultOverrideValue;
|
||||
}
|
||||
|
||||
// substitute value
|
||||
if (input.SubstituteValue != null)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.SubstituteValue;
|
||||
output.vSubstituteValue = input.SubstituteValue;
|
||||
}
|
||||
|
||||
// enable substitute value
|
||||
if (input.EnableSubstituteValueSpecified)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.EnableSubstituteValue;
|
||||
output.bEnableSubstituteValue = (input.EnableSubstituteValue)?1:0;
|
||||
}
|
||||
|
||||
// target item name
|
||||
if (input.TargetItemName != null)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.TargetItemName;
|
||||
output.szTargetItemName = input.TargetItemName;
|
||||
}
|
||||
|
||||
// target item path
|
||||
if (input.TargetItemPath != null)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.TargetItemPath;
|
||||
output.szTargetItemPath = input.TargetItemPath;
|
||||
}
|
||||
|
||||
// source server name
|
||||
if (input.SourceServerName != null)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.SourceServerName;
|
||||
output.szSourceServerName = input.SourceServerName;
|
||||
}
|
||||
|
||||
// source item name
|
||||
if (input.SourceItemName != null)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.SourceItemName;
|
||||
output.szSourceItemName = input.SourceItemName;
|
||||
}
|
||||
|
||||
// source item path
|
||||
if (input.SourceItemPath != null)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.SourceItemPath;
|
||||
output.szSourceItemPath = input.SourceItemPath;
|
||||
}
|
||||
|
||||
// source item queue size
|
||||
if (input.SourceItemQueueSizeSpecified)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.SourceItemQueueSize;
|
||||
output.dwSourceItemQueueSize = input.SourceItemQueueSize;
|
||||
}
|
||||
|
||||
// update rate
|
||||
if (input.UpdateRateSpecified)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.UpdateRate;
|
||||
output.dwUpdateRate = input.UpdateRate;
|
||||
}
|
||||
|
||||
// deadband
|
||||
if (input.DeadbandSpecified)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.DeadBand;
|
||||
output.fltDeadBand = input.Deadband;
|
||||
}
|
||||
|
||||
// vendor data
|
||||
if (input.VendorData != null)
|
||||
{
|
||||
output.dwMask |= (uint)OpcRcw.Dx.Mask.VendorData;
|
||||
output.szVendorData = input.VendorData;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a COM DXConnection structure to a .NET DXConnection object.
|
||||
/// </summary>
|
||||
internal static DXConnection GetDXConnection(OpcRcw.Dx.DXConnection input, bool deallocate)
|
||||
{
|
||||
DXConnection output = new DXConnection();
|
||||
|
||||
// set output default values.
|
||||
output.ItemPath = null;
|
||||
output.ItemName = null;
|
||||
output.Version = null;
|
||||
output.BrowsePaths.Clear();
|
||||
output.Name = null;
|
||||
output.Description = null;
|
||||
output.Keyword = null;
|
||||
output.DefaultSourceItemConnected = false;
|
||||
output.DefaultSourceItemConnectedSpecified = false;
|
||||
output.DefaultTargetItemConnected = false;
|
||||
output.DefaultTargetItemConnectedSpecified = false;
|
||||
output.DefaultOverridden = false;
|
||||
output.DefaultOverriddenSpecified = false;
|
||||
output.DefaultOverrideValue = null;
|
||||
output.SubstituteValue = null;
|
||||
output.EnableSubstituteValue = false;
|
||||
output.EnableSubstituteValueSpecified = false;
|
||||
output.TargetItemPath = null;
|
||||
output.TargetItemName = null;
|
||||
output.SourceServerName = null;
|
||||
output.SourceItemPath = null;
|
||||
output.SourceItemName = null;
|
||||
output.SourceItemQueueSize = 0;
|
||||
output.SourceItemQueueSizeSpecified = false;
|
||||
output.UpdateRate = 0;
|
||||
output.UpdateRateSpecified = false;
|
||||
output.Deadband = 0;
|
||||
output.DeadbandSpecified = false;
|
||||
output.VendorData = null;
|
||||
|
||||
// item name
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.ItemName) != 0)
|
||||
{
|
||||
output.ItemName = input.szItemName;
|
||||
}
|
||||
|
||||
// item path
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.ItemPath) != 0)
|
||||
{
|
||||
output.ItemPath = input.szItemPath;
|
||||
}
|
||||
|
||||
// version
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.Version) != 0)
|
||||
{
|
||||
output.Version = input.szVersion;
|
||||
}
|
||||
|
||||
// browse paths
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.BrowsePaths) != 0)
|
||||
{
|
||||
string[] browsePaths = OpcCom.Interop.GetUnicodeStrings(ref input.pszBrowsePaths, input.dwBrowsePathCount, deallocate);
|
||||
|
||||
if (browsePaths != null)
|
||||
{
|
||||
output.BrowsePaths.AddRange(browsePaths);
|
||||
}
|
||||
}
|
||||
|
||||
// name
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.Name) != 0)
|
||||
{
|
||||
output.Name = input.szName;
|
||||
}
|
||||
|
||||
// description
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.Description) != 0)
|
||||
{
|
||||
output.Description = input.szDescription;
|
||||
}
|
||||
|
||||
// keyword
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.Keyword) != 0)
|
||||
{
|
||||
output.Keyword = input.szKeyword;
|
||||
}
|
||||
|
||||
// default source item connected
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.DefaultSourceItemConnected) != 0)
|
||||
{
|
||||
output.DefaultSourceItemConnected = input.bDefaultSourceItemConnected != 0;
|
||||
output.DefaultSourceItemConnectedSpecified = true;
|
||||
}
|
||||
|
||||
// default target item connected
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.DefaultTargetItemConnected) != 0)
|
||||
{
|
||||
output.DefaultTargetItemConnected = input.bDefaultTargetItemConnected != 0;
|
||||
output.DefaultTargetItemConnectedSpecified = true;
|
||||
}
|
||||
|
||||
// default overridden
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.DefaultOverridden) != 0)
|
||||
{
|
||||
output.DefaultOverridden = input.bDefaultOverridden != 0;
|
||||
output.DefaultOverriddenSpecified = true;
|
||||
}
|
||||
|
||||
// default override value
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.DefaultOverrideValue) != 0)
|
||||
{
|
||||
output.DefaultOverrideValue = input.vDefaultOverrideValue;
|
||||
}
|
||||
|
||||
// substitute value
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.SubstituteValue) != 0)
|
||||
{
|
||||
output.SubstituteValue = input.vSubstituteValue;
|
||||
}
|
||||
|
||||
// enable substitute value
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.EnableSubstituteValue) != 0)
|
||||
{
|
||||
output.EnableSubstituteValue = input.bEnableSubstituteValue != 0;
|
||||
output.EnableSubstituteValueSpecified = true;
|
||||
}
|
||||
|
||||
// target item name
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.TargetItemName) != 0)
|
||||
{
|
||||
output.TargetItemName = input.szTargetItemName;
|
||||
}
|
||||
|
||||
// target item path
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.TargetItemPath) != 0)
|
||||
{
|
||||
output.TargetItemPath = input.szTargetItemPath;
|
||||
}
|
||||
|
||||
// source server name
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.SourceServerName) != 0)
|
||||
{
|
||||
output.SourceServerName = input.szSourceServerName;
|
||||
}
|
||||
|
||||
// source item name
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.SourceItemName) != 0)
|
||||
{
|
||||
output.SourceItemName = input.szSourceItemName;
|
||||
}
|
||||
|
||||
// source item path
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.SourceItemPath) != 0)
|
||||
{
|
||||
output.SourceItemPath = input.szSourceItemPath;
|
||||
}
|
||||
|
||||
// source item queue size
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.SourceItemQueueSize) != 0)
|
||||
{
|
||||
output.SourceItemQueueSize = input.dwSourceItemQueueSize;
|
||||
output.SourceItemQueueSizeSpecified = true;
|
||||
}
|
||||
|
||||
// update rate
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.UpdateRate) != 0)
|
||||
{
|
||||
output.UpdateRate = input.dwUpdateRate;
|
||||
output.UpdateRateSpecified = true;
|
||||
}
|
||||
|
||||
// deadband
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.DeadBand) != 0)
|
||||
{
|
||||
output.Deadband = input.fltDeadBand;
|
||||
output.DeadbandSpecified = true;
|
||||
}
|
||||
|
||||
// vendor data
|
||||
if ((input.dwMask & (uint)OpcRcw.Dx.Mask.VendorData) != 0)
|
||||
{
|
||||
output.VendorData = input.szVendorData;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an array of .NET ItemIdentifier objects to COM ItemIdentifier structures.
|
||||
/// </summary>
|
||||
internal static OpcRcw.Dx.ItemIdentifier[] GetItemIdentifiers(Opc.Dx.ItemIdentifier[] input)
|
||||
{
|
||||
OpcRcw.Dx.ItemIdentifier[] output = null;
|
||||
|
||||
if (input != null && input.Length > 0)
|
||||
{
|
||||
output = new OpcRcw.Dx.ItemIdentifier[input.Length];
|
||||
|
||||
for (int ii = 0; ii < input.Length; ii++)
|
||||
{
|
||||
output[ii] = new OpcRcw.Dx.ItemIdentifier();
|
||||
|
||||
output[ii].szItemName = input[ii].ItemName;
|
||||
output[ii].szItemPath = input[ii].ItemPath;
|
||||
output[ii].szVersion = input[ii].Version;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
51
Technosoftware/DaAeHdaClient.Com/Ae/Result.cs
Normal file
51
Technosoftware/DaAeHdaClient.Com/Ae/Result.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
#region Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
|
||||
// Web: https://www.technosoftware.com
|
||||
//
|
||||
// The source code in this file is covered under a dual-license scenario:
|
||||
// - Owner of a purchased license: SCLA 1.0
|
||||
// - GPL V3: everybody else
|
||||
//
|
||||
// SCLA license terms accompanied with this source code.
|
||||
// See SCLA 1.0: https://technosoftware.com/license/Source_Code_License_Agreement.pdf
|
||||
//
|
||||
// GNU General Public License as published by the Free Software Foundation;
|
||||
// version 3 of the License are accompanied with this source code.
|
||||
// See https://technosoftware.com/license/GPLv3License.txt
|
||||
//
|
||||
// This source code is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//-----------------------------------------------------------------------------
|
||||
#endregion Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
|
||||
|
||||
#region Using Directives
|
||||
|
||||
#endregion
|
||||
|
||||
namespace Technosoftware.DaAeHdaClient.Com.Ae
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines all well known COM AE HRESULT codes.
|
||||
/// </summary>
|
||||
internal struct Result
|
||||
{
|
||||
/// <remarks/>
|
||||
public const int S_ALREADYACKED = +0x00040200; // 0x00040200
|
||||
/// <remarks/>
|
||||
public const int S_INVALIDBUFFERTIME = +0x00040201; // 0x00040201
|
||||
/// <remarks/>
|
||||
public const int S_INVALIDMAXSIZE = +0x00040202; // 0x00040202
|
||||
/// <remarks/>
|
||||
public const int S_INVALIDKEEPALIVETIME = +0x00040203; // 0x00040203
|
||||
/// <remarks/>
|
||||
public const int E_INVALIDBRANCHNAME = -0x3FFBFDFD; // 0xC0040203
|
||||
/// <remarks/>
|
||||
public const int E_INVALIDTIME = -0x3FFBFDFC; // 0xC0040204
|
||||
/// <remarks/>
|
||||
public const int E_BUSY = -0x3FFBFDFB; // 0xC0040205
|
||||
/// <remarks/>
|
||||
public const int E_NOINFO = -0x3FFBFDFA; // 0xC0040206
|
||||
}
|
||||
}
|
||||
1574
Technosoftware/DaAeHdaClient.Com/Ae/Server.cs
Normal file
1574
Technosoftware/DaAeHdaClient.Com/Ae/Server.cs
Normal file
File diff suppressed because it is too large
Load Diff
610
Technosoftware/DaAeHdaClient.Com/Ae/Subscription.cs
Normal file
610
Technosoftware/DaAeHdaClient.Com/Ae/Subscription.cs
Normal file
@@ -0,0 +1,610 @@
|
||||
#region Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
|
||||
// Web: https://www.technosoftware.com
|
||||
//
|
||||
// The source code in this file is covered under a dual-license scenario:
|
||||
// - Owner of a purchased license: SCLA 1.0
|
||||
// - GPL V3: everybody else
|
||||
//
|
||||
// SCLA license terms accompanied with this source code.
|
||||
// See SCLA 1.0: https://technosoftware.com/license/Source_Code_License_Agreement.pdf
|
||||
//
|
||||
// GNU General Public License as published by the Free Software Foundation;
|
||||
// version 3 of the License are accompanied with this source code.
|
||||
// See https://technosoftware.com/license/GPLv3License.txt
|
||||
//
|
||||
// This source code is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//-----------------------------------------------------------------------------
|
||||
#endregion Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
|
||||
|
||||
#region Using Directives
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Technosoftware.DaAeHdaClient.Ae;
|
||||
using Technosoftware.DaAeHdaClient.Utilities;
|
||||
using Technosoftware.OpcRcw.Ae;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace Technosoftware.DaAeHdaClient.Com.Ae
|
||||
{
|
||||
/// <summary>
|
||||
/// A .NET wrapper for a COM server that implements the AE subscription interfaces.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
internal class Subscription : ITsCAeSubscription
|
||||
{
|
||||
#region Constructors
|
||||
/// <summary>
|
||||
/// Initializes the object with the specified URL and COM server.
|
||||
/// </summary>
|
||||
internal Subscription(TsCAeSubscriptionState state, object subscription)
|
||||
{
|
||||
subscription_ = subscription;
|
||||
clientHandle_ = OpcConvert.Clone(state.ClientHandle);
|
||||
supportsAe11_ = true;
|
||||
callback_ = new Callback(state.ClientHandle);
|
||||
|
||||
// check if the V1.1 interfaces are supported.
|
||||
try
|
||||
{
|
||||
var server = (IOPCEventSubscriptionMgt2)subscription_;
|
||||
}
|
||||
catch
|
||||
{
|
||||
supportsAe11_ = false;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IDisposable Members
|
||||
/// <summary>
|
||||
/// The finalizer.
|
||||
/// </summary>
|
||||
~Subscription()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose(bool disposing) executes in two distinct scenarios.
|
||||
/// If disposing equals true, the method has been called directly
|
||||
/// or indirectly by a user's code. Managed and unmanaged resources
|
||||
/// can be disposed.
|
||||
/// If disposing equals false, the method has been called by the
|
||||
/// runtime from inside the finalizer and you should not reference
|
||||
/// other objects. Only unmanaged resources can be disposed.
|
||||
/// </summary>
|
||||
/// <param name="disposing">If true managed and unmanaged resources can be disposed. If false only unmanaged resources.</param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposed_)
|
||||
{
|
||||
lock (lock_)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
// Free other state (managed objects).
|
||||
|
||||
if (subscription_ != null)
|
||||
{
|
||||
// close all connections.
|
||||
if (connection_ != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
connection_.Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore. COM Server probably no longer connected
|
||||
}
|
||||
connection_ = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Free your own state (unmanaged objects).
|
||||
// Set large fields to null.
|
||||
|
||||
if (subscription_ != null)
|
||||
{
|
||||
// release subscription object.
|
||||
try
|
||||
{
|
||||
Technosoftware.DaAeHdaClient.Com.Interop.ReleaseServer(subscription_);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore. COM Server probably no longer connected
|
||||
}
|
||||
subscription_ = null;
|
||||
}
|
||||
}
|
||||
|
||||
disposed_ = true;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Technosoftware.DaAeHdaClient.ISubscription Members
|
||||
/// <summary>
|
||||
/// An event to receive data change updates.
|
||||
/// </summary>
|
||||
public event TsCAeDataChangedEventHandler DataChangedEvent
|
||||
{
|
||||
add { lock (this) { Advise(); callback_.DataChangedEvent += value; } }
|
||||
remove { lock (this) { callback_.DataChangedEvent -= value; Unadvise(); } }
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// State Management
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current state of the subscription.
|
||||
/// </summary>
|
||||
/// <returns>The current state of the subscription.</returns>
|
||||
public TsCAeSubscriptionState GetState()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
// verify state and arguments.
|
||||
if (subscription_ == null) throw new NotConnectedException();
|
||||
|
||||
// initialize arguments.
|
||||
int pbActive;
|
||||
int pdwBufferTime;
|
||||
int pdwMaxSize;
|
||||
var pdwKeepAliveTime = 0;
|
||||
|
||||
// invoke COM method.
|
||||
try
|
||||
{
|
||||
((IOPCEventSubscriptionMgt)subscription_).GetState(
|
||||
out pbActive,
|
||||
out pdwBufferTime,
|
||||
out pdwMaxSize,
|
||||
out _);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw Technosoftware.DaAeHdaClient.Com.Interop.CreateException("IOPCEventSubscriptionMgt.GetState", e);
|
||||
}
|
||||
|
||||
// get keep alive.
|
||||
if (supportsAe11_)
|
||||
{
|
||||
try
|
||||
{
|
||||
((IOPCEventSubscriptionMgt2)subscription_).GetKeepAlive(out pdwKeepAliveTime);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw Technosoftware.DaAeHdaClient.Com.Interop.CreateException("IOPCEventSubscriptionMgt2.GetKeepAlive", e);
|
||||
}
|
||||
}
|
||||
|
||||
// build results
|
||||
var state = new TsCAeSubscriptionState
|
||||
{
|
||||
Active = pbActive != 0,
|
||||
ClientHandle = clientHandle_,
|
||||
BufferTime = pdwBufferTime,
|
||||
MaxSize = pdwMaxSize,
|
||||
KeepAlive = pdwKeepAliveTime
|
||||
};
|
||||
|
||||
// return results.
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the state of a subscription.
|
||||
/// </summary>
|
||||
/// <param name="masks">A bit mask that indicates which elements of the subscription state are changing.</param>
|
||||
/// <param name="state">The new subscription state.</param>
|
||||
/// <returns>The actual subscription state after applying the changes.</returns>
|
||||
public TsCAeSubscriptionState ModifyState(int masks, TsCAeSubscriptionState state)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
// verify state and arguments.
|
||||
if (subscription_ == null) throw new NotConnectedException();
|
||||
|
||||
// initialize arguments.
|
||||
var active = (state.Active) ? 1 : 0;
|
||||
|
||||
var hActive = GCHandle.Alloc(active, GCHandleType.Pinned);
|
||||
var hBufferTime = GCHandle.Alloc(state.BufferTime, GCHandleType.Pinned);
|
||||
var hMaxSize = GCHandle.Alloc(state.MaxSize, GCHandleType.Pinned);
|
||||
|
||||
var pbActive = ((masks & (int)TsCAeStateMask.Active) != 0) ? hActive.AddrOfPinnedObject() : IntPtr.Zero;
|
||||
var pdwBufferTime = ((masks & (int)TsCAeStateMask.BufferTime) != 0) ? hBufferTime.AddrOfPinnedObject() : IntPtr.Zero;
|
||||
var pdwMaxSize = ((masks & (int)TsCAeStateMask.MaxSize) != 0) ? hMaxSize.AddrOfPinnedObject() : IntPtr.Zero;
|
||||
|
||||
var phClientSubscription = 0;
|
||||
|
||||
// invoke COM method.
|
||||
try
|
||||
{
|
||||
((IOPCEventSubscriptionMgt)subscription_).SetState(
|
||||
pbActive,
|
||||
pdwBufferTime,
|
||||
pdwMaxSize,
|
||||
phClientSubscription,
|
||||
out _,
|
||||
out _);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw Technosoftware.DaAeHdaClient.Com.Interop.CreateException("IOPCEventSubscriptionMgt.SetState", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (hActive.IsAllocated) hActive.Free();
|
||||
if (hBufferTime.IsAllocated) hBufferTime.Free();
|
||||
if (hMaxSize.IsAllocated) hMaxSize.Free();
|
||||
}
|
||||
|
||||
// update keep alive.
|
||||
if (((masks & (int)TsCAeStateMask.KeepAlive) != 0) && supportsAe11_)
|
||||
{
|
||||
try
|
||||
{
|
||||
((IOPCEventSubscriptionMgt2)subscription_).SetKeepAlive(
|
||||
state.KeepAlive,
|
||||
out _);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw Technosoftware.DaAeHdaClient.Com.Interop.CreateException("IOPCEventSubscriptionMgt2.SetKeepAlive", e);
|
||||
}
|
||||
}
|
||||
|
||||
// return current state.
|
||||
return GetState();
|
||||
}
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// Filter Management
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current filters for the subscription.
|
||||
/// </summary>
|
||||
/// <returns>The current filters for the subscription.</returns>
|
||||
public TsCAeSubscriptionFilters GetFilters()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
// verify state and arguments.
|
||||
if (subscription_ == null) throw new NotConnectedException();
|
||||
|
||||
// initialize arguments.
|
||||
int pdwEventType;
|
||||
int pdwNumCategories;
|
||||
IntPtr ppidEventCategories;
|
||||
int pdwLowSeverity;
|
||||
int pdwHighSeverity;
|
||||
int pdwNumAreas;
|
||||
IntPtr ppsAreaList;
|
||||
int pdwNumSources;
|
||||
IntPtr ppsSourceList;
|
||||
|
||||
// invoke COM method.
|
||||
try
|
||||
{
|
||||
((IOPCEventSubscriptionMgt)subscription_).GetFilter(
|
||||
out pdwEventType,
|
||||
out pdwNumCategories,
|
||||
out ppidEventCategories,
|
||||
out pdwLowSeverity,
|
||||
out pdwHighSeverity,
|
||||
out pdwNumAreas,
|
||||
out ppsAreaList,
|
||||
out pdwNumSources,
|
||||
out ppsSourceList);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw Technosoftware.DaAeHdaClient.Com.Interop.CreateException("IOPCEventSubscriptionMgt.GetFilter", e);
|
||||
}
|
||||
|
||||
// marshal results
|
||||
var categoryIDs = Technosoftware.DaAeHdaClient.Com.Interop.GetInt32s(ref ppidEventCategories, pdwNumCategories, true);
|
||||
var areaIDs = Technosoftware.DaAeHdaClient.Com.Interop.GetUnicodeStrings(ref ppsAreaList, pdwNumAreas, true);
|
||||
var sourceIDs = Technosoftware.DaAeHdaClient.Com.Interop.GetUnicodeStrings(ref ppsSourceList, pdwNumSources, true);
|
||||
|
||||
// build results.
|
||||
var filters = new TsCAeSubscriptionFilters
|
||||
{
|
||||
EventTypes = pdwEventType,
|
||||
LowSeverity = pdwLowSeverity,
|
||||
HighSeverity = pdwHighSeverity
|
||||
};
|
||||
|
||||
|
||||
filters.Categories.AddRange(categoryIDs);
|
||||
filters.Areas.AddRange(areaIDs);
|
||||
filters.Sources.AddRange(sourceIDs);
|
||||
|
||||
// return results.
|
||||
return filters;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the current filters for the subscription.
|
||||
/// </summary>
|
||||
/// <param name="filters">The new filters to use for the subscription.</param>
|
||||
public void SetFilters(TsCAeSubscriptionFilters filters)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
// verify state and arguments.
|
||||
if (subscription_ == null) throw new NotConnectedException();
|
||||
|
||||
// invoke COM method.
|
||||
try
|
||||
{
|
||||
((IOPCEventSubscriptionMgt)subscription_).SetFilter(
|
||||
filters.EventTypes,
|
||||
filters.Categories.Count,
|
||||
filters.Categories.ToArray(),
|
||||
filters.LowSeverity,
|
||||
filters.HighSeverity,
|
||||
filters.Areas.Count,
|
||||
filters.Areas.ToArray(),
|
||||
filters.Sources.Count,
|
||||
filters.Sources.ToArray());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw Technosoftware.DaAeHdaClient.Com.Interop.CreateException("IOPCEventSubscriptionMgt.SetFilter", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// Attribute Management
|
||||
|
||||
/// <summary>
|
||||
/// Returns the set of attributes to return with event notifications.
|
||||
/// </summary>
|
||||
/// <returns>The set of attributes to returned with event notifications.</returns>
|
||||
public int[] GetReturnedAttributes(int eventCategory)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
// verify state and arguments.
|
||||
if (subscription_ == null) throw new NotConnectedException();
|
||||
|
||||
// initialize arguments.
|
||||
int pdwCount;
|
||||
IntPtr ppidAttributeIDs;
|
||||
|
||||
// invoke COM method.
|
||||
try
|
||||
{
|
||||
((IOPCEventSubscriptionMgt)subscription_).GetReturnedAttributes(
|
||||
eventCategory,
|
||||
out pdwCount,
|
||||
out ppidAttributeIDs);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw Technosoftware.DaAeHdaClient.Com.Interop.CreateException("IOPCEventSubscriptionMgt.GetReturnedAttributes", e);
|
||||
}
|
||||
|
||||
// marshal results
|
||||
var attributeIDs = Technosoftware.DaAeHdaClient.Com.Interop.GetInt32s(ref ppidAttributeIDs, pdwCount, true);
|
||||
|
||||
// return results.
|
||||
return attributeIDs;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Selects the set of attributes to return with event notifications.
|
||||
/// </summary>
|
||||
/// <param name="eventCategory">The specific event category for which the attributes apply.</param>
|
||||
/// <param name="attributeIDs">The list of attribute ids to return.</param>
|
||||
public void SelectReturnedAttributes(int eventCategory, int[] attributeIDs)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
// verify state and arguments.
|
||||
if (subscription_ == null) throw new NotConnectedException();
|
||||
|
||||
// invoke COM method.
|
||||
try
|
||||
{
|
||||
((IOPCEventSubscriptionMgt)subscription_).SelectReturnedAttributes(
|
||||
eventCategory,
|
||||
attributeIDs?.Length ?? 0,
|
||||
attributeIDs ?? Array.Empty<int>());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw Technosoftware.DaAeHdaClient.Com.Interop.CreateException("IOPCEventSubscriptionMgt.SelectReturnedAttributes", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// Refresh
|
||||
|
||||
/// <summary>
|
||||
/// Force a refresh for all active conditions and inactive, unacknowledged conditions whose event notifications match the filter of the event subscription.
|
||||
/// </summary>
|
||||
public void Refresh()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
// verify state and arguments.
|
||||
if (subscription_ == null) throw new NotConnectedException();
|
||||
|
||||
// invoke COM method.
|
||||
try
|
||||
{
|
||||
((IOPCEventSubscriptionMgt)subscription_).Refresh(0);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw Technosoftware.DaAeHdaClient.Com.Interop.CreateException("IOPCEventSubscriptionMgt.Refresh", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancels an outstanding refresh request.
|
||||
/// </summary>
|
||||
public void CancelRefresh()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
// verify state and arguments.
|
||||
if (subscription_ == null) throw new NotConnectedException();
|
||||
|
||||
// invoke COM method.
|
||||
try
|
||||
{
|
||||
((IOPCEventSubscriptionMgt)subscription_).CancelRefresh(0);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw Technosoftware.DaAeHdaClient.Com.Interop.CreateException("IOPCEventSubscriptionMgt.CancelRefresh", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IOPCEventSink Members
|
||||
/// <summary>
|
||||
/// A class that implements the IOPCEventSink interface.
|
||||
/// </summary>
|
||||
private class Callback : IOPCEventSink
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes the object with the containing subscription object.
|
||||
/// </summary>
|
||||
public Callback(object clientHandle)
|
||||
{
|
||||
clientHandle_ = clientHandle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised when data changed callbacks arrive.
|
||||
/// </summary>
|
||||
public event TsCAeDataChangedEventHandler DataChangedEvent
|
||||
{
|
||||
add { lock (this) { DataChangedEventHandler += value; } }
|
||||
remove { lock (this) { DataChangedEventHandler -= value; } }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when a data changed event is received.
|
||||
/// </summary>
|
||||
public void OnEvent(
|
||||
int hClientSubscription,
|
||||
int bRefresh,
|
||||
int bLastRefresh,
|
||||
int dwCount,
|
||||
ONEVENTSTRUCT[] pEvents)
|
||||
{
|
||||
LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.AlarmsConditions, true);
|
||||
try
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
// do nothing if no connections.
|
||||
if (DataChangedEventHandler == null) return;
|
||||
|
||||
// un marshal item values.
|
||||
var notifications = Interop.GetEventNotifications(pEvents);
|
||||
|
||||
foreach (var notification in notifications)
|
||||
{
|
||||
notification.ClientHandle = clientHandle_;
|
||||
}
|
||||
|
||||
if (!LicenseHandler.IsExpired)
|
||||
{
|
||||
// invoke the callback.
|
||||
DataChangedEventHandler?.Invoke(notifications, bRefresh != 0, bLastRefresh != 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Utils.Trace(e, "Exception '{0}' in event handler.", e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
#region Private Members
|
||||
private object clientHandle_;
|
||||
private event TsCAeDataChangedEventHandler DataChangedEventHandler;
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Establishes a connection point callback with the COM server.
|
||||
/// </summary>
|
||||
private void Advise()
|
||||
{
|
||||
if (connection_ == null)
|
||||
{
|
||||
connection_ = new ConnectionPoint(subscription_, typeof(IOPCEventSink).GUID);
|
||||
connection_.Advise(callback_);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes a connection point callback with the COM server.
|
||||
/// </summary>
|
||||
private void Unadvise()
|
||||
{
|
||||
if (connection_ != null)
|
||||
{
|
||||
if (connection_.Unadvise() == 0)
|
||||
{
|
||||
connection_.Dispose();
|
||||
connection_ = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Members
|
||||
private object subscription_;
|
||||
private object clientHandle_;
|
||||
private bool supportsAe11_ = true;
|
||||
private ConnectionPoint connection_;
|
||||
private Callback callback_;
|
||||
|
||||
/// <summary>
|
||||
/// The synchronization object for subscription access
|
||||
/// </summary>
|
||||
private static volatile object lock_ = new object();
|
||||
|
||||
private bool disposed_;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
97
Technosoftware/DaAeHdaClient.Com/ApplicationInstance.cs
Normal file
97
Technosoftware/DaAeHdaClient.Com/ApplicationInstance.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
#region Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
|
||||
// Web: https://www.technosoftware.com
|
||||
//
|
||||
// The source code in this file is covered under a dual-license scenario:
|
||||
// - Owner of a purchased license: SCLA 1.0
|
||||
// - GPL V3: everybody else
|
||||
//
|
||||
// SCLA license terms accompanied with this source code.
|
||||
// See SCLA 1.0: https://technosoftware.com/license/Source_Code_License_Agreement.pdf
|
||||
//
|
||||
// GNU General Public License as published by the Free Software Foundation;
|
||||
// version 3 of the License are accompanied with this source code.
|
||||
// See https://technosoftware.com/license/GPLv3License.txt
|
||||
//
|
||||
// This source code is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//-----------------------------------------------------------------------------
|
||||
#endregion Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
|
||||
|
||||
#region Using Directives
|
||||
using System;
|
||||
#endregion
|
||||
|
||||
namespace Technosoftware.DaAeHdaClient.Com
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages the license to enable the different product versions.
|
||||
/// </summary>
|
||||
public partial class ApplicationInstance
|
||||
{
|
||||
#region Nested Enums
|
||||
/// <summary>
|
||||
/// The possible authentication levels.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum AuthenticationLevel : uint
|
||||
{
|
||||
/// <summary>
|
||||
/// Tells DCOM to choose the authentication level using its normal security blanket negotiation algorithm.
|
||||
/// </summary>
|
||||
Default = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Performs no authentication.
|
||||
/// </summary>
|
||||
None = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Authenticates the credentials of the client only when the client establishes a relationship with the server. Datagram transports always use Packet instead.
|
||||
/// </summary>
|
||||
Connect = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Authenticates only at the beginning of each remote procedure call when the server receives the request. Datagram transports use Packet instead.
|
||||
/// </summary>
|
||||
Call = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Authenticates that all data received is from the expected client.
|
||||
/// </summary>
|
||||
Packet = 4,
|
||||
|
||||
/// <summary>
|
||||
/// Authenticates and verifies that none of the data transferred between client and server has been modified.
|
||||
/// </summary>
|
||||
Integrity = 5,
|
||||
|
||||
/// <summary>
|
||||
/// Authenticates all previous levels and encrypts the argument value of each remote procedure call.
|
||||
/// </summary>
|
||||
Privacy = 6,
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
/// <summary>
|
||||
/// Initializes COM security. This should be called directly at the beginning of an application and can only be called once.
|
||||
/// </summary>
|
||||
/// <param name="authenticationLevel">The default authentication level for the process. Both servers and clients use this parameter when they call CoInitializeSecurity. With the Windows Update KB5004442 a higher authentication level of Integrity must be used.</param>
|
||||
public static void InitializeSecurity(AuthenticationLevel authenticationLevel)
|
||||
{
|
||||
if (!InitializeSecurityCalled)
|
||||
{
|
||||
Com.Interop.InitializeSecurity((uint)authenticationLevel);
|
||||
InitializeSecurityCalled = true;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Internal Fields
|
||||
internal static bool InitializeSecurityCalled;
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
2791
Technosoftware/DaAeHdaClient.Com/ComUtils.cs
Normal file
2791
Technosoftware/DaAeHdaClient.Com/ComUtils.cs
Normal file
File diff suppressed because it is too large
Load Diff
107
Technosoftware/DaAeHdaClient.Com/ConnectionPoint.cs
Normal file
107
Technosoftware/DaAeHdaClient.Com/ConnectionPoint.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
#region Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
|
||||
// Web: https://www.technosoftware.com
|
||||
//
|
||||
// The source code in this file is covered under a dual-license scenario:
|
||||
// - Owner of a purchased license: SCLA 1.0
|
||||
// - GPL V3: everybody else
|
||||
//
|
||||
// SCLA license terms accompanied with this source code.
|
||||
// See SCLA 1.0: https://technosoftware.com/license/Source_Code_License_Agreement.pdf
|
||||
//
|
||||
// GNU General Public License as published by the Free Software Foundation;
|
||||
// version 3 of the License are accompanied with this source code.
|
||||
// See https://technosoftware.com/license/GPLv3License.txt
|
||||
//
|
||||
// This source code is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//-----------------------------------------------------------------------------
|
||||
#endregion Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
|
||||
|
||||
#region Using Directives
|
||||
using System;
|
||||
using Technosoftware.OpcRcw.Comn;
|
||||
#endregion
|
||||
|
||||
namespace Technosoftware.DaAeHdaClient.Com
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds and removes a connection point to a server.
|
||||
/// </summary>
|
||||
internal class ConnectionPoint : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// The COM server that supports connection points.
|
||||
/// </summary>
|
||||
private IConnectionPoint server_;
|
||||
|
||||
/// <summary>
|
||||
/// The id assigned to the connection by the COM server.
|
||||
/// </summary>
|
||||
private int cookie_;
|
||||
|
||||
/// <summary>
|
||||
/// The number of times Advise() has been called without a matching Unadvise().
|
||||
/// </summary>
|
||||
private int refs_;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the object by finding the specified connection point.
|
||||
/// </summary>
|
||||
public ConnectionPoint(object server, Guid iid)
|
||||
{
|
||||
((IConnectionPointContainer)server).FindConnectionPoint(ref iid, out server_);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (server_ != null)
|
||||
{
|
||||
while (Unadvise() > 0)
|
||||
{
|
||||
}
|
||||
try
|
||||
{
|
||||
Utilities.Interop.ReleaseServer(server_);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore. COM Server probably no longer connected
|
||||
}
|
||||
|
||||
server_ = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The cookie returned in the advise call.
|
||||
/// </summary>
|
||||
public int Cookie => cookie_;
|
||||
|
||||
//=====================================================================
|
||||
// IConnectionPoint
|
||||
|
||||
/// <summary>
|
||||
/// Establishes a connection, if necessary and increments the reference count.
|
||||
/// </summary>
|
||||
public int Advise(object callback)
|
||||
{
|
||||
if (refs_++ == 0) server_.Advise(callback, out cookie_);
|
||||
return refs_;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrements the reference count and closes the connection if no more references.
|
||||
/// </summary>
|
||||
public int Unadvise()
|
||||
{
|
||||
if (--refs_ == 0) server_.Unadvise(cookie_);
|
||||
return refs_;
|
||||
}
|
||||
}
|
||||
}
|
||||
60
Technosoftware/DaAeHdaClient.Com/Da/BrowsePosition.cs
Normal file
60
Technosoftware/DaAeHdaClient.Com/Da/BrowsePosition.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
#region Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
|
||||
// Web: https://www.technosoftware.com
|
||||
//
|
||||
// The source code in this file is covered under a dual-license scenario:
|
||||
// - Owner of a purchased license: SCLA 1.0
|
||||
// - GPL V3: everybody else
|
||||
//
|
||||
// SCLA license terms accompanied with this source code.
|
||||
// See SCLA 1.0: https://technosoftware.com/license/Source_Code_License_Agreement.pdf
|
||||
//
|
||||
// GNU General Public License as published by the Free Software Foundation;
|
||||
// version 3 of the License are accompanied with this source code.
|
||||
// See https://technosoftware.com/license/GPLv3License.txt
|
||||
//
|
||||
// This source code is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//-----------------------------------------------------------------------------
|
||||
#endregion Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
|
||||
|
||||
#region Using Directives
|
||||
using System;
|
||||
|
||||
using Technosoftware.DaAeHdaClient.Da;
|
||||
#endregion
|
||||
|
||||
namespace Technosoftware.DaAeHdaClient.Com.Da
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements an object that handles multi-step browse operations.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
internal class BrowsePosition : TsCDaBrowsePosition
|
||||
{
|
||||
/// <summary>
|
||||
/// The continuation point for a browse operation.
|
||||
/// </summary>
|
||||
internal string ContinuationPoint = null;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that elements that meet the filter criteria have not been returned.
|
||||
/// </summary>
|
||||
internal bool MoreElements = false;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a browse position
|
||||
/// </summary>
|
||||
internal BrowsePosition(
|
||||
OpcItem itemID,
|
||||
TsCDaBrowseFilters filters,
|
||||
string continuationPoint)
|
||||
:
|
||||
base(itemID, filters)
|
||||
{
|
||||
ContinuationPoint = continuationPoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
905
Technosoftware/DaAeHdaClient.Com/Da/Interop.cs
Normal file
905
Technosoftware/DaAeHdaClient.Com/Da/Interop.cs
Normal file
@@ -0,0 +1,905 @@
|
||||
#region Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
|
||||
// Web: https://www.technosoftware.com
|
||||
//
|
||||
// The source code in this file is covered under a dual-license scenario:
|
||||
// - Owner of a purchased license: SCLA 1.0
|
||||
// - GPL V3: everybody else
|
||||
//
|
||||
// SCLA license terms accompanied with this source code.
|
||||
// See SCLA 1.0: https://technosoftware.com/license/Source_Code_License_Agreement.pdf
|
||||
//
|
||||
// GNU General Public License as published by the Free Software Foundation;
|
||||
// version 3 of the License are accompanied with this source code.
|
||||
// See https://technosoftware.com/license/GPLv3License.txt
|
||||
//
|
||||
// This source code is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//-----------------------------------------------------------------------------
|
||||
#endregion Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
|
||||
|
||||
#region Using Directives
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using Technosoftware.DaAeHdaClient.Da;
|
||||
#endregion
|
||||
|
||||
#pragma warning disable 0618
|
||||
|
||||
namespace Technosoftware.DaAeHdaClient.Com.Da
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains state information for a single asynchronous Technosoftware.DaAeHdaClient.Com.Da.Interop.
|
||||
/// </summary>
|
||||
internal class Interop
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a standard FILETIME to an OpcRcw.Da.FILETIME structure.
|
||||
/// </summary>
|
||||
internal static OpcRcw.Da.FILETIME Convert(FILETIME input)
|
||||
{
|
||||
var output = new OpcRcw.Da.FILETIME();
|
||||
output.dwLowDateTime = input.dwLowDateTime;
|
||||
output.dwHighDateTime = input.dwHighDateTime;
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an OpcRcw.Da.FILETIME to a standard FILETIME structure.
|
||||
/// </summary>
|
||||
internal static FILETIME Convert(OpcRcw.Da.FILETIME input)
|
||||
{
|
||||
var output = new FILETIME();
|
||||
output.dwLowDateTime = input.dwLowDateTime;
|
||||
output.dwHighDateTime = input.dwHighDateTime;
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates and marshals a OPCSERVERSTATUS structure.
|
||||
/// </summary>
|
||||
internal static OpcRcw.Da.OPCSERVERSTATUS GetServerStatus(OpcServerStatus input, int groupCount)
|
||||
{
|
||||
var output = new OpcRcw.Da.OPCSERVERSTATUS();
|
||||
|
||||
if (input != null)
|
||||
{
|
||||
output.szVendorInfo = input.VendorInfo;
|
||||
output.wMajorVersion = 0;
|
||||
output.wMinorVersion = 0;
|
||||
output.wBuildNumber = 0;
|
||||
output.dwServerState = (OpcRcw.Da.OPCSERVERSTATE)input.ServerState;
|
||||
output.ftStartTime = Convert(Technosoftware.DaAeHdaClient.Com.Interop.GetFILETIME(input.StartTime));
|
||||
output.ftCurrentTime = Convert(Technosoftware.DaAeHdaClient.Com.Interop.GetFILETIME(input.CurrentTime));
|
||||
output.ftLastUpdateTime = Convert(Technosoftware.DaAeHdaClient.Com.Interop.GetFILETIME(input.LastUpdateTime));
|
||||
output.dwBandWidth = -1;
|
||||
output.dwGroupCount = groupCount;
|
||||
output.wReserved = 0;
|
||||
|
||||
if (input.ProductVersion != null)
|
||||
{
|
||||
var versions = input.ProductVersion.Split(new char[] { '.' });
|
||||
|
||||
if (versions.Length > 0)
|
||||
{
|
||||
try { output.wMajorVersion = System.Convert.ToInt16(versions[0]); }
|
||||
catch { output.wMajorVersion = 0; }
|
||||
}
|
||||
|
||||
if (versions.Length > 1)
|
||||
{
|
||||
try { output.wMinorVersion = System.Convert.ToInt16(versions[1]); }
|
||||
catch { output.wMinorVersion = 0; }
|
||||
}
|
||||
|
||||
output.wBuildNumber = 0;
|
||||
|
||||
for (var ii = 2; ii < versions.Length; ii++)
|
||||
{
|
||||
try
|
||||
{
|
||||
output.wBuildNumber = (short)(output.wBuildNumber * 100 + System.Convert.ToInt16(versions[ii]));
|
||||
}
|
||||
catch
|
||||
{
|
||||
output.wBuildNumber = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unmarshals and deallocates a OPCSERVERSTATUS structure.
|
||||
/// </summary>
|
||||
internal static OpcServerStatus GetServerStatus(ref IntPtr pInput, bool deallocate)
|
||||
{
|
||||
OpcServerStatus output = null;
|
||||
|
||||
if (pInput != IntPtr.Zero)
|
||||
{
|
||||
var status = (OpcRcw.Da.OPCSERVERSTATUS)Marshal.PtrToStructure(pInput, typeof(OpcRcw.Da.OPCSERVERSTATUS));
|
||||
|
||||
output = new OpcServerStatus();
|
||||
|
||||
output.VendorInfo = status.szVendorInfo;
|
||||
output.ProductVersion = string.Format("{0}.{1}.{2}", status.wMajorVersion, status.wMinorVersion, status.wBuildNumber);
|
||||
output.ServerState = (OpcServerState)status.dwServerState;
|
||||
output.StatusInfo = null;
|
||||
output.StartTime = Technosoftware.DaAeHdaClient.Com.Interop.GetFILETIME(Convert(status.ftStartTime));
|
||||
output.CurrentTime = Technosoftware.DaAeHdaClient.Com.Interop.GetFILETIME(Convert(status.ftCurrentTime));
|
||||
output.LastUpdateTime = Technosoftware.DaAeHdaClient.Com.Interop.GetFILETIME(Convert(status.ftLastUpdateTime));
|
||||
|
||||
if (deallocate)
|
||||
{
|
||||
Marshal.DestroyStructure(pInput, typeof(OpcRcw.Da.OPCSERVERSTATUS));
|
||||
Marshal.FreeCoTaskMem(pInput);
|
||||
pInput = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a browseFilter values to the COM equivalent.
|
||||
/// </summary>
|
||||
internal static OpcRcw.Da.OPCBROWSEFILTER GetBrowseFilter(TsCDaBrowseFilter input)
|
||||
{
|
||||
switch (input)
|
||||
{
|
||||
case TsCDaBrowseFilter.All: return OpcRcw.Da.OPCBROWSEFILTER.OPC_BROWSE_FILTER_ALL;
|
||||
case TsCDaBrowseFilter.Branch: return OpcRcw.Da.OPCBROWSEFILTER.OPC_BROWSE_FILTER_BRANCHES;
|
||||
case TsCDaBrowseFilter.Item: return OpcRcw.Da.OPCBROWSEFILTER.OPC_BROWSE_FILTER_ITEMS;
|
||||
}
|
||||
|
||||
return OpcRcw.Da.OPCBROWSEFILTER.OPC_BROWSE_FILTER_ALL;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a browseFilter values from the COM equivalent.
|
||||
/// </summary>
|
||||
internal static TsCDaBrowseFilter GetBrowseFilter(OpcRcw.Da.OPCBROWSEFILTER input)
|
||||
{
|
||||
switch (input)
|
||||
{
|
||||
case OpcRcw.Da.OPCBROWSEFILTER.OPC_BROWSE_FILTER_ALL: return TsCDaBrowseFilter.All;
|
||||
case OpcRcw.Da.OPCBROWSEFILTER.OPC_BROWSE_FILTER_BRANCHES: return TsCDaBrowseFilter.Branch;
|
||||
case OpcRcw.Da.OPCBROWSEFILTER.OPC_BROWSE_FILTER_ITEMS: return TsCDaBrowseFilter.Item;
|
||||
}
|
||||
|
||||
return TsCDaBrowseFilter.All;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates and marshals an array of HRESULT codes.
|
||||
/// </summary>
|
||||
internal static IntPtr GetHRESULTs(IOpcResult[] results)
|
||||
{
|
||||
// extract error codes from results.
|
||||
var errors = new int[results.Length];
|
||||
|
||||
for (var ii = 0; ii < results.Length; ii++)
|
||||
{
|
||||
if (results[ii] != null)
|
||||
{
|
||||
errors[ii] = Technosoftware.DaAeHdaClient.Com.Interop.GetResultID(results[ii].Result);
|
||||
}
|
||||
else
|
||||
{
|
||||
errors[ii] = Result.E_INVALIDHANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
// marshal error codes.
|
||||
var pErrors = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(int)) * results.Length);
|
||||
Marshal.Copy(errors, 0, pErrors, results.Length);
|
||||
|
||||
// return results.
|
||||
return pErrors;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unmarshals and deallocates an array of OPCBROWSEELEMENT structures.
|
||||
/// </summary>
|
||||
internal static TsCDaBrowseElement[] GetBrowseElements(ref IntPtr pInput, int count, bool deallocate)
|
||||
{
|
||||
TsCDaBrowseElement[] output = null;
|
||||
|
||||
if (pInput != IntPtr.Zero && count > 0)
|
||||
{
|
||||
output = new TsCDaBrowseElement[count];
|
||||
|
||||
var pos = pInput;
|
||||
|
||||
for (var ii = 0; ii < count; ii++)
|
||||
{
|
||||
output[ii] = GetBrowseElement(pos, deallocate);
|
||||
pos = (IntPtr)(pos.ToInt64() + Marshal.SizeOf(typeof(OpcRcw.Da.OPCBROWSEELEMENT)));
|
||||
}
|
||||
|
||||
if (deallocate)
|
||||
{
|
||||
Marshal.FreeCoTaskMem(pInput);
|
||||
pInput = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates and marshals an array of OPCBROWSEELEMENT structures.
|
||||
/// </summary>
|
||||
internal static IntPtr GetBrowseElements(TsCDaBrowseElement[] input, bool propertiesRequested)
|
||||
{
|
||||
var output = IntPtr.Zero;
|
||||
|
||||
if (input != null && input.Length > 0)
|
||||
{
|
||||
output = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(OpcRcw.Da.OPCBROWSEELEMENT)) * input.Length);
|
||||
|
||||
var pos = output;
|
||||
|
||||
for (var ii = 0; ii < input.Length; ii++)
|
||||
{
|
||||
var element = GetBrowseElement(input[ii], propertiesRequested);
|
||||
Marshal.StructureToPtr(element, pos, false);
|
||||
pos = (IntPtr)(pos.ToInt64() + Marshal.SizeOf(typeof(OpcRcw.Da.OPCBROWSEELEMENT)));
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unmarshals and deallocates a OPCBROWSEELEMENT structures.
|
||||
/// </summary>
|
||||
internal static TsCDaBrowseElement GetBrowseElement(IntPtr pInput, bool deallocate)
|
||||
{
|
||||
TsCDaBrowseElement output = null;
|
||||
|
||||
if (pInput != IntPtr.Zero)
|
||||
{
|
||||
var element = (OpcRcw.Da.OPCBROWSEELEMENT)Marshal.PtrToStructure(pInput, typeof(OpcRcw.Da.OPCBROWSEELEMENT));
|
||||
|
||||
output = new TsCDaBrowseElement();
|
||||
|
||||
output.Name = element.szName;
|
||||
output.ItemPath = null;
|
||||
output.ItemName = element.szItemID;
|
||||
output.IsItem = ((element.dwFlagValue & OpcRcw.Da.Constants.OPC_BROWSE_ISITEM) != 0);
|
||||
output.HasChildren = ((element.dwFlagValue & OpcRcw.Da.Constants.OPC_BROWSE_HASCHILDREN) != 0);
|
||||
output.Properties = GetItemProperties(ref element.ItemProperties, deallocate);
|
||||
|
||||
if (deallocate)
|
||||
{
|
||||
Marshal.DestroyStructure(pInput, typeof(OpcRcw.Da.OPCBROWSEELEMENT));
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates and marshals an OPCBROWSEELEMENT structure.
|
||||
/// </summary>
|
||||
internal static OpcRcw.Da.OPCBROWSEELEMENT GetBrowseElement(TsCDaBrowseElement input, bool propertiesRequested)
|
||||
{
|
||||
var output = new OpcRcw.Da.OPCBROWSEELEMENT();
|
||||
|
||||
if (input != null)
|
||||
{
|
||||
output.szName = input.Name;
|
||||
output.szItemID = input.ItemName;
|
||||
output.dwFlagValue = 0;
|
||||
output.ItemProperties = GetItemProperties(input.Properties);
|
||||
|
||||
if (input.IsItem)
|
||||
{
|
||||
output.dwFlagValue |= OpcRcw.Da.Constants.OPC_BROWSE_ISITEM;
|
||||
}
|
||||
|
||||
if (input.HasChildren)
|
||||
{
|
||||
output.dwFlagValue |= OpcRcw.Da.Constants.OPC_BROWSE_HASCHILDREN;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an array of property codes.
|
||||
/// </summary>
|
||||
internal static int[] GetPropertyIDs(TsDaPropertyID[] propertyIDs)
|
||||
{
|
||||
var output = new ArrayList();
|
||||
|
||||
if (propertyIDs != null)
|
||||
{
|
||||
foreach (var propertyID in propertyIDs)
|
||||
{
|
||||
output.Add(propertyID.Code);
|
||||
}
|
||||
}
|
||||
|
||||
return (int[])output.ToArray(typeof(int));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an array of property codes.
|
||||
/// </summary>
|
||||
internal static TsDaPropertyID[] GetPropertyIDs(int[] propertyIDs)
|
||||
{
|
||||
var output = new ArrayList();
|
||||
|
||||
if (propertyIDs != null)
|
||||
{
|
||||
foreach (var propertyID in propertyIDs)
|
||||
{
|
||||
output.Add(GetPropertyID(propertyID));
|
||||
}
|
||||
}
|
||||
|
||||
return (TsDaPropertyID[])output.ToArray(typeof(TsDaPropertyID));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unmarshals and deallocates an array of OPCITEMPROPERTIES structures.
|
||||
/// </summary>
|
||||
internal static TsCDaItemPropertyCollection[] GetItemPropertyCollections(ref IntPtr pInput, int count, bool deallocate)
|
||||
{
|
||||
TsCDaItemPropertyCollection[] output = null;
|
||||
|
||||
if (pInput != IntPtr.Zero && count > 0)
|
||||
{
|
||||
output = new TsCDaItemPropertyCollection[count];
|
||||
|
||||
var pos = pInput;
|
||||
|
||||
for (var ii = 0; ii < count; ii++)
|
||||
{
|
||||
var list = (OpcRcw.Da.OPCITEMPROPERTIES)Marshal.PtrToStructure(pos, typeof(OpcRcw.Da.OPCITEMPROPERTIES));
|
||||
|
||||
output[ii] = new TsCDaItemPropertyCollection();
|
||||
output[ii].ItemPath = null;
|
||||
output[ii].ItemName = null;
|
||||
output[ii].Result = Technosoftware.DaAeHdaClient.Com.Interop.GetResultID(list.hrErrorID);
|
||||
|
||||
var properties = GetItemProperties(ref list, deallocate);
|
||||
|
||||
if (properties != null)
|
||||
{
|
||||
output[ii].AddRange(properties);
|
||||
}
|
||||
|
||||
if (deallocate)
|
||||
{
|
||||
Marshal.DestroyStructure(pos, typeof(OpcRcw.Da.OPCITEMPROPERTIES));
|
||||
}
|
||||
|
||||
pos = (IntPtr)(pos.ToInt64() + Marshal.SizeOf(typeof(OpcRcw.Da.OPCITEMPROPERTIES)));
|
||||
}
|
||||
|
||||
if (deallocate)
|
||||
{
|
||||
Marshal.FreeCoTaskMem(pInput);
|
||||
pInput = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates and marshals an array of OPCITEMPROPERTIES structures.
|
||||
/// </summary>
|
||||
internal static IntPtr GetItemPropertyCollections(TsCDaItemPropertyCollection[] input)
|
||||
{
|
||||
var output = IntPtr.Zero;
|
||||
|
||||
if (input != null && input.Length > 0)
|
||||
{
|
||||
output = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(OpcRcw.Da.OPCITEMPROPERTIES)) * input.Length);
|
||||
|
||||
var pos = output;
|
||||
|
||||
for (var ii = 0; ii < input.Length; ii++)
|
||||
{
|
||||
var properties = new OpcRcw.Da.OPCITEMPROPERTIES();
|
||||
|
||||
if (input[ii].Count > 0)
|
||||
{
|
||||
properties = GetItemProperties((TsCDaItemProperty[])input[ii].ToArray(typeof(TsCDaItemProperty)));
|
||||
}
|
||||
|
||||
properties.hrErrorID = Technosoftware.DaAeHdaClient.Com.Interop.GetResultID(input[ii].Result);
|
||||
|
||||
Marshal.StructureToPtr(properties, pos, false);
|
||||
|
||||
pos = (IntPtr)(pos.ToInt64() + Marshal.SizeOf(typeof(OpcRcw.Da.OPCITEMPROPERTIES)));
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unmarshals and deallocates a OPCITEMPROPERTIES structures.
|
||||
/// </summary>
|
||||
internal static TsCDaItemProperty[] GetItemProperties(ref OpcRcw.Da.OPCITEMPROPERTIES input, bool deallocate)
|
||||
{
|
||||
TsCDaItemProperty[] output = null;
|
||||
|
||||
if (input.dwNumProperties > 0)
|
||||
{
|
||||
output = new TsCDaItemProperty[input.dwNumProperties];
|
||||
|
||||
var pos = input.pItemProperties;
|
||||
|
||||
for (var ii = 0; ii < output.Length; ii++)
|
||||
{
|
||||
try
|
||||
{
|
||||
output[ii] = GetItemProperty(pos, deallocate);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
output[ii] = new TsCDaItemProperty();
|
||||
output[ii].Description = e.Message;
|
||||
output[ii].Result = OpcResult.E_FAIL;
|
||||
}
|
||||
|
||||
pos = (IntPtr)(pos.ToInt64() + Marshal.SizeOf(typeof(OpcRcw.Da.OPCITEMPROPERTY)));
|
||||
}
|
||||
|
||||
if (deallocate)
|
||||
{
|
||||
Marshal.FreeCoTaskMem(input.pItemProperties);
|
||||
input.pItemProperties = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates and marshals an array of OPCITEMPROPERTIES structures.
|
||||
/// </summary>
|
||||
internal static OpcRcw.Da.OPCITEMPROPERTIES GetItemProperties(TsCDaItemProperty[] input)
|
||||
{
|
||||
var output = new OpcRcw.Da.OPCITEMPROPERTIES();
|
||||
|
||||
if (input != null && input.Length > 0)
|
||||
{
|
||||
output.hrErrorID = Result.S_OK;
|
||||
output.dwReserved = 0;
|
||||
output.dwNumProperties = input.Length;
|
||||
output.pItemProperties = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(OpcRcw.Da.OPCITEMPROPERTY)) * input.Length);
|
||||
|
||||
var error = false;
|
||||
|
||||
var pos = output.pItemProperties;
|
||||
|
||||
for (var ii = 0; ii < input.Length; ii++)
|
||||
{
|
||||
var property = GetItemProperty(input[ii]);
|
||||
Marshal.StructureToPtr(property, pos, false);
|
||||
pos = (IntPtr)(pos.ToInt64() + Marshal.SizeOf(typeof(OpcRcw.Da.OPCITEMPROPERTY)));
|
||||
|
||||
if (input[ii].Result.Failed())
|
||||
{
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
||||
// set flag indicating one or more properties contained errors.
|
||||
if (error)
|
||||
{
|
||||
output.hrErrorID = Result.S_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unmarshals and deallocates a OPCITEMPROPERTY structures.
|
||||
/// </summary>
|
||||
internal static TsCDaItemProperty GetItemProperty(IntPtr pInput, bool deallocate)
|
||||
{
|
||||
TsCDaItemProperty output = null;
|
||||
|
||||
if (pInput != IntPtr.Zero)
|
||||
{
|
||||
try
|
||||
{
|
||||
var property = (OpcRcw.Da.OPCITEMPROPERTY)Marshal.PtrToStructure(pInput, typeof(OpcRcw.Da.OPCITEMPROPERTY));
|
||||
|
||||
output = new TsCDaItemProperty();
|
||||
|
||||
output.ID = GetPropertyID(property.dwPropertyID);
|
||||
output.Description = property.szDescription;
|
||||
output.DataType = Technosoftware.DaAeHdaClient.Com.Interop.GetType((VarEnum)property.vtDataType);
|
||||
output.ItemPath = null;
|
||||
output.ItemName = property.szItemID;
|
||||
output.Value = UnmarshalPropertyValue(output.ID, property.vValue);
|
||||
output.Result = Technosoftware.DaAeHdaClient.Com.Interop.GetResultID(property.hrErrorID);
|
||||
|
||||
// convert COM DA code to unified DA code.
|
||||
if (property.hrErrorID == Result.E_BADRIGHTS) output.Result = new OpcResult(OpcResult.Da.E_WRITEONLY, Result.E_BADRIGHTS);
|
||||
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
if (deallocate)
|
||||
{
|
||||
Marshal.DestroyStructure(pInput, typeof(OpcRcw.Da.OPCITEMPROPERTY));
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates and marshals an arary of OPCITEMPROPERTY structures.
|
||||
/// </summary>
|
||||
internal static OpcRcw.Da.OPCITEMPROPERTY GetItemProperty(TsCDaItemProperty input)
|
||||
{
|
||||
var output = new OpcRcw.Da.OPCITEMPROPERTY();
|
||||
|
||||
if (input != null)
|
||||
{
|
||||
output.dwPropertyID = input.ID.Code;
|
||||
output.szDescription = input.Description;
|
||||
output.vtDataType = (short)Technosoftware.DaAeHdaClient.Com.Interop.GetType(input.DataType);
|
||||
output.vValue = MarshalPropertyValue(input.ID, input.Value);
|
||||
output.wReserved = 0;
|
||||
output.hrErrorID = Technosoftware.DaAeHdaClient.Com.Interop.GetResultID(input.Result);
|
||||
|
||||
// set the property data type.
|
||||
var description = TsDaPropertyDescription.Find(input.ID);
|
||||
|
||||
if (description != null)
|
||||
{
|
||||
output.vtDataType = (short)Technosoftware.DaAeHdaClient.Com.Interop.GetType(description.Type);
|
||||
}
|
||||
|
||||
// convert unified DA code to COM DA code.
|
||||
if (input.Result == OpcResult.Da.E_WRITEONLY) output.hrErrorID = Result.E_BADRIGHTS;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <remarks/>
|
||||
public static TsDaPropertyID GetPropertyID(int input)
|
||||
{
|
||||
var fields = typeof(TsDaProperty).GetFields(BindingFlags.Static | BindingFlags.Public);
|
||||
|
||||
foreach (var field in fields)
|
||||
{
|
||||
var property = (TsDaPropertyID)field.GetValue(typeof(TsDaPropertyID));
|
||||
|
||||
if (input == property.Code)
|
||||
{
|
||||
return property;
|
||||
}
|
||||
}
|
||||
|
||||
return new TsDaPropertyID(input);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the property value to a type supported by the unified interface.
|
||||
/// </summary>
|
||||
internal static object UnmarshalPropertyValue(TsDaPropertyID propertyID, object input)
|
||||
{
|
||||
if (input == null) return null;
|
||||
|
||||
try
|
||||
{
|
||||
if (propertyID == TsDaProperty.DATATYPE)
|
||||
{
|
||||
return Technosoftware.DaAeHdaClient.Com.Interop.GetType((VarEnum)System.Convert.ToUInt16(input));
|
||||
}
|
||||
|
||||
if (propertyID == TsDaProperty.ACCESSRIGHTS)
|
||||
{
|
||||
switch (System.Convert.ToInt32(input))
|
||||
{
|
||||
case OpcRcw.Da.Constants.OPC_READABLE: return TsDaAccessRights.Readable;
|
||||
case OpcRcw.Da.Constants.OPC_WRITEABLE: return TsDaAccessRights.Writable;
|
||||
|
||||
case OpcRcw.Da.Constants.OPC_READABLE | OpcRcw.Da.Constants.OPC_WRITEABLE:
|
||||
{
|
||||
return TsDaAccessRights.ReadWritable;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (propertyID == TsDaProperty.EUTYPE)
|
||||
{
|
||||
switch ((OpcRcw.Da.OPCEUTYPE)input)
|
||||
{
|
||||
case OpcRcw.Da.OPCEUTYPE.OPC_NOENUM: return TsDaEuType.NoEnum;
|
||||
case OpcRcw.Da.OPCEUTYPE.OPC_ANALOG: return TsDaEuType.Analog;
|
||||
case OpcRcw.Da.OPCEUTYPE.OPC_ENUMERATED: return TsDaEuType.Enumerated;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (propertyID == TsDaProperty.QUALITY)
|
||||
{
|
||||
return new TsCDaQuality(System.Convert.ToInt16(input));
|
||||
}
|
||||
|
||||
// convert UTC time in property to local time for the unified DA interface.
|
||||
if (propertyID == TsDaProperty.TIMESTAMP)
|
||||
{
|
||||
if (input.GetType() == typeof(DateTime))
|
||||
{
|
||||
var dateTime = (DateTime)input;
|
||||
|
||||
if (dateTime != DateTime.MinValue)
|
||||
{
|
||||
return dateTime.ToLocalTime();
|
||||
}
|
||||
|
||||
return dateTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the property value to a type supported by COM-DA interface.
|
||||
/// </summary>
|
||||
internal static object MarshalPropertyValue(TsDaPropertyID propertyID, object input)
|
||||
{
|
||||
if (input == null) return null;
|
||||
|
||||
try
|
||||
{
|
||||
if (propertyID == TsDaProperty.DATATYPE)
|
||||
{
|
||||
return (short)Technosoftware.DaAeHdaClient.Com.Interop.GetType((Type)input);
|
||||
}
|
||||
|
||||
if (propertyID == TsDaProperty.ACCESSRIGHTS)
|
||||
{
|
||||
switch ((TsDaAccessRights)input)
|
||||
{
|
||||
case TsDaAccessRights.Readable: return OpcRcw.Da.Constants.OPC_READABLE;
|
||||
case TsDaAccessRights.Writable: return OpcRcw.Da.Constants.OPC_WRITEABLE;
|
||||
case TsDaAccessRights.ReadWritable: return OpcRcw.Da.Constants.OPC_READABLE | OpcRcw.Da.Constants.OPC_WRITEABLE;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (propertyID == TsDaProperty.EUTYPE)
|
||||
{
|
||||
switch ((TsDaEuType)input)
|
||||
{
|
||||
case TsDaEuType.NoEnum: return OpcRcw.Da.OPCEUTYPE.OPC_NOENUM;
|
||||
case TsDaEuType.Analog: return OpcRcw.Da.OPCEUTYPE.OPC_ANALOG;
|
||||
case TsDaEuType.Enumerated: return OpcRcw.Da.OPCEUTYPE.OPC_ENUMERATED;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (propertyID == TsDaProperty.QUALITY)
|
||||
{
|
||||
return ((TsCDaQuality)input).GetCode();
|
||||
}
|
||||
|
||||
// convert local time in property to UTC time for the COM DA interface.
|
||||
if (propertyID == TsDaProperty.TIMESTAMP)
|
||||
{
|
||||
if (input.GetType() == typeof(DateTime))
|
||||
{
|
||||
var dateTime = (DateTime)input;
|
||||
|
||||
if (dateTime != DateTime.MinValue)
|
||||
{
|
||||
return dateTime.ToUniversalTime();
|
||||
}
|
||||
|
||||
return dateTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an array of item values to an array of OPCITEMVQT objects.
|
||||
/// </summary>
|
||||
internal static OpcRcw.Da.OPCITEMVQT[] GetOPCITEMVQTs(TsCDaItemValue[] input)
|
||||
{
|
||||
OpcRcw.Da.OPCITEMVQT[] output = null;
|
||||
|
||||
if (input != null)
|
||||
{
|
||||
output = new OpcRcw.Da.OPCITEMVQT[input.Length];
|
||||
|
||||
for (var ii = 0; ii < input.Length; ii++)
|
||||
{
|
||||
output[ii] = new OpcRcw.Da.OPCITEMVQT();
|
||||
|
||||
var timestamp = (input[ii].TimestampSpecified) ? input[ii].Timestamp : DateTime.MinValue;
|
||||
|
||||
output[ii].vDataValue = Technosoftware.DaAeHdaClient.Com.Interop.GetVARIANT(input[ii].Value);
|
||||
output[ii].bQualitySpecified = (input[ii].QualitySpecified) ? 1 : 0;
|
||||
output[ii].wQuality = (input[ii].QualitySpecified) ? input[ii].Quality.GetCode() : (short)0;
|
||||
output[ii].bTimeStampSpecified = (input[ii].TimestampSpecified) ? 1 : 0;
|
||||
output[ii].ftTimeStamp = Convert(Technosoftware.DaAeHdaClient.Com.Interop.GetFILETIME(timestamp));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an array of item objects to an array of GetOPCITEMDEF objects.
|
||||
/// </summary>
|
||||
internal static OpcRcw.Da.OPCITEMDEF[] GetOPCITEMDEFs(TsCDaItem[] input)
|
||||
{
|
||||
OpcRcw.Da.OPCITEMDEF[] output = null;
|
||||
|
||||
if (input != null)
|
||||
{
|
||||
output = new OpcRcw.Da.OPCITEMDEF[input.Length];
|
||||
|
||||
for (var ii = 0; ii < input.Length; ii++)
|
||||
{
|
||||
output[ii] = new OpcRcw.Da.OPCITEMDEF();
|
||||
|
||||
output[ii].szItemID = input[ii].ItemName;
|
||||
output[ii].szAccessPath = (input[ii].ItemPath == null) ? string.Empty : input[ii].ItemPath;
|
||||
output[ii].bActive = (input[ii].ActiveSpecified) ? ((input[ii].Active) ? 1 : 0) : 1;
|
||||
output[ii].vtRequestedDataType = (short)Technosoftware.DaAeHdaClient.Com.Interop.GetType(input[ii].ReqType);
|
||||
output[ii].hClient = 0;
|
||||
output[ii].dwBlobSize = 0;
|
||||
output[ii].pBlob = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unmarshals and deallocates a OPCITEMSTATE structures.
|
||||
/// </summary>
|
||||
internal static TsCDaItemValue[] GetItemValues(ref IntPtr pInput, int count, bool deallocate)
|
||||
{
|
||||
TsCDaItemValue[] output = null;
|
||||
|
||||
if (pInput != IntPtr.Zero && count > 0)
|
||||
{
|
||||
output = new TsCDaItemValue[count];
|
||||
|
||||
var pos = pInput;
|
||||
|
||||
for (var ii = 0; ii < count; ii++)
|
||||
{
|
||||
var result = (OpcRcw.Da.OPCITEMSTATE)Marshal.PtrToStructure(pos, typeof(OpcRcw.Da.OPCITEMSTATE));
|
||||
|
||||
output[ii] = new TsCDaItemValue();
|
||||
output[ii].ClientHandle = result.hClient;
|
||||
output[ii].Value = result.vDataValue;
|
||||
output[ii].Quality = new TsCDaQuality(result.wQuality);
|
||||
output[ii].QualitySpecified = true;
|
||||
output[ii].Timestamp = Technosoftware.DaAeHdaClient.Com.Interop.GetFILETIME(Convert(result.ftTimeStamp));
|
||||
output[ii].TimestampSpecified = output[ii].Timestamp != DateTime.MinValue;
|
||||
|
||||
if (deallocate)
|
||||
{
|
||||
Marshal.DestroyStructure(pos, typeof(OpcRcw.Da.OPCITEMSTATE));
|
||||
}
|
||||
|
||||
pos = (IntPtr)(pos.ToInt64() + Marshal.SizeOf(typeof(OpcRcw.Da.OPCITEMSTATE)));
|
||||
}
|
||||
|
||||
if (deallocate)
|
||||
{
|
||||
Marshal.FreeCoTaskMem(pInput);
|
||||
pInput = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unmarshals and deallocates a OPCITEMRESULT structures.
|
||||
/// </summary>
|
||||
internal static int[] GetItemResults(ref IntPtr pInput, int count, bool deallocate)
|
||||
{
|
||||
int[] output = null;
|
||||
|
||||
if (pInput != IntPtr.Zero && count > 0)
|
||||
{
|
||||
output = new int[count];
|
||||
|
||||
var pos = pInput;
|
||||
|
||||
for (var ii = 0; ii < count; ii++)
|
||||
{
|
||||
var result = (OpcRcw.Da.OPCITEMRESULT)Marshal.PtrToStructure(pos, typeof(OpcRcw.Da.OPCITEMRESULT));
|
||||
|
||||
output[ii] = result.hServer;
|
||||
|
||||
if (deallocate)
|
||||
{
|
||||
Marshal.FreeCoTaskMem(result.pBlob);
|
||||
result.pBlob = IntPtr.Zero;
|
||||
|
||||
Marshal.DestroyStructure(pos, typeof(OpcRcw.Da.OPCITEMRESULT));
|
||||
}
|
||||
|
||||
pos = (IntPtr)(pos.ToInt64() + Marshal.SizeOf(typeof(OpcRcw.Da.OPCITEMRESULT)));
|
||||
}
|
||||
|
||||
if (deallocate)
|
||||
{
|
||||
Marshal.FreeCoTaskMem(pInput);
|
||||
pInput = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates and marshals an array of OPCBROWSEELEMENT structures.
|
||||
/// </summary>
|
||||
internal static IntPtr GetItemStates(TsCDaItemValueResult[] input)
|
||||
{
|
||||
var output = IntPtr.Zero;
|
||||
|
||||
if (input != null && input.Length > 0)
|
||||
{
|
||||
output = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(OpcRcw.Da.OPCITEMSTATE)) * input.Length);
|
||||
|
||||
var pos = output;
|
||||
|
||||
for (var ii = 0; ii < input.Length; ii++)
|
||||
{
|
||||
var item = new OpcRcw.Da.OPCITEMSTATE();
|
||||
|
||||
item.hClient = System.Convert.ToInt32(input[ii].ClientHandle);
|
||||
item.vDataValue = input[ii].Value;
|
||||
item.wQuality = (input[ii].QualitySpecified) ? input[ii].Quality.GetCode() : (short)0;
|
||||
item.ftTimeStamp = Convert(Technosoftware.DaAeHdaClient.Com.Interop.GetFILETIME(input[ii].Timestamp));
|
||||
item.wReserved = 0;
|
||||
|
||||
Marshal.StructureToPtr(item, pos, false);
|
||||
pos = (IntPtr)(pos.ToInt64() + Marshal.SizeOf(typeof(OpcRcw.Da.OPCITEMSTATE)));
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
}
|
||||
128
Technosoftware/DaAeHdaClient.Com/Da/Result.cs
Normal file
128
Technosoftware/DaAeHdaClient.Com/Da/Result.cs
Normal file
@@ -0,0 +1,128 @@
|
||||
#region Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
|
||||
// Web: https://www.technosoftware.com
|
||||
//
|
||||
// The source code in this file is covered under a dual-license scenario:
|
||||
// - Owner of a purchased license: SCLA 1.0
|
||||
// - GPL V3: everybody else
|
||||
//
|
||||
// SCLA license terms accompanied with this source code.
|
||||
// See SCLA 1.0: https://technosoftware.com/license/Source_Code_License_Agreement.pdf
|
||||
//
|
||||
// GNU General Public License as published by the Free Software Foundation;
|
||||
// version 3 of the License are accompanied with this source code.
|
||||
// See https://technosoftware.com/license/GPLv3License.txt
|
||||
//
|
||||
// This source code is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//-----------------------------------------------------------------------------
|
||||
#endregion Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
|
||||
|
||||
#region Using Directives
|
||||
|
||||
#endregion
|
||||
|
||||
namespace Technosoftware.DaAeHdaClient.Com
|
||||
{
|
||||
namespace Da
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines all well known COM DA HRESULT codes.
|
||||
/// </summary>
|
||||
internal struct Result
|
||||
{
|
||||
/// <remarks/>
|
||||
public const int S_OK = +0x00000000; // 0x00000000
|
||||
/// <remarks/>
|
||||
public const int S_FALSE = +0x00000001; // 0x00000001
|
||||
/// <remarks/>
|
||||
public const int E_NOTIMPL = -0x7FFFBFFF; // 0x80004001
|
||||
/// <remarks/>
|
||||
public const int E_OUTOFMEMORY = -0x7FF8FFF2; // 0x8007000E
|
||||
/// <remarks/>
|
||||
public const int E_INVALIDARG = -0x7FF8FFA9; // 0x80070057
|
||||
/// <remarks/>
|
||||
public const int E_NOINTERFACE = -0x7FFFBFFE; // 0x80004002
|
||||
/// <remarks/>
|
||||
public const int E_POINTER = -0x7FFFBFFD; // 0x80004003
|
||||
/// <remarks/>
|
||||
public const int E_FAIL = -0x7FFFBFFB; // 0x80004005
|
||||
/// <remarks/>
|
||||
public const int CONNECT_E_NOCONNECTION = -0x7FFBFE00; // 0x80040200
|
||||
/// <remarks/>
|
||||
public const int CONNECT_E_ADVISELIMIT = -0x7FFBFDFF; // 0x80040201
|
||||
/// <remarks/>
|
||||
public const int DISP_E_TYPEMISMATCH = -0x7FFDFFFB; // 0x80020005
|
||||
/// <remarks/>
|
||||
public const int DISP_E_OVERFLOW = -0x7FFDFFF6; // 0x8002000A
|
||||
/// <remarks/>
|
||||
public const int E_INVALIDHANDLE = -0x3FFBFFFF; // 0xC0040001
|
||||
/// <remarks/>
|
||||
public const int E_BADTYPE = -0x3FFBFFFC; // 0xC0040004
|
||||
/// <remarks/>
|
||||
public const int E_PUBLIC = -0x3FFBFFFB; // 0xC0040005
|
||||
/// <remarks/>
|
||||
public const int E_BADRIGHTS = -0x3FFBFFFA; // 0xC0040006
|
||||
/// <remarks/>
|
||||
public const int E_UNKNOWNITEMID = -0x3FFBFFF9; // 0xC0040007
|
||||
/// <remarks/>
|
||||
public const int E_INVALIDITEMID = -0x3FFBFFF8; // 0xC0040008
|
||||
/// <remarks/>
|
||||
public const int E_INVALIDFILTER = -0x3FFBFFF7; // 0xC0040009
|
||||
/// <remarks/>
|
||||
public const int E_UNKNOWNPATH = -0x3FFBFFF6; // 0xC004000A
|
||||
/// <remarks/>
|
||||
public const int E_RANGE = -0x3FFBFFF5; // 0xC004000B
|
||||
/// <remarks/>
|
||||
public const int E_DUPLICATENAME = -0x3FFBFFF4; // 0xC004000C
|
||||
/// <remarks/>
|
||||
public const int S_UNSUPPORTEDRATE = +0x0004000D; // 0x0004000D
|
||||
/// <remarks/>
|
||||
public const int S_CLAMP = +0x0004000E; // 0x0004000E
|
||||
/// <remarks/>
|
||||
public const int S_INUSE = +0x0004000F; // 0x0004000F
|
||||
/// <remarks/>
|
||||
public const int E_INVALIDCONFIGFILE = -0x3FFBFFF0; // 0xC0040010
|
||||
/// <remarks/>
|
||||
public const int E_NOTFOUND = -0x3FFBFFEF; // 0xC0040011
|
||||
/// <remarks/>
|
||||
public const int E_INVALID_PID = -0x3FFBFDFD; // 0xC0040203
|
||||
/// <remarks/>
|
||||
public const int E_DEADBANDNOTSET = -0x3FFBFC00; // 0xC0040400
|
||||
/// <remarks/>
|
||||
public const int E_DEADBANDNOTSUPPORTED = -0x3FFBFBFF; // 0xC0040401
|
||||
/// <remarks/>
|
||||
public const int E_NOBUFFERING = -0x3FFBFBFE; // 0xC0040402
|
||||
/// <remarks/>
|
||||
public const int E_INVALIDCONTINUATIONPOINT = -0x3FFBFBFD; // 0xC0040403
|
||||
/// <remarks/>
|
||||
public const int S_DATAQUEUEOVERFLOW = +0x00040404; // 0x00040404
|
||||
/// <remarks/>
|
||||
public const int E_RATENOTSET = -0x3FFBFBFB; // 0xC0040405
|
||||
/// <remarks/>
|
||||
public const int E_NOTSUPPORTED = -0x3FFBFBFA; // 0xC0040406
|
||||
}
|
||||
}
|
||||
|
||||
namespace Cpx
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines all well known Complex Data HRESULT codes.
|
||||
/// </summary>
|
||||
internal struct Result
|
||||
{
|
||||
/// <remarks/>
|
||||
public const int E_TYPE_CHANGED = -0x3FFBFBF9; // 0xC0040407
|
||||
/// <remarks/>
|
||||
public const int E_FILTER_DUPLICATE = -0x3FFBFBF8; // 0xC0040408
|
||||
/// <remarks/>
|
||||
public const int E_FILTER_INVALID = -0x3FFBFBF7; // 0xC0040409
|
||||
/// <remarks/>
|
||||
public const int E_FILTER_ERROR = -0x3FFBFBF6; // 0xC004040A
|
||||
/// <remarks/>
|
||||
public const int S_FILTER_NO_DATA = +0x0004040B; // 0xC004040B
|
||||
}
|
||||
}
|
||||
}
|
||||
981
Technosoftware/DaAeHdaClient.Com/Da/Server.cs
Normal file
981
Technosoftware/DaAeHdaClient.Com/Da/Server.cs
Normal file
@@ -0,0 +1,981 @@
|
||||
#region Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
|
||||
// Web: https://www.technosoftware.com
|
||||
//
|
||||
// The source code in this file is covered under a dual-license scenario:
|
||||
// - Owner of a purchased license: SCLA 1.0
|
||||
// - GPL V3: everybody else
|
||||
//
|
||||
// SCLA license terms accompanied with this source code.
|
||||
// See SCLA 1.0: https://technosoftware.com/license/Source_Code_License_Agreement.pdf
|
||||
//
|
||||
// GNU General Public License as published by the Free Software Foundation;
|
||||
// version 3 of the License are accompanied with this source code.
|
||||
// See https://technosoftware.com/license/GPLv3License.txt
|
||||
//
|
||||
// This source code is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//-----------------------------------------------------------------------------
|
||||
#endregion Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
|
||||
|
||||
#region Using Directives
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Globalization;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using Technosoftware.DaAeHdaClient.Com.Utilities;
|
||||
using Technosoftware.DaAeHdaClient.Da;
|
||||
using Technosoftware.OpcRcw.Da;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace Technosoftware.DaAeHdaClient.Com.Da
|
||||
{
|
||||
/// <summary>
|
||||
/// A .NET wrapper for a COM server that implements the DA server interfaces.
|
||||
/// </summary>
|
||||
internal class Server : Com.Server, ITsDaServer
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <summary>
|
||||
/// The default result filters for the server.
|
||||
/// </summary>
|
||||
private int filters_ = (int)TsCDaResultFilter.All | (int)TsCDaResultFilter.ClientHandle;
|
||||
|
||||
/// <summary>
|
||||
/// A table of active subscriptions for the server.
|
||||
/// </summary>
|
||||
private readonly Hashtable subscriptions_ = new Hashtable();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the object.
|
||||
/// </summary>
|
||||
internal Server() { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the object with the specified COM server.
|
||||
/// </summary>
|
||||
internal Server(OpcUrl url, object server)
|
||||
{
|
||||
if (url == null) throw new ArgumentNullException(nameof(url));
|
||||
|
||||
url_ = (OpcUrl)url.Clone();
|
||||
server_ = server;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IDisposable Members
|
||||
/// <summary>
|
||||
/// Dispose(bool disposing) executes in two distinct scenarios.
|
||||
/// If disposing equals true, the method has been called directly
|
||||
/// or indirectly by a user's code. Managed and unmanaged resources
|
||||
/// can be disposed.
|
||||
/// If disposing equals false, the method has been called by the
|
||||
/// runtime from inside the finalizer and you should not reference
|
||||
/// other objects. Only unmanaged resources can be disposed.
|
||||
/// </summary>
|
||||
/// <param name="disposing">If true managed and unmanaged resources can be disposed. If false only unmanaged resources.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposed_)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
// Release managed resources.
|
||||
if (server_ != null)
|
||||
{
|
||||
// release all groups.
|
||||
foreach (Subscription subscription in subscriptions_.Values)
|
||||
{
|
||||
var methodName = "IOPCServer.RemoveGroup";
|
||||
|
||||
// remove subscription from server.
|
||||
try
|
||||
{
|
||||
var state = subscription.GetState();
|
||||
if (state != null)
|
||||
{
|
||||
var server = BeginComCall<IOPCServer>(methodName, true);
|
||||
server?.RemoveGroup((int)state.ServerHandle, 0);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore error during Dispose
|
||||
}
|
||||
finally
|
||||
{
|
||||
EndComCall(methodName);
|
||||
}
|
||||
// dispose of the subscription object (disconnects all subscription connections).
|
||||
subscription.Dispose();
|
||||
}
|
||||
|
||||
// clear subscription table.
|
||||
subscriptions_.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Release unmanaged resources.
|
||||
// Set large fields to null.
|
||||
|
||||
if (server_ != null)
|
||||
{
|
||||
// release the COM server.
|
||||
Technosoftware.DaAeHdaClient.Com.Interop.ReleaseServer(server_);
|
||||
server_ = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Call Dispose on your base class.
|
||||
disposed_ = true;
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
private bool disposed_;
|
||||
#endregion
|
||||
|
||||
#region Technosoftware.DaAeHdaClient.Com.Server Overrides
|
||||
/// <summary>
|
||||
/// Returns the localized text for the specified result code.
|
||||
/// </summary>
|
||||
/// <param name="locale">The locale name in the format "[languagecode]-[country/regioncode]".</param>
|
||||
/// <param name="resultId">The result code identifier.</param>
|
||||
/// <returns>A message localized for the best match for the requested locale.</returns>
|
||||
public override string GetErrorText(string locale, OpcResult resultId)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (server_ == null) throw new NotConnectedException();
|
||||
var methodName = "IOPCServer.GetErrorString";
|
||||
|
||||
// invoke COM method.
|
||||
try
|
||||
{
|
||||
var server = BeginComCall<IOPCServer>(methodName, true);
|
||||
|
||||
(server).GetErrorString(
|
||||
resultId.Code,
|
||||
Technosoftware.DaAeHdaClient.Com.Interop.GetLocale(locale),
|
||||
out var errorText);
|
||||
|
||||
if (DCOMCallWatchdog.IsCancelled)
|
||||
{
|
||||
throw new Exception($"{methodName} call was cancelled due to response timeout");
|
||||
}
|
||||
|
||||
return errorText;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ComCallError(methodName, e);
|
||||
throw Technosoftware.DaAeHdaClient.Com.Interop.CreateException("IOPCServer.GetErrorString", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EndComCall(methodName);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Technosoftware.DaAeHdaClient.IOpcServer Members
|
||||
/// <summary>
|
||||
/// Returns the filters applied by the server to any item results returned to the client.
|
||||
/// </summary>
|
||||
/// <returns>A bit mask indicating which fields should be returned in any item results.</returns>
|
||||
public int GetResultFilters()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (server_ == null) throw new NotConnectedException();
|
||||
return filters_;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the filters applied by the server to any item results returned to the client.
|
||||
/// </summary>
|
||||
/// <param name="filters">A bit mask indicating which fields should be returned in any item results.</param>
|
||||
public void SetResultFilters(int filters)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (server_ == null) throw new NotConnectedException();
|
||||
filters_ = filters;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current server status.
|
||||
/// </summary>
|
||||
/// <returns>The current server status.</returns>
|
||||
public OpcServerStatus GetServerStatus()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (server_ == null) throw new NotConnectedException();
|
||||
var methodName = "IOPCServer.GetStatus";
|
||||
|
||||
// initialize arguments.
|
||||
IntPtr pStatus;
|
||||
|
||||
// invoke COM method.
|
||||
try
|
||||
{
|
||||
var server = BeginComCall<IOPCServer>(methodName, true);
|
||||
(server).GetStatus(out pStatus);
|
||||
|
||||
if (DCOMCallWatchdog.IsCancelled)
|
||||
{
|
||||
throw new Exception($"{methodName} call was cancelled due to response timeout");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ComCallError(methodName, e);
|
||||
throw Technosoftware.DaAeHdaClient.Com.Interop.CreateException(methodName, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EndComCall(methodName);
|
||||
}
|
||||
|
||||
// return status.
|
||||
return Interop.GetServerStatus(ref pStatus, true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the current values for a set of items.
|
||||
/// </summary>
|
||||
/// <param name="items">The set of items to read.</param>
|
||||
/// <returns>The results of the read operation for each item.</returns>
|
||||
public virtual TsCDaItemValueResult[] Read(TsCDaItem[] items)
|
||||
{
|
||||
if (items == null) throw new ArgumentNullException(nameof(items));
|
||||
|
||||
lock (this)
|
||||
{
|
||||
var methodName = "IOPCItemIO.Read";
|
||||
if (server_ == null) throw new NotConnectedException();
|
||||
|
||||
var count = items.Length;
|
||||
if (count == 0) throw new ArgumentOutOfRangeException(nameof(items.Length), @"0");
|
||||
|
||||
// initialize arguments.
|
||||
var itemIDs = new string[count];
|
||||
var maxAges = new int[count];
|
||||
|
||||
for (var ii = 0; ii < count; ii++)
|
||||
{
|
||||
itemIDs[ii] = items[ii].ItemName;
|
||||
maxAges[ii] = (items[ii].MaxAgeSpecified) ? items[ii].MaxAge : 0;
|
||||
}
|
||||
|
||||
var pValues = IntPtr.Zero;
|
||||
var pQualities = IntPtr.Zero;
|
||||
var pTimestamps = IntPtr.Zero;
|
||||
var pErrors = IntPtr.Zero;
|
||||
|
||||
// invoke COM method.
|
||||
try
|
||||
{
|
||||
var server = BeginComCall<IOPCItemIO>(methodName, true);
|
||||
server.Read(
|
||||
count,
|
||||
itemIDs,
|
||||
maxAges,
|
||||
out pValues,
|
||||
out pQualities,
|
||||
out pTimestamps,
|
||||
out pErrors);
|
||||
|
||||
if (DCOMCallWatchdog.IsCancelled)
|
||||
{
|
||||
throw new Exception($"{methodName} call was cancelled due to response timeout");
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ComCallError(methodName, e);
|
||||
throw Technosoftware.DaAeHdaClient.Com.Interop.CreateException(methodName, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EndComCall(methodName);
|
||||
}
|
||||
|
||||
// unmarshal results.
|
||||
var values = Technosoftware.DaAeHdaClient.Com.Interop.GetVARIANTs(ref pValues, count, true);
|
||||
var qualities = Technosoftware.DaAeHdaClient.Com.Interop.GetInt16s(ref pQualities, count, true);
|
||||
var timestamps = Technosoftware.DaAeHdaClient.Com.Interop.GetFILETIMEs(ref pTimestamps, count, true);
|
||||
var errors = Technosoftware.DaAeHdaClient.Com.Interop.GetInt32s(ref pErrors, count, true);
|
||||
|
||||
// pre-fetch the current locale to use for data conversions.
|
||||
var locale = GetLocale();
|
||||
|
||||
// construct result array.
|
||||
var results = new TsCDaItemValueResult[count];
|
||||
|
||||
for (var ii = 0; ii < results.Length; ii++)
|
||||
{
|
||||
results[ii] = new TsCDaItemValueResult(items[ii]);
|
||||
|
||||
results[ii].ServerHandle = null;
|
||||
results[ii].Value = values[ii];
|
||||
results[ii].Quality = new TsCDaQuality(qualities[ii]);
|
||||
results[ii].QualitySpecified = true;
|
||||
results[ii].Timestamp = timestamps[ii];
|
||||
results[ii].TimestampSpecified = timestamps[ii] != DateTime.MinValue;
|
||||
results[ii].Result = Utilities.Interop.GetResultId(errors[ii]);
|
||||
results[ii].DiagnosticInfo = null;
|
||||
|
||||
// convert COM code to unified DA code.
|
||||
if (errors[ii] == Result.E_BADRIGHTS) { results[ii].Result = new OpcResult(OpcResult.Da.E_WRITEONLY, Result.E_BADRIGHTS); }
|
||||
|
||||
// convert the data type since the server does not support the feature.
|
||||
if (results[ii].Value != null && items[ii].ReqType != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
results[ii].Value = ChangeType(values[ii], items[ii].ReqType, locale);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
results[ii].Value = null;
|
||||
results[ii].Quality = TsCDaQuality.Bad;
|
||||
results[ii].QualitySpecified = true;
|
||||
results[ii].Timestamp = DateTime.MinValue;
|
||||
results[ii].TimestampSpecified = false;
|
||||
|
||||
if (e.GetType() == typeof(OverflowException))
|
||||
{
|
||||
results[ii].Result = Utilities.Interop.GetResultId(Result.E_RANGE);
|
||||
}
|
||||
else
|
||||
{
|
||||
results[ii].Result = Utilities.Interop.GetResultId(Result.E_BADTYPE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// apply request options.
|
||||
if ((filters_ & (int)TsCDaResultFilter.ItemName) == 0) results[ii].ItemName = null;
|
||||
if ((filters_ & (int)TsCDaResultFilter.ItemPath) == 0) results[ii].ItemPath = null;
|
||||
if ((filters_ & (int)TsCDaResultFilter.ClientHandle) == 0) results[ii].ClientHandle = null;
|
||||
|
||||
if ((filters_ & (int)TsCDaResultFilter.ItemTime) == 0)
|
||||
{
|
||||
results[ii].Timestamp = DateTime.MinValue;
|
||||
results[ii].TimestampSpecified = false;
|
||||
}
|
||||
}
|
||||
|
||||
// return results.
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the value, quality and timestamp for a set of items.
|
||||
/// </summary>
|
||||
/// <param name="items">The set of item values to write.</param>
|
||||
/// <returns>The results of the write operation for each item.</returns>
|
||||
public virtual OpcItemResult[] Write(TsCDaItemValue[] items)
|
||||
{
|
||||
if (items == null) throw new ArgumentNullException(nameof(items));
|
||||
|
||||
lock (this)
|
||||
{
|
||||
if (server_ == null) throw new NotConnectedException();
|
||||
var methodName = "IOPCItemIO.WriteVQT";
|
||||
|
||||
var count = items.Length;
|
||||
if (count == 0) throw new ArgumentOutOfRangeException("items.Length", "0");
|
||||
|
||||
// initialize arguments.
|
||||
var itemIDs = new string[count];
|
||||
|
||||
for (var ii = 0; ii < count; ii++)
|
||||
{
|
||||
itemIDs[ii] = items[ii].ItemName;
|
||||
}
|
||||
|
||||
var values = Interop.GetOPCITEMVQTs(items);
|
||||
|
||||
var pErrors = IntPtr.Zero;
|
||||
|
||||
// invoke COM method.
|
||||
try
|
||||
{
|
||||
var server = BeginComCall<IOPCItemIO>(methodName, true);
|
||||
server.WriteVQT(
|
||||
count,
|
||||
itemIDs,
|
||||
values,
|
||||
out pErrors);
|
||||
|
||||
if (DCOMCallWatchdog.IsCancelled)
|
||||
{
|
||||
throw new Exception($"{methodName} call was cancelled due to response timeout");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ComCallError(methodName, e);
|
||||
throw Technosoftware.DaAeHdaClient.Com.Interop.CreateException(methodName, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EndComCall(methodName);
|
||||
}
|
||||
|
||||
// unmarshal results.
|
||||
var errors = Utilities.Interop.GetInt32s(ref pErrors, count, true);
|
||||
|
||||
// construct result array.
|
||||
var results = new OpcItemResult[count];
|
||||
|
||||
for (var ii = 0; ii < count; ii++)
|
||||
{
|
||||
results[ii] = new OpcItemResult(items[ii]);
|
||||
|
||||
results[ii].ServerHandle = null;
|
||||
results[ii].Result = Utilities.Interop.GetResultId(errors[ii]);
|
||||
results[ii].DiagnosticInfo = null;
|
||||
|
||||
// convert COM code to unified DA code.
|
||||
if (errors[ii] == Result.E_BADRIGHTS) { results[ii].Result = new OpcResult(OpcResult.Da.E_READONLY, Result.E_BADRIGHTS); }
|
||||
|
||||
// apply request options.
|
||||
if ((filters_ & (int)TsCDaResultFilter.ItemName) == 0) results[ii].ItemName = null;
|
||||
if ((filters_ & (int)TsCDaResultFilter.ItemPath) == 0) results[ii].ItemPath = null;
|
||||
if ((filters_ & (int)TsCDaResultFilter.ClientHandle) == 0) results[ii].ClientHandle = null;
|
||||
}
|
||||
|
||||
// return results.
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new subscription.
|
||||
/// </summary>
|
||||
/// <param name="state">The initial state of the subscription.</param>
|
||||
/// <returns>The new subscription object.</returns>
|
||||
public ITsCDaSubscription CreateSubscription(TsCDaSubscriptionState state)
|
||||
{
|
||||
if (state == null) throw new ArgumentNullException(nameof(state));
|
||||
|
||||
lock (this)
|
||||
{
|
||||
if (server_ == null) throw new NotConnectedException();
|
||||
var methodName = "IOPCServer.AddGroup";
|
||||
|
||||
// copy the subscription state.
|
||||
var result = (TsCDaSubscriptionState)state.Clone();
|
||||
|
||||
// initialize arguments.
|
||||
var iid = typeof(IOPCItemMgt).GUID;
|
||||
object group = null;
|
||||
|
||||
var serverHandle = 0;
|
||||
var revisedUpdateRate = 0;
|
||||
|
||||
var hDeadband = GCHandle.Alloc(result.Deadband, GCHandleType.Pinned);
|
||||
|
||||
// invoke COM method.
|
||||
try
|
||||
{
|
||||
var server = BeginComCall<IOPCServer>(methodName, true);
|
||||
server.AddGroup(
|
||||
(result.Name != null) ? result.Name : "",
|
||||
(result.Active) ? 1 : 0,
|
||||
result.UpdateRate,
|
||||
0,
|
||||
IntPtr.Zero,
|
||||
hDeadband.AddrOfPinnedObject(),
|
||||
Technosoftware.DaAeHdaClient.Com.Interop.GetLocale(result.Locale),
|
||||
out serverHandle,
|
||||
out revisedUpdateRate,
|
||||
ref iid,
|
||||
out group);
|
||||
|
||||
if (DCOMCallWatchdog.IsCancelled)
|
||||
{
|
||||
throw new Exception($"{methodName} call was cancelled due to response timeout");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ComCallError(methodName, e);
|
||||
throw Utilities.Interop.CreateException(methodName, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (hDeadband.IsAllocated)
|
||||
{
|
||||
hDeadband.Free();
|
||||
}
|
||||
EndComCall(methodName);
|
||||
}
|
||||
|
||||
if (group == null) throw new OpcResultException(OpcResult.E_FAIL, "The subscription was not created.");
|
||||
|
||||
methodName = "IOPCGroupStateMgt2.SetKeepAlive";
|
||||
|
||||
// set the keep alive rate if requested.
|
||||
try
|
||||
{
|
||||
var keepAlive = 0;
|
||||
var comObject = BeginComCall<IOPCGroupStateMgt2>(group, methodName, true);
|
||||
comObject.SetKeepAlive(result.KeepAlive, out keepAlive);
|
||||
|
||||
if (DCOMCallWatchdog.IsCancelled)
|
||||
{
|
||||
throw new Exception($"{methodName} call was cancelled due to response timeout");
|
||||
}
|
||||
|
||||
result.KeepAlive = keepAlive;
|
||||
}
|
||||
catch (Exception e1)
|
||||
{
|
||||
result.KeepAlive = 0;
|
||||
ComCallError(methodName, e1);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EndComCall(methodName);
|
||||
}
|
||||
|
||||
// save server handle.
|
||||
result.ServerHandle = serverHandle;
|
||||
|
||||
// set the revised update rate.
|
||||
if (revisedUpdateRate > result.UpdateRate)
|
||||
{
|
||||
result.UpdateRate = revisedUpdateRate;
|
||||
}
|
||||
|
||||
// create the subscription object.
|
||||
var subscription = CreateSubscription(group, result, filters_);
|
||||
|
||||
// index by server handle.
|
||||
subscriptions_[serverHandle] = subscription;
|
||||
|
||||
// return subscription.
|
||||
return subscription;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancels a subscription and releases all resources allocated for it.
|
||||
/// </summary>
|
||||
/// <param name="subscription">The subscription to cancel.</param>
|
||||
public void CancelSubscription(ITsCDaSubscription subscription)
|
||||
{
|
||||
if (subscription == null) throw new ArgumentNullException(nameof(subscription));
|
||||
|
||||
lock (this)
|
||||
{
|
||||
if (server_ == null) throw new NotConnectedException();
|
||||
var methodName = "IOPCServer.RemoveGroup";
|
||||
|
||||
// validate argument.
|
||||
if (!typeof(Subscription).IsInstanceOfType(subscription))
|
||||
{
|
||||
throw new ArgumentException("Incorrect object type.", nameof(subscription));
|
||||
}
|
||||
|
||||
// get the subscription state.
|
||||
var state = subscription.GetState();
|
||||
|
||||
if (!subscriptions_.ContainsKey(state.ServerHandle))
|
||||
{
|
||||
throw new ArgumentException("Handle not found.", nameof(subscription));
|
||||
}
|
||||
|
||||
subscriptions_.Remove(state.ServerHandle);
|
||||
|
||||
// release all subscription resources.
|
||||
subscription.Dispose();
|
||||
|
||||
// invoke COM method.
|
||||
try
|
||||
{
|
||||
var server = BeginComCall<IOPCServer>(methodName, true);
|
||||
server.RemoveGroup((int)state.ServerHandle, 0);
|
||||
|
||||
if (DCOMCallWatchdog.IsCancelled)
|
||||
{
|
||||
throw new Exception($"{methodName} call was cancelled due to response timeout");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ComCallError(methodName, e);
|
||||
throw Utilities.Interop.CreateException(methodName, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EndComCall(methodName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fetches the children of a branch that meet the filter criteria.
|
||||
/// </summary>
|
||||
/// <param name="itemId">The identifier of branch which is the target of the search.</param>
|
||||
/// <param name="filters">The filters to use to limit the set of child elements returned.</param>
|
||||
/// <param name="position">An object used to continue a browse that could not be completed.</param>
|
||||
/// <returns>The set of elements found.</returns>
|
||||
public virtual TsCDaBrowseElement[] Browse(
|
||||
OpcItem itemId,
|
||||
TsCDaBrowseFilters filters,
|
||||
out TsCDaBrowsePosition position)
|
||||
{
|
||||
if (filters == null) throw new ArgumentNullException(nameof(filters));
|
||||
|
||||
lock (this)
|
||||
{
|
||||
if (server_ == null) throw new NotConnectedException();
|
||||
var methodName = "IOPCBrowse.Browse";
|
||||
|
||||
position = null;
|
||||
|
||||
// initialize arguments.
|
||||
var count = 0;
|
||||
var moreElements = 0;
|
||||
|
||||
var pContinuationPoint = IntPtr.Zero;
|
||||
var pElements = IntPtr.Zero;
|
||||
|
||||
// invoke COM method.
|
||||
try
|
||||
{
|
||||
var server = BeginComCall<IOPCBrowse>(methodName, true);
|
||||
server.Browse(
|
||||
(itemId != null && itemId.ItemName != null) ? itemId.ItemName : "",
|
||||
ref pContinuationPoint,
|
||||
filters.MaxElementsReturned,
|
||||
Interop.GetBrowseFilter(filters.BrowseFilter),
|
||||
(filters.ElementNameFilter != null) ? filters.ElementNameFilter : "",
|
||||
(filters.VendorFilter != null) ? filters.VendorFilter : "",
|
||||
(filters.ReturnAllProperties) ? 1 : 0,
|
||||
(filters.ReturnPropertyValues) ? 1 : 0,
|
||||
(filters.PropertyIDs != null) ? filters.PropertyIDs.Length : 0,
|
||||
Interop.GetPropertyIDs(filters.PropertyIDs),
|
||||
out moreElements,
|
||||
out count,
|
||||
out pElements);
|
||||
|
||||
if (DCOMCallWatchdog.IsCancelled)
|
||||
{
|
||||
throw new Exception($"{methodName} call was cancelled due to response timeout");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ComCallError(methodName, e);
|
||||
throw Technosoftware.DaAeHdaClient.Com.Interop.CreateException(methodName, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EndComCall(methodName);
|
||||
}
|
||||
|
||||
// unmarshal results.
|
||||
var elements = Interop.GetBrowseElements(ref pElements, count, true);
|
||||
|
||||
var continuationPoint = Marshal.PtrToStringUni(pContinuationPoint);
|
||||
Marshal.FreeCoTaskMem(pContinuationPoint);
|
||||
|
||||
// check if more results exist.
|
||||
if (moreElements != 0 || (continuationPoint != null && continuationPoint != ""))
|
||||
{
|
||||
// allocate new browse position object.
|
||||
position = new BrowsePosition(itemId, filters, continuationPoint);
|
||||
}
|
||||
|
||||
// process results.
|
||||
ProcessResults(elements, filters.PropertyIDs);
|
||||
|
||||
return elements;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Continues a browse operation with previously specified search criteria.
|
||||
/// </summary>
|
||||
/// <param name="position">An object containing the browse operation state information.</param>
|
||||
/// <returns>The set of elements found.</returns>
|
||||
public virtual TsCDaBrowseElement[] BrowseNext(ref TsCDaBrowsePosition position)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (server_ == null) throw new NotConnectedException();
|
||||
var methodName = "IOPCBrowse.Browse";
|
||||
|
||||
// check for valid position object.
|
||||
if (position == null || position.GetType() != typeof(BrowsePosition))
|
||||
{
|
||||
throw new BrowseCannotContinueException();
|
||||
}
|
||||
|
||||
var pos = (BrowsePosition)position;
|
||||
|
||||
// check for valid continuation point.
|
||||
if (pos == null || pos.ContinuationPoint == null || pos.ContinuationPoint == "")
|
||||
{
|
||||
throw new BrowseCannotContinueException();
|
||||
}
|
||||
|
||||
// initialize arguments.
|
||||
var count = 0;
|
||||
var moreElements = 0;
|
||||
|
||||
var itemID = ((BrowsePosition)position).ItemID;
|
||||
var filters = ((BrowsePosition)position).Filters;
|
||||
|
||||
var pContinuationPoint = Marshal.StringToCoTaskMemUni(pos.ContinuationPoint);
|
||||
var pElements = IntPtr.Zero;
|
||||
|
||||
// invoke COM method.
|
||||
try
|
||||
{
|
||||
var server = BeginComCall<IOPCBrowse>(methodName, true);
|
||||
server.Browse(
|
||||
(itemID != null && itemID.ItemName != null) ? itemID.ItemName : "",
|
||||
ref pContinuationPoint,
|
||||
filters.MaxElementsReturned,
|
||||
Interop.GetBrowseFilter(filters.BrowseFilter),
|
||||
(filters.ElementNameFilter != null) ? filters.ElementNameFilter : "",
|
||||
(filters.VendorFilter != null) ? filters.VendorFilter : "",
|
||||
(filters.ReturnAllProperties) ? 1 : 0,
|
||||
(filters.ReturnPropertyValues) ? 1 : 0,
|
||||
(filters.PropertyIDs != null) ? filters.PropertyIDs.Length : 0,
|
||||
Interop.GetPropertyIDs(filters.PropertyIDs),
|
||||
out moreElements,
|
||||
out count,
|
||||
out pElements);
|
||||
|
||||
if (DCOMCallWatchdog.IsCancelled)
|
||||
{
|
||||
throw new Exception($"{methodName} call was cancelled due to response timeout");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ComCallError(methodName, e);
|
||||
throw Technosoftware.DaAeHdaClient.Com.Interop.CreateException(methodName, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EndComCall(methodName);
|
||||
}
|
||||
|
||||
// unmarshal results.
|
||||
var elements = Interop.GetBrowseElements(ref pElements, count, true);
|
||||
|
||||
pos.ContinuationPoint = Marshal.PtrToStringUni(pContinuationPoint);
|
||||
Marshal.FreeCoTaskMem(pContinuationPoint);
|
||||
|
||||
// check if more no results exist.
|
||||
if (moreElements == 0 && (pos.ContinuationPoint == null || pos.ContinuationPoint == ""))
|
||||
{
|
||||
position = null;
|
||||
}
|
||||
|
||||
// process results.
|
||||
ProcessResults(elements, filters.PropertyIDs);
|
||||
|
||||
return elements;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the item properties for a set of items.
|
||||
/// </summary>
|
||||
/// <param name="itemIds">A list of item identifiers.</param>
|
||||
/// <param name="propertyIDs">A list of properties to fetch for each item.</param>
|
||||
/// <param name="returnValues">Whether the property values should be returned with the properties.</param>
|
||||
/// <returns>A list of properties for each item.</returns>
|
||||
public virtual TsCDaItemPropertyCollection[] GetProperties(
|
||||
OpcItem[] itemIds,
|
||||
TsDaPropertyID[] propertyIDs,
|
||||
bool returnValues)
|
||||
{
|
||||
if (itemIds == null) throw new ArgumentNullException(nameof(itemIds));
|
||||
|
||||
lock (this)
|
||||
{
|
||||
if (server_ == null) throw new NotConnectedException();
|
||||
var methodName = "IOPCBrowse.GetProperties";
|
||||
|
||||
// initialize arguments.
|
||||
var pItemIDs = new string[itemIds.Length];
|
||||
|
||||
for (var ii = 0; ii < itemIds.Length; ii++)
|
||||
{
|
||||
pItemIDs[ii] = itemIds[ii].ItemName;
|
||||
}
|
||||
|
||||
var pPropertyLists = IntPtr.Zero;
|
||||
|
||||
// invoke COM method.
|
||||
try
|
||||
{
|
||||
var server = BeginComCall<IOPCBrowse>(methodName, true);
|
||||
server.GetProperties(
|
||||
itemIds.Length,
|
||||
pItemIDs,
|
||||
(returnValues) ? 1 : 0,
|
||||
(propertyIDs != null) ? propertyIDs.Length : 0,
|
||||
Interop.GetPropertyIDs(propertyIDs),
|
||||
out pPropertyLists);
|
||||
|
||||
if (DCOMCallWatchdog.IsCancelled)
|
||||
{
|
||||
throw new Exception($"{methodName} call was cancelled due to response timeout");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ComCallError(methodName, e);
|
||||
throw Technosoftware.DaAeHdaClient.Com.Interop.CreateException(methodName, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EndComCall(methodName);
|
||||
}
|
||||
|
||||
// unmarshal results.
|
||||
var resultLists = Interop.GetItemPropertyCollections(ref pPropertyLists, itemIds.Length, true);
|
||||
|
||||
// replace integer codes with qnames passed in.
|
||||
if (propertyIDs != null && propertyIDs.Length > 0)
|
||||
{
|
||||
foreach (var resultList in resultLists)
|
||||
{
|
||||
for (var ii = 0; ii < resultList.Count; ii++)
|
||||
{
|
||||
resultList[ii].ID = propertyIDs[ii];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// return the results.
|
||||
return resultLists;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Converts a value to the specified type using the specified locale.
|
||||
/// </summary>
|
||||
protected object ChangeType(object source, Type type, string locale)
|
||||
{
|
||||
var culture = Thread.CurrentThread.CurrentCulture;
|
||||
|
||||
// override the current thread culture to ensure conversions happen correctly.
|
||||
try
|
||||
{
|
||||
Thread.CurrentThread.CurrentCulture = new CultureInfo(locale);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var result = OpcConvert.ChangeType(source, type);
|
||||
|
||||
// check for overflow converting to float.
|
||||
if (typeof(float) == type)
|
||||
{
|
||||
if (float.IsInfinity(Convert.ToSingle(result)))
|
||||
{
|
||||
throw new OverflowException();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// restore the current thread culture after conversion.
|
||||
finally
|
||||
{
|
||||
Thread.CurrentThread.CurrentCulture = culture;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of a subscription.
|
||||
/// </summary>
|
||||
protected virtual Subscription CreateSubscription(
|
||||
object group,
|
||||
TsCDaSubscriptionState state,
|
||||
int filters)
|
||||
{
|
||||
return new Subscription(group, state, filters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the properties to convert COM values to OPC .NET API results.
|
||||
/// </summary>
|
||||
private void ProcessResults(TsCDaBrowseElement[] elements, TsDaPropertyID[] propertyIds)
|
||||
{
|
||||
// check for null.
|
||||
if (elements == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// process each element.
|
||||
foreach (var element in elements)
|
||||
{
|
||||
// check if no properties.
|
||||
if (element.Properties == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// process each property.
|
||||
foreach (var property in element.Properties)
|
||||
{
|
||||
// replace the property ids which on contain the codes with the proper qualified names passed in.
|
||||
if (propertyIds != null)
|
||||
{
|
||||
foreach (var propertyId in propertyIds)
|
||||
{
|
||||
if (property.ID.Code == propertyId.Code)
|
||||
{
|
||||
property.ID = propertyId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
2942
Technosoftware/DaAeHdaClient.Com/Da/Subscription.cs
Normal file
2942
Technosoftware/DaAeHdaClient.Com/Da/Subscription.cs
Normal file
File diff suppressed because it is too large
Load Diff
1647
Technosoftware/DaAeHdaClient.Com/Da20/Server.cs
Normal file
1647
Technosoftware/DaAeHdaClient.Com/Da20/Server.cs
Normal file
File diff suppressed because it is too large
Load Diff
583
Technosoftware/DaAeHdaClient.Com/Da20/Subscription.cs
Normal file
583
Technosoftware/DaAeHdaClient.Com/Da20/Subscription.cs
Normal file
@@ -0,0 +1,583 @@
|
||||
#region Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
|
||||
// Web: https://www.technosoftware.com
|
||||
//
|
||||
// The source code in this file is covered under a dual-license scenario:
|
||||
// - Owner of a purchased license: SCLA 1.0
|
||||
// - GPL V3: everybody else
|
||||
//
|
||||
// SCLA license terms accompanied with this source code.
|
||||
// See SCLA 1.0: https://technosoftware.com/license/Source_Code_License_Agreement.pdf
|
||||
//
|
||||
// GNU General Public License as published by the Free Software Foundation;
|
||||
// version 3 of the License are accompanied with this source code.
|
||||
// See https://technosoftware.com/license/GPLv3License.txt
|
||||
//
|
||||
// This source code is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//-----------------------------------------------------------------------------
|
||||
#endregion Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
|
||||
|
||||
#region Using Directives
|
||||
using System;
|
||||
using System.Collections;
|
||||
using Technosoftware.DaAeHdaClient.Com.Da;
|
||||
using Technosoftware.DaAeHdaClient.Com.Utilities;
|
||||
using Technosoftware.DaAeHdaClient.Da;
|
||||
using Technosoftware.OpcRcw.Da;
|
||||
#endregion
|
||||
|
||||
namespace Technosoftware.DaAeHdaClient.Com.Da20
|
||||
{
|
||||
/// <summary>
|
||||
/// An in-process wrapper for a remote OPC Data Access 2.0X subscription.
|
||||
/// </summary>
|
||||
internal class Subscription : Technosoftware.DaAeHdaClient.Com.Da.Subscription
|
||||
{
|
||||
#region Constructors
|
||||
/// <summary>
|
||||
/// Initializes a new instance of a subscription.
|
||||
/// </summary>
|
||||
internal Subscription(object subscription, TsCDaSubscriptionState state, int filters) :
|
||||
base(subscription, state, filters)
|
||||
{
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ISubscription Members
|
||||
/// <summary>
|
||||
/// Returns the current state of the subscription.
|
||||
/// </summary>
|
||||
/// <returns>The current state of the subscription.</returns>
|
||||
public override TsCDaSubscriptionState GetState()
|
||||
{
|
||||
if (subscription_ == null) throw new NotConnectedException();
|
||||
lock (lock_)
|
||||
{
|
||||
var methodName = "IOPCGroupStateMgt.GetState";
|
||||
var state = new TsCDaSubscriptionState { ClientHandle = _handle };
|
||||
|
||||
string name = null;
|
||||
|
||||
try
|
||||
{
|
||||
var active = 0;
|
||||
var updateRate = 0;
|
||||
float deadband = 0;
|
||||
var timebias = 0;
|
||||
var localeID = 0;
|
||||
var clientHandle = 0;
|
||||
var serverHandle = 0;
|
||||
|
||||
var subscription = BeginComCall<IOPCGroupStateMgt>(methodName, true);
|
||||
subscription.GetState(
|
||||
out updateRate,
|
||||
out active,
|
||||
out name,
|
||||
out timebias,
|
||||
out deadband,
|
||||
out localeID,
|
||||
out clientHandle,
|
||||
out serverHandle);
|
||||
|
||||
if (DCOMCallWatchdog.IsCancelled)
|
||||
{
|
||||
throw new Exception($"{methodName} call was cancelled due to response timeout");
|
||||
}
|
||||
|
||||
state.Name = name;
|
||||
state.ServerHandle = serverHandle;
|
||||
state.Active = active != 0;
|
||||
state.UpdateRate = updateRate;
|
||||
state.TimeBias = timebias;
|
||||
state.Deadband = deadband;
|
||||
state.Locale = Technosoftware.DaAeHdaClient.Com.Interop.GetLocale(localeID);
|
||||
|
||||
// cache the name separately.
|
||||
name_ = state.Name;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ComCallError(methodName, e);
|
||||
throw Technosoftware.DaAeHdaClient.Com.Interop.CreateException(methodName, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EndComCall(methodName);
|
||||
}
|
||||
|
||||
state.KeepAlive = 0;
|
||||
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tells the server to send an data change update for all subscription items containing the cached values.
|
||||
/// </summary>
|
||||
public override void Refresh()
|
||||
{
|
||||
if (subscription_ == null) throw new NotConnectedException();
|
||||
lock (lock_)
|
||||
{
|
||||
var methodName = "IOPCAsyncIO2.Refresh2";
|
||||
try
|
||||
{
|
||||
var cancelID = 0;
|
||||
var subscription = BeginComCall<IOPCAsyncIO2>(methodName, true);
|
||||
subscription.Refresh2(OPCDATASOURCE.OPC_DS_CACHE, ++_counter, out cancelID);
|
||||
|
||||
if (DCOMCallWatchdog.IsCancelled)
|
||||
{
|
||||
throw new Exception($"{methodName} call was cancelled due to response timeout");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ComCallError(methodName, e);
|
||||
throw Utilities.Interop.CreateException(methodName, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EndComCall(methodName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets whether data change callbacks are enabled.
|
||||
/// </summary>
|
||||
public override void SetEnabled(bool enabled)
|
||||
{
|
||||
if (subscription_ == null) throw new NotConnectedException();
|
||||
lock (lock_)
|
||||
{
|
||||
var methodName = "IOPCAsyncIO2.SetEnable";
|
||||
try
|
||||
{
|
||||
var subscription = BeginComCall<IOPCAsyncIO2>(methodName, true);
|
||||
subscription.SetEnable((enabled) ? 1 : 0);
|
||||
|
||||
if (DCOMCallWatchdog.IsCancelled)
|
||||
{
|
||||
throw new Exception($"{methodName} call was cancelled due to response timeout");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ComCallError(methodName, e);
|
||||
throw Utilities.Interop.CreateException(methodName, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EndComCall(methodName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether data change callbacks are enabled.
|
||||
/// </summary>
|
||||
public override bool GetEnabled()
|
||||
{
|
||||
if (subscription_ == null) throw new NotConnectedException();
|
||||
lock (lock_)
|
||||
{
|
||||
var methodName = "IOPCAsyncIO2.GetEnable";
|
||||
try
|
||||
{
|
||||
var enabled = 0;
|
||||
var subscription = BeginComCall<IOPCAsyncIO2>(methodName, true);
|
||||
subscription.GetEnable(out enabled);
|
||||
|
||||
if (DCOMCallWatchdog.IsCancelled)
|
||||
{
|
||||
throw new Exception($"{methodName} call was cancelled due to response timeout");
|
||||
}
|
||||
|
||||
return enabled != 0;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ComCallError(methodName, e);
|
||||
throw Utilities.Interop.CreateException(methodName, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EndComCall(methodName);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private and Protected Members
|
||||
/// <summary>
|
||||
/// Reads a set of items using DA2.0 interfaces.
|
||||
/// </summary>
|
||||
protected override TsCDaItemValueResult[] Read(OpcItem[] itemIDs, TsCDaItem[] items)
|
||||
{
|
||||
if (subscription_ == null) throw new NotConnectedException();
|
||||
// create result list.
|
||||
var results = new TsCDaItemValueResult[itemIDs.Length];
|
||||
|
||||
// separate into cache reads and device reads.
|
||||
var cacheReads = new ArrayList();
|
||||
var deviceReads = new ArrayList();
|
||||
|
||||
for (var ii = 0; ii < itemIDs.Length; ii++)
|
||||
{
|
||||
results[ii] = new TsCDaItemValueResult(itemIDs[ii]);
|
||||
|
||||
if (items[ii].MaxAgeSpecified && (items[ii].MaxAge < 0 || items[ii].MaxAge == int.MaxValue))
|
||||
{
|
||||
cacheReads.Add(results[ii]);
|
||||
}
|
||||
else
|
||||
{
|
||||
deviceReads.Add(results[ii]);
|
||||
}
|
||||
}
|
||||
|
||||
// read items from cache.
|
||||
if (cacheReads.Count > 0)
|
||||
{
|
||||
Read((TsCDaItemValueResult[])cacheReads.ToArray(typeof(TsCDaItemValueResult)), true);
|
||||
}
|
||||
|
||||
// read items from device.
|
||||
if (deviceReads.Count > 0)
|
||||
{
|
||||
Read((TsCDaItemValueResult[])deviceReads.ToArray(typeof(TsCDaItemValueResult)), false);
|
||||
}
|
||||
|
||||
// return results.
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a set of values.
|
||||
/// </summary>
|
||||
private void Read(TsCDaItemValueResult[] items, bool cache)
|
||||
{
|
||||
if (items.Length == 0) return;
|
||||
|
||||
// marshal input parameters.
|
||||
var serverHandles = new int[items.Length];
|
||||
|
||||
for (var ii = 0; ii < items.Length; ii++)
|
||||
{
|
||||
serverHandles[ii] = (int)items[ii].ServerHandle;
|
||||
}
|
||||
|
||||
// initialize output parameters.
|
||||
var pValues = IntPtr.Zero;
|
||||
var pErrors = IntPtr.Zero;
|
||||
|
||||
var methodName = "IOPCSyncIO.Read";
|
||||
try
|
||||
{
|
||||
var subscription = BeginComCall<IOPCSyncIO>(methodName, true);
|
||||
subscription.Read(
|
||||
(cache) ? OPCDATASOURCE.OPC_DS_CACHE : OPCDATASOURCE.OPC_DS_DEVICE,
|
||||
items.Length,
|
||||
serverHandles,
|
||||
out pValues,
|
||||
out pErrors);
|
||||
|
||||
if (DCOMCallWatchdog.IsCancelled)
|
||||
{
|
||||
throw new Exception($"{methodName} call was cancelled due to response timeout");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ComCallError(methodName, e);
|
||||
throw Utilities.Interop.CreateException(methodName, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EndComCall(methodName);
|
||||
}
|
||||
|
||||
// unmarshal output parameters.
|
||||
var values = Technosoftware.DaAeHdaClient.Com.Da.Interop.GetItemValues(ref pValues, items.Length, true);
|
||||
var errors = Utilities.Interop.GetInt32s(ref pErrors, items.Length, true);
|
||||
|
||||
// construct results list.
|
||||
for (var ii = 0; ii < items.Length; ii++)
|
||||
{
|
||||
items[ii].Result = Utilities.Interop.GetResultId(errors[ii]);
|
||||
items[ii].DiagnosticInfo = null;
|
||||
|
||||
// convert COM code to unified DA code.
|
||||
if (errors[ii] == Result.E_BADRIGHTS) { items[ii].Result = new OpcResult(OpcResult.Da.E_WRITEONLY, Result.E_BADRIGHTS); }
|
||||
|
||||
if (items[ii].Result.Succeeded())
|
||||
{
|
||||
items[ii].Value = values[ii].Value;
|
||||
items[ii].Quality = values[ii].Quality;
|
||||
items[ii].QualitySpecified = values[ii].QualitySpecified;
|
||||
items[ii].Timestamp = values[ii].Timestamp;
|
||||
items[ii].TimestampSpecified = values[ii].TimestampSpecified;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a set of items using DA2.0 interfaces.
|
||||
/// </summary>
|
||||
protected override OpcItemResult[] Write(OpcItem[] itemIDs, TsCDaItemValue[] items)
|
||||
{
|
||||
if (subscription_ == null) throw new NotConnectedException();
|
||||
// create result list.
|
||||
var results = new OpcItemResult[itemIDs.Length];
|
||||
|
||||
// construct list of valid items to write.
|
||||
var writeItems = new ArrayList(itemIDs.Length);
|
||||
var writeValues = new ArrayList(itemIDs.Length);
|
||||
|
||||
for (var ii = 0; ii < items.Length; ii++)
|
||||
{
|
||||
results[ii] = new OpcItemResult(itemIDs[ii]);
|
||||
|
||||
if (items[ii].QualitySpecified || items[ii].TimestampSpecified)
|
||||
{
|
||||
results[ii].Result = OpcResult.Da.E_NO_WRITEQT;
|
||||
results[ii].DiagnosticInfo = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
writeItems.Add(results[ii]);
|
||||
writeValues.Add(items[ii]);
|
||||
}
|
||||
|
||||
// check if there is nothing to do.
|
||||
if (writeItems.Count == 0)
|
||||
{
|
||||
return results;
|
||||
}
|
||||
|
||||
// initialize input parameters.
|
||||
var serverHandles = new int[writeItems.Count];
|
||||
var values = new object[writeItems.Count];
|
||||
|
||||
for (var ii = 0; ii < serverHandles.Length; ii++)
|
||||
{
|
||||
serverHandles[ii] = (int)((OpcItemResult)writeItems[ii]).ServerHandle;
|
||||
values[ii] = Utilities.Interop.GetVARIANT(((TsCDaItemValue)writeValues[ii]).Value);
|
||||
}
|
||||
|
||||
var pErrors = IntPtr.Zero;
|
||||
|
||||
// write item values.
|
||||
var methodName = "IOPCSyncIO.Write";
|
||||
try
|
||||
{
|
||||
var subscription = BeginComCall<IOPCSyncIO>(methodName, true);
|
||||
subscription.Write(
|
||||
writeItems.Count,
|
||||
serverHandles,
|
||||
values,
|
||||
out pErrors);
|
||||
|
||||
if (DCOMCallWatchdog.IsCancelled)
|
||||
{
|
||||
throw new Exception($"{methodName} call was cancelled due to response timeout");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ComCallError(methodName, e);
|
||||
throw Utilities.Interop.CreateException(methodName, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EndComCall(methodName);
|
||||
}
|
||||
|
||||
// unmarshal results.
|
||||
var errors = Utilities.Interop.GetInt32s(ref pErrors, writeItems.Count, true);
|
||||
|
||||
for (var ii = 0; ii < writeItems.Count; ii++)
|
||||
{
|
||||
var result = (OpcItemResult)writeItems[ii];
|
||||
|
||||
result.Result = Utilities.Interop.GetResultId(errors[ii]);
|
||||
result.DiagnosticInfo = null;
|
||||
|
||||
// convert COM code to unified DA code.
|
||||
if (errors[ii] == Result.E_BADRIGHTS) { results[ii].Result = new OpcResult(OpcResult.Da.E_READONLY, Result.E_BADRIGHTS); }
|
||||
}
|
||||
|
||||
// return results.
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Begins an asynchronous read of a set of items using DA2.0 interfaces.
|
||||
/// </summary>
|
||||
protected override OpcItemResult[] BeginRead(
|
||||
OpcItem[] itemIDs,
|
||||
TsCDaItem[] items,
|
||||
int requestID,
|
||||
out int cancelID)
|
||||
{
|
||||
var methodName = "IOPCAsyncIO2.Read";
|
||||
try
|
||||
{
|
||||
// marshal input parameters.
|
||||
var serverHandles = new int[itemIDs.Length];
|
||||
|
||||
for (var ii = 0; ii < itemIDs.Length; ii++)
|
||||
{
|
||||
serverHandles[ii] = (int)itemIDs[ii].ServerHandle;
|
||||
}
|
||||
|
||||
// initialize output parameters.
|
||||
var pErrors = IntPtr.Zero;
|
||||
|
||||
var subscription = BeginComCall<IOPCAsyncIO2>(methodName, true);
|
||||
subscription.Read(
|
||||
itemIDs.Length,
|
||||
serverHandles,
|
||||
requestID,
|
||||
out cancelID,
|
||||
out pErrors);
|
||||
|
||||
if (DCOMCallWatchdog.IsCancelled)
|
||||
{
|
||||
throw new Exception($"{methodName} call was cancelled due to response timeout");
|
||||
}
|
||||
|
||||
// unmarshal output parameters.
|
||||
var errors = Utilities.Interop.GetInt32s(ref pErrors, itemIDs.Length, true);
|
||||
|
||||
// create item results.
|
||||
var results = new OpcItemResult[itemIDs.Length];
|
||||
|
||||
for (var ii = 0; ii < itemIDs.Length; ii++)
|
||||
{
|
||||
results[ii] = new OpcItemResult(itemIDs[ii]);
|
||||
results[ii].Result = Utilities.Interop.GetResultId(errors[ii]);
|
||||
results[ii].DiagnosticInfo = null;
|
||||
|
||||
// convert COM code to unified DA code.
|
||||
if (errors[ii] == Result.E_BADRIGHTS) { results[ii].Result = new OpcResult(OpcResult.Da.E_WRITEONLY, Result.E_BADRIGHTS); }
|
||||
}
|
||||
|
||||
// return results.
|
||||
return results;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ComCallError(methodName, e);
|
||||
throw Utilities.Interop.CreateException(methodName, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EndComCall(methodName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Begins an asynchronous write for a set of items using DA2.0 interfaces.
|
||||
/// </summary>
|
||||
protected override OpcItemResult[] BeginWrite(
|
||||
OpcItem[] itemIDs,
|
||||
TsCDaItemValue[] items,
|
||||
int requestID,
|
||||
out int cancelID)
|
||||
{
|
||||
cancelID = 0;
|
||||
|
||||
var validItems = new ArrayList();
|
||||
var validValues = new ArrayList();
|
||||
|
||||
// construct initial result list.
|
||||
var results = new OpcItemResult[itemIDs.Length];
|
||||
|
||||
for (var ii = 0; ii < itemIDs.Length; ii++)
|
||||
{
|
||||
results[ii] = new OpcItemResult(itemIDs[ii]);
|
||||
|
||||
results[ii].Result = OpcResult.S_OK;
|
||||
results[ii].DiagnosticInfo = null;
|
||||
|
||||
if (items[ii].QualitySpecified || items[ii].TimestampSpecified)
|
||||
{
|
||||
results[ii].Result = OpcResult.Da.E_NO_WRITEQT;
|
||||
results[ii].DiagnosticInfo = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
validItems.Add(results[ii]);
|
||||
validValues.Add(Utilities.Interop.GetVARIANT(items[ii].Value));
|
||||
}
|
||||
|
||||
// check if any valid items exist.
|
||||
if (validItems.Count == 0)
|
||||
{
|
||||
return results;
|
||||
}
|
||||
|
||||
var methodName = "IOPCAsyncIO2.Write";
|
||||
try
|
||||
{
|
||||
// initialize input parameters.
|
||||
var serverHandles = new int[validItems.Count];
|
||||
|
||||
for (var ii = 0; ii < validItems.Count; ii++)
|
||||
{
|
||||
serverHandles[ii] = (int)((OpcItemResult)validItems[ii]).ServerHandle;
|
||||
}
|
||||
|
||||
// write to sever.
|
||||
var pErrors = IntPtr.Zero;
|
||||
|
||||
var subscription = BeginComCall<IOPCAsyncIO2>(methodName, true);
|
||||
subscription.Write(
|
||||
validItems.Count,
|
||||
serverHandles,
|
||||
(object[])validValues.ToArray(typeof(object)),
|
||||
requestID,
|
||||
out cancelID,
|
||||
out pErrors);
|
||||
|
||||
if (DCOMCallWatchdog.IsCancelled)
|
||||
{
|
||||
throw new Exception($"{methodName} call was cancelled due to response timeout");
|
||||
}
|
||||
|
||||
// unmarshal results.
|
||||
var errors = Utilities.Interop.GetInt32s(ref pErrors, validItems.Count, true);
|
||||
|
||||
// create result list.
|
||||
for (var ii = 0; ii < validItems.Count; ii++)
|
||||
{
|
||||
var result = (OpcItemResult)validItems[ii];
|
||||
|
||||
result.Result = Utilities.Interop.GetResultId(errors[ii]);
|
||||
result.DiagnosticInfo = null;
|
||||
|
||||
// convert COM code to unified DA code.
|
||||
if (errors[ii] == Result.E_BADRIGHTS) { results[ii].Result = new OpcResult(OpcResult.Da.E_READONLY, Result.E_BADRIGHTS); }
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ComCallError(methodName, e);
|
||||
throw Utilities.Interop.CreateException(methodName, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EndComCall(methodName);
|
||||
}
|
||||
|
||||
// return results.
|
||||
return results;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
128
Technosoftware/DaAeHdaClient.Com/EnumString.cs
Normal file
128
Technosoftware/DaAeHdaClient.Com/EnumString.cs
Normal file
@@ -0,0 +1,128 @@
|
||||
#region Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
|
||||
// Web: https://www.technosoftware.com
|
||||
//
|
||||
// The source code in this file is covered under a dual-license scenario:
|
||||
// - Owner of a purchased license: SCLA 1.0
|
||||
// - GPL V3: everybody else
|
||||
//
|
||||
// SCLA license terms accompanied with this source code.
|
||||
// See SCLA 1.0: https://technosoftware.com/license/Source_Code_License_Agreement.pdf
|
||||
//
|
||||
// GNU General Public License as published by the Free Software Foundation;
|
||||
// version 3 of the License are accompanied with this source code.
|
||||
// See https://technosoftware.com/license/GPLv3License.txt
|
||||
//
|
||||
// This source code is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//-----------------------------------------------------------------------------
|
||||
#endregion Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
|
||||
|
||||
#region Using Directives
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Technosoftware.OpcRcw.Comn;
|
||||
#endregion
|
||||
|
||||
namespace Technosoftware.DaAeHdaClient.Com
|
||||
{
|
||||
/// <summary>
|
||||
/// A wrapper for the COM IEnumString interface.
|
||||
/// </summary>
|
||||
internal class EnumString : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// A reference to the remote COM object.
|
||||
/// </summary>
|
||||
private IEnumString m_enumerator = null;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the object with an enumerator.
|
||||
/// </summary>
|
||||
public EnumString(object enumerator)
|
||||
{
|
||||
m_enumerator = (IEnumString)enumerator;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases the remote COM object.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Utilities.Interop.ReleaseServer(m_enumerator);
|
||||
m_enumerator = null;
|
||||
}
|
||||
|
||||
//=====================================================================
|
||||
// IEnumString
|
||||
|
||||
/// <summary>
|
||||
/// Fetches the next subscription of strings.
|
||||
/// </summary>
|
||||
public string[] Next(int count)
|
||||
{
|
||||
try
|
||||
{
|
||||
// create buffer.
|
||||
var buffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(IntPtr)) * count);
|
||||
|
||||
try
|
||||
{
|
||||
// fetch next subscription of strings.
|
||||
var fetched = 0;
|
||||
|
||||
m_enumerator.RemoteNext(
|
||||
count,
|
||||
buffer,
|
||||
out fetched);
|
||||
|
||||
// return empty array if at end of list.
|
||||
if (fetched == 0)
|
||||
{
|
||||
return new string[0];
|
||||
}
|
||||
|
||||
return Interop.GetUnicodeStrings(ref buffer, fetched, true);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeCoTaskMem(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
// return null on any error.
|
||||
catch (Exception)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Skips a number of strings.
|
||||
/// </summary>
|
||||
public void Skip(int count)
|
||||
{
|
||||
m_enumerator.Skip(count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets pointer to the start of the list.
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
m_enumerator.Reset();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clones the enumerator.
|
||||
/// </summary>
|
||||
public EnumString Clone()
|
||||
{
|
||||
IEnumString enumerator;
|
||||
m_enumerator.Clone(out enumerator);
|
||||
return new EnumString(enumerator);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user