mirror of
				https://gitee.com/ThingsGateway/ThingsGateway.git
				synced 2025-10-31 07:33:58 +08:00 
			
		
		
		
	Compare commits
	
		
			6 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 5ec65b2fb0 | ||
|   | 926eced724 | ||
|   | f7f8802272 | ||
|   | c6910dff02 | ||
|   | ad299d0dbb | ||
|   | 8b124d1050 | 
| @@ -28,7 +28,7 @@ public class UserIdProvider : IUserIdProvider | ||||
|  | ||||
|         if (UserId > 0) | ||||
|         { | ||||
|             return $"{UserId}{SysHub.Separate}{YitIdHelper.NextId()}";//返回用户ID | ||||
|             return $"{UserId}{SysHub.Separate}{CommonUtils.GetSingleId()}";//返回用户ID | ||||
|         } | ||||
|  | ||||
|         return connection.ConnectionId; | ||||
|   | ||||
| @@ -117,6 +117,25 @@ public class SugarAopService : ISugarAopService | ||||
|         db.Aop.DataExecuted = (value, entity) => | ||||
|         { | ||||
|         }; | ||||
|  | ||||
|  | ||||
|         db.Aop.OnLogExecuted = (sql, pars) => | ||||
|         { | ||||
|             //执行时间超过1秒 | ||||
|             if (db.Ado.SqlExecutionTime.TotalSeconds > 1) | ||||
|             { | ||||
|                 //代码CS文件名 | ||||
|                 var fileName = db.Ado.SqlStackTrace.FirstFileName; | ||||
|                 //代码行数 | ||||
|                 var fileLine = db.Ado.SqlStackTrace.FirstLine; | ||||
|                 //方法名 | ||||
|                 var FirstMethodName = db.Ado.SqlStackTrace.FirstMethodName; | ||||
|  | ||||
|                 DbContext.WriteLog($"{fileName}-{FirstMethodName}-{fileLine} 执行时间超过1秒"); | ||||
|                 DbContext.WriteLogWithSql(UtilMethods.GetNativeSql(sql, pars)); | ||||
|  | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -37,7 +37,7 @@ public sealed class SqlSugarOption : ConnectionConfig | ||||
|     /// <summary> | ||||
|     /// 是否控制台显示Sql语句 | ||||
|     /// </summary> | ||||
|     public bool IsShowSql { get; set; } | ||||
|     public bool? IsShowSql { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 更新数据 | ||||
|   | ||||
| @@ -98,6 +98,21 @@ public class Startup : AppStartup | ||||
|         CodeFirstUtils.CodeFirst(fullName!);//CodeFirst | ||||
|  | ||||
|  | ||||
|         try | ||||
|         { | ||||
|             using var db = DbContext.GetDB<SysOperateLog>(); | ||||
|             if (db.CurrentConnectionConfig.DbType == SqlSugar.DbType.Sqlite) | ||||
|             { | ||||
|                 if (!db.DbMaintenance.IsAnyIndex("idx_operatelog_optime_date")) | ||||
|                 { | ||||
|                     var indexsql = "CREATE INDEX idx_operatelog_optime_date ON sys_operatelog(strftime('%Y-%m-%d', OpTime));"; | ||||
|                     db.Ado.ExecuteCommand(indexsql); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         catch { } | ||||
|  | ||||
|  | ||||
|         //删除在线用户统计 | ||||
|         var verificatInfoService = App.RootServices.GetService<IVerificatInfoService>(); | ||||
|         verificatInfoService.RemoveAllClientId(); | ||||
|   | ||||
| @@ -25,7 +25,7 @@ public static class ILoggerExtensions | ||||
|     /// <param name="logger"></param> | ||||
|     /// <param name="properties">建议使用 ConcurrentDictionary 类型</param> | ||||
|     /// <returns></returns> | ||||
|     public static IDisposable ScopeContext(this ILogger logger, IDictionary<object, object> properties) | ||||
|     public static IDisposable ScopeContext(this ILogger logger, IDictionary<string, object> properties) | ||||
|     { | ||||
|         if (logger == null) throw new ArgumentNullException(nameof(logger)); | ||||
|  | ||||
|   | ||||
| @@ -26,11 +26,11 @@ public static class LogContextExtensions | ||||
|     /// <param name="key">键</param> | ||||
|     /// <param name="value">值</param> | ||||
|     /// <returns></returns> | ||||
|     public static LogContext Set(this LogContext logContext, object key, object value) | ||||
|     public static LogContext Set(this LogContext logContext, string key, object value) | ||||
|     { | ||||
|         if (logContext == null || key == null) return logContext; | ||||
|  | ||||
|         logContext.Properties ??= new Dictionary<object, object>(); | ||||
|         logContext.Properties ??= new Dictionary<string, object>(); | ||||
|  | ||||
|         logContext.Properties.Remove(key); | ||||
|         logContext.Properties.Add(key, value); | ||||
| @@ -43,7 +43,7 @@ public static class LogContextExtensions | ||||
|     /// <param name="logContext"></param> | ||||
|     /// <param name="properties"></param> | ||||
|     /// <returns></returns> | ||||
|     public static LogContext SetRange(this LogContext logContext, IDictionary<object, object> properties) | ||||
|     public static LogContext SetRange(this LogContext logContext, IDictionary<string, object> properties) | ||||
|     { | ||||
|         if (logContext == null | ||||
|             || properties == null | ||||
| @@ -63,7 +63,7 @@ public static class LogContextExtensions | ||||
|     /// <param name="logContext"></param> | ||||
|     /// <param name="key">键</param> | ||||
|     /// <returns></returns> | ||||
|     public static object Get(this LogContext logContext, object key) | ||||
|     public static object Get(this LogContext logContext, string key) | ||||
|     { | ||||
|         if (logContext == null | ||||
|             || key == null | ||||
| @@ -80,7 +80,7 @@ public static class LogContextExtensions | ||||
|     /// <param name="logContext"></param> | ||||
|     /// <param name="key">键</param> | ||||
|     /// <returns></returns> | ||||
|     public static T Get<T>(this LogContext logContext, object key) | ||||
|     public static T Get<T>(this LogContext logContext, string key) | ||||
|     { | ||||
|         var value = logContext.Get(key); | ||||
|         return value.ChangeType<T>(); | ||||
|   | ||||
| @@ -84,7 +84,7 @@ public static class StringLoggingExtensions | ||||
|     /// <param name="message"></param> | ||||
|     /// <param name="properties">建议使用 ConcurrentDictionary 类型</param> | ||||
|     /// <returns></returns> | ||||
|     public static StringLoggingPart ScopeContext(this string message, IDictionary<object, object> properties) | ||||
|     public static StringLoggingPart ScopeContext(this string message, IDictionary<string, object> properties) | ||||
|     { | ||||
|         return StringLoggingPart.Default().SetMessage(message).ScopeContext(properties); | ||||
|     } | ||||
|   | ||||
| @@ -20,7 +20,7 @@ public sealed class LogContext : IDisposable | ||||
|     /// <summary> | ||||
|     /// 日志上下文数据 | ||||
|     /// </summary> | ||||
|     public IDictionary<object, object> Properties { get; set; } | ||||
|     public IDictionary<string, object> Properties { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 原生日志上下文数据 | ||||
|   | ||||
| @@ -96,7 +96,7 @@ public sealed partial class StringLoggingPart | ||||
|     /// </summary> | ||||
|     /// <param name="properties">建议使用 ConcurrentDictionary 类型</param> | ||||
|     /// <returns></returns> | ||||
|     public StringLoggingPart ScopeContext(IDictionary<object, object> properties) | ||||
|     public StringLoggingPart ScopeContext(IDictionary<string, object> properties) | ||||
|     { | ||||
|         if (properties == null) return this; | ||||
|         LogContext = new LogContext { Properties = properties }; | ||||
|   | ||||
| @@ -59,7 +59,7 @@ public static class Log | ||||
|     /// </summary> | ||||
|     /// <param name="properties">建议使用 ConcurrentDictionary 类型</param> | ||||
|     /// <returns></returns> | ||||
|     public static (ILogger logger, IDisposable scope) ScopeContext(IDictionary<object, object> properties) | ||||
|     public static (ILogger logger, IDisposable scope) ScopeContext(IDictionary<string, object> properties) | ||||
|     { | ||||
|         return GetLogger(StringLoggingPart.Default().ScopeContext(properties)); | ||||
|     } | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| //下载文件 | ||||
| export function blazor_downloadFile(url, fileName, dtoObject) { | ||||
| export async function blazor_downloadFile(url, fileName, dtoObject) { | ||||
|     const params = new URLSearchParams(); | ||||
|  | ||||
|     // 将 dtoObject 的属性添加到 URLSearchParams 中 | ||||
| @@ -12,97 +12,92 @@ export function blazor_downloadFile(url, fileName, dtoObject) { | ||||
|     // 构建完整的 URL | ||||
|     const fullUrl = `${url}?${params.toString()}`; | ||||
|  | ||||
|     // 发起 fetch 请求 | ||||
|     fetch(fullUrl) | ||||
|         .then(response => { | ||||
|             // 获取响应头中的 content-disposition | ||||
|             const dispositionHeader = response.headers.get('content-disposition'); | ||||
|             let resolvedFileName = fileName; | ||||
|     try { | ||||
|         // 发起 fetch 请求 | ||||
|         const response = await fetch(fullUrl); | ||||
|  | ||||
|             if (dispositionHeader) { | ||||
|                 // 解析出文件名 | ||||
|                 const match = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(dispositionHeader); | ||||
|                 const serverFileName = match && match[1] ? match[1].replace(/['"]/g, '') : null; | ||||
|                 if (serverFileName) { | ||||
|                     resolvedFileName = serverFileName; | ||||
|                 } | ||||
|         // 获取响应头中的 content-disposition | ||||
|         const dispositionHeader = response.headers.get('content-disposition'); | ||||
|         let resolvedFileName = fileName; | ||||
|  | ||||
|         if (dispositionHeader) { | ||||
|             // 解析出文件名 | ||||
|             const match = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(dispositionHeader); | ||||
|             const serverFileName = match && match[1] ? match[1].replace(/['"]/g, '') : null; | ||||
|             if (serverFileName) { | ||||
|                 resolvedFileName = serverFileName; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|             // 将响应转换为 blob 对象 | ||||
|             return response.blob().then(blob => { | ||||
|                 // 创建临时的文件 URL | ||||
|                 const fileUrl = window.URL.createObjectURL(blob); | ||||
|         // 将响应转换为 blob 对象 | ||||
|         const blob = await response.blob(); | ||||
|  | ||||
|                 // 创建一个 <a> 元素并设置下载链接和文件名 | ||||
|                 const anchorElement = document.createElement('a'); | ||||
|                 anchorElement.href = fileUrl; | ||||
|                 anchorElement.download = resolvedFileName; | ||||
|                 anchorElement.style.display = 'none'; | ||||
|         // 创建临时的文件 URL | ||||
|         const fileUrl = window.URL.createObjectURL(blob); | ||||
|  | ||||
|                 // 将 <a> 元素添加到 body 中并触发下载 | ||||
|                 document.body.appendChild(anchorElement); | ||||
|                 anchorElement.click(); | ||||
|                 document.body.removeChild(anchorElement); | ||||
|         // 创建一个 <a> 元素并设置下载链接和文件名 | ||||
|         const anchorElement = document.createElement('a'); | ||||
|         anchorElement.href = fileUrl; | ||||
|         anchorElement.download = resolvedFileName; | ||||
|         anchorElement.style.display = 'none'; | ||||
|  | ||||
|                 // 撤销临时的文件 URL | ||||
|                 window.URL.revokeObjectURL(fileUrl); | ||||
|             }); | ||||
|         }) | ||||
|         .catch(error => { | ||||
|             console.error('DownFile error ', error); | ||||
|         }); | ||||
|         // 将 <a> 元素添加到 body 中并触发下载 | ||||
|         document.body.appendChild(anchorElement); | ||||
|         anchorElement.click(); | ||||
|         document.body.removeChild(anchorElement); | ||||
|  | ||||
|         // 撤销临时的文件 URL | ||||
|         window.URL.revokeObjectURL(fileUrl); | ||||
|  | ||||
|         return true; | ||||
|     } catch (error) { | ||||
|         console.error('DownFile error ', error); | ||||
|         throw error; | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| //下载文件 | ||||
| export function postJson_downloadFile(url, fileName, jsonBody) { | ||||
|     const params = new URLSearchParams(); | ||||
| export async function postJson_downloadFile(url, fileName, jsonBody) { | ||||
|  | ||||
|  | ||||
|     // 发起 fetch 请求 | ||||
|     fetch(url, { | ||||
|         method: 'POST', | ||||
|         headers: { | ||||
|             'Content-Type': 'application/json' | ||||
|         }, | ||||
|         body: jsonBody | ||||
|     }) | ||||
|         .then(response => { | ||||
|             // 获取响应头中的 content-disposition | ||||
|             const dispositionHeader = response.headers.get('content-disposition'); | ||||
|             let resolvedFileName = fileName; | ||||
|  | ||||
|             if (dispositionHeader) { | ||||
|                 // 解析出文件名 | ||||
|                 const match = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(dispositionHeader); | ||||
|                 const serverFileName = match && match[1] ? match[1].replace(/['"]/g, '') : null; | ||||
|                 if (serverFileName) { | ||||
|                     resolvedFileName = serverFileName; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // 将响应转换为 blob 对象 | ||||
|             return response.blob().then(blob => { | ||||
|                 // 创建临时的文件 URL | ||||
|                 const fileUrl = window.URL.createObjectURL(blob); | ||||
|  | ||||
|                 // 创建一个 <a> 元素并设置下载链接和文件名 | ||||
|                 const anchorElement = document.createElement('a'); | ||||
|                 anchorElement.href = fileUrl; | ||||
|                 anchorElement.download = resolvedFileName; | ||||
|                 anchorElement.style.display = 'none'; | ||||
|  | ||||
|                 // 将 <a> 元素添加到 body 中并触发下载 | ||||
|                 document.body.appendChild(anchorElement); | ||||
|                 anchorElement.click(); | ||||
|                 document.body.removeChild(anchorElement); | ||||
|  | ||||
|                 // 撤销临时的文件 URL | ||||
|                 window.URL.revokeObjectURL(fileUrl); | ||||
|             }); | ||||
|         }) | ||||
|         .catch(error => { | ||||
|             console.error('downfile error ', error); | ||||
|     try { | ||||
|         const response = await fetch(url, { | ||||
|             method: 'POST', | ||||
|             headers: { | ||||
|                 'Content-Type': 'application/json' | ||||
|             }, | ||||
|             body: jsonBody | ||||
|         }); | ||||
|  | ||||
|         const dispositionHeader = response.headers.get('content-disposition'); | ||||
|         let resolvedFileName = fileName; | ||||
|  | ||||
|         if (dispositionHeader) { | ||||
|             const match = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(dispositionHeader); | ||||
|             const serverFileName = match && match[1] ? match[1].replace(/['"]/g, '') : null; | ||||
|             if (serverFileName) { | ||||
|                 resolvedFileName = serverFileName; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         const blob = await response.blob(); | ||||
|         const fileUrl = window.URL.createObjectURL(blob); | ||||
|  | ||||
|         const anchorElement = document.createElement('a'); | ||||
|         anchorElement.href = fileUrl; | ||||
|         anchorElement.download = resolvedFileName; | ||||
|         anchorElement.style.display = 'none'; | ||||
|  | ||||
|         document.body.appendChild(anchorElement); | ||||
|         anchorElement.click(); | ||||
|         document.body.removeChild(anchorElement); | ||||
|  | ||||
|         window.URL.revokeObjectURL(fileUrl); | ||||
|  | ||||
|         return true; // 唯一新增的返回值 | ||||
|     } catch (error) { | ||||
|         console.error('downfile error ', error); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| <Project> | ||||
|  | ||||
| 	<PropertyGroup> | ||||
| 		<PluginVersion>10.6.24</PluginVersion> | ||||
| 		<ProPluginVersion>10.6.24</ProPluginVersion> | ||||
| 		<PluginVersion>10.6.29</PluginVersion> | ||||
| 		<ProPluginVersion>10.6.29</ProPluginVersion> | ||||
| 		<AuthenticationVersion>2.1.8</AuthenticationVersion> | ||||
| 	</PropertyGroup> | ||||
|  | ||||
|   | ||||
| @@ -61,14 +61,14 @@ internal sealed class HeartbeatAndReceivePlugin : PluginBase, ITcpConnectedPlugi | ||||
|         { | ||||
|             return;//此处可判断,如果为服务器,则不用使用心跳。 | ||||
|         } | ||||
|  | ||||
|         if (HeartbeatTime > 0) | ||||
|             SendHeartbeat = true; | ||||
|         HeartbeatTime = Math.Max(HeartbeatTime, 1000); | ||||
|  | ||||
|         if (DtuId.IsNullOrWhiteSpace()) return; | ||||
|  | ||||
|         if (client is ITcpClient tcpClient) | ||||
|         { | ||||
|             SendHeartbeat = true; | ||||
|             await tcpClient.SendAsync(DtuIdByte).ConfigureAwait(false); | ||||
|  | ||||
|             if (Task == null) | ||||
|   | ||||
| @@ -26,28 +26,53 @@ internal sealed class GatewayExportService : IGatewayExportService | ||||
|  | ||||
|     private IJSRuntime JSRuntime { get; set; } | ||||
|  | ||||
|     public async Task OnChannelExport(ExportFilter exportFilter) | ||||
|     public async Task<bool> OnChannelExport(ExportFilter exportFilter) | ||||
|     { | ||||
|         await using var ajaxJS = await JSRuntime.InvokeAsync<IJSObjectReference>("import", $"/_content/ThingsGateway.Razor/js/downloadFile.js"); | ||||
|         string url = "api/gatewayExport/channel"; | ||||
|         string fileName = $"{DateTime.Now.ToFileDateTimeFormat()}.xlsx"; | ||||
|         await ajaxJS.InvokeVoidAsync("postJson_downloadFile", url, fileName, exportFilter.ToJsonString()); | ||||
|         try | ||||
|         { | ||||
|  | ||||
|             await using var ajaxJS = await JSRuntime.InvokeAsync<IJSObjectReference>("import", $"/_content/ThingsGateway.Razor/js/downloadFile.js"); | ||||
|             string url = "api/gatewayExport/channel"; | ||||
|             string fileName = $"{DateTime.Now.ToFileDateTimeFormat()}.xlsx"; | ||||
|             return await ajaxJS.InvokeAsync<bool>("postJson_downloadFile", url, fileName, exportFilter.ToJsonString()); | ||||
|  | ||||
|         } | ||||
|         catch | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task OnDeviceExport(ExportFilter exportFilter) | ||||
|     public async Task<bool> OnDeviceExport(ExportFilter exportFilter) | ||||
|     { | ||||
|         await using var ajaxJS = await JSRuntime.InvokeAsync<IJSObjectReference>("import", $"/_content/ThingsGateway.Razor/js/downloadFile.js"); | ||||
|         string url = "api/gatewayExport/device"; | ||||
|         string fileName = $"{DateTime.Now.ToFileDateTimeFormat()}.xlsx"; | ||||
|         await ajaxJS.InvokeVoidAsync("postJson_downloadFile", url, fileName, exportFilter.ToJsonString()); | ||||
|         try | ||||
|         { | ||||
|  | ||||
|  | ||||
|             await using var ajaxJS = await JSRuntime.InvokeAsync<IJSObjectReference>("import", $"/_content/ThingsGateway.Razor/js/downloadFile.js"); | ||||
|             string url = "api/gatewayExport/device"; | ||||
|             string fileName = $"{DateTime.Now.ToFileDateTimeFormat()}.xlsx"; | ||||
|             return await ajaxJS.InvokeAsync<bool>("postJson_downloadFile", url, fileName, exportFilter.ToJsonString()); | ||||
|         } | ||||
|         catch | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task OnVariableExport(ExportFilter exportFilter) | ||||
|     public async Task<bool> OnVariableExport(ExportFilter exportFilter) | ||||
|     { | ||||
|         await using var ajaxJS = await JSRuntime.InvokeAsync<IJSObjectReference>("import", $"/_content/ThingsGateway.Razor/js/downloadFile.js"); | ||||
|         string url = "api/gatewayExport/variable"; | ||||
|         string fileName = $"{DateTime.Now.ToFileDateTimeFormat()}.xlsx"; | ||||
|         await ajaxJS.InvokeVoidAsync("postJson_downloadFile", url, fileName, exportFilter.ToJsonString()); | ||||
|         try | ||||
|         { | ||||
|             await using var ajaxJS = await JSRuntime.InvokeAsync<IJSObjectReference>("import", $"/_content/ThingsGateway.Razor/js/downloadFile.js"); | ||||
|             string url = "api/gatewayExport/variable"; | ||||
|             string fileName = $"{DateTime.Now.ToFileDateTimeFormat()}.xlsx"; | ||||
|             return await ajaxJS.InvokeAsync<bool>("postJson_downloadFile", url, fileName, exportFilter.ToJsonString()); | ||||
|         } | ||||
|         catch | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -35,18 +35,30 @@ public sealed class HybridGatewayExportService : IGatewayExportService | ||||
|  | ||||
|     } | ||||
|  | ||||
|     public async Task OnChannelExport(ExportFilter exportFilter) | ||||
|     public async Task<bool> OnChannelExport(ExportFilter exportFilter) | ||||
|     { | ||||
|         exportFilter.QueryPageOptions.IsPage = false; | ||||
|         exportFilter.QueryPageOptions.IsVirtualScroll = false; | ||||
|         try | ||||
|         { | ||||
|  | ||||
|         var sheets = await _channelService.ExportChannelAsync(exportFilter).ConfigureAwait(false); | ||||
|         var path = await _importExportService.CreateFileAsync<Device>(sheets, "Channel", false).ConfigureAwait(false); | ||||
|  | ||||
|         Open(path); | ||||
|             exportFilter.QueryPageOptions.IsPage = false; | ||||
|             exportFilter.QueryPageOptions.IsVirtualScroll = false; | ||||
|  | ||||
|             var sheets = await _channelService.ExportChannelAsync(exportFilter).ConfigureAwait(false); | ||||
|             var path = await _importExportService.CreateFileAsync<Device>(sheets, "Channel", false).ConfigureAwait(false); | ||||
|  | ||||
|             Open(path); | ||||
|             return true; | ||||
|  | ||||
|         } | ||||
|         catch | ||||
|         { | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private static void Open(string path) | ||||
|     private static bool Open(string path) | ||||
|     { | ||||
|         path = System.IO.Path.GetDirectoryName(path); // Ensure the path is absolute | ||||
|  | ||||
| @@ -63,25 +75,47 @@ public sealed class HybridGatewayExportService : IGatewayExportService | ||||
|         { | ||||
|             System.Diagnostics.Process.Start("open", path); | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     public async Task OnDeviceExport(ExportFilter exportFilter) | ||||
|     public async Task<bool> OnDeviceExport(ExportFilter exportFilter) | ||||
|     { | ||||
|         exportFilter.QueryPageOptions.IsPage = false; | ||||
|         exportFilter.QueryPageOptions.IsVirtualScroll = false; | ||||
|         var sheets = await _deviceService.ExportDeviceAsync(exportFilter).ConfigureAwait(false); | ||||
|         var path = await _importExportService.CreateFileAsync<Device>(sheets, "Device", false).ConfigureAwait(false); | ||||
|         Open(path); | ||||
|         try | ||||
|         { | ||||
|  | ||||
|             exportFilter.QueryPageOptions.IsPage = false; | ||||
|             exportFilter.QueryPageOptions.IsVirtualScroll = false; | ||||
|             var sheets = await _deviceService.ExportDeviceAsync(exportFilter).ConfigureAwait(false); | ||||
|             var path = await _importExportService.CreateFileAsync<Device>(sheets, "Device", false).ConfigureAwait(false); | ||||
|             Open(path); | ||||
|  | ||||
|             return true; | ||||
|  | ||||
|         } | ||||
|         catch | ||||
|         { | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task OnVariableExport(ExportFilter exportFilter) | ||||
|     public async Task<bool> OnVariableExport(ExportFilter exportFilter) | ||||
|     { | ||||
|         exportFilter.QueryPageOptions.IsPage = false; | ||||
|         exportFilter.QueryPageOptions.IsVirtualScroll = false; | ||||
|         var sheets = await _variableService.ExportVariableAsync(exportFilter).ConfigureAwait(false); | ||||
|         var path = await _importExportService.CreateFileAsync<Variable>(sheets, "Variable", false).ConfigureAwait(false); | ||||
|         Open(path); | ||||
|         try | ||||
|         { | ||||
|  | ||||
|             exportFilter.QueryPageOptions.IsPage = false; | ||||
|             exportFilter.QueryPageOptions.IsVirtualScroll = false; | ||||
|             var sheets = await _variableService.ExportVariableAsync(exportFilter).ConfigureAwait(false); | ||||
|             var path = await _importExportService.CreateFileAsync<Variable>(sheets, "Variable", false).ConfigureAwait(false); | ||||
|             Open(path); | ||||
|             return true; | ||||
|         } | ||||
|         catch | ||||
|         { | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -12,7 +12,7 @@ namespace ThingsGateway.Gateway.Application; | ||||
|  | ||||
| public interface IGatewayExportService | ||||
| { | ||||
|     Task OnChannelExport(ExportFilter exportFilter); | ||||
|     Task OnDeviceExport(ExportFilter exportFilter); | ||||
|     Task OnVariableExport(ExportFilter exportFilter); | ||||
|     Task<bool> OnChannelExport(ExportFilter exportFilter); | ||||
|     Task<bool> OnDeviceExport(ExportFilter exportFilter); | ||||
|     Task<bool> OnVariableExport(ExportFilter exportFilter); | ||||
| } | ||||
|   | ||||
| @@ -518,7 +518,7 @@ internal sealed class PluginService : IPluginService | ||||
|     { | ||||
|         var fileInfo = new FileInfo(path); | ||||
|         if (fileInfo.Exists) | ||||
|             fileInfo.MoveTo($"{path}{YitIdHelper.NextId()}{DelEx}", true); | ||||
|             fileInfo.MoveTo($"{path}{CommonUtils.GetSingleId()}{DelEx}", true); | ||||
|         else | ||||
|             return false; | ||||
|         return true; | ||||
|   | ||||
| @@ -130,8 +130,35 @@ public class Startup : AppStartup | ||||
|         } | ||||
|         catch { } | ||||
|  | ||||
|         try | ||||
|         { | ||||
|             using var db = DbContext.GetDB<BackendLog>(); | ||||
|             if (db.CurrentConnectionConfig.DbType == SqlSugar.DbType.Sqlite) | ||||
|             { | ||||
|  | ||||
|                 if (!db.DbMaintenance.IsAnyIndex("idx_backendlog_logtime_date")) | ||||
|                 { | ||||
|                     var indexsql = "CREATE INDEX idx_backendlog_logtime_date ON backend_log(strftime('%Y-%m-%d', LogTime));"; | ||||
|                     db.Ado.ExecuteCommand(indexsql); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         catch { } | ||||
|  | ||||
|         try | ||||
|         { | ||||
|             using var db = DbContext.GetDB<RpcLog>(); | ||||
|             if (db.CurrentConnectionConfig.DbType == SqlSugar.DbType.Sqlite) | ||||
|             { | ||||
|  | ||||
|                 if (!db.DbMaintenance.IsAnyIndex("idx_rpclog_logtime_date")) | ||||
|                 { | ||||
|                     var indexsql = "CREATE INDEX idx_rpclog_logtime_date ON rpc_log(strftime('%Y-%m-%d', LogTime));"; | ||||
|                     db.Ado.ExecuteCommand(indexsql); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         catch { } | ||||
|  | ||||
|         serviceProvider.GetService<IHostApplicationLifetime>().ApplicationStarted.Register(() => | ||||
|         { | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
|  | ||||
| 	<Import Project="$(SolutionDir)Version.props" /> | ||||
| 	<Import Project="$(SolutionDir)PackNuget.props" /> | ||||
| @@ -10,8 +10,7 @@ | ||||
| 		<PackageReference Include="Rougamo.Fody" Version="5.0.0" /> | ||||
| 		<PackageReference Include="TouchSocket.Dmtp" Version="3.1.5" /> | ||||
| 		<PackageReference Include="TouchSocket.WebApi.Swagger" Version="3.1.5" /> | ||||
| 		<!--<PackageReference Include="ThingsGateway.Authentication" Version="$(AuthenticationVersion)" />--> | ||||
| 		<ProjectReference Include="..\..\PluginPro\ThingsGateway.Authentication\ThingsGateway.Authentication.csproj" /> | ||||
| 		<PackageReference Include="ThingsGateway.Authentication" Version="$(AuthenticationVersion)" /> | ||||
|  | ||||
| 	</ItemGroup> | ||||
|  | ||||
|   | ||||
| @@ -10,6 +10,7 @@ | ||||
|  | ||||
| using Mapster; | ||||
|  | ||||
| using ThingsGateway.Admin.Application; | ||||
| using ThingsGateway.Gateway.Application; | ||||
|  | ||||
| using Yitter.IdGenerator; | ||||
| @@ -54,14 +55,14 @@ public partial class ChannelCopyComponent | ||||
|             for (int i = 0; i < CopyCount; i++) | ||||
|             { | ||||
|                 Channel channel = Model.Adapt<Channel>(); | ||||
|                 channel.Id = YitIdHelper.NextId(); | ||||
|                 channel.Id = CommonUtils.GetSingleId(); | ||||
|                 channel.Name = $"{CopyChannelNamePrefix}{CopyChannelNameSuffixNumber + i}"; | ||||
|  | ||||
|                 int index = 0; | ||||
|                 foreach (var item in Devices) | ||||
|                 { | ||||
|                     Device device = item.Key.Adapt<Device>(); | ||||
|                     device.Id = YitIdHelper.NextId(); | ||||
|                     device.Id = CommonUtils.GetSingleId(); | ||||
|                     device.Name = $"{channel.Name}_{CopyDeviceNamePrefix}{CopyDeviceNameSuffixNumber + (index++)}"; | ||||
|                     device.ChannelId = channel.Id; | ||||
|                     List<Variable> variables = new(); | ||||
| @@ -69,7 +70,7 @@ public partial class ChannelCopyComponent | ||||
|                     foreach (var variable in item.Value) | ||||
|                     { | ||||
|                         Variable v = variable.Adapt<Variable>(); | ||||
|                         v.Id = YitIdHelper.NextId(); | ||||
|                         v.Id = CommonUtils.GetSingleId(); | ||||
|                         v.DeviceId = device.Id; | ||||
|                         variables.Add(v); | ||||
|                     } | ||||
|   | ||||
| @@ -220,9 +220,10 @@ public partial class ChannelTable : IDisposable | ||||
|  | ||||
|     private async Task ExcelExportAsync(ITableExportContext<ChannelRuntime> tableExportContext, bool all = false) | ||||
|     { | ||||
|         bool ret; | ||||
|         if (all) | ||||
|         { | ||||
|             await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new() }); | ||||
|             ret = await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new() }); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
| @@ -230,16 +231,16 @@ public partial class ChannelTable : IDisposable | ||||
|             { | ||||
|  | ||||
|                 case ChannelDevicePluginTypeEnum.PluginName: | ||||
|                     await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new(), PluginName = SelectModel.PluginName }); | ||||
|                     ret = await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new(), PluginName = SelectModel.PluginName }); | ||||
|                     break; | ||||
|                 case ChannelDevicePluginTypeEnum.Channel: | ||||
|                     await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new(), ChannelId = SelectModel.ChannelRuntime.Id }); | ||||
|                     ret = await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new(), ChannelId = SelectModel.ChannelRuntime.Id }); | ||||
|                     break; | ||||
|                 case ChannelDevicePluginTypeEnum.Device: | ||||
|                     await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new(), DeviceId = SelectModel.DeviceRuntime.Id, PluginType = SelectModel.DeviceRuntime.PluginType }); | ||||
|                     ret = await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new(), DeviceId = SelectModel.DeviceRuntime.Id, PluginType = SelectModel.DeviceRuntime.PluginType }); | ||||
|                     break; | ||||
|                 default: | ||||
|                     await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new() }); | ||||
|                     ret = await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new() }); | ||||
|  | ||||
|                     break; | ||||
|             } | ||||
| @@ -247,7 +248,8 @@ public partial class ChannelTable : IDisposable | ||||
|         } | ||||
|  | ||||
|         // 返回 true 时自动弹出提示框 | ||||
|         await ToastService.Default(); | ||||
|         if (ret) | ||||
|             await ToastService.Default(); | ||||
|     } | ||||
|  | ||||
|     async Task ExcelChannelAsync(ITableExportContext<ChannelRuntime> tableExportContext) | ||||
|   | ||||
| @@ -530,20 +530,21 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e => | ||||
|     } | ||||
|     async Task ExportCurrentChannel(ContextMenuItem item, object value) | ||||
|     { | ||||
|         bool ret; | ||||
|         if (value is not ChannelDeviceTreeItem channelDeviceTreeItem) return; | ||||
|  | ||||
|         if (channelDeviceTreeItem.TryGetChannelRuntime(out var channelRuntime)) | ||||
|         { | ||||
|             await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new(), DeviceId = channelRuntime.Id }); | ||||
|             ret = await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new(), DeviceId = channelRuntime.Id }); | ||||
|         } | ||||
|         else if (channelDeviceTreeItem.TryGetPluginName(out var pluginName)) | ||||
|         { | ||||
|             //插件名称 | ||||
|             await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new(), PluginName = pluginName }); | ||||
|             ret = await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new(), PluginName = pluginName }); | ||||
|         } | ||||
|         else if (channelDeviceTreeItem.TryGetPluginType(out var pluginType)) | ||||
|         { | ||||
|             await GatewayExportService.OnChannelExport(new ExportFilter() { QueryPageOptions = new(), PluginType = pluginType }); | ||||
|             ret = await GatewayExportService.OnChannelExport(new ExportFilter() { QueryPageOptions = new(), PluginType = pluginType }); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
| @@ -551,14 +552,17 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e => | ||||
|         } | ||||
|  | ||||
|         // 返回 true 时自动弹出提示框 | ||||
|         await ToastService.Default(); | ||||
|         if (ret) | ||||
|             await ToastService.Default(); | ||||
|     } | ||||
|     async Task ExportAllChannel(ContextMenuItem item, object value) | ||||
|     { | ||||
|         await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new() }); | ||||
|         bool ret; | ||||
|         ret = await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new() }); | ||||
|  | ||||
|         // 返回 true 时自动弹出提示框 | ||||
|         await ToastService.Default(); | ||||
|         if (ret) | ||||
|             await ToastService.Default(); | ||||
|     } | ||||
|  | ||||
|  | ||||
| @@ -1065,25 +1069,26 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e => | ||||
|  | ||||
|     async Task ExportCurrentDevice(ContextMenuItem item, object value) | ||||
|     { | ||||
|         bool ret; | ||||
|         if (value is not ChannelDeviceTreeItem channelDeviceTreeItem) return; | ||||
|  | ||||
|         if (channelDeviceTreeItem.TryGetDeviceRuntime(out var deviceRuntime)) | ||||
|         { | ||||
|             await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), DeviceId = deviceRuntime.Id }); | ||||
|             ret = await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), DeviceId = deviceRuntime.Id }); | ||||
|         } | ||||
|         else if (channelDeviceTreeItem.TryGetChannelRuntime(out var channelRuntime)) | ||||
|         { | ||||
|             await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), ChannelId = channelRuntime.Id }); | ||||
|             ret = await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), ChannelId = channelRuntime.Id }); | ||||
|         } | ||||
|         else if (channelDeviceTreeItem.TryGetPluginName(out var pluginName)) | ||||
|         { | ||||
|             //插件名称 | ||||
|             await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), PluginName = pluginName }); | ||||
|             ret = await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), PluginName = pluginName }); | ||||
|         } | ||||
|         else if (channelDeviceTreeItem.TryGetPluginType(out var pluginType)) | ||||
|         { | ||||
|             //采集 | ||||
|             await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), PluginType = pluginType }); | ||||
|             ret = await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), PluginType = pluginType }); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
| @@ -1091,14 +1096,17 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e => | ||||
|         } | ||||
|  | ||||
|         // 返回 true 时自动弹出提示框 | ||||
|         await ToastService.Default(); | ||||
|         if (ret) | ||||
|             await ToastService.Default(); | ||||
|     } | ||||
|     async Task ExportAllDevice(ContextMenuItem item, object value) | ||||
|     { | ||||
|         await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new() }); | ||||
|         bool ret; | ||||
|         ret = await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new() }); | ||||
|  | ||||
|         // 返回 true 时自动弹出提示框 | ||||
|         await ToastService.Default(); | ||||
|         if (ret) | ||||
|             await ToastService.Default(); | ||||
|     } | ||||
|  | ||||
|     async Task ImportDevice(ContextMenuItem item, object value) | ||||
|   | ||||
| @@ -10,6 +10,7 @@ | ||||
|  | ||||
| using Mapster; | ||||
|  | ||||
| using ThingsGateway.Admin.Application; | ||||
| using ThingsGateway.Gateway.Application; | ||||
|  | ||||
| using Yitter.IdGenerator; | ||||
| @@ -50,14 +51,14 @@ public partial class DeviceCopyComponent | ||||
|             for (int i = 0; i < CopyCount; i++) | ||||
|             { | ||||
|                 Device device = Model.Adapt<Device>(); | ||||
|                 device.Id = YitIdHelper.NextId(); | ||||
|                 device.Id = CommonUtils.GetSingleId(); | ||||
|                 device.Name = $"{CopyDeviceNamePrefix}{CopyDeviceNameSuffixNumber + i}"; | ||||
|                 List<Variable> variables = new(); | ||||
|  | ||||
|                 foreach (var item in Variables) | ||||
|                 { | ||||
|                     Variable v = item.Adapt<Variable>(); | ||||
|                     v.Id = YitIdHelper.NextId(); | ||||
|                     v.Id = CommonUtils.GetSingleId(); | ||||
|                     v.DeviceId = device.Id; | ||||
|                     variables.Add(v); | ||||
|                 } | ||||
|   | ||||
| @@ -221,9 +221,10 @@ public partial class DeviceTable : IDisposable | ||||
|  | ||||
|     private async Task ExcelExportAsync(ITableExportContext<DeviceRuntime> tableExportContext, bool all = false) | ||||
|     { | ||||
|         bool ret; | ||||
|         if (all) | ||||
|         { | ||||
|             await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new() }); | ||||
|             ret = await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new() }); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
| @@ -231,16 +232,16 @@ public partial class DeviceTable : IDisposable | ||||
|             { | ||||
|  | ||||
|                 case ChannelDevicePluginTypeEnum.PluginName: | ||||
|                     await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), PluginName = SelectModel.PluginName }); | ||||
|                     ret = await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), PluginName = SelectModel.PluginName }); | ||||
|                     break; | ||||
|                 case ChannelDevicePluginTypeEnum.Channel: | ||||
|                     await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), ChannelId = SelectModel.ChannelRuntime.Id }); | ||||
|                     ret = await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), ChannelId = SelectModel.ChannelRuntime.Id }); | ||||
|                     break; | ||||
|                 case ChannelDevicePluginTypeEnum.Device: | ||||
|                     await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), DeviceId = SelectModel.DeviceRuntime.Id, PluginType = SelectModel.DeviceRuntime.PluginType }); | ||||
|                     ret = await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), DeviceId = SelectModel.DeviceRuntime.Id, PluginType = SelectModel.DeviceRuntime.PluginType }); | ||||
|                     break; | ||||
|                 default: | ||||
|                     await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new() }); | ||||
|                     ret = await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new() }); | ||||
|  | ||||
|                     break; | ||||
|             } | ||||
| @@ -248,7 +249,8 @@ public partial class DeviceTable : IDisposable | ||||
|         } | ||||
|  | ||||
|         // 返回 true 时自动弹出提示框 | ||||
|         await ToastService.Default(); | ||||
|         if (ret) | ||||
|             await ToastService.Default(); | ||||
|     } | ||||
|  | ||||
|     async Task ExcelDeviceAsync(ITableExportContext<DeviceRuntime> tableExportContext) | ||||
|   | ||||
| @@ -10,6 +10,7 @@ | ||||
|  | ||||
| using Mapster; | ||||
|  | ||||
| using ThingsGateway.Admin.Application; | ||||
| using ThingsGateway.Gateway.Application; | ||||
|  | ||||
| using Yitter.IdGenerator; | ||||
| @@ -47,7 +48,7 @@ public partial class VariableCopyComponent | ||||
|                 var variable = Model.Adapt<List<Variable>>(); | ||||
|                 foreach (var item in variable) | ||||
|                 { | ||||
|                     item.Id = YitIdHelper.NextId(); | ||||
|                     item.Id = CommonUtils.GetSingleId(); | ||||
|                     item.Name = $"{CopyVariableNamePrefix}{CopyVariableNameSuffixNumber + i}"; | ||||
|                     variables.Add(item); | ||||
|                 } | ||||
|   | ||||
| @@ -268,9 +268,10 @@ public partial class VariableRuntimeInfo : IDisposable | ||||
|  | ||||
|     private async Task ExcelExportAsync(ITableExportContext<VariableRuntime> tableExportContext, bool all = false) | ||||
|     { | ||||
|         bool ret; | ||||
|         if (all) | ||||
|         { | ||||
|             await GatewayExportService.OnVariableExport(new() { QueryPageOptions = new() }); | ||||
|             ret = await GatewayExportService.OnVariableExport(new() { QueryPageOptions = new() }); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
| @@ -278,16 +279,16 @@ public partial class VariableRuntimeInfo : IDisposable | ||||
|             { | ||||
|  | ||||
|                 case ChannelDevicePluginTypeEnum.PluginName: | ||||
|                     await GatewayExportService.OnVariableExport(new() { QueryPageOptions = new(), PluginName = SelectModel.PluginName }); | ||||
|                     ret = await GatewayExportService.OnVariableExport(new() { QueryPageOptions = new(), PluginName = SelectModel.PluginName }); | ||||
|                     break; | ||||
|                 case ChannelDevicePluginTypeEnum.Channel: | ||||
|                     await GatewayExportService.OnVariableExport(new() { QueryPageOptions = new(), ChannelId = SelectModel.ChannelRuntime.Id }); | ||||
|                     ret = await GatewayExportService.OnVariableExport(new() { QueryPageOptions = new(), ChannelId = SelectModel.ChannelRuntime.Id }); | ||||
|                     break; | ||||
|                 case ChannelDevicePluginTypeEnum.Device: | ||||
|                     await GatewayExportService.OnVariableExport(new() { QueryPageOptions = new(), DeviceId = SelectModel.DeviceRuntime.Id, PluginType = SelectModel.DeviceRuntime.PluginType }); | ||||
|                     ret = await GatewayExportService.OnVariableExport(new() { QueryPageOptions = new(), DeviceId = SelectModel.DeviceRuntime.Id, PluginType = SelectModel.DeviceRuntime.PluginType }); | ||||
|                     break; | ||||
|                 default: | ||||
|                     await GatewayExportService.OnVariableExport(new() { QueryPageOptions = new() }); | ||||
|                     ret = await GatewayExportService.OnVariableExport(new() { QueryPageOptions = new() }); | ||||
|  | ||||
|                     break; | ||||
|             } | ||||
| @@ -295,7 +296,8 @@ public partial class VariableRuntimeInfo : IDisposable | ||||
|         } | ||||
|  | ||||
|         // 返回 true 时自动弹出提示框 | ||||
|         await ToastService.Default(); | ||||
|         if (ret) | ||||
|             await ToastService.Default(); | ||||
|     } | ||||
|  | ||||
|     async Task ExcelVariableAsync(ITableExportContext<VariableRuntime> tableExportContext) | ||||
|   | ||||
| @@ -51,7 +51,7 @@ public class PackHelper | ||||
|             // 获取变量的位偏移量 | ||||
|             //if (item.DataType == DataTypeEnum.Boolean) | ||||
|             item.Index = device.GetBitOffsetDefault(address); | ||||
|             if (item.DataType == DataTypeEnum.Byte) | ||||
|             if (item.DataType == DataTypeEnum.Byte&& !(item.ArrayLength>1)) | ||||
|                 item.Index += (item.Index % 2 == 0) ? 1 : -1; | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -12,6 +12,7 @@ using Mapster; | ||||
|  | ||||
| using Newtonsoft.Json.Linq; | ||||
|  | ||||
| using ThingsGateway.Admin.Application; | ||||
| using ThingsGateway.Extension.Generic; | ||||
| using ThingsGateway.Foundation; | ||||
|  | ||||
| @@ -21,6 +22,8 @@ using TouchSocket.Dmtp.Rpc; | ||||
| using TouchSocket.Rpc; | ||||
| using TouchSocket.Sockets; | ||||
|  | ||||
| using Yitter.IdGenerator; | ||||
|  | ||||
| namespace ThingsGateway.Plugin.Synchronization; | ||||
|  | ||||
|  | ||||
| @@ -258,10 +261,7 @@ public partial class Synchronization : BusinessBase, IRpcDriver | ||||
|                     var data = await _tcpDmtpClient.GetDmtpRpcActor().InvokeTAsync<List<DataWithDatabase>>( | ||||
|                                        nameof(ReverseCallbackServer.GetData), waitInvoke).ConfigureAwait(false); | ||||
|  | ||||
|                     data.ForEach(a => a.Channel.Enable = false); | ||||
|                     await GlobalData.ChannelRuntimeService.CopyAsync(data.Select(a => a.Channel).ToList(), data.SelectMany(a => a.DeviceVariables).ToDictionary(a => a.Device, a => a.Variables), true, cancellationToken).ConfigureAwait(false); | ||||
|  | ||||
|                     LogMessage?.LogTrace($"ForcedSync data success"); | ||||
|                     await Add(data, cancellationToken).ConfigureAwait(false); | ||||
|  | ||||
|                 } | ||||
|             } | ||||
| @@ -277,10 +277,10 @@ public partial class Synchronization : BusinessBase, IRpcDriver | ||||
|                     foreach (var item in _tcpDmtpService.Clients) | ||||
|                     { | ||||
|                         var data = await item.GetDmtpRpcActor().InvokeTAsync<List<DataWithDatabase>>(nameof(ReverseCallbackServer.GetData), waitInvoke).ConfigureAwait(false); | ||||
|                         data.ForEach(a => a.Channel.Enable = false); | ||||
|  | ||||
|                         await GlobalData.ChannelRuntimeService.CopyAsync(data.Select(a => a.Channel).ToList(), data.SelectMany(a => a.DeviceVariables).ToDictionary(a => a.Device, a => a.Variables), true, cancellationToken).ConfigureAwait(false); | ||||
|                         LogMessage?.LogTrace($"{item.GetIPPort()}: ForcedSync data success"); | ||||
|  | ||||
|                         await Add(data, cancellationToken).ConfigureAwait(false); | ||||
|  | ||||
|                     } | ||||
|  | ||||
|                 } | ||||
| @@ -300,6 +300,25 @@ public partial class Synchronization : BusinessBase, IRpcDriver | ||||
|         } | ||||
|  | ||||
|     } | ||||
|     private async Task Add(List<DataWithDatabase> data, CancellationToken cancellationToken) | ||||
|     { | ||||
|         data.ForEach(a => | ||||
|         { | ||||
|             a.Channel.Enable = false; | ||||
|             a.Channel.Id = CommonUtils.GetSingleId(); | ||||
|             a.DeviceVariables.ForEach(b => | ||||
|             { | ||||
|                 b.Device.Id = 0; | ||||
|                 b.Variables.ForEach(c => | ||||
|                 { | ||||
|                     c.Id = 0; | ||||
|                 }); | ||||
|             }); | ||||
|         }); | ||||
|         await GlobalData.ChannelRuntimeService.CopyAsync(data.Select(a => a.Channel).ToList(), data.SelectMany(a => a.DeviceVariables).ToDictionary(a => a.Device, a => a.Variables), true, cancellationToken).ConfigureAwait(false); | ||||
|  | ||||
|         LogMessage?.LogTrace($"ForcedSync data success"); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 异步写入方法 | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| <Project> | ||||
|   <PropertyGroup> | ||||
|     <Version>10.6.24</Version> | ||||
|     <Version>10.6.29</Version> | ||||
|   </PropertyGroup> | ||||
|  | ||||
|   <ItemGroup> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user