Compare commits

...

20 Commits

Author SHA1 Message Date
Kimdiego2098
71ebb36fe9 更新文档 2023-10-19 20:36:28 +08:00
Kimdiego2098
78a0b86327 更新版本:3.0.0.20 2023-10-19 20:27:31 +08:00
Kimdiego2098
2636c16a97 优化modbusServer内存管理 2023-10-19 20:24:39 +08:00
Kimdiego2098
fd77c0242d update touchsocket 2023-10-19 20:23:43 +08:00
Kimdiego2098
e74819a900 update toucksocket 2023-10-18 21:51:49 +08:00
Kimdiego2098
9b7f696c9b 更新文档 2023-10-18 20:42:10 +08:00
Kimdiego2098
0230d614e7 发布驱动包 2023-10-18 18:04:37 +08:00
Diego2098
252d99ad78 !12 【轻量级 PR】:修正心跳事件中的参数
Merge pull request !12 from youthalan/N/A
2023-10-18 06:59:17 +00:00
youthalan
1ffc200350 修正心跳事件中的参数
Signed-off-by: youthalan <youthalan@126.com>
2023-10-18 06:58:03 +00:00
Kimdiego2098
807d89b2b2 更新版本号 2023-10-18 13:13:03 +08:00
Kimdiego2098
4013afa1f1 初始化 采集/上传线程时 直接返回线程控制 2023-10-18 13:11:59 +08:00
Kimdiego2098
a580927ceb 更新OPCUAClient类库 2023-10-18 13:09:25 +08:00
Kimdiego2098
bf2cf52034 更新OPCUAClient类库 2023-10-18 12:45:26 +08:00
Kimdiego2098
81bb8b7c31 更新OPCUAClient类库 2023-10-18 12:44:58 +08:00
Kimdiego2098
a825007fb5 更新版本号与nuget发布 2023-10-18 12:39:42 +08:00
Kimdiego2098
988124d96a 更新OPCUAClient类库 2023-10-18 12:38:20 +08:00
Diego2098
f0de815296 !11 补充OPCClient类库事件的缺失文件
Merge pull request !11 from youthalan/N/A
2023-10-18 04:34:34 +00:00
youthalan
0e2d58c887 补充OPCClient类库事件的缺失文件
Signed-off-by: youthalan <youthalan@126.com>
2023-10-18 04:32:39 +00:00
Diego2098
b155382626 !10 添加连接或断开事件
Merge pull request !10 from youthalan/N/A
2023-10-18 04:20:22 +00:00
youthalan
f362d740af 修改OPCUAClient类添加连接或断开事件,修改注入时不需要带参数
Signed-off-by: youthalan <youthalan@126.com>
2023-10-18 03:48:05 +00:00
80 changed files with 588 additions and 324 deletions

View File

@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>3.0.0.16</Version>
<Version>3.0.0.20</Version>
<LangVersion>latest</LangVersion>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<Authors>Diego</Authors>

View File

@@ -116,16 +116,16 @@
<ItemGroup >
<PackageReference Include="ThingsGateway.Foundation.Adapter.DLT645" Version="*" />
<!--<PackageReference Include="ThingsGateway.Foundation.Adapter.DLT645" Version="*" />
<PackageReference Include="ThingsGateway.Foundation.Adapter.Modbus" Version="*" />
<PackageReference Include="ThingsGateway.Foundation.Adapter.OPCDA" Version="*" />
<PackageReference Include="ThingsGateway.Foundation.Adapter.OPCUA" Version="*" />
<PackageReference Include="ThingsGateway.Foundation.Adapter.Siemens" Version="*" />
<!--<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.DLT645\ThingsGateway.Foundation.Adapter.DLT645.csproj" />
<PackageReference Include="ThingsGateway.Foundation.Adapter.Siemens" Version="*" />-->
<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.DLT645\ThingsGateway.Foundation.Adapter.DLT645.csproj" />
<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.Modbus\ThingsGateway.Foundation.Adapter.Modbus.csproj" />
<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.OPCDA\ThingsGateway.Foundation.Adapter.OPCDA.csproj" />
<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.OPCUA\ThingsGateway.Foundation.Adapter.OPCUA.csproj" />
<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.Siemens\ThingsGateway.Foundation.Adapter.Siemens.csproj" />-->
<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.Siemens\ThingsGateway.Foundation.Adapter.Siemens.csproj" />
</ItemGroup>
<ItemGroup >

View File

@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>3.0.0.16</Version>
<Version>3.0.0.20</Version>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<LangVersion>latest</LangVersion>
<TargetFrameworks>net45;netstandard2.0;net6.0;net7.0</TargetFrameworks>

View File

@@ -409,13 +409,9 @@ public class ModbusSerialServer : ReadWriteDevicesSerialSessionBase
private void Init(ModbusAddress mAddress)
{
if (ModbusServer01ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)))
ModbusServer01ByteBlocks[mAddress.Station].SetLength(1024 * 128);
if (ModbusServer02ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)))
ModbusServer02ByteBlocks[mAddress.Station].SetLength(1024 * 128);
if (ModbusServer03ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)))
ModbusServer03ByteBlocks[mAddress.Station].SetLength(1024 * 128);
if (ModbusServer04ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)))
ModbusServer04ByteBlocks[mAddress.Station].SetLength(1024 * 128);
ModbusServer01ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
ModbusServer02ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
ModbusServer03ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
ModbusServer04ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
}
}

