Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
79789388fc | ||
|
|
2c4194ee18 | ||
|
|
1b2be585af | ||
|
|
83736647e7 |
@@ -4,10 +4,20 @@
|
||||
/// <summary>表示可以更改其到期时间和时间段的计时器。</summary>
|
||||
public interface ITimer : IDisposable
|
||||
{
|
||||
/// <summary>更改计时器的启动时间和方法调用之间的时间间隔,使用 TimeSpan 值度量时间间隔。</summary>
|
||||
/// <summary>更改计时器的启动时间和方法调用之间的时间间隔。</summary>
|
||||
/// <param name="dueTime">一个 TimeSpan,表示在调用构造 ITimer 时指定的回调方法之前的延迟时间量。 指定 InfiniteTimeSpan 可防止重新启动计时器。 指定 Zero 可立即重新启动计时器。</param>
|
||||
/// <param name="period">构造 Timer 时指定的回调方法调用之间的时间间隔。 指定 InfiniteTimeSpan 可以禁用定期终止。</param>
|
||||
/// <returns></returns>
|
||||
Boolean Change(TimeSpan dueTime, TimeSpan period);
|
||||
public Boolean Change(TimeSpan dueTime, TimeSpan period);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/// <summary>表示可以更改其到期时间和时间段的计时器。</summary>
|
||||
public interface ITimerx : IDisposable
|
||||
{
|
||||
/// <summary>更改计时器的启动时间和方法调用之间的时间间隔。</summary>
|
||||
/// <param name="dueTime">一个 TimeSpan,表示在调用构造 ITimer 时指定的回调方法之前的延迟时间量。 指定 InfiniteTimeSpan 可防止重新启动计时器。 指定 Zero 可立即重新启动计时器。</param>
|
||||
/// <param name="period">构造 Timer 时指定的回调方法调用之间的时间间隔。 指定 InfiniteTimeSpan 可以禁用定期终止。</param>
|
||||
/// <returns></returns>
|
||||
public Boolean Change(int dueTime, int period);
|
||||
}
|
||||
@@ -17,7 +17,7 @@ namespace ThingsGateway.NewLife.Threading;
|
||||
///
|
||||
/// TimerX必须维持对象,否则Scheduler也没有维持对象时,大家很容易一起被GC回收。
|
||||
/// </remarks>
|
||||
public class TimerX : ITimer, IDisposable
|
||||
public class TimerX : ITimer, ITimerx, IDisposable
|
||||
{
|
||||
#region 属性
|
||||
/// <summary>编号</summary>
|
||||
@@ -382,25 +382,32 @@ public class TimerX : ITimer, IDisposable
|
||||
return period;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>更改计时器的启动时间和方法调用之间的时间间隔,使用 TimeSpan 值度量时间间隔。</summary>
|
||||
/// <summary>更改计时器的启动时间和方法调用之间的时间间隔。</summary>
|
||||
/// <param name="dueTime">一个 TimeSpan,表示在调用构造 ITimer 时指定的回调方法之前的延迟时间量。 指定 InfiniteTimeSpan 可防止重新启动计时器。 指定 Zero 可立即重新启动计时器。</param>
|
||||
/// <param name="period">构造 Timer 时指定的回调方法调用之间的时间间隔。 指定 InfiniteTimeSpan 可以禁用定期终止。</param>
|
||||
/// <returns></returns>
|
||||
public Boolean Change(TimeSpan dueTime, TimeSpan period)
|
||||
{
|
||||
return Change(dueTime.Milliseconds, period.Milliseconds);
|
||||
}
|
||||
/// <summary>更改计时器的启动时间和方法调用之间的时间间隔。</summary>
|
||||
/// <param name="dueTime">一个 TimeSpan,表示在调用构造 ITimer 时指定的回调方法之前的延迟时间量。 指定 InfiniteTimeSpan 可防止重新启动计时器。 指定 Zero 可立即重新启动计时器。</param>
|
||||
/// <param name="period">构造 Timer 时指定的回调方法调用之间的时间间隔。 指定 InfiniteTimeSpan 可以禁用定期终止。</param>
|
||||
/// <returns></returns>
|
||||
public Boolean Change(int dueTime, int period)
|
||||
{
|
||||
if (Absolutely) return false;
|
||||
if (Crons?.Length > 0) return false;
|
||||
|
||||
if (period.TotalMilliseconds <= 0)
|
||||
if (period <= 0)
|
||||
{
|
||||
Dispose();
|
||||
return true;
|
||||
}
|
||||
|
||||
Period = (Int32)period.TotalMilliseconds;
|
||||
Period = period;
|
||||
|
||||
if (dueTime.TotalMilliseconds >= 0) SetNext((Int32)dueTime.TotalMilliseconds);
|
||||
if (dueTime >= 0) SetNext(dueTime);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<PluginVersion>10.9.9</PluginVersion>
|
||||
<ProPluginVersion>10.9.9</ProPluginVersion>
|
||||
<PluginVersion>10.9.13</PluginVersion>
|
||||
<ProPluginVersion>10.9.13</ProPluginVersion>
|
||||
<AuthenticationVersion>2.9.5</AuthenticationVersion>
|
||||
<SourceGeneratorVersion>10.9.5</SourceGeneratorVersion>
|
||||
<NET8Version>8.0.17</NET8Version>
|
||||
|
||||
@@ -131,7 +131,7 @@ public abstract class DeviceBase : DisposableObject, IDevice
|
||||
public virtual int RegisterByteLength { get; protected set; } = 1;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual IThingsGatewayBitConverter ThingsGatewayBitConverter { get; protected set; } = new ThingsGatewayBitConverter();
|
||||
public virtual IThingsGatewayBitConverter ThingsGatewayBitConverter { get; } = new ThingsGatewayBitConverter();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool OnLine => Channel.Online;
|
||||
|
||||
@@ -325,6 +325,7 @@ public interface IThingsGatewayBitConverter
|
||||
/// <param name="length">length</param>
|
||||
/// <returns>decimal对象</returns>
|
||||
decimal[] ToDecimal(byte[] buffer, int offset, int length);
|
||||
IThingsGatewayBitConverter GetTransByAddress(string? registerAddress);
|
||||
|
||||
#endregion ToValue
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
|
||||
using ThingsGateway.Foundation.Extension.Generic;
|
||||
using ThingsGateway.Foundation.Extension.String;
|
||||
|
||||
namespace ThingsGateway.Foundation;
|
||||
|
||||
@@ -87,6 +88,132 @@ public partial class ThingsGatewayBitConverter : IThingsGatewayBitConverter
|
||||
/// </summary>
|
||||
public static readonly ThingsGatewayBitConverter LittleEndian;
|
||||
|
||||
|
||||
public virtual void OtherPropertySet(IThingsGatewayBitConverter thingsGatewayBitConverter, string registerAddress)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从设备地址中解析附加信息
|
||||
/// 这个方法获取<see cref="IThingsGatewayBitConverter"/>
|
||||
/// 解析步骤将被缓存。
|
||||
/// </summary>
|
||||
/// <param name="registerAddress">设备地址</param>
|
||||
/// <returns><see cref="IThingsGatewayBitConverter"/> 实例</returns>
|
||||
public virtual IThingsGatewayBitConverter GetTransByAddress(string? registerAddress)
|
||||
{
|
||||
if (registerAddress.IsNullOrEmpty()) return this;
|
||||
|
||||
var type = this.GetType();
|
||||
// 尝试从缓存中获取解析结果
|
||||
//var cacheKey = $"{nameof(ThingsGatewayBitConverterExtension)}_{nameof(GetTransByAddress)}_{type.FullName}_{type.TypeHandle.Value}_{this.ToJsonString()}_{registerAddress}_{this.GetHashCode()}";
|
||||
//if (MemoryCache.TryGetValue(cacheKey, out IThingsGatewayBitConverter cachedConverter))
|
||||
//{
|
||||
// if (cachedConverter.Equals(this))
|
||||
// {
|
||||
// return this;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// return (IThingsGatewayBitConverter)cachedConverter.Map(type);
|
||||
// }
|
||||
//}
|
||||
|
||||
// 去除设备地址两端的空格
|
||||
registerAddress = registerAddress.Trim();
|
||||
|
||||
// 根据分号拆分附加信息
|
||||
var strs = registerAddress.SplitStringBySemicolon();
|
||||
|
||||
DataFormatEnum? dataFormat = null;
|
||||
Encoding? encoding = null;
|
||||
bool? wstring = null;
|
||||
int? stringlength = null;
|
||||
BcdFormatEnum? bcdFormat = null;
|
||||
StringBuilder sb = new();
|
||||
foreach (var str in strs)
|
||||
{
|
||||
// 解析 dataFormat
|
||||
if (str.StartsWith("data=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var dataFormatName = str.Substring(5);
|
||||
try { if (Enum.TryParse<DataFormatEnum>(dataFormatName, true, out var dataFormat1)) dataFormat = dataFormat1; } catch { }
|
||||
}
|
||||
else if (str.StartsWith("vsl=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var wstringName = str.Substring(4);
|
||||
try { if (bool.TryParse(wstringName, out var wstring1)) wstring = wstring1; } catch { }
|
||||
}
|
||||
// 解析 encoding
|
||||
else if (str.StartsWith("encoding=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var encodingName = str.Substring(9);
|
||||
try { encoding = Encoding.GetEncoding(encodingName); } catch { }
|
||||
}
|
||||
// 解析 length
|
||||
else if (str.StartsWith("len=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var lenStr = str.Substring(4);
|
||||
stringlength = lenStr.IsNullOrEmpty() ? null : Convert.ToUInt16(lenStr);
|
||||
}
|
||||
// 解析 bcdFormat
|
||||
else if (str.StartsWith("bcd=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var bcdName = str.Substring(4);
|
||||
try { if (Enum.TryParse<BcdFormatEnum>(bcdName, true, out var bcdFormat1)) bcdFormat = bcdFormat1; } catch { }
|
||||
}
|
||||
|
||||
// 处理其他情况,将未识别的部分拼接回去
|
||||
else
|
||||
{
|
||||
if (sb.Length > 0)
|
||||
sb.Append($";{str}");
|
||||
else
|
||||
sb.Append($"{str}");
|
||||
}
|
||||
}
|
||||
|
||||
// 更新设备地址为去除附加信息后的地址
|
||||
registerAddress = sb.ToString();
|
||||
|
||||
var converter = (IThingsGatewayBitConverter)this!.Map(type);
|
||||
// 如果没有解析出任何附加信息,则直接返回默认的数据转换器
|
||||
if (bcdFormat == null && stringlength == null && encoding == null && dataFormat == null && wstring == null)
|
||||
{
|
||||
//MemoryCache.Set(cacheKey, this!, 3600);
|
||||
return converter;
|
||||
}
|
||||
|
||||
// 根据默认的数据转换器创建新的数据转换器实例
|
||||
|
||||
// 更新新的数据转换器实例的属性值
|
||||
if (encoding != null)
|
||||
{
|
||||
converter.Encoding = encoding;
|
||||
}
|
||||
if (bcdFormat != null)
|
||||
{
|
||||
converter.BcdFormat = bcdFormat.Value;
|
||||
}
|
||||
if (wstring != null)
|
||||
{
|
||||
converter.IsVariableStringLength = wstring.Value;
|
||||
}
|
||||
if (stringlength != null)
|
||||
{
|
||||
converter.StringLength = stringlength.Value;
|
||||
}
|
||||
if (dataFormat != null)
|
||||
{
|
||||
converter.DataFormat = dataFormat.Value;
|
||||
}
|
||||
OtherPropertySet(converter, registerAddress);
|
||||
// 将解析结果添加到缓存中,缓存有效期为3600秒
|
||||
//MemoryCache.Set(cacheKey, converter!, 3600);
|
||||
return converter;
|
||||
}
|
||||
#region GetBytes
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
||||
@@ -10,10 +10,6 @@
|
||||
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
using System.Text;
|
||||
|
||||
using ThingsGateway.Foundation.Extension.String;
|
||||
|
||||
namespace ThingsGateway.Foundation;
|
||||
|
||||
/// <summary>
|
||||
@@ -23,126 +19,6 @@ public static class ThingsGatewayBitConverterExtension
|
||||
{
|
||||
|
||||
//private static MemoryCache MemoryCache = new() { Capacity = 10000000 };
|
||||
/// <summary>
|
||||
/// 从设备地址中解析附加信息
|
||||
/// 这个方法获取<see cref="IThingsGatewayBitConverter"/>
|
||||
/// 解析步骤将被缓存。
|
||||
/// </summary>
|
||||
/// <param name="registerAddress">设备地址</param>
|
||||
/// <param name="defaultBitConverter">默认的数据转换器</param>
|
||||
/// <returns><see cref="IThingsGatewayBitConverter"/> 实例</returns>
|
||||
public static IThingsGatewayBitConverter GetTransByAddress(this IThingsGatewayBitConverter defaultBitConverter, string? registerAddress)
|
||||
{
|
||||
if (registerAddress.IsNullOrEmpty()) return defaultBitConverter;
|
||||
|
||||
var type = defaultBitConverter.GetType();
|
||||
// 尝试从缓存中获取解析结果
|
||||
//var cacheKey = $"{nameof(ThingsGatewayBitConverterExtension)}_{nameof(GetTransByAddress)}_{type.FullName}_{type.TypeHandle.Value}_{defaultBitConverter.ToJsonString()}_{registerAddress}_{defaultBitConverter.GetHashCode()}";
|
||||
//if (MemoryCache.TryGetValue(cacheKey, out IThingsGatewayBitConverter cachedConverter))
|
||||
//{
|
||||
// if (cachedConverter.Equals(defaultBitConverter))
|
||||
// {
|
||||
// return defaultBitConverter;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// return (IThingsGatewayBitConverter)cachedConverter.Map(type);
|
||||
// }
|
||||
//}
|
||||
|
||||
// 去除设备地址两端的空格
|
||||
registerAddress = registerAddress.Trim();
|
||||
|
||||
// 根据分号拆分附加信息
|
||||
var strs = registerAddress.SplitStringBySemicolon();
|
||||
|
||||
DataFormatEnum? dataFormat = null;
|
||||
Encoding? encoding = null;
|
||||
bool? wstring = null;
|
||||
int? stringlength = null;
|
||||
BcdFormatEnum? bcdFormat = null;
|
||||
StringBuilder sb = new();
|
||||
foreach (var str in strs)
|
||||
{
|
||||
// 解析 dataFormat
|
||||
if (str.StartsWith("data=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var dataFormatName = str.Substring(5);
|
||||
try { if (Enum.TryParse<DataFormatEnum>(dataFormatName, true, out var dataFormat1)) dataFormat = dataFormat1; } catch { }
|
||||
}
|
||||
else if (str.StartsWith("vsl=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var wstringName = str.Substring(4);
|
||||
try { if (bool.TryParse(wstringName, out var wstring1)) wstring = wstring1; } catch { }
|
||||
}
|
||||
// 解析 encoding
|
||||
else if (str.StartsWith("encoding=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var encodingName = str.Substring(9);
|
||||
try { encoding = Encoding.GetEncoding(encodingName); } catch { }
|
||||
}
|
||||
// 解析 length
|
||||
else if (str.StartsWith("len=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var lenStr = str.Substring(4);
|
||||
stringlength = lenStr.IsNullOrEmpty() ? null : Convert.ToUInt16(lenStr);
|
||||
}
|
||||
// 解析 bcdFormat
|
||||
else if (str.StartsWith("bcd=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var bcdName = str.Substring(4);
|
||||
try { if (Enum.TryParse<BcdFormatEnum>(bcdName, true, out var bcdFormat1)) bcdFormat = bcdFormat1; } catch { }
|
||||
}
|
||||
|
||||
// 处理其他情况,将未识别的部分拼接回去
|
||||
else
|
||||
{
|
||||
if (sb.Length > 0)
|
||||
sb.Append($";{str}");
|
||||
else
|
||||
sb.Append($"{str}");
|
||||
}
|
||||
}
|
||||
|
||||
// 更新设备地址为去除附加信息后的地址
|
||||
registerAddress = sb.ToString();
|
||||
|
||||
// 如果没有解析出任何附加信息,则直接返回默认的数据转换器
|
||||
if (bcdFormat == null && stringlength == null && encoding == null && dataFormat == null && wstring == null)
|
||||
{
|
||||
//MemoryCache.Set(cacheKey, defaultBitConverter!, 3600);
|
||||
return defaultBitConverter;
|
||||
}
|
||||
|
||||
// 根据默认的数据转换器创建新的数据转换器实例
|
||||
var converter = (IThingsGatewayBitConverter)defaultBitConverter!.Map(type);
|
||||
|
||||
// 更新新的数据转换器实例的属性值
|
||||
if (encoding != null)
|
||||
{
|
||||
converter.Encoding = encoding;
|
||||
}
|
||||
if (bcdFormat != null)
|
||||
{
|
||||
converter.BcdFormat = bcdFormat.Value;
|
||||
}
|
||||
if (wstring != null)
|
||||
{
|
||||
converter.IsVariableStringLength = wstring.Value;
|
||||
}
|
||||
if (stringlength != null)
|
||||
{
|
||||
converter.StringLength = stringlength.Value;
|
||||
}
|
||||
if (dataFormat != null)
|
||||
{
|
||||
converter.DataFormat = dataFormat.Value;
|
||||
}
|
||||
|
||||
// 将解析结果添加到缓存中,缓存有效期为3600秒
|
||||
//MemoryCache.Set(cacheKey, converter!, 3600);
|
||||
return converter;
|
||||
}
|
||||
|
||||
#region 获取对应数据类型的数据
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ public class CronScheduledTask : DisposeBase, IScheduledTask
|
||||
private ILog LogMessage;
|
||||
private volatile int _isRunning = 0;
|
||||
private volatile int _pendingTriggers = 0;
|
||||
public Int32 Period => _timer?.Period ?? 0;
|
||||
|
||||
public CronScheduledTask(string interval, Func<object?, CancellationToken, Task> taskFunc, object? state, ILog log, CancellationToken token)
|
||||
{
|
||||
@@ -143,7 +144,14 @@ public class CronScheduledTask : DisposeBase, IScheduledTask
|
||||
if (!Check())
|
||||
_timer?.SetNext(interval);
|
||||
}
|
||||
public bool Change(int dueTime, int period)
|
||||
{
|
||||
// 延迟触发下一次
|
||||
if (!Check())
|
||||
return _timer?.Change(dueTime, period) == true;
|
||||
|
||||
return false;
|
||||
}
|
||||
public void Stop()
|
||||
{
|
||||
_timer?.SafeDispose();
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
{
|
||||
public interface IScheduledTask
|
||||
{
|
||||
bool Change(int dueTime, int period);
|
||||
void SetNext(int interval);
|
||||
void Start();
|
||||
void Stop();
|
||||
|
||||
public Int32 Period { get; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ public class ScheduledAsyncTask : DisposeBase, IScheduledTask, IScheduledIntInte
|
||||
private ILog LogMessage;
|
||||
private volatile int _isRunning = 0;
|
||||
private volatile int _pendingTriggers = 0;
|
||||
public Int32 Period => _timer?.Period??0;
|
||||
|
||||
public ScheduledAsyncTask(int interval, Func<object?, CancellationToken, Task> taskFunc, object? state, ILog log, CancellationToken token)
|
||||
{
|
||||
@@ -86,7 +87,14 @@ public class ScheduledAsyncTask : DisposeBase, IScheduledTask, IScheduledIntInte
|
||||
_timer?.SetNext(interval);
|
||||
}
|
||||
|
||||
public bool Change(int dueTime, int period)
|
||||
{
|
||||
// 延迟触发下一次
|
||||
if (!Check())
|
||||
return _timer?.Change(dueTime, period) == true;
|
||||
|
||||
return false;
|
||||
}
|
||||
public void Stop()
|
||||
{
|
||||
_timer?.SafeDispose();
|
||||
|
||||
@@ -16,6 +16,7 @@ public class ScheduledSyncTask : DisposeBase, IScheduledTask, IScheduledIntInter
|
||||
private ILog LogMessage;
|
||||
private volatile int _isRunning = 0;
|
||||
private volatile int _pendingTriggers = 0;
|
||||
public Int32 Period => _timer?.Period ?? 0;
|
||||
|
||||
public ScheduledSyncTask(int interval, Action<object?, CancellationToken> taskFunc, object? state, ILog log, CancellationToken token)
|
||||
{
|
||||
@@ -89,6 +90,14 @@ public class ScheduledSyncTask : DisposeBase, IScheduledTask, IScheduledIntInter
|
||||
if (!Check())
|
||||
_timer?.SetNext(interval);
|
||||
}
|
||||
public bool Change(int dueTime, int period)
|
||||
{
|
||||
// 延迟触发下一次
|
||||
if (!Check())
|
||||
return _timer?.Change(dueTime, period) == true;
|
||||
|
||||
return false;
|
||||
}
|
||||
public void Stop()
|
||||
{
|
||||
_timer?.SafeDispose();
|
||||
|
||||
@@ -128,6 +128,7 @@ public class Variable : BaseDataEntity, IValidatableObject
|
||||
[IgnoreExcel]
|
||||
[Required]
|
||||
[NotNull]
|
||||
[MinValue(1)]
|
||||
public virtual long DeviceId { get => deviceId; set => deviceId = value; }
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
using ThingsGateway.Gateway.Application.Extensions;
|
||||
using ThingsGateway.NewLife.Extension;
|
||||
|
||||
@@ -398,65 +401,64 @@ internal sealed class AlarmHostedService : BackgroundService, IAlarmHostedServic
|
||||
/// <param name="cancellation">取消任务的 CancellationToken</param>
|
||||
private void DoWork(object? state, CancellationToken cancellation)
|
||||
{
|
||||
while (!cancellation.IsCancellationRequested)
|
||||
|
||||
try
|
||||
{
|
||||
if (!GlobalData.StartBusinessChannelEnable)
|
||||
return;
|
||||
|
||||
try
|
||||
//Stopwatch stopwatch = Stopwatch.StartNew();
|
||||
// 遍历设备变量列表
|
||||
|
||||
if (!GlobalData.AlarmEnableIdVariables.IsEmpty)
|
||||
{
|
||||
var list = GlobalData.AlarmEnableIdVariables.Select(a => a.Value).ToArray();
|
||||
list.ParallelForEach((item, state, index) =>
|
||||
{
|
||||
if (!GlobalData.StartBusinessChannelEnable)
|
||||
return;
|
||||
|
||||
//Stopwatch stopwatch = Stopwatch.StartNew();
|
||||
// 遍历设备变量列表
|
||||
|
||||
if (!GlobalData.AlarmEnableIdVariables.IsEmpty)
|
||||
{
|
||||
var list = GlobalData.AlarmEnableIdVariables.Select(a => a.Value).ToArray();
|
||||
list.ParallelForEach((item, state, index) =>
|
||||
{
|
||||
{
|
||||
// 如果取消请求已经被触发,则结束任务
|
||||
if (cancellation.IsCancellationRequested)
|
||||
return;
|
||||
// 如果取消请求已经被触发,则结束任务
|
||||
if (cancellation.IsCancellationRequested)
|
||||
return;
|
||||
|
||||
// 如果该变量的报警功能未启用,则跳过该变量
|
||||
if (!item.AlarmEnable)
|
||||
return;
|
||||
// 如果该变量的报警功能未启用,则跳过该变量
|
||||
if (!item.AlarmEnable)
|
||||
return;
|
||||
|
||||
// 如果该变量离线,则跳过该变量
|
||||
if (!item.IsOnline)
|
||||
return;
|
||||
// 如果该变量离线,则跳过该变量
|
||||
if (!item.IsOnline)
|
||||
return;
|
||||
|
||||
// 对该变量进行报警分析
|
||||
AlarmAnalysis(item);
|
||||
// 对该变量进行报警分析
|
||||
AlarmAnalysis(item);
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
scheduledTask.SetNext(5000); // 如果没有启用报警的变量,则设置下次执行时间为5秒后
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
//if (scheduledTask.Period != 5000)
|
||||
// scheduledTask.Change(0, 5000); // 如果没有启用报警的变量,则设置下次执行时间为5秒后
|
||||
scheduledTask.SetNext(5000); // 如果没有启用报警的变量,则设置下次执行时间为5秒后
|
||||
}
|
||||
|
||||
//stopwatch.Stop();
|
||||
//_logger.LogInformation("报警分析耗时:" + stopwatch.ElapsedMilliseconds + "ms");
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Alarm analysis fail");
|
||||
}
|
||||
//stopwatch.Stop();
|
||||
//_logger.LogInformation("报警分析耗时:" + stopwatch.ElapsedMilliseconds + "ms");
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Alarm analysis fail");
|
||||
}
|
||||
}
|
||||
|
||||
private IScheduledTask scheduledTask;
|
||||
private ScheduledSyncTask scheduledTask;
|
||||
protected override Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
_logger.LogInformation(AppResource.RealAlarmTaskStart);
|
||||
scheduledTask = ScheduledTaskHelper.GetTask("10", DoWork, null, null, stoppingToken);
|
||||
scheduledTask = new ScheduledSyncTask(10, DoWork, null, null, stoppingToken);
|
||||
scheduledTask.Start();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ namespace ThingsGateway.Gateway.Application
|
||||
{
|
||||
Task<bool> BatchEditAsync(IEnumerable<Variable> models, Variable oldModel, Variable model, bool restart, CancellationToken cancellationToken);
|
||||
Task<bool> DeleteVariableAsync(IEnumerable<long> ids, bool restart, CancellationToken cancellationToken);
|
||||
Task<bool> ClearVariableAsync(bool restart, CancellationToken cancellationToken);
|
||||
Task<Dictionary<string, object>> ExportVariableAsync(ExportFilter exportFilter);
|
||||
|
||||
Task ImportVariableAsync(Dictionary<string, ImportPreviewOutputBase> input, bool restart, CancellationToken cancellationToken);
|
||||
|
||||
@@ -106,13 +106,14 @@ public class VariableRuntimeService : IVariableRuntimeService
|
||||
|
||||
var result = await GlobalData.VariableService.DeleteVariableAsync(variableIds).ConfigureAwait(false);
|
||||
|
||||
ConcurrentHashSet<IDriver> changedDriver = new();
|
||||
|
||||
RuntimeServiceHelper.AddBusinessChangedDriver(variableIds, changedDriver);
|
||||
RuntimeServiceHelper.VariableRuntimesDispose(variableIds);
|
||||
|
||||
if (restart)
|
||||
{
|
||||
|
||||
ConcurrentHashSet<IDriver> changedDriver = new();
|
||||
|
||||
RuntimeServiceHelper.AddBusinessChangedDriver(variableIds, changedDriver);
|
||||
RuntimeServiceHelper.VariableRuntimesDispose(variableIds);
|
||||
await RuntimeServiceHelper.ChangedDriverAsync(changedDriver, _logger, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -125,6 +126,38 @@ public class VariableRuntimeService : IVariableRuntimeService
|
||||
|
||||
|
||||
}
|
||||
|
||||
public async Task<bool> ClearVariableAsync(bool restart, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
// await WaitLock.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
|
||||
var result = await GlobalData.VariableService.DeleteVariableAsync(null).ConfigureAwait(false);
|
||||
|
||||
|
||||
|
||||
if (restart)
|
||||
{
|
||||
ConcurrentHashSet<IDriver> changedDriver = new();
|
||||
var variableIds = GlobalData.IdVariables.Select(a => a.Key).ToHashSet();
|
||||
RuntimeServiceHelper.AddBusinessChangedDriver(variableIds, changedDriver);
|
||||
RuntimeServiceHelper.VariableRuntimesDispose(variableIds);
|
||||
await RuntimeServiceHelper.ChangedDriverAsync(changedDriver, _logger, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
//WaitLock.Release();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public Task<Dictionary<string, object>> ExportVariableAsync(ExportFilter exportFilter) => GlobalData.VariableService.ExportVariableAsync(exportFilter);
|
||||
|
||||
public async Task ImportVariableAsync(Dictionary<string, ImportPreviewOutputBase> input, bool restart, CancellationToken cancellationToken)
|
||||
|
||||
@@ -335,8 +335,8 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
{
|
||||
using var db = GetDB();
|
||||
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
|
||||
var ids = input.ToList();
|
||||
var result = (await db.Deleteable<Variable>().Where(a => ids.Contains(a.Id))
|
||||
var ids = input?.ToList();
|
||||
var result = (await db.Deleteable<Variable>().WhereIF(input != null, a => ids.Contains(a.Id))
|
||||
.WhereIF(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.CreateOrgId))//在指定机构列表查询
|
||||
.WhereIF(dataScope?.Count == 0, u => u.CreateUserId == UserManager.UserId)
|
||||
.ExecuteCommandAsync().ConfigureAwait(false)) > 0;
|
||||
|
||||
@@ -424,7 +424,7 @@ finally
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
|
||||
await GlobalData.VariableRuntimeService.DeleteVariableAsync(Items.Select(a => a.Id), AutoRestartThread, default);
|
||||
await GlobalData.VariableRuntimeService.ClearVariableAsync(AutoRestartThread, default);
|
||||
await InvokeAsync(async () =>
|
||||
{
|
||||
await ToastService.Default();
|
||||
|
||||
@@ -24,7 +24,7 @@ public class Dlt645_2007Master : DtuServiceDeviceBase
|
||||
RegisterByteLength = 2;
|
||||
channel.MaxSign = ushort.MaxValue;
|
||||
}
|
||||
public override IThingsGatewayBitConverter ThingsGatewayBitConverter { get; protected set; } = new Dlt645_2007BitConverter(EndianType.Big) { };
|
||||
public override IThingsGatewayBitConverter ThingsGatewayBitConverter { get; } = new Dlt645_2007BitConverter(EndianType.Big) { };
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string FEHead { get; set; } = "FEFEFEFE";
|
||||
|
||||
@@ -30,7 +30,7 @@ public partial class ModbusMaster : DtuServiceDeviceBase, IModbusAddress
|
||||
}
|
||||
}
|
||||
|
||||
public override IThingsGatewayBitConverter ThingsGatewayBitConverter { get; protected set; } = new ThingsGatewayBitConverter(EndianType.Big) { };
|
||||
public override IThingsGatewayBitConverter ThingsGatewayBitConverter { get; } = new ThingsGatewayBitConverter(EndianType.Big) { };
|
||||
|
||||
/// <summary>
|
||||
/// Modbus类型,在initChannelAsync之前设置
|
||||
|
||||
@@ -51,7 +51,7 @@ public class ModbusSlave : DeviceBase, IModbusAddress
|
||||
RegisterByteLength = 2;
|
||||
channel.MaxSign = ushort.MaxValue;
|
||||
}
|
||||
public override IThingsGatewayBitConverter ThingsGatewayBitConverter { get; protected set; } = new ThingsGatewayBitConverter(EndianType.Big) { };
|
||||
public override IThingsGatewayBitConverter ThingsGatewayBitConverter { get; } = new ThingsGatewayBitConverter(EndianType.Big) { };
|
||||
|
||||
public override bool SupportMultipleDevice()
|
||||
{
|
||||
|
||||
@@ -1099,7 +1099,7 @@ public class OpcUaMaster : IDisposable
|
||||
}
|
||||
|
||||
|
||||
private static List<VariableNode> GetVariableNodes(ReadValueIdCollection itemsToRead, DataValueCollection values, DiagnosticInfoCollection diagnosticInfos, ResponseHeader responseHeader, int count = 1)
|
||||
private List<VariableNode> GetVariableNodes(ReadValueIdCollection itemsToRead, DataValueCollection values, DiagnosticInfoCollection diagnosticInfos, ResponseHeader responseHeader, int count = 1)
|
||||
{
|
||||
ClientBase.ValidateResponse(values, itemsToRead);
|
||||
ClientBase.ValidateDiagnosticInfos(diagnosticInfos, itemsToRead);
|
||||
@@ -1130,11 +1130,11 @@ public class OpcUaMaster : IDisposable
|
||||
// NodeId Attribute
|
||||
if (!DataValue.IsGood(value))
|
||||
{
|
||||
throw ServiceResultException.Create(value.StatusCode, 0 + 2 * i, diagnosticInfos, responseHeader.StringTable);
|
||||
Log(3, ServiceResultException.Create(value.StatusCode, 0 + 2 * i, diagnosticInfos, responseHeader.StringTable), $"Get nodeid {itemsToRead[0 + 2 * i].NodeId} fail");
|
||||
}
|
||||
if (value == null)
|
||||
{
|
||||
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "Node does not support the NodeId attribute.");
|
||||
Log(3, ServiceResultException.Create(StatusCodes.BadUnexpectedError, "Node does not support the NodeId attribute."), $"Get nodeid {itemsToRead[0 + 2 * i].NodeId} fail");
|
||||
}
|
||||
|
||||
variableNode.NodeId = (NodeId)value.GetValue(typeof(NodeId));
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
"AddressStart": "AddressStart",
|
||||
"BitCode": "BitBitCode",
|
||||
"DataCode": "DataCode",
|
||||
"DbBlock": "DbBlock"
|
||||
"DbBlock": "DbBlock",
|
||||
"WStringEnable": "WString"
|
||||
},
|
||||
"ThingsGateway.Foundation.SiemensS7.SiemensS7Master": {
|
||||
"LocalTSAP": "LocalTSAP",
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
"AddressStart": "起始地址",
|
||||
"BitCode": "Bit地址",
|
||||
"DataCode": "寄存器区",
|
||||
"DbBlock": "DB块"
|
||||
"DbBlock": "DB块",
|
||||
"WStringEnable": "WString"
|
||||
},
|
||||
"ThingsGateway.Foundation.SiemensS7.SiemensS7Master": {
|
||||
"LocalTSAP": "本地TSAP",
|
||||
|
||||
@@ -24,18 +24,44 @@ public class S7BitConverter : ThingsGatewayBitConverter
|
||||
public S7BitConverter(EndianType endianType) : base(endianType)
|
||||
{
|
||||
}
|
||||
|
||||
public bool SMART200 { get; set; } = false;
|
||||
public bool WStringEnable { get; set; } = false;
|
||||
public override int? StringLength { get; set; } = 100;
|
||||
/// <inheritdoc/>
|
||||
public override string ToString(byte[] buffer, int offset, int length)
|
||||
{
|
||||
if (!IsVariableStringLength)
|
||||
if (WStringEnable)
|
||||
{
|
||||
return base.ToString(buffer, offset, length);
|
||||
if (!SMART200)
|
||||
return base.ToString(buffer, offset, this.ToUInt16(buffer, offset - 2) * 2);
|
||||
else
|
||||
return base.ToString(buffer, offset, buffer[offset - 1] * 2);
|
||||
}
|
||||
else if (IsVariableStringLength)
|
||||
{
|
||||
if (!SMART200)
|
||||
return base.ToString(buffer, offset, buffer[offset - 1]);
|
||||
else
|
||||
return base.ToString(buffer, offset, buffer[offset - 1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
return base.ToString(buffer, offset, buffer[offset - 1]);
|
||||
return base.ToString(buffer, offset, length);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override void OtherPropertySet(IThingsGatewayBitConverter thingsGatewayBitConverter, string registerAddress)
|
||||
{
|
||||
if (thingsGatewayBitConverter is not S7BitConverter s7BitConverter)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var sAddress = SiemensS7Address.ParseFrom(registerAddress);
|
||||
s7BitConverter.WStringEnable = sAddress.WStringEnable;
|
||||
|
||||
base.OtherPropertySet(thingsGatewayBitConverter, registerAddress);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ namespace ThingsGateway.Foundation.SiemensS7;
|
||||
/// </summary>
|
||||
public class SiemensS7Address : S7Request
|
||||
{
|
||||
|
||||
public bool WStringEnable { get; set; }
|
||||
public SiemensS7Address()
|
||||
{
|
||||
}
|
||||
@@ -42,6 +44,7 @@ public class SiemensS7Address : S7Request
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder stringBuilder = Pool.StringBuilder.Get();
|
||||
stringBuilder.Append($"W={WStringEnable};");
|
||||
if (DataCode == S7Area.TM)
|
||||
{
|
||||
stringBuilder.Append($"T{AddressStart}");
|
||||
@@ -273,6 +276,10 @@ public class SiemensS7Address : S7Request
|
||||
throw new Exception(string.Format(AppResource.AddressError, address));
|
||||
}
|
||||
}
|
||||
else if (strArr[index].StartsWith("W=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
s7AddressData.WStringEnable = strArr[index].Substring(2).ToBoolean(false);
|
||||
}
|
||||
}
|
||||
|
||||
//if (isCache)
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System.Text;
|
||||
|
||||
namespace ThingsGateway.Foundation.SiemensS7;
|
||||
|
||||
internal static class PackHelper
|
||||
@@ -49,6 +51,13 @@ internal static class PackHelper
|
||||
{
|
||||
// 解析SiemensS7Address对象
|
||||
var s7Address = SiemensS7Address.ParseFrom(it.RegisterAddress);
|
||||
// 根据地址获取转换参数
|
||||
if (it.ThingsGatewayBitConverter is S7BitConverter s7BitConverter)
|
||||
{
|
||||
s7BitConverter.WStringEnable = s7Address.WStringEnable;
|
||||
}
|
||||
|
||||
|
||||
int lastLen = it.DataType.GetByteLength();
|
||||
|
||||
// 处理特殊情况下的长度
|
||||
@@ -76,10 +85,23 @@ internal static class PackHelper
|
||||
{
|
||||
lastLen = it.ThingsGatewayBitConverter.StringLength.Value;
|
||||
}
|
||||
|
||||
if (s7Address.WStringEnable)
|
||||
{
|
||||
it.ThingsGatewayBitConverter.Encoding = Encoding.Unicode;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (it.ThingsGatewayBitConverter.IsVariableStringLength)
|
||||
if (s7Address.WStringEnable)
|
||||
{
|
||||
// 字符串在S7中,前四个字节不属于实际内容
|
||||
it.Index += 4;
|
||||
lastLen = it.ThingsGatewayBitConverter.StringLength.Value + 4;
|
||||
it.ThingsGatewayBitConverter.Encoding = Encoding.BigEndianUnicode;
|
||||
|
||||
}
|
||||
else if (it.ThingsGatewayBitConverter.IsVariableStringLength)
|
||||
{
|
||||
// 字符串在S7中,前两个字节不属于实际内容
|
||||
it.Index += 2;
|
||||
|
||||
@@ -106,7 +106,7 @@ internal sealed partial class SiemensHelper
|
||||
}
|
||||
}
|
||||
|
||||
internal static async ValueTask<OperResult> WriteAsync(SiemensS7Master plc, string address, string value, Encoding encoding, CancellationToken cancellationToken = default)
|
||||
internal static async ValueTask<OperResult> WriteStringAsync(SiemensS7Master plc, string address, string value, Encoding encoding, CancellationToken cancellationToken = default)
|
||||
{
|
||||
value ??= string.Empty;
|
||||
byte[] inBytes = encoding.GetBytes(value);
|
||||
@@ -125,4 +125,71 @@ internal sealed partial class SiemensHelper
|
||||
}
|
||||
return await plc.WriteAsync(address, DataTransUtil.SpliceArray([(byte)value.Length], inBytes), DataTypeEnum.String, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
internal static async ValueTask<OperResult<string>> ReadWStringAsync(SiemensS7Master plc, string address, CancellationToken cancellationToken)
|
||||
{
|
||||
//先读取一次获取长度,再读取实际值
|
||||
if (plc.SiemensS7Type != SiemensTypeEnum.S200Smart)
|
||||
{
|
||||
var encoding = Encoding.BigEndianUnicode;
|
||||
var result1 = await plc.ReadAsync(address, 4, cancellationToken).ConfigureAwait(false);
|
||||
if (!result1.IsSuccess)
|
||||
{
|
||||
return new OperResult<string>(result1);
|
||||
}
|
||||
if (result1.Content[0] == 0 || result1.Content[0] == byte.MaxValue)
|
||||
{
|
||||
return new OperResult<string>(AppResource.NotString);
|
||||
}
|
||||
var result2 = await plc.ReadAsync(address, 4 + (plc.ThingsGatewayBitConverter.ToUInt16(result1.Content, 2) * 2), cancellationToken).ConfigureAwait(false);
|
||||
if (!result2.IsSuccess)
|
||||
{
|
||||
return new OperResult<string>(result2);
|
||||
}
|
||||
else
|
||||
{
|
||||
return OperResult.CreateSuccessResult(encoding.GetString(result2.Content, 4, result2.Content.Length - 4));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var encoding = Encoding.Unicode;
|
||||
var result1 = await plc.ReadAsync(address, 1, cancellationToken).ConfigureAwait(false);
|
||||
if (!result1.IsSuccess)
|
||||
return new OperResult<string>(result1);
|
||||
var result2 = await plc.ReadAsync(address, 1 + (result1.Content[0] * 2), cancellationToken).ConfigureAwait(false);
|
||||
if (!result2.IsSuccess)
|
||||
{
|
||||
return new OperResult<string>(result2);
|
||||
}
|
||||
else
|
||||
{
|
||||
return OperResult.CreateSuccessResult(encoding.GetString(result2.Content, 1, result2.Content.Length - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static async ValueTask<OperResult> WriteWStringAsync(SiemensS7Master plc, string address, string value, CancellationToken cancellationToken = default)
|
||||
{
|
||||
value ??= string.Empty;
|
||||
if (plc.SiemensS7Type != SiemensTypeEnum.S200Smart)
|
||||
{
|
||||
byte[] inBytes1 = Encoding.BigEndianUnicode.GetBytes(value);
|
||||
var result = await plc.ReadAsync(address, 4, cancellationToken).ConfigureAwait(false);
|
||||
if (!result.IsSuccess) return result;
|
||||
var num = plc.ThingsGatewayBitConverter.ToUInt16(result.Content, 0);
|
||||
if (num == 0)
|
||||
num = 254;
|
||||
if (value.Length > num) return new OperResult<string>(AppResource.WriteDataLengthMore);
|
||||
return await plc.WriteAsync(
|
||||
address,
|
||||
DataTransUtil.SpliceArray(plc.ThingsGatewayBitConverter.GetBytes(num), plc.ThingsGatewayBitConverter.GetBytes((ushort)value.Length),
|
||||
inBytes1
|
||||
), DataTypeEnum.String, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
byte[] inBytes2 = Encoding.Unicode.GetBytes(value);
|
||||
return await plc.WriteAsync(address, DataTransUtil.SpliceArray([(byte)value.Length], inBytes2), DataTypeEnum.String, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,8 +32,8 @@ public partial class SiemensS7Master : DeviceBase
|
||||
{
|
||||
}
|
||||
|
||||
public override IThingsGatewayBitConverter ThingsGatewayBitConverter { get; protected set; } = new S7BitConverter(EndianType.Big) { };
|
||||
|
||||
public override IThingsGatewayBitConverter ThingsGatewayBitConverter => s7BitConverter;
|
||||
private S7BitConverter s7BitConverter = new S7BitConverter(EndianType.Big) { };
|
||||
/// <summary>
|
||||
/// PduLength
|
||||
/// </summary>
|
||||
@@ -54,7 +54,14 @@ public partial class SiemensS7Master : DeviceBase
|
||||
/// <summary>
|
||||
/// S7类型
|
||||
/// </summary>
|
||||
public SiemensTypeEnum SiemensS7Type { get; set; }
|
||||
public SiemensTypeEnum SiemensS7Type
|
||||
{
|
||||
get => siemensS7Type; set
|
||||
{
|
||||
siemensS7Type = value;
|
||||
s7BitConverter.SMART200 = value == SiemensTypeEnum.S200Smart;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 槽号,需重新连接
|
||||
@@ -371,6 +378,8 @@ public partial class SiemensS7Master : DeviceBase
|
||||
|
||||
#region 初始握手
|
||||
private WaitLock ChannelStartedWaitLock = new();
|
||||
private SiemensTypeEnum siemensS7Type;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async ValueTask<bool> ChannelStarted(IClientChannel channel, bool last)
|
||||
{
|
||||
@@ -553,7 +562,24 @@ public partial class SiemensS7Master : DeviceBase
|
||||
public override async ValueTask<OperResult<string[]>> ReadStringAsync(string address, int length, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
bitConverter ??= ThingsGatewayBitConverter.GetTransByAddress(address);
|
||||
if (bitConverter.IsVariableStringLength)
|
||||
|
||||
if (((S7BitConverter)bitConverter)?.WStringEnable == true)
|
||||
{
|
||||
if (length > 1)
|
||||
{
|
||||
return new OperResult<string[]>(AppResource.StringLengthReadError);
|
||||
}
|
||||
var result = await SiemensHelper.ReadWStringAsync(this, address, cancellationToken).ConfigureAwait(false);
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
return OperResult.CreateSuccessResult(new string[] { result.Content });
|
||||
}
|
||||
else
|
||||
{
|
||||
return new OperResult<string[]>(result);
|
||||
}
|
||||
}
|
||||
else if (bitConverter.IsVariableStringLength)
|
||||
{
|
||||
if (length > 1)
|
||||
{
|
||||
@@ -579,9 +605,14 @@ public partial class SiemensS7Master : DeviceBase
|
||||
public override ValueTask<OperResult> WriteAsync(string address, string value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
bitConverter ??= ThingsGatewayBitConverter.GetTransByAddress(address);
|
||||
|
||||
if (((S7BitConverter)bitConverter)?.WStringEnable == true)
|
||||
{
|
||||
return SiemensHelper.WriteWStringAsync(this, address, value, cancellationToken);
|
||||
}
|
||||
if (bitConverter.IsVariableStringLength)
|
||||
{
|
||||
return SiemensHelper.WriteAsync(this, address, value, bitConverter.Encoding, cancellationToken);
|
||||
return SiemensHelper.WriteStringAsync(this, address, value, bitConverter.Encoding, cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -95,11 +95,11 @@
|
||||
<div class="col-12 col-md-12 min-height-500">
|
||||
<BootstrapLabel Value=@Localizer["BigTextScriptRpc"] ShowLabelTooltip="true" />
|
||||
<CodeEditor ShowLineNo @bind-Value=@clientProperty.BigTextScriptRpc Language="csharp" Theme="vs-dark" IsReadonly=@(!CanWrite) />
|
||||
<div class="ms-2 d-flex justify-content-center align-items-center">
|
||||
@* <div class="ms-2 d-flex justify-content-center align-items-center">
|
||||
<Button IsDisabled=@(!CanWrite) OnClick=@(() => PropertyComponent.CheckScript(clientProperty, nameof(clientProperty.BigTextScriptRpc), Localizer["check"], this, DialogService))>
|
||||
@Localizer["Check"]
|
||||
</Button>
|
||||
</div>
|
||||
</div> *@
|
||||
</div>
|
||||
|
||||
</EditTemplate>
|
||||
|
||||
@@ -14,8 +14,6 @@ using BootstrapBlazor.Components;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
using ThingsGateway.Gateway.Razor;
|
||||
using ThingsGateway.NewLife.Json.Extension;
|
||||
using ThingsGateway.Razor;
|
||||
|
||||
namespace ThingsGateway.Plugin.Mqtt
|
||||
|
||||
@@ -165,10 +165,11 @@ public class OpcUaMaster : CollectBase
|
||||
//如果是订阅模式,连接时添加订阅组
|
||||
if (_plc.OpcUaProperty?.ActiveSubscribe == true && CurrentDevice.VariableSourceReads.Count > 0 && _plc.Session.SubscriptionCount < CurrentDevice.VariableSourceReads.Count)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
foreach (var variableSourceRead in CurrentDevice.VariableSourceReads)
|
||||
|
||||
foreach (var variableSourceRead in CurrentDevice.VariableSourceReads)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_plc.Session.Subscriptions.FirstOrDefault(a => a.DisplayName == variableSourceRead.RegisterAddress) == null)
|
||||
{
|
||||
@@ -180,19 +181,22 @@ public class OpcUaMaster : CollectBase
|
||||
|
||||
await Task.Delay(100, cancellationToken).ConfigureAwait(false); // allow for subscription to be finished on server?
|
||||
|
||||
checkLog = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (!checkLog)
|
||||
LogMessage?.LogWarning(ex, "AddSubscriptions error");
|
||||
checkLog = false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
}
|
||||
|
||||
LogMessage?.LogInformation("AddSubscriptions done");
|
||||
checkLog = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (!checkLog)
|
||||
LogMessage?.LogWarning(ex, "AddSubscriptions error");
|
||||
checkLog = false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,8 @@ public class ThingsGatewayNodeManager : CustomNodeManager2
|
||||
{
|
||||
if (rootFolder == null) return;
|
||||
|
||||
NodeIdTags?.Clear();
|
||||
RemoveRootNotifier(rootFolder);
|
||||
rootFolder?.SafeDispose();
|
||||
rootFolder = null;
|
||||
rootFolder = CreateFolder(null, "ThingsGateway", "ThingsGateway");
|
||||
@@ -100,7 +102,6 @@ public class ThingsGatewayNodeManager : CustomNodeManager2
|
||||
}
|
||||
}
|
||||
AddPredefinedNode(SystemContext, rootFolder);
|
||||
|
||||
rootFolder.ClearChangeMasks(SystemContext, true);
|
||||
|
||||
}
|
||||
@@ -526,7 +527,7 @@ public class ThingsGatewayNodeManager : CustomNodeManager2
|
||||
a.ToDictionary(a => a.Item1.Name, a => a.Item2)
|
||||
);
|
||||
var result = GlobalData.RpcService.InvokeDeviceMethodAsync("OpcUaServer - " + context?.Session?.Identity?.DisplayName, writeDatas
|
||||
).GetAwaiter().GetResult(); ;
|
||||
).GetAwaiter().GetResult();
|
||||
|
||||
for (int ii = 0; ii < nodesToWrite.Count; ii++)
|
||||
{
|
||||
|
||||
@@ -44,19 +44,6 @@ public partial class ThingsGatewayServer : StandardServer
|
||||
}
|
||||
|
||||
|
||||
//public void AfterVariablesChanged()
|
||||
//{
|
||||
// if(masterNodeManager!=null)
|
||||
// {
|
||||
// masterNodeManager?.Dispose();
|
||||
// masterNodeManager = CreateMasterNodeManager((ServerInternalData)NodeManager.Server, _opcUaServer.m_configuration);
|
||||
// ((ServerInternalData)NodeManager.Server).SetNodeManager(masterNodeManager);
|
||||
// // put the node manager into a state that allows it to be used by other objects.
|
||||
// masterNodeManager.Startup();
|
||||
// }
|
||||
|
||||
//}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override MasterNodeManager CreateMasterNodeManager(IServerInternal server, ApplicationConfiguration configuration)
|
||||
{
|
||||
|
||||
@@ -78,14 +78,14 @@ public partial class OpcUaServer : BusinessBase
|
||||
|
||||
CollectVariableRuntimes.Clear();
|
||||
|
||||
IdVariableRuntimes.ForEach(a =>
|
||||
{
|
||||
VariableValueChange(a.Value, a.Value.AdaptVariableBasicData());
|
||||
});
|
||||
|
||||
|
||||
m_server?.NodeManager?.RefreshVariable();
|
||||
//IdVariableRuntimes.ForEach(a =>
|
||||
//{
|
||||
// VariableValueChange(a.Value, a.Value.AdaptVariableBasicData());
|
||||
//});
|
||||
|
||||
//动态更新UA库节点暂时取消
|
||||
//m_server?.NodeManager?.RefreshVariable();
|
||||
m_server?.Stop();
|
||||
}
|
||||
|
||||
|
||||
@@ -112,6 +112,7 @@ public partial class OpcUaServer : BusinessBase
|
||||
GlobalData.VariableValueChangeEvent += VariableValueChange;
|
||||
|
||||
Localizer = App.CreateLocalizerByType(typeof(OpcUaServer))!;
|
||||
|
||||
await base.InitChannelAsync(channel, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<EditorItem @bind-Field=context.BitCode />
|
||||
<EditorItem @bind-Field=context.DataCode />
|
||||
<EditorItem @bind-Field=context.DbBlock />
|
||||
<EditorItem @bind-Field=context.WStringEnable />
|
||||
</FieldItems>
|
||||
</EditorFormObject>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<Version>10.9.9</Version>
|
||||
<Version>10.9.13</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
Reference in New Issue
Block a user