支持OPCUAServer数组类型,添加缓存文件存在判断

This commit is contained in:
Kimdiego2098
2023-07-21 21:28:39 +08:00
parent 3515775267
commit 82b7881765
31 changed files with 2098 additions and 175 deletions

View File

@@ -7,8 +7,8 @@
[![NuGet(ThingsGateway)](https://img.shields.io/nuget/v/ThingsGateway.Foundation.svg?label=ThingsGateway.Foundation)](https://www.nuget.org/packages/kimdiego/)
[![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html)
[![star](https://gitee.com/diego2098/ThingsGateway/badge/star.svg)](https://gitee.com/diego2098/ThingsGateway/stargazers)
[![star](https://gitee.com/diego2098/ThingsGateway/badge/fork.svg)](https://gitee.com/diego2098/ThingsGateway/members)
![star](https://img.shields.io/badge/QQ群-605534569-blue)
[![fork](https://gitee.com/diego2098/ThingsGateway/badge/fork.svg)](https://gitee.com/diego2098/ThingsGateway/members)
![qq](https://img.shields.io/badge/QQ群-605534569-blue)
</div>
</div>

View File

@@ -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());
}

View File

@@ -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

View File

@@ -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 =>

View File

@@ -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;
}

View File

@@ -0,0 +1 @@
- Json转换代码来自[OPCUAWebPlatformUniCT](https://github.com/OPCUAUniCT/OPCUAWebPlatformUniCT),特此说明,感谢开源项目!

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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>

View File

@@ -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))

View File

@@ -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();
}

View File

@@ -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>();

View File

@@ -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();
}
}

View File

@@ -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>
/// 获取数据库链接

View File

@@ -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);
//更新插件信息

View File

@@ -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>

View File

@@ -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);

View File

@@ -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();
}

View File

@@ -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)

View File

@@ -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);
//更新插件信息

View File

@@ -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>