View File

@@ -429,13 +429,9 @@ public class ModbusTcpServer : ReadWriteDevicesTcpServerBase
private void Init(ModbusAddress mAddress)
{
if (ModbusServer01ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)))
ModbusServer01ByteBlocks[mAddress.Station].SetLength(1024 * 128);
if (ModbusServer02ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)))
ModbusServer02ByteBlocks[mAddress.Station].SetLength(1024 * 128);
if (ModbusServer03ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)))
ModbusServer03ByteBlocks[mAddress.Station].SetLength(1024 * 128);
if (ModbusServer04ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)))
ModbusServer04ByteBlocks[mAddress.Station].SetLength(1024 * 128);
ModbusServer01ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
ModbusServer02ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
ModbusServer03ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
ModbusServer04ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
}
}

View File

@@ -1,4 +1,5 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
@@ -8,6 +9,7 @@
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Newtonsoft.Json.Linq;
@@ -17,22 +19,23 @@ using Opc.Ua.Client;
using Opc.Ua.Client.ComplexTypes;
using Opc.Ua.Configuration;
//修改自https://github.com/dathlin/OpcUaHelper 与OPC基金会net库
namespace ThingsGateway.Foundation.Adapter.OPCUA;
/// <summary>
/// 订阅委托
/// </summary>
/// <param name="value"></param>
public delegate void DataChangedEventHandler((VariableNode variableNode, DataValue dataValue, JToken jToken) value);
/// <summary>
/// OPCUAClient
/// </summary>
public class OPCUAClient : IDisposable
{
#region
/// <summary>
/// 当前配置
/// </summary>
@@ -48,30 +51,35 @@ public class OPCUAClient : IDisposable
/// </summary>
public List<string> Variables = new();
private readonly Action<byte, object, string, Exception> _logAction;
/// <summary>
/// 当前的变量名称/OPC变量节点
/// </summary>
private readonly Dictionary<string, VariableNode> _variableDicts = new();
private readonly object checkLock = new();
/// <summary>
/// 当前的订阅组,组名称/组
/// </summary>
private readonly Dictionary<string, Subscription> dic_subscriptions = new();
private readonly ApplicationInstance m_application = new();
private readonly ApplicationConfiguration m_configuration;
private SessionReconnectHandler m_reConnectHandler;
private EventHandler m_ReconnectComplete;
private EventHandler m_ReconnectStarting;
private EventHandler<KeepAliveEventArgs> m_KeepAliveComplete;
private EventHandler<bool> m_ConnectComplete;
private EventHandler<OpcUaStatusEventArgs> m_OpcStatusChange;
private ISession m_session;
/// <summary>
/// 默认的构造函数实例化一个新的OPC UA类
/// </summary>
public OPCUAClient(Action<byte, object, string, Exception> log)
public OPCUAClient()
{
_logAction = log;
var certificateValidator = new CertificateValidator();
certificateValidator.CertificateValidation += CertificateValidation;
@@ -90,7 +98,6 @@ public class OPCUAClient : IDisposable
MaxMessageQueueSize = 1000000,
MaxNotificationQueueSize = 1000000,
MaxPublishRequestCount = 10000000,
},
SecurityConfiguration = new SecurityConfiguration
@@ -133,8 +140,6 @@ public class OPCUAClient : IDisposable
StoreType = CertificateStoreType.Directory,
StorePath = AppContext.BaseDirectory + @"OPCUAClientCertificate\pki\trustedUser",
}
},
TransportQuotas = new TransportQuotas
@@ -160,8 +165,6 @@ public class OPCUAClient : IDisposable
m_configuration.Validate(ApplicationType.Client);
m_application.ApplicationConfiguration = m_configuration;
}
/// <summary>
@@ -188,6 +191,52 @@ public class OPCUAClient : IDisposable
/// SessionReconnectHandler
/// </summary>
public SessionReconnectHandler ReConnectHandler => m_reConnectHandler;
/// <summary>
/// Raised when a good keep alive from the server arrives.
/// </summary>
public event EventHandler<KeepAliveEventArgs> KeepAliveComplete
{
add { m_KeepAliveComplete += value; }
remove { m_KeepAliveComplete -= value; }
}
/// <summary>
/// Raised when a reconnect operation starts.
/// </summary>
public event EventHandler ReconnectStarting
{
add { m_ReconnectStarting += value; }
remove { m_ReconnectStarting -= value; }
}
/// <summary>
/// Raised when a reconnect operation completes.
/// </summary>
public event EventHandler ReconnectComplete
{
add { m_ReconnectComplete += value; }
remove { m_ReconnectComplete -= value; }
}
/// <summary>
/// Raised after successfully connecting to or disconnecing from a server.
/// </summary>
public event EventHandler<bool> ConnectComplete
{
add { m_ConnectComplete += value; }
remove { m_ConnectComplete -= value; }
}
/// <summary>
/// Raised after the client status change
/// </summary>
public event EventHandler<OpcUaStatusEventArgs> OpcStatusChange
{
add { m_OpcStatusChange += value; }
remove { m_OpcStatusChange -= value; }
}
/// <summary>
/// 当前活动会话。
/// </summary>
@@ -231,7 +280,7 @@ public class OPCUAClient : IDisposable
}
catch (Exception ex)
{
_logAction?.Invoke(3, this, $"初始化{items[i]}变量订阅失败", ex);
UpdateStatus(3, DateTime.Now, $"初始化{items[i]}变量订阅失败,错误原因:{ex}");
}
}
m_subscription.AddItems(monitoredItems);
@@ -247,9 +296,9 @@ public class OPCUAClient : IDisposable
var isError = m_subscription.MonitoredItems.Any(a => a.Status.Error != null && StatusCode.IsBad(a.Status.Error.StatusCode));
if (isError)
{
_logAction?.Invoke(3, this, $"创建以下变量订阅失败:{Environment.NewLine}{m_subscription.MonitoredItems.Where(
UpdateStatus(3, DateTime.Now, $"创建以下变量订阅失败:{Environment.NewLine}{m_subscription.MonitoredItems.Where(
a => a.Status.Error != null && StatusCode.IsBad(a.Status.Error.StatusCode))
.Select(a => $"{a.StartNodeId.ToString()}{a.Status.Error.ToString()}").ToJsonString()}", null);
.Select(a => $"{a.StartNodeId.ToString()}{a.Status.Error.ToString()}").ToJsonString()}");
}
lock (dic_subscriptions)
@@ -281,7 +330,6 @@ public class OPCUAClient : IDisposable
item.Value.Delete(true);
m_session.RemoveSubscription(item.Value);
try { item.Value.Dispose(); } catch { }
}
dic_subscriptions.Clear();
}
@@ -304,39 +352,40 @@ public class OPCUAClient : IDisposable
dic_subscriptions.RemoveWhere(a => a.Key == subscriptionName);
}
}
}
private void Callback(MonitoredItem monitoreditem, MonitoredItemNotificationEventArgs monitoredItemNotificationEventArgs)
{
try
{
var variableNode = ReadNode(monitoreditem.StartNodeId.ToString(), false);
foreach (var value in monitoreditem.DequeueValues())
if (m_session != null)
{
if (value.Value != null)
var variableNode = ReadNode(monitoreditem.StartNodeId.ToString(), false);
foreach (var value in monitoreditem.DequeueValues())
{
var data = JsonUtils.Encode(m_session.MessageContext, TypeInfo.GetBuiltInType(variableNode.DataType, m_session.SystemContext.TypeTable), value.Value);
if (data == null && value.Value != null)
if (value.Value != null)
{
_logAction?.Invoke(3, this, $"{monitoreditem.StartNodeId}转换出错原始值String为{value.Value}", null);
var data1 = JsonUtils.Encode(m_session.MessageContext, TypeInfo.GetBuiltInType(variableNode.DataType, m_session.SystemContext.TypeTable), value.Value);
var data = JsonUtils.Encode(m_session.MessageContext, TypeInfo.GetBuiltInType(variableNode.DataType, m_session.SystemContext.TypeTable), value.Value);
if (data == null && value.Value != null)
{
UpdateStatus(3, DateTime.Now, $"{monitoreditem.StartNodeId}转换出错原始值String为{value.Value}");
var data1 = JsonUtils.Encode(m_session.MessageContext, TypeInfo.GetBuiltInType(variableNode.DataType, m_session.SystemContext.TypeTable), value.Value);
}
DataChangedHandler?.Invoke((variableNode, value, data));
}
else
{
var data = JValue.CreateNull();
DataChangedHandler?.Invoke((variableNode, value, data));
}
DataChangedHandler?.Invoke((variableNode, value, data));
}
else
{
var data = JValue.CreateNull();
DataChangedHandler?.Invoke((variableNode, value, data));
}
}
}
catch (Exception ex)
{
_logAction?.Invoke(3, this, $"{monitoreditem.StartNodeId}订阅处理错误", ex);
UpdateStatus(3, DateTime.Now, $"{monitoreditem.StartNodeId}订阅处理错误,错误原因:" + ex);
}
}
#endregion
@@ -462,8 +511,8 @@ public class OPCUAClient : IDisposable
#endregion
#region
private ComplexTypeSystem typeSystem;
/// <summary>
@@ -472,7 +521,6 @@ public class OPCUAClient : IDisposable
public async Task ConnectAsync()
{
await ConnectAsync(OPCNode.OPCUrl);
_logAction?.Invoke(1, this, $"连接成功", null);
}
/// <summary>
@@ -484,10 +532,10 @@ public class OPCUAClient : IDisposable
// disconnect any existing session.
if (m_session != null)
{
_logAction?.Invoke(1, this, $"主动断开连接", null);
m_session = null;
}
}
/// <summary>
/// Creates a new session.
/// </summary>
@@ -516,20 +564,24 @@ public class OPCUAClient : IDisposable
//创建本地证书
await m_application.CheckApplicationInstanceCertificate(true, 0, 1200);
m_session = await Opc.Ua.Client.Session.Create(
m_configuration,
endpoint,
false,
OPCNode.CheckDomain,
(string.IsNullOrEmpty(OPCUAName)) ? m_configuration.ApplicationName : OPCUAName,
60000,
userIdentity,
Array.Empty<string>());
m_configuration,
endpoint,
false,
OPCNode.CheckDomain,
(string.IsNullOrEmpty(OPCUAName)) ? m_configuration.ApplicationName : OPCUAName,
60000,
userIdentity,
Array.Empty<string>());
typeSystem = new ComplexTypeSystem(m_session);
m_session.KeepAliveInterval = OPCNode.KeepAliveInterval == 0 ? 60000 : OPCNode.KeepAliveInterval;
m_session.KeepAlive += new KeepAliveEventHandler(Session_KeepAlive);
// raise an event.
DoConnectComplete(true);
UpdateStatus(2, DateTime.UtcNow, "Connected");
//如果是订阅模式,连接时添加订阅组
if (OPCNode.ActiveSubscribe)
await AddSubscriptionAsync(Guid.NewGuid().ToString(), Variables.ToArray(), OPCNode.LoadType);
@@ -538,6 +590,9 @@ public class OPCUAClient : IDisposable
private void PrivateDisconnect()
{
bool state = m_session?.Connected == true;
if (m_reConnectHandler != null)
{
try { m_reConnectHandler.Dispose(); } catch { }
@@ -549,8 +604,14 @@ public class OPCUAClient : IDisposable
m_session.Close(10000);
}
if (state)
{
UpdateStatus(2, DateTime.UtcNow, "Disconnected");
DoConnectComplete(false);
}
}
#endregion
#region /
@@ -593,7 +654,6 @@ public class OPCUAClient : IDisposable
valuesToWrite.Add(valueToWrite);
}
var result = await m_session.WriteAsync(
requestHeader: null,
nodesToWrite: valuesToWrite, cancellationToken);
@@ -601,7 +661,6 @@ public class OPCUAClient : IDisposable
ClientBase.ValidateResponse(result.Results, valuesToWrite);
ClientBase.ValidateDiagnosticInfos(result.DiagnosticInfos, valuesToWrite);
var keys = writeInfoLists.Keys.ToList();
for (int i = 0; i < keys.Count; i++)
{
@@ -624,7 +683,6 @@ public class OPCUAClient : IDisposable
}
return results;
}
}
/// <summary>
@@ -706,7 +764,6 @@ public class OPCUAClient : IDisposable
return node;
}
/// <summary>
/// 从服务器读取节点
/// </summary>
@@ -729,18 +786,14 @@ public class OPCUAClient : IDisposable
}
else
{
_logAction?.Invoke(3, this, $"获取服务器节点信息失败{nodes.Item2[i]}", null);
UpdateStatus(3, DateTime.Now, $"获取服务器节点信息失败{nodes.Item2[i]}");
}
}
return nodes.Item1.ToList();
}
#endregion
#region
/// <summary>
@@ -805,7 +858,6 @@ public class OPCUAClient : IDisposable
ResultMask = (uint)BrowseResultMask.All
};
nodesToBrowse.Add(nodeToBrowse);
}
return await ReadNoteAttributeAsync(nodesToBrowse, nodesToRead, cancellationToken);
@@ -968,11 +1020,9 @@ public class OPCUAClient : IDisposable
return nodeAttribute.ToArray();
}
#endregion
/// <inheritdoc/>
public void Dispose()
{
@@ -991,7 +1041,6 @@ public class OPCUAClient : IDisposable
throw new Exception(string.Format("验证证书失败,错误代码:{0}: {1}", eventArgs.Error.Code, eventArgs.Error.AdditionalInfo));
}
private async Task<Dictionary<string, List<OPCNodeAttribute>>> ReadNoteAttributeAsync(BrowseDescriptionCollection nodesToBrowse, ReadValueIdCollection nodesToRead, CancellationToken cancellationToken)
{
int startOfProperties = nodesToRead.Count;
@@ -1068,7 +1117,6 @@ public class OPCUAClient : IDisposable
}
}
if (nodeAttributes.ContainsKey(nodeToRead.NodeId.ToString()))
{
nodeAttributes[nodeToRead.NodeId.ToString()].Add(item);
@@ -1086,21 +1134,47 @@ public class OPCUAClient : IDisposable
/// </summary>
private void Server_ReconnectComplete(object sender, EventArgs e)
{
if (!Object.ReferenceEquals(sender, m_reConnectHandler))
try
{
return;
if (!Object.ReferenceEquals(sender, m_reConnectHandler))
{
return;
}
m_session = m_reConnectHandler.Session;
m_reConnectHandler.Dispose();
m_reConnectHandler = null;
// raise any additional notifications.
m_ReconnectComplete?.Invoke(this, e);
}
catch (Exception)
{
throw;
}
}
m_session = m_reConnectHandler.Session;
m_reConnectHandler = null;
/// <summary>
/// Report the client status
/// </summary>
/// <param name="logLevel">Whether the status represents an error. </param>
/// <param name="time">The time associated with the status.</param>
/// <param name="status">The status message.</param>
/// <param name="args">Arguments used to format the status message.</param>
private void UpdateStatus(int logLevel, DateTime time, string status, params object[] args)
{
m_OpcStatusChange?.Invoke(this, new OpcUaStatusEventArgs()
{
LogLevel = logLevel,
Time = time.ToLocalTime(),
Text = String.Format(status, args),
});
}
private void Session_KeepAlive(ISession session, KeepAliveEventArgs e)
{
lock (checkLock)
{
if (!Object.ReferenceEquals(session, m_session))
{
return;
@@ -1108,22 +1182,39 @@ public class OPCUAClient : IDisposable
if (ServiceResult.IsBad(e.Status))
{
_logAction?.Invoke(3, this, $"心跳检测错误:{e.Status}", null);
if (m_session.KeepAliveInterval <= 0)
{
UpdateStatus(3, e.CurrentTime, "Communication Error ({0})", e.Status);
return;
}
UpdateStatus(3, e.CurrentTime, "Reconnecting in {0}s", m_session.KeepAliveInterval / 1000);
if (m_reConnectHandler == null)
{
m_ReconnectStarting?.Invoke(this, e);
m_reConnectHandler = new SessionReconnectHandler();
m_reConnectHandler.BeginReconnect(m_session, m_session.KeepAliveInterval, Server_ReconnectComplete);
}
return;
}
// update status.
UpdateStatus(0, e.CurrentTime, "Session_KeepAlive Connected [{0}]", session.Endpoint.EndpointUrl);
// raise any additional notifications.
m_KeepAliveComplete?.Invoke(this, e);
}
}
/// <summary>
/// Raises the connect complete event on the main GUI thread.
/// </summary>
private void DoConnectComplete(bool state)
{
m_ConnectComplete?.Invoke(this, state);
}
#endregion
}

View File

@@ -1,4 +1,5 @@
#region copyright
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
@@ -8,11 +9,39 @@
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Opc.Ua;
namespace ThingsGateway.Foundation.Adapter.OPCUA;
/// <summary>
/// OPC UA的状态更新消息
/// </summary>
public class OpcUaStatusEventArgs
{
/// <summary>
/// 日志等级,<br></br>
/// 更为详细的步骤型日志输出 Trace = 0,<br></br>
/// 调试信息日志Debug = 1,<br></br>
/// 消息类日志输出 Info = 2,<br></br>
/// 警告类日志输出 Warning = 3,<br></br>
/// 错误类日志输出 Error = 4,<br></br>
/// 不可控中断类日输出Critical = 5,
/// </summary>
public int LogLevel { get; set; }
/// <summary>
/// 时间
/// </summary>
public DateTime Time { get; set; }
/// <summary>
/// 文本
/// </summary>
public string Text { get; set; }
}
/// <summary>
/// 读取属性过程中用于描述的
/// </summary>
@@ -22,6 +51,7 @@ public class OPCNodeAttribute
/// 属性的名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 操作结果状态描述
/// </summary>
@@ -31,10 +61,9 @@ public class OPCNodeAttribute
/// 属性的类型描述
/// </summary>
public string Type { get; set; }
/// <summary>
/// 属性的值,如果读取错误,返回文本描述
/// </summary>
public object Value { get; set; }
}
}

