mirror of
https://gitee.com/ThingsGateway/ThingsGateway.git
synced 2025-10-27 21:57:10 +08:00
支持OPCUAServer数组类型,添加缓存文件存在判断
This commit is contained in:
@@ -7,8 +7,8 @@
|
||||
[](https://www.nuget.org/packages/kimdiego/)
|
||||
[](https://www.apache.org/licenses/LICENSE-2.0.html)
|
||||
[](https://gitee.com/diego2098/ThingsGateway/stargazers)
|
||||
[](https://gitee.com/diego2098/ThingsGateway/members)
|
||||

|
||||
[](https://gitee.com/diego2098/ThingsGateway/members)
|
||||

|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -283,14 +283,14 @@ public class OPCUAClient : DisposableObject
|
||||
|
||||
m_session.AddSubscription(m_subscription);
|
||||
m_subscription.Create();
|
||||
foreach (var item in m_subscription.MonitoredItems.Where(a => a.Status.Error!=null&& StatusCode.IsBad( a.Status.Error.StatusCode)))
|
||||
foreach (var item in m_subscription.MonitoredItems.Where(a => a.Status.Error != null && StatusCode.IsBad(a.Status.Error.StatusCode)))
|
||||
{
|
||||
item.Filter = new DataChangeFilter() { DeadbandValue = OPCNode.DeadBand, DeadbandType = (int)DeadbandType.None, Trigger = DataChangeTrigger.StatusValue };
|
||||
}
|
||||
m_subscription.ApplyChanges();
|
||||
|
||||
var iserror = m_subscription.MonitoredItems.Any(a => a.Status.Error != null && StatusCode.IsBad(a.Status.Error.StatusCode));
|
||||
if(iserror)
|
||||
if (iserror)
|
||||
{
|
||||
UpdateStatus(iserror, DateTime.UtcNow, m_subscription.MonitoredItems.FirstOrDefault(a => a.Status.Error != null && StatusCode.IsBad(a.Status.Error.StatusCode)).Status.Error.ToString());
|
||||
}
|
||||
|
||||
@@ -178,6 +178,7 @@ public class KafkaProducer : UpLoadBase
|
||||
}
|
||||
else
|
||||
{
|
||||
isSuccess = true;
|
||||
//连接成功时补发缓存数据
|
||||
var cacheData = await CacheDb.GetCacheData();
|
||||
foreach (var item in cacheData)
|
||||
@@ -198,20 +199,22 @@ public class KafkaProducer : UpLoadBase
|
||||
else
|
||||
{
|
||||
stoppingToken.Cancel();
|
||||
isSuccess = false;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, ToString());
|
||||
await CacheDb.AddCacheData(topic, payLoad, driverPropertys.CacheMaxCount);
|
||||
CurDevice.LastErrorMessage = ex.Message;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private bool isSuccess = true;
|
||||
public override OperResult IsConnected()
|
||||
{
|
||||
return producer == null ? new("初始化失败") : OperResult.CreateSuccessResult();
|
||||
return isSuccess ? new() : OperResult.CreateSuccessResult();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
@@ -249,6 +252,8 @@ public class KafkaProducer : UpLoadBase
|
||||
//3、错误日志监视
|
||||
producerBuilder.SetErrorHandler((p, msg) =>
|
||||
{
|
||||
isSuccess = false;
|
||||
CurDevice.LastErrorMessage = msg.Reason;
|
||||
_logger.LogWarning($"Producer_Erro信息:Code:{msg.Code};Reason:{msg.Reason};IsError:{msg.IsError}");
|
||||
});
|
||||
//kafka
|
||||
|
||||
@@ -27,16 +27,6 @@ using ThingsGateway.Web.Foundation;
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.OPCUA;
|
||||
/// <inheritdoc/>
|
||||
public class OPCUAServerVariableProperty : VariablePropertyBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 数据类型
|
||||
/// </summary>
|
||||
[VariableProperty("数据类型", "")]
|
||||
public DataTypeEnum DataTypeEnum { get; set; } = DataTypeEnum.Object;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
@@ -138,7 +128,6 @@ public partial class OPCUAServer : UpLoadBase
|
||||
m_server?.SafeDispose();
|
||||
_uploadVariables = null;
|
||||
_collectVariableRunTimes.Clear();
|
||||
_collectVariableRunTimes = null;
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
protected override void Init(UploadDeviceRunTime device)
|
||||
@@ -159,6 +148,7 @@ public partial class OPCUAServer : UpLoadBase
|
||||
|
||||
var serviceScope = _scopeFactory.CreateScope();
|
||||
var _globalDeviceData = serviceScope.ServiceProvider.GetService<GlobalDeviceData>();
|
||||
_collectVariableRunTimes.Clear();
|
||||
|
||||
_uploadVariables = _globalDeviceData.AllVariables;
|
||||
_globalDeviceData.AllVariables.ForEach(a =>
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using ThingsGateway.Web.Foundation;
|
||||
|
||||
namespace ThingsGateway.OPCUA;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public class OPCUAServerVariableProperty : VariablePropertyBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 数据类型
|
||||
/// </summary>
|
||||
[VariableProperty("数据类型", "")]
|
||||
public DataTypeEnum DataTypeEnum { get; set; } = DataTypeEnum.Object;
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
- Json转换代码来自[OPCUAWebPlatformUniCT](https://github.com/OPCUAUniCT/OPCUAWebPlatformUniCT),特此说明,感谢开源项目!
|
||||
@@ -0,0 +1,86 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
using Opc.Ua;
|
||||
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
using TypeInfo = Opc.Ua.TypeInfo;
|
||||
|
||||
namespace ThingsGateway.OPCUA;
|
||||
|
||||
public class ServerDataTypeManager
|
||||
{
|
||||
private readonly IServiceMessageContext _messageContext;
|
||||
private readonly SystemContext _systemContext;
|
||||
public ServerDataTypeManager(IServiceMessageContext messageContext, SystemContext systemContext)
|
||||
{
|
||||
_messageContext = messageContext;
|
||||
_systemContext = systemContext;
|
||||
}
|
||||
|
||||
|
||||
#region Write UA Values
|
||||
|
||||
public object GetDataValueFromVariableState(JToken variableState, NodeId dataType)
|
||||
{
|
||||
int actualValueRank = variableState.CalculateActualValueRank();
|
||||
//if (!ValueRanks.IsValid(actualValueRank, variableNode.ValueRank))
|
||||
// throw new("Rank of the Value provided does not match the Variable ValueRank");
|
||||
|
||||
ServerPlatformJSONDecoder platformJsonDecoder;
|
||||
var type = TypeInfo.GetBuiltInType(dataType, _systemContext.TypeTable);
|
||||
switch (actualValueRank)
|
||||
{
|
||||
case -1:
|
||||
platformJsonDecoder = ServerPlatformJSONDecoder.CreateDecoder(
|
||||
JsonConvert.SerializeObject(new { Value = variableState }),
|
||||
dataType,
|
||||
_messageContext);
|
||||
return type.GetDecodeDelegate(platformJsonDecoder);
|
||||
case 1:
|
||||
platformJsonDecoder = ServerPlatformJSONDecoder.CreateDecoder(
|
||||
JsonConvert.SerializeObject(new { Value = variableState }),
|
||||
dataType,
|
||||
_messageContext);
|
||||
return type.GetDecodeArrayDelegate(platformJsonDecoder);
|
||||
default:
|
||||
var dimensions = variableState.GetJsonArrayDimensions();
|
||||
variableState = variableState.ToOneDimensionJArray();
|
||||
platformJsonDecoder = ServerPlatformJSONDecoder.CreateDecoder(
|
||||
JsonConvert.SerializeObject(new { Value = variableState }),
|
||||
dataType,
|
||||
_messageContext,
|
||||
dimensions);
|
||||
return type.GetDecodeMatrixDelegate(platformJsonDecoder);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
internal class PlatformStatusCode
|
||||
{
|
||||
public readonly string code;
|
||||
public readonly bool structureChanged;
|
||||
|
||||
public PlatformStatusCode(StatusCode statusCode)
|
||||
{
|
||||
code = Regex.Match(statusCode.ToString(), @"(Good|Uncertain|Bad)").Groups[1].ToString();
|
||||
structureChanged = statusCode.StructureChanged;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
using Opc.Ua;
|
||||
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace ThingsGateway.OPCUA;
|
||||
|
||||
public static class BuiltInExtensionMethods
|
||||
{
|
||||
public static object GetDecodeArrayDelegate(this BuiltInType builtIn, ServerPlatformJSONDecoder decoder)
|
||||
{
|
||||
switch (builtIn)
|
||||
{
|
||||
case BuiltInType.Boolean:
|
||||
return decoder.ReadBooleanArray("Value").ToArray();
|
||||
case BuiltInType.SByte:
|
||||
return decoder.ReadSByteArray("Value").ToArray();
|
||||
case BuiltInType.Byte:
|
||||
return decoder.ReadByteArray("Value").ToArray();
|
||||
case BuiltInType.Int16:
|
||||
return decoder.ReadInt16Array("Value").ToArray();
|
||||
case BuiltInType.UInt16:
|
||||
return decoder.ReadUInt16Array("Value").ToArray();
|
||||
case BuiltInType.Int32:
|
||||
return decoder.ReadInt32Array("Value").ToArray();
|
||||
case BuiltInType.UInt32:
|
||||
return decoder.ReadUInt32Array("Value").ToArray();
|
||||
case BuiltInType.Int64:
|
||||
return decoder.ReadInt64Array("Value").ToArray();
|
||||
case BuiltInType.UInt64:
|
||||
return decoder.ReadUInt64Array("Value").ToArray();
|
||||
case BuiltInType.Float:
|
||||
return decoder.ReadFloatArray("Value").ToArray();
|
||||
case BuiltInType.Double:
|
||||
return decoder.ReadDoubleArray("Value").ToArray();
|
||||
case BuiltInType.String:
|
||||
return decoder.ReadStringArray("Value").ToArray();
|
||||
case BuiltInType.DateTime:
|
||||
return decoder.ReadDateTimeArray("Value").ToArray();
|
||||
case BuiltInType.Guid:
|
||||
return decoder.ReadGuidArray("Value").ToArray();
|
||||
case BuiltInType.ByteString:
|
||||
return decoder.ReadByteStringArray("Value").ToArray();
|
||||
case BuiltInType.XmlElement:
|
||||
return decoder.ReadXmlElementArray("Value").ToArray();
|
||||
case BuiltInType.NodeId:
|
||||
return decoder.ReadNodeIdArray("Value").ToArray();
|
||||
case BuiltInType.ExpandedNodeId:
|
||||
return decoder.ReadExpandedNodeIdArray("Value").ToArray();
|
||||
case BuiltInType.StatusCode:
|
||||
return decoder.ReadStatusCodeArray("Value").ToArray();
|
||||
case BuiltInType.QualifiedName:
|
||||
return decoder.ReadQualifiedNameArray("Value").ToArray();
|
||||
case BuiltInType.LocalizedText:
|
||||
return decoder.ReadLocalizedTextArray("Value").ToArray();
|
||||
case BuiltInType.ExtensionObject:
|
||||
return decoder.ReadExtensionObjectArray("Value").ToArray();
|
||||
case BuiltInType.DiagnosticInfo:
|
||||
return decoder.ReadDiagnosticInfoArray("Value").ToArray();
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public static object GetDecodeDelegate(this BuiltInType builtIn, ServerPlatformJSONDecoder decoder)
|
||||
{
|
||||
switch (builtIn)
|
||||
{
|
||||
case BuiltInType.Boolean:
|
||||
return decoder.ReadBoolean("Value");
|
||||
case BuiltInType.SByte:
|
||||
return decoder.ReadSByte("Value");
|
||||
case BuiltInType.Byte:
|
||||
return decoder.ReadByte("Value");
|
||||
case BuiltInType.Int16:
|
||||
return decoder.ReadInt16("Value");
|
||||
case BuiltInType.UInt16:
|
||||
return decoder.ReadUInt16("Value");
|
||||
case BuiltInType.Int32:
|
||||
return decoder.ReadInt32("Value");
|
||||
case BuiltInType.UInt32:
|
||||
return decoder.ReadUInt32("Value");
|
||||
case BuiltInType.Int64:
|
||||
return decoder.ReadInt64("Value");
|
||||
case BuiltInType.UInt64:
|
||||
return decoder.ReadUInt64("Value");
|
||||
case BuiltInType.Float:
|
||||
return decoder.ReadFloat("Value");
|
||||
case BuiltInType.Double:
|
||||
return decoder.ReadDouble("Value");
|
||||
case BuiltInType.String:
|
||||
return decoder.ReadString("Value");
|
||||
case BuiltInType.DateTime:
|
||||
return decoder.ReadDateTime("Value");
|
||||
case BuiltInType.Guid:
|
||||
return decoder.ReadGuid("Value");
|
||||
case BuiltInType.ByteString:
|
||||
return decoder.ReadByteString("Value");
|
||||
case BuiltInType.XmlElement:
|
||||
return decoder.ReadXmlElement("Value");
|
||||
case BuiltInType.NodeId:
|
||||
return decoder.ReadNodeId("Value");
|
||||
case BuiltInType.ExpandedNodeId:
|
||||
return decoder.ReadExpandedNodeId("Value");
|
||||
case BuiltInType.StatusCode:
|
||||
return decoder.ReadStatusCode("Value");
|
||||
case BuiltInType.QualifiedName:
|
||||
return decoder.ReadQualifiedName("Value");
|
||||
case BuiltInType.LocalizedText:
|
||||
return decoder.ReadLocalizedText("Value");
|
||||
case BuiltInType.ExtensionObject:
|
||||
return decoder.ReadExtensionObject("Value");
|
||||
case BuiltInType.DiagnosticInfo:
|
||||
return decoder.ReadDiagnosticInfo("Value");
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
public static object GetDecodeMatrixDelegate(this BuiltInType builtIn, ServerPlatformJSONDecoder decoder)
|
||||
{
|
||||
switch (builtIn)
|
||||
{
|
||||
case BuiltInType.Boolean:
|
||||
return decoder.ReadBooleanArray("Value").ToArray();
|
||||
case BuiltInType.SByte:
|
||||
return decoder.ReadSByteArray("Value").ToArray();
|
||||
case BuiltInType.Byte:
|
||||
return decoder.ReadByteArray("Value").ToArray();
|
||||
case BuiltInType.Int16:
|
||||
return decoder.ReadInt16Array("Value").ToArray();
|
||||
case BuiltInType.UInt16:
|
||||
return decoder.ReadUInt16Array("Value").ToArray();
|
||||
case BuiltInType.Int32:
|
||||
return decoder.ReadInt32Array("Value").ToArray();
|
||||
case BuiltInType.UInt32:
|
||||
return decoder.ReadUInt32Array("Value").ToArray();
|
||||
case BuiltInType.Int64:
|
||||
return decoder.ReadInt64Array("Value").ToArray();
|
||||
case BuiltInType.UInt64:
|
||||
return decoder.ReadUInt64Array("Value").ToArray();
|
||||
case BuiltInType.Float:
|
||||
return decoder.ReadFloatArray("Value").ToArray();
|
||||
case BuiltInType.Double:
|
||||
return decoder.ReadDoubleArray("Value").ToArray();
|
||||
case BuiltInType.String:
|
||||
return decoder.ReadStringArray("Value").ToArray();
|
||||
case BuiltInType.DateTime:
|
||||
return decoder.ReadDateTimeArray("Value").ToArray();
|
||||
case BuiltInType.Guid:
|
||||
return decoder.ReadGuidArray("Value").ToArray();
|
||||
case BuiltInType.ByteString:
|
||||
return decoder.ReadByteStringArray("Value").ToArray();
|
||||
case BuiltInType.XmlElement:
|
||||
return decoder.ReadXmlElementArray("Value").ToArray();
|
||||
case BuiltInType.NodeId:
|
||||
return decoder.ReadNodeIdArray("Value").ToArray();
|
||||
case BuiltInType.ExpandedNodeId:
|
||||
return decoder.ReadExpandedNodeIdArray("Value").ToArray();
|
||||
case BuiltInType.StatusCode:
|
||||
return decoder.ReadStatusCodeArray("Value").ToArray();
|
||||
case BuiltInType.QualifiedName:
|
||||
return decoder.ReadQualifiedNameArray("Value").ToArray();
|
||||
case BuiltInType.LocalizedText:
|
||||
return decoder.ReadLocalizedTextArray("Value").ToArray();
|
||||
case BuiltInType.ExtensionObject:
|
||||
return decoder.ReadExtensionObjectArray("Value").ToArray();
|
||||
case BuiltInType.DiagnosticInfo:
|
||||
return decoder.ReadDiagnosticInfoArray("Value").ToArray();
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class CollectionInitializerExtensionMethods
|
||||
{
|
||||
public static void Add(this IList<JToken> list, IList<JToken> toAdd)
|
||||
{
|
||||
foreach (var a in toAdd)
|
||||
{
|
||||
list.Add(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ExpandedNodeIdExtensionMethods
|
||||
{
|
||||
public static string ToStringId(this ExpandedNodeId expandedNodeId, NamespaceTable namespaceTable)
|
||||
{
|
||||
var nodeId = ExpandedNodeId.ToNodeId(expandedNodeId, namespaceTable);
|
||||
return $"{nodeId.NamespaceIndex}-{nodeId.Identifier}";
|
||||
}
|
||||
}
|
||||
public static class JTokenExtensionMethods
|
||||
{
|
||||
public static int CalculateActualValueRank(this JToken jToken)
|
||||
{
|
||||
if (jToken.Type != JTokenType.Array)
|
||||
return -1;
|
||||
|
||||
var jArray = jToken.ToArray();
|
||||
int numDimensions = 1;
|
||||
|
||||
while (jArray.GetElementsType() == JTokenType.Array)
|
||||
{
|
||||
jArray = jArray.Children().ToArray();
|
||||
numDimensions++;
|
||||
}
|
||||
|
||||
return numDimensions;
|
||||
}
|
||||
|
||||
public static JTokenType GetElementsType(this JToken[] jTokens)
|
||||
{
|
||||
if (!jTokens.ElementsHasSameType())
|
||||
throw new Exception("The array sent must have the same type of element in each dimension");
|
||||
return jTokens.First().Type;
|
||||
}
|
||||
|
||||
public static int[] GetJsonArrayDimensions(this JToken jToken)
|
||||
{
|
||||
if (jToken.Type != JTokenType.Array)
|
||||
throw new Exception("Expected a JSON Array but received a " + jToken.Type);
|
||||
while (jToken.HasValues)
|
||||
{
|
||||
var children = jToken.Children();
|
||||
var count = children.First().Count();
|
||||
|
||||
//if(children.All(x => x.Count() == count)) throw new Exception("The array sent must have the same number of element in each dimension");
|
||||
|
||||
foreach (var child in children)
|
||||
{
|
||||
if (child.Count() != count)
|
||||
throw new Exception("The array sent must have the same number of element in each dimension");
|
||||
}
|
||||
jToken = jToken.Last;
|
||||
}
|
||||
|
||||
const string pattern = @"\[(\d+)\]";
|
||||
var regex = new Regex(pattern);
|
||||
var matchColl = regex.Matches(jToken.Path);
|
||||
var dimensions = new int[matchColl.Count];
|
||||
for (var i = 0; i < matchColl.Count; i++)
|
||||
{
|
||||
dimensions[i] = int.Parse(matchColl[i].Groups[1].Value) + 1;
|
||||
}
|
||||
return dimensions;
|
||||
}
|
||||
|
||||
public static JArray ToOneDimensionJArray(this JToken jToken)
|
||||
{
|
||||
var dimensions = jToken.GetJsonArrayDimensions();
|
||||
return jToken.ToOneDimensionJArray(dimensions);
|
||||
}
|
||||
|
||||
public static JArray ToOneDimensionJArray(this JToken jToken, int[] dimensions)
|
||||
{
|
||||
var flatValuesToWrite = jToken.Children().ToArray();
|
||||
for (var i = 0; i < dimensions.Length - 1; i++)
|
||||
flatValuesToWrite = flatValuesToWrite.SelectMany(a => a).ToArray();
|
||||
|
||||
return new JArray(flatValuesToWrite);
|
||||
}
|
||||
private static bool ElementsHasSameType(this JToken[] jTokens)
|
||||
{
|
||||
var checkType = jTokens[0].Type == JTokenType.Integer ? JTokenType.Float : jTokens[0].Type;
|
||||
return jTokens
|
||||
.Select(x => (x.Type == JTokenType.Integer) ? JTokenType.Float : x.Type)
|
||||
.All(t => t == checkType);
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,48 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using Opc.Ua;
|
||||
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace ThingsGateway.OPCUA;
|
||||
|
||||
public static class ServerPlatformUtils
|
||||
{
|
||||
public static NodeId ParsePlatformNodeIdString(string str)
|
||||
{
|
||||
const string pattern = @"^(\d+)-(?:(\d+)|(\S+))$";
|
||||
var match = Regex.Match(str, pattern);
|
||||
var isString = match.Groups[3].Length != 0;
|
||||
var isNumeric = match.Groups[2].Length != 0;
|
||||
|
||||
var idStr = (isString) ? $"s={match.Groups[3]}" : $"i={match.Groups[2]}";
|
||||
var builtStr = $"ns={match.Groups[1]};" + idStr;
|
||||
NodeId nodeId = null;
|
||||
try
|
||||
{
|
||||
nodeId = new NodeId(builtStr);
|
||||
}
|
||||
catch (ServiceResultException exc)
|
||||
{
|
||||
switch (exc.StatusCode)
|
||||
{
|
||||
case StatusCodes.BadNodeIdInvalid:
|
||||
throw new Exception("Wrong Type Error: String is not formatted as expected (number-yyy where yyy can be string or number or guid)");
|
||||
default:
|
||||
throw new Exception(exc.Message);
|
||||
}
|
||||
}
|
||||
|
||||
return nodeId;
|
||||
}
|
||||
}
|
||||
@@ -17,10 +17,8 @@ internal class OPCUATag : BaseDataVariableState
|
||||
public OPCUATag(NodeState parent) : base(parent)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// 变量数据类型
|
||||
/// </summary>
|
||||
public Type NETDataType { get; set; }
|
||||
|
||||
public bool IsDataTypeInit { get; set; }
|
||||
/// <summary>
|
||||
/// 变量Id
|
||||
/// </summary>
|
||||
|
||||
@@ -14,6 +14,8 @@ using Mapster;
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
using Opc.Ua;
|
||||
using Opc.Ua.Server;
|
||||
|
||||
@@ -98,7 +100,12 @@ public class ThingsGatewayNodeManager : CustomNodeManager2
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
FolderState memoryfs = CreateFolder(null, "ThingsGateway中间变量", "ThingsGateway中间变量");
|
||||
var variableRunTimes = _globalDeviceData.MemoryVariables;
|
||||
foreach (var item in variableRunTimes)
|
||||
{
|
||||
CreateVariable(memoryfs, item);
|
||||
}
|
||||
AddPredefinedNode(SystemContext, rootFolder);
|
||||
|
||||
}
|
||||
@@ -108,7 +115,6 @@ public class ThingsGatewayNodeManager : CustomNodeManager2
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
_idTags.Clear();
|
||||
_idTags = null;
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
/// <summary>
|
||||
@@ -256,7 +262,6 @@ public class ThingsGatewayNodeManager : CustomNodeManager2
|
||||
}
|
||||
uaTag.ClearChangeMasks(SystemContext, false);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,7 +290,35 @@ public class ThingsGatewayNodeManager : CustomNodeManager2
|
||||
object newValue;
|
||||
try
|
||||
{
|
||||
newValue = Convert.ChangeType(value, tag.NETDataType);
|
||||
if (!tag.IsDataTypeInit && tag.DataType == DataTypeIds.ObjectNode)
|
||||
{
|
||||
if (tag.Value != null)
|
||||
{
|
||||
tag.IsDataTypeInit = true;
|
||||
var tp = tag.Value.GetType();
|
||||
if (tp == typeof(JArray))
|
||||
{
|
||||
try
|
||||
{
|
||||
tp = ((JValue)((JArray)tag.Value).FirstOrDefault()).Value.GetType();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
if (tp == typeof(JValue))
|
||||
{
|
||||
tp = ((JValue)tag.Value).Value.GetType();
|
||||
}
|
||||
tag.DataType = DataNodeType(tp);
|
||||
|
||||
tag.ClearChangeMasks(SystemContext, false);
|
||||
}
|
||||
}
|
||||
|
||||
//注意OPCUA Value值不能是JToken类型
|
||||
var typeManager = new ServerDataTypeManager(this.Server.MessageContext, this.SystemContext);
|
||||
newValue = typeManager.GetDataValueFromVariableState(JToken.FromObject(value), tag.DataType);
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -338,15 +371,14 @@ public class ThingsGatewayNodeManager : CustomNodeManager2
|
||||
variable.DisplayName = new LocalizedText(variableRunTime.Name);
|
||||
variable.WriteMask = AttributeWriteMask.DisplayName | AttributeWriteMask.Description;
|
||||
variable.UserWriteMask = AttributeWriteMask.DisplayName | AttributeWriteMask.Description;
|
||||
variable.ValueRank = ValueRanks.Scalar;
|
||||
variable.ValueRank = ValueRanks.Any;
|
||||
variable.Id = variableRunTime.Id;
|
||||
variable.DataType = DataNodeType(variableRunTime);
|
||||
variable.NETDataType = NETDataNodeType(variableRunTime);
|
||||
var level = ProtectTypeTrans(variableRunTime);
|
||||
variable.AccessLevel = level;
|
||||
variable.UserAccessLevel = level;
|
||||
variable.Historizing = variableRunTime.HisEnable;
|
||||
variable.Value = Opc.Ua.TypeInfo.GetDefaultValue(variable.DataType, ValueRanks.Scalar, Server.TypeTree);
|
||||
variable.Value = Opc.Ua.TypeInfo.GetDefaultValue(variable.DataType, ValueRanks.Any, Server.TypeTree);
|
||||
var code = variableRunTime.IsOnline ? StatusCodes.Good : StatusCodes.Bad;
|
||||
variable.StatusCode = code;
|
||||
variable.Timestamp = variableRunTime.CollectTime;
|
||||
@@ -365,99 +397,44 @@ public class ThingsGatewayNodeManager : CustomNodeManager2
|
||||
/// <returns></returns>
|
||||
private NodeId DataNodeType(DeviceVariableRunTime variableRunTime)
|
||||
{
|
||||
Type tp;
|
||||
Type tp = typeof(object);
|
||||
var str = GetPropertyValue(variableRunTime, nameof(OPCUAServerVariableProperty.DataTypeEnum));
|
||||
if (Enum.TryParse<DataTypeEnum>(str, out DataTypeEnum result))
|
||||
{
|
||||
tp = result.GetSystemType();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if (variableRunTime.Value != null)
|
||||
{
|
||||
tp = variableRunTime.Value.GetType();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (variableRunTime.ReadExpressions.IsNullOrEmpty())
|
||||
{
|
||||
tp = variableRunTime.DataTypeEnum.GetSystemType();
|
||||
}
|
||||
else
|
||||
{
|
||||
var tp1 = variableRunTime.DataTypeEnum.GetSystemType();
|
||||
var data = variableRunTime.ReadExpressions.GetExpressionsResult(GetDefaultValue(tp1));
|
||||
tp = data.GetType();
|
||||
}
|
||||
}
|
||||
}
|
||||
return DataNodeType(tp);
|
||||
}
|
||||
|
||||
private static NodeId DataNodeType(Type tp)
|
||||
{
|
||||
if (tp == typeof(bool))
|
||||
return DataTypeIds.Boolean;
|
||||
if (tp == typeof(byte))
|
||||
return DataTypeIds.Byte;
|
||||
if (tp == typeof(sbyte))
|
||||
return DataTypeIds.SByte;
|
||||
if (tp == typeof(Int16))
|
||||
if (tp == typeof(short))
|
||||
return DataTypeIds.Int16;
|
||||
if (tp == typeof(UInt16))
|
||||
if (tp == typeof(ushort))
|
||||
return DataTypeIds.UInt16;
|
||||
if (tp == typeof(Int32))
|
||||
if (tp == typeof(int))
|
||||
return DataTypeIds.Int32;
|
||||
if (tp == typeof(UInt32))
|
||||
if (tp == typeof(uint))
|
||||
return DataTypeIds.UInt32;
|
||||
if (tp == typeof(Int64))
|
||||
if (tp == typeof(long))
|
||||
return DataTypeIds.Int64;
|
||||
if (tp == typeof(UInt64))
|
||||
if (tp == typeof(ulong))
|
||||
return DataTypeIds.UInt64;
|
||||
if (tp == typeof(float))
|
||||
return DataTypeIds.Float;
|
||||
if (tp == typeof(Double))
|
||||
if (tp == typeof(double))
|
||||
return DataTypeIds.Double;
|
||||
if (tp == typeof(String))
|
||||
if (tp == typeof(string))
|
||||
return DataTypeIds.String;
|
||||
if (tp == typeof(DateTime))
|
||||
return DataTypeIds.TimeString;
|
||||
return DataTypeIds.ObjectNode;
|
||||
}
|
||||
/// <summary>
|
||||
/// 网关转OPC数据类型
|
||||
/// </summary>
|
||||
/// <param name="variableRunTime"></param>
|
||||
/// <returns></returns>
|
||||
private Type NETDataNodeType(DeviceVariableRunTime variableRunTime)
|
||||
{
|
||||
Type tp;
|
||||
var str = GetPropertyValue(variableRunTime, nameof(OPCUAServerVariableProperty.DataTypeEnum));
|
||||
if (Enum.TryParse<DataTypeEnum>(str, out DataTypeEnum result))
|
||||
{
|
||||
tp = result.GetSystemType();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if (variableRunTime.Value != null)
|
||||
{
|
||||
tp = variableRunTime.Value.GetType();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (variableRunTime.ReadExpressions.IsNullOrEmpty())
|
||||
{
|
||||
tp = variableRunTime.DataTypeEnum.GetSystemType();
|
||||
}
|
||||
else
|
||||
{
|
||||
var tp1 = variableRunTime.DataTypeEnum.GetSystemType();
|
||||
var data = variableRunTime.ReadExpressions.GetExpressionsResult(GetDefaultValue(tp1));
|
||||
tp = data.GetType();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tp;
|
||||
}
|
||||
|
||||
private ServiceResult OnWriteDataValue(ISystemContext context, NodeState node, NumericRange indexRange, QualifiedName dataEncoding, ref object value, ref StatusCode statusCode, ref DateTime timestamp)
|
||||
{
|
||||
@@ -469,19 +446,6 @@ public class ThingsGatewayNodeManager : CustomNodeManager2
|
||||
return StatusCodes.BadUserAccessDenied;
|
||||
}
|
||||
OPCUATag variable = node as OPCUATag;
|
||||
// 验证数据类型。
|
||||
//Opc.Ua.TypeInfo typeInfo = Opc.Ua.TypeInfo.IsInstanceOfDataType(
|
||||
// value,
|
||||
// variable.DataType,
|
||||
// variable.ValueRank,
|
||||
// context.NamespaceUris,
|
||||
// context.TypeTable);
|
||||
|
||||
//if (typeInfo == null || typeInfo == Opc.Ua.TypeInfo.Unknown)
|
||||
//{
|
||||
// return StatusCodes.BadTypeMismatch;
|
||||
//}
|
||||
// 检查索引范围。
|
||||
if (_idTags.TryGetValue(variable.NodeId, out OPCUATag tag))
|
||||
{
|
||||
if (StatusCode.IsGood(variable.StatusCode))
|
||||
|
||||
@@ -138,7 +138,7 @@ public partial class ThingsGatewayServer : StandardServer
|
||||
/// <inheritdoc/>
|
||||
protected override void OnServerStarting(ApplicationConfiguration configuration)
|
||||
{
|
||||
_logger.LogInformation("OPCUAServer启动中......");
|
||||
_logger.LogInformation("OPCUAServer正在启动");
|
||||
base.OnServerStarting(configuration);
|
||||
|
||||
// 由应用程序决定如何验证用户身份令牌。
|
||||
@@ -149,7 +149,7 @@ public partial class ThingsGatewayServer : StandardServer
|
||||
/// <inheritdoc/>
|
||||
protected override void OnServerStopping()
|
||||
{
|
||||
_logger.LogInformation("OPCUAServer停止中......");
|
||||
_logger.LogInformation("OPCUAServer正在停止");
|
||||
base.OnServerStopping();
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace ThingsGateway.Application
|
||||
public async Task DeleteUserCacheByRoleIds(EventHandlerExecutingContext context)
|
||||
{
|
||||
var roleIds = (List<long>)context.Source.Payload;//获取角色ID
|
||||
// 创建新的作用域
|
||||
// 创建新的作用域
|
||||
using var scope = _services.CreateScope();
|
||||
// 解析角色服务
|
||||
var relationService = scope.ServiceProvider.GetRequiredService<IRelationService>();
|
||||
|
||||
@@ -168,7 +168,6 @@ public class AlarmWorker : BackgroundService
|
||||
private Task<Task> HisAlarmTask;
|
||||
private Task<Task> RealAlarmTask;
|
||||
private CacheDb CacheDb { get; set; }
|
||||
private EasyLock easyLock { get; set; } = new();
|
||||
/// <summary>
|
||||
/// 重启服务
|
||||
/// </summary>
|
||||
@@ -180,9 +179,8 @@ public class AlarmWorker : BackgroundService
|
||||
|
||||
internal void Start()
|
||||
{
|
||||
try
|
||||
lock (this)
|
||||
{
|
||||
easyLock.Lock();
|
||||
|
||||
foreach (var item in _globalDeviceData.CollectDevices)
|
||||
{
|
||||
@@ -192,19 +190,13 @@ public class AlarmWorker : BackgroundService
|
||||
Init();
|
||||
RealAlarmTask.Start();
|
||||
HisAlarmTask.Start();
|
||||
}
|
||||
finally
|
||||
{
|
||||
easyLock.UnLock();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
internal void Stop(IEnumerable<CollectDeviceRunTime> devices)
|
||||
{
|
||||
try
|
||||
lock (this)
|
||||
{
|
||||
easyLock.Lock();
|
||||
|
||||
foreach (var device in devices)
|
||||
{
|
||||
@@ -212,8 +204,10 @@ public class AlarmWorker : BackgroundService
|
||||
device.DeviceVariableRunTimes?.ForEach(v => { v.VariableCollectChange -= DeviceVariableChange; });
|
||||
}
|
||||
|
||||
CancellationTokenSource StoppingToken = StoppingTokens.LastOrDefault();
|
||||
StoppingToken?.Cancel();
|
||||
foreach (var token in StoppingTokens)
|
||||
{
|
||||
token.Cancel();
|
||||
}
|
||||
_logger?.LogInformation($"实时报警线程停止中");
|
||||
var realAlarmResult = RealAlarmTask?.Result;
|
||||
if (realAlarmResult?.Status != TaskStatus.Canceled)
|
||||
@@ -266,12 +260,11 @@ public class AlarmWorker : BackgroundService
|
||||
_logger?.LogWarning($"历史报警线程停止超时,已强制取消");
|
||||
}
|
||||
HisAlarmTask?.SafeDispose();
|
||||
StoppingToken?.SafeDispose();
|
||||
StoppingTokens.Remove(StoppingToken);
|
||||
}
|
||||
finally
|
||||
{
|
||||
easyLock.UnLock();
|
||||
foreach (var token in StoppingTokens)
|
||||
{
|
||||
token.SafeDispose();
|
||||
}
|
||||
StoppingTokens.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,9 +29,12 @@ public class CacheDb
|
||||
public CacheDb(string id)
|
||||
{
|
||||
Id = id;
|
||||
Directory.CreateDirectory("CacheDb");
|
||||
GetCacheDb().DbMaintenance.CreateDatabase();//创建数据库
|
||||
GetCacheDb().CodeFirst.InitTables(typeof(CacheTable));
|
||||
Directory.CreateDirectory(Path.Combine(AppContext.BaseDirectory, "CacheDb"));
|
||||
if (!File.Exists(Path.Combine(AppContext.BaseDirectory, "CacheDb", $"{Id}.db")))
|
||||
{
|
||||
GetCacheDb().DbMaintenance.CreateDatabase();//创建数据库
|
||||
GetCacheDb().CodeFirst.InitTables(typeof(CacheTable));
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取数据库链接
|
||||
|
||||
@@ -303,7 +303,7 @@ public class CollectDeviceCore : DisposableObject
|
||||
isUpDevice = true;
|
||||
}
|
||||
_device = device;
|
||||
using var scope = _scopeFactory.CreateScope();
|
||||
var scope = _scopeFactory.CreateScope();
|
||||
var loggerFactory = scope.ServiceProvider.GetRequiredService<ILoggerFactory>();
|
||||
_logger = loggerFactory.CreateLogger("采集设备:" + _device.Name);
|
||||
//更新插件信息
|
||||
|
||||
@@ -166,8 +166,10 @@ public class CollectDeviceThread : IDisposable
|
||||
{
|
||||
return;
|
||||
}
|
||||
CancellationTokenSource StoppingToken = StoppingTokens.LastOrDefault();
|
||||
StoppingToken?.Cancel();
|
||||
foreach (var token in StoppingTokens)
|
||||
{
|
||||
token.Cancel();
|
||||
}
|
||||
bool? taskResult = false;
|
||||
try
|
||||
{
|
||||
@@ -188,14 +190,14 @@ public class CollectDeviceThread : IDisposable
|
||||
device.Logger?.LogInformation($"{device.Device.Name}采集线程停止超时,已强制取消");
|
||||
}
|
||||
}
|
||||
StoppingToken?.SafeDispose();
|
||||
|
||||
foreach (var token in StoppingTokens)
|
||||
{
|
||||
token?.SafeDispose();
|
||||
}
|
||||
DeviceTask?.SafeDispose();
|
||||
DeviceTask = null;
|
||||
if (StoppingToken != null)
|
||||
{
|
||||
StoppingTokens.Remove(StoppingToken);
|
||||
}
|
||||
StoppingTokens.Clear();
|
||||
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
|
||||
@@ -91,6 +91,7 @@ public class CollectDeviceWorker : BackgroundService
|
||||
RemoveAllDeviceThread();
|
||||
await CreatAllDeviceThreadsAsync();
|
||||
StartAllDeviceThreads();
|
||||
_logger.LogInformation("启动其他服务线程");
|
||||
StartOtherHostService();
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -313,7 +314,7 @@ public class CollectDeviceWorker : BackgroundService
|
||||
/// </summary>
|
||||
private void StartOtherHostService()
|
||||
{
|
||||
using var scope = _scopeFactory.CreateScope();
|
||||
var scope = _scopeFactory.CreateScope();
|
||||
var alarmHostService = scope.GetBackgroundService<AlarmWorker>();
|
||||
var historyValueService = scope.GetBackgroundService<HistoryValueWorker>();
|
||||
alarmHostService?.Start();
|
||||
@@ -330,8 +331,7 @@ public class CollectDeviceWorker : BackgroundService
|
||||
{
|
||||
if (_globalDeviceData.CollectDevices?.Count > 0)
|
||||
{
|
||||
using var scope = _scopeFactory.CreateScope();
|
||||
|
||||
var scope = _scopeFactory.CreateScope();
|
||||
var alarmHostService = scope.GetBackgroundService<AlarmWorker>();
|
||||
var historyValueService = scope.GetBackgroundService<HistoryValueWorker>();
|
||||
alarmHostService?.Stop(_globalDeviceData.CollectDevices);
|
||||
|
||||
@@ -185,8 +185,10 @@ public class MemoryVariableWorker : BackgroundService
|
||||
|
||||
internal void Stop()
|
||||
{
|
||||
CancellationTokenSource StoppingToken = StoppingTokens.LastOrDefault();
|
||||
StoppingToken?.Cancel();
|
||||
foreach (var token in StoppingTokens)
|
||||
{
|
||||
token.Cancel();
|
||||
}
|
||||
|
||||
_logger?.LogInformation($"中间变量计算线程停止中");
|
||||
var hisHisResult = MemoryWorkerTask?.GetAwaiter().GetResult();
|
||||
@@ -212,8 +214,11 @@ public class MemoryVariableWorker : BackgroundService
|
||||
_logger?.LogInformation($"历史数据线程停止超时,已强制取消");
|
||||
}
|
||||
MemoryWorkerTask?.SafeDispose();
|
||||
StoppingToken?.SafeDispose();
|
||||
StoppingTokens.Remove(StoppingToken);
|
||||
foreach (var token in StoppingTokens)
|
||||
{
|
||||
token.SafeDispose();
|
||||
}
|
||||
StoppingTokens.Clear();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -147,7 +147,6 @@ public class HistoryValueWorker : BackgroundService
|
||||
/// </summary>
|
||||
public ConcurrentList<CancellationTokenSource> StoppingTokens = new();
|
||||
private Task<Task> HistoryValueTask;
|
||||
private EasyLock easyLock { get; set; } = new();
|
||||
/// <summary>
|
||||
/// 离线缓存
|
||||
/// </summary>
|
||||
@@ -331,9 +330,8 @@ public class HistoryValueWorker : BackgroundService
|
||||
|
||||
internal void Start()
|
||||
{
|
||||
try
|
||||
lock (this)
|
||||
{
|
||||
easyLock.Lock();
|
||||
|
||||
foreach (var device in _globalDeviceData.CollectDevices)
|
||||
{
|
||||
@@ -344,17 +342,13 @@ public class HistoryValueWorker : BackgroundService
|
||||
Init();
|
||||
HistoryValueTask.Start();
|
||||
}
|
||||
finally
|
||||
{
|
||||
easyLock.UnLock();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal void Stop(IEnumerable<CollectDeviceRunTime> devices)
|
||||
{
|
||||
try
|
||||
lock (this)
|
||||
{
|
||||
easyLock.Lock();
|
||||
|
||||
foreach (var device in devices)
|
||||
{
|
||||
@@ -362,8 +356,10 @@ public class HistoryValueWorker : BackgroundService
|
||||
device.DeviceVariableRunTimes?.Where(a => a.HisEnable == true)?.ForEach(v => { v.VariableValueChange -= DeviceVariableValueChange; });
|
||||
}
|
||||
|
||||
CancellationTokenSource StoppingToken = StoppingTokens.LastOrDefault();
|
||||
StoppingToken?.Cancel();
|
||||
foreach (var token in StoppingTokens)
|
||||
{
|
||||
token.Cancel();
|
||||
}
|
||||
|
||||
_logger?.LogInformation($"历史数据线程停止中");
|
||||
var hisHisResult = HistoryValueTask?.GetAwaiter().GetResult();
|
||||
@@ -389,12 +385,11 @@ public class HistoryValueWorker : BackgroundService
|
||||
_logger?.LogInformation($"历史数据线程停止超时,已强制取消");
|
||||
}
|
||||
HistoryValueTask?.SafeDispose();
|
||||
StoppingToken?.SafeDispose();
|
||||
StoppingTokens.Remove(StoppingToken);
|
||||
}
|
||||
finally
|
||||
{
|
||||
easyLock.UnLock();
|
||||
foreach (var token in StoppingTokens)
|
||||
{
|
||||
token.SafeDispose();
|
||||
}
|
||||
StoppingTokens.Clear();
|
||||
}
|
||||
}
|
||||
private void DeviceVariableCollectChange(DeviceVariableRunTime variable)
|
||||
|
||||
@@ -58,7 +58,7 @@ public class UploadDeviceCore : DisposableObject
|
||||
{
|
||||
|
||||
_scopeFactory = scopeFactory;
|
||||
using var scope = scopeFactory.CreateScope();
|
||||
var scope = scopeFactory.CreateScope();
|
||||
|
||||
_pluginService = scope.ServiceProvider.GetService<PluginSingletonService>();
|
||||
_driverPluginService = scope.ServiceProvider.GetService<IDriverPluginService>();
|
||||
@@ -258,7 +258,7 @@ public class UploadDeviceCore : DisposableObject
|
||||
try
|
||||
{
|
||||
_device = device;
|
||||
using var scope = _scopeFactory.CreateScope();
|
||||
var scope = _scopeFactory.CreateScope();
|
||||
var loggerFactory = scope.ServiceProvider.GetRequiredService<ILoggerFactory>();
|
||||
_logger = loggerFactory.CreateLogger("上传设备:" + _device.Name);
|
||||
//更新插件信息
|
||||
|
||||
@@ -155,8 +155,10 @@ public class UploadDeviceThread : IDisposable
|
||||
{
|
||||
return;
|
||||
}
|
||||
CancellationTokenSource StoppingToken = StoppingTokens.LastOrDefault();
|
||||
StoppingToken?.Cancel();
|
||||
foreach (var token in StoppingTokens)
|
||||
{
|
||||
token.Cancel();
|
||||
}
|
||||
bool? taskResult = false;
|
||||
try
|
||||
{
|
||||
@@ -177,13 +179,14 @@ public class UploadDeviceThread : IDisposable
|
||||
device.Logger?.LogInformation($"{device.Device.Name}上传线程停止超时,已强制取消");
|
||||
}
|
||||
}
|
||||
StoppingToken?.SafeDispose();
|
||||
foreach (var token in StoppingTokens)
|
||||
{
|
||||
token?.SafeDispose();
|
||||
}
|
||||
DeviceTask?.SafeDispose();
|
||||
DeviceTask = null;
|
||||
if (StoppingToken != null)
|
||||
{
|
||||
StoppingTokens.Remove(StoppingToken);
|
||||
}
|
||||
|
||||
StoppingTokens.Clear();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user