using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using Abp.Dependency; using Abp.Domain.Repositories; using Microsoft.EntityFrameworkCore; using Newtonsoft.Json; using ToolLibrary.LogHelper; using YunDa.SOMS.Entities.DataMonitoring; using YunDa.SOMS.Redis.Entities.DataMonitorCategory; using YunDa.SOMS.Redis.Repositories; using Jint; namespace YunDa.SOMS.Application.DataMonitoring.SecondaryCircuitInspection.Services { /// /// 表达式计算服务实现 /// public class ExpressionEvaluationService : IExpressionEvaluationService, ITransientDependency { private readonly IRedisRepository _telemeteringRedisRepository; private readonly IRedisRepository _telesignalisationRedisRepository; private readonly IRepository _telemeteringConfigRepository; private readonly IRepository _telesignalisationConfigRepository; // 变量代码正则表达式,匹配 {16385_0} 格式 private static readonly Regex VariableCodeRegex = new Regex(@"\{(\d+_\d+)\}", RegexOptions.Compiled); public ExpressionEvaluationService( IRedisRepository telemeteringRedisRepository, IRedisRepository telesignalisationRedisRepository, IRepository telemeteringConfigRepository, IRepository telesignalisationConfigRepository) { _telemeteringRedisRepository = telemeteringRedisRepository; _telesignalisationRedisRepository = telesignalisationRedisRepository; _telemeteringConfigRepository = telemeteringConfigRepository; _telesignalisationConfigRepository = telesignalisationConfigRepository; } /// /// 计算表达式结果 /// public async Task EvaluateExpressionAsync( string expression, int timeWindowSeconds = 60, CancellationToken cancellationToken = default) { var stopwatch = Stopwatch.StartNew(); var result = new ExpressionEvaluationResult(); try { // 验证表达式 var validation = ValidateExpression(expression); if (!validation.IsValid) { result.ErrorMessage = string.Join("; ", validation.Errors); return result; } // 提取变量代码 var variableCodes = ExtractVariableCodes(expression); result.EvaluationDetails.Add($"提取到 {variableCodes.Count} 个变量: {string.Join(", ", variableCodes)}"); // 获取变量值 var variableValues = await GetVariableValuesAsync(variableCodes, timeWindowSeconds, cancellationToken); result.VariableValues = variableValues; // 替换表达式中的变量 var evaluableExpression = ReplaceVariablesInExpression(expression, variableValues); result.EvaluationDetails.Add($"替换后的表达式: {evaluableExpression}"); // 使用JavaScript引擎计算表达式 var engine = new Engine(); var jsResult = engine.Evaluate(evaluableExpression); result.Result = Convert.ToBoolean(jsResult.ToObject()); result.IsSuccess = true; result.EvaluationDetails.Add($"计算结果: {result.Result}"); } catch (Exception ex) { result.ErrorMessage = $"表达式计算异常: {ex.Message}"; result.EvaluationDetails.Add($"异常详情: {ex}"); Log4Helper.Error($"表达式计算失败: {expression}", ex); } finally { stopwatch.Stop(); result.ExecutionTimeMs = stopwatch.ElapsedMilliseconds; } return result; } /// /// 验证表达式语法 /// public ExpressionValidationResult ValidateExpression(string expression) { var result = new ExpressionValidationResult(); if (string.IsNullOrWhiteSpace(expression)) { result.Errors.Add("表达式不能为空"); return result; } try { // 提取变量代码 result.VariableCodes = ExtractVariableCodes(expression); if (!result.VariableCodes.Any()) { result.Warnings.Add("表达式中未找到变量代码"); } // 基本语法检查 if (!IsValidJavaScriptExpression(expression)) { result.Errors.Add("表达式语法不正确"); } result.IsValid = !result.Errors.Any(); } catch (Exception ex) { result.Errors.Add($"表达式验证异常: {ex.Message}"); } return result; } /// /// 提取表达式中的变量代码 /// public List ExtractVariableCodes(string expression) { if (string.IsNullOrWhiteSpace(expression)) return new List(); var matches = VariableCodeRegex.Matches(expression); return matches.Cast() .Select(m => m.Groups[1].Value) .Distinct() .ToList(); } #region 私有方法 /// /// 获取变量值 /// private async Task> GetVariableValuesAsync( List variableCodes, int timeWindowSeconds, CancellationToken cancellationToken) { var result = new Dictionary(); foreach (var code in variableCodes) { try { var value = await GetSingleVariableValueAsync(code, timeWindowSeconds, cancellationToken); result[code] = value; } catch (Exception ex) { Log4Helper.Warning($"获取变量 {code} 的值失败: {ex.Message}"); result[code] = 0; // 默认值 } } return result; } /// /// 获取单个变量的值 /// private async Task GetSingleVariableValueAsync( string variableCode, int timeWindowSeconds, CancellationToken cancellationToken) { // 解析变量代码,格式为 "16385_0" var parts = variableCode.Split('_'); if (parts.Length != 2 || !int.TryParse(parts[0], out var address) || !int.TryParse(parts[1], out var type)) { throw new ArgumentException($"无效的变量代码格式: {variableCode}"); } // type: 0=遥测, 1=遥信 if (type == 0) { // 遥测数据 var telemetryConfig = await _telemeteringConfigRepository.GetAll() .FirstOrDefaultAsync(x => x.DispatcherAddress == address, cancellationToken); if (telemetryConfig != null) { var telemetryData = await _telemeteringRedisRepository.HashSetGetAsync( nameof(TelemeteringModel), telemetryConfig.Id.ToString()); return telemetryData?.Value ?? 0m; } } else if (type == 1) { // 遥信数据 var telesignalConfig = await _telesignalisationConfigRepository.GetAll() .FirstOrDefaultAsync(x => x.DispatcherAddress == address, cancellationToken); if (telesignalConfig != null) { var telesignalData = await _telesignalisationRedisRepository.HashSetGetAsync( nameof(TelesignalisationModel), telesignalConfig.Id.ToString()); return telesignalData?.Status == true ? 1 : 0; } } return 0; // 默认值 } /// /// 替换表达式中的变量 /// private string ReplaceVariablesInExpression(string expression, Dictionary variableValues) { var result = expression; foreach (var kvp in variableValues) { var placeholder = $"{{{kvp.Key}}}"; var value = kvp.Value?.ToString() ?? "0"; result = result.Replace(placeholder, value); } return result; } /// /// 检查是否为有效的JavaScript表达式 /// private bool IsValidJavaScriptExpression(string expression) { try { // 创建一个临时的JavaScript引擎来验证语法 var engine = new Engine(); // 替换变量为测试值 var testExpression = VariableCodeRegex.Replace(expression, "1"); // 尝试解析表达式 engine.Evaluate(testExpression); return true; } catch { return false; } } #endregion } }