View File

@@ -27,8 +27,7 @@ using System.Runtime.CompilerServices;
namespace ThingsGateway.Foundation.Core
{
/// <summary>
/// 具有释放的对象。
/// 并未实现析构函数相关。
/// 具有释放的对象。内部实现了GC.SuppressFinalize但不包括析构函数相关。
/// </summary>
public partial class DisposableObject : IDisposable
{
@@ -65,11 +64,12 @@ namespace ThingsGateway.Foundation.Core
}
/// <summary>
/// 释放资源。
/// 释放资源。内部已经处理了<see cref="GC.SuppressFinalize(object)"/>
/// </summary>
public void Dispose()
{
this.Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
}

View File

@@ -70,6 +70,15 @@ namespace ThingsGateway.Foundation.Core
return isPeriod;
}
/// <summary>
/// 累计增加一个计数
/// </summary>
/// <returns></returns>
public bool Increment()
{
return this.Increment(1);
}
/// <summary>
/// 重置<see cref="Count"/>和<see cref="LastIncrement"/>
/// </summary>

View File

@@ -47,14 +47,7 @@ namespace ThingsGateway.Foundation.Core
/// </summary>
public TimeSpan Period { get; set; }
/// <summary>
/// 重置<see cref="Count"/>和<see cref="LastIncrement"/>
/// </summary>
public void Reset()
{
this.m_count = 0;
this.m_lastIncrement = default;
}
/// <summary>
/// 累计增加计数
@@ -78,5 +71,26 @@ namespace ThingsGateway.Foundation.Core
Interlocked.Add(ref this.m_count, value);
return isPeriod;
}
/// <summary>
/// 累计增加一个计数
/// </summary>
/// <returns></returns>
public bool Increment()
{
return this.Increment(1);
}
/// <summary>
/// 重置<see cref="Count"/>和<see cref="LastIncrement"/>
/// </summary>
public void Reset()
{
this.m_count = 0;
this.m_lastIncrement = default;
}
}
}

