mirror of
https://gitee.com/ThingsGateway/ThingsGateway.git
synced 2025-10-20 18:51:28 +08:00
适配远程管理客户端
This commit is contained in:
@@ -39,7 +39,7 @@
|
||||
<BlazorReconnector @rendermode="new InteractiveServerRenderMode(false)" />
|
||||
|
||||
<script src=@($"_content/BootstrapBlazor/js/bootstrap.blazor.bundle.min.js?v={this.GetType().Assembly.GetName().Version}")></script>
|
||||
<script src=@($"{WebsiteConst.DefaultResourceUrl}js/culture.js?v={this.GetType().Assembly.GetName().Version}")></script>
|
||||
<script src=@($"{WebsiteConst.DefaultResourceUrl}js/localStorageUtil.js?v={this.GetType().Assembly.GetName().Version}")></script>
|
||||
<script src="_framework/blazor.web.js"></script>
|
||||
<!-- PWA Service Worker -->
|
||||
<script type="text/javascript">'serviceWorker' in navigator && navigator.serviceWorker.register('./service-worker.js')</script>
|
||||
|
@@ -45,7 +45,7 @@
|
||||
</app>
|
||||
|
||||
<script src="_content/BootstrapBlazor/js/bootstrap.blazor.bundle.min.js"></script>
|
||||
<script src=@($"{WebsiteConst.DefaultResourceUrl}js/culture.js")></script>
|
||||
<script src=@($"{WebsiteConst.DefaultResourceUrl}js/localStorageUtil.js")></script>
|
||||
<script src="_framework/blazor.server.js"></script>
|
||||
|
||||
<!-- PWA Service Worker -->
|
||||
|
@@ -8,8 +8,8 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using ThingsGateway.Common.Extension;
|
||||
using ThingsGateway.NewLife;
|
||||
using ThingsGateway.Razor.Extension;
|
||||
|
||||
namespace ThingsGateway.Razor;
|
||||
|
||||
|
@@ -10,7 +10,7 @@
|
||||
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace ThingsGateway.Common.Extension;
|
||||
namespace ThingsGateway.Razor.Extension;
|
||||
|
||||
/// <summary>
|
||||
/// JSRuntime扩展方法
|
||||
@@ -49,4 +49,28 @@ public static class JSRuntimeExtensions
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static async ValueTask<T> GetLocalStorage<T>(this IJSRuntime jsRuntime, string name)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await jsRuntime.InvokeAsync<T>("getLocalStorage", name).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
public static async ValueTask SetLocalStorage<T>(this IJSRuntime jsRuntime, string name, T data)
|
||||
{
|
||||
try
|
||||
{
|
||||
await jsRuntime.InvokeVoidAsync("setLocalStorage", name, data).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,9 +0,0 @@
|
||||
// 设置 culture
|
||||
function setCultureLocalStorage(culture) {
|
||||
localStorage.setItem("culture", culture);
|
||||
}
|
||||
|
||||
// 获取 culture
|
||||
function getCultureLocalStorage() {
|
||||
return localStorage.getItem("culture");
|
||||
}
|
18
src/Admin/ThingsGateway.Razor/wwwroot/js/localStorageUtil.js
Normal file
18
src/Admin/ThingsGateway.Razor/wwwroot/js/localStorageUtil.js
Normal file
@@ -0,0 +1,18 @@
|
||||
// 设置 culture
|
||||
function setCultureLocalStorage(culture) {
|
||||
localStorage.setItem("culture", culture);
|
||||
}
|
||||
|
||||
// 获取 culture
|
||||
function getCultureLocalStorage() {
|
||||
return localStorage.getItem("culture");
|
||||
}
|
||||
|
||||
function getLocalStorage(name) {
|
||||
return JSON.parse(localStorage.getItem(name)) ?? 0;
|
||||
}
|
||||
function setLocalStorage(name, data) {
|
||||
if (localStorage) {
|
||||
localStorage.setItem(name, JSON.stringify(data));
|
||||
}
|
||||
}
|
@@ -1,9 +1,9 @@
|
||||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<PluginVersion>10.10.11</PluginVersion>
|
||||
<ProPluginVersion>10.10.11</ProPluginVersion>
|
||||
<DefaultVersion>10.10.14</DefaultVersion>
|
||||
<PluginVersion>10.10.12</PluginVersion>
|
||||
<ProPluginVersion>10.10.12</ProPluginVersion>
|
||||
<DefaultVersion>10.10.15</DefaultVersion>
|
||||
<AuthenticationVersion>10.10.1</AuthenticationVersion>
|
||||
<SourceGeneratorVersion>10.10.1</SourceGeneratorVersion>
|
||||
<NET8Version>8.0.19</NET8Version>
|
||||
|
@@ -33,6 +33,8 @@ public class AsyncReadWriteLock
|
||||
{
|
||||
Interlocked.Increment(ref _readerCount);
|
||||
|
||||
|
||||
|
||||
// 第一个读者需要获取写入锁,防止写操作
|
||||
await _readerLock.WaitOneAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
@@ -63,11 +65,16 @@ public class AsyncReadWriteLock
|
||||
private object lockObject = new();
|
||||
private void ReleaseWriter()
|
||||
{
|
||||
|
||||
var writerCount = Interlocked.Decrement(ref _writerCount);
|
||||
|
||||
// 每次释放写时,总是唤醒至少一个读
|
||||
_readerLock.Set();
|
||||
|
||||
if (writerCount == 0)
|
||||
{
|
||||
var resetEvent = _readerLock;
|
||||
_readerLock = new(false);
|
||||
//_readerLock = new(false);
|
||||
Interlocked.Exchange(ref _writeSinceLastReadCount, 0);
|
||||
resetEvent.SetAll();
|
||||
}
|
||||
@@ -83,12 +90,12 @@ public class AsyncReadWriteLock
|
||||
if (count >= _writeReadRatio)
|
||||
{
|
||||
Interlocked.Exchange(ref _writeSinceLastReadCount, 0);
|
||||
_readerLock.Set();
|
||||
//_readerLock.Set();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_readerLock.Set();
|
||||
//_readerLock.Set();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -177,6 +177,15 @@ public static class GlobalData
|
||||
}
|
||||
return GlobalData.ChannelThreadManage.DeviceThreadManages.TryGetValue(deviceRuntime.ChannelId, out deviceThreadManage);
|
||||
}
|
||||
|
||||
public static IChannelThreadManage GetChannelThreadManage(ChannelRuntime channelRuntime)
|
||||
{
|
||||
if (channelRuntime.DeviceThreadManage?.ChannelThreadManage != null)
|
||||
return channelRuntime.DeviceThreadManage.ChannelThreadManage;
|
||||
else
|
||||
return GlobalData.ChannelThreadManage;
|
||||
}
|
||||
|
||||
public static Dictionary<IDeviceThreadManage, List<DeviceRuntime>> GetDeviceThreadManages(IEnumerable<DeviceRuntime> deviceRuntimes)
|
||||
{
|
||||
Dictionary<IDeviceThreadManage, List<DeviceRuntime>> deviceThreadManages = new();
|
||||
|
@@ -83,5 +83,8 @@ public partial class AlarmRuntimePropertys
|
||||
public DateTime EventTime { get; set; } = DateTime.UnixEpoch.ToLocalTime();
|
||||
|
||||
internal object AlarmLockObject = new();
|
||||
|
||||
#if !Management
|
||||
internal bool AlarmConfirm;
|
||||
#endif
|
||||
}
|
||||
|
@@ -23,27 +23,15 @@ namespace ThingsGateway.Gateway.Application;
|
||||
/// <summary>
|
||||
/// 业务设备运行状态
|
||||
/// </summary>
|
||||
public class ChannelRuntime : Channel, IChannelOptions, IDisposable
|
||||
public class ChannelRuntime : Channel
|
||||
#if !Management
|
||||
,
|
||||
IChannelOptions,
|
||||
IDisposable
|
||||
#endif
|
||||
{
|
||||
/// <summary>
|
||||
/// 插件信息
|
||||
/// </summary>
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
[MapperIgnore]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public PluginInfo? PluginInfo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否采集
|
||||
/// </summary>
|
||||
public PluginTypeEnum? PluginType => PluginInfo?.PluginType;
|
||||
|
||||
/// <summary>
|
||||
/// 是否采集
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public bool? IsCollect => PluginInfo == null ? null : PluginInfo?.PluginType == PluginTypeEnum.Collect;
|
||||
#if !Management
|
||||
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
@@ -105,14 +93,67 @@ public class ChannelRuntime : Channel, IChannelOptions, IDisposable
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
public int? DeviceRuntimeCount => DeviceRuntimes?.Count;
|
||||
|
||||
public bool Started => DeviceThreadManage != null;
|
||||
#else
|
||||
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
[MinValue(1)]
|
||||
public override int MaxConcurrentCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 设备数量
|
||||
/// </summary>
|
||||
public int? DeviceRuntimeCount { get; set; }
|
||||
|
||||
public bool Started { get; set; }
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 插件信息
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public PluginInfo? PluginInfo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否采集
|
||||
/// </summary>
|
||||
public PluginTypeEnum? PluginType => PluginInfo?.PluginType;
|
||||
|
||||
/// <summary>
|
||||
/// 是否采集
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public bool? IsCollect => PluginInfo == null ? null : PluginInfo?.PluginType == PluginTypeEnum.Collect;
|
||||
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (ChannelType == ChannelTypeEnum.Other)
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
return $"{Name}[{base.ToString()}]";
|
||||
}
|
||||
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public string LogPath => Name.GetChannelLogPath();
|
||||
|
||||
|
||||
#if !Management
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
[MapperIgnore]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public IDeviceThreadManage? DeviceThreadManage { get; internal set; }
|
||||
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public string LogPath => Name.GetChannelLogPath();
|
||||
|
||||
|
||||
|
||||
|
||||
public void Init()
|
||||
{
|
||||
@@ -135,14 +176,8 @@ public class ChannelRuntime : Channel, IChannelOptions, IDisposable
|
||||
DeviceThreadManage = null;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
public override string ToString()
|
||||
{
|
||||
if (ChannelType == ChannelTypeEnum.Other)
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
return $"{Name}[{base.ToString()}]";
|
||||
}
|
||||
|
||||
|
||||
|
||||
public IChannel GetChannel(TouchSocketConfig config)
|
||||
{
|
||||
@@ -192,4 +227,7 @@ public class ChannelRuntime : Channel, IChannelOptions, IDisposable
|
||||
return ichannel;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
}
|
||||
|
@@ -23,12 +23,17 @@ namespace ThingsGateway.Gateway.Application;
|
||||
/// <summary>
|
||||
/// 业务设备运行状态
|
||||
/// </summary>
|
||||
public class DeviceRuntime : Device, IDisposable
|
||||
public class DeviceRuntime : Device
|
||||
#if !Management
|
||||
, IDisposable
|
||||
#endif
|
||||
{
|
||||
protected volatile DeviceStatusEnum _deviceStatus = DeviceStatusEnum.Default;
|
||||
|
||||
private string? _lastErrorMessage;
|
||||
|
||||
private readonly object _lockObject = new object();
|
||||
|
||||
/// <summary>
|
||||
/// 设备活跃时间
|
||||
/// </summary>
|
||||
@@ -67,11 +72,15 @@ public class DeviceRuntime : Device, IDisposable
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public string LogPath => Name.GetDeviceLogPath();
|
||||
|
||||
|
||||
#if !Management
|
||||
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
[MapperIgnore]
|
||||
public DateTime DeviceStatusChangeTime = DateTime.UnixEpoch.ToLocalTime();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 设备状态
|
||||
/// </summary>
|
||||
@@ -98,6 +107,58 @@ public class DeviceRuntime : Device, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 设备变量数量
|
||||
/// </summary>
|
||||
public int DeviceVariableCount { get => Driver == null ? VariableRuntimes?.Count ?? 0 : Driver.IdVariableRuntimes.Count; }
|
||||
|
||||
/// <summary>
|
||||
/// 设备读取打包数量
|
||||
/// </summary>
|
||||
public int SourceVariableCount => VariableSourceReads?.Count ?? 0;
|
||||
|
||||
#else
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 设备状态
|
||||
/// </summary>
|
||||
public virtual DeviceStatusEnum DeviceStatus
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!Pause)
|
||||
return _deviceStatus;
|
||||
else
|
||||
return DeviceStatusEnum.Pause;
|
||||
}
|
||||
set
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
if (_deviceStatus != value)
|
||||
{
|
||||
_deviceStatus = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 设备变量数量
|
||||
/// </summary>
|
||||
public int DeviceVariableCount { get; set; }
|
||||
/// <summary>
|
||||
/// 设备读取打包数量
|
||||
/// </summary>
|
||||
public int SourceVariableCount { get; set; }
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// 暂停
|
||||
/// </summary>
|
||||
@@ -130,12 +191,17 @@ public class DeviceRuntime : Device, IDisposable
|
||||
/// </summary>
|
||||
public RedundantTypeEnum? RedundantType { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// 设备变量数量
|
||||
/// </summary>
|
||||
public int DeviceVariableCount { get => Driver == null ? VariableRuntimes?.Count ?? 0 : Driver.IdVariableRuntimes.Count; }
|
||||
|
||||
#region 采集
|
||||
|
||||
/// <summary>
|
||||
/// 特殊方法数量
|
||||
/// </summary>
|
||||
public int MethodVariableCount { get; set; }
|
||||
|
||||
|
||||
|
||||
|
||||
#if !Management
|
||||
|
||||
/// <summary>
|
||||
/// 设备变量
|
||||
@@ -154,11 +220,6 @@ public class DeviceRuntime : Device, IDisposable
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
internal ConcurrentDictionary<string, VariableRuntime>? VariableRuntimes { get; } = new(Environment.ProcessorCount, 1000);
|
||||
|
||||
/// <summary>
|
||||
/// 特殊方法数量
|
||||
/// </summary>
|
||||
public int MethodVariableCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 特殊方法变量
|
||||
/// </summary>
|
||||
@@ -168,11 +229,6 @@ public class DeviceRuntime : Device, IDisposable
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public List<VariableMethod>? ReadVariableMethods { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 设备读取打包数量
|
||||
/// </summary>
|
||||
public int SourceVariableCount => VariableSourceReads?.Count ?? 0;
|
||||
|
||||
/// <summary>
|
||||
/// 打包变量
|
||||
/// </summary>
|
||||
@@ -191,10 +247,11 @@ public class DeviceRuntime : Device, IDisposable
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public List<VariableScriptRead>? VariableScriptReads { get; set; }
|
||||
|
||||
public volatile bool CheckEnable;
|
||||
private readonly object _lockObject = new object();
|
||||
#endif
|
||||
|
||||
#endregion 采集
|
||||
|
||||
|
||||
#if !Management
|
||||
|
||||
/// <summary>
|
||||
/// 传入设备的状态信息
|
||||
@@ -218,6 +275,7 @@ public class DeviceRuntime : Device, IDisposable
|
||||
LastErrorMessage = lastErrorMessage;
|
||||
}
|
||||
|
||||
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
[MapperIgnore]
|
||||
@@ -230,6 +288,7 @@ public class DeviceRuntime : Device, IDisposable
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public IRpcDriver? RpcDriver { get; set; }
|
||||
|
||||
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
@@ -263,4 +322,7 @@ public class DeviceRuntime : Device, IDisposable
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
}
|
||||
|
@@ -17,12 +17,13 @@ namespace ThingsGateway.Gateway.Application;
|
||||
/// </summary>
|
||||
public class PluginInfo
|
||||
{
|
||||
#if !Management
|
||||
/// <summary>
|
||||
/// 插件文件名称.插件类型名称
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public List<PluginInfo>? Children { get; set; } = new();
|
||||
|
||||
#endif
|
||||
/// <summary>
|
||||
/// 插件文件名称.插件类型名称
|
||||
/// </summary>
|
||||
|
@@ -8,9 +8,15 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
using Riok.Mapperly.Abstractions;
|
||||
|
||||
#if !Management
|
||||
using ThingsGateway.Gateway.Application.Extensions;
|
||||
#endif
|
||||
using ThingsGateway.NewLife.DictionaryExtensions;
|
||||
using ThingsGateway.NewLife.Json.Extension;
|
||||
|
||||
@@ -19,8 +25,194 @@ namespace ThingsGateway.Gateway.Application;
|
||||
/// <summary>
|
||||
/// 变量运行态
|
||||
/// </summary>
|
||||
public partial class VariableRuntime : Variable, IVariable, IDisposable
|
||||
public partial class VariableRuntime : Variable
|
||||
#if !Management
|
||||
,
|
||||
IVariable,
|
||||
IDisposable
|
||||
#endif
|
||||
{
|
||||
[AutoGenerateColumn(Visible = false)]
|
||||
public bool ValueInited { get => _valueInited; set => _valueInited = value; }
|
||||
|
||||
#region 属性
|
||||
|
||||
/// <summary>
|
||||
/// 这个参数值由自动打包方法写入<see cref="IDevice.LoadSourceRead{T}(IEnumerable{IVariable}, int, string)"/>
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = false)]
|
||||
public int Index { get => index; set => index = value; }
|
||||
|
||||
/// <summary>
|
||||
/// 变化时间
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true, Order = 5)]
|
||||
public DateTime ChangeTime { get => changeTime; set => changeTime = value; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 采集时间
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true, Order = 5)]
|
||||
public DateTime CollectTime { get => collectTime; set => collectTime = value; }
|
||||
|
||||
[SugarColumn(ColumnDescription = "排序码", IsNullable = true)]
|
||||
[AutoGenerateColumn(Visible = false, DefaultSort = false, Sortable = true)]
|
||||
[IgnoreExcel]
|
||||
public override int SortCode { get => sortCode; set => sortCode = value; }
|
||||
|
||||
/// <summary>
|
||||
/// 上次值
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = false, Order = 6)]
|
||||
public object LastSetValue { get => lastSetValue; set => lastSetValue = value; }
|
||||
|
||||
/// <summary>
|
||||
/// 原始值
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = false, Order = 6)]
|
||||
public object RawValue { get => rawValue; set => rawValue = value; }
|
||||
|
||||
|
||||
#if !Management
|
||||
|
||||
/// <summary>
|
||||
/// 所在采集设备
|
||||
/// </summary>
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public DeviceRuntime? DeviceRuntime { get => deviceRuntime; set => deviceRuntime = value; }
|
||||
|
||||
/// <summary>
|
||||
/// VariableSource
|
||||
/// </summary>
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[MapperIgnore]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public IVariableSource? VariableSource { get => variableSource; set => variableSource = value; }
|
||||
|
||||
/// <summary>
|
||||
/// VariableMethod
|
||||
/// </summary>
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[MapperIgnore]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public VariableMethod? VariableMethod { get => variableMethod; set => variableMethod = value; }
|
||||
|
||||
/// <summary>
|
||||
/// 这个参数值由自动打包方法写入<see cref="IDevice.LoadSourceRead{T}(IEnumerable{IVariable}, int, string)"/>
|
||||
/// </summary>
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public IThingsGatewayBitConverter? ThingsGatewayBitConverter { get => thingsGatewayBitConverter; set => thingsGatewayBitConverter = value; }
|
||||
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// 是否在线
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true, Order = 5)]
|
||||
public bool IsOnline
|
||||
{
|
||||
get
|
||||
{
|
||||
return _isOnline;
|
||||
}
|
||||
private set
|
||||
{
|
||||
if (IsOnline != value)
|
||||
{
|
||||
_isOnlineChanged = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_isOnlineChanged = false;
|
||||
}
|
||||
_isOnline = value;
|
||||
}
|
||||
}
|
||||
|
||||
#if !Management
|
||||
/// <summary>
|
||||
/// 设备名称
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true, Order = 4)]
|
||||
public string DeviceName => DeviceRuntime?.Name;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true, Order = 5)]
|
||||
public string LastErrorMessage
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_isOnline == false)
|
||||
return _lastErrorMessage ?? VariableSource?.LastErrorMessage ?? VariableMethod?.LastErrorMessage;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 实时值类型
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = true, Order = 6)]
|
||||
public string RuntimeType => Value?.GetType()?.ToString();
|
||||
|
||||
#else
|
||||
|
||||
/// <summary>
|
||||
/// 设备名称
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true, Order = 4)]
|
||||
public string DeviceName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true, Order = 5)]
|
||||
public string LastErrorMessage { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 实时值类型
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = true, Order = 6)]
|
||||
public string RuntimeType { get; set; }
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 实时值
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = true, Order = 6)]
|
||||
public object Value { get => _value; set => _value = value; }
|
||||
|
||||
/// <summary>
|
||||
/// 报警使能
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||
public bool AlarmEnable
|
||||
{
|
||||
get
|
||||
{
|
||||
return AlarmPropertys != null && (AlarmPropertys.LAlarmEnable || AlarmPropertys.LLAlarmEnable || AlarmPropertys.HAlarmEnable || AlarmPropertys.HHAlarmEnable || AlarmPropertys.BoolOpenAlarmEnable || AlarmPropertys.BoolCloseAlarmEnable || AlarmPropertys.CustomAlarmEnable);
|
||||
}
|
||||
}
|
||||
|
||||
[IgnoreExcel]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public AlarmRuntimePropertys? AlarmRuntimePropertys { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
private int index;
|
||||
@@ -33,15 +225,19 @@ public partial class VariableRuntime : Variable, IVariable, IDisposable
|
||||
private bool _isOnlineChanged;
|
||||
private bool _valueInited;
|
||||
|
||||
private string _lastErrorMessage;
|
||||
private object _value;
|
||||
private object lastSetValue;
|
||||
private object rawValue;
|
||||
#if !Management
|
||||
#pragma warning disable CS0649
|
||||
private string _lastErrorMessage;
|
||||
#pragma warning restore CS0649
|
||||
private DeviceRuntime? deviceRuntime;
|
||||
private IVariableSource? variableSource;
|
||||
private VariableMethod? variableMethod;
|
||||
private IThingsGatewayBitConverter? thingsGatewayBitConverter;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 设置变量值与时间/质量戳
|
||||
/// </summary>
|
||||
@@ -224,4 +420,10 @@ public partial class VariableRuntime : Variable, IVariable, IDisposable
|
||||
{
|
||||
_lastErrorMessage = lastErrorMessage;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@@ -1,173 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://thingsgateway.cn/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
using Riok.Mapperly.Abstractions;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 变量运行态
|
||||
/// </summary>
|
||||
public partial class VariableRuntime : Variable, IVariable, IDisposable
|
||||
{
|
||||
|
||||
[AutoGenerateColumn(Visible = false)]
|
||||
public bool ValueInited { get => _valueInited; set => _valueInited = value; }
|
||||
|
||||
#region 属性
|
||||
|
||||
/// <summary>
|
||||
/// 这个参数值由自动打包方法写入<see cref="IDevice.LoadSourceRead{T}(IEnumerable{IVariable}, int, string)"/>
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = false)]
|
||||
public int Index { get => index; set => index = value; }
|
||||
|
||||
/// <summary>
|
||||
/// 变化时间
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true, Order = 5)]
|
||||
public DateTime ChangeTime { get => changeTime; set => changeTime = value; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 采集时间
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true, Order = 5)]
|
||||
public DateTime CollectTime { get => collectTime; set => collectTime = value; }
|
||||
|
||||
[SugarColumn(ColumnDescription = "排序码", IsNullable = true)]
|
||||
[AutoGenerateColumn(Visible = false, DefaultSort = false, Sortable = true)]
|
||||
[IgnoreExcel]
|
||||
public override int SortCode { get => sortCode; set => sortCode = value; }
|
||||
|
||||
/// <summary>
|
||||
/// 上次值
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = false, Order = 6)]
|
||||
public object LastSetValue { get => lastSetValue; set => lastSetValue = value; }
|
||||
|
||||
/// <summary>
|
||||
/// 原始值
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = false, Order = 6)]
|
||||
public object RawValue { get => rawValue; set => rawValue = value; }
|
||||
|
||||
/// <summary>
|
||||
/// 所在采集设备
|
||||
/// </summary>
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public DeviceRuntime? DeviceRuntime { get => deviceRuntime; set => deviceRuntime = value; }
|
||||
|
||||
/// <summary>
|
||||
/// VariableSource
|
||||
/// </summary>
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[MapperIgnore]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public IVariableSource? VariableSource { get => variableSource; set => variableSource = value; }
|
||||
|
||||
/// <summary>
|
||||
/// VariableMethod
|
||||
/// </summary>
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[MapperIgnore]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public VariableMethod? VariableMethod { get => variableMethod; set => variableMethod = value; }
|
||||
|
||||
/// <summary>
|
||||
/// 这个参数值由自动打包方法写入<see cref="IDevice.LoadSourceRead{T}(IEnumerable{IVariable}, int, string)"/>
|
||||
/// </summary>
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public IThingsGatewayBitConverter? ThingsGatewayBitConverter { get => thingsGatewayBitConverter; set => thingsGatewayBitConverter = value; }
|
||||
|
||||
/// <summary>
|
||||
/// 设备名称
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true, Order = 4)]
|
||||
public string DeviceName => DeviceRuntime?.Name;
|
||||
|
||||
/// <summary>
|
||||
/// 是否在线
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true, Order = 5)]
|
||||
public bool IsOnline
|
||||
{
|
||||
get
|
||||
{
|
||||
return _isOnline;
|
||||
}
|
||||
private set
|
||||
{
|
||||
if (IsOnline != value)
|
||||
{
|
||||
_isOnlineChanged = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_isOnlineChanged = false;
|
||||
}
|
||||
_isOnline = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true, Order = 5)]
|
||||
public string LastErrorMessage
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_isOnline == false)
|
||||
return _lastErrorMessage ?? VariableSource?.LastErrorMessage ?? VariableMethod?.LastErrorMessage;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 实时值类型
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = true, Order = 6)]
|
||||
public string RuntimeType => Value?.GetType()?.ToString();
|
||||
|
||||
/// <summary>
|
||||
/// 实时值
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = true, Order = 6)]
|
||||
public object Value { get => _value; set => _value = value; }
|
||||
|
||||
/// <summary>
|
||||
/// 报警使能
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||
public bool AlarmEnable
|
||||
{
|
||||
get
|
||||
{
|
||||
return AlarmPropertys != null && (AlarmPropertys.LAlarmEnable || AlarmPropertys.LLAlarmEnable || AlarmPropertys.HAlarmEnable || AlarmPropertys.HHAlarmEnable || AlarmPropertys.BoolOpenAlarmEnable || AlarmPropertys.BoolCloseAlarmEnable || AlarmPropertys.CustomAlarmEnable);
|
||||
}
|
||||
}
|
||||
|
||||
[IgnoreExcel]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public AlarmRuntimePropertys? AlarmRuntimePropertys { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
@@ -19,13 +19,90 @@ namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
public class ChannelRuntimeService : IChannelRuntimeService
|
||||
{
|
||||
private ILogger _logger;
|
||||
public ChannelRuntimeService(ILogger<ChannelRuntimeService> logger)
|
||||
private Microsoft.Extensions.Logging.ILogger _logger;
|
||||
public ChannelRuntimeService(Microsoft.Extensions.Logging.ILogger<ChannelRuntimeService> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
private WaitLock WaitLock { get; set; } = new WaitLock(nameof(ChannelRuntimeService));
|
||||
|
||||
|
||||
public Task<TouchSocket.Core.LogLevel> ChannelLogLevelAsync(long id)
|
||||
{
|
||||
GlobalData.IdChannels.TryGetValue(id, out var ChannelRuntime);
|
||||
var data = ChannelRuntime?.DeviceThreadManage?.LogMessage?.LogLevel ?? TouchSocket.Core.LogLevel.Trace;
|
||||
return Task.FromResult(data);
|
||||
}
|
||||
|
||||
public async Task RestartChannelAsync(long channelId)
|
||||
{
|
||||
GlobalData.IdChannels.TryGetValue(channelId, out var channelRuntime);
|
||||
await GlobalData.GetChannelThreadManage(channelRuntime).RestartChannelAsync(channelRuntime).ConfigureAwait(false);
|
||||
|
||||
}
|
||||
|
||||
public async Task SetChannelLogLevelAsync(long id, TouchSocket.Core.LogLevel logLevel)
|
||||
{
|
||||
if (GlobalData.IdChannels.TryGetValue(id, out var ChannelRuntime))
|
||||
{
|
||||
if (ChannelRuntime.DeviceThreadManage != null)
|
||||
{
|
||||
await ChannelRuntime.DeviceThreadManage.SetLogAsync(logLevel).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public async Task CopyChannelAsync(int CopyCount, string CopyChannelNamePrefix, int CopyChannelNameSuffixNumber, string CopyDeviceNamePrefix, int CopyDeviceNameSuffixNumber, long channelId, bool AutoRestartThread)
|
||||
{
|
||||
if (!GlobalData.IdChannels.TryGetValue(channelId, out var channelRuntime))
|
||||
{
|
||||
return;
|
||||
}
|
||||
Dictionary<Device, List<Variable>> deviceDict = new();
|
||||
Channel Model = channelRuntime.AdaptChannel();
|
||||
Model.Id = 0;
|
||||
|
||||
var Devices = channelRuntime.ReadDeviceRuntimes.ToDictionary(a => a.Value.AdaptDevice(), a => a.Value.ReadOnlyVariableRuntimes.Select(a => a.Value).AdaptListVariable());
|
||||
|
||||
List<Channel> channels = new();
|
||||
Dictionary<Device, List<Variable>> devices = new();
|
||||
for (int i = 0; i < CopyCount; i++)
|
||||
{
|
||||
Channel channel = Model.AdaptChannel();
|
||||
channel.Id = CommonUtils.GetSingleId();
|
||||
channel.Name = $"{CopyChannelNamePrefix}{CopyChannelNameSuffixNumber + i}";
|
||||
|
||||
int index = 0;
|
||||
foreach (var item in Devices)
|
||||
{
|
||||
Device device = item.Key.AdaptDevice();
|
||||
device.Id = CommonUtils.GetSingleId();
|
||||
device.Name = $"{channel.Name}_{CopyDeviceNamePrefix}{CopyDeviceNameSuffixNumber + (index++)}";
|
||||
device.ChannelId = channel.Id;
|
||||
List<Variable> variables = new();
|
||||
|
||||
foreach (var variable in item.Value)
|
||||
{
|
||||
Variable v = variable.AdaptVariable();
|
||||
v.Id = CommonUtils.GetSingleId();
|
||||
v.DeviceId = device.Id;
|
||||
variables.Add(v);
|
||||
}
|
||||
devices.Add(device, variables);
|
||||
}
|
||||
|
||||
channels.Add(channel);
|
||||
}
|
||||
|
||||
await GlobalData.ChannelRuntimeService.CopyAsync(channels, devices, AutoRestartThread, default).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public async Task<bool> CopyAsync(List<Channel> models, Dictionary<Device, List<Variable>> devices, bool restart, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
@@ -204,6 +281,31 @@ public class ChannelRuntimeService : IChannelRuntimeService
|
||||
WaitLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ImportChannelAsync(List<Channel> upData, List<Channel> insertData, bool restart)
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
await WaitLock.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
var result = await GlobalData.ChannelService.ImportAsync(upData, insertData).ConfigureAwait(false);
|
||||
|
||||
var newChannelRuntimes = await RuntimeServiceHelper.GetNewChannelRuntimesAsync(result).ConfigureAwait(false);
|
||||
|
||||
RuntimeServiceHelper.Init(newChannelRuntimes);
|
||||
|
||||
//根据条件重启通道线程
|
||||
if (restart)
|
||||
await GlobalData.ChannelThreadManage.RestartChannelAsync(newChannelRuntimes).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
finally
|
||||
{
|
||||
WaitLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> SaveChannelAsync(Channel input, ItemChangedType type, bool restart)
|
||||
{
|
||||
try
|
||||
|
@@ -367,21 +367,16 @@ internal sealed class ChannelService : BaseService<Channel>, IChannelService
|
||||
|
||||
/// <inheritdoc/>
|
||||
[OperDesc("ImportChannel", isRecordPar: false, localizerType: typeof(Channel))]
|
||||
public async Task<HashSet<long>> ImportChannelAsync(Dictionary<string, ImportPreviewOutputBase> input)
|
||||
public Task<HashSet<long>> ImportChannelAsync(Dictionary<string, ImportPreviewOutputBase> input)
|
||||
{
|
||||
List<Channel>? channels = new List<Channel>();
|
||||
foreach (var item in input)
|
||||
{
|
||||
if (item.Key == ExportString.ChannelName)
|
||||
{
|
||||
var channelImports = ((ImportPreviewListOutput<Channel>)item.Value).Data;
|
||||
channels = channelImports;
|
||||
break;
|
||||
}
|
||||
}
|
||||
var upData = channels.Where(a => a.IsUp).ToList();
|
||||
var insertData = channels.Where(a => !a.IsUp).ToList();
|
||||
ChannelServiceHelpers.GetImportChannelData(input, out var upData, out var insertData);
|
||||
return ImportAsync(upData, insertData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public async Task<HashSet<long>> ImportAsync(List<Channel> upData, List<Channel> insertData)
|
||||
{
|
||||
ManageHelper.CheckChannelCount(insertData.Count);
|
||||
|
||||
using var db = GetDB();
|
||||
@@ -396,7 +391,7 @@ internal sealed class ChannelService : BaseService<Channel>, IChannelService
|
||||
await db.BulkUpdateAsync(upData, 10000).ConfigureAwait(false);
|
||||
}
|
||||
DeleteChannelFromCache();
|
||||
return channels.Select(a => a.Id).ToHashSet();
|
||||
return upData.Select(a => a.Id).Concat(insertData.Select(a => a.Id)).ToHashSet();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -441,7 +436,7 @@ internal sealed class ChannelService : BaseService<Channel>, IChannelService
|
||||
// 获取目标类型的所有属性,并根据是否需要过滤 IgnoreExcelAttribute 进行筛选
|
||||
var channelProperties = type.GetRuntimeProperties().Where(a => (a.GetCustomAttribute<IgnoreExcelAttribute>() == null) && a.CanWrite)
|
||||
.ToDictionary(a => type.GetPropertyDisplayName(a.Name), a => (a, a.IsNullableType()));
|
||||
|
||||
string unportNull = App.CreateLocalizerByType(typeof(Channel))["ImportNullError"];
|
||||
rows.ForEach(item =>
|
||||
{
|
||||
try
|
||||
@@ -450,7 +445,7 @@ internal sealed class ChannelService : BaseService<Channel>, IChannelService
|
||||
if (channel == null)
|
||||
{
|
||||
importPreviewOutput.HasError = true;
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, Localizer["ImportNullError"]));
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, unportNull));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -18,6 +18,23 @@ namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
public static class ChannelServiceHelpers
|
||||
{
|
||||
|
||||
public static void GetImportChannelData(Dictionary<string, ImportPreviewOutputBase> input, out List<Channel> upData, out List<Channel> insertData)
|
||||
{
|
||||
List<Channel>? channels = new List<Channel>();
|
||||
foreach (var item in input)
|
||||
{
|
||||
if (item.Key == ExportString.ChannelName)
|
||||
{
|
||||
var channelImports = ((ImportPreviewListOutput<Channel>)item.Value).Data;
|
||||
channels = channelImports;
|
||||
break;
|
||||
}
|
||||
}
|
||||
upData = channels.Where(a => a.IsUp).ToList();
|
||||
insertData = channels.Where(a => !a.IsUp).ToList();
|
||||
}
|
||||
|
||||
public static USheetDatas ExportChannel(IEnumerable<Channel> channels)
|
||||
{
|
||||
var rows = ExportRows(channels); // IEnumerable 延迟执行
|
||||
|
@@ -0,0 +1,55 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://thingsgateway.cn/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application
|
||||
{
|
||||
public interface IChannelPageService
|
||||
{
|
||||
Task RestartChannelAsync(long channelId);
|
||||
|
||||
Task<TouchSocket.Core.LogLevel> ChannelLogLevelAsync(long id);
|
||||
Task SetChannelLogLevelAsync(long id, TouchSocket.Core.LogLevel logLevel);
|
||||
Task CopyChannelAsync(int CopyCount, string CopyChannelNamePrefix, int CopyChannelNameSuffixNumber, string CopyDeviceNamePrefix, int CopyDeviceNameSuffixNumber, long channelId, bool AutoRestartThread);
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 保存通道
|
||||
/// </summary>
|
||||
/// <param name="input">通道对象</param>
|
||||
/// <param name="type">保存类型</param>
|
||||
/// <param name="restart">重启</param>
|
||||
Task<bool> SaveChannelAsync(Channel input, ItemChangedType type, bool restart);
|
||||
|
||||
/// <summary>
|
||||
/// 批量修改
|
||||
/// </summary>
|
||||
/// <param name="models">列表</param>
|
||||
/// <param name="oldModel">旧数据</param>
|
||||
/// <param name="model">新数据</param>
|
||||
/// <param name="restart">重启</param>
|
||||
/// <returns></returns>
|
||||
Task<bool> BatchEditAsync(IEnumerable<Channel> models, Channel oldModel, Channel model, bool restart);
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 删除通道
|
||||
/// </summary>
|
||||
Task<bool> DeleteChannelAsync(IEnumerable<long> ids, bool restart, CancellationToken cancellationToken);
|
||||
|
||||
Task ImportChannelAsync(List<Channel> upData, List<Channel> insertData, bool restart);
|
||||
|
||||
}
|
||||
}
|
@@ -14,15 +14,8 @@ using Microsoft.AspNetCore.Components.Forms;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
public interface IChannelRuntimeService
|
||||
public interface IChannelRuntimeService : IChannelPageService
|
||||
{
|
||||
/// <summary>
|
||||
/// 保存通道
|
||||
/// </summary>
|
||||
/// <param name="input">通道对象</param>
|
||||
/// <param name="type">保存类型</param>
|
||||
/// <param name="restart">重启</param>
|
||||
Task<bool> SaveChannelAsync(Channel input, ItemChangedType type, bool restart);
|
||||
|
||||
/// <summary>
|
||||
/// 保存通道
|
||||
@@ -32,25 +25,13 @@ public interface IChannelRuntimeService
|
||||
/// <param name="restart">重启</param>
|
||||
Task<bool> BatchSaveChannelAsync(List<Channel> input, ItemChangedType type, bool restart);
|
||||
|
||||
/// <summary>
|
||||
/// 批量修改
|
||||
/// </summary>
|
||||
/// <param name="models">列表</param>
|
||||
/// <param name="oldModel">旧数据</param>
|
||||
/// <param name="model">新数据</param>
|
||||
/// <param name="restart">重启</param>
|
||||
/// <returns></returns>
|
||||
Task<bool> BatchEditAsync(IEnumerable<Channel> models, Channel oldModel, Channel model, bool restart);
|
||||
|
||||
/// <summary>
|
||||
/// 删除通道
|
||||
/// </summary>
|
||||
Task<bool> DeleteChannelAsync(IEnumerable<long> ids, bool restart, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// 导入通道数据
|
||||
/// </summary>
|
||||
Task ImportChannelAsync(Dictionary<string, ImportPreviewOutputBase> input, bool restart);
|
||||
|
||||
|
||||
Task<Dictionary<string, object>> ExportChannelAsync(ExportFilter exportFilter);
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(IBrowserFile browserFile);
|
||||
Task<MemoryStream> ExportMemoryStream(IEnumerable<Channel> data);
|
||||
|
@@ -102,4 +102,6 @@ internal interface IChannelService
|
||||
Task UpdateLogAsync(long channelId, TouchSocket.Core.LogLevel logLevel);
|
||||
Task<bool> InsertAsync(List<Channel> models, List<Device> devices, List<Variable> variables);
|
||||
Task<bool> UpdateAsync(List<Channel> models, List<Device> devices, List<Variable> variables);
|
||||
Task<HashSet<long>> ImportAsync(List<Channel> upData, List<Channel> insertData);
|
||||
|
||||
}
|
||||
|
@@ -439,6 +439,12 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
|
||||
public void SetDeviceData(HashSet<long>? dataScope, IReadOnlyDictionary<string, DeviceRuntime> deviceDicts, IReadOnlyDictionary<string, ChannelRuntime> channelDicts, Dictionary<string, ImportPreviewOutputBase> ImportPreviews, ref ImportPreviewOutput<Device> deviceImportPreview, Dictionary<string, PluginInfo> driverPluginNameDict, ConcurrentDictionary<string, (Type, Dictionary<string, PropertyInfo>, Dictionary<string, PropertyInfo>)> propertysDict, string sheetName, IEnumerable<IDictionary<string, object>> rows)
|
||||
{
|
||||
#region 采集设备sheet
|
||||
string ImportNullError = Localizer["ImportNullError"];
|
||||
string RedundantDeviceError = Localizer["RedundantDeviceError"];
|
||||
string ChannelError = Localizer["ChannelError"];
|
||||
|
||||
string PluginNotNull = Localizer["PluginNotNull"];
|
||||
string DeviceNotNull = Localizer["DeviceNotNull"];
|
||||
|
||||
if (sheetName == ExportString.DeviceName)
|
||||
{
|
||||
@@ -459,6 +465,8 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
|
||||
var deviceProperties = type.GetRuntimeProperties().Where(a => (a.GetCustomAttribute<IgnoreExcelAttribute>() == null) && a.CanWrite)
|
||||
.ToDictionary(a => type.GetPropertyDisplayName(a.Name), a => (a, a.IsNullableType()));
|
||||
|
||||
|
||||
|
||||
// 遍历每一行数据
|
||||
rows.ForEach(item =>
|
||||
{
|
||||
@@ -471,7 +479,7 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
|
||||
if (device == null)
|
||||
{
|
||||
importPreviewOutput.HasError = true;
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, Localizer["ImportNullError"]));
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, ImportNullError));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -488,7 +496,7 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
|
||||
{
|
||||
// 如果找不到对应的冗余设备,则添加错误信息到导入预览结果并返回
|
||||
importPreviewOutput.HasError = true;
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, Localizer["RedundantDeviceError"]));
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, RedundantDeviceError));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -498,7 +506,7 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
|
||||
if (device.RedundantEnable)
|
||||
{
|
||||
importPreviewOutput.HasError = true;
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, Localizer["RedundantDeviceError"]));
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, RedundantDeviceError));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -512,7 +520,7 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
|
||||
{
|
||||
// 如果找不到对应的通道信息,则添加错误信息到导入预览结果并返回
|
||||
importPreviewOutput.HasError = true;
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, Localizer["ChannelError"]));
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, ChannelError));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -520,7 +528,7 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
|
||||
{
|
||||
// 如果未提供通道信息,则添加错误信息到导入预览结果并返回
|
||||
importPreviewOutput.HasError = true;
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, Localizer["ChannelError"]));
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, ChannelError));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -653,7 +661,7 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
|
||||
if (propertys.Item1 == null)
|
||||
{
|
||||
importPreviewOutput.HasError = true;
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, Localizer["PluginNotNull"]));
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, PluginNotNull));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -661,7 +669,7 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
|
||||
if (!item.TryGetValue(ExportString.DeviceName, out var deviceName))
|
||||
{
|
||||
importPreviewOutput.HasError = true;
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, Localizer["DeviceNotNull"]));
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, DeviceNotNull));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -684,7 +692,7 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
|
||||
if (pluginProp == null)
|
||||
{
|
||||
importPreviewOutput.HasError = true;
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, Localizer["ImportNullError"]));
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, ImportNullError));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -47,7 +47,7 @@ public partial class ManagementTask : AsyncDisposableObject
|
||||
{
|
||||
_logger?.Log_Out(logLevel, source, message, exception);
|
||||
}
|
||||
|
||||
private bool success = true;
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (!_managementOptions.Enable) return;
|
||||
@@ -65,10 +65,14 @@ public partial class ManagementTask : AsyncDisposableObject
|
||||
try
|
||||
{
|
||||
await EnsureChannelOpenAsync(cancellationToken).ConfigureAwait(false);
|
||||
success = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogMessage?.LogWarning(ex, "Start");
|
||||
if (success)
|
||||
LogMessage?.LogWarning(ex, "Start");
|
||||
|
||||
success = false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@@ -619,6 +619,14 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
|
||||
public ImportPreviewOutput<Dictionary<string, Variable>> SetVariableData(HashSet<long>? dataScope, IReadOnlyDictionary<string, DeviceRuntime> deviceDicts, Dictionary<string, ImportPreviewOutputBase> ImportPreviews, ImportPreviewOutput<Dictionary<string, Variable>> deviceImportPreview, Dictionary<string, PluginInfo> driverPluginNameDict, ConcurrentDictionary<string, (Type, Dictionary<string, PropertyInfo>, Dictionary<string, PropertyInfo>)> propertysDict, string sheetName, IEnumerable<IDictionary<string, object>> rows)
|
||||
{
|
||||
string ImportNullError = Localizer["ImportNullError"];
|
||||
string RedundantDeviceError = Localizer["RedundantDeviceError"];
|
||||
|
||||
string PluginNotNull = Localizer["PluginNotNull"];
|
||||
string DeviceNotNull = Localizer["DeviceNotNull"];
|
||||
string VariableNotNull = Localizer["VariableNotNull"];
|
||||
|
||||
|
||||
// 变量页处理
|
||||
if (sheetName == ExportString.VariableName)
|
||||
{
|
||||
@@ -742,7 +750,7 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
if (alarm == null)
|
||||
{
|
||||
importPreviewOutput.HasError = true;
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, Localizer["ImportNullError"]));
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, ImportNullError));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -754,13 +762,13 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
if (collectDevName == null || collectDevice == null)
|
||||
{
|
||||
importPreviewOutput.HasError = true;
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, Localizer["DeviceNotNull"]));
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, DeviceNotNull));
|
||||
return;
|
||||
}
|
||||
if (variableNameObj == null)
|
||||
{
|
||||
importPreviewOutput.HasError = true;
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, Localizer["VariableNotNull"]));
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, VariableNotNull));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -798,7 +806,7 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
else
|
||||
{
|
||||
importPreviewOutput.HasError = true;
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, Localizer["VariableNotNull"]));
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, VariableNotNull));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -863,7 +871,7 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
if (propertys.Item3?.Count == null || propertys.Item1 == null)
|
||||
{
|
||||
importPreviewOutput.HasError = true;
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, Localizer["ImportNullError"]));
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, ImportNullError));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -874,7 +882,7 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
if (pluginProp == null)
|
||||
{
|
||||
importPreviewOutput.HasError = true;
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, Localizer["ImportNullError"]));
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, ImportNullError));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -889,13 +897,13 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
if (businessDevName == null || businessDevice == null || collectDevName == null || collectDevice == null)
|
||||
{
|
||||
importPreviewOutput.HasError = true;
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, Localizer["DeviceNotNull"]));
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, DeviceNotNull));
|
||||
return;
|
||||
}
|
||||
if (variableNameObj == null)
|
||||
{
|
||||
importPreviewOutput.HasError = true;
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, Localizer["VariableNotNull"]));
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, VariableNotNull));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -72,6 +72,7 @@ public class Startup : AppStartup
|
||||
|
||||
services.AddSingleton<IChannelThreadManage, ChannelThreadManage>();
|
||||
services.AddSingleton<IChannelService, ChannelService>();
|
||||
services.AddSingleton<IChannelPageService>(a => a.GetService<IChannelRuntimeService>());
|
||||
services.AddSingleton<IChannelRuntimeService, ChannelRuntimeService>();
|
||||
services.AddSingleton<IVariableService, VariableService>();
|
||||
services.AddSingleton<IVariableRuntimeService, VariableRuntimeService>();
|
||||
|
@@ -10,14 +10,7 @@
|
||||
export function getAutoRestartThread() {
|
||||
return JSON.parse(localStorage.getItem('autoRestartThread'))??true;
|
||||
}
|
||||
export function getShowType() {
|
||||
return JSON.parse(localStorage.getItem('showType'))??0;
|
||||
}
|
||||
export function saveShowType(showType) {
|
||||
if (localStorage) {
|
||||
localStorage.setItem('showType', JSON.stringify(showType));
|
||||
}
|
||||
}
|
||||
|
||||
export function saveAutoRestartThread(autoRestartThread) {
|
||||
if (localStorage) {
|
||||
localStorage.setItem('autoRestartThread', JSON.stringify(autoRestartThread));
|
||||
|
@@ -8,8 +8,6 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using ThingsGateway.DB;
|
||||
|
||||
namespace ThingsGateway.Gateway.Razor;
|
||||
|
||||
public partial class ChannelCopyComponent
|
||||
@@ -17,16 +15,10 @@ public partial class ChannelCopyComponent
|
||||
[Inject]
|
||||
IStringLocalizer<ThingsGateway.Gateway.Razor._Imports> GatewayLocalizer { get; set; }
|
||||
|
||||
[Parameter]
|
||||
[EditorRequired]
|
||||
public Channel Model { get; set; }
|
||||
|
||||
|
||||
[Parameter]
|
||||
[EditorRequired]
|
||||
public Dictionary<Device, List<Variable>> Devices { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<List<Channel>, Dictionary<Device, List<Variable>>, Task> OnSave { get; set; }
|
||||
public Func<int, string, int, string, int, Task> OnSave { get; set; }
|
||||
|
||||
[CascadingParameter]
|
||||
private Func<Task>? OnCloseAsync { get; set; }
|
||||
@@ -44,38 +36,8 @@ public partial class ChannelCopyComponent
|
||||
{
|
||||
try
|
||||
{
|
||||
List<Channel> channels = new();
|
||||
Dictionary<Device, List<Variable>> devices = new();
|
||||
for (int i = 0; i < CopyCount; i++)
|
||||
{
|
||||
Channel channel = Model.AdaptChannel();
|
||||
channel.Id = CommonUtils.GetSingleId();
|
||||
channel.Name = $"{CopyChannelNamePrefix}{CopyChannelNameSuffixNumber + i}";
|
||||
|
||||
int index = 0;
|
||||
foreach (var item in Devices)
|
||||
{
|
||||
Device device = item.Key.AdaptDevice();
|
||||
device.Id = CommonUtils.GetSingleId();
|
||||
device.Name = $"{channel.Name}_{CopyDeviceNamePrefix}{CopyDeviceNameSuffixNumber + (index++)}";
|
||||
device.ChannelId = channel.Id;
|
||||
List<Variable> variables = new();
|
||||
|
||||
foreach (var variable in item.Value)
|
||||
{
|
||||
Variable v = variable.AdaptVariable();
|
||||
v.Id = CommonUtils.GetSingleId();
|
||||
v.DeviceId = device.Id;
|
||||
variables.Add(v);
|
||||
}
|
||||
devices.Add(device, variables);
|
||||
}
|
||||
|
||||
channels.Add(channel);
|
||||
}
|
||||
|
||||
if (OnSave != null)
|
||||
await OnSave(channels, devices);
|
||||
await OnSave(CopyCount, CopyChannelNamePrefix, CopyChannelNameSuffixNumber, CopyDeviceNamePrefix, CopyDeviceNameSuffixNumber);
|
||||
if (OnCloseAsync != null)
|
||||
await OnCloseAsync();
|
||||
await ToastService.Default();
|
||||
|
@@ -56,9 +56,12 @@ public partial class ChannelEditComponent
|
||||
[Parameter]
|
||||
public PluginTypeEnum? PluginType { get; set; }
|
||||
|
||||
protected override void OnInitialized()
|
||||
[Inject]
|
||||
IPluginPageService PluginService { get; set; }
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var plugins = GlobalData.PluginService.GetPluginList(PluginType);
|
||||
var plugins = await PluginService.GetPluginsAsync(PluginType);
|
||||
|
||||
PluginDcit = plugins.ToDictionary(a => a.FullName);
|
||||
PluginNames = plugins.BuildPluginSelectList();
|
||||
|
@@ -5,10 +5,10 @@
|
||||
|
||||
<ChannelRuntimeInfo1 ChannelRuntime="ChannelRuntime" />
|
||||
|
||||
<LogConsole HeightString="calc(100% - 270px)" LogLevel=@((ChannelRuntime?.DeviceThreadManage?.LogMessage)?.LogLevel ?? TouchSocket.Core.LogLevel.Trace)
|
||||
LogLevelChanged="(logLevel)=>
|
||||
{
|
||||
ChannelRuntime.DeviceThreadManage?.SetLogAsync(logLevel);
|
||||
}"
|
||||
|
||||
<LogConsole HeightString="calc(100% - 270px)" LogLevel=@(LogLevel)
|
||||
LogLevelChanged="async(logLevel)=>
|
||||
{
|
||||
LogLevel = logLevel;
|
||||
await ChannelPageService.SetChannelLogLevelAsync(ChannelRuntime?.Id ?? 0, logLevel);
|
||||
}"
|
||||
LogPath=@ChannelRuntime.LogPath></LogConsole>
|
||||
|
@@ -14,4 +14,27 @@ public partial class ChannelRuntimeInfo
|
||||
{
|
||||
[Parameter, EditorRequired]
|
||||
public ChannelRuntime ChannelRuntime { get; set; }
|
||||
|
||||
public TouchSocket.Core.LogLevel LogLevel { get; set; }
|
||||
[Inject]
|
||||
IChannelPageService ChannelPageService { get; set; }
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (ChannelRuntime?.Id > 0)
|
||||
{
|
||||
var logLevel = await ChannelPageService.ChannelLogLevelAsync(ChannelRuntime.Id);
|
||||
|
||||
if (logLevel != LogLevel)
|
||||
{
|
||||
LogLevel = logLevel;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
if (firstRender)
|
||||
await InvokeAsync(StateHasChanged);
|
||||
await base.OnAfterRenderAsync(firstRender);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -12,7 +12,7 @@
|
||||
|
||||
<HeaderTemplate>
|
||||
|
||||
<span class=@((ChannelRuntime?.DeviceThreadManage!=null?"enable--text mx-1 text-h6 overflow-y-auto":"disabled--text mx-1 text-h6 overflow-y-auto"))>
|
||||
<span class=@((ChannelRuntime?.Started==true ?"enable--text mx-1 text-h6 overflow-y-auto":"disabled--text mx-1 text-h6 overflow-y-auto"))>
|
||||
<span class="text-truncate" title=@Name>@(Name)</span>
|
||||
</span>
|
||||
|
||||
|
@@ -17,14 +17,12 @@ public partial class ChannelRuntimeInfo1 : IDisposable
|
||||
|
||||
[Parameter, EditorRequired]
|
||||
public ChannelRuntime ChannelRuntime { get; set; }
|
||||
private string Name => $"{ChannelRuntime.ToString()} - {(ChannelRuntime.DeviceThreadManage == null ? "Task cancel" : "Task run")}";
|
||||
|
||||
private string Name => $"{ChannelRuntime.ToString()} - {(ChannelRuntime.Started == false ? "Task cancel" : "Task run")}";
|
||||
[Inject]
|
||||
IChannelPageService ChannelPageService { get; set; }
|
||||
private async Task RestartChannelAsync()
|
||||
{
|
||||
if (ChannelRuntime.DeviceThreadManage?.ChannelThreadManage != null)
|
||||
await Task.Run(() => ChannelRuntime.DeviceThreadManage.ChannelThreadManage.RestartChannelAsync(ChannelRuntime));
|
||||
else
|
||||
await Task.Run(() => GlobalData.ChannelThreadManage.RestartChannelAsync(ChannelRuntime));
|
||||
await ChannelPageService.RestartChannelAsync(ChannelRuntime.Id);
|
||||
}
|
||||
|
||||
protected override void OnInitialized()
|
||||
|
@@ -100,6 +100,9 @@ public partial class ChannelTable : IDisposable
|
||||
|
||||
#region 编辑
|
||||
|
||||
[Inject]
|
||||
IChannelPageService ChannelPageService { get; set; }
|
||||
|
||||
#region 修改
|
||||
private async Task Copy(IEnumerable<ChannelRuntime> channels)
|
||||
{
|
||||
@@ -110,13 +113,6 @@ public partial class ChannelTable : IDisposable
|
||||
return;
|
||||
}
|
||||
|
||||
Channel oneModel = null;
|
||||
Dictionary<Device, List<Variable>> deviceDict = new();
|
||||
oneModel = channelRuntime.AdaptChannel();
|
||||
oneModel.Id = 0;
|
||||
|
||||
deviceDict = channelRuntime.ReadDeviceRuntimes.ToDictionary(a => a.Value.AdaptDevice(), a => a.Value.ReadOnlyVariableRuntimes.Select(a => a.Value).AdaptListVariable());
|
||||
|
||||
var op = new DialogOption()
|
||||
{
|
||||
IsScrolling = false,
|
||||
@@ -129,13 +125,11 @@ public partial class ChannelTable : IDisposable
|
||||
|
||||
op.Component = BootstrapDynamicComponent.CreateComponent<ChannelCopyComponent>(new Dictionary<string, object?>
|
||||
{
|
||||
{nameof(ChannelCopyComponent.OnSave), async (List<Channel> channels,Dictionary<Device,List<Variable>> devices) =>
|
||||
{nameof(ChannelCopyComponent.OnSave), async (int CopyCount, string CopyChannelNamePrefix, int CopyChannelNameSuffixNumber, string CopyDeviceNamePrefix, int CopyDeviceNameSuffixNumber) =>
|
||||
{
|
||||
await Task.Run(() =>GlobalData.ChannelRuntimeService.CopyAsync(channels,devices,AutoRestartThread, default));
|
||||
await Task.Run(() =>ChannelPageService.CopyChannelAsync(CopyCount,CopyChannelNamePrefix,CopyChannelNameSuffixNumber,CopyDeviceNamePrefix,CopyDeviceNameSuffixNumber,channelRuntime.Id,AutoRestartThread));
|
||||
await InvokeAsync(table.QueryAsync);
|
||||
}},
|
||||
{nameof(ChannelCopyComponent.Model),oneModel },
|
||||
{nameof(ChannelCopyComponent.Devices),deviceDict },
|
||||
});
|
||||
|
||||
await DialogService.Show(op);
|
||||
@@ -168,7 +162,7 @@ public partial class ChannelTable : IDisposable
|
||||
{
|
||||
{nameof(ChannelEditComponent.OnValidSubmit), async () =>
|
||||
{
|
||||
await Task.Run(() => GlobalData.ChannelRuntimeService.BatchEditAsync(changedModels, oldModel, oneModel,AutoRestartThread));
|
||||
await Task.Run(() => ChannelPageService.BatchEditAsync(changedModels, oldModel, oneModel,AutoRestartThread));
|
||||
|
||||
await InvokeAsync(table.QueryAsync);
|
||||
} },
|
||||
@@ -185,7 +179,7 @@ public partial class ChannelTable : IDisposable
|
||||
{
|
||||
try
|
||||
{
|
||||
return await Task.Run(async () => await GlobalData.ChannelRuntimeService.DeleteChannelAsync(channels.Select(a => a.Id), AutoRestartThread, default));
|
||||
return await Task.Run(async () => await ChannelPageService.DeleteChannelAsync(channels.Select(a => a.Id), AutoRestartThread, default));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -199,7 +193,7 @@ public partial class ChannelTable : IDisposable
|
||||
try
|
||||
{
|
||||
channel = channel.AdaptChannel();
|
||||
return await Task.Run(() => GlobalData.ChannelRuntimeService.SaveChannelAsync(channel, itemChangedType, AutoRestartThread));
|
||||
return await Task.Run(() => ChannelPageService.SaveChannelAsync(channel, itemChangedType, AutoRestartThread));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -286,7 +280,9 @@ public partial class ChannelTable : IDisposable
|
||||
await Task.Run(async ()=>
|
||||
{
|
||||
var importData=await ChannelServiceHelpers.ImportAsync(data);
|
||||
await GlobalData.ChannelRuntimeService.ImportChannelAsync(importData,AutoRestartThread);
|
||||
ChannelServiceHelpers. GetImportChannelData(importData, out var upData, out var insertData);
|
||||
|
||||
await ChannelPageService.ImportChannelAsync(upData,insertData,AutoRestartThread);
|
||||
})
|
||||
;
|
||||
}
|
||||
@@ -338,7 +334,7 @@ finally
|
||||
{
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
await GlobalData.ChannelRuntimeService.DeleteChannelAsync(Items.Select(a => a.Id), AutoRestartThread, default);
|
||||
await ChannelPageService.DeleteChannelAsync(Items.Select(a => a.Id), AutoRestartThread, default);
|
||||
await InvokeAsync(async () =>
|
||||
{
|
||||
await ToastService.Default();
|
||||
|
@@ -80,35 +80,6 @@ namespace ThingsGateway.Gateway.Razor
|
||||
return pluginTypeEnum;
|
||||
}
|
||||
|
||||
internal static async Task ShowCopy(Channel oneModel, Dictionary<Device, List<Variable>> deviceDict, string text, bool autoRestart, Func<Task> onsave, DialogService dialogService)
|
||||
{
|
||||
var op = new DialogOption()
|
||||
{
|
||||
IsScrolling = false,
|
||||
ShowMaximizeButton = true,
|
||||
Size = Size.ExtraLarge,
|
||||
Title = text,
|
||||
ShowFooter = false,
|
||||
ShowCloseButton = false,
|
||||
};
|
||||
|
||||
op.Component = BootstrapDynamicComponent.CreateComponent<ChannelCopyComponent>(new Dictionary<string, object?>
|
||||
{
|
||||
{nameof(ChannelCopyComponent.OnSave), async (List<Channel> channels,Dictionary<Device,List<Variable>> devices) =>
|
||||
{
|
||||
await Task.Run(() =>GlobalData.ChannelRuntimeService.CopyAsync(channels,devices,autoRestart, default));
|
||||
if(onsave!=null)
|
||||
await onsave();
|
||||
}},
|
||||
{nameof(ChannelCopyComponent.Model),oneModel },
|
||||
{nameof(ChannelCopyComponent.Devices),deviceDict },
|
||||
});
|
||||
|
||||
await dialogService.Show(op);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
@@ -1,6 +1,4 @@
|
||||
@inherits ThingsGatewayModuleComponentBase
|
||||
@attribute [JSModuleAutoLoader("Pages/GatewayMonitorPage/ChannelDeviceTree.razor.js", AutoInvokeDispose = false)]
|
||||
@namespace ThingsGateway.Gateway.Razor
|
||||
@namespace ThingsGateway.Gateway.Razor
|
||||
@using ThingsGateway.Admin.Application
|
||||
@using ThingsGateway.Admin.Razor
|
||||
@using ThingsGateway.Gateway.Application
|
||||
|
@@ -10,16 +10,18 @@
|
||||
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
using ThingsGateway.Admin.Application;
|
||||
using ThingsGateway.Admin.Razor;
|
||||
using ThingsGateway.NewLife.Extension;
|
||||
using ThingsGateway.NewLife.Json.Extension;
|
||||
using ThingsGateway.Razor.Extension;
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
namespace ThingsGateway.Gateway.Razor;
|
||||
|
||||
public partial class ChannelDeviceTree
|
||||
public partial class ChannelDeviceTree : IDisposable
|
||||
{
|
||||
SpinnerComponent Spinner;
|
||||
[Inject]
|
||||
@@ -41,21 +43,29 @@ public partial class ChannelDeviceTree
|
||||
public EventCallback<ShowTypeEnum?> ShowTypeChanged { get; set; }
|
||||
[Parameter]
|
||||
public ShowTypeEnum? ShowType { get; set; }
|
||||
|
||||
[Inject]
|
||||
IJSRuntime JSRuntime { get; set; }
|
||||
private async Task OnShowTypeChanged(ShowTypeEnum? showType)
|
||||
{
|
||||
ShowType = showType;
|
||||
if (showType != null && Module != null)
|
||||
await Module!.InvokeVoidAsync("saveShowType", ShowType);
|
||||
if (showType != null)
|
||||
await JSRuntime.SetLocalStorage("showType", ShowType);
|
||||
if (ShowTypeChanged.HasDelegate)
|
||||
await ShowTypeChanged.InvokeAsync(showType);
|
||||
}
|
||||
protected override async Task InvokeInitAsync()
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
var showType = await Module!.InvokeAsync<ShowTypeEnum>("getShowType");
|
||||
await OnShowTypeChanged(showType);
|
||||
if (firstRender)
|
||||
{
|
||||
var showType = await JSRuntime!.GetLocalStorage<ShowTypeEnum>("showType");
|
||||
await OnShowTypeChanged(showType);
|
||||
StateHasChanged();
|
||||
}
|
||||
await base.OnAfterRenderAsync(firstRender);
|
||||
}
|
||||
|
||||
|
||||
[Parameter]
|
||||
public bool AutoRestartThread { get; set; }
|
||||
|
||||
@@ -140,15 +150,10 @@ public partial class ChannelDeviceTree
|
||||
|
||||
async Task CopyChannel(string text, ChannelDeviceTreeItem channelDeviceTreeItem)
|
||||
{
|
||||
Channel oneModel = null;
|
||||
Dictionary<Device, List<Variable>> deviceDict = new();
|
||||
|
||||
if (channelDeviceTreeItem.TryGetChannelRuntime(out var channelRuntime))
|
||||
{
|
||||
oneModel = channelRuntime.AdaptChannel();
|
||||
oneModel.Id = 0;
|
||||
|
||||
deviceDict = channelRuntime.ReadDeviceRuntimes.ToDictionary(a => a.Value.AdaptDevice(), a => a.Value.ReadOnlyVariableRuntimes.Select(a => a.Value).AdaptListVariable());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -167,19 +172,16 @@ public partial class ChannelDeviceTree
|
||||
|
||||
op.Component = BootstrapDynamicComponent.CreateComponent<ChannelCopyComponent>(new Dictionary<string, object?>
|
||||
{
|
||||
{nameof(ChannelCopyComponent.OnSave), async (List<Channel> channels,Dictionary<Device,List<Variable>> devices) =>
|
||||
{nameof(ChannelCopyComponent.OnSave), async (int CopyCount, string CopyChannelNamePrefix, int CopyChannelNameSuffixNumber, string CopyDeviceNamePrefix, int CopyDeviceNameSuffixNumber) =>
|
||||
{
|
||||
await Task.Run(() =>GlobalData.ChannelRuntimeService.CopyAsync(channels,devices,AutoRestartThread, default));
|
||||
//await Notify();
|
||||
|
||||
await Task.Run(() =>ChannelPageService.CopyChannelAsync(CopyCount,CopyChannelNamePrefix,CopyChannelNameSuffixNumber,CopyDeviceNamePrefix,CopyDeviceNameSuffixNumber,channelRuntime.Id,AutoRestartThread));
|
||||
}},
|
||||
{nameof(ChannelCopyComponent.Model),oneModel },
|
||||
{nameof(ChannelCopyComponent.Devices),deviceDict },
|
||||
});
|
||||
|
||||
await DialogService.Show(op);
|
||||
}
|
||||
|
||||
[Inject]
|
||||
IChannelPageService ChannelPageService { get; set; }
|
||||
Task BatchEditChannel(ContextMenuItem item, object value)
|
||||
{
|
||||
return BatchEditChannel(item.Text, value as ChannelDeviceTreeItem);
|
||||
@@ -1360,11 +1362,10 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e =>
|
||||
}
|
||||
}
|
||||
private bool Disposed;
|
||||
protected override ValueTask DisposeAsync(bool disposing)
|
||||
public void Dispose()
|
||||
{
|
||||
Disposed = true;
|
||||
ChannelRuntimeDispatchService.UnSubscribe(Refresh);
|
||||
return base.DisposeAsync(disposing);
|
||||
}
|
||||
|
||||
ChannelDeviceTreeItem? SelectModel = default;
|
||||
@@ -1381,4 +1382,6 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e =>
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@@ -1,9 +0,0 @@
|
||||
|
||||
export function getShowType() {
|
||||
return JSON.parse(localStorage.getItem('showType'))??0;
|
||||
}
|
||||
export function saveShowType(showType) {
|
||||
if (localStorage) {
|
||||
localStorage.setItem('showType', JSON.stringify(showType));
|
||||
}
|
||||
}
|
@@ -13,7 +13,7 @@
|
||||
<link href="ThingsGateway.Debug.Photino.styles.css" rel="stylesheet" />
|
||||
<link href="/_content/ThingsGateway.Razor/css/site.css" rel="stylesheet" />
|
||||
<script src="/_content/ThingsGateway.Razor/js/theme.js" type="module"></script><!-- 初始主题 -->
|
||||
<script src="/_content/ThingsGateway.Razor/js/culture.js"></script>
|
||||
<script src="/_content/ThingsGateway.Razor/js/localStorageUtil.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
|
@@ -15,7 +15,7 @@
|
||||
<link href="/_content/ThingsGateway.Razor/css/site.css" rel="stylesheet" />
|
||||
<link href="/_content/ThingsGateway.Razor/css/devui.css" rel="stylesheet" />
|
||||
<script src="/_content/ThingsGateway.Razor/js/theme.js" type="module"></script><!-- 初始主题 -->
|
||||
<script src="/_content/ThingsGateway.Razor/js/culture.js"></script>
|
||||
<script src="/_content/ThingsGateway.Razor/js/localStorageUtil.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
|
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"$schema": "null",
|
||||
|
||||
"RemoteClientManagement": {
|
||||
"Enable": true,
|
||||
"Name": "ThingsGateway",
|
||||
"ServerUri": "127.0.0.1:8299",
|
||||
"VerifyToken": "ThingsGateway",
|
||||
"HeartbeatInterval": 3000
|
||||
},
|
||||
|
||||
"RemoteServerManagement": {
|
||||
"Enable": true,
|
||||
"Name": "ThingsGateway",
|
||||
"ServerUri": "0.0.0.0:7999",
|
||||
"VerifyToken": "ThingsGateway",
|
||||
"HeartbeatInterval": 3000
|
||||
}
|
||||
}
|
@@ -4,13 +4,13 @@
|
||||
"RemoteClientManagement": {
|
||||
"Enable": false,
|
||||
"Name": "ThingsGateway",
|
||||
"ServerUri": "127.0.0.1:7999",
|
||||
"ServerUri": "127.0.0.1:8299",
|
||||
"VerifyToken": "ThingsGateway",
|
||||
"HeartbeatInterval": 3000
|
||||
},
|
||||
|
||||
"RemoteServerManagement": {
|
||||
"Enable": true,
|
||||
"Enable": false,
|
||||
"Name": "ThingsGateway",
|
||||
"ServerUri": "0.0.0.0:7999",
|
||||
"VerifyToken": "ThingsGateway",
|
||||
|
@@ -47,7 +47,7 @@
|
||||
<BlazorReconnector @rendermode="new InteractiveServerRenderMode(false)" />
|
||||
|
||||
<script src="_content/BootstrapBlazor/js/bootstrap.blazor.bundle.min.js"></script>
|
||||
<script src=@($"{WebsiteConst.DefaultResourceUrl}js/culture.js")></script>
|
||||
<script src=@($"{WebsiteConst.DefaultResourceUrl}js/localStorageUtil.js")></script>
|
||||
<script src="_framework/blazor.web.js"></script>
|
||||
<!-- PWA Service Worker -->
|
||||
<script type="text/javascript">'serviceWorker' in navigator && navigator.serviceWorker.register('./service-worker.js')</script>
|
||||
|
@@ -43,7 +43,7 @@
|
||||
<BlazorReconnector @rendermode="new InteractiveServerRenderMode(false)" />
|
||||
|
||||
<script src="_content/BootstrapBlazor/js/bootstrap.blazor.bundle.min.js"></script>
|
||||
<script src=@($"{WebsiteConst.DefaultResourceUrl}js/culture.js")></script>
|
||||
<script src=@($"{WebsiteConst.DefaultResourceUrl}js/localStorageUtil.js")></script>
|
||||
<script src="_framework/blazor.web.js"></script>
|
||||
<!-- PWA Service Worker -->
|
||||
<script type="text/javascript">'serviceWorker' in navigator && navigator.serviceWorker.register('./service-worker.js')</script>
|
||||
|
@@ -48,7 +48,7 @@
|
||||
</app>
|
||||
|
||||
<script src="_content/BootstrapBlazor/js/bootstrap.blazor.bundle.min.js"></script>
|
||||
<script src=@($"{WebsiteConst.DefaultResourceUrl}js/culture.js")></script>
|
||||
<script src=@($"{WebsiteConst.DefaultResourceUrl}js/localStorageUtil.js")></script>
|
||||
<script src="_framework/blazor.server.js"></script>
|
||||
|
||||
<!-- PWA Service Worker -->
|
||||
|
Reference in New Issue
Block a user