opc fix
This commit is contained in:
73
Modbus.Net/Modbus.Net.Opc/AddressFormaterOpc.cs
Normal file
73
Modbus.Net/Modbus.Net.Opc/AddressFormaterOpc.cs
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Modbus.Net.Opc
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Opc地址编码器
|
||||||
|
/// </summary>
|
||||||
|
public class AddressFormaterOpc<TMachineKey, TUnitKey> : AddressFormater where TMachineKey : IEquatable<TMachineKey>
|
||||||
|
where TUnitKey : IEquatable<TUnitKey>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 协议构造器
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tagGeter">如何通过BaseMachine和AddressUnit构造Opc的标签</param>
|
||||||
|
/// <param name="machine">调用这个编码器的设备</param>
|
||||||
|
/// <param name="seperator">每两个标签之间用什么符号隔开,默认为/</param>
|
||||||
|
public AddressFormaterOpc(Func<BaseMachine<TMachineKey, TUnitKey>, AddressUnit<TUnitKey>, string[]> tagGeter,
|
||||||
|
BaseMachine<TMachineKey, TUnitKey> machine,
|
||||||
|
char seperator = '/')
|
||||||
|
{
|
||||||
|
Machine = machine;
|
||||||
|
TagGeter = tagGeter;
|
||||||
|
Seperator = seperator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设备
|
||||||
|
/// </summary>
|
||||||
|
public BaseMachine<TMachineKey, TUnitKey> Machine { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 标签构造器
|
||||||
|
/// (设备,地址)->不具备分隔符的标签数组
|
||||||
|
/// </summary>
|
||||||
|
protected Func<BaseMachine<TMachineKey, TUnitKey>, AddressUnit<TUnitKey>, string[]> TagGeter { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 分割符
|
||||||
|
/// </summary>
|
||||||
|
public char Seperator { get; protected set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 编码地址
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="area">地址所在的数据区域</param>
|
||||||
|
/// <param name="address">地址</param>
|
||||||
|
/// <returns>编码后的地址</returns>
|
||||||
|
public override string FormatAddress(string area, int address)
|
||||||
|
{
|
||||||
|
var findAddress = Machine?.GetAddresses.FirstOrDefault(p => p.Area == area && p.Address == 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);
|
||||||
|
return ans;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 编码地址
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="area">地址所在的数据区域</param>
|
||||||
|
/// <param name="address">地址</param>
|
||||||
|
/// <param name="subAddress">子地址(忽略)</param>
|
||||||
|
/// <returns>编码后的地址</returns>
|
||||||
|
public override string FormatAddress(string area, int address, int subAddress)
|
||||||
|
{
|
||||||
|
return FormatAddress(area, address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
31
Modbus.Net/Modbus.Net.Opc/AddressTranslatorOpc.cs
Normal file
31
Modbus.Net/Modbus.Net.Opc/AddressTranslatorOpc.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Modbus.Net.Opc
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Opc地址解析器
|
||||||
|
/// </summary>
|
||||||
|
public class AddressTranslatorOpc : AddressTranslator
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 地址转换
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="address">格式化的地址</param>
|
||||||
|
/// <param name="isRead">是否为读取,是为读取,否为写入</param>
|
||||||
|
/// <returns>翻译后的地址</returns>
|
||||||
|
public override AddressDef AddressTranslate(string address, bool isRead)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取区域中的单个地址占用的字节长度
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="area">区域名称</param>
|
||||||
|
/// <returns>字节长度</returns>
|
||||||
|
public override double GetAreaByteLength(string area)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
146
Modbus.Net/Modbus.Net.Opc/ClientExtend.cs
Normal file
146
Modbus.Net/Modbus.Net.Opc/ClientExtend.cs
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
using Hylasoft.Opc.Common;
|
||||||
|
using Hylasoft.Opc.Da;
|
||||||
|
using Hylasoft.Opc.Ua;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Modbus.Net.Opc
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Opc Client Extend interface, Unified for DA and UA
|
||||||
|
/// </summary>
|
||||||
|
public interface IClientExtend : IDisposable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Unified Root Node
|
||||||
|
/// </summary>
|
||||||
|
Node RootNodeBase { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Connect the client to the Opc Server
|
||||||
|
/// </summary>
|
||||||
|
void Connect();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read a tag
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of tag to read</typeparam>
|
||||||
|
/// <param name="tag">
|
||||||
|
/// The fully-qualified identifier of the tag. You can specify a subfolder by using a comma delimited name.
|
||||||
|
/// E.g: the tag `foo.bar` reads the tag `bar` on the folder `foo`
|
||||||
|
/// </param>
|
||||||
|
/// <returns>The value retrieved from the Opc</returns>
|
||||||
|
ReadEvent<T> Read<T>(string tag);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write a value on the specified Opc tag
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of tag to write on</typeparam>
|
||||||
|
/// <param name="tag">
|
||||||
|
/// The fully-qualified identifier of the tag. You can specify a subfolder by using a comma delimited name.
|
||||||
|
/// E.g: the tag `foo.bar` writes on the tag `bar` on the folder `foo`
|
||||||
|
/// </param>
|
||||||
|
/// <param name="item"></param>
|
||||||
|
void Write<T>(string tag, T item);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read a tag asynchronusly
|
||||||
|
/// </summary>
|
||||||
|
Task<ReadEvent<T>> ReadAsync<T>(string tag);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write a value on the specified Opc tag asynchronously
|
||||||
|
/// </summary>
|
||||||
|
Task WriteAsync<T>(string tag, T item);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds a node on the Opc Server asynchronously
|
||||||
|
/// </summary>
|
||||||
|
Task<Node> FindNodeAsync(string tag);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Explore a folder on the Opc Server asynchronously
|
||||||
|
/// </summary>
|
||||||
|
Task<IEnumerable<Node>> ExploreFolderAsync(string tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// UaClient Extend
|
||||||
|
/// </summary>
|
||||||
|
public class MyDaClient : DaClient, IClientExtend
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// UaClient Extend
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="serverUrl">Url address of Opc UA server</param>
|
||||||
|
public MyDaClient(Uri serverUrl) : base(serverUrl)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unified root node
|
||||||
|
/// </summary>
|
||||||
|
public Node RootNodeBase => RootNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DaClient Extend
|
||||||
|
/// </summary>
|
||||||
|
public class MyUaClient : UaClient, IClientExtend
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// DaClient Extend
|
||||||
|
/// </summary>
|
||||||
|
public MyUaClient(Uri serverUrl) : base(serverUrl)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unified root node
|
||||||
|
/// </summary>
|
||||||
|
public Node RootNodeBase => RootNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Param input of OpcConnector
|
||||||
|
/// </summary>
|
||||||
|
public class OpcParamIn
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Is the action read (not is write)
|
||||||
|
/// </summary>
|
||||||
|
public bool IsRead { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tag of a node
|
||||||
|
/// </summary>
|
||||||
|
public string[] Tag { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tag splitter of a node
|
||||||
|
/// </summary>
|
||||||
|
public char Split { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The value set to node(only available when IsRead is false
|
||||||
|
/// </summary>
|
||||||
|
public object SetValue { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Param output of OpcConnector
|
||||||
|
/// </summary>
|
||||||
|
public class OpcParamOut
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Is the action success
|
||||||
|
/// </summary>
|
||||||
|
public bool Success { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Action return values
|
||||||
|
/// </summary>
|
||||||
|
public byte[] Value { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
43
Modbus.Net/Modbus.Net.Opc/Modbus.Net.Opc.csproj
Normal file
43
Modbus.Net/Modbus.Net.Opc/Modbus.Net.Opc.csproj
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net462</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>
|
||||||
|
<Authors>Chris L.(Luo Sheng)</Authors>
|
||||||
|
<Company>Hangzhou Delian Science Technology Co.,Ltd.</Company>
|
||||||
|
<Product>Modbus.Net.Opc</Product>
|
||||||
|
<Description>Modbus.Net Opc 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.Opc</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>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
<DocumentationFile>bin\Debug\Modbus.Net.Opc.xml</DocumentationFile>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="H.Opc" Version="0.9.3" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Modbus.Net\Modbus.Net.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="README.md" Pack="true" PackagePath="" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
220
Modbus.Net/Modbus.Net.Opc/OpcConnector.cs
Normal file
220
Modbus.Net/Modbus.Net.Opc/OpcConnector.cs
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
using Hylasoft.Opc.Common;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Modbus.Net.Opc
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Opc连接器
|
||||||
|
/// </summary>
|
||||||
|
public abstract class OpcConnector : BaseConnector<OpcParamIn, OpcParamOut>
|
||||||
|
{
|
||||||
|
private static readonly ILogger<OpcConnector> logger = LogProvider.CreateLogger<OpcConnector>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否正在连接
|
||||||
|
/// </summary>
|
||||||
|
protected bool _connect;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Opc客户端
|
||||||
|
/// </summary>
|
||||||
|
protected IClientExtend Client;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否开启正则匹配
|
||||||
|
/// </summary>
|
||||||
|
protected bool RegexOn { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="host">服务端url</param>
|
||||||
|
/// <param name="isRegexOn">是否开启正则匹配</param>
|
||||||
|
protected OpcConnector(string host, bool isRegexOn)
|
||||||
|
{
|
||||||
|
ConnectionToken = host;
|
||||||
|
RegexOn = isRegexOn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 连接标识
|
||||||
|
/// </summary>
|
||||||
|
public override string ConnectionToken { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否正在连接
|
||||||
|
/// </summary>
|
||||||
|
public override bool IsConnected => _connect;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 断开连接
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override bool Disconnect()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Client?.Dispose();
|
||||||
|
Client = null;
|
||||||
|
_connect = false;
|
||||||
|
logger.LogInformation("Opc client {ConnectionToken} disconnected success", ConnectionToken);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.LogError(ex, "Opc client {ConnectionToken} disconnected error", ConnectionToken);
|
||||||
|
_connect = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void ReceiveMsgThreadStart()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void ReceiveMsgThreadStop()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override Task SendMsgWithoutConfirm(OpcParamIn message)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 带返回发送数据
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">需要发送的数据</param>
|
||||||
|
/// <returns>是否发送成功</returns>
|
||||||
|
public override async Task<OpcParamOut> SendMsgAsync(OpcParamIn message)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (message.IsRead)
|
||||||
|
{
|
||||||
|
var split = message.Split;
|
||||||
|
var tag = message.Tag;
|
||||||
|
var rootDirectory = await Client.ExploreFolderAsync("");
|
||||||
|
var answerTag = await SearchTag(tag, split, 0, rootDirectory);
|
||||||
|
if (answerTag != null)
|
||||||
|
{
|
||||||
|
var result = await Client.ReadAsync<object>(answerTag);
|
||||||
|
logger.LogDebug($"Opc Machine {ConnectionToken} Read Opc tag {answerTag} for value {result.Value}");
|
||||||
|
return new OpcParamOut
|
||||||
|
{
|
||||||
|
Success = true,
|
||||||
|
Value = BigEndianValueHelper.Instance.GetBytes(result.Value, result.Value.GetType())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return new OpcParamOut
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
Value = Encoding.ASCII.GetBytes("NoData")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var tag = message.Tag;
|
||||||
|
var split = message.Split;
|
||||||
|
var value = message.SetValue;
|
||||||
|
|
||||||
|
var rootDirectory = await Client.ExploreFolderAsync("");
|
||||||
|
var answerTag = await SearchTag(tag, split, 0, rootDirectory);
|
||||||
|
if (answerTag != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Client.WriteAsync(answerTag, value);
|
||||||
|
logger.LogDebug($"Opc Machine {ConnectionToken} Write Opc tag {answerTag} for value {value}");
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logger.LogError(e, "Opc client {ConnectionToken} write exception", ConnectionToken);
|
||||||
|
return new OpcParamOut
|
||||||
|
{
|
||||||
|
Success = false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return new OpcParamOut
|
||||||
|
{
|
||||||
|
Success = true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return new OpcParamOut
|
||||||
|
{
|
||||||
|
Success = false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logger.LogError(e, "Opc client {ConnectionToken} read exception", ConnectionToken);
|
||||||
|
return new OpcParamOut
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
Value = Encoding.ASCII.GetBytes("NoData")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 搜索标签
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tags">标签</param>
|
||||||
|
/// <param name="split">分隔符</param>
|
||||||
|
/// <param name="deep">递归深度(第几级标签)</param>
|
||||||
|
/// <param name="nodes">当前搜索的节点</param>
|
||||||
|
/// <returns>搜索到的标签</returns>
|
||||||
|
private async Task<string> SearchTag(string[] tags, char split, int deep, IEnumerable<Node> nodes)
|
||||||
|
{
|
||||||
|
foreach (var node in nodes)
|
||||||
|
{
|
||||||
|
var currentTag = node.Tag.Substring(node.Tag.LastIndexOf(split) + 1);
|
||||||
|
if (RegexOn && Regex.IsMatch(currentTag, tags[deep]) || !RegexOn && currentTag == tags[deep])
|
||||||
|
{
|
||||||
|
if (deep == tags.Length - 1) return node.Tag;
|
||||||
|
var subDirectories = await Client.ExploreFolderAsync(node.Tag);
|
||||||
|
var answerTag = await SearchTag(tags, split, deep + 1, subDirectories);
|
||||||
|
if (answerTag != null) return answerTag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool Connect()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Client.Connect();
|
||||||
|
_connect = true;
|
||||||
|
logger.LogInformation("Opc client {ConnectionToken} connect success", ConnectionToken);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.LogError(ex, "Opc client {ConnectionToken} connected failed", ConnectionToken);
|
||||||
|
_connect = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 连接PLC,异步
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>是否连接成功</returns>
|
||||||
|
public override Task<bool> ConnectAsync()
|
||||||
|
{
|
||||||
|
return Task.FromResult(Connect());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
42
Modbus.Net/Modbus.Net.Opc/OpcDaConnector.cs
Normal file
42
Modbus.Net/Modbus.Net.Opc/OpcDaConnector.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Modbus.Net.Opc
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Opc DA连接实现
|
||||||
|
/// </summary>
|
||||||
|
public class OpcDaConnector : OpcConnector
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// DA单例管理
|
||||||
|
/// </summary>
|
||||||
|
protected static Dictionary<string, OpcDaConnector> _instances = new Dictionary<string, OpcDaConnector>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="host">Opc DA 服务地址</param>
|
||||||
|
/// <param name="isRegexOn">是否开启正则匹配</param>
|
||||||
|
protected OpcDaConnector(string host, bool isRegexOn) : base(host, isRegexOn)
|
||||||
|
{
|
||||||
|
Client = new MyDaClient(new Uri(ConnectionToken));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据服务地址生成DA单例
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="host">Opc DA 服务地址</param>
|
||||||
|
/// <param name="isRegexOn">是否开启正则匹配</param>
|
||||||
|
/// <returns>Opc DA 连接器实例</returns>
|
||||||
|
public static OpcDaConnector Instance(string host, bool isRegexOn)
|
||||||
|
{
|
||||||
|
if (!_instances.ContainsKey(host))
|
||||||
|
{
|
||||||
|
var connector = new OpcDaConnector(host, isRegexOn);
|
||||||
|
_instances.Add(host, connector);
|
||||||
|
}
|
||||||
|
return _instances[host];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
38
Modbus.Net/Modbus.Net.Opc/OpcDaProtocal.cs
Normal file
38
Modbus.Net/Modbus.Net.Opc/OpcDaProtocal.cs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Modbus.Net.Opc
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Opc Da协议
|
||||||
|
/// </summary>
|
||||||
|
public class OpcDaProtocol : OpcProtocol
|
||||||
|
{
|
||||||
|
private readonly string _host;
|
||||||
|
|
||||||
|
private readonly bool _isRegexOn;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="host">Opc DA服务地址</param>
|
||||||
|
/// <param name="isRegexOn">是否开启正则匹配</param>
|
||||||
|
public OpcDaProtocol(string host, bool isRegexOn)
|
||||||
|
{
|
||||||
|
_host = host;
|
||||||
|
_isRegexOn = isRegexOn;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 连接设备
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>是否连接成功</returns>
|
||||||
|
public override async Task<bool> ConnectAsync()
|
||||||
|
{
|
||||||
|
ProtocolLinker = new OpcDaProtocolLinker(_host, _isRegexOn);
|
||||||
|
if (!await ProtocolLinker.ConnectAsync())
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
26
Modbus.Net/Modbus.Net.Opc/OpcDaProtocalLinker.cs
Normal file
26
Modbus.Net/Modbus.Net.Opc/OpcDaProtocalLinker.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
namespace Modbus.Net.Opc
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Opc Da协议连接器
|
||||||
|
/// </summary>
|
||||||
|
public class OpcDaProtocolLinker : OpcProtocolLinker
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="isRegexOn">是否开启正则匹配</param>
|
||||||
|
public OpcDaProtocolLinker(bool isRegexOn) : this(ConfigurationReader.GetValueDirect("OpcDa", "Host"), isRegexOn)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="host">Opc DA服务地址</param>
|
||||||
|
/// <param name="isRegexOn">是否开启正则匹配</param>
|
||||||
|
public OpcDaProtocolLinker(string host, bool isRegexOn)
|
||||||
|
{
|
||||||
|
BaseConnector = OpcDaConnector.Instance(host, isRegexOn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
31
Modbus.Net/Modbus.Net.Opc/OpcMachine.cs
Normal file
31
Modbus.Net/Modbus.Net.Opc/OpcMachine.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Modbus.Net.Opc
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Opc设备
|
||||||
|
/// </summary>
|
||||||
|
public class OpcMachine<TKey, TUnitKey> : BaseMachine<TKey, TUnitKey> where TKey : IEquatable<TKey>
|
||||||
|
where TUnitKey : IEquatable<TUnitKey>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">设备的ID号</param>
|
||||||
|
/// <param name="connectionType">连接类型</param>
|
||||||
|
/// <param name="connectionString">连接地址</param>
|
||||||
|
/// <param name="getAddresses">需要读写的地址</param>
|
||||||
|
/// <param name="isRegexOn">开启正则匹配</param>
|
||||||
|
public OpcMachine(TKey id, OpcType connectionType, string connectionString, IEnumerable<AddressUnit<TUnitKey>> getAddresses, bool isRegexOn = false)
|
||||||
|
: base(id, getAddresses, true)
|
||||||
|
{
|
||||||
|
BaseUtility = new OpcUtility(connectionType, connectionString, isRegexOn);
|
||||||
|
AddressFormater = new AddressFormaterOpc<TKey, TUnitKey>((machine, unit) => { return new string[] { unit.Area }; }, this);
|
||||||
|
((OpcUtility)BaseUtility).GetSeperator +=
|
||||||
|
() => ((AddressFormaterOpc<TKey, TUnitKey>)AddressFormater).Seperator;
|
||||||
|
AddressCombiner = new AddressCombinerSingle<TUnitKey>();
|
||||||
|
AddressCombinerSet = new AddressCombinerSingle<TUnitKey>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
197
Modbus.Net/Modbus.Net.Opc/OpcProtocol.cs
Normal file
197
Modbus.Net/Modbus.Net.Opc/OpcProtocol.cs
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
namespace Modbus.Net.Opc
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Opc协议
|
||||||
|
/// </summary>
|
||||||
|
public abstract class OpcProtocol : BaseProtocol<OpcParamIn, OpcParamOut, ProtocolUnit<OpcParamIn, OpcParamOut>,
|
||||||
|
PipeUnit<OpcParamIn, OpcParamOut, IProtocolLinker<OpcParamIn, OpcParamOut>,
|
||||||
|
ProtocolUnit<OpcParamIn, OpcParamOut>>>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
protected OpcProtocol() : base(0, 0, Endian.BigEndianLsb)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region 读数据
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 读数据输入
|
||||||
|
/// </summary>
|
||||||
|
public class ReadRequestOpcInputStruct : IInputStruct
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tag">标签</param>
|
||||||
|
/// <param name="split">分隔符</param>
|
||||||
|
public ReadRequestOpcInputStruct(string[] tag, char split)
|
||||||
|
{
|
||||||
|
Tag = tag;
|
||||||
|
Split = split;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 标签
|
||||||
|
/// </summary>
|
||||||
|
public string[] Tag { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 分隔符
|
||||||
|
/// </summary>
|
||||||
|
public char Split { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 读地址输出
|
||||||
|
/// </summary>
|
||||||
|
public class ReadRequestOpcOutputStruct : IOutputStruct
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">读取的数据</param>
|
||||||
|
public ReadRequestOpcOutputStruct(byte[] value)
|
||||||
|
{
|
||||||
|
GetValue = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 读取的地址
|
||||||
|
/// </summary>
|
||||||
|
public byte[] GetValue { get; private set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 读数据协议
|
||||||
|
/// </summary>
|
||||||
|
[SpecialProtocolUnit]
|
||||||
|
public class ReadRequestOpcProtocol : ProtocolUnit<OpcParamIn, OpcParamOut>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 从对象的参数数组格式化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">非结构化的输入数据</param>
|
||||||
|
/// <returns>格式化后的字节流</returns>
|
||||||
|
public override OpcParamIn Format(IInputStruct message)
|
||||||
|
{
|
||||||
|
var r_message = (ReadRequestOpcInputStruct)message;
|
||||||
|
return new OpcParamIn
|
||||||
|
{
|
||||||
|
IsRead = true,
|
||||||
|
Tag = r_message.Tag,
|
||||||
|
Split = r_message.Split
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 把仪器返回的内容填充到输出结构中
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="messageBytes">返回数据的字节流</param>
|
||||||
|
/// <param name="pos">转换标记位</param>
|
||||||
|
/// <returns>结构化的输出数据</returns>
|
||||||
|
public override IOutputStruct Unformat(OpcParamOut messageBytes, ref int pos)
|
||||||
|
{
|
||||||
|
return new ReadRequestOpcOutputStruct(messageBytes.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 写数据
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 写数据输入
|
||||||
|
/// </summary>
|
||||||
|
public class WriteRequestOpcInputStruct : IInputStruct
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tag">标签</param>
|
||||||
|
/// <param name="split">分隔符</param>
|
||||||
|
/// <param name="setValue">写入的数据</param>
|
||||||
|
public WriteRequestOpcInputStruct(string[] tag, char split, object setValue)
|
||||||
|
{
|
||||||
|
Tag = tag;
|
||||||
|
Split = split;
|
||||||
|
SetValue = setValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 标签
|
||||||
|
/// </summary>
|
||||||
|
public string[] Tag { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 分隔符
|
||||||
|
/// </summary>
|
||||||
|
public char Split { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 写入的数据
|
||||||
|
/// </summary>
|
||||||
|
public object SetValue { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 写数据输出
|
||||||
|
/// </summary>
|
||||||
|
public class WriteRequestOpcOutputStruct : IOutputStruct
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="writeResult">写入是否成功</param>
|
||||||
|
public WriteRequestOpcOutputStruct(bool writeResult)
|
||||||
|
{
|
||||||
|
WriteResult = writeResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 写入是否成功
|
||||||
|
/// </summary>
|
||||||
|
public bool WriteResult { get; private set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 写数据协议
|
||||||
|
/// </summary>
|
||||||
|
[SpecialProtocolUnit]
|
||||||
|
public class WriteRequestOpcProtocol : ProtocolUnit<OpcParamIn, OpcParamOut>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 从对象的参数数组格式化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">非结构化的输入数据</param>
|
||||||
|
/// <returns>格式化后的字节流</returns>
|
||||||
|
public override OpcParamIn Format(IInputStruct message)
|
||||||
|
{
|
||||||
|
var r_message = (WriteRequestOpcInputStruct)message;
|
||||||
|
return new OpcParamIn
|
||||||
|
{
|
||||||
|
IsRead = false,
|
||||||
|
Tag = r_message.Tag,
|
||||||
|
Split = r_message.Split,
|
||||||
|
SetValue = r_message.SetValue
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 把仪器返回的内容填充到输出结构中
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="messageBytes">返回数据的字节流</param>
|
||||||
|
/// <param name="pos">转换标记位</param>
|
||||||
|
/// <returns>结构化的输出数据</returns>
|
||||||
|
public override IOutputStruct Unformat(OpcParamOut messageBytes, ref int pos)
|
||||||
|
{
|
||||||
|
var ansByte = BigEndianValueHelper.Instance.GetByte(messageBytes.Value, ref pos);
|
||||||
|
var ans = ansByte != 0;
|
||||||
|
return new WriteRequestOpcOutputStruct(ans);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
55
Modbus.Net/Modbus.Net.Opc/OpcProtocolLinker.cs
Normal file
55
Modbus.Net/Modbus.Net.Opc/OpcProtocolLinker.cs
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Modbus.Net.Opc
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 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>
|
||||||
|
/// <param name="content">发送协议的内容</param>
|
||||||
|
/// <returns>接收协议的内容</returns>
|
||||||
|
public override async Task<OpcParamOut> SendReceiveWithoutExtAndDecAsync(OpcParamIn content)
|
||||||
|
{
|
||||||
|
//发送数据
|
||||||
|
var receiveBytes = await BaseConnector.SendMsgAsync(content);
|
||||||
|
//容错处理
|
||||||
|
var checkRight = CheckRight(receiveBytes);
|
||||||
|
return checkRight == null
|
||||||
|
? new OpcParamOut { Success = false, Value = new byte[0] }
|
||||||
|
: (!checkRight.Value ? null : receiveBytes);
|
||||||
|
//返回字符
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检查接收的数据是否正确
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="content">接收协议的内容</param>
|
||||||
|
/// <returns>协议是否是正确的</returns>
|
||||||
|
public override bool? CheckRight(OpcParamOut content)
|
||||||
|
{
|
||||||
|
if (content == null || !content.Success) return false;
|
||||||
|
if (content.Value.Length == 6 && Encoding.ASCII.GetString(content.Value) == "NoData")
|
||||||
|
return null;
|
||||||
|
return base.CheckRight(content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
42
Modbus.Net/Modbus.Net.Opc/OpcUaConnector.cs
Normal file
42
Modbus.Net/Modbus.Net.Opc/OpcUaConnector.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Modbus.Net.Opc
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Opc UA连接实现
|
||||||
|
/// </summary>
|
||||||
|
public class OpcUaConnector : OpcConnector
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// UA单例管理
|
||||||
|
/// </summary>
|
||||||
|
protected static Dictionary<string, OpcUaConnector> _instances = new Dictionary<string, OpcUaConnector>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="host">Opc UA 服务地址</param>
|
||||||
|
/// <param name="isRegexOn">是否开启正则匹配</param>
|
||||||
|
protected OpcUaConnector(string host, bool isRegexOn) : base(host, isRegexOn)
|
||||||
|
{
|
||||||
|
Client = new MyUaClient(new Uri(ConnectionToken));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据地址获取UA连接器单例
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="host">Opc UA服务地址</param>
|
||||||
|
/// <param name="isRegexOn">是否开启正则匹配</param>
|
||||||
|
/// <returns>Opc UA实例</returns>
|
||||||
|
public static OpcUaConnector Instance(string host, bool isRegexOn)
|
||||||
|
{
|
||||||
|
if (!_instances.ContainsKey(host))
|
||||||
|
{
|
||||||
|
var connector = new OpcUaConnector(host, isRegexOn);
|
||||||
|
_instances.Add(host, connector);
|
||||||
|
}
|
||||||
|
return _instances[host];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
36
Modbus.Net/Modbus.Net.Opc/OpcUaProtocol.cs
Normal file
36
Modbus.Net/Modbus.Net.Opc/OpcUaProtocol.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Modbus.Net.Opc
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Opc UA协议
|
||||||
|
/// </summary>
|
||||||
|
public class OpcUaProtocol : OpcProtocol
|
||||||
|
{
|
||||||
|
private readonly string _host;
|
||||||
|
|
||||||
|
private readonly bool _isRegexOn;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="host">Opc UA服务地址</param>
|
||||||
|
/// <param name="isRegexOn">是否开启正则匹配</param>
|
||||||
|
public OpcUaProtocol(string host, bool isRegexOn)
|
||||||
|
{
|
||||||
|
_host = host;
|
||||||
|
_isRegexOn = isRegexOn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 连接设备
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>是否连接成功</returns>
|
||||||
|
public override async Task<bool> ConnectAsync()
|
||||||
|
{
|
||||||
|
ProtocolLinker = new OpcUaProtocolLinker(_host, _isRegexOn);
|
||||||
|
if (!await ProtocolLinker.ConnectAsync()) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
26
Modbus.Net/Modbus.Net.Opc/OpcUaProtocolLinker.cs
Normal file
26
Modbus.Net/Modbus.Net.Opc/OpcUaProtocolLinker.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
namespace Modbus.Net.Opc
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Opc UA协议连接器
|
||||||
|
/// </summary>
|
||||||
|
public class OpcUaProtocolLinker : OpcProtocolLinker
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="isRegexOn">是否开启正则匹配</param>
|
||||||
|
public OpcUaProtocolLinker(bool isRegexOn) : this(ConfigurationReader.GetValueDirect("OpcUa", "Host"), isRegexOn)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="host">Opc UA服务地址</param>
|
||||||
|
/// <param name="isRegexOn">是否开启正则匹配</param>
|
||||||
|
public OpcUaProtocolLinker(string host, bool isRegexOn)
|
||||||
|
{
|
||||||
|
BaseConnector = OpcUaConnector.Instance(host, isRegexOn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
190
Modbus.Net/Modbus.Net.Opc/OpcUtility.cs
Normal file
190
Modbus.Net/Modbus.Net.Opc/OpcUtility.cs
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Modbus.Net.Opc
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Opc类型
|
||||||
|
/// </summary>
|
||||||
|
public enum OpcType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// DA连接
|
||||||
|
/// </summary>
|
||||||
|
Da = 0,
|
||||||
|
/// <summary>
|
||||||
|
/// UA连接
|
||||||
|
/// </summary>
|
||||||
|
Ua = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Opc通用Api入口
|
||||||
|
/// </summary>
|
||||||
|
public class OpcUtility : BaseUtility<OpcParamIn, OpcParamOut, ProtocolUnit<OpcParamIn, OpcParamOut>,
|
||||||
|
PipeUnit<OpcParamIn, OpcParamOut, IProtocolLinker<OpcParamIn, OpcParamOut>,
|
||||||
|
ProtocolUnit<OpcParamIn, OpcParamOut>>>
|
||||||
|
{
|
||||||
|
private static readonly ILogger<OpcUtility> logger = LogProvider.CreateLogger<OpcUtility>();
|
||||||
|
|
||||||
|
private OpcType _opcType;
|
||||||
|
|
||||||
|
private bool IsRegexOn { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 协议类型
|
||||||
|
/// </summary>
|
||||||
|
public OpcType OpcType
|
||||||
|
{
|
||||||
|
get { return _opcType; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_opcType = value;
|
||||||
|
switch (_opcType)
|
||||||
|
{
|
||||||
|
//Da协议
|
||||||
|
case OpcType.Da:
|
||||||
|
{
|
||||||
|
Wrapper = new OpcDaProtocol(ConnectionString, IsRegexOn);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
//Ua协议
|
||||||
|
case OpcType.Ua:
|
||||||
|
{
|
||||||
|
Wrapper = new OpcUaProtocol(ConnectionString, IsRegexOn);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取分隔符
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>分隔符</returns>
|
||||||
|
public delegate char GetSeperatorDelegate();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="connectionType">连接类型</param>
|
||||||
|
/// <param name="connectionString">连接地址</param>
|
||||||
|
/// <param name="isRegexOn">是否开启正则匹配</param>
|
||||||
|
public OpcUtility(int connectionType, string connectionString, bool isRegexOn = false) : base(0, 0)
|
||||||
|
{
|
||||||
|
ConnectionString = connectionString;
|
||||||
|
IsRegexOn = isRegexOn;
|
||||||
|
OpcType = (OpcType)connectionType;
|
||||||
|
AddressTranslator = new AddressTranslatorOpc();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="connectionType">连接类型</param>
|
||||||
|
/// <param name="connectionString">连接地址</param>
|
||||||
|
/// <param name="isRegexOn">是否开启正则匹配</param>
|
||||||
|
public OpcUtility(OpcType connectionType, string connectionString, bool isRegexOn = false) : base(0, 0)
|
||||||
|
{
|
||||||
|
ConnectionString = connectionString;
|
||||||
|
IsRegexOn = isRegexOn;
|
||||||
|
OpcType = connectionType;
|
||||||
|
AddressTranslator = new AddressTranslatorOpc();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 端格式(大端)
|
||||||
|
/// </summary>
|
||||||
|
public override Endian Endian => Endian.BigEndianLsb;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取分隔符
|
||||||
|
/// </summary>
|
||||||
|
public event GetSeperatorDelegate GetSeperator;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置连接方式(Opc忽略该函数)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="connectionType">连接方式</param>
|
||||||
|
public override void SetConnectionType(int connectionType)
|
||||||
|
{
|
||||||
|
//ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取数据
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="startAddress">开始地址</param>
|
||||||
|
/// <param name="getByteCount">获取字节数个数</param>
|
||||||
|
/// <returns>接收到的byte数据</returns>
|
||||||
|
public override async Task<ReturnStruct<byte[]>> GetDatasAsync(string startAddress, int getByteCount)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var split = GetSeperator?.Invoke() ?? '/';
|
||||||
|
var readRequestOpcInputStruct = new ReadRequestOpcInputStruct(startAddress.Split('\r'), split);
|
||||||
|
var readRequestOpcOutputStruct =
|
||||||
|
await
|
||||||
|
Wrapper.SendReceiveAsync<ReadRequestOpcOutputStruct>(Wrapper[typeof(ReadRequestOpcProtocol)],
|
||||||
|
readRequestOpcInputStruct);
|
||||||
|
return new ReturnStruct<byte[]>
|
||||||
|
{
|
||||||
|
Datas = readRequestOpcOutputStruct?.GetValue,
|
||||||
|
IsSuccess = true,
|
||||||
|
ErrorCode = 0,
|
||||||
|
ErrorMsg = ""
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logger.LogError(e, $"OpcUtility -> GetDatas: {ConnectionString} error: {e.Message}");
|
||||||
|
return new ReturnStruct<byte[]>
|
||||||
|
{
|
||||||
|
Datas = null,
|
||||||
|
IsSuccess = true,
|
||||||
|
ErrorCode = -100,
|
||||||
|
ErrorMsg = e.Message
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置数据
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="startAddress">开始地址</param>
|
||||||
|
/// <param name="setContents">设置数据</param>
|
||||||
|
/// <returns>是否设置成功</returns>
|
||||||
|
public override async Task<ReturnStruct<bool>> SetDatasAsync(string startAddress, object[] setContents)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var split = GetSeperator?.Invoke() ?? '/';
|
||||||
|
var writeRequestOpcInputStruct =
|
||||||
|
new WriteRequestOpcInputStruct(startAddress.Split('\r'), split, setContents[0]);
|
||||||
|
var writeRequestOpcOutputStruct =
|
||||||
|
await
|
||||||
|
Wrapper.SendReceiveAsync<WriteRequestOpcOutputStruct>(Wrapper[typeof(WriteRequestOpcProtocol)],
|
||||||
|
writeRequestOpcInputStruct);
|
||||||
|
return new ReturnStruct<bool>
|
||||||
|
{
|
||||||
|
Datas = writeRequestOpcOutputStruct?.WriteResult == true,
|
||||||
|
IsSuccess = writeRequestOpcOutputStruct?.WriteResult == true,
|
||||||
|
ErrorCode = writeRequestOpcOutputStruct?.WriteResult == true ? 0 : 1,
|
||||||
|
ErrorMsg = writeRequestOpcOutputStruct?.WriteResult == true ? "" : "Write Failed"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logger.LogError(e, $"OpcUtility -> SetDatas: {ConnectionString} error: {e.Message}");
|
||||||
|
return new ReturnStruct<bool>
|
||||||
|
{
|
||||||
|
Datas = false,
|
||||||
|
IsSuccess = false,
|
||||||
|
ErrorCode = -100,
|
||||||
|
ErrorMsg = e.Message
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
Modbus.Net/Modbus.Net.Opc/README.md
Normal file
7
Modbus.Net/Modbus.Net.Opc/README.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
Modbus.Net.OPC
|
||||||
|
===================
|
||||||
|
[](https://www.nuget.org/packages/Modbus.Net.OPC/)
|
||||||
|
|
||||||
|
OPC Implementation of Modbus.Net
|
||||||
|
|
||||||
|
Doc has been moved to wiki.
|
||||||
Reference in New Issue
Block a user