View File

@@ -760,7 +760,15 @@ public class SerialSessionBase : BaseSerial, ISerialSession
this.m_serialCore.Reset(serialPort);
this.m_serialCore.OnReceived = this.HandleReceived;
this.m_serialCore.OnBreakOut = this.BreakOut;
if (this.Config.GetValue(TouchSocketConfigExtension.MinBufferSizeProperty) is int minValue)
{
this.m_serialCore.MinBufferSize = minValue;
}
if (this.Config.GetValue(TouchSocketConfigExtension.MaxBufferSizeProperty) is int maxValue)
{
this.m_serialCore.MaxBufferSize = maxValue;
}
}
private void HandleReceived(SerialCore core, ByteBlock byteBlock)

View File

@@ -227,6 +227,15 @@ namespace ThingsGateway.Foundation.Sockets
tcpCore.Reset(socket);
tcpCore.OnReceived = this.HandleReceived;
tcpCore.OnBreakOut = this.BreakOut;
if (this.Config.GetValue(TouchSocketConfigExtension.MinBufferSizeProperty) is int minValue)
{
tcpCore.MinBufferSize = minValue;
}
if (this.Config.GetValue(TouchSocketConfigExtension.MaxBufferSizeProperty) is int maxValue)
{
tcpCore.MaxBufferSize = maxValue;
}
this.m_tcpCore = tcpCore;
}

