Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
6ae44ccf58 | ||
![]() |
1aa0df6339 | ||
![]() |
62c3693dbe | ||
![]() |
e4e503c97b | ||
![]() |
5ec1ee7627 | ||
![]() |
79789388fc | ||
![]() |
2c4194ee18 |
@@ -90,8 +90,8 @@
|
||||
<h6> @((100 - (availableMemory * 100.00 / memory)).ToString("F2") + " %") </h6>
|
||||
|
||||
<span> @Localizer["WorkingSet"] <i> @(HardwareJob.HardwareInfo.WorkingSet + " MB")</i></span>
|
||||
<span> @Localizer["AvailableMemory"] <i> @((availableMemory / 1024.00 / 1024 / 1024).ToString("F2") + " GB")</i></span>
|
||||
<span> @Localizer["TotalMemory"] <i> @((memory / 1024.00 / 1024 / 1024).ToString("F2") + " GB")</i></span>
|
||||
<span> @Localizer["AvailableMemory"] <i> @((availableMemory / 1024.00 / 1024).ToString("F2") + " GB")</i></span>
|
||||
<span> @Localizer["TotalMemory"] <i> @((memory / 1024.00 / 1024).ToString("F2") + " GB")</i></span>
|
||||
|
||||
</div>
|
||||
</Circle>
|
||||
|
@@ -107,7 +107,7 @@ public class Startup : AppStartup
|
||||
.AddHubOptions(options =>
|
||||
{
|
||||
//单个传入集线器消息的最大大小。默认 32 KB
|
||||
options.MaximumReceiveMessageSize = 1024 * 1024;
|
||||
options.MaximumReceiveMessageSize = 32 * 1024 * 1024;
|
||||
//可为客户端上载流缓冲的最大项数。 如果达到此限制,则会阻止处理调用,直到服务器处理流项。
|
||||
options.StreamBufferCapacity = 30;
|
||||
options.ClientTimeoutInterval = TimeSpan.FromMinutes(2);
|
||||
@@ -126,7 +126,7 @@ public class Startup : AppStartup
|
||||
}).AddHubOptions(options =>
|
||||
{
|
||||
//单个传入集线器消息的最大大小。默认 32 KB
|
||||
options.MaximumReceiveMessageSize = 1024 * 1024;
|
||||
options.MaximumReceiveMessageSize = 32 * 1024 * 1024;
|
||||
//可为客户端上载流缓冲的最大项数。 如果达到此限制,则会阻止处理调用,直到服务器处理流项。
|
||||
options.StreamBufferCapacity = 30;
|
||||
options.ClientTimeoutInterval = TimeSpan.FromMinutes(2);
|
||||
|
@@ -56,6 +56,35 @@ public static class QueryPageOptionsExtensions
|
||||
return datas;
|
||||
}
|
||||
|
||||
public static IEnumerable<T> GetQuery<T>(this IEnumerable<T> query, QueryPageOptions option, Func<IEnumerable<T>, IEnumerable<T>>? queryFunc = null, FilterKeyValueAction where = null)
|
||||
{
|
||||
if (queryFunc != null)
|
||||
query = queryFunc(query);
|
||||
where ??= option.ToFilter();
|
||||
|
||||
if (where.HasFilters())
|
||||
{
|
||||
query = query.Where(where.GetFilterFunc<T>());//name asc模式
|
||||
}
|
||||
|
||||
if (option.SortOrder != SortOrder.Unset && !string.IsNullOrEmpty(option.SortName))
|
||||
{
|
||||
var invoker = Utility.GetSortFunc<T>();
|
||||
query = invoker(query, option.SortName, option.SortOrder);
|
||||
}
|
||||
else if (option.SortList.Count > 0)
|
||||
{
|
||||
var invoker = Utility.GetSortListFunc<T>();
|
||||
query = invoker(query, option.SortList);
|
||||
}
|
||||
else if (option.AdvancedSortList.Count > 0)
|
||||
{
|
||||
var invoker = Utility.GetSortListFunc<T>();
|
||||
query = invoker(query, option.AdvancedSortList);
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据查询条件返回sqlsugar ISugarQueryable
|
||||
/// </summary>
|
||||
|
@@ -78,6 +78,13 @@ public class BaseService<T> : IDataService<T>, IDisposable where T : class, new(
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async Task<QueryData<T>> QueryAsync(QueryPageOptions option, Func<ISugarQueryable<T>, ISugarQueryable<T>>? queryFunc = null, FilterKeyValueAction where = null)
|
||||
{
|
||||
using var db = GetDB();
|
||||
ISugarQueryable<T> query = GetQuery(db, option, queryFunc, where);
|
||||
return await GetQueryData(option, query).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public static async Task<QueryData<T>> GetQueryData(QueryPageOptions option, ISugarQueryable<T> query)
|
||||
{
|
||||
var ret = new QueryData<T>()
|
||||
{
|
||||
@@ -87,12 +94,6 @@ public class BaseService<T> : IDataService<T>, IDisposable where T : class, new(
|
||||
IsSearch = option.Searches.Count > 0
|
||||
};
|
||||
|
||||
using var db = GetDB();
|
||||
var query = db.Queryable<T>();
|
||||
if (queryFunc != null)
|
||||
query = queryFunc(query);
|
||||
query = db.GetQuery<T>(option, query, where);
|
||||
|
||||
if (option.IsPage)
|
||||
{
|
||||
RefAsync<int> totalCount = 0;
|
||||
@@ -117,9 +118,19 @@ public class BaseService<T> : IDataService<T>, IDisposable where T : class, new(
|
||||
ret.TotalCount = items.Count;
|
||||
ret.Items = items;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static ISugarQueryable<T> GetQuery(SqlSugarClient db, QueryPageOptions option, Func<ISugarQueryable<T>, ISugarQueryable<T>>? queryFunc, FilterKeyValueAction where)
|
||||
{
|
||||
var query = db.Queryable<T>();
|
||||
if (queryFunc != null)
|
||||
query = queryFunc(query);
|
||||
query = db.GetQuery<T>(option, query, where);
|
||||
return query;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async Task<bool> SaveAsync(T model, ItemChangedType changedType)
|
||||
{
|
||||
|
@@ -88,11 +88,11 @@ public class MachineInfo : IExtend
|
||||
[DisplayName("磁盘序列号")]
|
||||
public String? DiskID { get; set; }
|
||||
|
||||
/// <summary>内存总量。单位Byte</summary>
|
||||
/// <summary>内存总量。单位KB</summary>
|
||||
[DisplayName("内存总量")]
|
||||
public UInt64 Memory { get; set; }
|
||||
|
||||
/// <summary>可用内存。单位Byte</summary>
|
||||
/// <summary>可用内存。单位KB</summary>
|
||||
[DisplayName("可用内存")]
|
||||
public UInt64 AvailableMemory { get; set; }
|
||||
|
||||
@@ -337,7 +337,7 @@ public class MachineInfo : IExtend
|
||||
#if NETFRAMEWORK || WINDOWS
|
||||
{
|
||||
var ci = new Microsoft.VisualBasic.Devices.ComputerInfo();
|
||||
Memory = ci.TotalPhysicalMemory;
|
||||
Memory = (ulong)(ci.TotalPhysicalMemory / 1024.0);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -557,7 +557,7 @@ public class MachineInfo : IExtend
|
||||
//if (dic2.TryGetValue("Model Name", out str)) Product = str;
|
||||
if (dic.TryGetValue("Model Identifier", out var str)) Product = str;
|
||||
if (dic.TryGetValue("Processor Name", out str)) Processor = str;
|
||||
if (dic.TryGetValue("Memory", out str)) Memory = (UInt64)str.TrimEnd("GB").Trim().ToLong() * 1024 * 1024 * 1024;
|
||||
if (dic.TryGetValue("Memory", out str)) Memory = (UInt64)str.TrimEnd("GB").Trim().ToLong() * 1024 * 1024;
|
||||
if (dic.TryGetValue("Serial Number (system)", out str)) Serial = str;
|
||||
if (dic.TryGetValue("Hardware UUID", out str)) UUID = str;
|
||||
if (dic.TryGetValue("Processor Name", out str)) Processor = str;
|
||||
@@ -594,8 +594,8 @@ public class MachineInfo : IExtend
|
||||
ms.Init();
|
||||
if (GlobalMemoryStatusEx(ref ms))
|
||||
{
|
||||
Memory = ms.ullTotalPhys;
|
||||
AvailableMemory = ms.ullAvailPhys;
|
||||
Memory = (ulong)(ms.ullTotalPhys / 1024.0);
|
||||
AvailableMemory = (ulong)(ms.ullAvailPhys / 1024.0);
|
||||
}
|
||||
|
||||
GetSystemTimes(out var idleTime, out var kernelTime, out var userTime);
|
||||
@@ -695,15 +695,15 @@ public class MachineInfo : IExtend
|
||||
if (dic != null)
|
||||
{
|
||||
if (dic.TryGetValue("MemTotal", out var str) && !str.IsNullOrEmpty())
|
||||
Memory = (UInt64)str.TrimEnd(" kB").ToInt() * 1024;
|
||||
Memory = (UInt64)str.TrimEnd(" kB").ToLong();
|
||||
|
||||
if (dic.TryGetValue("MemAvailable", out str) && !str.IsNullOrEmpty())
|
||||
AvailableMemory = (UInt64)str.TrimEnd(" kB").ToInt() * 1024;
|
||||
AvailableMemory = (UInt64)str.TrimEnd(" kB").ToLong();
|
||||
else if (dic.TryGetValue("MemFree", out str) && !str.IsNullOrEmpty())
|
||||
AvailableMemory =
|
||||
(UInt64)(str.TrimEnd(" kB").ToInt() +
|
||||
dic["Buffers"]?.TrimEnd(" kB").ToInt() ?? 0 +
|
||||
dic["Cached"]?.TrimEnd(" kB").ToInt() ?? 0) * 1024;
|
||||
(UInt64)(str.TrimEnd(" kB").ToLong() +
|
||||
dic["Buffers"]?.TrimEnd(" kB").ToLong() ?? 0 +
|
||||
dic["Cached"]?.TrimEnd(" kB").ToLong() ?? 0);
|
||||
}
|
||||
|
||||
// A2/A4温度获取,Buildroot,CPU温度和主板温度
|
||||
|
@@ -245,7 +245,7 @@ public abstract class Logger : ILog
|
||||
sb.AppendFormat("#CPU: {0}\r\n", Environment.ProcessorCount);
|
||||
if (mi != null)
|
||||
{
|
||||
sb.AppendFormat("#Memory: {0:n0}M/{1:n0}M\r\n", mi.AvailableMemory / 1024 / 1024, mi.Memory / 1024 / 1024);
|
||||
sb.AppendFormat("#Memory: {0:n0}M/{1:n0}M\r\n", mi.AvailableMemory / 1024, mi.Memory / 1024);
|
||||
sb.AppendFormat("#Processor: {0}\r\n", mi.Processor);
|
||||
if (!mi.Product.IsNullOrEmpty()) sb.AppendFormat("#Product: {0} / {1}\r\n", mi.Product, mi.Vendor);
|
||||
if (mi.Temperature > 0) sb.AppendFormat("#Temperature: {0}\r\n", mi.Temperature);
|
||||
|
@@ -44,3 +44,14 @@ public class ImportPreviewOutput<T> : ImportPreviewOutputBase where T : class
|
||||
/// </summary>
|
||||
public Dictionary<string, T> Data { get; set; } = new();
|
||||
}
|
||||
/// <summary>
|
||||
/// 导入预览
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public class ImportPreviewListOutput<T> : ImportPreviewOutputBase where T : class
|
||||
{
|
||||
/// <summary>
|
||||
/// 数据
|
||||
/// </summary>
|
||||
public List<T> Data { get; set; } = new();
|
||||
}
|
||||
|
@@ -683,7 +683,7 @@ namespace ThingsGateway.SqlSugar
|
||||
var entityInfo = this.Context.EntityMaintenance.GetEntityInfo<T>();
|
||||
var columns = UtilMethods.GetColumnInfo(dr);
|
||||
var cacheKey = "ForEachDataReader" + typeof(T).GetHashCode() + string.Join(",", columns.Select(it => it.Item1 + it.Item2.Name + "_"));
|
||||
IDataReaderEntityBuilder<T> entytyList = this.Context.Utilities.GetReflectionInoCacheInstance().GetOrCreate("cacheKey", () =>
|
||||
IDataReaderEntityBuilder<T> entytyList = this.Context.Utilities.GetReflectionInoCacheInstance().GetOrCreate(cacheKey, () =>
|
||||
{
|
||||
var cacheResult = new IDataReaderEntityBuilder<T>(this.Context, dr,
|
||||
columns.Select(it => it.Item1).ToList()).CreateBuilder(typeof(T));
|
||||
@@ -703,6 +703,38 @@ namespace ThingsGateway.SqlSugar
|
||||
this.Context.Ado.Close();
|
||||
}
|
||||
}
|
||||
public IEnumerable<T> GetEnumerable()
|
||||
{
|
||||
var queryable = this.Clone();
|
||||
var sql = queryable.ToSql();
|
||||
var dr = this.Context.Ado.GetDataReader(sql.Key, sql.Value);
|
||||
var entityInfo = this.Context.EntityMaintenance.GetEntityInfo<T>();
|
||||
var columns = UtilMethods.GetColumnInfo(dr);
|
||||
var cacheKey = "GetEnumerable" + typeof(T).GetHashCode() + string.Join(",", columns.Select(it => it.Item1 + it.Item2.Name + "_"));
|
||||
IDataReaderEntityBuilder<T> entytyList = this.Context.Utilities.GetReflectionInoCacheInstance().GetOrCreate(cacheKey, () =>
|
||||
{
|
||||
var cacheResult = new IDataReaderEntityBuilder<T>(this.Context, dr,
|
||||
columns.Select(it => it.Item1).ToList()).CreateBuilder(typeof(T));
|
||||
return cacheResult;
|
||||
});
|
||||
|
||||
|
||||
using (dr)
|
||||
{
|
||||
while (dr.Read())
|
||||
{
|
||||
|
||||
var order = entytyList.Build(dr);
|
||||
yield return order;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (this.Context.CurrentConnectionConfig.IsAutoCloseConnection)
|
||||
{
|
||||
this.Context.Ado.Close();
|
||||
}
|
||||
}
|
||||
public async Task ForEachDataReaderAsync(Action<T> action)
|
||||
{
|
||||
var queryable = this.Clone();
|
||||
@@ -711,7 +743,7 @@ namespace ThingsGateway.SqlSugar
|
||||
var entityInfo = this.Context.EntityMaintenance.GetEntityInfo<T>();
|
||||
var columns = UtilMethods.GetColumnInfo(dr);
|
||||
var cacheKey = "ForEachDataReader" + typeof(T).GetHashCode() + string.Join(",", columns.Select(it => it.Item1 + it.Item2.Name + "_"));
|
||||
IDataReaderEntityBuilder<T> entytyList = this.Context.Utilities.GetReflectionInoCacheInstance().GetOrCreate("cacheKey", () =>
|
||||
IDataReaderEntityBuilder<T> entytyList = this.Context.Utilities.GetReflectionInoCacheInstance().GetOrCreate(cacheKey, () =>
|
||||
{
|
||||
var cacheResult = new IDataReaderEntityBuilder<T>(this.Context, dr,
|
||||
columns.Select(it => it.Item1).ToList()).CreateBuilder(typeof(T));
|
||||
@@ -731,6 +763,42 @@ namespace ThingsGateway.SqlSugar
|
||||
this.Context.Ado.Close();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Diego 新增一个延迟返回
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async IAsyncEnumerable<T> GetAsyncEnumerable()
|
||||
{
|
||||
var queryable = this.Clone();
|
||||
var sql = queryable.ToSql();
|
||||
var dr = await Context.Ado.GetDataReaderAsync(sql.Key, sql.Value).ConfigureAwait(false);
|
||||
var entityInfo = this.Context.EntityMaintenance.GetEntityInfo<T>();
|
||||
var columns = UtilMethods.GetColumnInfo(dr);
|
||||
var cacheKey = "GetAsyncEnumerable" + typeof(T).GetHashCode() + string.Join(",", columns.Select(it => it.Item1 + it.Item2.Name + "_"));
|
||||
IDataReaderEntityBuilder<T> entytyList = this.Context.Utilities.GetReflectionInoCacheInstance().GetOrCreate(cacheKey, () =>
|
||||
{
|
||||
var cacheResult = new IDataReaderEntityBuilder<T>(this.Context, dr,
|
||||
columns.Select(it => it.Item1).ToList()).CreateBuilder(typeof(T));
|
||||
return cacheResult;
|
||||
});
|
||||
|
||||
|
||||
using (dr)
|
||||
{
|
||||
while (dr.Read())
|
||||
{
|
||||
|
||||
var order = entytyList.Build(dr);
|
||||
yield return order;
|
||||
}
|
||||
}
|
||||
if (this.Context.CurrentConnectionConfig.IsAutoCloseConnection)
|
||||
{
|
||||
this.Context.Ado.Close();
|
||||
}
|
||||
|
||||
}
|
||||
public virtual void ForEach(Action<T> action, int singleMaxReads = 300, System.Threading.CancellationTokenSource cancellationTokenSource = null)
|
||||
{
|
||||
Check.Exception(this.QueryBuilder.Skip > 0 || this.QueryBuilder.Take > 0, ErrorMessage.GetThrowMessage("no support Skip take, use PageForEach", "不支持Skip Take,请使用 Queryale.PageForEach"));
|
||||
|
@@ -32,7 +32,8 @@ namespace ThingsGateway.SqlSugar
|
||||
NavISugarQueryable<T> Includes<TReturn1, TReturn2, TReturn3>(Expression<Func<T, TReturn1>> include1, Expression<Func<TReturn1, TReturn2>> include2, Expression<Func<TReturn2, List<TReturn3>>> include3);
|
||||
NavISugarQueryable<T> Includes<TReturn1, TReturn2, TReturn3>(Expression<Func<T, List<TReturn1>>> include1, Expression<Func<TReturn1, TReturn2>> include2, Expression<Func<TReturn2, TReturn3>> include3);
|
||||
NavISugarQueryable<T> Includes<TReturn1, TReturn2, TReturn3>(Expression<Func<T, TReturn1>> include1, Expression<Func<TReturn1, List<TReturn2>>> include2, Expression<Func<TReturn2, List<TReturn3>>> include3);
|
||||
|
||||
IAsyncEnumerable<T> GetAsyncEnumerable();
|
||||
IEnumerable<T> GetEnumerable();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@@ -1,10 +1,10 @@
|
||||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<PluginVersion>10.9.11</PluginVersion>
|
||||
<ProPluginVersion>10.9.11</ProPluginVersion>
|
||||
<AuthenticationVersion>2.9.5</AuthenticationVersion>
|
||||
<SourceGeneratorVersion>10.9.5</SourceGeneratorVersion>
|
||||
<PluginVersion>10.9.17</PluginVersion>
|
||||
<ProPluginVersion>10.9.17</ProPluginVersion>
|
||||
<AuthenticationVersion>2.9.7</AuthenticationVersion>
|
||||
<SourceGeneratorVersion>10.9.7</SourceGeneratorVersion>
|
||||
<NET8Version>8.0.17</NET8Version>
|
||||
<NET9Version>9.0.6</NET9Version>
|
||||
<SatelliteResourceLanguages>zh-Hans;en-US</SatelliteResourceLanguages>
|
||||
|
@@ -27,7 +27,7 @@ public interface IThingsGatewayBitConverter
|
||||
/// <summary>
|
||||
/// 指定大小端。
|
||||
/// </summary>
|
||||
EndianType EndianType { get; }
|
||||
EndianType EndianType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前的字符串编码类型
|
||||
|
@@ -62,7 +62,14 @@ public partial class ThingsGatewayBitConverter : IThingsGatewayBitConverter
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual EndianType EndianType { get; }
|
||||
public virtual EndianType EndianType
|
||||
{
|
||||
get => endianType; set
|
||||
{
|
||||
endianType = value;
|
||||
TouchSocketBitConverter = new TouchSocketBitConverter(endianType);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual bool IsStringReverseByteWord { get; set; }
|
||||
@@ -70,7 +77,7 @@ public partial class ThingsGatewayBitConverter : IThingsGatewayBitConverter
|
||||
/// <inheritdoc/>
|
||||
public virtual bool IsVariableStringLength { get; set; }
|
||||
|
||||
internal protected TouchSocketBitConverter TouchSocketBitConverter => TouchSocketBitConverter.GetBitConverter(EndianType);
|
||||
internal protected TouchSocketBitConverter TouchSocketBitConverter { get; set; }
|
||||
|
||||
static ThingsGatewayBitConverter()
|
||||
{
|
||||
@@ -87,7 +94,7 @@ public partial class ThingsGatewayBitConverter : IThingsGatewayBitConverter
|
||||
/// 以小端
|
||||
/// </summary>
|
||||
public static readonly ThingsGatewayBitConverter LittleEndian;
|
||||
|
||||
private EndianType endianType;
|
||||
|
||||
public virtual void OtherPropertySet(IThingsGatewayBitConverter thingsGatewayBitConverter, string registerAddress)
|
||||
{
|
||||
|
@@ -16,7 +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 Int32 Period => _timer?.Period ?? 0;
|
||||
|
||||
public ScheduledAsyncTask(int interval, Func<object?, CancellationToken, Task> taskFunc, object? state, ILog log, CancellationToken token)
|
||||
{
|
||||
|
@@ -150,23 +150,26 @@ public abstract class CollectBase : DriverBase, IRpcDriver
|
||||
return variablesMethodResult;
|
||||
}
|
||||
}
|
||||
|
||||
private volatile bool _addVariableTasks;
|
||||
protected void RefreshVariableTasks(CancellationToken cancellationToken)
|
||||
{
|
||||
if (VariableTasks.Count > 0)
|
||||
if (_addVariableTasks)
|
||||
{
|
||||
foreach (var item in VariableTasks)
|
||||
if (VariableTasks != null)
|
||||
{
|
||||
item.Stop();
|
||||
TaskSchedulerLoop.Remove(item);
|
||||
}
|
||||
foreach (var item in VariableTasks)
|
||||
{
|
||||
item.Stop();
|
||||
TaskSchedulerLoop?.Remove(item);
|
||||
}
|
||||
|
||||
VariableTasks = AddVariableTask(cancellationToken);
|
||||
VariableTasks = AddVariableTask(cancellationToken);
|
||||
|
||||
foreach (var item in VariableTasks)
|
||||
{
|
||||
TaskSchedulerLoop.Add(item);
|
||||
item.Start();
|
||||
foreach (var item in VariableTasks)
|
||||
{
|
||||
TaskSchedulerLoop?.Add(item);
|
||||
item.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -198,7 +201,7 @@ public abstract class CollectBase : DriverBase, IRpcDriver
|
||||
tasks.Add(testOnline);
|
||||
|
||||
VariableTasks = AddVariableTask(cancellationToken);
|
||||
|
||||
_addVariableTasks = true;
|
||||
tasks.AddRange(VariableTasks);
|
||||
return tasks;
|
||||
|
||||
@@ -242,6 +245,7 @@ public abstract class CollectBase : DriverBase, IRpcDriver
|
||||
|
||||
protected virtual void SetDeviceStatus(object? state, CancellationToken cancellationToken)
|
||||
{
|
||||
CurrentDevice.SetDeviceStatus(TimerX.Now);
|
||||
if (IsConnected())
|
||||
{
|
||||
if (CurrentDevice.DeviceStatus == DeviceStatusEnum.OffLine)
|
||||
|
@@ -20,7 +20,7 @@ namespace ThingsGateway.Gateway.Application
|
||||
|
||||
foreach (var a in data)
|
||||
{
|
||||
var value = a.Value as IList<Dictionary<string, object>>;
|
||||
var value = (a.Value as IEnumerable<Dictionary<string, object>>).ToList();
|
||||
|
||||
var uSheetData = new USheetData();
|
||||
uSheetData.id = a.Key;
|
||||
|
@@ -28,7 +28,6 @@ public static partial class GatewayMapper
|
||||
|
||||
[MapProperty(nameof(Variable.InitValue), nameof(VariableRuntime.Value))]
|
||||
public static partial VariableRuntime AdaptVariableRuntime(this Variable src);
|
||||
|
||||
public static partial List<Variable> AdaptListVariable(this IEnumerable<Variable> src);
|
||||
|
||||
public static partial DeviceRuntime AdaptDeviceRuntime(this Device src);
|
||||
|
@@ -113,11 +113,11 @@ public class ChannelRuntimeService : IChannelRuntimeService
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(IBrowserFile browserFile) => GlobalData.ChannelService.PreviewAsync(browserFile);
|
||||
|
||||
public Task<Dictionary<string, object>> ExportChannelAsync(ExportFilter exportFilter) => GlobalData.ChannelService.ExportChannelAsync(exportFilter);
|
||||
public Task<MemoryStream> ExportMemoryStream(List<Channel> data) =>
|
||||
|
||||
public Task<MemoryStream> ExportMemoryStream(IEnumerable<Channel> data) =>
|
||||
GlobalData.ChannelService.ExportMemoryStream(data);
|
||||
|
||||
public async Task ImportChannelAsync(Dictionary<string, ImportPreviewOutputBase> input, bool restart = true)
|
||||
|
@@ -192,6 +192,14 @@ internal sealed class ChannelService : BaseService<Channel>, IChannelService
|
||||
/// </summary>
|
||||
/// <param name="exportFilter">查询条件</param>
|
||||
public async Task<QueryData<Channel>> PageAsync(ExportFilter exportFilter)
|
||||
{
|
||||
var whereQuery = await GetWhereQueryFunc(exportFilter).ConfigureAwait(false);
|
||||
|
||||
return await QueryAsync(exportFilter.QueryPageOptions, whereQuery
|
||||
, exportFilter.FilterKeyValueAction).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task<Func<ISugarQueryable<Channel>, ISugarQueryable<Channel>>> GetWhereQueryFunc(ExportFilter exportFilter)
|
||||
{
|
||||
HashSet<long>? channel = null;
|
||||
if (exportFilter.PluginType != null)
|
||||
@@ -200,16 +208,15 @@ internal sealed class ChannelService : BaseService<Channel>, IChannelService
|
||||
channel = (await GetAllAsync().ConfigureAwait(false)).Where(a => pluginInfo.Contains(a.PluginName)).Select(a => a.Id).ToHashSet();
|
||||
}
|
||||
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
|
||||
return await QueryAsync(exportFilter.QueryPageOptions, a => a
|
||||
var whereQuery = (ISugarQueryable<Channel> a) => a
|
||||
.WhereIF(!exportFilter.QueryPageOptions.SearchText.IsNullOrWhiteSpace(), a => a.Name.Contains(exportFilter.QueryPageOptions.SearchText!))
|
||||
.WhereIF(!exportFilter.PluginName.IsNullOrWhiteSpace(), a => a.PluginName == exportFilter.PluginName)
|
||||
.WhereIF(channel != null, a => channel.Contains(a.Id))
|
||||
.WhereIF(exportFilter.ChannelId != null, a => a.Id == exportFilter.ChannelId)
|
||||
|
||||
.WhereIF(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.CreateOrgId))//在指定机构列表查询
|
||||
.WhereIF(dataScope?.Count == 0, u => u.CreateUserId == UserManager.UserId)
|
||||
|
||||
, exportFilter.FilterKeyValueAction).ConfigureAwait(false);
|
||||
.WhereIF(dataScope?.Count == 0, u => u.CreateUserId == UserManager.UserId);
|
||||
return whereQuery;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -273,19 +280,31 @@ internal sealed class ChannelService : BaseService<Channel>, IChannelService
|
||||
[OperDesc("ExportChannel", isRecordPar: false, localizerType: typeof(Channel))]
|
||||
public async Task<Dictionary<string, object>> ExportChannelAsync(ExportFilter exportFilter)
|
||||
{
|
||||
var data = await PageAsync(exportFilter).ConfigureAwait(false);
|
||||
return ChannelServiceHelpers.ExportChannelCore(data.Items);
|
||||
var channels = await GetEnumerableData(exportFilter).ConfigureAwait(false);
|
||||
var rows = ChannelServiceHelpers.ExportRows(channels); // IEnumerable 延迟执行
|
||||
var sheets = ChannelServiceHelpers.WrapAsSheet(ExportString.ChannelName, rows);
|
||||
return sheets;
|
||||
}
|
||||
|
||||
private async Task<IAsyncEnumerable<Channel>> GetEnumerableData(ExportFilter exportFilter)
|
||||
{
|
||||
var db = GetDB();
|
||||
var whereQuery = await GetWhereQueryFunc(exportFilter).ConfigureAwait(false);
|
||||
|
||||
var query = GetQuery(db, exportFilter.QueryPageOptions, whereQuery, exportFilter.FilterKeyValueAction);
|
||||
|
||||
return query.GetAsyncEnumerable();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[OperDesc("ExportChannel", isRecordPar: false, localizerType: typeof(Channel))]
|
||||
public async Task<MemoryStream> ExportMemoryStream(List<Channel> data)
|
||||
public async Task<MemoryStream> ExportMemoryStream(IEnumerable<Channel> channels)
|
||||
{
|
||||
var sheets = ChannelServiceHelpers.ExportChannelCore(data);
|
||||
var rows = ChannelServiceHelpers.ExportRows(channels); // IEnumerable 延迟执行
|
||||
var sheets = ChannelServiceHelpers.WrapAsSheet(ExportString.ChannelName, rows);
|
||||
var memoryStream = new MemoryStream();
|
||||
await memoryStream.SaveAsAsync(sheets).ConfigureAwait(false);
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
return memoryStream;
|
||||
}
|
||||
|
||||
@@ -304,8 +323,8 @@ internal sealed class ChannelService : BaseService<Channel>, IChannelService
|
||||
{
|
||||
if (item.Key == ExportString.ChannelName)
|
||||
{
|
||||
var channelImports = ((ImportPreviewOutput<Channel>)item.Value).Data;
|
||||
channels = channelImports.Select(a => a.Value).ToList();
|
||||
var channelImports = ((ImportPreviewListOutput<Channel>)item.Value).Data;
|
||||
channels = channelImports;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -315,8 +334,16 @@ internal sealed class ChannelService : BaseService<Channel>, IChannelService
|
||||
ManageHelper.CheckChannelCount(insertData.Count);
|
||||
|
||||
using var db = GetDB();
|
||||
await db.BulkCopyAsync(insertData, 100000).ConfigureAwait(false);
|
||||
await db.BulkUpdateAsync(upData, 100000).ConfigureAwait(false);
|
||||
if (GlobalData.HardwareJob.HardwareInfo.MachineInfo.AvailableMemory > 2 * 1024 * 1024)
|
||||
{
|
||||
await db.BulkCopyAsync(insertData, 200000).ConfigureAwait(false);
|
||||
await db.BulkUpdateAsync(upData, 200000).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await db.BulkCopyAsync(insertData, 10000).ConfigureAwait(false);
|
||||
await db.BulkUpdateAsync(upData, 10000).ConfigureAwait(false);
|
||||
}
|
||||
DeleteChannelFromCache();
|
||||
return channels.Select(a => a.Id).ToHashSet();
|
||||
}
|
||||
@@ -333,11 +360,10 @@ internal sealed class ChannelService : BaseService<Channel>, IChannelService
|
||||
//导入检验结果
|
||||
Dictionary<string, ImportPreviewOutputBase> ImportPreviews = new();
|
||||
//设备页
|
||||
ImportPreviewOutput<Channel> channelImportPreview = new();
|
||||
foreach (var sheetName in sheetNames)
|
||||
{
|
||||
var rows = MiniExcel.Query(path, useHeaderRow: true, sheetName: sheetName).Cast<IDictionary<string, object>>();
|
||||
SetChannelData(dataScope, channelDicts, ImportPreviews, channelImportPreview, sheetName, rows);
|
||||
SetChannelData(dataScope, channelDicts, ImportPreviews, sheetName, rows);
|
||||
}
|
||||
|
||||
return ImportPreviews;
|
||||
@@ -348,16 +374,15 @@ internal sealed class ChannelService : BaseService<Channel>, IChannelService
|
||||
}
|
||||
}
|
||||
|
||||
public void SetChannelData(HashSet<long>? dataScope, Dictionary<string, Channel> channelDicts, Dictionary<string, ImportPreviewOutputBase> ImportPreviews, ImportPreviewOutput<Channel> channelImportPreview, string sheetName, IEnumerable<IDictionary<string, object>> rows)
|
||||
public void SetChannelData(HashSet<long>? dataScope, Dictionary<string, Channel> channelDicts, Dictionary<string, ImportPreviewOutputBase> ImportPreviews, string sheetName, IEnumerable<IDictionary<string, object>> rows)
|
||||
{
|
||||
#region sheet
|
||||
|
||||
if (sheetName == ExportString.ChannelName)
|
||||
{
|
||||
int row = 1;
|
||||
ImportPreviewOutput<Channel> importPreviewOutput = new();
|
||||
ImportPreviewListOutput<Channel> importPreviewOutput = new();
|
||||
ImportPreviews.Add(sheetName, importPreviewOutput);
|
||||
channelImportPreview = importPreviewOutput;
|
||||
List<Channel> channels = new();
|
||||
var type = typeof(Channel);
|
||||
// 获取目标类型的所有属性,并根据是否需要过滤 IgnoreExcelAttribute 进行筛选
|
||||
@@ -432,7 +457,7 @@ internal sealed class ChannelService : BaseService<Channel>, IChannelService
|
||||
return;
|
||||
}
|
||||
});
|
||||
importPreviewOutput.Data = channels.ToDictionary(a => a.Name);
|
||||
importPreviewOutput.Data = channels.DistinctBy(a => a.Name).ToList();
|
||||
}
|
||||
|
||||
#endregion sheet
|
||||
|
@@ -18,20 +18,17 @@ namespace ThingsGateway.Gateway.Application;
|
||||
public static class ChannelServiceHelpers
|
||||
{
|
||||
|
||||
public static USheetDatas ExportChannel(IEnumerable<Channel> models)
|
||||
public static USheetDatas ExportChannel(IEnumerable<Channel> channels)
|
||||
{
|
||||
var data = ExportChannelCore(models);
|
||||
return USheetDataHelpers.GetUSheetDatas(data);
|
||||
|
||||
var rows = ExportRows(channels); // IEnumerable 延迟执行
|
||||
var sheets = WrapAsSheet(ExportString.ChannelName, rows);
|
||||
return USheetDataHelpers.GetUSheetDatas(sheets);
|
||||
}
|
||||
|
||||
|
||||
internal static Dictionary<string, object> ExportChannelCore(IEnumerable<Channel>? data)
|
||||
internal static IEnumerable<Dictionary<string, object>> ExportRows(IEnumerable<Channel>? data)
|
||||
{
|
||||
//总数据
|
||||
Dictionary<string, object> sheets = new();
|
||||
//通道页
|
||||
List<Dictionary<string, object>> channelExports = new();
|
||||
if (data == null)
|
||||
yield break;
|
||||
|
||||
#region 列名称
|
||||
|
||||
@@ -58,23 +55,74 @@ public static class ChannelServiceHelpers
|
||||
|
||||
foreach (var device in data)
|
||||
{
|
||||
Dictionary<string, object> channelExport = new();
|
||||
foreach (var item in propertyInfos)
|
||||
Dictionary<string, object> row = new();
|
||||
foreach (var prop in propertyInfos)
|
||||
{
|
||||
//描述
|
||||
var desc = type.GetPropertyDisplayName(item.Name);
|
||||
//数据源增加
|
||||
channelExport.Add(desc ?? item.Name, item.GetValue(device)?.ToString());
|
||||
var desc = type.GetPropertyDisplayName(prop.Name);
|
||||
row.Add(desc ?? prop.Name, prop.GetValue(device)?.ToString());
|
||||
}
|
||||
|
||||
//添加完整设备信息
|
||||
channelExports.Add(channelExport);
|
||||
yield return row;
|
||||
}
|
||||
//添加设备页
|
||||
sheets.Add(ExportString.ChannelName, channelExports);
|
||||
return sheets;
|
||||
}
|
||||
|
||||
internal static async IAsyncEnumerable<Dictionary<string, object>> ExportRows(IAsyncEnumerable<Channel>? data)
|
||||
{
|
||||
if (data == null)
|
||||
yield break;
|
||||
|
||||
#region 列名称
|
||||
|
||||
var type = typeof(Channel);
|
||||
var propertyInfos = type.GetRuntimeProperties().Where(a => a.GetCustomAttribute<IgnoreExcelAttribute>(false) == null)
|
||||
.OrderBy(
|
||||
a =>
|
||||
{
|
||||
var order = a.GetCustomAttribute<AutoGenerateColumnAttribute>()?.Order ?? int.MaxValue; ;
|
||||
if (order < 0)
|
||||
{
|
||||
order = order + 10000000;
|
||||
}
|
||||
else if (order == 0)
|
||||
{
|
||||
order = 10000000;
|
||||
}
|
||||
return order;
|
||||
}
|
||||
)
|
||||
;
|
||||
|
||||
#endregion 列名称
|
||||
var enumerator = data.GetAsyncEnumerator();
|
||||
while (await enumerator.MoveNextAsync().ConfigureAwait(false))
|
||||
{
|
||||
var device = enumerator.Current;
|
||||
{
|
||||
Dictionary<string, object> row = new();
|
||||
foreach (var prop in propertyInfos)
|
||||
{
|
||||
var desc = type.GetPropertyDisplayName(prop.Name);
|
||||
row.Add(desc ?? prop.Name, prop.GetValue(device)?.ToString());
|
||||
}
|
||||
yield return row;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static Dictionary<string, object> WrapAsSheet(string sheetName, IEnumerable<IDictionary<string, object>> rows)
|
||||
{
|
||||
return new Dictionary<string, object>
|
||||
{
|
||||
[sheetName] = rows
|
||||
};
|
||||
}
|
||||
|
||||
internal static Dictionary<string, object> WrapAsSheet(string sheetName, IAsyncEnumerable<IDictionary<string, object>> rows)
|
||||
{
|
||||
return new Dictionary<string, object>
|
||||
{
|
||||
[sheetName] = rows
|
||||
};
|
||||
}
|
||||
|
||||
public static async Task<Dictionary<string, ImportPreviewOutputBase>> ImportAsync(USheetDatas uSheetDatas)
|
||||
{
|
||||
@@ -85,7 +133,6 @@ public static class ChannelServiceHelpers
|
||||
//导入检验结果
|
||||
Dictionary<string, ImportPreviewOutputBase> ImportPreviews = new();
|
||||
//设备页
|
||||
ImportPreviewOutput<Channel> channelImportPreview = new();
|
||||
|
||||
var sheetNames = uSheetDatas.sheets.Keys.ToList();
|
||||
foreach (var sheetName in sheetNames)
|
||||
@@ -108,10 +155,11 @@ public static class ChannelServiceHelpers
|
||||
rows.Add(expando);
|
||||
}
|
||||
|
||||
GlobalData.ChannelService.SetChannelData(dataScope, channelDicts, ImportPreviews, channelImportPreview, sheetName, rows);
|
||||
if (channelImportPreview.HasError)
|
||||
GlobalData.ChannelService.SetChannelData(dataScope, channelDicts, ImportPreviews, sheetName, rows);
|
||||
var data = ImportPreviews?.FirstOrDefault().Value;
|
||||
if (data?.HasError == true)
|
||||
{
|
||||
throw new(channelImportPreview.Results.FirstOrDefault(a => !a.Success).ErrorMessage ?? "error");
|
||||
throw new(data.Results.FirstOrDefault(a => !a.Success).ErrorMessage ?? "error");
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -54,7 +54,7 @@ public interface IChannelRuntimeService
|
||||
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(List<Channel> data);
|
||||
Task<MemoryStream> ExportMemoryStream(IEnumerable<Channel> data);
|
||||
Task RestartChannelAsync(IEnumerable<ChannelRuntime> oldChannelRuntimes);
|
||||
Task<bool> CopyAsync(List<Channel> models, Dictionary<Device, List<Variable>> devices, bool restart, CancellationToken cancellationToken);
|
||||
|
||||
|
@@ -53,7 +53,7 @@ internal interface IChannelService
|
||||
/// </summary>
|
||||
/// <param name="data">通道数据</param>
|
||||
/// <returns>内存流</returns>
|
||||
Task<MemoryStream> ExportMemoryStream(List<Channel> data);
|
||||
Task<MemoryStream> ExportMemoryStream(IEnumerable<Channel> data);
|
||||
|
||||
/// <summary>
|
||||
/// 从缓存/数据库获取全部信息
|
||||
@@ -95,7 +95,7 @@ internal interface IChannelService
|
||||
Task<bool> BatchSaveAsync(List<Channel> input, ItemChangedType type);
|
||||
|
||||
|
||||
void SetChannelData(HashSet<long>? dataScope, Dictionary<string, Channel> channelDicts, Dictionary<string, ImportPreviewOutputBase> ImportPreviews, ImportPreviewOutput<Channel> channelImportPreview, string sheetName, IEnumerable<IDictionary<string, object>> rows);
|
||||
void SetChannelData(HashSet<long>? dataScope, Dictionary<string, Channel> channelDicts, Dictionary<string, ImportPreviewOutputBase> ImportPreviews, string sheetName, IEnumerable<IDictionary<string, object>> rows);
|
||||
|
||||
/// <summary>
|
||||
/// 保存是否输出日志和日志等级
|
||||
|
@@ -218,6 +218,14 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
|
||||
/// </summary>
|
||||
/// <param name="exportFilter">查询条件</param>
|
||||
public async Task<QueryData<Device>> PageAsync(ExportFilter exportFilter)
|
||||
{
|
||||
var whereQuery = await GetWhereQueryFunc(exportFilter).ConfigureAwait(false);
|
||||
|
||||
return await QueryAsync(exportFilter.QueryPageOptions, whereQuery
|
||||
, exportFilter.FilterKeyValueAction).ConfigureAwait(false);
|
||||
|
||||
}
|
||||
private async Task<Func<ISugarQueryable<Device>, ISugarQueryable<Device>>> GetWhereQueryFunc(ExportFilter exportFilter)
|
||||
{
|
||||
HashSet<long>? channel = null;
|
||||
if (!exportFilter.PluginName.IsNullOrWhiteSpace())
|
||||
@@ -230,16 +238,38 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
|
||||
channel = (await _channelService.GetAllAsync().ConfigureAwait(false)).Where(a => pluginInfo.Contains(a.PluginName)).Select(a => a.Id).ToHashSet();
|
||||
}
|
||||
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
|
||||
return await QueryAsync(exportFilter.QueryPageOptions, a => a
|
||||
var whereQuery = (ISugarQueryable<Device> a) => a
|
||||
.WhereIF(channel != null, a => channel.Contains(a.ChannelId))
|
||||
.WhereIF(exportFilter.DeviceId != null, a => a.Id == exportFilter.DeviceId)
|
||||
.WhereIF(!exportFilter.QueryPageOptions.SearchText.IsNullOrWhiteSpace(), a => a.Name.Contains(exportFilter.QueryPageOptions.SearchText!))
|
||||
.WhereIF(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.CreateOrgId))//在指定机构列表查询
|
||||
.WhereIF(dataScope?.Count == 0, u => u.CreateUserId == UserManager.UserId)
|
||||
, exportFilter.FilterKeyValueAction).ConfigureAwait(false);
|
||||
|
||||
.WhereIF(dataScope?.Count == 0, u => u.CreateUserId == UserManager.UserId);
|
||||
return whereQuery;
|
||||
}
|
||||
|
||||
private async Task<Func<IEnumerable<Device>, IEnumerable<Device>>> GetWhereEnumerableFunc(ExportFilter exportFilter)
|
||||
{
|
||||
HashSet<long>? channel = null;
|
||||
if (!exportFilter.PluginName.IsNullOrWhiteSpace())
|
||||
{
|
||||
channel = (await _channelService.GetAllAsync().ConfigureAwait(false)).Where(a => a.PluginName == exportFilter.PluginName).Select(a => a.Id).ToHashSet();
|
||||
}
|
||||
if (exportFilter.PluginType != null)
|
||||
{
|
||||
var pluginInfo = GlobalData.PluginService.GetList(exportFilter.PluginType).Select(a => a.FullName).ToHashSet();
|
||||
channel = (await _channelService.GetAllAsync().ConfigureAwait(false)).Where(a => pluginInfo.Contains(a.PluginName)).Select(a => a.Id).ToHashSet();
|
||||
}
|
||||
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
|
||||
var whereQuery = (IEnumerable<Device> a) => a
|
||||
.WhereIF(channel != null, a => channel.Contains(a.ChannelId))
|
||||
.WhereIF(exportFilter.DeviceId != null, a => a.Id == exportFilter.DeviceId)
|
||||
.WhereIF(!exportFilter.QueryPageOptions.SearchText.IsNullOrWhiteSpace(), a => a.Name.Contains(exportFilter.QueryPageOptions.SearchText!))
|
||||
.WhereIF(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.CreateOrgId))//在指定机构列表查询
|
||||
.WhereIF(dataScope?.Count == 0, u => u.CreateUserId == UserManager.UserId);
|
||||
return whereQuery;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 保存设备
|
||||
/// </summary>
|
||||
@@ -298,18 +328,52 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
|
||||
public async Task<Dictionary<string, object>> ExportDeviceAsync(ExportFilter exportFilter)
|
||||
{
|
||||
//导出
|
||||
var data = await PageAsync(exportFilter).ConfigureAwait(false);
|
||||
var sheets = await DeviceServiceHelpers.ExportCoreAsync(data.Items).ConfigureAwait(false);
|
||||
var devices = await GetAsyncEnumerableData(exportFilter).ConfigureAwait(false);
|
||||
var plugins = await GetAsyncEnumerableData(exportFilter).ConfigureAwait(false);
|
||||
var devicesSql = await GetEnumerableData(exportFilter).ConfigureAwait(false);
|
||||
var deviceDicts = (await GlobalData.DeviceService.GetAllAsync().ConfigureAwait(false)).ToDictionary(a => a.Id);
|
||||
var channelDicts = (await GlobalData.ChannelService.GetAllAsync().ConfigureAwait(false)).ToDictionary(a => a.Id);
|
||||
var pluginSheetNames = devicesSql.Select(a => a.ChannelId).ToList().Select(a =>
|
||||
{
|
||||
channelDicts.TryGetValue(a, out var channel);
|
||||
var pluginKey = channel?.PluginName;
|
||||
return pluginKey;
|
||||
}).ToHashSet();
|
||||
|
||||
var sheets = DeviceServiceHelpers.ExportSheets(devices, plugins, deviceDicts, channelDicts, pluginSheetNames); // IEnumerable 延迟执行
|
||||
|
||||
return sheets;
|
||||
}
|
||||
private async Task<IAsyncEnumerable<Device>> GetAsyncEnumerableData(ExportFilter exportFilter)
|
||||
{
|
||||
var whereQuery = await GetEnumerableData(exportFilter).ConfigureAwait(false);
|
||||
return whereQuery.GetAsyncEnumerable();
|
||||
}
|
||||
private async Task<ISugarQueryable<Device>> GetEnumerableData(ExportFilter exportFilter)
|
||||
{
|
||||
var db = GetDB();
|
||||
var whereQuery = await GetWhereQueryFunc(exportFilter).ConfigureAwait(false);
|
||||
|
||||
return GetQuery(db, exportFilter.QueryPageOptions, whereQuery, exportFilter.FilterKeyValueAction);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 导出文件
|
||||
/// </summary>
|
||||
[OperDesc("ExportDevice", isRecordPar: false, localizerType: typeof(Device))]
|
||||
public async Task<MemoryStream> ExportMemoryStream(IEnumerable<Device>? data, string channelName = null, string plugin = null)
|
||||
public async Task<MemoryStream> ExportMemoryStream(List<Device>? models, string channelName = null, string plugin = null)
|
||||
{
|
||||
var sheets = await DeviceServiceHelpers.ExportCoreAsync(data, channelName, plugin).ConfigureAwait(false);
|
||||
var deviceDicts = (await GlobalData.DeviceService.GetAllAsync().ConfigureAwait(false)).ToDictionary(a => a.Id);
|
||||
var channelDicts = (await GlobalData.ChannelService.GetAllAsync().ConfigureAwait(false)).ToDictionary(a => a.Id);
|
||||
var pluginSheetNames = models.Select(a => a.ChannelId).Select(a =>
|
||||
{
|
||||
channelDicts.TryGetValue(a, out var channel);
|
||||
var pluginKey = channel?.PluginName;
|
||||
return pluginKey;
|
||||
}).ToHashSet();
|
||||
|
||||
var sheets = DeviceServiceHelpers.ExportSheets(models, deviceDicts, channelDicts, pluginSheetNames, channelName);
|
||||
var memoryStream = new MemoryStream();
|
||||
await memoryStream.SaveAsAsync(sheets).ConfigureAwait(false);
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
@@ -343,8 +407,16 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
|
||||
ManageHelper.CheckDeviceCount(insertData.Count);
|
||||
|
||||
using var db = GetDB();
|
||||
await db.BulkCopyAsync(insertData, 100000).ConfigureAwait(false);
|
||||
await db.BulkUpdateAsync(upData, 100000).ConfigureAwait(false);
|
||||
if (GlobalData.HardwareJob.HardwareInfo.MachineInfo.AvailableMemory > 2 * 1024 * 1024)
|
||||
{
|
||||
await db.BulkCopyAsync(insertData, 200000).ConfigureAwait(false);
|
||||
await db.BulkUpdateAsync(upData, 200000).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await db.BulkCopyAsync(insertData, 10000).ConfigureAwait(false);
|
||||
await db.BulkUpdateAsync(upData, 10000).ConfigureAwait(false);
|
||||
}
|
||||
DeleteDeviceFromCache();
|
||||
return devices.Select(a => a.Id).ToHashSet();
|
||||
}
|
||||
|
@@ -21,106 +21,255 @@ public static class DeviceServiceHelpers
|
||||
|
||||
public static async Task<USheetDatas> ExportDeviceAsync(IEnumerable<Device> models)
|
||||
{
|
||||
|
||||
var data = await ExportCoreAsync(models).ConfigureAwait(false);
|
||||
var deviceDicts = (await GlobalData.DeviceService.GetAllAsync().ConfigureAwait(false)).ToDictionary(a => a.Id);
|
||||
var channelDicts = (await GlobalData.ChannelService.GetAllAsync().ConfigureAwait(false)).ToDictionary(a => a.Id);
|
||||
var pluginSheetNames = models.Select(a => a.ChannelId).Select(a =>
|
||||
{
|
||||
channelDicts.TryGetValue(a, out var channel);
|
||||
var pluginKey = channel?.PluginName;
|
||||
return pluginKey;
|
||||
}).ToHashSet();
|
||||
var data = ExportSheets(models, deviceDicts, channelDicts, pluginSheetNames); // IEnumerable 延迟执行
|
||||
return USheetDataHelpers.GetUSheetDatas(data);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static async Task<Dictionary<string, object>> ExportCoreAsync(IEnumerable<Device>? data, string channelName = null, string plugin = null)
|
||||
public static Dictionary<string, object> ExportSheets(
|
||||
IEnumerable<Device>? data,
|
||||
Dictionary<long, Device>? deviceDicts,
|
||||
Dictionary<long, Channel> channelDicts,
|
||||
HashSet<string> pluginSheetNames,
|
||||
string? channelName = null)
|
||||
{
|
||||
if (data?.Any() != true)
|
||||
{
|
||||
data = new List<Device>();
|
||||
}
|
||||
var deviceDicts = (await GlobalData.DeviceService.GetAllAsync().ConfigureAwait(false)).ToDictionary(a => a.Id);
|
||||
var channelDicts = (await GlobalData.ChannelService.GetAllAsync().ConfigureAwait(false)).ToDictionary(a => a.Id);
|
||||
//总数据
|
||||
Dictionary<string, object> sheets = new();
|
||||
//设备页
|
||||
List<Dictionary<string, object>> deviceExports = new();
|
||||
//设备附加属性,转成Dict<表名,List<Dict<列名,列数据>>>的形式
|
||||
Dictionary<string, List<Dictionary<string, object>>> devicePropertys = new();
|
||||
|
||||
|
||||
var result = new Dictionary<string, object>();
|
||||
result.Add(ExportString.DeviceName, GetDeviceSheets(data, deviceDicts, channelDicts, channelName));
|
||||
ConcurrentDictionary<string, (object, Dictionary<string, PropertyInfo>)> propertysDict = new();
|
||||
|
||||
#region 列名称
|
||||
|
||||
var type = typeof(Device);
|
||||
var propertyInfos = type.GetRuntimeProperties().Where(a => a.GetCustomAttribute<IgnoreExcelAttribute>(false) == null)
|
||||
.OrderBy(
|
||||
a =>
|
||||
{
|
||||
var order = a.GetCustomAttribute<AutoGenerateColumnAttribute>()?.Order ?? int.MaxValue; ;
|
||||
if (order < 0)
|
||||
{
|
||||
order = order + 10000000;
|
||||
}
|
||||
else if (order == 0)
|
||||
{
|
||||
order = 10000000;
|
||||
}
|
||||
return order;
|
||||
}
|
||||
)
|
||||
;
|
||||
|
||||
#endregion 列名称
|
||||
|
||||
foreach (var device in data)
|
||||
foreach (var plugin in pluginSheetNames)
|
||||
{
|
||||
Dictionary<string, object> devExport = new();
|
||||
deviceDicts.TryGetValue(device.RedundantDeviceId ?? 0, out var redundantDevice);
|
||||
channelDicts.TryGetValue(device.ChannelId, out var channel);
|
||||
var filtered = FilterPluginDevices(data, plugin, channelDicts);
|
||||
var filtResult = PluginServiceUtil.GetFileNameAndTypeName(plugin);
|
||||
var pluginSheets = GetPluginSheets(filtered, propertysDict, plugin);
|
||||
result.Add(filtResult.TypeName, pluginSheets);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
devExport.Add(ExportString.ChannelName, channel?.Name ?? channelName);
|
||||
|
||||
foreach (var item in propertyInfos)
|
||||
{
|
||||
//描述
|
||||
var desc = type.GetPropertyDisplayName(item.Name);
|
||||
//数据源增加
|
||||
devExport.Add(desc ?? item.Name, item.GetValue(device)?.ToString());
|
||||
}
|
||||
|
||||
//设备实体没有包含冗余设备名称,手动插入
|
||||
devExport.Add(ExportString.RedundantDeviceName, redundantDevice?.Name);
|
||||
|
||||
//添加完整设备信息
|
||||
deviceExports.Add(devExport);
|
||||
|
||||
#region 插件sheet
|
||||
|
||||
//插件属性
|
||||
//单个设备的行数据
|
||||
Dictionary<string, object> driverInfo = new();
|
||||
|
||||
var propDict = device.DevicePropertys;
|
||||
if (propertysDict.TryGetValue(channel?.PluginName ?? plugin, out var propertys))
|
||||
public static Dictionary<string, object> ExportSheets(
|
||||
IAsyncEnumerable<Device>? data1,
|
||||
IAsyncEnumerable<Device>? data2,
|
||||
Dictionary<long, Device>? deviceDicts,
|
||||
Dictionary<long, Channel> channelDicts,
|
||||
HashSet<string> pluginSheetNames,
|
||||
string? channelName = null)
|
||||
{
|
||||
if (data1 == null || data2 == null)
|
||||
return new();
|
||||
|
||||
var result = new Dictionary<string, object>();
|
||||
result.Add(ExportString.DeviceName, GetDeviceSheets(data1, deviceDicts, channelDicts, channelName));
|
||||
ConcurrentDictionary<string, (object, Dictionary<string, PropertyInfo>)> propertysDict = new();
|
||||
|
||||
|
||||
foreach (var plugin in pluginSheetNames)
|
||||
{
|
||||
var filtered = FilterPluginDevices(data2, plugin, channelDicts);
|
||||
var filtResult = PluginServiceUtil.GetFileNameAndTypeName(plugin);
|
||||
var pluginSheets = GetPluginSheets(filtered, propertysDict, plugin);
|
||||
result.Add(filtResult.TypeName, pluginSheets);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
static IAsyncEnumerable<Device> FilterPluginDevices(IAsyncEnumerable<Device> data, string plugin, Dictionary<long, Channel> channelDicts)
|
||||
{
|
||||
return data.Where(device =>
|
||||
{
|
||||
if (channelDicts.TryGetValue(device.ChannelId, out var channel))
|
||||
{
|
||||
if (channel.PluginName == plugin)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
var driverProperties = GlobalData.PluginService.GetDriver(channel?.PluginName ?? plugin).DriverProperties;
|
||||
propertys.Item1 = driverProperties;
|
||||
var driverPropertyType = driverProperties.GetType();
|
||||
propertys.Item2 = driverPropertyType.GetRuntimeProperties()
|
||||
.Where(a => a.GetCustomAttribute<DynamicPropertyAttribute>() != null)
|
||||
.ToDictionary(a => driverPropertyType.GetPropertyDisplayName(a.Name, a => a.GetCustomAttribute<DynamicPropertyAttribute>(true)?.Description), a => a);
|
||||
propertysDict.TryAdd(channel?.PluginName ?? plugin, propertys);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
static IEnumerable<Device> FilterPluginDevices(IEnumerable<Device> data, string plugin, Dictionary<long, Channel> channelDicts)
|
||||
{
|
||||
return data.Where(device =>
|
||||
{
|
||||
if (channelDicts.TryGetValue(device.ChannelId, out var channel))
|
||||
{
|
||||
if (channel.PluginName == plugin)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
static IEnumerable<Dictionary<string, object>> GetDeviceSheets(
|
||||
IEnumerable<Device> data,
|
||||
Dictionary<long, Device>? deviceDicts,
|
||||
Dictionary<long, Channel> channelDicts,
|
||||
string? channelName)
|
||||
{
|
||||
var type = typeof(Device);
|
||||
var propertyInfos = type.GetRuntimeProperties()
|
||||
.Where(a => a.GetCustomAttribute<IgnoreExcelAttribute>(false) == null)
|
||||
.OrderBy(a =>
|
||||
{
|
||||
var order = a.GetCustomAttribute<AutoGenerateColumnAttribute>()?.Order ?? int.MaxValue;
|
||||
if (order < 0) order += 10000000;
|
||||
else if (order == 0) order = 10000000;
|
||||
return order;
|
||||
});
|
||||
|
||||
}
|
||||
foreach (var device in data)
|
||||
{
|
||||
yield return GetDeviceRows(device, propertyInfos, type, deviceDicts, channelDicts, channelName);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static IEnumerable<Dictionary<string, object>> GetPluginSheets(
|
||||
IEnumerable<Device> data,
|
||||
ConcurrentDictionary<string, (object, Dictionary<string, PropertyInfo>)> propertysDict,
|
||||
string? plugin)
|
||||
{
|
||||
|
||||
foreach (var device in data)
|
||||
{
|
||||
var row = GetPluginRows(device, plugin, propertysDict);
|
||||
if (row != null)
|
||||
{
|
||||
yield return row;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static async IAsyncEnumerable<Dictionary<string, object>> GetDeviceSheets(
|
||||
IAsyncEnumerable<Device> data,
|
||||
Dictionary<long, Device>? deviceDicts,
|
||||
Dictionary<long, Channel> channelDicts,
|
||||
string? channelName)
|
||||
{
|
||||
var type = typeof(Device);
|
||||
var propertyInfos = type.GetRuntimeProperties()
|
||||
.Where(a => a.GetCustomAttribute<IgnoreExcelAttribute>(false) == null)
|
||||
.OrderBy(a =>
|
||||
{
|
||||
var order = a.GetCustomAttribute<AutoGenerateColumnAttribute>()?.Order ?? int.MaxValue;
|
||||
if (order < 0) order += 10000000;
|
||||
else if (order == 0) order = 10000000;
|
||||
return order;
|
||||
});
|
||||
|
||||
var enumerator = data.GetAsyncEnumerator();
|
||||
while (await enumerator.MoveNextAsync().ConfigureAwait(false))
|
||||
{
|
||||
var device = enumerator.Current;
|
||||
yield return GetDeviceRows(device, propertyInfos, type, deviceDicts, channelDicts, channelName);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static async IAsyncEnumerable<Dictionary<string, object>> GetPluginSheets(
|
||||
IAsyncEnumerable<Device> data,
|
||||
ConcurrentDictionary<string, (object, Dictionary<string, PropertyInfo>)> propertysDict,
|
||||
string? plugin)
|
||||
{
|
||||
|
||||
var enumerator = data.GetAsyncEnumerator();
|
||||
while (await enumerator.MoveNextAsync().ConfigureAwait(false))
|
||||
{
|
||||
|
||||
var device = enumerator.Current;
|
||||
var row = GetPluginRows(device, plugin, propertysDict);
|
||||
if (row != null)
|
||||
{
|
||||
yield return row;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Dictionary<string, object> GetDeviceRows(
|
||||
Device device,
|
||||
IEnumerable<PropertyInfo>? propertyInfos,
|
||||
Type type,
|
||||
Dictionary<long, Device>? deviceDicts,
|
||||
Dictionary<long, Channel>? channelDicts,
|
||||
string? channelName)
|
||||
{
|
||||
|
||||
Dictionary<string, object> devExport = new();
|
||||
deviceDicts.TryGetValue(device.RedundantDeviceId ?? 0, out var redundantDevice);
|
||||
channelDicts.TryGetValue(device.ChannelId, out var channel);
|
||||
|
||||
devExport.Add(ExportString.ChannelName, channel?.Name ?? channelName);
|
||||
|
||||
foreach (var item in propertyInfos)
|
||||
{
|
||||
//描述
|
||||
var desc = type.GetPropertyDisplayName(item.Name);
|
||||
//数据源增加
|
||||
devExport.Add(desc ?? item.Name, item.GetValue(device)?.ToString());
|
||||
}
|
||||
|
||||
//设备实体没有包含冗余设备名称,手动插入
|
||||
devExport.Add(ExportString.RedundantDeviceName, redundantDevice?.Name);
|
||||
return devExport;
|
||||
}
|
||||
|
||||
static Dictionary<string, object> GetPluginRows(Device device, string? plugin, ConcurrentDictionary<string, (object, Dictionary<string, PropertyInfo>)> propertysDict)
|
||||
{
|
||||
|
||||
Dictionary<string, object> driverInfo = new();
|
||||
var propDict = device.DevicePropertys;
|
||||
if (!propertysDict.TryGetValue(plugin, out var propertys))
|
||||
{
|
||||
try
|
||||
{
|
||||
var driverProperties = GlobalData.PluginService.GetDriver(plugin).DriverProperties;
|
||||
propertys.Item1 = driverProperties;
|
||||
var driverPropertyType = driverProperties.GetType();
|
||||
propertys.Item2 = driverPropertyType.GetRuntimeProperties()
|
||||
.Where(a => a.GetCustomAttribute<DynamicPropertyAttribute>() != null)
|
||||
.ToDictionary(a => driverPropertyType.GetPropertyDisplayName(a.Name, a => a.GetCustomAttribute<DynamicPropertyAttribute>(true)?.Description), a => a);
|
||||
propertysDict.TryAdd(plugin, propertys);
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
if (propertys.Item2 == null)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (propertys.Item2 != null)
|
||||
{
|
||||
|
||||
|
||||
if (propertys.Item2.Count > 0)
|
||||
{
|
||||
@@ -141,51 +290,13 @@ public static class DeviceServiceHelpers
|
||||
}
|
||||
}
|
||||
|
||||
var pluginName = PluginServiceUtil.GetFileNameAndTypeName(channel?.PluginName ?? plugin);
|
||||
if (devicePropertys.ContainsKey(pluginName.TypeName))
|
||||
{
|
||||
if (driverInfo.Count > 0)
|
||||
devicePropertys[pluginName.TypeName].Add(driverInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (driverInfo.Count > 0)
|
||||
devicePropertys.Add(pluginName.TypeName, new() { driverInfo });
|
||||
}
|
||||
|
||||
#endregion 插件sheet
|
||||
|
||||
if (driverInfo.Count > 0)
|
||||
return driverInfo;
|
||||
|
||||
}
|
||||
//添加设备页
|
||||
sheets.Add(ExportString.DeviceName, deviceExports);
|
||||
|
||||
//HASH
|
||||
foreach (var item in devicePropertys)
|
||||
{
|
||||
HashSet<string> allKeys = new();
|
||||
|
||||
foreach (var dict in item.Value)
|
||||
{
|
||||
foreach (var key in dict.Keys)
|
||||
{
|
||||
allKeys.Add(key);
|
||||
}
|
||||
}
|
||||
foreach (var dict in item.Value)
|
||||
{
|
||||
foreach (var key in allKeys)
|
||||
{
|
||||
if (!dict.ContainsKey(key))
|
||||
{
|
||||
// 添加缺失的键,并设置默认值
|
||||
dict.Add(key, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sheets.Add(item.Key, item.Value);
|
||||
}
|
||||
|
||||
return sheets;
|
||||
return null;
|
||||
}
|
||||
|
||||
public static async Task<Dictionary<string, ImportPreviewOutputBase>> ImportAsync(USheetDatas uSheetDatas)
|
||||
|
@@ -69,7 +69,7 @@ internal interface IDeviceService
|
||||
/// <param name="channelName">通道名称(可选)</param>
|
||||
/// <param name="plugin">插件名称(可选)</param>
|
||||
/// <returns>导出的内存流</returns>
|
||||
Task<MemoryStream> ExportMemoryStream(IEnumerable<Device>? data, string channelName = null, string plugin = null);
|
||||
Task<MemoryStream> ExportMemoryStream(List<Device>? data, string channelName = null, string plugin = null);
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有设备信息。
|
||||
|
@@ -11,9 +11,6 @@
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
using ThingsGateway.Gateway.Application.Extensions;
|
||||
using ThingsGateway.NewLife.Extension;
|
||||
|
||||
|
@@ -748,7 +748,7 @@ internal sealed class DeviceThreadManage : IAsyncDisposable, IDeviceThreadManage
|
||||
|
||||
|
||||
var num = Drivers.Count;
|
||||
foreach (var driver in Drivers.Select(a => a.Value).ToList())
|
||||
foreach (var driver in Drivers.Select(a => a.Value).Where(a => a != null).ToList())
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@@ -42,7 +42,9 @@ internal sealed class GatewayMonitorHostedService : BackgroundService, IGatewayM
|
||||
//网关启动时,获取所有通道
|
||||
var channelRuntimes = (await GlobalData.ChannelService.GetAllAsync().ConfigureAwait(false)).AdaptListChannelRuntime();
|
||||
var deviceRuntimes = (await GlobalData.DeviceService.GetAllAsync().ConfigureAwait(false)).AdaptListDeviceRuntime();
|
||||
var variableRuntimes = (await GlobalData.VariableService.GetAllAsync().ConfigureAwait(false)).AdaptListVariableRuntime();
|
||||
|
||||
var variableRuntimes = GlobalData.VariableService.GetAllVariableRuntime();
|
||||
|
||||
foreach (var channelRuntime in channelRuntimes)
|
||||
{
|
||||
try
|
||||
|
@@ -8,6 +8,8 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using ThingsGateway.Authentication;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
internal static class ManageHelper
|
||||
@@ -21,26 +23,31 @@ internal static class ManageHelper
|
||||
|
||||
public static void CheckChannelCount(int addCount)
|
||||
{
|
||||
if (GlobalData.Channels.Count + addCount > ManageHelper.ChannelThreadOptions.MaxChannelCount)
|
||||
var data = GlobalData.Channels.Count + addCount;
|
||||
ProAuthentication.TryGetAuthorizeInfo(out var authorizeInfo);
|
||||
if (data > ManageHelper.ChannelThreadOptions.MaxChannelCount || data > authorizeInfo?.MaxChannelCount)
|
||||
{
|
||||
throw new Exception($"The number of channels exceeds the limit {ManageHelper.ChannelThreadOptions.MaxChannelCount}");
|
||||
throw new Exception($"The number of channels exceeds the limit {Math.Min(ManageHelper.ChannelThreadOptions.MaxChannelCount, authorizeInfo?.MaxChannelCount ?? 0)}");
|
||||
}
|
||||
}
|
||||
|
||||
public static void CheckDeviceCount(int addCount)
|
||||
{
|
||||
if (GlobalData.Devices.Count + addCount > ManageHelper.ChannelThreadOptions.MaxDeviceCount)
|
||||
var data = GlobalData.Devices.Count + addCount;
|
||||
ProAuthentication.TryGetAuthorizeInfo(out var authorizeInfo);
|
||||
if (data > ManageHelper.ChannelThreadOptions.MaxDeviceCount || data > authorizeInfo?.MaxDeviceCount)
|
||||
{
|
||||
throw new Exception($"The number of devices exceeds the limit {ManageHelper.ChannelThreadOptions.MaxDeviceCount}");
|
||||
throw new Exception($"The number of devices exceeds the limit {Math.Min(ManageHelper.ChannelThreadOptions.MaxDeviceCount, authorizeInfo?.MaxDeviceCount ?? 0)}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void CheckVariableCount(int addCount)
|
||||
{
|
||||
if (GlobalData.IdVariables.Count + addCount > ManageHelper.ChannelThreadOptions.MaxVariableCount)
|
||||
var data = GlobalData.IdVariables.Count + addCount;
|
||||
ProAuthentication.TryGetAuthorizeInfo(out var authorizeInfo);
|
||||
if (data > ManageHelper.ChannelThreadOptions.MaxVariableCount || data > authorizeInfo?.MaxVariableCount)
|
||||
{
|
||||
throw new Exception($"The number of variables exceeds the limit {ManageHelper.ChannelThreadOptions.MaxVariableCount}");
|
||||
throw new Exception($"The number of variables exceeds the limit {Math.Min(ManageHelper.ChannelThreadOptions.MaxVariableCount, authorizeInfo?.MaxVariableCount ?? 0)}");
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -30,7 +30,6 @@ namespace ThingsGateway.Gateway.Application
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(IBrowserFile browserFile);
|
||||
|
||||
Task<bool> SaveVariableAsync(Variable input, ItemChangedType type, bool restart, CancellationToken cancellationToken);
|
||||
void PreheatCache();
|
||||
|
||||
Task<MemoryStream> ExportMemoryStream(List<Variable> data, string devName);
|
||||
}
|
||||
|
@@ -58,7 +58,7 @@ internal interface IVariableService
|
||||
/// </summary>
|
||||
/// <param name="data">要导出的变量数据。</param>
|
||||
/// <param name="deviceName">设备名称(可选)。</param>
|
||||
Task<MemoryStream> ExportMemoryStream(IEnumerable<Variable> data, string deviceName = null);
|
||||
Task<MemoryStream> ExportMemoryStream(List<Variable> data, string deviceName = null);
|
||||
|
||||
/// <summary>
|
||||
/// 异步导出变量数据到文件流中。
|
||||
@@ -88,8 +88,6 @@ internal interface IVariableService
|
||||
/// <param name="exportFilter">查询分页选项</param>
|
||||
Task<QueryData<Variable>> PageAsync(ExportFilter exportFilter);
|
||||
|
||||
Task PreheatCache();
|
||||
|
||||
/// <summary>
|
||||
/// 异步预览导入的数据。
|
||||
/// </summary>
|
||||
@@ -113,5 +111,6 @@ internal interface IVariableService
|
||||
|
||||
Task<List<Variable>> GetByDeviceIdAsync(List<long> deviceIds);
|
||||
void DeleteVariableCache();
|
||||
Task<ImportPreviewOutput<Dictionary<string, Variable>>> SetVariableData(HashSet<long>? dataScope, Dictionary<string, Device> 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);
|
||||
ImportPreviewOutput<Dictionary<string, Variable>> SetVariableData(HashSet<long>? dataScope, Dictionary<string, Device> 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);
|
||||
List<VariableRuntime> GetAllVariableRuntime();
|
||||
}
|
||||
|
@@ -107,13 +107,13 @@ 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);
|
||||
}
|
||||
|
||||
@@ -136,14 +136,14 @@ public class VariableRuntimeService : IVariableRuntimeService
|
||||
|
||||
var result = await GlobalData.VariableService.DeleteVariableAsync(null).ConfigureAwait(false);
|
||||
|
||||
|
||||
ConcurrentHashSet<IDriver> changedDriver = new();
|
||||
var variableIds = GlobalData.IdVariables.Select(a => a.Key).ToHashSet();
|
||||
RuntimeServiceHelper.AddBusinessChangedDriver(variableIds, changedDriver);
|
||||
RuntimeServiceHelper.VariableRuntimesDispose(variableIds);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -286,8 +286,6 @@ public class VariableRuntimeService : IVariableRuntimeService
|
||||
}
|
||||
}
|
||||
|
||||
public void PreheatCache() => GlobalData.VariableService.PreheatCache();
|
||||
|
||||
|
||||
public Task<MemoryStream> ExportMemoryStream(List<Variable> data, string deviceName) => GlobalData.VariableService.ExportMemoryStream(data, deviceName);
|
||||
|
||||
|
@@ -21,7 +21,6 @@ using System.Text;
|
||||
|
||||
using ThingsGateway.Extension.Generic;
|
||||
using ThingsGateway.Foundation.Extension.Dynamic;
|
||||
using ThingsGateway.NewLife;
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
using TouchSocket.Core;
|
||||
@@ -212,9 +211,19 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
|
||||
var result = await db.UseTranAsync(async () =>
|
||||
{
|
||||
await db.BulkCopyAsync(newChannels, 100000).ConfigureAwait(false);
|
||||
await db.BulkCopyAsync(newDevices, 100000).ConfigureAwait(false);
|
||||
await db.BulkCopyAsync(newVariables, 100000).ConfigureAwait(false);
|
||||
if (GlobalData.HardwareJob.HardwareInfo.MachineInfo.AvailableMemory > 2 * 1024 * 1024)
|
||||
{
|
||||
await db.BulkCopyAsync(newChannels, 200000).ConfigureAwait(false);
|
||||
await db.BulkCopyAsync(newDevices, 200000).ConfigureAwait(false);
|
||||
await db.BulkCopyAsync(newVariables, 200000).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await db.BulkCopyAsync(newChannels, 10000).ConfigureAwait(false);
|
||||
await db.BulkCopyAsync(newDevices, 10000).ConfigureAwait(false);
|
||||
await db.BulkCopyAsync(newVariables, 10000).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
}).ConfigureAwait(false);
|
||||
if (result.IsSuccess)//如果成功了
|
||||
{
|
||||
@@ -372,6 +381,12 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
/// </summary>
|
||||
/// <param name="exportFilter">查询条件</param>
|
||||
public async Task<QueryData<Variable>> PageAsync(ExportFilter exportFilter)
|
||||
{
|
||||
var whereQuery = await GetWhereQueryFunc(exportFilter).ConfigureAwait(false);
|
||||
|
||||
return await QueryAsync(exportFilter.QueryPageOptions, whereQuery).ConfigureAwait(false);
|
||||
}
|
||||
private async Task<Func<ISugarQueryable<Variable>, ISugarQueryable<Variable>>> GetWhereQueryFunc(ExportFilter exportFilter)
|
||||
{
|
||||
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
|
||||
HashSet<long>? deviceId = null;
|
||||
@@ -384,7 +399,7 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
{
|
||||
deviceId = (await _deviceService.GetAllAsync().ConfigureAwait(false)).Where(a => a.ChannelId == exportFilter.ChannelId).Select(a => a.Id).ToHashSet();
|
||||
}
|
||||
return await QueryAsync(exportFilter.QueryPageOptions, a => a
|
||||
var whereQuery = (ISugarQueryable<Variable> a) => a
|
||||
.WhereIF(!exportFilter.QueryPageOptions.SearchText.IsNullOrWhiteSpace(), a => a.Name.Contains(exportFilter.QueryPageOptions.SearchText!))
|
||||
.WhereIF(exportFilter.PluginType == PluginTypeEnum.Collect, a => a.DeviceId == exportFilter.DeviceId)
|
||||
.WhereIF(deviceId != null, a => deviceId.Contains(a.DeviceId))
|
||||
@@ -393,9 +408,34 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
.WhereIF(dataScope?.Count == 0, u => u.CreateUserId == UserManager.UserId)
|
||||
|
||||
|
||||
.WhereIF(exportFilter.PluginType == PluginTypeEnum.Business, u => SqlFunc.JsonLike(u.VariablePropertys, exportFilter.DeviceId.ToString()))
|
||||
.WhereIF(exportFilter.PluginType == PluginTypeEnum.Business, u => SqlFunc.JsonLike(u.VariablePropertys, exportFilter.DeviceId.ToString()));
|
||||
return whereQuery;
|
||||
}
|
||||
|
||||
).ConfigureAwait(false);
|
||||
private async Task<Func<IEnumerable<Variable>, IEnumerable<Variable>>> GetWhereEnumerableFunc(ExportFilter exportFilter)
|
||||
{
|
||||
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
|
||||
HashSet<long>? deviceId = null;
|
||||
if (!exportFilter.PluginName.IsNullOrWhiteSpace())
|
||||
{
|
||||
var channel = (await _channelService.GetAllAsync().ConfigureAwait(false)).Where(a => a.PluginName == exportFilter.PluginName).Select(a => a.Id).ToHashSet();
|
||||
deviceId = (await _deviceService.GetAllAsync().ConfigureAwait(false)).Where(a => channel.Contains(a.ChannelId)).Select(a => a.Id).ToHashSet();
|
||||
}
|
||||
else if (exportFilter.ChannelId != null)
|
||||
{
|
||||
deviceId = (await _deviceService.GetAllAsync().ConfigureAwait(false)).Where(a => a.ChannelId == exportFilter.ChannelId).Select(a => a.Id).ToHashSet();
|
||||
}
|
||||
var whereQuery = (IEnumerable<Variable> a) => a
|
||||
.WhereIF(!exportFilter.QueryPageOptions.SearchText.IsNullOrWhiteSpace(), a => a.Name.Contains(exportFilter.QueryPageOptions.SearchText!))
|
||||
.WhereIF(exportFilter.PluginType == PluginTypeEnum.Collect, a => a.DeviceId == exportFilter.DeviceId)
|
||||
.WhereIF(deviceId != null, a => deviceId.Contains(a.DeviceId))
|
||||
|
||||
.WhereIF(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.CreateOrgId))//在指定机构列表查询
|
||||
.WhereIF(dataScope?.Count == 0, u => u.CreateUserId == UserManager.UserId)
|
||||
|
||||
|
||||
.WhereIF(exportFilter.PluginType == PluginTypeEnum.Business, u => SqlFunc.JsonLike(u.VariablePropertys, exportFilter.DeviceId.ToString()));
|
||||
return whereQuery;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -424,15 +464,36 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
App.CacheService.Remove(ThingsGatewayCacheConst.Cache_Variable);
|
||||
}
|
||||
|
||||
|
||||
public List<VariableRuntime> GetAllVariableRuntime()
|
||||
{
|
||||
using (var db = DbContext.GetDB<Variable>())
|
||||
{
|
||||
var deviceVariables = db.Queryable<Variable>().OrderBy(a => a.Id).GetEnumerable();
|
||||
return deviceVariables.AdaptListVariableRuntime();
|
||||
}
|
||||
}
|
||||
#region 导出
|
||||
|
||||
/// <summary>
|
||||
/// 导出文件
|
||||
/// </summary>
|
||||
[OperDesc("ExportVariable", isRecordPar: false, localizerType: typeof(Variable))]
|
||||
public async Task<MemoryStream> ExportMemoryStream(IEnumerable<Variable> data, string deviceName = null)
|
||||
public async Task<MemoryStream> ExportMemoryStream(List<Variable> variables, string deviceName = null)
|
||||
{
|
||||
var sheets = await VariableServiceHelpers.ExportCoreAsync(data, deviceName).ConfigureAwait(false);
|
||||
var deviceDicts = (await GlobalData.DeviceService.GetAllAsync().ConfigureAwait(false)).ToDictionary(a => a.Id);
|
||||
var channelDicts = (await GlobalData.ChannelService.GetAllAsync().ConfigureAwait(false)).ToDictionary(a => a.Id);
|
||||
var pluginSheetNames = variables.Where(a => a.VariablePropertys?.Count > 0).SelectMany(a => a.VariablePropertys).Select(a =>
|
||||
{
|
||||
if (deviceDicts.TryGetValue(a.Key, out var device) && channelDicts.TryGetValue(device.ChannelId, out var channel))
|
||||
{
|
||||
var pluginKey = channel?.PluginName;
|
||||
using var businessBase = (BusinessBase)GlobalData.PluginService.GetDriver(pluginKey);
|
||||
return new KeyValuePair<string, VariablePropertyBase>(pluginKey, businessBase.VariablePropertys);
|
||||
}
|
||||
return new KeyValuePair<string, VariablePropertyBase>(string.Empty, null);
|
||||
}).Where(a => a.Value != null).DistinctBy(a => a.Key).ToDictionary();
|
||||
var sheets = VariableServiceHelpers.ExportSheets(variables, deviceDicts, channelDicts, pluginSheetNames); // IEnumerable 延迟执行
|
||||
|
||||
var memoryStream = new MemoryStream();
|
||||
await memoryStream.SaveAsAsync(sheets).ConfigureAwait(false);
|
||||
@@ -446,11 +507,52 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
[OperDesc("ExportVariable", isRecordPar: false, localizerType: typeof(Variable))]
|
||||
public async Task<Dictionary<string, object>> ExportVariableAsync(ExportFilter exportFilter)
|
||||
{
|
||||
var data = (await PageAsync(exportFilter).ConfigureAwait(false));
|
||||
var sheets = await VariableServiceHelpers.ExportCoreAsync(data.Items, sortName: exportFilter.QueryPageOptions.SortName, sortOrder: exportFilter.QueryPageOptions.SortOrder).ConfigureAwait(false);
|
||||
return sheets;
|
||||
}
|
||||
if (GlobalData.HardwareJob.HardwareInfo.MachineInfo.AvailableMemory < 4 * 1024 * 1024)
|
||||
{
|
||||
|
||||
var whereQuery = await GetWhereEnumerableFunc(exportFilter).ConfigureAwait(false);
|
||||
//导出
|
||||
var variables = GlobalData.IdVariables.Select(a => a.Value).GetQuery(exportFilter.QueryPageOptions, whereQuery, exportFilter.FilterKeyValueAction);
|
||||
|
||||
var deviceDicts = (await GlobalData.DeviceService.GetAllAsync().ConfigureAwait(false)).ToDictionary(a => a.Id);
|
||||
var channelDicts = (await GlobalData.ChannelService.GetAllAsync().ConfigureAwait(false)).ToDictionary(a => a.Id);
|
||||
var pluginSheetNames = variables.Where(a => a.VariablePropertys?.Count > 0).SelectMany(a => a.VariablePropertys).Select(a =>
|
||||
{
|
||||
if (deviceDicts.TryGetValue(a.Key, out var device) && channelDicts.TryGetValue(device.ChannelId, out var channel))
|
||||
{
|
||||
var pluginKey = channel?.PluginName;
|
||||
using var businessBase = (BusinessBase)GlobalData.PluginService.GetDriver(pluginKey);
|
||||
return new KeyValuePair<string, VariablePropertyBase>(pluginKey, businessBase.VariablePropertys);
|
||||
}
|
||||
return new KeyValuePair<string, VariablePropertyBase>(string.Empty, null);
|
||||
}).Where(a => a.Value != null).DistinctBy(a => a.Key).ToDictionary();
|
||||
|
||||
var sheets = VariableServiceHelpers.ExportSheets(variables, deviceDicts, channelDicts, pluginSheetNames); // IEnumerable 延迟执行
|
||||
|
||||
return sheets;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
var data = (await PageAsync(exportFilter).ConfigureAwait(false));
|
||||
var sheets = await VariableServiceHelpers.ExportCoreAsync(data.Items, sortName: exportFilter.QueryPageOptions.SortName, sortOrder: exportFilter.QueryPageOptions.SortOrder).ConfigureAwait(false);
|
||||
return sheets;
|
||||
}
|
||||
|
||||
}
|
||||
private async Task<IAsyncEnumerable<Variable>> GetAsyncEnumerableData(ExportFilter exportFilter)
|
||||
{
|
||||
var whereQuery = await GetEnumerableData(exportFilter).ConfigureAwait(false);
|
||||
return whereQuery.GetAsyncEnumerable();
|
||||
}
|
||||
private async Task<ISugarQueryable<Variable>> GetEnumerableData(ExportFilter exportFilter)
|
||||
{
|
||||
var db = GetDB();
|
||||
var whereQuery = await GetWhereQueryFunc(exportFilter).ConfigureAwait(false);
|
||||
|
||||
return GetQuery(db, exportFilter.QueryPageOptions, whereQuery, exportFilter.FilterKeyValueAction);
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endregion 导出
|
||||
@@ -475,53 +577,21 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
var insertData = variables.Where(a => !a.IsUp).ToList();
|
||||
ManageHelper.CheckVariableCount(insertData.Count);
|
||||
using var db = GetDB();
|
||||
await db.BulkCopyAsync(insertData, 100000).ConfigureAwait(false);
|
||||
await db.BulkUpdateAsync(upData, 100000).ConfigureAwait(false);
|
||||
if (GlobalData.HardwareJob.HardwareInfo.MachineInfo.AvailableMemory > 2 * 1024 * 1024)
|
||||
{
|
||||
await db.BulkCopyAsync(insertData, 200000).ConfigureAwait(false);
|
||||
await db.BulkUpdateAsync(upData, 200000).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await db.BulkCopyAsync(insertData, 10000).ConfigureAwait(false);
|
||||
await db.BulkUpdateAsync(upData, 10000).ConfigureAwait(false);
|
||||
}
|
||||
DeleteVariableCache();
|
||||
return variables.Select(a => a.Id).ToHashSet();
|
||||
}
|
||||
|
||||
private static readonly WaitLock _cacheLock = new();
|
||||
|
||||
private async Task<Dictionary<long, Dictionary<string, DeviecIdVariableImportData>>> GetVariableImportData()
|
||||
{
|
||||
var key = ThingsGatewayCacheConst.Cache_Variable;
|
||||
var datas = App.CacheService.Get<Dictionary<long, Dictionary<string, DeviecIdVariableImportData>>>(key);
|
||||
|
||||
if (datas == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _cacheLock.WaitAsync().ConfigureAwait(false);
|
||||
datas = App.CacheService.Get<Dictionary<long, Dictionary<string, DeviecIdVariableImportData>>>(key);
|
||||
if (datas == null)
|
||||
{
|
||||
using var db = GetDB();
|
||||
datas = (await db.Queryable<Variable>().Select<DeviecIdVariableImportData>().ToListAsync().ConfigureAwait(false)).GroupBy(a => a.DeviceId).ToDictionary(a => a.Key, a => a.ToDictionary(a => a.Name));
|
||||
|
||||
App.CacheService.Set(key, datas);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_cacheLock.Release();
|
||||
}
|
||||
}
|
||||
return datas;
|
||||
}
|
||||
|
||||
public Task PreheatCache()
|
||||
{
|
||||
return GetVariableImportData();
|
||||
}
|
||||
private sealed class DeviecIdVariableImportData
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public long DeviceId { get; set; }
|
||||
public long CreateOrgId { get; set; }
|
||||
public long CreateUserId { get; set; }
|
||||
}
|
||||
|
||||
public async Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(IBrowserFile browserFile)
|
||||
{
|
||||
@@ -552,7 +622,7 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
// 获取当前工作表的所有行数据
|
||||
var rows = MiniExcel.Query(path, useHeaderRow: true, sheetName: sheetName).Cast<IDictionary<string, object>>();
|
||||
|
||||
deviceImportPreview = await SetVariableData(dataScope, deviceDicts, ImportPreviews, deviceImportPreview, driverPluginNameDict, propertysDict, sheetName, rows).ConfigureAwait(false);
|
||||
deviceImportPreview = SetVariableData(dataScope, deviceDicts, ImportPreviews, deviceImportPreview, driverPluginNameDict, propertysDict, sheetName, rows);
|
||||
}
|
||||
|
||||
return ImportPreviews;
|
||||
@@ -564,7 +634,7 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ImportPreviewOutput<Dictionary<string, Variable>>> SetVariableData(HashSet<long>? dataScope, Dictionary<string, Device> 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)
|
||||
public ImportPreviewOutput<Dictionary<string, Variable>> SetVariableData(HashSet<long>? dataScope, Dictionary<string, Device> 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)
|
||||
{
|
||||
// 变量页处理
|
||||
if (sheetName == ExportString.VariableName)
|
||||
@@ -581,8 +651,6 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
var variableProperties = type.GetRuntimeProperties().Where(a => (a.GetCustomAttribute<IgnoreExcelAttribute>() == null) && a.CanWrite)
|
||||
.ToDictionary(a => type.GetPropertyDisplayName(a.Name), a => (a, a.IsNullableType()));
|
||||
|
||||
var dbVariableDicts = await GetVariableImportData().ConfigureAwait(false);
|
||||
|
||||
// 并行处理每一行数据
|
||||
rows.ParallelForEachStreamed((item, state, index) =>
|
||||
{
|
||||
@@ -629,7 +697,7 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
return;
|
||||
}
|
||||
|
||||
if (dbVariableDicts.TryGetValue(variable.DeviceId, out var dbvar1s) && dbvar1s.TryGetValue(variable.Name, out var dbvar1))
|
||||
if (GlobalData.IdDevices.TryGetValue(variable.DeviceId, out var dbvar1s) && dbvar1s.VariableRuntimes.TryGetValue(variable.Name, out var dbvar1))
|
||||
{
|
||||
variable.Id = dbvar1.Id;
|
||||
variable.CreateOrgId = dbvar1.CreateOrgId;
|
||||
|
@@ -21,12 +21,295 @@ namespace ThingsGateway.Gateway.Application;
|
||||
public static class VariableServiceHelpers
|
||||
{
|
||||
|
||||
public static async Task<USheetDatas> ExportVariableAsync(IEnumerable<Variable> models, string sortName = nameof(Variable.Id), SortOrder sortOrder = SortOrder.Asc)
|
||||
public static async Task<USheetDatas> ExportVariableAsync(IEnumerable<Variable> variables, string sortName = nameof(Variable.Id), SortOrder sortOrder = SortOrder.Asc)
|
||||
{
|
||||
var data = await ExportCoreAsync(models, sortName: sortName, sortOrder: sortOrder).ConfigureAwait(false);
|
||||
|
||||
var deviceDicts = (await GlobalData.DeviceService.GetAllAsync().ConfigureAwait(false)).ToDictionary(a => a.Id);
|
||||
var channelDicts = (await GlobalData.ChannelService.GetAllAsync().ConfigureAwait(false)).ToDictionary(a => a.Id);
|
||||
var pluginSheetNames = variables.Where(a => a.VariablePropertys?.Count > 0).SelectMany(a => a.VariablePropertys).Select(a =>
|
||||
{
|
||||
if (deviceDicts.TryGetValue(a.Key, out var device) && channelDicts.TryGetValue(device.ChannelId, out var channel))
|
||||
{
|
||||
var pluginKey = channel?.PluginName;
|
||||
using var businessBase = (BusinessBase)GlobalData.PluginService.GetDriver(pluginKey);
|
||||
return new KeyValuePair<string, VariablePropertyBase>(pluginKey, businessBase.VariablePropertys);
|
||||
}
|
||||
return new KeyValuePair<string, VariablePropertyBase>(string.Empty, null);
|
||||
}).Where(a => a.Value != null).DistinctBy(a => a.Key).ToDictionary();
|
||||
var data = ExportSheets(variables, deviceDicts, channelDicts, pluginSheetNames); // IEnumerable 延迟执行
|
||||
return USheetDataHelpers.GetUSheetDatas(data);
|
||||
}
|
||||
static IAsyncEnumerable<Variable> FilterPluginDevices(
|
||||
IAsyncEnumerable<Variable> data,
|
||||
string pluginName,
|
||||
Dictionary<long, Device> deviceDicts,
|
||||
Dictionary<long, Channel> channelDicts)
|
||||
{
|
||||
return data.Where(variable =>
|
||||
{
|
||||
if (variable.VariablePropertys == null)
|
||||
return false;
|
||||
|
||||
foreach (var a in variable.VariablePropertys)
|
||||
{
|
||||
if (deviceDicts.TryGetValue(a.Key, out var device) && channelDicts.TryGetValue(device.ChannelId, out var channel))
|
||||
|
||||
{
|
||||
if (channel.PluginName == pluginName)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
static IEnumerable<Variable> FilterPluginDevices(
|
||||
IEnumerable<Variable> data,
|
||||
string pluginName,
|
||||
Dictionary<long, Device> deviceDicts,
|
||||
Dictionary<long, Channel> channelDicts)
|
||||
{
|
||||
return data.Where(variable =>
|
||||
{
|
||||
if (variable.VariablePropertys == null)
|
||||
return false;
|
||||
|
||||
foreach (var a in variable.VariablePropertys)
|
||||
{
|
||||
if (deviceDicts.TryGetValue(a.Key, out var device) && channelDicts.TryGetValue(device.ChannelId, out var channel))
|
||||
{
|
||||
if (channel.PluginName == pluginName)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public static Dictionary<string, object> ExportSheets(
|
||||
IEnumerable<Variable> data,
|
||||
Dictionary<long, Device> deviceDicts,
|
||||
Dictionary<long, Channel> channelDicts,
|
||||
Dictionary<string, VariablePropertyBase> pluginDrivers,
|
||||
string? deviceName = null)
|
||||
{
|
||||
var sheets = new Dictionary<string, object>();
|
||||
var propertysDict = new ConcurrentDictionary<string, (VariablePropertyBase, Dictionary<string, PropertyInfo>)>();
|
||||
|
||||
// 主变量页
|
||||
sheets.Add(ExportString.VariableName, GetVariableSheets(data, deviceDicts, deviceName));
|
||||
|
||||
// 插件页(动态推导)
|
||||
foreach (var plugin in pluginDrivers.Keys.Distinct())
|
||||
{
|
||||
var filtered = FilterPluginDevices(data, plugin, deviceDicts, channelDicts);
|
||||
var pluginName = PluginServiceUtil.GetFileNameAndTypeName(plugin).Item2;
|
||||
sheets.Add(pluginName, GetPluginSheets(filtered, deviceDicts, channelDicts, plugin, pluginDrivers, propertysDict));
|
||||
}
|
||||
|
||||
return sheets;
|
||||
}
|
||||
|
||||
static IEnumerable<Dictionary<string, object>> GetVariableSheets(
|
||||
IEnumerable<Variable> data,
|
||||
Dictionary<long, Device> deviceDicts,
|
||||
string? deviceName)
|
||||
{
|
||||
var type = typeof(Variable);
|
||||
var propertyInfos = type.GetRuntimeProperties()
|
||||
.Where(a => a.GetCustomAttribute<IgnoreExcelAttribute>(false) == null)
|
||||
.OrderBy(a =>
|
||||
{
|
||||
var order = a.GetCustomAttribute<AutoGenerateColumnAttribute>()?.Order ?? int.MaxValue;
|
||||
if (order < 0) order += 10000000;
|
||||
else if (order == 0) order = 10000000;
|
||||
return order;
|
||||
});
|
||||
|
||||
foreach (var variable in data)
|
||||
{
|
||||
yield return GetVariable(deviceDicts, deviceName, type, propertyInfos, variable);
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<string, object> GetVariable(Dictionary<long, Device> deviceDicts, string? deviceName, Type type, IOrderedEnumerable<PropertyInfo> propertyInfos, Variable variable)
|
||||
{
|
||||
var row = new Dictionary<string, object>();
|
||||
deviceDicts.TryGetValue(variable.DeviceId, out var device);
|
||||
row.TryAdd(ExportString.DeviceName, device?.Name ?? deviceName);
|
||||
|
||||
foreach (var item in propertyInfos)
|
||||
{
|
||||
var desc = type.GetPropertyDisplayName(item.Name);
|
||||
row.TryAdd(desc ?? item.Name, item.GetValue(variable)?.ToString());
|
||||
}
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
static IEnumerable<Dictionary<string, object>> GetPluginSheets(
|
||||
IEnumerable<Variable> data,
|
||||
Dictionary<long, Device> deviceDicts,
|
||||
Dictionary<long, Channel> channelDicts,
|
||||
string plugin,
|
||||
Dictionary<string, VariablePropertyBase> pluginDrivers,
|
||||
ConcurrentDictionary<string, (VariablePropertyBase, Dictionary<string, PropertyInfo>)> propertysDict)
|
||||
{
|
||||
if (!pluginDrivers.TryGetValue(plugin, out var variablePropertyBase))
|
||||
yield break;
|
||||
|
||||
if (!propertysDict.TryGetValue(plugin, out var propertys))
|
||||
{
|
||||
var driverProperties = variablePropertyBase;
|
||||
var driverPropertyType = driverProperties.GetType();
|
||||
propertys.Item1 = driverProperties;
|
||||
propertys.Item2 = driverPropertyType.GetRuntimeProperties()
|
||||
.Where(a => a.GetCustomAttribute<DynamicPropertyAttribute>() != null)
|
||||
.ToDictionary(
|
||||
a => driverPropertyType.GetPropertyDisplayName(a.Name, a => a.GetCustomAttribute<DynamicPropertyAttribute>(true)?.Description));
|
||||
propertysDict.TryAdd(plugin, propertys);
|
||||
}
|
||||
if (propertys.Item2?.Count == null || propertys.Item2?.Count == 0)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
foreach (var variable in data)
|
||||
{
|
||||
if (variable.VariablePropertys == null)
|
||||
continue;
|
||||
|
||||
foreach (var item in variable.VariablePropertys)
|
||||
{
|
||||
if (!(deviceDicts.TryGetValue(item.Key, out var businessDevice) &&
|
||||
deviceDicts.TryGetValue(variable.DeviceId, out var collectDevice)))
|
||||
continue;
|
||||
|
||||
channelDicts.TryGetValue(businessDevice.ChannelId, out var channel);
|
||||
if (channel?.PluginName != plugin)
|
||||
continue;
|
||||
|
||||
yield return GetPlugin(propertys, variable, item, businessDevice, collectDevice);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<string, object> GetPlugin((VariablePropertyBase, Dictionary<string, PropertyInfo>) propertys, Variable variable, KeyValuePair<long, Dictionary<string, string>> item, Device businessDevice, Device collectDevice)
|
||||
{
|
||||
var row = new Dictionary<string, object>
|
||||
{
|
||||
{ ExportString.DeviceName, collectDevice.Name },
|
||||
{ ExportString.BusinessDeviceName, businessDevice.Name },
|
||||
{ ExportString.VariableName, variable.Name }
|
||||
};
|
||||
|
||||
foreach (var kv in propertys.Item2)
|
||||
{
|
||||
var propDict = item.Value;
|
||||
if (propDict.TryGetValue(kv.Value.Name, out var dependencyProperty))
|
||||
{
|
||||
row.TryAdd(kv.Key, dependencyProperty);
|
||||
}
|
||||
else
|
||||
{
|
||||
row.TryAdd(kv.Key, ThingsGatewayStringConverter.Default.Serialize(null, kv.Value.GetValue(propertys.Item1)));
|
||||
}
|
||||
}
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
public static Dictionary<string, object> ExportSheets(
|
||||
IAsyncEnumerable<Variable> data,
|
||||
Dictionary<long, Device> deviceDicts,
|
||||
Dictionary<long, Channel> channelDicts,
|
||||
Dictionary<string, VariablePropertyBase> pluginDrivers,
|
||||
string? deviceName = null)
|
||||
{
|
||||
var sheets = new Dictionary<string, object>();
|
||||
var propertysDict = new ConcurrentDictionary<string, (VariablePropertyBase, Dictionary<string, PropertyInfo>)>();
|
||||
|
||||
// 主变量页
|
||||
sheets.Add(ExportString.VariableName, GetVariableSheets(data, deviceDicts, deviceName));
|
||||
|
||||
// 插件页(动态推导)
|
||||
foreach (var plugin in pluginDrivers.Keys.Distinct())
|
||||
{
|
||||
var filtered = FilterPluginDevices(data, plugin, deviceDicts, channelDicts);
|
||||
var pluginName = PluginServiceUtil.GetFileNameAndTypeName(plugin).Item2;
|
||||
sheets.Add(pluginName, GetPluginSheets(filtered, deviceDicts, channelDicts, plugin, pluginDrivers, propertysDict));
|
||||
}
|
||||
|
||||
return sheets;
|
||||
}
|
||||
|
||||
static async IAsyncEnumerable<Dictionary<string, object>> GetVariableSheets(
|
||||
IAsyncEnumerable<Variable> data,
|
||||
Dictionary<long, Device> deviceDicts,
|
||||
string? deviceName)
|
||||
{
|
||||
var type = typeof(Variable);
|
||||
var propertyInfos = type.GetRuntimeProperties()
|
||||
.Where(a => a.GetCustomAttribute<IgnoreExcelAttribute>(false) == null)
|
||||
.OrderBy(a =>
|
||||
{
|
||||
var order = a.GetCustomAttribute<AutoGenerateColumnAttribute>()?.Order ?? int.MaxValue;
|
||||
if (order < 0) order += 10000000;
|
||||
else if (order == 0) order = 10000000;
|
||||
return order;
|
||||
});
|
||||
|
||||
await foreach (var variable in data.ConfigureAwait(false))
|
||||
{
|
||||
yield return GetVariable(deviceDicts, deviceName, type, propertyInfos, variable);
|
||||
}
|
||||
}
|
||||
|
||||
static async IAsyncEnumerable<Dictionary<string, object>> GetPluginSheets(
|
||||
IAsyncEnumerable<Variable> data,
|
||||
Dictionary<long, Device> deviceDicts,
|
||||
Dictionary<long, Channel> channelDicts,
|
||||
string plugin,
|
||||
Dictionary<string, VariablePropertyBase> pluginDrivers,
|
||||
ConcurrentDictionary<string, (VariablePropertyBase, Dictionary<string, PropertyInfo>)> propertysDict)
|
||||
{
|
||||
if (!pluginDrivers.TryGetValue(plugin, out var variablePropertyBase))
|
||||
yield break;
|
||||
|
||||
if (!propertysDict.TryGetValue(plugin, out var propertys))
|
||||
{
|
||||
var driverProperties = variablePropertyBase;
|
||||
var driverPropertyType = driverProperties.GetType();
|
||||
propertys.Item1 = driverProperties;
|
||||
propertys.Item2 = driverPropertyType.GetRuntimeProperties()
|
||||
.Where(a => a.GetCustomAttribute<DynamicPropertyAttribute>() != null)
|
||||
.ToDictionary(
|
||||
a => driverPropertyType.GetPropertyDisplayName(a.Name, a => a.GetCustomAttribute<DynamicPropertyAttribute>(true)?.Description));
|
||||
propertysDict.TryAdd(plugin, propertys);
|
||||
}
|
||||
if (propertys.Item2?.Count == null || propertys.Item2?.Count == 0)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
await foreach (var variable in data.ConfigureAwait(false))
|
||||
{
|
||||
if (variable.VariablePropertys == null)
|
||||
continue;
|
||||
|
||||
foreach (var item in variable.VariablePropertys)
|
||||
{
|
||||
if (!(deviceDicts.TryGetValue(item.Key, out var businessDevice) &&
|
||||
deviceDicts.TryGetValue(variable.DeviceId, out var collectDevice)))
|
||||
continue;
|
||||
|
||||
channelDicts.TryGetValue(businessDevice.ChannelId, out var channel);
|
||||
if (channel?.PluginName != plugin)
|
||||
continue;
|
||||
|
||||
yield return GetPlugin(propertys, variable, item, businessDevice, collectDevice);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<Dictionary<string, object>> ExportCoreAsync(IEnumerable<Variable> data, string deviceName = null, string sortName = nameof(Variable.Id), SortOrder sortOrder = SortOrder.Asc)
|
||||
{
|
||||
@@ -67,7 +350,6 @@ public static class VariableServiceHelpers
|
||||
;
|
||||
|
||||
#endregion 列名称
|
||||
var varName = nameof(Variable.Name);
|
||||
data.ParallelForEachStreamed((variable, state, index) =>
|
||||
{
|
||||
Dictionary<string, object> varExport = new();
|
||||
@@ -83,10 +365,6 @@ public static class VariableServiceHelpers
|
||||
}
|
||||
//描述
|
||||
var desc = type.GetPropertyDisplayName(item.Name);
|
||||
if (item.Name == varName)
|
||||
{
|
||||
varName = desc;
|
||||
}
|
||||
//数据源增加
|
||||
varExport.TryAdd(desc ?? item.Name, item.GetValue(variable)?.ToString());
|
||||
}
|
||||
@@ -128,7 +406,8 @@ public static class VariableServiceHelpers
|
||||
var variablePropertyType = variableProperty.GetType();
|
||||
propertys.Item2 = variablePropertyType.GetRuntimeProperties()
|
||||
.Where(a => a.GetCustomAttribute<DynamicPropertyAttribute>() != null)
|
||||
.ToDictionary(a => variablePropertyType.GetPropertyDisplayName(a.Name, a => a.GetCustomAttribute<DynamicPropertyAttribute>(true)?.Description));
|
||||
.ToDictionary(a => variablePropertyType.GetPropertyDisplayName(a.Name, a =>
|
||||
a.GetCustomAttribute<DynamicPropertyAttribute>(true)?.Description));
|
||||
propertysDict.TryAdd(channel.PluginName, propertys);
|
||||
|
||||
}
|
||||
@@ -263,7 +542,7 @@ public static class VariableServiceHelpers
|
||||
rows.Add(expando);
|
||||
}
|
||||
|
||||
deviceImportPreview = await GlobalData.VariableService.SetVariableData(dataScope, deviceDicts, ImportPreviews, deviceImportPreview, driverPluginNameDict, propertysDict, sheetName, rows).ConfigureAwait(false);
|
||||
deviceImportPreview = GlobalData.VariableService.SetVariableData(dataScope, deviceDicts, ImportPreviews, deviceImportPreview, driverPluginNameDict, propertysDict, sheetName, rows);
|
||||
if (ImportPreviews.Any(a => a.Value.HasError))
|
||||
{
|
||||
throw new(ImportPreviews.FirstOrDefault(a => a.Value.HasError).Value.Results.FirstOrDefault(a => !a.Success).ErrorMessage ?? "error");
|
||||
|
@@ -9,6 +9,7 @@
|
||||
<PackageReference Include="Portable.BouncyCastle" Version="1.9.0" />
|
||||
<PackageReference Include="Riok.Mapperly" Version="4.2.1" ExcludeAssets="runtime" PrivateAssets="all" />
|
||||
<PackageReference Include="Rougamo.Fody" Version="5.0.1" />
|
||||
<PackageReference Include="System.Linq.Async" Version="6.0.3" />
|
||||
<PackageReference Include="TouchSocket.Dmtp" Version="3.1.11" />
|
||||
<PackageReference Include="TouchSocket.WebApi.Swagger" Version="3.1.11" />
|
||||
<PackageReference Include="ThingsGateway.Authentication" Version="$(AuthenticationVersion)" />
|
||||
|
@@ -26,7 +26,10 @@
|
||||
"RegisterStatus": "RegisterStatus",
|
||||
"Unauthorized": "Unauthorized",
|
||||
"Unregister": "Unregister",
|
||||
"UUID": "UUID"
|
||||
"UUID": "UUID",
|
||||
"MaxChannelCount": "MaxChannelCount",
|
||||
"MaxDeviceCount": "MaxDeviceCount",
|
||||
"MaxVariableCount": "MaxVariableCount"
|
||||
},
|
||||
|
||||
"ThingsGateway.Gateway.Razor._Imports": {
|
||||
|
@@ -24,7 +24,10 @@
|
||||
"RegisterStatus": "注册状态",
|
||||
"Unauthorized": "未授权",
|
||||
"Unregister": "取消注册",
|
||||
"UUID": "唯一编码"
|
||||
"UUID": "唯一编码",
|
||||
"MaxChannelCount": "最大通道数量",
|
||||
"MaxDeviceCount": "最大设备数量",
|
||||
"MaxVariableCount": "最大变量数量"
|
||||
},
|
||||
|
||||
"ThingsGateway.Gateway.Razor._Imports": {
|
||||
|
@@ -20,3 +20,4 @@ public static partial class GatewayMapper
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@@ -0,0 +1,54 @@
|
||||
// <auto-generated />
|
||||
#nullable enable
|
||||
namespace ThingsGateway.Gateway.Razor
|
||||
{
|
||||
public static class TreeViewItemMapper
|
||||
{
|
||||
|
||||
public static global::System.Collections.Generic.List<global::BootstrapBlazor.Components.TreeViewItem<global::ThingsGateway.Gateway.Razor.ChannelDeviceTreeItem>> AdaptListTreeViewItemChannelDeviceTreeItem(this global::System.Collections.Generic.List<global::BootstrapBlazor.Components.TreeViewItem<global::ThingsGateway.Gateway.Razor.ChannelDeviceTreeItem>> src, global::BootstrapBlazor.Components.TreeViewItem<global::ThingsGateway.Gateway.Razor.ChannelDeviceTreeItem> parent = null)
|
||||
{
|
||||
var target = new global::System.Collections.Generic.List<global::BootstrapBlazor.Components.TreeViewItem<global::ThingsGateway.Gateway.Razor.ChannelDeviceTreeItem>>(src.Count);
|
||||
foreach (var item in src)
|
||||
{
|
||||
target.Add(MapToTreeViewItemOfChannelDeviceTreeItem(item, parent));
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
|
||||
private static global::BootstrapBlazor.Components.TreeViewItem<global::ThingsGateway.Gateway.Razor.ChannelDeviceTreeItem> MapToTreeViewItemOfChannelDeviceTreeItem(global::BootstrapBlazor.Components.TreeViewItem<global::ThingsGateway.Gateway.Razor.ChannelDeviceTreeItem> source, global::BootstrapBlazor.Components.TreeViewItem<global::ThingsGateway.Gateway.Razor.ChannelDeviceTreeItem> parent)
|
||||
{
|
||||
var target = new global::BootstrapBlazor.Components.TreeViewItem<global::ThingsGateway.Gateway.Razor.ChannelDeviceTreeItem>(source.Value);
|
||||
target.CheckedState = source.CheckedState;
|
||||
target.Items = AdaptListTreeViewItemChannelDeviceTreeItem(source.Items.ToList(), target);
|
||||
if (source.Parent != null && parent != null)
|
||||
{
|
||||
target.Parent = parent;
|
||||
}
|
||||
else
|
||||
{
|
||||
target.Parent = null;
|
||||
}
|
||||
target.Text = source.Text;
|
||||
target.Icon = source.Icon;
|
||||
target.ExpandIcon = source.ExpandIcon;
|
||||
target.CssClass = source.CssClass;
|
||||
target.IsDisabled = source.IsDisabled;
|
||||
target.IsActive = source.IsActive;
|
||||
target.Template = source.Template;
|
||||
if (source.Value != null)
|
||||
{
|
||||
target.Value = source.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
target.Value = null;
|
||||
}
|
||||
target.IsExpand = source.IsExpand;
|
||||
target.HasChildren = source.HasChildren;
|
||||
return target;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
@@ -39,48 +39,55 @@
|
||||
<TableColumn @bind-Field="@context.LogLevel" Filterable=true Sortable=true Visible="false" />
|
||||
<TableColumn @bind-Field="@context.ChannelType" Filterable=true Sortable=true Visible="false" />
|
||||
|
||||
<TableColumn Field="@context.CacheTimeout" FieldExpression=@(()=>context.CacheTimeout) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.CheckClearTime" FieldExpression=@(()=>context.CheckClearTime) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.ConnectTimeout" FieldExpression=@(()=>context.ConnectTimeout) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.Heartbeat" FieldExpression=@(()=>context.Heartbeat) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.HeartbeatTime" FieldExpression=@(()=>context.HeartbeatTime) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.DtuSeviceType" FieldExpression=@(()=>context.DtuSeviceType) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.DtuId" FieldExpression=@(()=>context.DtuId) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.CacheTimeout" FieldExpression=@(() => context.CacheTimeout) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.CheckClearTime" FieldExpression=@(() => context.CheckClearTime) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.ConnectTimeout" FieldExpression=@(() => context.ConnectTimeout) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.Heartbeat" FieldExpression=@(() => context.Heartbeat) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.HeartbeatTime" FieldExpression=@(() => context.HeartbeatTime) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.DtuSeviceType" FieldExpression=@(() => context.DtuSeviceType) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.DtuId" FieldExpression=@(() => context.DtuId) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
|
||||
<TableColumn Field="@context.PluginType" FieldExpression=@(()=>context.PluginType) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.PortName" FieldExpression=@(()=>context.PortName) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.RemoteUrl" FieldExpression=@(()=>context.RemoteUrl) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.BindUrl" FieldExpression=@(()=>context.BindUrl) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.MaxClientCount" FieldExpression=@(()=>context.MaxClientCount) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.MaxConcurrentCount" FieldExpression=@(()=>context.MaxConcurrentCount) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.PluginType" FieldExpression=@(() => context.PluginType) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.PortName" FieldExpression=@(() => context.PortName) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.RemoteUrl" FieldExpression=@(() => context.RemoteUrl) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.BindUrl" FieldExpression=@(() => context.BindUrl) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.MaxClientCount" FieldExpression=@(() => context.MaxClientCount) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.MaxConcurrentCount" FieldExpression=@(() => context.MaxConcurrentCount) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
|
||||
<TableColumn @bind-Field="@context.Id" Filterable=true Sortable=true Visible="false" DefaultSort=true DefaultSortOrder="SortOrder.Asc" />
|
||||
|
||||
</TableColumns>
|
||||
|
||||
<EditTemplate Context="context">
|
||||
<ChannelEditComponent Model=@(context) ValidateEnable=false BatchEditEnable=false PluginType="ChannelDeviceHelpers.GetPluginType( SelectModel)"></ChannelEditComponent>
|
||||
<ChannelEditComponent Model=@(context) ValidateEnable=false BatchEditEnable=false PluginType="ChannelDeviceHelpers.GetPluginType(SelectModel)"></ChannelEditComponent>
|
||||
</EditTemplate>
|
||||
|
||||
|
||||
|
||||
<ExportButtonDropdownTemplate Context="ExportContext">
|
||||
<Button class="dropdown-item" OnClick="() => ExcelExportAsync(ExportContext,true)" IsDisabled=@(!AuthorizeButton("导出"))>
|
||||
<i class="fas fa-file-export"></i>
|
||||
<span>@RazorLocalizer["ExportAll"]</span>
|
||||
</Button>
|
||||
<Button class="dropdown-item" OnClick="() => ExcelExportAsync(ExportContext)" IsDisabled=@(!AuthorizeButton("导出"))>
|
||||
<i class="fas fa-file-export"></i>
|
||||
<span>@RazorLocalizer["TablesExportButtonExcelText"]</span>
|
||||
</Button>
|
||||
<Button class="dropdown-item" OnClick="() => ExcelImportAsync(ExportContext)" IsDisabled=@(!AuthorizeButton("导入"))>
|
||||
<i class="fas fa-file-import"></i>
|
||||
<span>@RazorLocalizer["TablesImportButtonExcelText"]</span>
|
||||
</Button>
|
||||
<Button class="dropdown-item" OnClick="() => ExcelChannelAsync(ExportContext)" IsDisabled=@(!AuthorizeButton("导入"))>
|
||||
<i class="fas fa-file-import"></i>
|
||||
<span>@GatewayLocalizer["ExcelChannel"]</span>
|
||||
</Button>
|
||||
@if ((AuthorizeButton("导出")))
|
||||
{
|
||||
|
||||
<Button class="dropdown-item" OnClick="() => ExcelExportAsync(ExportContext, true)" IsKeepDisabled=@(!AuthorizeButton("导出"))>
|
||||
<i class="fas fa-file-export"></i>
|
||||
<span>@RazorLocalizer["ExportAll"]</span>
|
||||
</Button>
|
||||
<Button class="dropdown-item" OnClick="() => ExcelExportAsync(ExportContext)" IsKeepDisabled=@(!AuthorizeButton("导出"))>
|
||||
<i class="fas fa-file-export"></i>
|
||||
<span>@RazorLocalizer["TablesExportButtonExcelText"]</span>
|
||||
</Button>
|
||||
}
|
||||
@if ((AuthorizeButton("导入")))
|
||||
{
|
||||
<Button class="dropdown-item" OnClick="() => ExcelImportAsync(ExportContext)" IsKeepDisabled=@(!AuthorizeButton("导入"))>
|
||||
<i class="fas fa-file-import"></i>
|
||||
<span>@RazorLocalizer["TablesImportButtonExcelText"]</span>
|
||||
</Button>
|
||||
<Button class="dropdown-item" OnClick="() => ExcelChannelAsync(ExportContext)" IsKeepDisabled=@(!AuthorizeButton("导入"))>
|
||||
<i class="fas fa-file-import"></i>
|
||||
<span>@GatewayLocalizer["ExcelChannel"]</span>
|
||||
</Button>
|
||||
}
|
||||
</ExportButtonDropdownTemplate>
|
||||
<TableToolbarTemplate>
|
||||
|
||||
|
@@ -8,7 +8,7 @@
|
||||
<div class="listtree-view">
|
||||
|
||||
<div class="d-flex align-items-center">
|
||||
<RadioList class="m-2" IsButton="true" AutoSelectFirstWhenValueIsNull="false" TValue="ShowTypeEnum?" ValueExpression=@(()=> ShowType ) Value="ShowType" ValueChanged="OnShowTypeChanged" ShowLabel="false" />
|
||||
<RadioList class="m-2" IsButton="true" AutoSelectFirstWhenValueIsNull="false" TValue="ShowTypeEnum?" ValueExpression=@(() => ShowType) Value="ShowType" ValueChanged="OnShowTypeChanged" ShowLabel="false" />
|
||||
|
||||
<SpinnerComponent @ref=Spinner></SpinnerComponent>
|
||||
</div>
|
||||
@@ -16,25 +16,25 @@
|
||||
<span style="color:var(--bs-body-color)" class="text-h6 mb-2">@GatewayLocalizer["DeviceList"]</span>
|
||||
|
||||
<ContextMenuZone title="Right click operation">
|
||||
<TreeView TItem="ChannelDeviceTreeItem" Items="Items" ShowIcon="false" ShowSearch IsAccordion=false IsVirtualize="true" OnTreeItemClick="OnTreeItemClick" OnSearchAsync="OnClickSearch" ModelEqualityComparer=ModelEqualityComparer>
|
||||
<TreeView TItem="ChannelDeviceTreeItem" Items="Items" ShowIcon="false" ShowSearch IsAccordion=false IsVirtualize="true" OnTreeItemClick="OnTreeItemClick" OnSearchAsync="OnClickSearch" ModelEqualityComparer=ModelEqualityComparer>
|
||||
|
||||
</TreeView>
|
||||
|
||||
<ContextMenu style="max-height:800px" class="tgTree" OnBeforeShowCallback="OnBeforeShowCallback">
|
||||
@if (SelectModel != null)
|
||||
{
|
||||
<ContextMenuItem Icon="fa-solid fa-plus" Disabled="(SelectModel.ChannelDevicePluginType >ChannelDevicePluginTypeEnum.Channel||!AuthorizeButton(AdminOperConst.Add))" Text="@GatewayLocalizer["AddChannel"]" OnClick=@((a,b)=>EditChannel(a,b,ItemChangedType.Add))></ContextMenuItem>
|
||||
<ContextMenuItem Icon="fa-solid fa-plus" Disabled="(SelectModel.ChannelDevicePluginType > ChannelDevicePluginTypeEnum.Channel || !AuthorizeButton(AdminOperConst.Add))" Text="@GatewayLocalizer["AddChannel"]" OnClick=@((a, b) => EditChannel(a, b, ItemChangedType.Add))></ContextMenuItem>
|
||||
|
||||
<ContextMenuItem Icon="fa-regular fa-pen-to-square" Disabled="(SelectModel.ChannelDevicePluginType >=ChannelDevicePluginTypeEnum.Channel||!AuthorizeButton(AdminOperConst.Edit))" Text="@GatewayLocalizer["BatchEditChannel"]" OnClick=@((a,b)=>BatchEditChannel(a,b))></ContextMenuItem>
|
||||
<ContextMenuItem Icon="fa-regular fa-pen-to-square" Disabled="(SelectModel.ChannelDevicePluginType >= ChannelDevicePluginTypeEnum.Channel || !AuthorizeButton(AdminOperConst.Edit))" Text="@GatewayLocalizer["BatchEditChannel"]" OnClick=@((a, b) => BatchEditChannel(a, b))></ContextMenuItem>
|
||||
|
||||
<ContextMenuItem Icon="fa-regular fa-pen-to-square" Disabled="(!AuthorizeButton(AdminOperConst.Add))" Text="@GatewayLocalizer["ExcelChannel"]" OnClick=@((a,b)=>ExcelChannel(a,b))></ContextMenuItem>
|
||||
<ContextMenuItem Icon="fa-regular fa-pen-to-square" Disabled="(!AuthorizeButton(AdminOperConst.Add))" Text="@GatewayLocalizer["ExcelChannel"]" OnClick=@((a, b) => ExcelChannel(a, b))></ContextMenuItem>
|
||||
|
||||
|
||||
<ContextMenuItem Icon="fa-regular fa-pen-to-square" Disabled="(SelectModel.ChannelDevicePluginType !=ChannelDevicePluginTypeEnum.Channel||!AuthorizeButton(AdminOperConst.Edit))" Text="@GatewayLocalizer["UpdateChannel"]" OnClick=@((a,b)=>EditChannel(a,b,ItemChangedType.Update))></ContextMenuItem>
|
||||
<ContextMenuItem Icon="fa-regular fa-pen-to-square" Disabled="(SelectModel.ChannelDevicePluginType != ChannelDevicePluginTypeEnum.Channel || !AuthorizeButton(AdminOperConst.Edit))" Text="@GatewayLocalizer["UpdateChannel"]" OnClick=@((a, b) => EditChannel(a, b, ItemChangedType.Update))></ContextMenuItem>
|
||||
|
||||
<ContextMenuItem Icon="fa-regular fa-copy" Disabled="(SelectModel.ChannelDevicePluginType !=ChannelDevicePluginTypeEnum.Channel||!AuthorizeButton(AdminOperConst.Add))" Text="@GatewayLocalizer["CopyChannel"]" OnClick=@((a,b)=>CopyChannel(a,b))></ContextMenuItem>
|
||||
<ContextMenuItem Icon="fa-regular fa-copy" Disabled="(SelectModel.ChannelDevicePluginType != ChannelDevicePluginTypeEnum.Channel || !AuthorizeButton(AdminOperConst.Add))" Text="@GatewayLocalizer["CopyChannel"]" OnClick=@((a, b) => CopyChannel(a, b))></ContextMenuItem>
|
||||
|
||||
<ContextMenuItem Icon="fa-solid fa-xmark" Text="@GatewayLocalizer["DeleteChannel"]" Disabled="(SelectModel.ChannelDevicePluginType ==ChannelDevicePluginTypeEnum.Device||!AuthorizeButton(AdminOperConst.Delete))" OnClick="DeleteChannel"></ContextMenuItem>
|
||||
<ContextMenuItem Icon="fa-solid fa-xmark" Text="@GatewayLocalizer["DeleteChannel"]" Disabled="(SelectModel.ChannelDevicePluginType == ChannelDevicePluginTypeEnum.Device || !AuthorizeButton(AdminOperConst.Delete))" OnClick="DeleteChannel"></ContextMenuItem>
|
||||
|
||||
<ContextMenuItem Icon="fas fa-file-export" Text="@GatewayLocalizer["ExportChannel"]"
|
||||
Disabled=@(SelectModel.ChannelDevicePluginType ==ChannelDevicePluginTypeEnum.Device||!AuthorizeButton("导出"))
|
||||
@@ -44,16 +44,16 @@
|
||||
|
||||
<ContextMenuDivider></ContextMenuDivider>
|
||||
|
||||
<ContextMenuItem Disabled="(SelectModel.ChannelDevicePluginType <ChannelDevicePluginTypeEnum.Channel)||!AuthorizeButton(AdminOperConst.Add)" Icon="fa-solid fa-plus device" Text="@GatewayLocalizer["AddDevice"]" OnClick=@((a,b)=>EditDevice(a,b,ItemChangedType.Add))></ContextMenuItem>
|
||||
<ContextMenuItem Disabled="(SelectModel.ChannelDevicePluginType < ChannelDevicePluginTypeEnum.Channel) || !AuthorizeButton(AdminOperConst.Add)" Icon="fa-solid fa-plus device" Text="@GatewayLocalizer["AddDevice"]" OnClick=@((a, b) => EditDevice(a, b, ItemChangedType.Add))></ContextMenuItem>
|
||||
|
||||
<ContextMenuItem Icon="fa-regular fa-pen-to-square" Text="@GatewayLocalizer["BatchEditDevice"]" Disabled="(SelectModel.ChannelDevicePluginType >=ChannelDevicePluginTypeEnum.Device)||!AuthorizeButton(AdminOperConst.Edit)" OnClick=@((a,b)=>BatchEditDevice(a,b))></ContextMenuItem>
|
||||
<ContextMenuItem Icon="fa-regular fa-pen-to-square" Text="@GatewayLocalizer["BatchEditDevice"]" Disabled="(SelectModel.ChannelDevicePluginType >= ChannelDevicePluginTypeEnum.Device) || !AuthorizeButton(AdminOperConst.Edit)" OnClick=@((a, b) => BatchEditDevice(a, b))></ContextMenuItem>
|
||||
|
||||
<ContextMenuItem Disabled="!AuthorizeButton(AdminOperConst.Add)" Icon="fa-solid fa-plus" Text="@GatewayLocalizer["ExcelDevice"]" OnClick=@((a,b)=>ExcelDevice(a,b))></ContextMenuItem>
|
||||
<ContextMenuItem Disabled="!AuthorizeButton(AdminOperConst.Add)" Icon="fa-solid fa-plus" Text="@GatewayLocalizer["ExcelDevice"]" OnClick=@((a, b) => ExcelDevice(a, b))></ContextMenuItem>
|
||||
|
||||
|
||||
<ContextMenuItem Disabled="(SelectModel.ChannelDevicePluginType !=ChannelDevicePluginTypeEnum.Device)||!AuthorizeButton(AdminOperConst.Edit)" Icon="fa-regular fa-pen-to-square" Text="@GatewayLocalizer["UpdateDevice"]" OnClick=@((a,b)=>EditDevice(a,b,ItemChangedType.Update))></ContextMenuItem>
|
||||
<ContextMenuItem Disabled="(SelectModel.ChannelDevicePluginType != ChannelDevicePluginTypeEnum.Device) || !AuthorizeButton(AdminOperConst.Edit)" Icon="fa-regular fa-pen-to-square" Text="@GatewayLocalizer["UpdateDevice"]" OnClick=@((a, b) => EditDevice(a, b, ItemChangedType.Update))></ContextMenuItem>
|
||||
|
||||
<ContextMenuItem Disabled="(SelectModel.ChannelDevicePluginType !=ChannelDevicePluginTypeEnum.Device)||!AuthorizeButton(AdminOperConst.Add)" Icon="fa-regular fa-pen-to-square" Text="@GatewayLocalizer["CopyDevice"]" OnClick=@((a,b)=>CopyDevice(a,b))></ContextMenuItem>
|
||||
<ContextMenuItem Disabled="(SelectModel.ChannelDevicePluginType != ChannelDevicePluginTypeEnum.Device) || !AuthorizeButton(AdminOperConst.Add)" Icon="fa-regular fa-pen-to-square" Text="@GatewayLocalizer["CopyDevice"]" OnClick=@((a, b) => CopyDevice(a, b))></ContextMenuItem>
|
||||
|
||||
<ContextMenuItem Icon="fa-solid fa-xmark" Disabled="(!AuthorizeButton(AdminOperConst.Delete))" Text="@GatewayLocalizer["DeleteDevice"]" OnClick="DeleteDevice"></ContextMenuItem>
|
||||
|
||||
|
@@ -1259,7 +1259,7 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e =>
|
||||
}
|
||||
}
|
||||
|
||||
Items = ZItem;
|
||||
Items = ZItem.AdaptListTreeViewItemChannelDeviceTreeItem();
|
||||
ChannelRuntimeDispatchService.Subscribe(Refresh);
|
||||
|
||||
scheduler = new SmartTriggerScheduler(Notify, TimeSpan.FromMilliseconds(3000));
|
||||
@@ -1375,7 +1375,7 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e =>
|
||||
{
|
||||
}
|
||||
}
|
||||
Items = ZItem;
|
||||
Items = ZItem.AdaptListTreeViewItemChannelDeviceTreeItem();
|
||||
return Items;
|
||||
}
|
||||
else
|
||||
@@ -1459,7 +1459,7 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e =>
|
||||
}
|
||||
}
|
||||
|
||||
Items = ZItem;
|
||||
Items = ZItem.AdaptListTreeViewItemChannelDeviceTreeItem();
|
||||
return Items;
|
||||
}
|
||||
|
||||
|
@@ -28,10 +28,10 @@
|
||||
IsPagination=true>
|
||||
<TableColumns>
|
||||
|
||||
<TableColumn Field="@context.Name" FieldExpression=@(()=>context.Name) ShowTips=true Filterable=true Sortable=true Visible=true />
|
||||
<TableColumn Field="@context.Description" FieldExpression=@(()=>context.Description) ShowTips=true Filterable=true Sortable=true Visible=true />
|
||||
<TableColumn Field="@context.IntervalTime" FieldExpression=@(()=>context.IntervalTime) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.ChannelName" FieldExpression=@(()=>context.ChannelName) ShowTips=true Filterable=true Sortable=true Visible=true />
|
||||
<TableColumn Field="@context.Name" FieldExpression=@(() => context.Name) ShowTips=true Filterable=true Sortable=true Visible=true />
|
||||
<TableColumn Field="@context.Description" FieldExpression=@(() => context.Description) ShowTips=true Filterable=true Sortable=true Visible=true />
|
||||
<TableColumn Field="@context.IntervalTime" FieldExpression=@(() => context.IntervalTime) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.ChannelName" FieldExpression=@(() => context.ChannelName) ShowTips=true Filterable=true Sortable=true Visible=true />
|
||||
<TableColumn @bind-Field="@context.Enable" Filterable=true Sortable=true Visible="true" />
|
||||
<TableColumn @bind-Field="@context.LogLevel" Filterable=true Sortable=true Visible="false" />
|
||||
<TableColumn @bind-Field="@context.Remark1" Filterable=true Sortable=true Visible="false" />
|
||||
@@ -40,25 +40,25 @@
|
||||
<TableColumn @bind-Field="@context.Remark4" Filterable=true Sortable=true Visible="false" />
|
||||
<TableColumn @bind-Field="@context.Remark5" Filterable=true Sortable=true Visible="false" />
|
||||
|
||||
<TableColumn Field="@context.ActiveTime" FieldExpression=@(()=>context.ActiveTime) ShowTips=true Filterable=true Sortable=true Visible=true />
|
||||
<TableColumn Field="@context.DeviceStatus" FieldExpression=@(()=>context.DeviceStatus) ShowTips=true Filterable=true Sortable=true Visible=true />
|
||||
<TableColumn Field="@context.ActiveTime" FieldExpression=@(() => context.ActiveTime) ShowTips=true Filterable=true Sortable=true Visible=true />
|
||||
<TableColumn Field="@context.DeviceStatus" FieldExpression=@(() => context.DeviceStatus) ShowTips=true Filterable=true Sortable=true Visible=true />
|
||||
|
||||
<TableColumn Field="@context.Pause" FieldExpression=@(()=>context.Pause) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.LastErrorMessage" FieldExpression=@(()=>context.LastErrorMessage) ShowTips=true Filterable=true Sortable=true Visible=true />
|
||||
<TableColumn Field="@context.Pause" FieldExpression=@(() => context.Pause) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.LastErrorMessage" FieldExpression=@(() => context.LastErrorMessage) ShowTips=true Filterable=true Sortable=true Visible=true />
|
||||
|
||||
<TableColumn Field="@context.PluginName" FieldExpression=@(()=>context.PluginName) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.PluginName" FieldExpression=@(() => context.PluginName) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
|
||||
<TableColumn Field="@context.DeviceVariableCount" FieldExpression=@(()=>context.DeviceVariableCount) ShowTips=true Filterable=true Sortable=true Visible=true />
|
||||
<TableColumn Field="@context.MethodVariableCount" FieldExpression=@(()=>context.MethodVariableCount) ShowTips=true Filterable=true Sortable=true Visible=true />
|
||||
<TableColumn Field="@context.SourceVariableCount" FieldExpression=@(()=>context.SourceVariableCount) ShowTips=true Filterable=true Sortable=true Visible=true />
|
||||
<TableColumn Field="@context.DeviceVariableCount" FieldExpression=@(() => context.DeviceVariableCount) ShowTips=true Filterable=true Sortable=true Visible=true />
|
||||
<TableColumn Field="@context.MethodVariableCount" FieldExpression=@(() => context.MethodVariableCount) ShowTips=true Filterable=true Sortable=true Visible=true />
|
||||
<TableColumn Field="@context.SourceVariableCount" FieldExpression=@(() => context.SourceVariableCount) ShowTips=true Filterable=true Sortable=true Visible=true />
|
||||
|
||||
<TableColumn Field="@context.ChannelId" FieldExpression=@(()=>context.ChannelId) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.RedundantEnable" FieldExpression=@(()=>context.RedundantEnable) Ignore="true" ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.RedundantType" FieldExpression=@(()=>context.RedundantType) Ignore="true" ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.RedundantDeviceId" FieldExpression=@(()=>context.RedundantDeviceId) Ignore="true" ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.RedundantScanIntervalTime" FieldExpression=@(()=>context.RedundantScanIntervalTime) Ignore="true" ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.RedundantScript" FieldExpression=@(()=>context.RedundantScript) Ignore="true" ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.RedundantSwitchType" FieldExpression=@(()=>context.RedundantSwitchType) Ignore="true" ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.ChannelId" FieldExpression=@(() => context.ChannelId) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.RedundantEnable" FieldExpression=@(() => context.RedundantEnable) Ignore="true" ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.RedundantType" FieldExpression=@(() => context.RedundantType) Ignore="true" ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.RedundantDeviceId" FieldExpression=@(() => context.RedundantDeviceId) Ignore="true" ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.RedundantScanIntervalTime" FieldExpression=@(() => context.RedundantScanIntervalTime) Ignore="true" ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.RedundantScript" FieldExpression=@(() => context.RedundantScript) Ignore="true" ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.RedundantSwitchType" FieldExpression=@(() => context.RedundantSwitchType) Ignore="true" ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
|
||||
|
||||
<TableColumn @bind-Field="@context.Id" Filterable=true Sortable=true Visible="false" DefaultSort=true DefaultSortOrder="SortOrder.Asc" />
|
||||
@@ -66,28 +66,35 @@
|
||||
</TableColumns>
|
||||
|
||||
<EditTemplate Context="context">
|
||||
<DeviceEditComponent Model=@(context) ValidateEnable=false BatchEditEnable=false AutoRestartThread=AutoRestartThread ></DeviceEditComponent>
|
||||
<DeviceEditComponent Model=@(context) ValidateEnable=false BatchEditEnable=false AutoRestartThread=AutoRestartThread></DeviceEditComponent>
|
||||
</EditTemplate>
|
||||
|
||||
|
||||
|
||||
<ExportButtonDropdownTemplate Context="ExportContext">
|
||||
<Button class="dropdown-item" OnClick="() => ExcelExportAsync(ExportContext,true)" IsDisabled=@(!AuthorizeButton("导出"))>
|
||||
<i class="fas fa-file-export"></i>
|
||||
<span>@RazorLocalizer["ExportAll"]</span>
|
||||
</Button>
|
||||
<Button class="dropdown-item" OnClick="() => ExcelExportAsync(ExportContext)" IsDisabled=@(!AuthorizeButton("导出"))>
|
||||
<i class="fas fa-file-export"></i>
|
||||
<span>@RazorLocalizer["TablesExportButtonExcelText"]</span>
|
||||
</Button>
|
||||
<Button class="dropdown-item" OnClick="() => ExcelImportAsync(ExportContext)" IsDisabled=@(!AuthorizeButton("导入"))>
|
||||
<i class="fas fa-file-import"></i>
|
||||
<span>@RazorLocalizer["TablesImportButtonExcelText"]</span>
|
||||
</Button>
|
||||
<Button class="dropdown-item" OnClick="() => ExcelDeviceAsync(ExportContext)" IsDisabled=@(!AuthorizeButton("导入"))>
|
||||
<i class="fas fa-file-import"></i>
|
||||
<span>@GatewayLocalizer["ExcelDevice"]</span>
|
||||
</Button>
|
||||
|
||||
@if ((AuthorizeButton("导出")))
|
||||
{
|
||||
<Button class="dropdown-item" OnClick="() => ExcelExportAsync(ExportContext, true)" IsKeepDisabled=@(!AuthorizeButton("导出"))>
|
||||
<i class="fas fa-file-export"></i>
|
||||
<span>@RazorLocalizer["ExportAll"]</span>
|
||||
</Button>
|
||||
<Button class="dropdown-item" OnClick="() => ExcelExportAsync(ExportContext)" IsKeepDisabled=@(!AuthorizeButton("导出"))>
|
||||
<i class="fas fa-file-export"></i>
|
||||
<span>@RazorLocalizer["TablesExportButtonExcelText"]</span>
|
||||
</Button>
|
||||
}
|
||||
@if ((AuthorizeButton("导入")))
|
||||
{
|
||||
<Button class="dropdown-item" OnClick="() => ExcelImportAsync(ExportContext)" IsKeepDisabled=@(!AuthorizeButton("导入"))>
|
||||
<i class="fas fa-file-import"></i>
|
||||
<span>@RazorLocalizer["TablesImportButtonExcelText"]</span>
|
||||
</Button>
|
||||
<Button class="dropdown-item" OnClick="() => ExcelDeviceAsync(ExportContext)" IsKeepDisabled=@(!AuthorizeButton("导入"))>
|
||||
<i class="fas fa-file-import"></i>
|
||||
<span>@GatewayLocalizer["ExcelDevice"]</span>
|
||||
</Button>
|
||||
}
|
||||
</ExportButtonDropdownTemplate>
|
||||
<TableToolbarTemplate>
|
||||
|
||||
|
@@ -29,8 +29,8 @@
|
||||
OnQueryAsync="OnQueryAsync"
|
||||
IsPagination=true>
|
||||
<TableColumns>
|
||||
<TableColumn Field="@context.DeviceId" FieldExpression=@(()=>context.DeviceId) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.DeviceName" FieldExpression=@(()=>context.DeviceName) ShowTips=true Filterable=true Sortable=true Visible=true />
|
||||
<TableColumn Field="@context.DeviceId" FieldExpression=@(() => context.DeviceId) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.DeviceName" FieldExpression=@(() => context.DeviceName) ShowTips=true Filterable=true Sortable=true Visible=true />
|
||||
<TableColumn @bind-Field="@context.Name" ShowTips=true Filterable=true Sortable=true Visible=true />
|
||||
<TableColumn @bind-Field="@context.Description" ShowTips=true Filterable=true Sortable=true Visible=true />
|
||||
<TableColumn @bind-Field="@context.BusinessGroup" ShowTips=true Filterable=true Sortable=true Visible=true />
|
||||
@@ -38,19 +38,19 @@
|
||||
<TableColumn @bind-Field="@context.RpcWriteCheck" ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
|
||||
<TableColumn @bind-Field="@context.Enable" Filterable=true Sortable=true Visible="false" />
|
||||
<TableColumn Field="@context.ChangeTime" ShowTips=true FieldExpression=@(()=>context.ChangeTime) Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.CollectTime" ShowTips=true FieldExpression=@(()=>context.CollectTime) Filterable=true Sortable=true Visible=true />
|
||||
<TableColumn Field="@context.IsOnline" FieldExpression=@(()=>context.IsOnline) Filterable=true Sortable=true Visible=true />
|
||||
<TableColumn Field="@context.LastErrorMessage" ShowTips=true FieldExpression=@(()=>context.LastErrorMessage) Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.ChangeTime" ShowTips=true FieldExpression=@(() => context.ChangeTime) Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.CollectTime" ShowTips=true FieldExpression=@(() => context.CollectTime) Filterable=true Sortable=true Visible=true />
|
||||
<TableColumn Field="@context.IsOnline" FieldExpression=@(() => context.IsOnline) Filterable=true Sortable=true Visible=true />
|
||||
<TableColumn Field="@context.LastErrorMessage" ShowTips=true FieldExpression=@(() => context.LastErrorMessage) Filterable=true Sortable=true Visible=false />
|
||||
|
||||
<TableColumn Field="@context.LastSetValue" FieldExpression=@(()=>context.LastSetValue) Visible=false ShowTips=true Formatter=@(JsonFormatter) />
|
||||
<TableColumn Field="@context.LastSetValue" FieldExpression=@(() => context.LastSetValue) Visible=false ShowTips=true Formatter=@(JsonFormatter) />
|
||||
|
||||
<TableColumn Field="@context.RawValue" FieldExpression=@(()=>context.RawValue) Visible=false ShowTips=true Formatter=@(JsonFormatter) />
|
||||
<TableColumn Field="@context.Value" FieldExpression=@(()=>context.Value) Visible=true ShowTips=true Formatter=@(JsonFormatter) />
|
||||
<TableColumn Field="@context.RawValue" FieldExpression=@(() => context.RawValue) Visible=false ShowTips=true Formatter=@(JsonFormatter) />
|
||||
<TableColumn Field="@context.Value" FieldExpression=@(() => context.Value) Visible=true ShowTips=true Formatter=@(JsonFormatter) />
|
||||
|
||||
<TableColumn @bind-Field="@context.SaveValue" Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn @bind-Field="@context.DataType" Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.RuntimeType" ShowTips=true FieldExpression=@(()=>context.RuntimeType) Filterable=true Sortable=true Visible=true />
|
||||
<TableColumn Field="@context.RuntimeType" ShowTips=true FieldExpression=@(() => context.RuntimeType) Filterable=true Sortable=true Visible=true />
|
||||
|
||||
<TableColumn @bind-Field="@context.RegisterAddress" Filterable=true Sortable=true Visible=true />
|
||||
<TableColumn @bind-Field="@context.ArrayLength" Filterable=true Sortable=true Visible=false />
|
||||
@@ -70,7 +70,7 @@
|
||||
</TableColumns>
|
||||
<RowButtonTemplate>
|
||||
|
||||
<PopConfirmButton IsDisabled=@(!AuthorizeButton("写入变量")) Size="Size.ExtraSmall" Color="Color.Warning" Icon="fa-solid fa-bars" Text="@Localizer["WriteVariable"]" IsAsync OnConfirm="()=>OnWriteVariable(context)">
|
||||
<PopConfirmButton IsDisabled=@(!AuthorizeButton("写入变量")) Size="Size.ExtraSmall" Color="Color.Warning" Icon="fa-solid fa-bars" Text="@Localizer["WriteVariable"]" IsAsync OnConfirm="() => OnWriteVariable(context)">
|
||||
|
||||
<BodyTemplate>
|
||||
<Textarea @bind-Value=WriteValue ShowLabel="true" ShowLabelTooltip="true" />
|
||||
@@ -85,22 +85,28 @@
|
||||
|
||||
|
||||
<ExportButtonDropdownTemplate Context="ExportContext">
|
||||
<Button class="dropdown-item" OnClick="() => ExcelExportAsync(ExportContext,true)" IsDisabled=@(!AuthorizeButton("导出"))>
|
||||
<i class="fas fa-file-export"></i>
|
||||
<span>@RazorLocalizer["ExportAll"]</span>
|
||||
</Button>
|
||||
<Button class="dropdown-item" OnClick="() => ExcelExportAsync(ExportContext)" IsDisabled=@(!AuthorizeButton("导出"))>
|
||||
<i class="fas fa-file-export"></i>
|
||||
<span>@RazorLocalizer["TablesExportButtonExcelText"]</span>
|
||||
</Button>
|
||||
<Button class="dropdown-item" OnClick="() => ExcelImportAsync(ExportContext)" IsDisabled=@(!AuthorizeButton("导入"))>
|
||||
<i class="fas fa-file-import"></i>
|
||||
<span>@RazorLocalizer["TablesImportButtonExcelText"]</span>
|
||||
</Button>
|
||||
<Button class="dropdown-item" OnClick="() => ExcelVariableAsync(ExportContext)" IsDisabled=@(!AuthorizeButton("导入"))>
|
||||
<i class="fas fa-file-import"></i>
|
||||
<span>@GatewayLocalizer["ExcelVariable"]</span>
|
||||
</Button>
|
||||
@if ((AuthorizeButton("导出")))
|
||||
{
|
||||
<Button IsAsync class="dropdown-item" OnClick="() => ExcelExportAsync(ExportContext, true)" IsKeepDisabled=@(!AuthorizeButton("导出"))>
|
||||
<i class="fas fa-file-export"></i>
|
||||
<span>@RazorLocalizer["ExportAll"]</span>
|
||||
</Button>
|
||||
<Button class="dropdown-item" OnClick="() => ExcelExportAsync(ExportContext)" IsKeepDisabled=@(!AuthorizeButton("导出"))>
|
||||
<i class="fas fa-file-export"></i>
|
||||
<span>@RazorLocalizer["TablesExportButtonExcelText"]</span>
|
||||
</Button>
|
||||
}
|
||||
@if ((AuthorizeButton("导入")))
|
||||
{
|
||||
<Button class="dropdown-item" OnClick="() => ExcelImportAsync(ExportContext)" IsKeepDisabled=@(!AuthorizeButton("导入"))>
|
||||
<i class="fas fa-file-import"></i>
|
||||
<span>@RazorLocalizer["TablesImportButtonExcelText"]</span>
|
||||
</Button>
|
||||
<Button class="dropdown-item" OnClick="() => ExcelVariableAsync(ExportContext)" IsKeepDisabled=@(!AuthorizeButton("导入"))>
|
||||
<i class="fas fa-file-import"></i>
|
||||
<span>@GatewayLocalizer["ExcelVariable"]</span>
|
||||
</Button>
|
||||
}
|
||||
</ExportButtonDropdownTemplate>
|
||||
<TableToolbarTemplate>
|
||||
|
||||
|
@@ -410,7 +410,6 @@ finally
|
||||
});
|
||||
await DialogService.Show(op);
|
||||
|
||||
_ = Task.Run(GlobalData.VariableRuntimeService.PreheatCache);
|
||||
}
|
||||
|
||||
#endregion 导出
|
||||
|
@@ -40,7 +40,7 @@
|
||||
</label>
|
||||
|
||||
<label class="form-control">
|
||||
@(AuthorizeInfo?.Auth==true ? Localizer["Authorized"] : Localizer["Unauthorized"])
|
||||
@(AuthorizeInfo?.Auth == true ? Localizer["Authorized"] : Localizer["Unauthorized"])
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@@ -75,6 +75,41 @@
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-sm-12">
|
||||
|
||||
<label class="form-label">
|
||||
|
||||
@Localizer["MaxChannelCount"]
|
||||
</label>
|
||||
|
||||
<label class="form-control">
|
||||
@AuthorizeInfo?.MaxChannelCount
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-sm-12">
|
||||
|
||||
<label class="form-label">
|
||||
|
||||
@Localizer["MaxDeviceCount"]
|
||||
</label>
|
||||
|
||||
<label class="form-control">
|
||||
@AuthorizeInfo?.MaxDeviceCount
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-sm-12">
|
||||
|
||||
<label class="form-label">
|
||||
|
||||
@Localizer["MaxVariableCount"]
|
||||
</label>
|
||||
|
||||
<label class="form-control">
|
||||
@AuthorizeInfo?.MaxVariableCount
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
|
6
src/NuGet.Config
Normal file
6
src/NuGet.Config
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<add key="ThingsGateway" value="..\..\nupkgs" />
|
||||
</packageSources>
|
||||
</configuration>
|
@@ -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));
|
||||
@@ -1376,7 +1376,7 @@ public class OpcUaMaster : IDisposable
|
||||
{
|
||||
if (m_reConnectHandler == null)
|
||||
{
|
||||
Log(3, null, "Reconnecting in {0}s", 1);
|
||||
Log(3, null, "Reconnecting : {0}", e.Status.ToString());
|
||||
m_ReconnectStarting?.Invoke(this, e);
|
||||
|
||||
m_reConnectHandler = new SessionReconnectHandler(true, 10000);
|
||||
|
@@ -10,6 +10,8 @@
|
||||
|
||||
using ThingsGateway.Foundation.Extension.String;
|
||||
using ThingsGateway.Foundation.SiemensS7;
|
||||
using ThingsGateway.NewLife.Extension;
|
||||
using ThingsGateway.NewLife.Reflection;
|
||||
|
||||
using TouchSocket.Core;
|
||||
|
||||
@@ -20,7 +22,7 @@ public class SiemensS7Test
|
||||
|
||||
[Theory]
|
||||
[InlineData("M100", true, "03 00 00 1B 02 F0 80 32 03 00 00 00 03 00 02 00 06 00 00 04 01 FF 04 00 10 00 00")]
|
||||
[InlineData("M100", false, "03 00 00 16 02 F0 80 32 03 00 00 00 04 00 02 00 01 00 00 05 01 FF", "1", DataTypeEnum.UInt16)]
|
||||
[InlineData("M100", false, "03 00 00 16 02 F0 80 32 03 00 00 00 03 00 02 00 01 00 00 05 01 FF", "1", DataTypeEnum.UInt16)]
|
||||
public async Task SiemensS7_ReadWrite_OK(string address, bool read, string data, string writeData = null, DataTypeEnum dataTypeEnum = DataTypeEnum.UInt16)
|
||||
{
|
||||
byte[] bytes = data.HexStringToBytes();
|
||||
@@ -32,7 +34,7 @@ public class SiemensS7Test
|
||||
siemensS7Master.InitChannel(siemensS7Channel);
|
||||
await siemensS7Channel.SetupAsync(siemensS7Channel.Config);
|
||||
var adapter = siemensS7Channel.ReadOnlyDataHandlingAdapter as SingleStreamDataHandlingAdapter;
|
||||
|
||||
await siemensS7Master.ConnectAsync(CancellationToken.None);
|
||||
var task1 = Task.Run(async () =>
|
||||
{
|
||||
if (read)
|
||||
@@ -47,9 +49,11 @@ public class SiemensS7Test
|
||||
}
|
||||
|
||||
});
|
||||
await Task.Delay(500);
|
||||
await Task.Delay(100);
|
||||
bytes[12] = (byte)(((IClientChannel)(siemensS7Master.Channel)).WaitHandlePool.GetValue("m_currentSign").ToInt() - 1);
|
||||
var task2 = Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(100).ConfigureAwait(false);
|
||||
foreach (var item in bytes)
|
||||
{
|
||||
var data = new ByteBlock([item]);
|
||||
|
@@ -118,11 +118,22 @@ public partial class MqttClient : BusinessBaseWithCacheIntervalScript<VariableBa
|
||||
}
|
||||
else
|
||||
{
|
||||
mqttClientSubscribeOptionsBuilder = mqttClientSubscribeOptionsBuilder.WithTopicFilter(
|
||||
f =>
|
||||
{
|
||||
f.WithTopic(string.Format(null, RpcTopic, _driverPropertys.RpcWriteTopic));
|
||||
});
|
||||
if (!_driverPropertys.BigTextScriptRpc.IsNullOrEmpty())
|
||||
{
|
||||
mqttClientSubscribeOptionsBuilder = mqttClientSubscribeOptionsBuilder.WithTopicFilter(
|
||||
f =>
|
||||
{
|
||||
f.WithTopic(_driverPropertys.RpcWriteTopic);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
mqttClientSubscribeOptionsBuilder = mqttClientSubscribeOptionsBuilder.WithTopicFilter(
|
||||
f =>
|
||||
{
|
||||
f.WithTopic(string.Format(null, RpcTopic, _driverPropertys.RpcWriteTopic));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!_driverPropertys.RpcQuestTopic.IsNullOrWhiteSpace())
|
||||
|
@@ -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);
|
||||
|
||||
|
||||
|
@@ -100,7 +100,7 @@ public partial class OpcUaMaster : IDisposable
|
||||
LogMessage?.AddLogger(logger);
|
||||
|
||||
_plc.LogEvent = (a, b, c, d) => LogMessage?.Log((LogLevel)a, b, c, d);
|
||||
_plc.DataChangedHandler += (a) => LogMessage?.Trace(a.ToSystemTextJsonString());
|
||||
_plc.DataChangedHandler += (a) => LogMessage?.Trace($"id:{a.monitoredItem?.StartNodeId};stateCode:{a.dataValue?.StatusCode};value:{a.jToken?.ToString()}");
|
||||
base.OnInitialized();
|
||||
}
|
||||
|
||||
|
@@ -108,7 +108,7 @@ public class Startup : AppStartup
|
||||
.AddHubOptions(options =>
|
||||
{
|
||||
//单个传入集线器消息的最大大小。默认 32 KB
|
||||
options.MaximumReceiveMessageSize = 1024 * 1024;
|
||||
options.MaximumReceiveMessageSize = 32 * 1024 * 1024;
|
||||
//可为客户端上载流缓冲的最大项数。 如果达到此限制,则会阻止处理调用,直到服务器处理流项。
|
||||
options.StreamBufferCapacity = 30;
|
||||
options.ClientTimeoutInterval = TimeSpan.FromMinutes(2);
|
||||
@@ -127,7 +127,7 @@ public class Startup : AppStartup
|
||||
}).AddHubOptions(options =>
|
||||
{
|
||||
//单个传入集线器消息的最大大小。默认 32 KB
|
||||
options.MaximumReceiveMessageSize = 1024 * 1024;
|
||||
options.MaximumReceiveMessageSize = 32 * 1024 * 1024;
|
||||
//可为客户端上载流缓冲的最大项数。 如果达到此限制,则会阻止处理调用,直到服务器处理流项。
|
||||
options.StreamBufferCapacity = 30;
|
||||
options.ClientTimeoutInterval = TimeSpan.FromMinutes(2);
|
||||
|
@@ -19,3 +19,6 @@ ENV ASPNETCORE_FORWARDEDHEADERS_ENABLED=true
|
||||
RUN ln -snf /usr/share/zoneinfo/$TimeZone /etc/localtime && echo $TimeZone > /etc/timezone
|
||||
|
||||
ENTRYPOINT ["dotnet", "ThingsGateway.Server.dll","--urls","http://*:5000"]
|
||||
|
||||
|
||||
|
@@ -108,7 +108,7 @@ public class Startup : AppStartup
|
||||
.AddHubOptions(options =>
|
||||
{
|
||||
//单个传入集线器消息的最大大小。默认 32 KB
|
||||
options.MaximumReceiveMessageSize = 1024 * 1024;
|
||||
options.MaximumReceiveMessageSize = 32 * 1024 * 1024;
|
||||
//可为客户端上载流缓冲的最大项数。 如果达到此限制,则会阻止处理调用,直到服务器处理流项。
|
||||
options.StreamBufferCapacity = 30;
|
||||
options.ClientTimeoutInterval = TimeSpan.FromMinutes(2);
|
||||
@@ -127,7 +127,7 @@ public class Startup : AppStartup
|
||||
}).AddHubOptions(options =>
|
||||
{
|
||||
//单个传入集线器消息的最大大小。默认 32 KB
|
||||
options.MaximumReceiveMessageSize = 1024 * 1024;
|
||||
options.MaximumReceiveMessageSize =32 * 1024 * 1024;
|
||||
//可为客户端上载流缓冲的最大项数。 如果达到此限制,则会阻止处理调用,直到服务器处理流项。
|
||||
options.StreamBufferCapacity = 30;
|
||||
options.ClientTimeoutInterval = TimeSpan.FromMinutes(2);
|
||||
|
@@ -1,9 +1,12 @@
|
||||
|
||||
#docker run -d --name tg --restart always -p 127.0.0.1:5000:5000 -e ASPNETCORE_ENVIRONMENT=Demo -v /thingsgateway/Keys:/app/Keys -v /thingsgateway/DB:/app/DB --memory="512m" registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway:latest
|
||||
|
||||
version: "latest" # Docker Compose 配置版本
|
||||
|
||||
services: # 定义所有要跑的容器(服务)
|
||||
thingsgateway: #服务名称
|
||||
image: registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway:latest #镜像名:tag
|
||||
container_name: thingsgateway #容器名字
|
||||
container_name: tg #容器名字
|
||||
ports:
|
||||
- "127.0.0.1:5000:5000" #"主机端口:容器端口"
|
||||
environment:
|
||||
@@ -16,3 +19,4 @@ services: # 定义所有要跑的容器(服务)
|
||||
limits:
|
||||
memory: 512M
|
||||
restart: always # 容器异常退出自动重启
|
||||
|
||||
|
@@ -105,7 +105,7 @@ public class Startup : AppStartup
|
||||
.AddHubOptions(options =>
|
||||
{
|
||||
//单个传入集线器消息的最大大小。默认 32 KB
|
||||
options.MaximumReceiveMessageSize = 1024 * 1024;
|
||||
options.MaximumReceiveMessageSize = 32 * 1024 * 1024;
|
||||
//可为客户端上载流缓冲的最大项数。 如果达到此限制,则会阻止处理调用,直到服务器处理流项。
|
||||
options.StreamBufferCapacity = 30;
|
||||
options.ClientTimeoutInterval = TimeSpan.FromMinutes(2);
|
||||
@@ -124,7 +124,7 @@ public class Startup : AppStartup
|
||||
}).AddHubOptions(options =>
|
||||
{
|
||||
//单个传入集线器消息的最大大小。默认 32 KB
|
||||
options.MaximumReceiveMessageSize = 1024 * 1024;
|
||||
options.MaximumReceiveMessageSize = 32 * 1024 * 1024;
|
||||
//可为客户端上载流缓冲的最大项数。 如果达到此限制,则会阻止处理调用,直到服务器处理流项。
|
||||
options.StreamBufferCapacity = 30;
|
||||
options.ClientTimeoutInterval = TimeSpan.FromMinutes(2);
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<Version>10.9.11</Version>
|
||||
<Version>10.9.17</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
Reference in New Issue
Block a user