Compare commits

..

12 Commits

Author SHA1 Message Date
Diego
8e938f18be 去除不必要的控制台日志输出 2025-05-28 16:52:51 +08:00
Diego
ab1b364c54 fix: 同步插件反写空错误 2025-05-28 12:30:16 +08:00
Diego
5ec65b2fb0 10.6.29 2025-05-28 10:47:31 +08:00
Diego
926eced724 10.6.28 2025-05-27 17:20:05 +08:00
Diego
f7f8802272 2025-05-27 13:19:39 +08:00
Diego
c6910dff02 update src/Gateway/ThingsGateway.Gateway.Application/ThingsGateway.Gateway.Application.csproj.
Signed-off-by: Diego <2248356998@qq.com>
2025-05-27 01:37:29 +00:00
Diego
ad299d0dbb 2025-05-27 08:52:06 +08:00
2248356998 qq.com
8b124d1050 日志统计查询性能增强 2025-05-27 00:03:30 +08:00
Diego
ff41080dbd 更新授权类 2025-05-26 19:51:21 +08:00
Diego
0e28606e3d 10.6.23 2025-05-26 18:43:42 +08:00
Diego
6a025ceee5 序列化配置增加nan的情况 2025-05-26 17:41:13 +08:00
Diego
6b2e53d6dc 更新配置 2025-05-26 09:17:59 +08:00
42 changed files with 421 additions and 232 deletions

View File

@@ -21,29 +21,36 @@ using System.Logging;
using ThingsGateway.FriendlyException;
using ThingsGateway.Logging;
using ThingsGateway.NewLife.Json.Extension;
using ThingsGateway.UnifyResult;
namespace ThingsGateway.Admin.Application;
public class RequestAuditFilter : IAsyncActionFilter
public class RequestAuditFilter : IAsyncActionFilter, IOrderedFilter
{
private const int FilterOrder = -3000;
public int Order => FilterOrder;
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var timeOperation = Stopwatch.StartNew();
var resultContext = await next().ConfigureAwait(false);
// 计算接口执行时间
timeOperation.Stop();
var controllerActionDescriptor = (context.ActionDescriptor as ControllerActionDescriptor);
// 获取动作方法描述器
var actionMethod = controllerActionDescriptor?.MethodInfo;
// 处理 Blazor Server
if (actionMethod == null)
{
_ = await next.Invoke().ConfigureAwait(false);
return;
}
// 排除 WebSocket 请求处理
if (context.HttpContext.IsWebSocketRequest())
{
_ = await next().ConfigureAwait(false);
return;
}
@@ -51,7 +58,6 @@ public class RequestAuditFilter : IAsyncActionFilter
if (actionMethod.IsDefined(typeof(SuppressRequestAuditAttribute), true)
|| actionMethod.DeclaringType.IsDefined(typeof(SuppressRequestAuditAttribute), true))
{
_ = await next().ConfigureAwait(false);
return;
}
@@ -65,10 +71,7 @@ public class RequestAuditFilter : IAsyncActionFilter
return;
}
// 计算接口执行时间
var timeOperation = Stopwatch.StartNew();
var resultContext = await next().ConfigureAwait(false);
timeOperation.Stop();
var logData = new RequestAuditData();
@@ -88,21 +91,29 @@ public class RequestAuditFilter : IAsyncActionFilter
var requestUrl = Uri.UnescapeDataString(httpRequest.GetRequestUrlAddress());
logData.RequestUrl = requestUrl;
object returnValue = null;
Type finalReturnType;
var result = resultContext.Result as IActionResult;
var data = result switch
// 解析返回值
if (UnifyContext.CheckVaildResult(result, out var data))
{
// 处理内容结果
ContentResult content => content.Content,
// 处理对象结果
ObjectResult obj => obj.Value,
// 处理 JSON 对象
JsonResult json => json.Value,
_ => null,
};
logData.ReturnInformation = data;
returnValue = data;
finalReturnType = data?.GetType();
}
// 处理文件类型
else if (result is FileResult fresult)
{
returnValue = new
{
FileName = fresult.FileDownloadName,
fresult.ContentType,
Length = fresult is FileContentResult cresult ? (object)cresult.FileContents.Length : null
};
finalReturnType = fresult?.GetType();
}
else finalReturnType = result?.GetType();
logData.ReturnInformation = returnValue;
//获取客户端信息
var client = App.GetService<IAppService>().UserAgent;