View File

@@ -497,6 +497,11 @@ namespace ThingsGateway.Foundation.Sockets
#endregion
/// <inheritdoc/>
public override string ToString()
{
return this.GetIPPort();
}
private void BreakOut(TcpCore core, bool manual, string msg)
{
lock (this.SyncRoot)
@@ -931,7 +936,15 @@ namespace ThingsGateway.Foundation.Sockets
this.m_tcpCore.Reset(socket);
this.m_tcpCore.OnReceived = this.HandleReceived;
this.m_tcpCore.OnBreakOut = this.BreakOut;
if (this.Config.GetValue(TouchSocketConfigExtension.MinBufferSizeProperty) is int minValue)
{
this.m_tcpCore.MinBufferSize = minValue;
}
if (this.Config.GetValue(TouchSocketConfigExtension.MaxBufferSizeProperty) is int maxValue)
{
this.m_tcpCore.MaxBufferSize = maxValue;
}
}
private void HandleReceived(TcpCore core, ByteBlock byteBlock)

View File

@@ -209,7 +209,7 @@ namespace ThingsGateway.Foundation.Sockets
/// <param name="e"></param>
protected virtual Task OnDisconnecting(TClient socketClient, DisconnectEventArgs e)
{
if (this.Disconnected != null)
if (this.Disconnecting != null)
{
return this.Disconnecting.Invoke(socketClient, e);
}

View File

@@ -155,6 +155,11 @@ namespace ThingsGateway.Foundation.Sockets
/// <param name="tcpCore"></param>
public void ReturnTcpCore(TcpCore tcpCore)
{
if (this.DisposedValue)
{
tcpCore.SafeDispose();
return;
}
this.m_tcpCores.Push(tcpCore);
}

View File

@@ -51,6 +51,54 @@ namespace ThingsGateway.Foundation.Sockets
public static readonly DependencyProperty<Func<UdpDataHandlingAdapter>> UdpDataHandlingAdapterProperty = DependencyProperty<Func<UdpDataHandlingAdapter>>.Register("UdpDataHandlingAdapter", () => { return new NormalUdpDataHandlingAdapter(); });
/// <summary>
/// 最小缓存池尺寸
/// 所需类型<see cref="int"/>
/// </summary>
public static readonly DependencyProperty<int?> MinBufferSizeProperty = DependencyProperty<int?>.Register("MinBufferSize", default);
/// <summary>
/// 最大缓存池尺寸
/// 所需类型<see cref="int"/>
/// </summary>
public static readonly DependencyProperty<int?> MaxBufferSizeProperty = DependencyProperty<int?>.Register("MinBufferSize", default);
/// <summary>
/// 最小缓存容量,默认缺省。
/// <list type="number">
/// </list>
/// </summary>
/// <param name="config"></param>
/// <param name="value"></param>
/// <returns></returns>
public static TouchSocketConfig MinBufferSize(this TouchSocketConfig config, int value)
{
if (value < 1024)
{
throw new ArgumentOutOfRangeException(nameof(value), value, "数值不能小于1024");
}
config.SetValue(MinBufferSizeProperty, value);
return config;
}
/// <summary>
/// 最大缓存容量,默认缺省。
/// <list type="number">
/// </list>
/// </summary>
/// <param name="config"></param>
/// <param name="value"></param>
/// <returns></returns>
public static TouchSocketConfig MaxBufferSize(this TouchSocketConfig config, int value)
{
if (value < 1024)
{
throw new ArgumentOutOfRangeException(nameof(value), value, "数值不能小于1024");
}
config.SetValue(MaxBufferSizeProperty, value);
return config;
}
/// <summary>
/// 发送超时设定单位毫秒默认为0。意为禁用该配置。
/// </summary>

View File

@@ -22,7 +22,13 @@ namespace ThingsGateway.Foundation.Sockets
private readonly AsyncAutoResetEvent m_resetEventForRead = new AsyncAutoResetEvent(false);
private ByteBlock m_byteBlock;
private IRequestInfo m_requestInfo;
/// <summary>
/// Receiver
/// </summary>
~Receiver()
{
this.Dispose(false);
}
/// <summary>
/// Receiver
/// </summary>
@@ -74,9 +80,15 @@ namespace ThingsGateway.Foundation.Sockets
/// <inheritdoc/>
protected override void Dispose(bool disposing)
{
this.m_client.ClearReceiver();
//this.m_resetEventForComplateRead.SafeDispose();
this.m_resetEventForRead.SafeDispose();
if (disposing)
{
this.m_client.ClearReceiver();
}
else
{
this.m_resetEventForRead.SafeDispose();
}
this.m_byteBlock = null;
base.Dispose(disposing);
}

View File

@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>3.0.0.16</Version>
<Version>3.0.0.20</Version>
<LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>

View File

@@ -177,6 +177,8 @@ public class OPCUAClient : CollectBase
if (_plc != null)
{
_plc.DataChangedHandler -= DataChangedHandler;
_plc.OpcStatusChange -= _plc_OpcStatusChange;
_plc.Disconnect();
_plc.SafeDispose();
_plc = null;
@@ -204,13 +206,19 @@ public class OPCUAClient : CollectBase
};
if (_plc == null)
{
_plc = new((arg1, arg2, arg3, arg4) => Log_Out((LogLevel)arg1, arg2, arg3, arg4));
_plc = new();
_plc.OpcStatusChange += _plc_OpcStatusChange;
_plc.DataChangedHandler += DataChangedHandler;
}
_plc.OPCNode = opcNode;
}
private void _plc_OpcStatusChange(object sender, OpcUaStatusEventArgs e)
{
Log_Out((LogLevel)e.LogLevel, null, e.Text, null);
}
/// <inheritdoc/>
protected override Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken)
{

View File

@@ -36,16 +36,24 @@ public partial class OPCUAClientPage
/// </summary>
public void Dispose()
{
OPC.OpcStatusChange -= OPC_OpcStatusChange;
OPC.SafeDispose();
}
/// <inheritdoc/>
protected override void OnInitialized()
{
OPC = new ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient(LogOut);
OPC = new ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient();
OPC.OpcStatusChange += OPC_OpcStatusChange;
base.OnInitialized();
}
private void OPC_OpcStatusChange(object sender, OpcUaStatusEventArgs e)
{
if (e.LogLevel > 0)
LogAction?.Invoke((LogLevel)e.LogLevel, null, e.Text, null);
}
private async Task ConnectAsync()
{
try
@@ -75,6 +83,4 @@ public partial class OPCUAClientPage
return OPC;
}
private void LogOut(byte logLevel, object source, string message, Exception exception) => LogAction?.Invoke((LogLevel)logLevel, source, message, exception);
}

