275 lines
9.9 KiB
C#
275 lines
9.9 KiB
C#
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
|
|
{
|
|
/// <summary>
|
|
/// 表达式计算服务实现
|
|
/// </summary>
|
|
public class ExpressionEvaluationService : IExpressionEvaluationService, ITransientDependency
|
|
{
|
|
private readonly IRedisRepository<TelemeteringModel, string> _telemeteringRedisRepository;
|
|
private readonly IRedisRepository<TelesignalisationModel, string> _telesignalisationRedisRepository;
|
|
private readonly IRepository<TelemeteringConfiguration, Guid> _telemeteringConfigRepository;
|
|
private readonly IRepository<TelesignalisationConfiguration, Guid> _telesignalisationConfigRepository;
|
|
|
|
// 变量代码正则表达式,匹配 {16385_0} 格式
|
|
private static readonly Regex VariableCodeRegex = new Regex(@"\{(\d+_\d+)\}", RegexOptions.Compiled);
|
|
|
|
public ExpressionEvaluationService(
|
|
IRedisRepository<TelemeteringModel, string> telemeteringRedisRepository,
|
|
IRedisRepository<TelesignalisationModel, string> telesignalisationRedisRepository,
|
|
IRepository<TelemeteringConfiguration, Guid> telemeteringConfigRepository,
|
|
IRepository<TelesignalisationConfiguration, Guid> telesignalisationConfigRepository)
|
|
{
|
|
_telemeteringRedisRepository = telemeteringRedisRepository;
|
|
_telesignalisationRedisRepository = telesignalisationRedisRepository;
|
|
_telemeteringConfigRepository = telemeteringConfigRepository;
|
|
_telesignalisationConfigRepository = telesignalisationConfigRepository;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 计算表达式结果
|
|
/// </summary>
|
|
public async Task<ExpressionEvaluationResult> 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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 验证表达式语法
|
|
/// </summary>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 提取表达式中的变量代码
|
|
/// </summary>
|
|
public List<string> ExtractVariableCodes(string expression)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(expression))
|
|
return new List<string>();
|
|
|
|
var matches = VariableCodeRegex.Matches(expression);
|
|
return matches.Cast<Match>()
|
|
.Select(m => m.Groups[1].Value)
|
|
.Distinct()
|
|
.ToList();
|
|
}
|
|
|
|
#region 私有方法
|
|
|
|
/// <summary>
|
|
/// 获取变量值
|
|
/// </summary>
|
|
private async Task<Dictionary<string, object>> GetVariableValuesAsync(
|
|
List<string> variableCodes,
|
|
int timeWindowSeconds,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var result = new Dictionary<string, object>();
|
|
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取单个变量的值
|
|
/// </summary>
|
|
private async Task<object> 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; // 默认值
|
|
}
|
|
|
|
/// <summary>
|
|
/// 替换表达式中的变量
|
|
/// </summary>
|
|
private string ReplaceVariablesInExpression(string expression, Dictionary<string, object> 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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 检查是否为有效的JavaScript表达式
|
|
/// </summary>
|
|
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
|
|
}
|
|
}
|