View File

@@ -16,7 +16,6 @@ using ThingsGateway.Extension;
using ThingsGateway.FriendlyException;
using ThingsGateway.Logging;
using ThingsGateway.NewLife.Json.Extension;
using ThingsGateway.Razor;
namespace ThingsGateway.Admin.Application;
@@ -160,8 +159,7 @@ public class DatabaseLoggingWriter : IDatabaseLoggingWriter
if (path == "/api/auth/login")
{
//如果是登录,用户信息就从返回值里拿
var result = requestAuditData.ReturnInformation?.ToSystemTextJsonString();//返回值转json
var userInfo = result.FromJsonNetString<UnifyResult<LoginOutput>>();//格式化成user表
dynamic userInfo = requestAuditData.ReturnInformation;
opAccount = userInfo.Data.Account;//赋值账号
verificatId = userInfo.Data.VerificatId;
}

View File

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

View File

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

View File

@@ -37,7 +37,7 @@ public sealed class SqlSugarOption : ConnectionConfig
/// <summary>
/// 是否控制台显示Sql语句
/// </summary>
public bool IsShowSql { get; set; }
public bool? IsShowSql { get; set; }
/// <summary>
/// 更新数据

View File

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

View File

@@ -29,7 +29,6 @@ using ThingsGateway.Admin.Application;
using ThingsGateway.Admin.Razor;
using ThingsGateway.Extension;
using ThingsGateway.NewLife.Caching;
using ThingsGateway.NewLife.Extension;
namespace ThingsGateway.AdminServer;
@@ -88,6 +87,7 @@ public class Startup : AppStartup
}
;
services.AddMvcFilter<RequestAuditFilter>();
services.AddControllers()
.AddNewtonsoftJson(options => SetNewtonsoftJsonSetting(options.SerializerSettings))
//.AddXmlSerializerFormatters()
@@ -160,7 +160,8 @@ public class Startup : AppStartup
{
options.WriteFilter = (logMsg) =>
{
if (logMsg.Message.IsNullOrEmpty()) return false;
if (App.HostApplicationLifetime.ApplicationStopping.IsCancellationRequested && logMsg.LogLevel >= LogLevel.Warning) return false;
if (string.IsNullOrEmpty(logMsg.Message)) return false;
else return true;
};
@@ -237,7 +238,6 @@ public class Startup : AppStartup
// logContext.Set(LoggingConst.Method, httpContext.Request.Method);//请求方法
// });
//});
services.AddMvcFilter<RequestAuditFilter>();
//日志写入数据库配置
services.AddDatabaseLogging<DatabaseLoggingWriter>(options =>

View File

@@ -71,13 +71,25 @@ public static class App
/// </summary>
public static IServiceProvider RootServices => InternalApp.RootServices;
private static IHostApplicationLifetime hostApplicationLifetime;
public static IHostApplicationLifetime HostApplicationLifetime
{
get
{
if ((hostApplicationLifetime == null))
{
hostApplicationLifetime = RootServices?.GetService<IHostApplicationLifetime>();
}
return hostApplicationLifetime;
}
}
private static IStringLocalizerFactory? stringLocalizerFactory;
/// <summary>
/// 本地化服务工厂
/// </summary>
public static IStringLocalizerFactory? StringLocalizerFactory
{
get
{

View File

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

View File

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

View File

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

View File

@@ -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>
/// 原生日志上下文数据

View File

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

View File

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

View File

@@ -349,7 +349,7 @@ public static class UnifyContext
/// <param name="result"></param>
/// <param name="data"></param>
/// <returns></returns>
internal static bool CheckVaildResult(IActionResult result, out object data)
public static bool CheckVaildResult(IActionResult result, out object data)
{
data = default;

View File

@@ -37,7 +37,8 @@ public static class SystemTextJsonExtension
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
WriteIndented = true, // 缩进
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull // 忽略 null
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, // 忽略 null
NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals,
};
// 如有自定义Converter这里添加
// IndentedOptions.Converters.Add(new ByteArrayJsonConverter());
@@ -50,7 +51,8 @@ public static class SystemTextJsonExtension
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
WriteIndented = false, // 不缩进
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals,
};
NoneIndentedOptions.Converters.Add(new ByteArrayToNumberArrayConverterSystemTextJson());
NoneIndentedOptions.Converters.Add(new JTokenSystemTextJsonConverter());

View File

@@ -27,6 +27,13 @@ public class Startup : AppStartup
{
services.AddBootstrapBlazor(
option => option.JSModuleVersion = Random.Shared.Next(10000).ToString()
, jsonLocalizationOptions =>
{
jsonLocalizationOptions.DisableGetLocalizerFromResourceManager = true;
jsonLocalizationOptions.DisableGetLocalizerFromService = true;
jsonLocalizationOptions.IgnoreLocalizerMissing = true;
jsonLocalizationOptions.UseKeyWhenValueIsNull = true;
}
);
services.AddConfigurableOptions<MenuOptions>();
services.ConfigureIconThemeOptions(options => options.ThemeKey = "fa");

View File

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

View File

@@ -1,9 +1,9 @@
<Project>
<PropertyGroup>
<PluginVersion>10.6.21</PluginVersion>
<ProPluginVersion>10.6.21</ProPluginVersion>
<AuthenticationVersion>2.1.7</AuthenticationVersion>
<PluginVersion>10.6.31</PluginVersion>
<ProPluginVersion>10.6.31</ProPluginVersion>
<AuthenticationVersion>2.1.8</AuthenticationVersion>
</PropertyGroup>
<PropertyGroup>

View File

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

View File

@@ -90,6 +90,7 @@ public class RuntimeInfoController : ControllerBase
/// </summary>
/// <returns></returns>
[HttpPost("checkRealAlarm")]
[RequestAudit]
[DisplayName("确认实时报警")]
public async Task CheckRealAlarm(long variableId)
{

View File

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

View File

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

View File

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

View File

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

View File

@@ -18,6 +18,8 @@ using SqlSugar;
using System.Reflection;
using ThingsGateway.Authentication;
namespace ThingsGateway.Gateway.Application;
[AppStartup(-100)]
@@ -25,6 +27,9 @@ public class Startup : AppStartup
{
public void Configure(IServiceCollection services)
{
ProAuthentication.TryGetAuthorizeInfo(out var authorizeInfo);
services.AddConfigurableOptions<ChannelThreadOptions>();
services.AddConfigurableOptions<GatewayLogOptions>();
services.AddConfigurableOptions<RpcLogOptions>();
@@ -125,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(() =>
{

View File

@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="$(SolutionDir)Version.props" />
<Import Project="$(SolutionDir)PackNuget.props" />

View File

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

View File

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

View File

@@ -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)
@@ -1299,7 +1307,6 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e =>
try
{
if (Disposed) return;
await Task.Delay(1000);
await OnClickSearch(SearchText);
Value = GetValue(Value);
@@ -1311,6 +1318,7 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e =>
}
finally
{
await Task.Delay(1000);
_isExecuting = false;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -357,7 +357,7 @@ public partial class OpcUaServer : BusinessBase
{
StoreType = CertificateStoreType.X509Store,
StorePath = "CurrentUser\\UAServer_ThingsGateway",
SubjectName = _driverPropertys.BigTextSubjectName,
SubjectName = $"{_driverPropertys.BigTextSubjectName}{_driverPropertys.OpcUaStringUrl}",
//ValidationOptions = CertificateValidationOptions.SuppressHostNameInvalid,
},

View File

@@ -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;
@@ -60,11 +63,8 @@ public partial class Synchronization : BusinessBase, IRpcDriver
protected override async ValueTask ProtectedExecuteAsync(CancellationToken cancellationToken)
{
try
{
if (_driverPropertys.IsServer)
{
if (_tcpDmtpService.ServerState != ServerState.Running)
@@ -129,6 +129,7 @@ public partial class Synchronization : BusinessBase, IRpcDriver
// 如果 online 为 true表示设备在线
if (online)
{
var deviceRunTimes = CollectDevices.Where(a => a.Value.IsCollect == true).Select(a => a.Value).Adapt<List<DeviceDataWithValue>>();
@@ -258,10 +259,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 +275,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 +298,27 @@ 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.ChannelId = a.Channel.Id;
b.Device.Id = CommonUtils.GetSingleId();
b.Variables.ForEach(c =>
{
c.DeviceId = b.Device.Id;
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>
/// 异步写入方法
@@ -321,12 +340,12 @@ public partial class Synchronization : BusinessBase, IRpcDriver
{
if (deviceDatas.TryGetValue(item.Key.DeviceName ?? string.Empty, out var variableDatas))
{
variableDatas.Add(item.Key.Name, item.Value?.ToString() ?? string.Empty);
variableDatas.TryAdd(item.Key.Name, item.Value?.ToString() ?? string.Empty);
}
else
{
deviceDatas.Add(item.Key.DeviceName ?? string.Empty, new());
deviceDatas[item.Key.DeviceName ?? string.Empty].Add(item.Key.Name, item.Value?.ToString() ?? string.Empty);
deviceDatas.TryAdd(item.Key.DeviceName ?? string.Empty, new());
deviceDatas[item.Key.DeviceName ?? string.Empty].TryAdd(item.Key.Name, item.Value?.ToString() ?? string.Empty);
}
}
@@ -383,7 +402,7 @@ public partial class Synchronization : BusinessBase, IRpcDriver
try
{
var data = await _tcpDmtpClient.GetDmtpRpcActor().InvokeTAsync<Dictionary<string, Dictionary<string, OperResult<object>>>>(
var data = await client.GetDmtpRpcActor().InvokeTAsync<Dictionary<string, Dictionary<string, OperResult<object>>>>(
nameof(ReverseCallbackServer.Rpc), waitInvoke, new Dictionary<string, Dictionary<string, string>>() { { item.Key, item.Value } }).ConfigureAwait(false);
dataResult.AddRange(data);
@@ -396,7 +415,7 @@ public partial class Synchronization : BusinessBase, IRpcDriver
foreach (var vItem in item.Value)
{
dataResult[item.Key].Add(vItem.Key, new OperResult<object>(ex));
dataResult[item.Key].TryAdd(vItem.Key, new OperResult<object>(ex));
}
}
}
@@ -407,7 +426,7 @@ public partial class Synchronization : BusinessBase, IRpcDriver
foreach (var vItem in item.Value)
{
dataResult[item.Key].Add(vItem.Key, new OperResult<object>("No online"));
dataResult[item.Key].TryAdd(vItem.Key, new OperResult<object>("No online"));
}
}

View File

@@ -15,7 +15,7 @@ namespace ThingsGateway.Plugin.Synchronization;
/// <summary>
/// <inheritdoc/>
/// </summary>
public class SynchronizationProperty : BusinessPropertyBase
public class SynchronizationProperty : BusinessPropertyBase, IBusinessPropertyAllVariableBase
{
[DynamicProperty]
public bool IsServer { get; set; } = true;

View File

@@ -37,7 +37,6 @@ using ThingsGateway.Debug;
using ThingsGateway.Extension;
using ThingsGateway.Gateway.Application;
using ThingsGateway.NewLife.Caching;
using ThingsGateway.NewLife.Extension;
namespace ThingsGateway.Server;
@@ -109,6 +108,7 @@ public class Startup : AppStartup
// setting.Converters.Add(new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }); // 解决DateTimeOffset异常
}
;
services.AddMvcFilter<RequestAuditFilter>();
services.AddControllers()
.AddNewtonsoftJson(options => SetNewtonsoftJsonSetting(options.SerializerSettings))
@@ -137,7 +137,8 @@ public class Startup : AppStartup
{
options.WriteFilter = (logMsg) =>
{
if (logMsg.Message.IsNullOrEmpty()) return false;
if (App.HostApplicationLifetime.ApplicationStopping.IsCancellationRequested && logMsg.LogLevel >= LogLevel.Warning) return false;
if (string.IsNullOrEmpty(logMsg.Message)) return false;
else return true;
};
@@ -215,7 +216,6 @@ public class Startup : AppStartup
// });
//});
services.AddMvcFilter<RequestAuditFilter>();
//日志写入数据库配置
services.AddDatabaseLogging<DatabaseLoggingWriter>(options =>

View File

@@ -29,7 +29,6 @@ using ThingsGateway.Admin.Application;
using ThingsGateway.Admin.Razor;
using ThingsGateway.Extension;
using ThingsGateway.NewLife.Caching;
using ThingsGateway.NewLife.Extension;
namespace ThingsGateway.Server;
@@ -88,6 +87,8 @@ public class Startup : AppStartup
}
;
services.AddMvcFilter<RequestAuditFilter>();
services.AddControllers()
.AddNewtonsoftJson(options => SetNewtonsoftJsonSetting(options.SerializerSettings))
//.AddXmlSerializerFormatters()
@@ -160,7 +161,8 @@ public class Startup : AppStartup
{
options.WriteFilter = (logMsg) =>
{
if (logMsg.Message.IsNullOrEmpty()) return false;
if (App.HostApplicationLifetime.ApplicationStopping.IsCancellationRequested && logMsg.LogLevel >= LogLevel.Warning) return false;
if (string.IsNullOrEmpty(logMsg.Message)) return false;
else return true;
};
@@ -237,7 +239,6 @@ public class Startup : AppStartup
// logContext.Set(LoggingConst.Method, httpContext.Request.Method);//请求方法
// });
//});
services.AddMvcFilter<RequestAuditFilter>();
//日志写入数据库配置
services.AddDatabaseLogging<DatabaseLoggingWriter>(options =>

View File

@@ -83,6 +83,7 @@ public class Startup : AppStartup
// setting.Converters.Add(new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }); // 解决DateTimeOffset异常
}
;
services.AddMvcFilter<RequestAuditFilter>();
services.AddControllers()
.AddNewtonsoftJson(options => SetNewtonsoftJsonSetting(options.SerializerSettings))
@@ -156,6 +157,7 @@ public class Startup : AppStartup
{
options.WriteFilter = (logMsg) =>
{
if (App.HostApplicationLifetime.ApplicationStopping.IsCancellationRequested && logMsg.LogLevel >= LogLevel.Warning) return false;
if (string.IsNullOrEmpty(logMsg.Message)) return false;
else return true;
};
@@ -233,7 +235,6 @@ public class Startup : AppStartup
// logContext.Set(LoggingConst.Method, httpContext.Request.Method);//请求方法
// });
//});
services.AddMvcFilter<RequestAuditFilter>();
//日志写入数据库配置
services.AddDatabaseLogging<DatabaseLoggingWriter>(options =>

View File

@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>10.6.21</Version>
<Version>10.6.31</Version>
</PropertyGroup>
<ItemGroup>