View File

@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>3.0.0.16</Version>
<Version>3.0.0.20</Version>
<LangVersion>latest</LangVersion>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<Authors>Diego</Authors>

View File

@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>3.0.0.16</Version>
<Version>3.0.0.20</Version>
<LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>

View File

@@ -164,6 +164,7 @@ public class CollectDeviceThread : IAsyncDisposable
var stoppingToken = StoppingTokens.Last().Token;
DeviceTask = await Task.Factory.StartNew(async () =>
{
await Task.Yield();
var channelResult = CollectDeviceCores.FirstOrDefault().Driver.GetShareChannel();
LoggerGroup log = CollectDeviceCores.FirstOrDefault().Driver.LogMessage;
foreach (var device in CollectDeviceCores)

View File

@@ -153,6 +153,7 @@ public class UploadDeviceThread : IAsyncDisposable
var stoppingToken = StoppingTokens.Last().Token;
DeviceTask = await Task.Factory.StartNew(async () =>
{
await Task.Yield();
LoggerGroup log = UploadDeviceCores.FirstOrDefault().Driver.LogMessage;
foreach (var device in UploadDeviceCores)
{
@@ -225,5 +226,7 @@ public class UploadDeviceThread : IAsyncDisposable
}
, TaskCreationOptions.LongRunning);
}
}

View File

@@ -6,7 +6,7 @@ export const language = ["en","zh"];
export const removeDefaultStopWordFilter = false;
export const removeDefaultStemmer = false;
export { default as Mark } from "E:\\Tg\\ThingsGateway\\ThingsGateway-DEV\\handbook\\node_modules\\mark.js\\dist\\mark.js"
export const searchIndexUrl = "search-index{dir}.json?_=590a19da";
export const searchIndexUrl = "search-index{dir}.json?_=5f24d3ee";
export const searchResultLimits = 8;
export const searchResultContextMaxLength = 50;
export const explicitSearchResultPath = true;

View File

@@ -227,9 +227,9 @@
"179": {
"js": [
{
"file": "assets/js/main.4e65a7c4.js",
"hash": "7245ddb629dafe73",
"publicPath": "/thingsgateway-docs/assets/js/main.4e65a7c4.js"
"file": "assets/js/main.cf42840d.js",
"hash": "41e8aa8c7cbe401b",
"publicPath": "/thingsgateway-docs/assets/js/main.cf42840d.js"
}
]
},
@@ -299,9 +299,9 @@
"1303": {
"js": [
{
"file": "assets/js/runtime~main.7f6e9c09.js",
"hash": "aedff306f0f845ba",
"publicPath": "/thingsgateway-docs/assets/js/runtime~main.7f6e9c09.js"
"file": "assets/js/runtime~main.2b19bbf8.js",
"hash": "e48e0c0408aa1c4e",
"publicPath": "/thingsgateway-docs/assets/js/runtime~main.2b19bbf8.js"
}
]
},
@@ -326,9 +326,9 @@
"1822": {
"js": [
{
"file": "assets/js/f05a39b7.bfb8e652.js",
"hash": "3d8f498000b417cb",
"publicPath": "/thingsgateway-docs/assets/js/f05a39b7.bfb8e652.js"
"file": "assets/js/f05a39b7.a300eca8.js",
"hash": "67b94ba06477a5ab",
"publicPath": "/thingsgateway-docs/assets/js/f05a39b7.a300eca8.js"
}
]
},
@@ -619,9 +619,9 @@
"8707": {
"js": [
{
"file": "assets/js/4c79e569.3f1e7d8c.js",
"hash": "5ca0c0dec325073e",
"publicPath": "/thingsgateway-docs/assets/js/4c79e569.3f1e7d8c.js"
"file": "assets/js/4c79e569.5c5c0d05.js",
"hash": "e9c712a478ed50e6",
"publicPath": "/thingsgateway-docs/assets/js/4c79e569.5c5c0d05.js"
}
]
},

View File

@@ -12,8 +12,8 @@
"tags": [],
"version": "current",
"lastUpdatedBy": "Kimdiego2098",
"lastUpdatedAt": 1695729838,
"formattedLastUpdatedAt": "Sep 26, 2023",
"lastUpdatedAt": 1697449154,
"formattedLastUpdatedAt": "Oct 16, 2023",
"frontMatter": {
"id": "enterprise",
"title": "Pro版相关"

View File

@@ -11,9 +11,9 @@
"editUrl": "https://gitee.com/diego2098/ThingsGateway/tree/master/handbook/docs/upgrade.mdx",
"tags": [],
"version": "current",
"lastUpdatedBy": "Diego2098",
"lastUpdatedAt": 1691418711,
"formattedLastUpdatedAt": "Aug 7, 2023",
"lastUpdatedBy": "Kimdiego2098",
"lastUpdatedAt": 1697632930,
"formattedLastUpdatedAt": "Oct 18, 2023",
"frontMatter": {
"id": "upgrade",
"title": "历史更新"

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -18,15 +18,31 @@ import Tag from "@site/src/components/Tag.js";
:::
## v2.1.0(未发布)
- &nbsp;<Tag>新增</Tag> 添加TDengine时序库存储
## v3.0.0.20(已发布)
- &nbsp;<Tag>新增</Tag> 添加报警延时逻辑
- &nbsp;<Tag>优化</Tag> 优化ModbusServer GC频繁的问题
- &nbsp;<Tag>新增</Tag> 添加采集数据延时确认逻辑
- &nbsp;<Tag>优化</Tag> 去除DateTimeOffset类型
## v3.0.0.19(已发布)
- &nbsp;<Tag>新增</Tag> 添加TDengineDB时序库上传插件
- &nbsp;<Tag>新增</Tag> 添加QuestDB时序库上传插件
- &nbsp;<Tag>新增</Tag> 添加DLT645采集插件
- &nbsp;<Tag>新增</Tag> 添加调试软件
- &nbsp;<Tag>新增</Tag> 添加远程更新软件
- &nbsp;<Tag>优化</Tag> 优化OPCUA驱动
- &nbsp;<Tag>优化</Tag> 优化Modbus驱动
- &nbsp;<Tag>优化</Tag> 优化S7驱动
- &nbsp;<Tag>优化</Tag> RPC服务
- &nbsp;<Tag>优化</Tag> 其他人性化操作
- &nbsp;<Tag>调整</Tag> 内嵌TouckSocket
- &nbsp;<Tag>修复</Tag> 内存泄露
- &nbsp;<Tag>修复</Tag> 串口断连/拔出/断电等情况,重新连接
- &nbsp;<Tag>修复</Tag> 其他bug
## v2.0.0(已发布)