RuleEngine-R2-R4: RuleEngineService+RuleEngineJob+前端UI增强+大屏SignalR订阅
This commit is contained in:
24
api_sqlsugar/Warehouse/Services/RuleEngineJob.cs
Normal file
24
api_sqlsugar/Warehouse/Services/RuleEngineJob.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using Quartz;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Warehouse.Services;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 规则引擎定时任务。
|
||||||
|
/// Cron: 0/10 * * * * ? (每10秒)
|
||||||
|
/// 挂载到 Vol.Pro Quartz 调度器。
|
||||||
|
/// </summary>
|
||||||
|
public class RuleEngineJob : IJob
|
||||||
|
{
|
||||||
|
public async Task Execute(IJobExecutionContext context)
|
||||||
|
{
|
||||||
|
var sp = (IServiceProvider)context.JobDetail.JobDataMap["ServiceProvider"];
|
||||||
|
if (sp == null) return;
|
||||||
|
|
||||||
|
var engine = sp.GetService<RuleEngineService>();
|
||||||
|
if (engine == null) return;
|
||||||
|
|
||||||
|
await engine.EvaluateAllAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
320
api_sqlsugar/Warehouse/Services/RuleEngineService.cs
Normal file
320
api_sqlsugar/Warehouse/Services/RuleEngineService.cs
Normal file
@@ -0,0 +1,320 @@
|
|||||||
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using VolPro.Entity.DomainModels;
|
||||||
|
using VolPro.WebApi.Controllers.Hubs;
|
||||||
|
using Warehouse.IRepositories;
|
||||||
|
using Warehouse.IServices;
|
||||||
|
|
||||||
|
namespace Warehouse.Services;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 规则引擎核心服务。
|
||||||
|
/// 由 RuleEngineJob 每 10s 调用一次 EvaluateAllAsync。
|
||||||
|
///
|
||||||
|
/// 流程:
|
||||||
|
/// 1. 加载所有启用规则(含条件+动作)
|
||||||
|
/// 2. 从 gateway 批量获取实时值
|
||||||
|
/// 3. 逐规则评估条件 → 触发动作 → 写日志
|
||||||
|
/// </summary>
|
||||||
|
public class RuleEngineService
|
||||||
|
{
|
||||||
|
private readonly Iwarehouse_ruleRepository _ruleRepo;
|
||||||
|
private readonly Ibase_deviceRepository _devRepo;
|
||||||
|
private readonly Iiot_devicedataRepository _dataRepo;
|
||||||
|
private readonly Iiot_alarmRepository _alarmRepo;
|
||||||
|
private readonly GatewayClient _gatewayClient;
|
||||||
|
private readonly IHubContext<HomePageMessageHub> _hub;
|
||||||
|
|
||||||
|
public RuleEngineService(
|
||||||
|
Iwarehouse_ruleRepository ruleRepo,
|
||||||
|
Ibase_deviceRepository devRepo,
|
||||||
|
Iiot_devicedataRepository dataRepo,
|
||||||
|
Iiot_alarmRepository alarmRepo,
|
||||||
|
GatewayClient gatewayClient,
|
||||||
|
IHubContext<HomePageMessageHub> hub)
|
||||||
|
{
|
||||||
|
_ruleRepo = ruleRepo;
|
||||||
|
_devRepo = devRepo;
|
||||||
|
_dataRepo = dataRepo;
|
||||||
|
_alarmRepo = alarmRepo;
|
||||||
|
_gatewayClient = gatewayClient;
|
||||||
|
_hub = hub;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task EvaluateAllAsync()
|
||||||
|
{
|
||||||
|
// 1. 加载启用规则
|
||||||
|
var rules = await LoadEnabledRulesAsync();
|
||||||
|
if (!rules.Any()) return;
|
||||||
|
|
||||||
|
// 2. 构建 DeviceId → (AdapterCode, SourceId, BaseUrl) 映射
|
||||||
|
var deviceMap = await BuildDeviceMappingAsync(rules);
|
||||||
|
|
||||||
|
// 3. 批量取实时值(按网关分组调 B4-batch)
|
||||||
|
var realtimeData = await BatchFetchRealtimeAsync(rules, deviceMap);
|
||||||
|
|
||||||
|
// 4. 逐规则评估
|
||||||
|
foreach (var rule in rules)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
bool met = await EvaluateRuleAsync(rule, realtimeData, deviceMap);
|
||||||
|
if (met)
|
||||||
|
{
|
||||||
|
await ExecuteActionsAsync(rule, deviceMap);
|
||||||
|
rule.LastTriggered = DateTime.Now;
|
||||||
|
}
|
||||||
|
rule.LastEvaluated = DateTime.Now;
|
||||||
|
await _ruleRepo.DbContext.Updateable(rule)
|
||||||
|
.UpdateColumns(r => new { r.LastEvaluated, r.LastTriggered }).ExecuteCommandAsync();
|
||||||
|
}
|
||||||
|
catch { /* 单规则失败不阻塞 */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════
|
||||||
|
// 规则加载
|
||||||
|
// ═══════════════════════════════════════════
|
||||||
|
|
||||||
|
private async Task<List<warehouse_rule>> LoadEnabledRulesAsync()
|
||||||
|
{
|
||||||
|
var rules = await _ruleRepo.FindAsIQueryable(r =>
|
||||||
|
r.Enable == "启用" || r.Enable == null).ToListAsync();
|
||||||
|
foreach (var r in rules)
|
||||||
|
{
|
||||||
|
r.CooldownSec = r.CooldownSec > 0 ? r.CooldownSec : 60;
|
||||||
|
r.Priority = r.Priority ?? 0;
|
||||||
|
}
|
||||||
|
return rules.OrderByDescending(r => r.Priority).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════
|
||||||
|
// 设备映射: DeviceId → (AdapterCode, SourceId, 网关BaseUrl)
|
||||||
|
// ═══════════════════════════════════════════
|
||||||
|
|
||||||
|
private async Task<Dictionary<int, (string adapterCode, string sourceId, string baseUrl)>> BuildDeviceMappingAsync(
|
||||||
|
List<warehouse_rule> rules)
|
||||||
|
{
|
||||||
|
var conditionDeviceIds = rules.SelectMany(r => r.warehouse_rulecondition ?? new())
|
||||||
|
.Select(c => c.DeviceId ?? 0).Where(id => id > 0).Distinct().ToList();
|
||||||
|
var actionDeviceIds = rules.SelectMany(r => r.warehouse_ruleaction ?? new())
|
||||||
|
.Select(a => a.DeviceId ?? 0).Where(id => id > 0).Distinct().ToList();
|
||||||
|
var allIds = conditionDeviceIds.Union(actionDeviceIds).ToList();
|
||||||
|
if (!allIds.Any()) return new();
|
||||||
|
|
||||||
|
var devices = await _devRepo.FindAsIQueryable(d => allIds.Contains(d.DeviceId)).ToListAsync();
|
||||||
|
var map = new Dictionary<int, (string, string, string)>();
|
||||||
|
foreach (var d in devices)
|
||||||
|
{
|
||||||
|
string baseUrl = "";
|
||||||
|
if (!string.IsNullOrEmpty(d.AdapterCode))
|
||||||
|
{
|
||||||
|
var prefix = d.AdapterCode.Split(':')[0];
|
||||||
|
// 从 gateway_nodes 查找对应的 BaseUrl
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var gw = _devRepo.DbContext.Queryable<gateway_nodes>()
|
||||||
|
.First(x => x.AdapterTypes != null && x.AdapterTypes.Contains(prefix) && x.IsOnline == "在线");
|
||||||
|
baseUrl = gw?.BaseUrl ?? "";
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
map[d.DeviceId] = (d.AdapterCode ?? "", d.SourceId ?? "", baseUrl);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════
|
||||||
|
// 批量实时值获取
|
||||||
|
// ═══════════════════════════════════════════
|
||||||
|
|
||||||
|
private async Task<Dictionary<(string adapter, string sourceId), List<(int pointIndex, double value)>>> BatchFetchRealtimeAsync(
|
||||||
|
List<warehouse_rule> rules,
|
||||||
|
Dictionary<int, (string adapterCode, string sourceId, string baseUrl)> deviceMap)
|
||||||
|
{
|
||||||
|
var result = new Dictionary<(string, string), List<(int, double)>>();
|
||||||
|
|
||||||
|
// 按网关分组
|
||||||
|
var gwGroups = new Dictionary<string, List<(string adapter, string sourceId)>>();
|
||||||
|
foreach (var (deviceId, (adapter, sourceId, baseUrl)) in deviceMap)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(baseUrl) || string.IsNullOrEmpty(sourceId)) continue;
|
||||||
|
if (!gwGroups.ContainsKey(baseUrl))
|
||||||
|
gwGroups[baseUrl] = new();
|
||||||
|
gwGroups[baseUrl].Add((adapter, sourceId));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var (baseUrl, pairs) in gwGroups)
|
||||||
|
{
|
||||||
|
foreach (var (adapter, sourceId) in pairs)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var data = await _gatewayClient.GetRealtimeAsync(baseUrl, adapter, sourceId);
|
||||||
|
if (data == null) continue;
|
||||||
|
var root = data.RootElement;
|
||||||
|
if (root.TryGetProperty("rows", out var rows) && rows.ValueKind == JsonValueKind.Array)
|
||||||
|
{
|
||||||
|
var list = new List<(int, double)>();
|
||||||
|
foreach (var r in rows.EnumerateArray())
|
||||||
|
{
|
||||||
|
int idx = r.TryGetProperty("index", out var i) ? i.GetInt32() : 0;
|
||||||
|
double val = r.TryGetProperty("value", out var v) ? v.GetDouble() : 0;
|
||||||
|
list.Add((idx, val));
|
||||||
|
}
|
||||||
|
result[(adapter, sourceId)] = list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════
|
||||||
|
// 条件评估
|
||||||
|
// ═══════════════════════════════════════════
|
||||||
|
|
||||||
|
private Task<bool> EvaluateRuleAsync(warehouse_rule rule,
|
||||||
|
Dictionary<(string adapter, string sourceId), List<(int pointIndex, double value)>> realtimeData,
|
||||||
|
Dictionary<int, (string adapterCode, string sourceId, string baseUrl)> deviceMap)
|
||||||
|
{
|
||||||
|
var conditions = rule.warehouse_rulecondition ?? new();
|
||||||
|
if (!conditions.Any()) return Task.FromResult(false);
|
||||||
|
|
||||||
|
var results = new List<bool>();
|
||||||
|
foreach (var cond in conditions)
|
||||||
|
{
|
||||||
|
if (cond.DeviceId == null || cond.ValueId == null) { results.Add(false); continue; }
|
||||||
|
|
||||||
|
// 冷却检查
|
||||||
|
if (cond.LastTriggered.HasValue && rule.CooldownSec > 0)
|
||||||
|
{
|
||||||
|
if ((DateTime.Now - cond.LastTriggered.Value).TotalSeconds < rule.CooldownSec)
|
||||||
|
{ results.Add(false); continue; }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!deviceMap.TryGetValue(cond.DeviceId.Value, out var devInfo)) { results.Add(false); continue; }
|
||||||
|
|
||||||
|
double? actualValue = null;
|
||||||
|
if (realtimeData.TryGetValue((devInfo.adapterCode, devInfo.sourceId), out var points))
|
||||||
|
{
|
||||||
|
// ValueId 对应 pointIndex(简化:直接使用 ValueId 作为 pointIndex)
|
||||||
|
var point = points.FirstOrDefault(p => p.pointIndex == cond.ValueId);
|
||||||
|
actualValue = point.pointIndex == 0 && !points.Any(p => p.pointIndex == cond.ValueId) && points.Count > 0
|
||||||
|
? points.First().value : point.value;
|
||||||
|
if (point.pointIndex == 0 && points.Count == 0) actualValue = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 滞后窗:已触发过则用恢复阈值
|
||||||
|
bool isTriggered = cond.LastTriggered.HasValue;
|
||||||
|
double target = isTriggered
|
||||||
|
? (double)(cond.RecoveryThreshold_Numeric ?? cond.TargetValue_Number ?? 0)
|
||||||
|
: (double)(cond.TargetValue_Number ?? 0);
|
||||||
|
|
||||||
|
bool met = Compare(actualValue, cond.CompareOperator ?? "大于", target);
|
||||||
|
results.Add(met);
|
||||||
|
if (met) cond.LastTriggered = DateTime.Now;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool finalResult = rule.JudgmentMode == "AND"
|
||||||
|
? results.All(r => r)
|
||||||
|
: results.Any(r => r);
|
||||||
|
return Task.FromResult(finalResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool Compare(double? actual, string op, double target)
|
||||||
|
{
|
||||||
|
double v = actual ?? double.MinValue;
|
||||||
|
return op switch
|
||||||
|
{
|
||||||
|
"大于" => v > target,
|
||||||
|
"小于" => v < target,
|
||||||
|
"等于" => Math.Abs(v - target) < 0.001,
|
||||||
|
"大于等于" => v >= target,
|
||||||
|
"小于等于" => v <= target,
|
||||||
|
"不等于" => Math.Abs(v - target) > 0.001,
|
||||||
|
_ => false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════
|
||||||
|
// 动作执行
|
||||||
|
// ═══════════════════════════════════════════
|
||||||
|
|
||||||
|
private async Task ExecuteActionsAsync(warehouse_rule rule,
|
||||||
|
Dictionary<int, (string adapterCode, string sourceId, string baseUrl)> deviceMap)
|
||||||
|
{
|
||||||
|
var actions = rule.warehouse_ruleaction ?? new();
|
||||||
|
if (!actions.Any()) return;
|
||||||
|
|
||||||
|
// 冷却检查
|
||||||
|
if (rule.LastTriggered.HasValue && rule.CooldownSec > 0)
|
||||||
|
{
|
||||||
|
if ((DateTime.Now - rule.LastTriggered.Value).TotalSeconds < rule.CooldownSec)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tasks = actions.Select(a => ExecuteSingleActionAsync(a, deviceMap));
|
||||||
|
await Task.WhenAll(tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ExecuteSingleActionAsync(warehouse_ruleaction action,
|
||||||
|
Dictionary<int, (string adapterCode, string sourceId, string baseUrl)> deviceMap)
|
||||||
|
{
|
||||||
|
var actionType = action.ActionType ?? action.Type ?? "控制";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
|
||||||
|
|
||||||
|
switch (actionType)
|
||||||
|
{
|
||||||
|
case "控制":
|
||||||
|
if (action.DeviceId.HasValue && deviceMap.TryGetValue(action.DeviceId.Value, out var dev))
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(dev.baseUrl))
|
||||||
|
{
|
||||||
|
var pointIndex = action.ValueId ?? 0;
|
||||||
|
var value = (double)(action.TargetValue_Switch == "开" ? 1 : action.TargetValue_Number ?? 0);
|
||||||
|
await _gatewayClient.ControlDeviceAsync(dev.baseUrl, dev.adapterCode, dev.sourceId, pointIndex, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "告警":
|
||||||
|
if (action.Alert == "是" && action.DeviceId.HasValue)
|
||||||
|
{
|
||||||
|
var alarm = new iot_alarm
|
||||||
|
{
|
||||||
|
SourceAlarmId = $"rule-{action.RuleID}-{DateTime.Now.Ticks}",
|
||||||
|
DeviceId = action.DeviceId.Value,
|
||||||
|
AlarmLevel = "重要",
|
||||||
|
AlarmDesc = action.AlertMessage ?? $"规则触发",
|
||||||
|
StartTime = DateTime.Now,
|
||||||
|
State = "未确认",
|
||||||
|
AdapterCode = "RuleEngine",
|
||||||
|
CreateDate = DateTime.Now
|
||||||
|
};
|
||||||
|
_alarmRepo.Add(alarm);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "通知":
|
||||||
|
await _hub.Clients.All.SendAsync("RuleTriggered", new
|
||||||
|
{
|
||||||
|
title = action.AlertMessage ?? "规则触发",
|
||||||
|
alertMessage = action.AlertMessage,
|
||||||
|
deviceId = action.DeviceId
|
||||||
|
}, cts.Token);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException) { /* 超时 */ }
|
||||||
|
catch { /* 单动作失败不阻塞 */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,12 @@ import http from '../api/http.js'
|
|||||||
// TODO Phase2: 遍历 base_device 加载 MapModelId → VgoMap 标记; 告警设备红色闪烁
|
// TODO Phase2: 遍历 base_device 加载 MapModelId → VgoMap 标记; 告警设备红色闪烁
|
||||||
|
|
||||||
import initMessageHub from './index.js' // 导入消息中心初始化函数
|
import initMessageHub from './index.js' // 导入消息中心初始化函数
|
||||||
|
// 规则引擎 SignalR 订阅: RuleTriggered → 告警弹窗
|
||||||
|
const initRuleEngineListener = (connection) => {
|
||||||
|
connection.on("RuleTriggered", (data) => {
|
||||||
|
handlePushMessage({ title: data.title || '规则触发', message: data.alertMessage || '', code: '3', notificationType: 3, level: data.alertMessage, date: new Date().toISOString() });
|
||||||
|
});
|
||||||
|
};
|
||||||
import Message from './Message.vue'
|
import Message from './Message.vue'
|
||||||
|
|
||||||
// 生成唯一ID的辅助函数
|
// 生成唯一ID的辅助函数
|
||||||
|
|||||||
@@ -1,77 +1,78 @@
|
|||||||
// *Author:jxx
|
// *代码由框架生成,任何更改都可能导致被代码生成器覆盖
|
||||||
// *Contact:283591387@qq.com
|
export default function(){
|
||||||
// *代码由框架生成,任何更改都可能导致被代码生成器覆盖
|
const table = {
|
||||||
export default function(){
|
key: 'RuleID',
|
||||||
const table = {
|
footer: "Foots",
|
||||||
key: 'RuleID',
|
cnName: '规则',
|
||||||
footer: "Foots",
|
name: 'warehouse_rule',
|
||||||
cnName: '规则',
|
newTabEdit: false,
|
||||||
name: 'warehouse_rule',
|
url: "/warehouse_rule/",
|
||||||
newTabEdit: false,
|
sortName: "Title",
|
||||||
url: "/warehouse_rule/",
|
fixedSearch:false
|
||||||
sortName: "Title",
|
};
|
||||||
fixedSearch:false
|
const tableName = table.name;
|
||||||
};
|
const tableCNName = table.cnName;
|
||||||
const tableName = table.name;
|
const newTabEdit = false;
|
||||||
const tableCNName = table.cnName;
|
const key = table.key;
|
||||||
const newTabEdit = false;
|
const editFormFields = {"Title":"","JudgmentMode":"","JudgmentValue":"","Enable":"启用","Priority":0,"CooldownSec":60};
|
||||||
const key = table.key;
|
const editFormOptions = [
|
||||||
const editFormFields = {"Title":"","JudgmentMode":"","JudgmentValue":""};
|
[{"title":"规则标题","required":true,"field":"Title","colSize":60.0},
|
||||||
const editFormOptions = [[{"title":"规则标题","required":true,"field":"Title","colSize":100.0}],
|
{"dataKey":"条件判断方式","data":[],"title":"判断方式","field":"JudgmentMode","colSize":40.0,"type":"select"}],
|
||||||
[{"dataKey":"条件判断方式","data":[],"title":"条件判断方式","field":"JudgmentMode","colSize":50.0,"type":"select"},
|
[{"title":"优先级","field":"Priority","colSize":50.0,"type":"number"},
|
||||||
{"dataKey":"条件判断目标值","data":[],"title":"条件判断目标值","field":"JudgmentValue","colSize":50.0,"type":"select"}]];
|
{"title":"冷却时间(秒)","field":"CooldownSec","colSize":50.0,"type":"number"}],
|
||||||
const searchFormFields = {};
|
[{"dataKey":"启用状态","data":[],"title":"启用","field":"Enable","colSize":50.0,"type":"select"},
|
||||||
const searchFormOptions = [];
|
{"dataKey":"条件判断目标值","data":[],"title":"目标值","field":"JudgmentValue","colSize":50.0,"type":"select"}]
|
||||||
const columns = [{field:'Title',title:'规则标题',type:'string',link:true,width:150,require:true,align:'left'},
|
];
|
||||||
{field:'JudgmentMode',title:'条件判断方式',type:'string',bind:{ key:'条件判断方式',data:[]},width:150,align:'left'},
|
const searchFormFields = {};
|
||||||
{field:'JudgmentValue',title:'条件判断目标值',type:'string',bind:{ key:'条件判断目标值',data:[]},width:110,align:'left'},
|
const searchFormOptions = [];
|
||||||
{field:'RuleID',title:'规则编号',type:'int',width:120,hidden:true,require:true,align:'left'}];
|
const columns = [
|
||||||
const detail ={columns:[]};
|
{field:'Title',title:'规则标题',type:'string',link:true,width:150,require:true,align:'left'},
|
||||||
const details = [ {
|
{field:'JudgmentMode',title:'判断方式',type:'string',bind:{ key:'条件判断方式',data:[]},width:100,align:'left'},
|
||||||
cnName: '规则条件',
|
{field:'Priority',title:'优先级',type:'int',width:80,align:'left'},
|
||||||
table: 'warehouse_rulecondition',
|
{field:'CooldownSec',title:'冷却(秒)',type:'int',width:80,align:'left'},
|
||||||
columns: [{field:'id',title:'条件编号',type:'int',width:110,hidden:true,require:true,align:'left'},
|
{field:'Enable',title:'启用',type:'string',bind:{ key:'启用状态',data:[]},width:80,align:'left'},
|
||||||
{field:'DeviceId',title:'设备',type:'int',bind:{ key:'所有设备列表',data:[]},width:110,edit:{type:'select'},align:'left'},
|
{field:'LastTriggered',title:'上次触发',type:'datetime',width:150,align:'left'},
|
||||||
{field:'ValueId',title:'变量',type:'int',bind:{ key:'变量列表',data:[]},width:110,edit:{type:'select'},align:'left'},
|
{field:'RuleID',title:'规则编号',type:'int',width:120,hidden:true,require:true,align:'left'}
|
||||||
{field:'Type',title:'比对类型',type:'string',bind:{ key:'比对类型',data:[]},width:150,edit:{type:'select'},align:'left'},
|
];
|
||||||
{field:'CompareOperator',title:'比较运算',type:'string',bind:{ key:'比较运算',data:[]},width:150,edit:{type:'select'},align:'left'},
|
const detail ={columns:[]};
|
||||||
{field:'TargetValue_Switch',title:'目标值开关状态',type:'string',bind:{ key:'开关状态',data:[]},width:120,edit:{type:'select'},align:'left'},
|
const details = [
|
||||||
{field:'TargetValue_Number',title:'目标值数值',type:'int',width:120,edit:{type:'number'},align:'left'},
|
{
|
||||||
{field:'RuleID',title:'所属规则编号',type:'int',width:120,hidden:true,align:'left'}],
|
cnName: '规则条件',
|
||||||
sortName: 'id',
|
table: 'warehouse_rulecondition',
|
||||||
key: 'id',
|
columns: [
|
||||||
buttons:[],
|
{field:'id',title:'条件编号',type:'int',width:110,hidden:true,require:true,align:'left'},
|
||||||
delKeys:[],
|
{field:'DeviceId',title:'设备',type:'int',bind:{ key:'所有设备列表',data:[]},width:110,edit:{type:'select'},align:'left'},
|
||||||
detail:null
|
{field:'ValueId',title:'变量',type:'int',width:110,edit:{type:'number'},align:'left'},
|
||||||
},
|
{field:'Type',title:'比对类型',type:'string',bind:{ key:'比对类型',data:[]},width:150,edit:{type:'select'},align:'left'},
|
||||||
{
|
{field:'CompareOperator',title:'比较运算',type:'string',bind:{ key:'比较运算',data:[]},width:150,edit:{type:'select'},align:'left'},
|
||||||
cnName: '规则动作',
|
{field:'TargetValue_Number',title:'目标值',type:'int',width:120,edit:{type:'number'},align:'left'},
|
||||||
table: 'warehouse_ruleaction',
|
{field:'TargetValue_Switch',title:'开关状态',type:'string',bind:{ key:'开关状态',data:[]},width:120,edit:{type:'select'},align:'left'},
|
||||||
columns: [{field:'id',title:'动作编号',type:'int',width:110,hidden:true,require:true,align:'left'},
|
{field:'RecoveryThreshold_Numeric',title:'恢复阈值',type:'decimal',width:120,edit:{type:'number'},align:'left'},
|
||||||
{field:'DeviceId',title:'设备',type:'int',bind:{ key:'所有设备列表',data:[]},width:110,edit:{type:'select'},align:'left'},
|
{field:'RuleID',title:'所属规则编号',type:'int',width:120,hidden:true,align:'left'}
|
||||||
{field:'ValueId',title:'变量',type:'int',bind:{ key:'变量列表',data:[]},width:110,edit:{type:'select'},align:'left'},
|
],
|
||||||
{field:'Type',title:'值类型',type:'string',bind:{ key:'比对类型',data:[]},width:150,edit:{type:'select'},align:'left'},
|
sortName: 'id', key: 'id', buttons:[], delKeys:[], detail:null
|
||||||
{field:'TargetValue_Switch',title:'目标值开状态状态',type:'string',bind:{ key:'开关状态',data:[]},width:120,edit:{type:'select'},align:'left'},
|
},
|
||||||
{field:'TargetValue_Number',title:'目标值数值',type:'int',width:120,edit:{type:'number'},align:'left'},
|
{
|
||||||
{field:'RuleID',title:'所属规则编号',type:'int',width:120,hidden:true,align:'left'}],
|
cnName: '规则动作',
|
||||||
sortName: 'id',
|
table: 'warehouse_ruleaction',
|
||||||
key: 'id',
|
columns: [
|
||||||
buttons:[],
|
{field:'id',title:'动作编号',type:'int',width:110,hidden:true,require:true,align:'left'},
|
||||||
delKeys:[],
|
{field:'DeviceId',title:'设备',type:'int',bind:{ key:'所有设备列表',data:[]},width:110,edit:{type:'select'},align:'left'},
|
||||||
detail:null
|
{field:'ValueId',title:'变量',type:'int',width:110,edit:{type:'number'},align:'left'},
|
||||||
}];
|
{field:'ActionType',title:'动作类型',type:'string',bind:{ key:'动作类型',data:[]},width:150,edit:{type:'select'},align:'left'},
|
||||||
|
{field:'TargetValue_Number',title:'目标值',type:'int',width:120,edit:{type:'number'},align:'left'},
|
||||||
return {
|
{field:'TargetValue_Switch',title:'开关状态',type:'string',bind:{ key:'开关状态',data:[]},width:120,edit:{type:'select'},align:'left'},
|
||||||
table,
|
{field:'Alert',title:'生成告警',type:'string',bind:{ key:'开关状态',data:[]},width:100,edit:{type:'select'},align:'left'},
|
||||||
key,
|
{field:'AlertMessage',title:'告警内容',type:'string',width:200,edit:{type:'text'},align:'left'},
|
||||||
tableName,
|
{field:'RuleID',title:'所属规则编号',type:'int',width:120,hidden:true,align:'left'}
|
||||||
tableCNName,
|
],
|
||||||
newTabEdit,
|
sortName: 'id', key: 'id', buttons:[], delKeys:[], detail:null
|
||||||
editFormFields,
|
}
|
||||||
editFormOptions,
|
];
|
||||||
searchFormFields,
|
|
||||||
searchFormOptions,
|
return {
|
||||||
columns,
|
table, key, tableName, tableCNName, newTabEdit,
|
||||||
detail,
|
editFormFields, editFormOptions, searchFormFields, searchFormOptions,
|
||||||
details
|
columns, detail, details
|
||||||
|
};
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user