diff --git a/api/.vscode/launch.json b/api/.vscode/launch.json
index 47f342e..10dd5d3 100644
--- a/api/.vscode/launch.json
+++ b/api/.vscode/launch.json
@@ -56,8 +56,8 @@
// "--qndmn http://resources.anxinyun.cn",
// "--qndmn http://rhvqdivo5.hn-bkt.clouddn.com",
// click 开发
- // "--clickHouseUrl http://10.8.30.95",
- // "--clickHousePort 30123",
+ "--clickHouseUrl http://10.8.30.95",
+ "--clickHousePort 30123",
// click 测试
// "--clickHouseUrl http://10.8.30.161",
// "--clickHousePort 30123",
@@ -71,20 +71,20 @@
//
//
// click 商用
- "--clickHouseUrl http://218.3.126.49",
- "--clickHousePort 18123",
- "--clickHouseUser default",
- "--clickHousePassword Wbo@hi1I",
- "--clickHouseAnxincloud anxinyun",
- "--clickHousePepEmis pepca",
- "--clickHouseProjectManage peppm",
- "--clickHouseVcmp video_access",
- "--clickHouseDataAlarm alarm",
- "--clickHouseIot iota",
- "--clickHouseCamworkflow camundaworkflow",
- "--confirmAlarmAnxinUserId 1",
- "--vcmpAppId 5048b08d-c449-4d7f-b1ec-f741012aefe8",
- "--vcmpAppSecret 5ba8c0ab-9fbd-4f07-9817-c48017c3cbad",
+ // "--clickHouseUrl http://218.3.126.49",
+ // "--clickHousePort 18123",
+ // "--clickHouseUser default",
+ // "--clickHousePassword Wbo@hi1I",
+ // "--clickHouseAnxincloud anxinyun",
+ // "--clickHousePepEmis pepca",
+ // "--clickHouseProjectManage peppm",
+ // "--clickHouseVcmp video_access",
+ // "--clickHouseDataAlarm alarm",
+ // "--clickHouseIot iota",
+ // "--clickHouseCamworkflow camundaworkflow",
+ // "--confirmAlarmAnxinUserId 1",
+ // "--vcmpAppId 5048b08d-c449-4d7f-b1ec-f741012aefe8",
+ // "--vcmpAppSecret 5ba8c0ab-9fbd-4f07-9817-c48017c3cbad",
//
//
// 似乎不能传空 先注释 * 2
@@ -98,17 +98,17 @@
// "--clickHouseDataAlarm default",
// "--clickHouseIot iot",
// 测试
- // "--clickHouseAnxincloud anxinyun",
- // "--clickHousePepEmis pepca",
- // "--clickHouseProjectManage peppm",
- // "--clickHouseVcmp video_access_dev",
- // "--clickHouseDataAlarm default",
- // "--clickHouseDataAlarmLocal default",
- // "--clickHouseIot iota",
- // "--clickHouseCamworkflow camworkflow",
- // "--confirmAlarmAnxinUserId 1",
- // "--vcmpAppId 5048b08d-c449-4d7f-b1ec-f741012aefe8",
- // "--vcmpAppSecret 5ba8c0ab-9fbd-4f07-9817-c48017c3cbad",
+ "--clickHouseAnxincloud anxinyun",
+ "--clickHousePepEmis pepca",
+ "--clickHouseProjectManage peppm",
+ "--clickHouseVcmp video_access_dev",
+ "--clickHouseDataAlarm alarm",
+ "--clickHouseDataAlarmLocal default",
+ "--clickHouseIot iota",
+ "--clickHouseCamworkflow camworkflow",
+ "--confirmAlarmAnxinUserId 1",
+ "--vcmpAppId 5048b08d-c449-4d7f-b1ec-f741012aefe8",
+ "--vcmpAppSecret 5ba8c0ab-9fbd-4f07-9817-c48017c3cbad",
"--apiCrawUrl http://218.3.126.49:30555/v1" //设备升级接口
]
},
diff --git a/api/app/lib/controllers/dataCacl/index.js b/api/app/lib/controllers/dataCacl/index.js
new file mode 100644
index 0000000..21eca58
--- /dev/null
+++ b/api/app/lib/controllers/dataCacl/index.js
@@ -0,0 +1,636 @@
+
+const moment = require('moment');
+//获取异常识别算法
+async function findAbnMethods(ctx, next) {
+ let rslt = null;
+ let error = { name: 'FindError', message: '异常识别算法获取失败' };
+ try {
+ const models = ctx.fs.dc.models;
+ let abnMethods = await models.AbnTypes.findAll();
+ rslt = abnMethods.map(s => ({
+ id: s.id,
+ name: s.name,
+ des: s.des
+ }));
+ error = null;
+ } catch (err) {
+ ctx.fs.logger.error(`path: ${ctx.path}, error: ${err}`);
+ }
+ if (error) {
+ ctx.status = 400;
+ ctx.body = error;
+ } else {
+ ctx.status = 200;
+ ctx.body = rslt;
+ }
+}
+//获取异常参数配置
+async function findAbnParamList(ctx) {
+ const { sensorId } = ctx.query
+ const id=sensorId.split(',')
+ let rslt = null;
+ let error = { name: 'FindError', message: '异常参数配置获取失败' };
+ try {
+ const models = ctx.fs.dc.models;
+ let abnParamList = await models.AbnReportParams.findAll({
+ where: { sensorId:{$in:id} }
+ })
+ rslt = abnParamList.map(s => ({
+ key: s.id,
+ id: s.id,
+ sensorId: s.sensorId,
+ sensorName: s.sensorLocationDescription,
+ abnType: s.abnTypeId,
+ enabled: s.enabled,
+ factorId: s.factorId,
+ factorName: s.factor,
+ params: s.params,
+ itemIndex:s.itemIndex
+ }));
+ error = null;
+ } catch (err) {
+ ctx.fs.logger.error(`path: ${ctx.path}, error: ${err}`);
+ }
+ if (error) {
+ ctx.status = 400;
+ ctx.body = error;
+ } else {
+ ctx.status = 200;
+ ctx.body = rslt;
+ }
+}
+
+//新增异常参数配置
+async function createAbnParam(ctx) {
+ let error = { name: 'CreateError', message: '异常参数配置新增失败' };
+ const models = ctx.fs.dc.models
+ const data = ctx.request.body
+
+ try {
+ for (let i = 0; i < data.length; i++) {
+ let dataItem = data[i];
+ if (dataItem && dataItem.params && dataItem.abnType && dataItem.enabled != null && dataItem.sensorId && dataItem.factorId) {
+ let dataToSave = {
+ sensorId: dataItem.sensorId,
+ sensorLocationDescription:dataItem.sensorName,
+ enabled: dataItem.enabled,
+ abnTypeId: dataItem.abnType,
+ factorId: dataItem.factorId,
+ factor: dataItem.factorName,
+ itemIndex: dataItem.itemId,
+ params: dataItem.params
+ };
+ await models.AbnReportParams.create(dataToSave)
+
+ error = null
+ // // 日志信息
+ // ctx.fs.api = ctx.fs.api || {}
+ // ctx.fs.api.actionParameter = JSON.stringify(data)
+ // ctx.fs.api.actionParameterShow = `新增异常推送配置id:${newId}`
+ }
+ }
+ }
+ catch (err) {
+ ctx.fs.logger.error(`path: ${ctx.path}, error: ${err}`)
+ }
+ if (error) {
+ ctx.status = 400
+ ctx.body = error
+ } else {
+ ctx.status = 204
+ }
+}
+//
+async function batchSwitch(ctx, next) {
+ const ids = ctx.params.ids.split(',')
+ const data = ctx.request.body
+ let error = { name: 'UpdateError', message: data ? '批量启用异常参数配置失败' : '批量禁用异常参数配置失败' };
+ try {
+ for (let i = 0; i < ids.length; i++) {
+ let id = ids[i];
+ const models = ctx.fs.dc.models;
+ let abnParam = await models.AbnReportParams.findOne({ where: { id: id } });
+ if (abnParam) {
+ let dataToSave = {};
+ if (data.use == 'switch') {
+ dataToSave.enabled = data.enabled;//批量启用or禁用
+ } else {
+ dataToSave.params = data.paramJson;//批量改参数
+ }
+ if (Object.keys(dataToSave).length) {
+ await models.AbnReportParams.update(dataToSave, { where: { id } });
+ }
+ error = null;
+ // // 日志信息
+ // ctx.fs.api = ctx.fs.api || {};
+ // ctx.fs.api.actionParameter = JSON.stringify(data);
+ // ctx.fs.api.actionParameterShow = `异常参数配置id:${id}`;
+ } else {
+ error = { name: 'NotFound', message: `不存在{id=${id}}的异常参数配置` };
+ }
+ }
+ } catch (err) {
+ ctx.fs.logger.error(`path: ${ctx.path}, error: ${err}`);
+ }
+ if (error) {
+ ctx.status = 400;
+ ctx.body = error;
+ } else {
+ ctx.status = 204;
+ }
+}
+
+//删除异常参数配置
+async function deleteAbnParam(ctx, next) {
+ let error = { name: 'DeleteError', message: '异常参数配置删除失败' };
+ const ids = ctx.params.ids.split(',');
+ //const { id } = ctx.params;
+ try {
+ for (let i = 0; i < ids.length; i++) {
+ let id = ids[i];
+ const models = ctx.fs.dc.models;
+ let abnParam = await models.AbnReportParams.findOne({ where: { id } });
+ if (abnParam) {
+ await models.AbnReportParams.destroy({ where: { id } });
+ error = null;
+ } else {
+ error = { name: 'NotFound', message: `不存在{id=${id}}的异常参数配置` };
+ }
+ }
+ } catch (err) {
+ ctx.fs.logger.error(`path: ${ctx.path}, error: ${err}`);
+ }
+ if (error) {
+ ctx.status = 400;
+ ctx.body = error;
+ } else {
+ ctx.status = 204;
+ }
+}
+
+//修改异常推送配置
+async function updateAbnParam(ctx) {
+ let error = { name: 'UpdateError', message: '异常参数配置修改失败' }
+ const ids = ctx.params.ids.split(',')
+ const data = ctx.request.body
+
+ if (data && Object.keys(data).length) {
+ try {
+ for (let i = 0; i < ids.length; i++) {
+ let id = ids[i];
+ const models = ctx.fs.dc.models;
+ let abnParam = await models.AbnReportParams.findOne({ where: { id: id } });
+ if (abnParam) {
+ let dataToSave = {};
+ const { abnType, params, enabled } = data;
+ if (enabled != null && enabled != abnParam.enabled)
+ dataToSave.enabled = enabled;
+ //中断
+ if (abnType == 1) {
+ if (params != null && params.thr_int !== abnParam.params.thr_int) {
+ dataToSave.params = params;
+ }
+ }
+ //毛刺
+ if (abnType == 2) {
+ if (params != null && params.thr_burr !== abnParam.params.thr_burr) {
+ dataToSave.params = params;
+ }
+ }
+ //趋势
+ if (abnType == 3) {
+ if (params != null &&
+ (params.thr_burr !== abnParam.params.thr_burr || params.win_med !== abnParam.params.win_med
+ || params.win_avg !== abnParam.params.win_avg || params.win_grad !== abnParam.params.win_grad
+ || params.thr_grad !== abnParam.params.thr_grad || params.thr_der !== abnParam.params.thr_der
+ || params.days_Last !== abnParam.params.days_Last)) {
+ dataToSave.params = params;
+ }
+ }
+ if (Object.keys(dataToSave).length) {
+ await models.AbnReportParams.update(dataToSave, { where: { id } });
+ }
+ error = null;
+ } else {
+ error = { name: 'NotFound', message: `不存在{id=${id}}的异常参数配置` };
+ }
+ }
+ } catch (err) {
+ ctx.fs.logger.error(`path: ${ctx.path}, error: ${err}`);
+ }
+ }
+ if (error) {
+ ctx.status = 400;
+ ctx.body = error;
+ } else {
+ ctx.status = 204;
+ }
+}
+//异常数据对比
+async function getAbnTaskResult(ctx, next) {
+ let error = { name: 'TaskError', message: '异常数据对比失败' }
+ const models = ctx.fs.dc.models
+ const structId = ctx.params.id
+ const startTime = ctx.params.start
+ const endTime = ctx.params.end
+ const data = ctx.request.body
+ const stationId = data.station
+ let factorProto = await models.Factor.findOne({
+ where: { id: data.factorId },
+ attributes: ['id', 'proto']
+ });
+ let protoItems = await models.FactorProtoItem.findAll({
+ where: { proto: factorProto.proto },
+ attributes: ['id', 'name']
+ });
+ let itemName = await models.FactorProtoItem.findOne({
+ where: { id: data.itemId ? data.itemId : protoItems[0].id },
+ attributes: ['id', 'field_name', 'name']
+ });
+ try {
+ const itemsObj = await findThemeItems(models, data.factorId)
+ const filter = {
+ query: {
+ bool: {
+ must: [
+ { match: { "sensor": stationId } }
+ ]
+ }
+ }
+ }
+ if (startTime && endTime) {
+ filter.query.bool.must.push({ range: { "collect_time": { gte: moment(startTime).toISOString(), lte: moment(endTime).toISOString() } } });
+ }
+ const esThemeData = await findThemeDataFromES(ctx.app.fs.esclient, filter)
+ const stationsData = esThemeData.reduce((p, c) => {
+ const { sensor, data, collect_time } = c._source;
+ p.unshift(Object.assign({}, data, { time: moment(collect_time).format('YYYY-MM-DD HH:mm:ss') }));
+ return p;
+ }, []);
+ //获取前一天的最后一条数据
+ const preFilter = {
+ query: {
+ bool: {
+ must: [
+ { match: { "sensor": stationId } },
+ { range: { "collect_time": { gte: moment(startTime).add('days', -1).toISOString(), lte: moment(startTime).toISOString() } } }
+ ]
+ }
+ }
+ }
+ const esPreData = await findThemeDataFromES(ctx.app.fs.esclient, preFilter);
+ const preOneData = esPreData.reduce((p, c) => {
+ const { data, collect_time } = c._source;
+ p.unshift(Object.assign({}, data, { time: moment(collect_time).format('YYYY-MM-DD HH:mm:ss') }));
+ return p;
+ }, []);
+ let one = preOneData && preOneData.length > 0 ? preOneData[preOneData.length - 1] : null;
+
+ let itemKey = itemName.get({ plain: true }).field_name;//监测项名称
+ let itemn = itemName.get({ plain: true }).name;
+ let calcResult = calcAlgorithm(one, stationsData, data, itemKey);//计算
+ ctx.status = 200;
+ ctx.body = {
+ unit: itemsObj[itemKey].unit,
+ method: data.abnType,
+ itemKey: itemKey,
+ itemName: itemn,
+ stationData: stationsData,
+ resultArray: calcResult
+ };
+ } catch (err) {
+ ctx.fs.logger.error(err);
+ ctx.status = 400;
+ ctx.body = {
+ name: "FindError",
+ message: "异常数据识别-数据对比失败"
+ }
+ }
+}
+
+let calcAlgorithm = function (dataOne, dataSource, params, itemKey) {
+ let result;
+ switch (params.abnType) {
+ case "interrupt":
+ result = interrupt(dataOne, dataSource, params, itemKey);
+ break;
+ case "burr":
+ result = burr(dataOne, dataSource, params, itemKey).result;
+ break;
+ case "trend":
+ result = trend(null, dataSource, params, itemKey);
+ break;
+ }
+ return result;
+};
+//中断
+let interrupt = function (dataOne, dataSource, params, key) {
+ let result = [];
+ if (dataSource.length != 0) {
+ if (dataOne == null) {
+ result.push({
+ type: "interrupt",
+ hour: 24.00,
+ time: dataSource[0].time,
+ value: dataSource[0][key]
+ });//第一个点中断
+ } else {
+ dataSource.unshift(dataOne);
+ }
+ for (let i = 0; i < dataSource.length - 1; i++) {
+ if (dataSource[i] == null || dataSource[i + 1] == null) continue;
+ let hour = getHour(dataSource[i + 1].time, dataSource[i].time);
+ if (hour >= params.params.thr_int) {
+ result.push({
+ type: "interrupt",
+ hour: hour,
+ time: dataSource[i + 1].time,
+ value: dataSource[i + 1][key]
+ });
+ }
+ }
+ }
+ return result;
+}
+//毛刺
+let burr = function (dataOne, dataSource, params, key) {
+ let burrTv = params.params.thr_burr;
+ let result = [];//毛刺点
+ let dataSAfterBurr = [];//去掉毛刺点的数组
+ if (dataSource.length != 0) {
+ if (dataOne != null) {
+ dataSource.unshift(dataOne);
+ }
+ for (let i = 1; i < dataSource.length - 1; i++) {
+ if (dataSource[i - 1] == null || dataSource[i] == null || dataSource[i + 1] == null) continue
+ let gap1 = dataSource[i][key] - dataSource[i - 1][key]
+ let gap2 = dataSource[i][key] - dataSource[i + 1][key]
+
+ let gap3 = dataSource[i - 1][key] - dataSource[i][key]
+ let gap4 = dataSource[i + 1][key] - dataSource[i][key]
+
+ let result1 = (gap1 > burrTv && gap2 > burrTv)
+ let result2 = (gap3 > burrTv && gap4 > burrTv)
+
+ if (i == 1) {//第一个点
+ dataSAfterBurr.push(dataSource[0])
+ }
+ if (result1 || result2) {
+ result.push({
+ type: "burr",
+ burr: result1 ? Math.min(gap1, gap2) : Math.min(gap3, gap4),
+ time: dataSource[i].time,
+ value: dataSource[i][key]
+ })
+ } else {
+ dataSAfterBurr.push(dataSource[i])
+ }
+ if (i == dataSource.length - 2) {//最后一个点
+ dataSAfterBurr.push(dataSource[dataSource.length - 1])
+ }
+ }
+ }
+ return { result: result, dataSAfterBurr: dataSAfterBurr }
+}
+//异常趋势
+let trend = function (dataOne, dataSource, params, key) {
+ let result;
+ if (dataSource.length != 0) {
+ //去完毛刺的新数组
+ let afterBurr = burr(dataOne, dataSource, params, key).dataSAfterBurr;
+ //滑动中值
+ let arrAfterMedian = [];
+ for (let i = 0; i < afterBurr.length; i += parseInt(params.params.win_med)) {
+ let arr = afterBurr.slice(i, i + parseInt(params.params.win_med))
+ let oneMedian = calcMedian(arr, key)
+ arrAfterMedian.push(oneMedian)
+ }
+ //滑动均值
+ let arrAfterAvg = calcMeanValue(arrAfterMedian, params.params.win_avg, key)
+ //错位相减,相当于求导
+ let arrAfterDe = []
+ for (let j = 0; j < arrAfterAvg.length - 1; j++) {
+ let one = {
+ value: arrAfterAvg[j + 1].value - arrAfterAvg[j].value,
+ time: arrAfterAvg[j + 1].time
+ }
+ arrAfterDe.push(one);
+ }
+ //最后判断
+ let finalArray = finalJudge(arrAfterDe, arrAfterMedian, params)
+ result = {
+ calcFinal: finalArray,
+ calcPreprocess: arrAfterAvg//要画预处理+滑动均值完了的曲线
+ };
+ }
+ return result
+}
+let getHour = function (s1, s2) {
+ s1 = new Date(s1.replace(/-/g, '/'))
+ s2 = new Date(s2.replace(/-/g, '/'))
+ let ms = Math.abs(s1.getTime() - s2.getTime())
+ return ms / 1000 / 60 / 60;
+}
+//计算一组数据的中值
+let calcMedian = function (array, key) {
+ let result;
+ if (array != null || array.length > 0) {
+ array.sort((a, b) => { return a[key] - b[key]})
+ if (array.length % 2 == 0) {//偶数
+ let index1 = array.length / 2;
+ result = {
+ value: (array[index1][key] + array[index1 - 1][key]) / 2,
+ time: array[index1].time
+ }
+
+ } else {//奇数
+ let index = (array.length - 1) / 2
+ result = {
+ value: array[index][key],
+ time: array[index].time
+ }
+ }
+ }
+ return result;
+}
+//计算一组数据的均值
+let calcMeanValue = function (array, coef, key) {
+ let result = [];
+ let sum = 0;
+ if (array != null || array.length > 0) {
+ for (let i = 0; i < array.length; i++) {
+ let value;
+ if (i < parseInt(coef)) {
+ sum = sum + array[i].value
+ value = sum / (i + 1)
+ } else {
+ let arr = array.slice(i - parseInt(coef) + 1, i + 1)
+ let ssum = 0;
+ for (let s = 0; s < arr.length; s++) {
+ ssum = ssum + arr[s].value
+ }
+ value = ssum / parseInt(coef)
+ }
+ let one = {
+ value: value,
+ time: array[i].time
+ }
+ result.push(one)
+ }
+ }
+ return result
+}
+
+let finalJudge = function (array, original, params) {
+ let ups = 1, downs = 1;
+ let tempUp = [], tempDown = [];
+ let point = params.params.win_grad;//渐变点个数
+ let deTv = params.params.thr_der;//导数阈值
+ let greTv = params.params.thr_grad;//渐变阈值
+ let finalArray = [];
+ for (let i = 0; i < array.length; i++)//对最新数组作阈值判断
+ {
+ if (array[i].value > deTv) {
+ ups = ups + 1
+ if (ups == 2) {
+ tempUp.push(original[i])
+ }
+ tempUp.push(original[i + 1])
+ if (tempDown.length >= point) {
+ let bbb = tempDown[tempDown.length - 1].value - tempDown[0].Value
+ if (downs >= point && bbb < -greTv) {
+ let one = {
+ startTime: tempDown[0].time,
+ endTime: tempDown[tempDown.length - 1].time,
+ startValue: tempDown[0].value,
+ endValue: tempDown[tempDown.length - 1].value,
+ value: bbb,
+ des: "异常下降"
+ };
+ finalArray.push(one)
+ }
+ }
+ downs = 1
+ tempDown = []
+ } else if (array[i].value < -deTv) {
+ downs = downs + 1;
+ if (downs == 2) {
+ tempDown.push(original[i])
+ }
+ tempDown.push(original[i + 1])
+ if (tempUp.length >= point) {
+ let aaa = tempUp[tempUp.length - 1].value - tempUp[0].value
+ if (ups >= point && aaa > greTv) {
+ let one = {
+ startTime: tempUp[0].time,
+ endTime: tempUp[tempUp.length - 1].time,
+ startValue: tempUp[0].value,
+ endValue: tempUp[tempUp.length - 1].value,
+ value: aaa,
+ des: "异常上升"
+ };
+ finalArray.push(one)
+ }
+ }
+ ups = 1;
+ tempUp = []
+ }
+ }
+ if (ups >= point) {
+ let ccc = tempUp[tempUp.length - 1].value - tempUp[0].value
+ if (ccc > greTv) {
+ let one = {
+ startTime: tempUp[0].time,
+ endTime: tempUp[tempUp.length - 1].time,
+ startValue: tempUp[0].value,
+ endValue: tempUp[tempUp.length - 1].value,
+ value: ccc,
+ des: "异常上升"
+ };
+ finalArray.push(one)
+ }
+ }
+ if (downs >= point) {
+ let ddd = tempDown[tempDown.length - 1].value - tempDown[0].value
+ if (ddd < -greTv) {
+ let one = {
+ startTime: tempDown[0].time,
+ endTime: tempDown[tempDown.length - 1].time,
+ startValue: tempDown[0].value,
+ endValue: tempDown[tempDown.length - 1].value,
+ value: ddd,
+ des: "异常下降"
+ };
+ finalArray.push(one);
+ }
+ }
+ return finalArray
+}
+async function findThemeItems(models, factor) {
+ try {
+ let factorProto = await models.Factor.findOne({
+ where: { id: factor },
+ attributes: ['id', 'proto']
+ });
+ let protoItems = await models.FactorProtoItem.findAll({
+ where: { proto: factorProto.proto },
+ attributes: ['name', 'fieldName'],
+ include: [{
+ model: models.ItemUnit,
+ where: { default: true },
+ required: true,
+ attributes: ['name']
+ }]
+ });
+ let itemsObj = protoItems.reduce((p, c) => {
+ p[c.fieldName] = {
+ name: c.name,
+ unit: c.itemUnits[0] ? c.itemUnits[0].name : null
+ }
+ return p
+ }, {})
+ return itemsObj
+ } catch (err) {
+ throw err
+ }
+};
+async function findThemeDataFromES(esclient, filter, limit, _source) {
+ try {
+ let rslt = []
+ const client = esclient[THEME_DATA]
+ let params = {
+ index: client.config.index,
+ type: client.config.type,
+ body: filter
+ }
+
+ params.size = limit
+ if (limit == null) {
+ const countRes = await client.count(params)
+ params.size = countRes.count > 10000 ? 10000 : countRes.count
+ }
+
+ params._source = _source || ["sensor", "collect_time", "data"]
+ params.body.sort = { "collect_time": { "order": "desc" } }
+ let res = await client.search(params)
+ rslt = res.hits.hits
+ return rslt
+ } catch (err) {
+ throw err
+ }
+}
+
+
+
+
+
+
+module.exports = {
+ findAbnMethods,
+ findAbnParamList,
+ createAbnParam,
+ updateAbnParam,
+ batchSwitch,
+ deleteAbnParam,
+ getAbnTaskResult
+};
\ No newline at end of file
diff --git a/api/app/lib/controllers/monitor/index.js b/api/app/lib/controllers/monitor/index.js
new file mode 100644
index 0000000..fff7966
--- /dev/null
+++ b/api/app/lib/controllers/monitor/index.js
@@ -0,0 +1,192 @@
+
+const moment = require('moment');
+
+async function getStructures (ctx) {
+ try {
+ const { models } = ctx.fs.dc;
+ const { clickHouse } = ctx.app.fs
+ const { pomsProjectId } = ctx.query
+ let bindRes=[]
+ //选择全局就是查询所有项目下的结构物,有选择项目就只查询对应项目的结构物
+ if(pomsProjectId){
+ bindRes = await models.ProjectCorrelation.findAll({where:{id:{ $in: pomsProjectId.split(',') }}})
+ }else{
+ bindRes = await models.ProjectCorrelation.findAll()
+ }
+ let anxinProjectIds = new Set()
+ for (let b of bindRes) {
+ if (b.anxinProjectId.length) {
+ for (let aid of b.anxinProjectId) {
+ anxinProjectIds.add(aid)
+ }
+ }
+ }
+ let undelStrucRes=[]
+ // if (bindRes) {
+ // undelStrucRes = anxinProjectIds.size ?
+ // await clickHouse.anxinyun.query(
+ // `
+ // SELECT
+ // t_structure.id AS strucId,
+ // t_structure.name AS strucName
+
+ // FROM
+ // t_project
+ // LEFT JOIN
+ // t_project_structure
+ // ON t_project_structure.project = t_project.id
+ // LEFT JOIN
+ // t_project_structuregroup
+ // ON t_project_structuregroup.project = t_project.id
+ // LEFT JOIN
+ // t_structuregroup_structure
+ // ON t_structuregroup_structure.structuregroup = t_project_structuregroup.structuregroup
+ // LEFT JOIN
+ // t_project_construction
+ // ON t_project_construction.project = t_project.id
+ // LEFT JOIN
+ // t_structure_site
+ // ON t_structure_site.siteid = t_project_construction.construction
+ // RIGHT JOIN
+ // t_structure
+ // ON t_structure.id = t_project_structure.structure
+ // OR t_structure.id = t_structuregroup_structure.structure
+ // OR t_structure.id = t_structure_site.structid
+
+ // WHERE
+ // project_state != -1
+ // AND
+ // t_project.id IN (${[...anxinProjectIds].join(',')}, -1)
+ // ORDER BY strucId
+ // `
+ // ).toPromise() :
+ // []
+ // }
+ undelStrucRes.push({strucId:4036,strucName:'象山港大桥'})
+ undelStrucRes.push({strucId:1,strucName:'象山港大'})
+ ctx.status = 200;
+ ctx.body = undelStrucRes
+ } catch (error) {
+ ctx.fs.logger.error(`path: ${ctx.path}, error: error`);
+ ctx.status = 400;
+ ctx.body = {
+ message: typeof error == 'string' ? error : undefined
+ }
+ }
+}
+
+async function getFactors (ctx) {
+ try {
+ const { models } = ctx.fs.dc;
+ const { clickHouse } = ctx.app.fs
+ const { structId,cacl } = ctx.query
+ let list=[]
+ if(cacl){
+ const factorList=await clickHouse.alarmLocal.query(`select distinct SafetyFactorTypeId, SafetyFactorTypeName from sensors where PlatformStructureId =${structId}`).toPromise()
+ let fList=factorList.map(m=>m.SafetyFactorTypeId)
+ list=await clickHouse.alarmLocal.query(`select distinct FactorID, Name,Items,ItemNames from factors where FactorID in (${[...fList,-1].join(',')})`).toPromise()
+ }else{
+ list=await clickHouse.alarmLocal.query(`select distinct SafetyFactorTypeId, SafetyFactorTypeName from sensors where PlatformStructureId =${structId}`).toPromise()
+ }
+ ctx.body=list
+ ctx.status=200
+ }catch(error){
+ ctx.fs.logger.error(`path: ${ctx.path}, error: error`);
+ ctx.status = 400;
+ ctx.body = {
+ message: typeof error == 'string' ? error : undefined
+ }
+ }
+}
+//查询设备
+async function getSensors (ctx) {
+ try {
+ const { models } = ctx.fs.dc;
+ const { clickHouse } = ctx.app.fs
+ const { structId,SafetyFactorTypeId } = ctx.query
+ const list=await clickHouse.alarmLocal.query(`
+ select distinct SensorId,SensorLocationDescription,Project
+ from sensors where PlatformStructureId =${structId}
+ and SafetyFactorTypeId=${SafetyFactorTypeId}`).toPromise()
+ ctx.body=list
+ ctx.status=200
+ }catch(error){
+ ctx.fs.logger.error(`path: ${ctx.path}, error: error`);
+ ctx.status = 400;
+ ctx.body = {
+ message: typeof error == 'string' ? error : undefined
+ }
+ }
+}
+//根据设备id和监测因素id查询监测数据
+async function getMonitorData (ctx) {
+ try{
+ const { clickHouse } = ctx.app.fs
+ const { factorId,sensorId,startTime, endTime,limit,page } = ctx.query
+ const offset=page*limit
+ const factorsList=await clickHouse.alarmLocal.query(`SELECT FactorID,Items,ItemNames,ItemUnits FROM factors
+ WHERE FactorID=${factorId}
+ `).toPromise()||[]
+ const monitorData=await clickHouse.alarmLocal.query(`SELECT SensorId,CollectTime,Values FROM themes
+ WHERE SensorId in ('${sensorId}')
+ AND CollectTime >= toDate('${startTime}')
+ AND CollectTime <= toDate('${endTime}')
+ LIMIT ${limit} OFFSET ${offset}
+ `).toPromise()||[]
+ console.log('vmonitorDatamonitorDatamonitorData',monitorData)
+ const id=sensorId&&sensorId.replace('-',':')
+ const sensor=await clickHouse.alarmLocal.query(`SELECT distinct SensorId,SensorLocationDescription FROM sensors WHERE ID='${id}'`).toPromise()
+ //监测项
+ let items={}
+ if(factorsList&&factorsList.length>0){
+ //因素解释
+ let factors=[]
+ //因素名词
+ let factorNames=[]
+ factorsList.map(item=>{
+ factors=item.ItemNames.split(',')
+ factorNames=item.Items.split(',')
+ factors.map(child=>{
+ factorNames.map(p=>{
+ items[p]={name:child,unit:item.ItemUnits}
+ })
+ })
+ })
+ }
+ //设备数据+数据
+ let sensors=[]
+ if(monitorData&&monitorData.length>0){
+ monitorData.map(data => {
+ const values = {};
+ Object.keys(items).forEach(key => {
+ const index = Object.keys(items).indexOf(key);
+ values[key] = data.Values[index];
+ });
+ sensors.push({ values, time: moment(data.CollectTime).format('YYYY-MM-DD HH:mm:ss') });
+ });
+
+ }
+ if(sensor&&sensor.length){
+ ctx.body={items,sensors:[{data:sensors,id:sensor[0].SensorId,name:sensor[0].SensorLocationDescription}]}
+ ctx.status=200
+ }else{
+ ctx.body={items,sensors:[]}
+ ctx.status=200
+ }
+ }catch(error){
+ ctx.fs.logger.error(`path: ${ctx.path}, error: error`);
+ ctx.status = 400;
+ ctx.body = {
+ message: typeof error == 'string' ? error : undefined
+ }
+ }
+}
+
+
+
+
+
+
+module.exports = {
+ getStructures,getFactors,getSensors,getMonitorData
+}
\ No newline at end of file
diff --git a/api/app/lib/index.js b/api/app/lib/index.js
index b8c4758..c5401bf 100644
--- a/api/app/lib/index.js
+++ b/api/app/lib/index.js
@@ -69,7 +69,7 @@ module.exports.models = function (dc) { // dc = { orm: Sequelize对象, ORM: Seq
AppInspection, ProjectApp, ProjectCorrelation, AppAlarm, App, AlarmAppearRecord, AlarmConfirmLog, EmailSendLog, LatestDynamicList, AlarmPushConfig,
MaintenanceRecord, MaintenanceRecordExecuteUser, MaintenancePlanExecuteUser, MaintenancePlan, EquipmentMaintenanceRecord, EquipmentMaintenanceRecordProject,
EquipmentMaintenanceRecordExecuteUser, ServerMaintenanceRecordRepairman, ServerMaintenanceRecord,
- AlarmDataContinuityType, AlarmDataContinuity,
+ AlarmDataContinuityType, AlarmDataContinuity,AbnTypes,AbnReportParams
} = dc.models;
AppInspection.belongsTo(App, { foreignKey: 'projectAppId', targetKey: 'id' });
@@ -135,5 +135,6 @@ module.exports.models = function (dc) { // dc = { orm: Sequelize对象, ORM: Seq
ServerMaintenanceRecordRepairman.belongsTo(ServerMaintenanceRecord, { foreignKey: 'serverMaintenanceRecordId', targetKey: 'id' })
ServerMaintenanceRecord.hasMany(ServerMaintenanceRecordRepairman, { foreignKey: 'serverMaintenanceRecordId', targetKey: 'id' })
-
+ AbnReportParams.belongsTo(AbnTypes, { foreignKey: 'abnTypeId', targetKey: 'id' })
+ AbnTypes.hasMany(AbnReportParams, { foreignKey: 'abnTypeId', targetKey: 'id' })
};
diff --git a/api/app/lib/models/abn_report_params.js b/api/app/lib/models/abn_report_params.js
new file mode 100644
index 0000000..04647f8
--- /dev/null
+++ b/api/app/lib/models/abn_report_params.js
@@ -0,0 +1,101 @@
+/* eslint-disable*/
+
+'use strict';
+
+module.exports = dc => {
+ const DataTypes = dc.ORM;
+ const sequelize = dc.orm;
+ const AbnReportParams = sequelize.define("abnReportParams", {
+ id: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ defaultValue: null,
+ comment: null,
+ primaryKey: true,
+ field: "id",
+ autoIncrement: true
+ },
+ sensorId: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ defaultValue: null,
+ comment: null,
+ primaryKey: false,
+ field: "sensor_id",
+ autoIncrement: false
+ },
+ sensorLocationDescription: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ defaultValue: null,
+ comment: null,
+ primaryKey: false,
+ field: "sensor_location_description",
+ autoIncrement: false
+ },
+ factor: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ defaultValue: null,
+ comment: null,
+ primaryKey: false,
+ field: "factor",
+ autoIncrement: false
+ },
+ factorId: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ defaultValue: null,
+ comment: null,
+ primaryKey: false,
+ field: "factor_id",
+ autoIncrement: false
+ },
+ abnTypeId: {
+ type: DataTypes.INTEGER,
+ allowNull: true,
+ defaultValue: null,
+ comment: null,
+ primaryKey: false,
+ field: "abn_type",
+ autoIncrement: false,
+ references: {
+ key: "id",
+ model: "abnType"
+ }
+ },
+ params: {
+ type: DataTypes.JSONB,
+ allowNull: false,
+ defaultValue: null,
+ comment: null,
+ primaryKey: false,
+ field: "params",
+ autoIncrement: false
+ },
+ enabled: {
+ type: DataTypes.BOOLEAN,
+ allowNull: false,
+ defaultValue: null,
+ comment: null,
+ primaryKey: false,
+ field: "enabled",
+ autoIncrement: false
+ },
+ itemIndex: {
+ type: DataTypes.INTEGER,
+ allowNull: true,
+ defaultValue: null,
+ comment: null,
+ primaryKey: false,
+ field: "item_index",
+ autoIncrement: false
+ }
+ }, {
+ tableName: "abn_report_params",
+ comment: "",
+ indexes: []
+ });
+ dc.models.AbnReportParams = AbnReportParams;
+ return AbnReportParams;
+};
\ No newline at end of file
diff --git a/api/app/lib/models/abn_type.js b/api/app/lib/models/abn_type.js
new file mode 100644
index 0000000..e67070d
--- /dev/null
+++ b/api/app/lib/models/abn_type.js
@@ -0,0 +1,31 @@
+'use strict';
+
+module.exports = (dc) => {
+ const AbnTypes = dc.orm.define('abnTypes',
+ {
+ id: {
+ field: 'id',
+ type: dc.ORM.INTEGER,
+ primaryKey: true,
+ unique: true,
+ allowNull: false,
+ autoIncrement: true
+ },
+ name: {
+ field: 'name',
+ type: dc.ORM.STRING,
+ allowNull: false
+ },
+ des: {
+ field: 'description',
+ type: dc.ORM.STRING,
+ allowNull: false
+ }
+ }, {
+ tableName: 'abn_type'
+ });
+
+ dc.models.AbnTypes = AbnTypes;
+
+ return AbnTypes;
+};
\ No newline at end of file
diff --git a/api/app/lib/routes/dataCacl/index.js b/api/app/lib/routes/dataCacl/index.js
new file mode 100644
index 0000000..f93ba74
--- /dev/null
+++ b/api/app/lib/routes/dataCacl/index.js
@@ -0,0 +1,28 @@
+
+'use strict';
+
+const dataCacl = require('../../controllers/dataCacl');
+
+module.exports = function (app, router, opts) {
+ app.fs.api.logAttr['GET/abnormal/methods'] = { content: '获取异常识别算法', visible: true }
+ router.get('/abnormal/methods', dataCacl.findAbnMethods)
+
+ app.fs.api.logAttr['GET/struct/abnormal/params'] = { content: '获取结构物下异常识别参数配置列表', visible: false }
+ router.get('/struct/abnormal/params', dataCacl.findAbnParamList)
+
+ app.fs.api.logAttr['POST/struct/abnormal/params'] = { content: '新增异常识别参数配置', visible: true }
+ router.post('/struct/abnormal/params', dataCacl.createAbnParam)
+
+ app.fs.api.logAttr['PUT/batch/switch/:ids'] = { content: '异常参数批量配置', visible: true }
+ router.put('/batch/switch/:ids', dataCacl.batchSwitch)
+
+ app.fs.api.logAttr['DELETE/delete/abnormal/params/:ids'] = { content: '删除异常识别参数配置', visible: true }
+ router.del('delete/abnormal/params/:ids', dataCacl.deleteAbnParam)
+
+ app.fs.api.logAttr['PUT/edit/abnormal/params/:ids'] = { content: '修改异常识别参数配置', visible: true }
+ router.put('/edit/abnormal/params/:ids', dataCacl.updateAbnParam)
+
+ app.fs.api.logAttr['POST/struct/:id/abnTask/result/:start/:end'] = { content: '获取异常数据识别任务结果', visible: true }
+ router.post('/struct/:id/abnTask/result/:start/:end', dataCacl.getAbnTaskResult)
+
+}
\ No newline at end of file
diff --git a/api/app/lib/routes/monitor/index.js b/api/app/lib/routes/monitor/index.js
new file mode 100644
index 0000000..84ae094
--- /dev/null
+++ b/api/app/lib/routes/monitor/index.js
@@ -0,0 +1,20 @@
+
+'use strict';
+
+const monitor = require('../../controllers/monitor');
+
+module.exports = function (app, router, opts) {
+
+ app.fs.api.logAttr['GET/project/allStructures'] = { content: '获取对应项目的结构物', visible: true };
+ router.get('/project/allStructures', monitor.getStructures);
+
+
+ app.fs.api.logAttr['GET/structure/factors'] = { content: '获取对应的监测因素', visible: true };
+ router.get('structure/factors', monitor.getFactors);
+
+ app.fs.api.logAttr['GET/structure/factors/sensors'] = { content: '获取对应的点位', visible: true };
+ router.get('structure/factors/sensors', monitor.getSensors);
+
+ app.fs.api.logAttr['GET/structure/factors/sensors/data'] = { content: '获取对应点位的设备', visible: true };
+ router.get('structure/factors/sensors/data', monitor.getMonitorData);
+}
\ No newline at end of file
diff --git a/api/config.js b/api/config.js
index 372508b..af91bcd 100644
--- a/api/config.js
+++ b/api/config.js
@@ -49,7 +49,7 @@ args.option('clickHousePepEmis', 'clickHouse 项企数据库名称');
args.option('clickHouseProjectManage', 'clickHouse 项目管理数据库名称');
args.option('clickHouseVcmp', 'clickHouse 视频平台数据库名称');
args.option('clickHouseDataAlarm', 'clickHouse 视频平台数据告警库名称');
-// args.option('clickHouseDataAlarmLocal', 'clickHouse 本地化告警相关数据');
+args.option('clickHouseDataAlarmLocal', 'clickHouse 本地化告警相关数据');
args.option('clickHouseIot', 'clickHouse IOT平台设备信息库名称');
args.option('clickHouseCamworkflow', 'clickHouse 工作流数据库名称');
@@ -125,7 +125,7 @@ const CLICKHOUST_VCMP = process.env.CLICKHOUST_VCMP || flags.clickHouseVcmp
const CLICKHOUST_DATA_ALARM = process.env.CLICKHOUST_DATA_ALARM || flags.clickHouseDataAlarm
const CLICKHOUST_IOT = process.env.CLICKHOUST_IOT || flags.clickHouseIot
const CLICKHOUST_CAM_WORKFLOW = process.env.CLICKHOUST_CAM_WORKFLOW || flags.clickHouseCamworkflow
-// const CLICKHOUST_DATA_ALARM_LOCAL=process.env.CLICKHOUST_DATA_ALARM_LOCAL|| flags.clickHouseDataAlarmLocal
+const CLICKHOUST_DATA_ALARM_LOCAL=process.env.CLICKHOUST_DATA_ALARM_LOCAL|| flags.clickHouseDataAlarmLocal
const CONFIRM_ALARM_ANXIN_USER_ID = process.env.CONFIRM_ALARM_ANXIN_USER_ID || flags.confirmAlarmAnxinUserId
@@ -171,7 +171,7 @@ const requireParams = {
CONFIRM_ALARM_ANXIN_USER_ID,
VCMP_APP_ID, VCMP_APP_SECRET,
API_CRAW_URL,
- // CLICKHOUST_DATA_ALARM_LOCAL,
+ CLICKHOUST_DATA_ALARM_LOCAL,
TYPES
}
@@ -341,10 +341,10 @@ const product = {
name: 'camWorkflow',
db: CLICKHOUST_CAM_WORKFLOW
},
- // {
- // name: 'alarmLocal',
- // db: CLICKHOUST_DATA_ALARM_LOCAL
- // }
+ {
+ name: 'alarmLocal',
+ db: CLICKHOUST_DATA_ALARM_LOCAL
+ }
]
},
es: {
diff --git a/script/3.7/schema/1.create_abn_type.sql b/script/3.7/schema/1.create_abn_type.sql
new file mode 100644
index 0000000..6e274a6
--- /dev/null
+++ b/script/3.7/schema/1.create_abn_type.sql
@@ -0,0 +1,12 @@
+create table abn_type
+(
+ id serial
+ primary key,
+ name varchar(50) not null,
+ description varchar(50) not null
+);
+INSERT INTO abn_type (id, name, description) VALUES (DEFAULT, 'interrupt'::varchar(50), '数据中断'::varchar(50));
+INSERT INTO abn_type (id, name, description) VALUES (DEFAULT, 'burr'::varchar(50), '毛刺'::varchar(50));
+INSERT INTO abn_type (id, name, description) VALUES (DEFAULT, 'trend'::varchar(50), '趋势'::varchar(50))
+
+
diff --git a/script/3.7/schema/2.create_abn_report_params.sql b/script/3.7/schema/2.create_abn_report_params.sql
new file mode 100644
index 0000000..f07268d
--- /dev/null
+++ b/script/3.7/schema/2.create_abn_report_params.sql
@@ -0,0 +1,15 @@
+create table abn_report_params
+(
+ id serial
+ primary key,
+ sensor_id varchar(300) not null,
+ sensor_location_description varchar(300) not null,
+ factor varchar(30) not null,
+ factor_id varchar(30) not null,
+ abn_type integer
+ constraint t_abn_report_params_t_abn_type_id_fk
+ references abn_type,
+ params jsonb not null,
+ enabled boolean default true not null,
+ item_index integer not null
+);
diff --git a/web/client/src/sections/data/actions/index.js b/web/client/src/sections/data/actions/index.js
index 66a9a99..8b92c7b 100644
--- a/web/client/src/sections/data/actions/index.js
+++ b/web/client/src/sections/data/actions/index.js
@@ -1,7 +1,7 @@
'use strict';
import * as dataQuery from './dataQuery'
-
+import * as monitor from './monitor'
export default {
- ...dataQuery
+ ...dataQuery,...monitor
}
\ No newline at end of file
diff --git a/web/client/src/sections/data/actions/monitor.js b/web/client/src/sections/data/actions/monitor.js
new file mode 100644
index 0000000..4035ec2
--- /dev/null
+++ b/web/client/src/sections/data/actions/monitor.js
@@ -0,0 +1,54 @@
+/*
+ * @Author: zhaobing
+ * @Date: 2023-11-02 11:31:56
+ */
+import { ApiTable, basicAction } from '$utils'
+
+export function getProjectAllStructures (query) {//获取对应项目下所有结构物
+ return (dispatch) => basicAction({
+ type: "get",
+ dispatch: dispatch,
+ actionType: "GET_PROJECT_ALL_STRUCTURES",
+ query: query,
+ url: `${ApiTable.getProjectAllStructures}`,
+ msg: { option: "获取对应项目下的所有结构物" },
+ reducer: { name: "ProjectAllStructures", params: { noClear: true } },
+ });
+ }
+
+
+ export function getFactors (query) {//获取对应结构物下的检测因素
+ return (dispatch) => basicAction({
+ type: "get",
+ dispatch: dispatch,
+ actionType: "GET_FACTORS",
+ query: query,
+ url: `${ApiTable.getFactors}`,
+ msg: { option: "获取对应结构物下的检测因素" },
+ reducer: { name: "Factors", params: { noClear: true } },
+ });
+ }
+
+ export function getSensors (query) {//获取对应检测因素下的设备
+ return (dispatch) => basicAction({
+ type: "get",
+ dispatch: dispatch,
+ actionType: "GET_SENSORS",
+ query: query,
+ url: `${ApiTable.getSensors}`,
+ msg: { option: "获取对应检测因素下的设备" },
+ reducer: { name: "Sensors", params: { noClear: true } },
+ });
+ }
+
+ export function getMonitorData (query) {//获取对应检测因素下的设备的数据(最终的数据)
+ return (dispatch) => basicAction({
+ type: "get",
+ dispatch: dispatch,
+ actionType: "GET_MONITOR_DATA",
+ query: query,
+ url: `${ApiTable.getMonitorData}`,
+ msg: { option: "获取对应检测因素下的设备的数据(最终的数据)" },
+ reducer: { name: "SensorsData", params: { noClear: true } },
+ });
+}
\ No newline at end of file
diff --git a/web/client/src/sections/data/components/lineTimeChartTemplate.jsx b/web/client/src/sections/data/components/lineTimeChartTemplate.jsx
new file mode 100644
index 0000000..e5c52ae
--- /dev/null
+++ b/web/client/src/sections/data/components/lineTimeChartTemplate.jsx
@@ -0,0 +1,359 @@
+
+'use strict';
+import React, { Component } from 'react';
+// 引入 echarts 主模块。
+import * as echarts from 'echarts/lib/echarts';
+// 引入折线图
+import 'echarts/lib/chart/line';
+//引入标签组件
+import ReactEcharts from 'echarts-for-react';
+
+let chartDownload = "";
+class LineTimeChart extends Component {
+ constructor(props) {
+ super(props);
+ this.sliderId = 'sliderId' + Math.floor(Math.random() * 1000000000);
+ this.chartId = 'chartId' + Math.floor(Math.random() * 1000000000);
+ this.legendId = 'legendId' + Math.floor(Math.random() * 1000000000);
+ this.legendListId = 'legendListId' + Math.floor(Math.random() * 1000000000);
+ this.tooltipId = 'tooltipId' + Math.floor(Math.random() * 1000000000);
+ this.state = {
+ option: {},
+ }
+ }
+
+ componentDidMount() {
+ let that = this;
+ this.renderChart();
+ this.reSetLegendStyle(that)
+ window.addEventListener('resize', function () {
+ let a = setInterval(function () { that.reSetLegendStyle(that) }, 100);
+ setTimeout(function () { clearInterval(a) }, 3500)
+ });
+ }
+
+ reSetLegendStyle = (that) => {
+ // Ps.initialize(document.getElementById(that.legendId));
+ // Ps.initialize(document.getElementById(that.tooltipId));
+ // let legendListDom = document.getElementById(that.legendListId)
+ // legendListDom.style.textAlign = 'left';
+ // legendListDom.style.maxWidth = '115px'
+ // document.getElementById(that.legendId).style.left = '10px'
+ }
+
+ download = () => {
+ this.chartDownload.downloadImage();
+ }
+
+ renderChart = () => {
+ const { options } = this.props;
+ const props = this.props;
+ let height = props.height || 300;
+ let showSlider = options && options.sliderStart;
+ let padding = [20, 135, 30, 140]
+ let startTime, endTime, ds, dv, view;
+
+ // let chart = new G2.Chart({
+ // container: this.chartId,
+ // forceFit: true,
+ // height: height,
+ // padding: padding,
+ // });
+ // chart.source(props.data);
+ // if (showSlider) {
+ // startTime = options.sliderStart;
+ // endTime = options.sliderEnd;
+ // ds = new DataSet({
+ // state: {
+ // start: startTime,
+ // end: endTime,
+ // }
+ // });
+ // dv = ds.createView();
+ // dv.source(props.data)
+ // .transform({ // !!! 根据状态量设置数据过滤规则,
+ // type: 'filter',
+ // callback: obj => {
+ // return obj.time <= ds.state.end && obj.time >= ds.state.start;
+ // }
+ // });
+ // }
+ // chart.scale({
+ // 'time': {
+ // range: [0, 1],
+ // type: 'time',
+ // alias: '时间',
+ // mask: 'YYYY-MM-DD HH:mm:ss',
+ // },
+ // });
+ // chart.on('tooltip:change', function (ev) {
+ // let item = ev.items[0];
+ // item.title = item.title;
+ // });
+ // chart.axis('value', {
+ // position: 'right'
+ // });
+ // chart.tooltip(true, {
+ // enterable: true,
+ // offset: 3,
+ // crosshairs: {
+ // type: 'cross',
+ // },
+ // inPlot: false,
+ // containerTpl: `
+ //
`,
+ // });
+ // chart.legend(true, {
+ // marker: 'circle',
+ // useHtml: true,
+ // position: 'left',
+ // containerTpl: ``,
+ // })
+ // let guideOption = {
+ // position: ['min', 'max'],
+ // content: options.yAlias,
+ // offsetY: -10
+ // }
+ // if (showSlider) {
+ // view = chart.view();
+ // view.source(dv); // !!! 注意数据源是 ds 创建 DataView 对象
+ // view.guide().text(guideOption);
+ // view.line().position('time*value').color('name').shape('line').size(2);
+ // } else {
+ // chart.guide().text(guideOption);
+ // chart.line().position('time*value').color('name').shape('line').size(2);
+ // }
+ // chart.render();
+ // if (showSlider) {
+ // // 创建滑动条
+ // let slider = new Slider({
+ // container: this.sliderId,
+ // padding: [35, 135, 30, 140],
+ // xAxis: 'time',
+ // yAxis: 'value',
+ // start: startTime,
+ // end: endTime,
+ // data: props.data,
+ // scales: {
+ // 'time': {
+ // type: 'time',
+ // mask: "YYYY-MM-DD HH:mm:ss"
+ // }
+ // },
+ // onChange: ({ startValue, endValue, startText, endText }) => {
+ // ds.setState('start', startText);
+ // ds.setState('end', endText);
+ // } // 更新数据状态量的回调函数
+ // });
+ // slider.render();
+ // }
+ // chart.changeData(props.data);
+
+ //组织dataset数据
+ // let data = this.props.data;
+ // let dataset = [], name = [], x = ['product'];
+ // data.map(d => {
+ // x.push(d.time);
+ // let value = [];
+ // if (!name.includes(d.name)) {
+ // name.push(d.name);
+ // value.push(d.name);
+ // value.push(d.value);
+ // dataset.push(value);
+ // } else {
+ // dataset.map(set => {
+ // if (set[0] == d.name) {
+ // set.push(d.value);
+ // }
+ // })
+ // }
+
+ // });
+ // dataset.unshift(x);
+ //组织后端数据组织data
+ let data = this.props.data;
+ let dataArray = [], types = {};
+ let xAxisValue = props.xAxis || 'time';
+ let yAxisValue = props.yAxis || 'value';
+ data.map(dd => {
+ if (!types[dd.name]) {
+ types[dd.name] = Object.assign({
+ name: dd.name, data: []
+ }, {
+ type: 'line',
+ //smooth: true
+ });
+ }
+ types[dd.name].data.push({
+ name: dd.time,
+ value: [dd[xAxisValue], dd[yAxisValue]]
+ })
+
+ });
+ for (let t in types) {
+ dataArray.push(types[t]);
+ }
+ // //根据dataset数据数量给出足够的线空间
+ // let line, lines = [];
+ // line = { type: 'line', smooth: true, seriesLayoutBy: 'row' }
+ // dataset.map(data => {
+ // lines.push(line);
+ // });
+
+ let eOption = {}
+ //解决只有一条数据时图例和线颜色不同
+ // if (dataset.length == 2) {
+ // eOption.color = ['#000000']
+ // }
+ //鼠标移入提示
+ eOption.tooltip = {
+ // axisPointer:{
+ // axis:{}
+ // }
+ trigger: 'axis',
+ //格式化 tooltip
+ formatter: function (params, ticket, callback) {
+ let htmlStr = "";
+ for (let i = 0; i < params.length; i++) {
+ let param = params[i];
+ let xName = param.name;//x轴的名称
+ let seriesName = param.seriesName.split("series");//图例名称
+ let value = param.value;//y轴值
+ let color = param.color;//图例颜色
+ let data = param.value[1] ? param.value[1] : '无数据';
+ if (value[1] && seriesName.length == 1) {
+ htmlStr += '' + xName + ' ' + seriesName[0] + ' ' + data + '
';
+ }
+ }
+ return htmlStr;
+ }
+ }
+ //X轴
+ eOption.xAxis = {
+ //type: 'category',
+ type: 'time',
+ // min: new Date(xxxxx),
+ // //结束时间
+ // max: new Date(xxxxx),
+ // axisLabel: {
+ // interale: 0,
+ // rotate: -40,
+ // formatter: function (value) {//在这里写你需要的时间格式
+ // var t_date = new Date(value);
+ // return [t_date.getFullYear(), t_date.getMonth() + 1, t_date.getDate()].join('-') + " "
+ // + [t_date.getHours(), t_date.getMinutes()].join(':');
+
+ // }
+ // }
+
+
+ }
+ //Y轴
+ eOption.yAxis = {
+ type: 'value',
+ min: 'dataMin',
+ max: 'dataMax',
+ name: options.yAlias,
+ axisLabel: {
+ formatter: function (value, index) {
+ if (value < 0.01) {
+ if(value<0.001){
+ return value?value.toFixed(4):0;
+ }
+ return value.toFixed(3);
+ }
+ return value.toFixed(2);
+ }
+ }
+
+ }
+ //toolbox 设置导出图片
+ eOption.toolbox = {
+ feature: {
+ saveAsImage: {}
+ }
+ }
+ //缩放
+ eOption.dataZoom = [
+ {
+ show: true,
+ realtime: true,
+ start: 0,
+ end: 100
+ },
+ {
+ type: 'inside',
+ realtime: true,
+ start: 0,
+ end: 100
+ }
+ ]
+ //颜色标识
+ eOption.legend = {
+ left: '0',
+ orient: 'vertical',
+ type: 'scroll',
+ icon: 'circle',
+ //文字过长可以使用下面方法截取字符串
+ formatter: function (name) {
+ if (!name) return '';
+ if (name.length > 8) {
+ if (name.length > 16) {
+ name = name.slice(0, 8) + `\n` + name.slice(0, 7) + "...";
+ } else {
+ name = name.slice(0, 8) + `\n` + name.slice(8);
+ }
+ return name;
+ } else {
+ return name
+ }
+ },
+ tooltip: {
+ show: true
+ }
+ }
+ // //数据源
+ // eOption.dataset = {
+ // source: dataset
+ // }
+ //props.data;
+ eOption.series = dataArray
+ this.setState({ option: eOption });
+
+ //this.chartDownload = chart;
+ }
+
+ render() {
+ const { data, width, height, options, slider } = this.props;
+ const showSlider = options && options.sliderStart;
+ return (
+
+ {/*
+
+
*/}
+
+ {/*
*/}
+
+ {/*
*/}
+ {/* {
+ showSlider ? [
+
+ ] : null
+ } */}
+
+
+ );
+ }
+}
+export default LineTimeChart;
\ No newline at end of file
diff --git a/web/client/src/sections/data/containers/dataComponent.jsx b/web/client/src/sections/data/containers/dataComponent.jsx
new file mode 100644
index 0000000..0e97599
--- /dev/null
+++ b/web/client/src/sections/data/containers/dataComponent.jsx
@@ -0,0 +1,122 @@
+'use strict'
+
+
+import React from 'react';
+import { connect } from 'react-redux';
+import moment from 'moment';
+import { Form, Button, Row, Col, DatePicker, Cascader, Select, Spin, Banner } from '@douyinfe/semi-ui';
+import LineTimeChart from '../components/lineTimeChartTemplate';
+
+
+const FormItem = Form.Item;
+const { RangePicker } = DatePicker;
+const Option = Select.Option;
+const createForm = Form.create;
+
+const DataComponent =(props)=>{
+ const {isRequesting,dataList}=props
+ const {items,sensors}=dataList
+
+ const renderChart = () => {
+ // const { dataAcquisitionPointList, dataAcquisitionList, isRequesting, dataType, aggregationData } =props;
+
+ if (isRequesting) {
+ return
+ }
+ if (JSON.stringify(dataList) == "{}") {
+ return
+ }
+ let chartContent = [];
+ let deviceItems ={pitstate: {name: "坑位状态", unit: null}}
+ let deviceSensors =[{id: "8d59eeef-147f-40e4-9516-768f8270b3fd", name: "女坑1",data:[{values: {pitstate: "1"}, time: "2023-11-06 03:03:58"},{values: {pitstate: "1"}, time: "2022-11-05 03:03:58"}]}];
+ for (let deviceItem in items) {
+ let chartDataD = [];
+ let startD = "";
+ let endD = "";
+ for (let d = 0; d < sensors.length; d++) {
+ let isHas = false;
+ for (let s = 0; s < sensors[d].data.length; s++) {
+ let cdataR = {};
+ let dataDS = sensors[d].data[s];
+ cdataR.time = moment(dataDS.time).format('YYYY-MM-DD HH:mm:ss');
+ if (startD == "") {
+ startD = dataDS.time;
+ endD = dataDS.time;
+ } else {
+ if (moment(startD) >= moment(dataDS.time))
+ startD = dataDS.time;
+ if (moment(endD) <= moment(dataDS.time))
+ endD = dataDS.time;
+ }
+ cdataR.name = sensors[d].name;
+ let deviceItemValue = Number(dataDS.values[deviceItem]);
+ if (!isNaN(deviceItemValue)) {
+ cdataR.value = deviceItemValue;
+ chartDataD.push(cdataR);
+ }
+ // if (deviceItemValue !== undefined) {
+ // cdataR.value = typeof deviceItemValue == 'number' ? Number(deviceItemValue) : null;
+ // chartDataD.push(cdataR);
+ // }
+ }
+ }
+ chartDataD.reverse();
+ if (chartDataD.length > 0) {
+ const optionsD = {
+ "xArray": [],
+ "yAlias": deviceItems[deviceItem].name + '(' + deviceItems[deviceItem].unit + ')',
+ "sliderStart": moment(startD).format('YYYY-MM-DD HH:mm:ss'),//默认数据源第一个,时间毫秒数
+ "sliderEnd": moment(endD).format('YYYY-MM-DD HH:mm:ss'),//默认数据源最后一个,时间毫秒数
+ "mask": "yyyy-mm-dd HH:MM:ss",//时间格式
+ }
+ chartContent.push(
+ )
+ }
+ }
+ if (chartContent.length == 0) {
+ return
+ } else {
+ return chartContent.map(p => {
+ return p;
+ })
+ }
+ }
+
+ //渲染图表
+ return (
+ {renderChart()}
+
)
+
+
+
+
+}
+
+const mapStateToProps = (state) => {
+ const { auth, global, dataContinuityType, dataContinuity,pepProjectId,SensorsData} = state;
+ return {
+ user: auth.user,
+ actions: global.actions,
+ dataContinuityType: dataContinuityType.data || [],
+ dataContinuity: dataContinuity.data || [],
+ pepProjectId:global.pepProjectId,
+ isRequesting:SensorsData.isRequesting
+ };
+}
+export default connect(mapStateToProps)(DataComponent);
+
+
+
+
+
diff --git a/web/client/src/sections/data/containers/dataDetail.jsx b/web/client/src/sections/data/containers/dataDetail.jsx
new file mode 100644
index 0000000..62dc2e7
--- /dev/null
+++ b/web/client/src/sections/data/containers/dataDetail.jsx
@@ -0,0 +1,284 @@
+import React, { useEffect, useState, useRef } from 'react';
+import { connect } from 'react-redux';
+import moment from 'moment';
+import { exportWord } from 'mhtml-to-word'
+import { saveAs } from 'file-saver';
+import { SkeletonScreen, } from "$components";
+import DataTableComponent from './dataTableComponent';
+import DataComponent from './dataComponent.jsx';
+import { IconSearch,IconLineChartStroked,IconStar,IconUser,IconBookOpenStroked} from '@douyinfe/semi-icons';
+import { Divider,Form, Button, Skeleton, Table, Pagination, Space, Row, Col, DatePicker, Cascader, Select, Spin, Alert, message, Menu, Icon,Nav,Tabs, TabPane} from '@douyinfe/semi-ui';
+import '../style.less'
+
+const DataDetail = (props) => {
+ const { dispatch, actions, user, dataContinuityType, dataContinuity,pepProjectId } = props
+ const { data } = actions
+ const [queryPage, setQueryPage] = useState({ limit: 10, page: 0 }); //页码信息
+ const [limits, setLimits] = useState(0)//每页实际条数
+ const [loading, setLoading] = useState(true);
+ const [params, setParams] = useState({})
+ const [checkVis, setCheckVis] = useState(false)
+ const [checkData, setCheckData] = useState({})
+ const [structList,setStructList]=useState([])//结构物筛选列表
+ const [structId,setStructId]=useState(null)//结构物id(根据结构物下拉框赋值)
+ const [factosList,setFactorsList]=useState([])//监测因素列表
+ const [factorId,setFactorId]=useState(null)
+ const [sensorList,setSensorList]=useState([])//设备列表
+ const [sensorId,setSensorId]=useState([])//设备数组id
+ const [project,setProject]=useState(null)//project(eg:{projcet:'nbjj'})
+ const form = useRef();
+ //初始化
+ useEffect(() => {
+ if(factorId&&project){
+ let query={
+ factorId,
+ sensorId:sensorId&&sensorId.length>0?sensorId.map(item=>`${project}-${item}`).join(','):[-11],
+ startTime:form.current.getValue('createTimes')[0],
+ endTime:form.current.getValue('createTimes')[1],
+ limit:queryPage.limit,
+ page:queryPage.page
+ }
+ queryData(query)
+ }
+
+ // dispatch(sectionData.getContinuityType())
+ // form.current.setValue('createTimes',[moment().subtract(24, 'hours').format('YYYY-MM-DD HH:mm:ss'), moment().format('YYYY-MM-DD HH:mm:ss')])
+
+ }, [factorId,project,sensorId])
+
+ //监听变化
+ useEffect(() => {
+ setStructList([])
+ form.current.reset()
+ getData()
+ }, [pepProjectId])
+ //监听结构物变化,查询监测因素
+ useEffect(()=>{
+ setLoading(true);
+ let queryParams = {structId}
+ if(structId){
+ dispatch(data.getFactors({...queryParams})).then(res => {
+ if(res.success){
+ const list=res.payload.data?.map(item=>{
+ return {
+ value: item.SafetyFactorTypeId,
+ label: item.SafetyFactorTypeName,
+ }
+ })
+ form.current.setValue('factor',list[0]?.value)
+ setFactorsList(list)
+ setFactorId(list[0]?.value)
+ setLoading(false);
+ }
+ })
+ }
+
+ },[structId])
+ useEffect(()=>{
+ let queryParams = {structId,SafetyFactorTypeId:factorId}
+ if(factorId){
+ dispatch(data.getSensors({...queryParams})).then(res => {
+ if(res.success){
+ const list=res.payload.data?.map(item=>{
+ return {
+ value: item.SensorId,
+ label: item.SensorLocationDescription,
+ }
+ })
+ form.current.setValue('point',[list[0]?.value])
+ setSensorList(list)
+ setLoading(false)
+ setProject(res.payload.data[0].Project)
+ setSensorId([list[0]?.value])
+ }
+ })
+ }
+ },[factorId])
+ const getData = (queryParams = {pomsProjectId:pepProjectId}) => {
+ setLoading(true);
+ dispatch(data.getProjectAllStructures({...queryParams})).then(res =>
+ {
+ if(res.success){
+ const uniqueIds = new Set();
+ const list=res.payload.data?.filter(item => {
+ const duplicate = uniqueIds.has(item.strucId);
+ uniqueIds.add(item.strucId);
+ return !duplicate
+ })?.map(item => ({
+ value: item.strucId,
+ label: item.strucName,
+ project: item.Project,
+ }))
+ form.current.setValue('struct',list[0]?.value)
+ setStructId(list[0]?.value)
+ setStructList(list)
+ setLoading(false)
+ }
+ }
+
+ )
+ }
+
+ const downloadWord = (html = '', name = '数据监测 ' + moment().format('YYYY-MM-DD HH:mm:ss')) => {
+ exportWord({
+ mhtml: html,
+ filename: name,
+ })
+ // let blob = new Blob([html], { type: "application/msword;charset=utf-8" });
+ // saveAs(blob, name + '.doc');
+ }
+ //结构物筛选变化回调函数
+ const structChange = (value) => {
+ setStructId(value)
+ form.current.setValue('point','')
+ form.current.setValue('factor','')
+
+ // setStructName(structList.find(item=>item.value===value)?.label)
+ }
+ //结构物筛选发生变化回调函数
+ const factorChange=(value)=>{
+ form.current.setValue('point','')
+ setFactorId(value)
+ }
+ //点位筛选发生改变的回调函数
+ const pointChange=(value)=>{
+ setSensorId(value)
+ // setProject(structList.find(item=>item.value===structId)?.project)
+ }
+ //查询按钮回调函数
+ const queryData=(query)=>{
+ dispatch(data.getMonitorData(query)).then(res=>{
+ if(res.success){
+ setCheckData(res.payload.data)
+ }
+ })
+ }
+ const searchHandler=(values) => {
+ form.current.validate().then(rs=>{
+ let query={
+ factorId,
+ sensorId:sensorId&&sensorId.length>0?sensorId.map(item=>`${project}-${item}`).join(','):[-11],
+ startTime:moment(rs.createTimes[0]).format('YYYY-MM-DD HH:mm:ss'),
+ endTime:moment(rs.createTimes[1]).format('YYYY-MM-DD HH:mm:ss'),
+ limit:queryPage.limit,
+ page:queryPage.page
+ }
+ queryData(query)
+ })
+ }
+
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+ 趋势图} itemKey="1">
+
+
+
+ 数据} itemKey="2">
+
+
+
+
+
+
+
+
+ >
+ )
+}
+
+function mapStateToProps(state) {
+ const { auth, global, dataContinuityType, dataContinuity,pepProjectId} = state;
+ return {
+ user: auth.user,
+ actions: global.actions,
+ dataContinuityType: dataContinuityType.data || [],
+ dataContinuity: dataContinuity.data || [],
+ pepProjectId:global.pepProjectId
+ };
+}
+
+export default connect(mapStateToProps)(DataDetail);
diff --git a/web/client/src/sections/data/containers/dataQuery.jsx b/web/client/src/sections/data/containers/dataQuery.jsx
index 8dc134d..036a7f0 100644
--- a/web/client/src/sections/data/containers/dataQuery.jsx
+++ b/web/client/src/sections/data/containers/dataQuery.jsx
@@ -33,7 +33,7 @@ const DataQuery = (props) => {
])
}
- const downloadWord = (html = '', name = '数据查询 ' + moment().format('YYYY-MM-DD HH:mm:ss')) => {
+ const downloadWord = (html = '', name = '数据监测 ' + moment().format('YYYY-MM-DD HH:mm:ss')) => {
exportWord({
mhtml: html,
filename: name,
@@ -48,7 +48,7 @@ const DataQuery = (props) => {
@@ -73,9 +73,9 @@ const DataQuery = (props) => {
/>
diff --git a/web/client/src/sections/data/containers/dataTableComponent.jsx b/web/client/src/sections/data/containers/dataTableComponent.jsx
new file mode 100644
index 0000000..25cd0df
--- /dev/null
+++ b/web/client/src/sections/data/containers/dataTableComponent.jsx
@@ -0,0 +1,137 @@
+/*
+ * @Author: zhaobing
+ * @Date: 2023-11-02 09:32:25
+ */
+import React, { useState, useEffect } from 'react';
+import { connect } from 'react-redux';
+import { Table, Button, Alert, Spin } from '@douyinfe/semi-ui';
+// import { ApiTable } from '../../../../utils/webapi';
+import FileSaver from 'file-saver';
+import moment from 'moment';
+
+const DataTableComponent = (props) => {
+ const {dataList}=props
+ const [currentPage, setCurrentPage] = useState(1);
+ const [exportingFlag, setExportingFlag] = useState(false);
+ const [isRequesting, setIsRequesting] = useState(false);
+ const [arr,setArr]=useState(dataList.items)
+ const [data,setData]=useState([])
+ const [columns,setColumns]=useState([])
+ //初始化
+
+ useEffect(()=>{
+
+ // let arr=[{readingNumber:{name: '电表示数', unit: 'kWh'}}]
+ // let dataSource = [{values: {total: 24138.97}, time: "2023-11-03 14:18:27.000"},{values: {total: 24138.97}, time: "2023-11-03 14:18:27.000"}]
+ let columns = []
+ columns.push({
+ title: '设备位置',
+ dataIndex: 'position',
+ key: 'position',
+ });
+ for (let index in arr) {
+ columns.push({
+ title: arr[index].name + '(' + arr[index].unit + ')',
+ dataIndex: index,
+ sorter: (a, b) => b[index] - a[index],
+ key: index,
+ })
+ };
+ columns.push({
+ title: '采集时间',
+ dataIndex: 'acqTime',
+ sorter: (a, b) => b['realTime'] - a['realTime'],
+ key: 'acqTime',
+ });
+ //设备
+ const data1=[]
+ for (let i = 0; i < dataList.length; i++) {
+ for (let k = 0; k < dataList[i].data.length; k++) {
+ let cdataT={}
+ let startT = "";
+ let endT = "";
+ let dataTS = dataList[i].data[k];
+ cdataT.key = `station-${dataList[i].id}-${k}`;
+ cdataT.position = dataList[i].name;
+ cdataT.acqTime = moment(dataTS.time).format('YYYY-MM-DD HH:mm:ss');
+ cdataT.realTime = moment(dataTS.time).valueOf();
+ if (startT == "") {
+ startT = dataTS.time;
+ endT = dataTS.time;
+ } else {
+ if (moment(startT) >= moment(dataTS.time))
+ startT = dataTS.time;
+ if (moment(endT) <= moment(dataTS.time))
+ endT = dataTS.time;
+ }
+ //动态列的值
+ for (let themeItem in arr) {
+ cdataT[themeItem] = dataTS.values[themeItem];
+ }
+ data1.push(cdataT)
+ }
+ }
+ setData(data1)
+ setColumns(columns)
+ },[])
+
+
+
+
+
+
+
+ const exporting = () => {
+ setIsRequesting(true);
+ };
+
+ const pageChange = (page, pageSize) => {
+ setCurrentPage(page);
+ };
+
+ const exportCsv = (data, title, filename) => {
+ setExportingFlag(true);
+ };
+
+ const exportVibrationCsv = (data, title, filename) => {
+ setExportingFlag(true);
+ };
+
+ const renderViberationChart = () => {
+ };
+
+ useEffect(() => {
+ setCurrentPage(1);
+ }, [props]);
+
+
+
+
+ return (
+
+
+
+ {/*
+ {
+ data.length ? : null
+ }
+
*/}
+
+
+
+
+ );
+};
+
+const mapStateToProps = (state) => {
+ const { auth, global, dataContinuityType, dataContinuity,pepProjectId} = state;
+ return {
+ user: auth.user,
+ actions: global.actions,
+ dataContinuityType: dataContinuityType.data || [],
+ dataContinuity: dataContinuity.data || [],
+ pepProjectId:global.pepProjectId
+ };
+};
+
+export default connect(mapStateToProps)(DataTableComponent);
\ No newline at end of file
diff --git a/web/client/src/sections/data/containers/index.js b/web/client/src/sections/data/containers/index.js
index 4c93e75..51d84bf 100644
--- a/web/client/src/sections/data/containers/index.js
+++ b/web/client/src/sections/data/containers/index.js
@@ -3,4 +3,5 @@ import DataQuery from './dataQuery';
import DataComparison from './dataComparison';
import DataAssociation from './dataAssociation';
import Notebook from './notebook';
-export { DataQuery, DataComparison, DataAssociation,Notebook};
\ No newline at end of file
+import DataDetail from './dataDetail';
+export { DataQuery, DataComparison, DataAssociation,Notebook,DataDetail};
\ No newline at end of file
diff --git a/web/client/src/sections/data/nav-item.jsx b/web/client/src/sections/data/nav-item.jsx
index 0a5118c..82e325d 100644
--- a/web/client/src/sections/data/nav-item.jsx
+++ b/web/client/src/sections/data/nav-item.jsx
@@ -1,3 +1,4 @@
+
import React from 'react';
import { IconCode } from '@douyinfe/semi-icons';
@@ -15,7 +16,9 @@ export function getNavItem (user, dispatch) {
icon: ,
to: '/data/dataMonitoring/dataQuery',
items: [{
- itemKey: 'dataQuery', to: '/data/dataMonitoring/dataQuery', text: '数据查询'
+ itemKey: 'dataQuery', to: '/data/dataMonitoring/dataQuery', text: '数据监测'
+ },{
+ itemKey: 'dataDetail', to: '/data/dataMonitoring/dataDetail', text: '数据详情'
}]
}, {
itemKey: 'dataAnalysis',
diff --git a/web/client/src/sections/data/routes.js b/web/client/src/sections/data/routes.js
index b497cdd..a93c65d 100644
--- a/web/client/src/sections/data/routes.js
+++ b/web/client/src/sections/data/routes.js
@@ -1,4 +1,5 @@
-import { DataQuery, DataComparison, DataAssociation, Notebook } from './containers';
+
+import { DataQuery, DataComparison, DataAssociation, Notebook,DataDetail,DataTableComponent } from './containers';
export default [{
type: 'inner',
@@ -15,7 +16,13 @@ export default [{
path: '/dataQuery',
key: 'dataQuery',
component: DataQuery,
- breadcrumb: '数据查询',
+ breadcrumb: '数据监测',
+ },{
+ path: '/dataDetail',
+ key: 'dataDetail',
+ component: DataDetail,
+ breadcrumb: '数据详情',
+
}]
}, {
path: '/dataAnalysis',
diff --git a/web/client/src/sections/data/style.less b/web/client/src/sections/data/style.less
index e69de29..5604f9f 100644
--- a/web/client/src/sections/data/style.less
+++ b/web/client/src/sections/data/style.less
@@ -0,0 +1,10 @@
+.semi-form-horizontal{
+ display: block;
+}
+.fontStyle{
+ font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, sans-serif;
+}
+.semi-col-12 {
+ display: flex;
+
+}
\ No newline at end of file
diff --git a/web/client/src/sections/install/actions/dataCacl.js b/web/client/src/sections/install/actions/dataCacl.js
new file mode 100644
index 0000000..f4aba77
--- /dev/null
+++ b/web/client/src/sections/install/actions/dataCacl.js
@@ -0,0 +1,117 @@
+
+'use strict';
+
+import { ApiTable, basicAction } from '$utils'
+
+export function getAbnMethods (query) {//获取异常识别算法
+ return (dispatch) => basicAction({
+ type: "get",
+ dispatch: dispatch,
+ actionType: "GET_ABN_METHODS",
+ query: query,
+ url: `${ApiTable.getAbnMethods}`,
+ msg: { option: "获取异常识别算法" },
+ reducer: { name: "abnMethods", params: { noClear: true } },
+ });
+}
+
+
+export function getAbnParamList (query) {//获取异常参数配置
+ return (dispatch) => basicAction({
+ type: "get",
+ dispatch: dispatch,
+ actionType: "GET_ABN_PARAM_LIST",
+ query: query,
+ url: `${ApiTable.getAbnParamList}`,
+ msg: { option: "获取异常参数配置" },
+ reducer: { name: "abnParam", params: { noClear: true } },
+ });
+}
+
+export function addAbnParam (data) {//新增异常参数配置
+ return (dispatch) => basicAction({
+ type: "post",
+ dispatch: dispatch,
+ actionType: "ADD_ABN_PARAM",
+ data,
+ url: `${ApiTable.addAbnParamList}`,
+ msg: { option: "新增异常参数配置" },
+
+ })
+}
+
+export function batchCfgAbnParams (ids, enable) {//批量启用or禁用
+ return (dispatch) => basicAction({
+ type: "put",
+ dispatch: dispatch,
+ actionType: "BATCH_CFG_ABNPARAMS",
+ data:enable,
+ url: `${ApiTable.batchSwitch.replace("{ids}", ids)}`,
+ msg: { option: enable?.enabled?"批量启用异常参数配置" : "批量禁用异常参数配置" },
+
+ })
+}
+
+export function deleteAbnParams (id) {//删除配置
+ return (dispatch) => basicAction({
+ type: "delete",
+ dispatch: dispatch,
+ actionType: "DELETE_ABN_PARAMS",
+ url: `${ApiTable.deleteAbnParams.replace("{id}", id)}`,
+ msg: { option: '删除异常参数配置' },
+
+ });
+}
+
+export function editAbnParams (id, pushData) {//编辑配置
+ return (dispatch) => basicAction({
+ type: "put",
+ data:pushData,
+ dispatch: dispatch,
+ actionType: "EDIT_ABN_PARAMS",
+ url: `${ApiTable.editAbnParams.replace("{id}", id)}`,
+ msg: { option: '修改异常参数配置' },
+
+ })
+}
+
+//中断数据对比
+export function getItemAbnResult_int (structId, start, end, pushData) {
+ return (dispatch) => basicAction({
+ type: "post",
+ data:pushData,
+ dispatch: dispatch,
+ actionType: "GET_ITEM_ABN_RESULT_INT",
+ url: `${ApiTable.getAbnTaskResult.replace("{structId}", structId).replace("{start}", start).replace("{end}", end)}`,
+ msg: { option: '中断数据对比' },
+ reducer: { name: "abnItemState_int", params: { noClear: true } },
+ })
+}
+
+
+//异常趋势数据对比
+export function getItemAbnResult_tr (structId, start, end, pushData) {//
+ return (dispatch) => basicAction({
+ type: "post",
+ data:pushData,
+ dispatch: dispatch,
+ actionType: "GET_ITEM_ABN_RESULT_TR",
+ url: `${ApiTable.getAbnTaskResult.replace("{structId}", structId).replace("{start}", start).replace("{end}", end)}`,
+ msg: { option: '异常趋势数据对比' },
+ reducer: { name: "abnItemState_tr", params: { noClear: true } },
+ })
+}
+
+
+//毛刺数据对比
+export function getItemAbnResult_burr (structId, start, end, pushData) {//
+ return (dispatch) => basicAction({
+ type: "post",
+ data:pushData,
+ dispatch: dispatch,
+ actionType: "GET_ITEM_ABN_RESULT_BURR",
+ url: `${ApiTable.getAbnTaskResult.replace("{structId}", structId).replace("{start}", start).replace("{end}", end)}`,
+ msg: { option: '毛刺数据对比' },
+ reducer: { name: "abnItemState_burr", params: { noClear: true } },
+ })
+}
diff --git a/web/client/src/sections/install/actions/index.js b/web/client/src/sections/install/actions/index.js
index 93e6a8a..4cacf08 100644
--- a/web/client/src/sections/install/actions/index.js
+++ b/web/client/src/sections/install/actions/index.js
@@ -2,7 +2,9 @@
import * as roles from './roles'
import * as system from './system'
+import * as dataCacl from './dataCacl'
export default {
...roles,
- ...system
+ ...system,
+ ...dataCacl
}
\ No newline at end of file
diff --git a/web/client/src/sections/install/components/TimeAbnValueLineChart.jsx b/web/client/src/sections/install/components/TimeAbnValueLineChart.jsx
new file mode 100644
index 0000000..b717c5d
--- /dev/null
+++ b/web/client/src/sections/install/components/TimeAbnValueLineChart.jsx
@@ -0,0 +1,168 @@
+import React, { Component } from 'react';
+import createG2 from 'g2-react';
+
+import G2, { Plugin } from 'g2';
+//import G2 from '@antv/g2'
+//import DataSet from '@antv/data-set';
+
+import 'g2-plugin-slider';
+//import Slider from '@antv/g2-plugin-slider';
+
+import './theme';
+import { Button } from '@douyinfe/semi-ui';
+import FileSaver from 'file-saver';
+/**
+ * props:
+ *
+ * data: [{
+ * name: 图例名称,
+ * value: y轴数值,
+ * time: x轴时间值
+ * }], // 数组或封装后的frame
+ * height: 图表高度,
+ * configs: {
+ * slider: {
+ * start: 初始开始点,
+ * end: 初始结束点
+ * }
+ * },
+ * yAxis: 修改默认y轴值对应的键
+ */
+
+let chartDownload = "";
+class TimeAbnValueLineChart extends Component {
+ constructor(props) {
+ super(props);
+
+ this.sliderId = 'range' + Math.floor(Math.random() * 1000000000);
+ const Chart = createG2((chart, configs) => {
+ chart.source(props.data, {
+ 'value': {
+ formatter: val => {
+ if (configs.unit) {
+ return val + configs.unit
+ } else {
+ return val
+ }
+ }
+ },
+ 'time': {
+ range: [0, 1],
+ type: 'time',
+ alias: '时间',
+ mask: 'yyyy-mm-dd HH:MM:ss'
+ },
+ 'days': {
+ type: 'linear',
+ alias: '',
+ formatter: val => {
+ return val + '天'
+ }
+ }
+ });
+ chart.axis(props.xAxis || 'time', {
+ title: null,
+ line: {
+ //stroke: '#C0C0C0',
+ }, label: {
+ textStyle: {
+ //fill: '#C0C0C0', // 文本的颜色
+ }
+ }
+ //mask: 'yyyy-mm-dd HH:MM:ss'
+ });
+ chart.axis(props.yAxis || 'value', {
+ title: null,
+ });
+ chart.legend({
+ title: null,
+ position: 'right', // 设置图例的显示位置
+ itemWrap: true,
+ //dx: -30,
+ height: 120,
+ });
+ chart.tooltip(true, {
+ map: {
+
+ },
+ });
+ chart.on('tooltipchange', function (ev) {
+ //let items = ev.items;
+
+ });
+ chart.line().position(`${props.xAxis || 'time'}*${props.yAxis || 'value'}`).color('name').shape('line').size(2);
+ // chart.point().position(`${props.xAxis || 'time'}*${props.yAxis || 'value'}`).color('name', (name) => name != this.props.itemName).size(4).shape('circle').style({
+ // stroke: '#fff',
+ // lineWidth: 1
+ // });
+ if (this.props.contentType != 'trend') {
+ chart.point().position(`${props.xAxis || 'time'}*${props.yAxis || 'value'}`).color('name').size(4).shape('circle').style({
+ stroke: '#fff',
+ lineWidth: 1
+ });
+ }
+
+ if (!configs || !configs.slider) {
+ chart.render();
+ } else {
+ // 创建滑动条
+ //Plugin.slider.ATTRS.textAttr = { fill: '#fff' };
+ var slider = new Plugin.slider({
+ domId: this.sliderId,
+ height: 30,
+ charts: chart,
+ xDim: props.xAxis || 'time',
+ yDim: props.yAxis || 'value',
+ start: configs.slider.start, // 滑块开始值,用户未定义则使用默认值
+ end: configs.slider.end // 滑块结束值,用户未定义则使用默认值
+ });
+ slider.render();
+ }
+ this.chartDownload = chart;
+ });
+ this.Chart = Chart;
+ }
+ download = () => {
+ //this.chartDownload.downloadImage();
+ const dataurl = this.chartDownload.toImage();
+ let arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
+ bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
+ while (n--) {
+ u8arr[n] = bstr.charCodeAt(n);
+ }
+ let blob = new Blob([u8arr], { type: mime });
+
+ FileSaver.saveAs(blob, "chart.png");
+ }
+
+ render() {
+ const { unit, data, width, height, configs, xAxis } = this.props;
+
+ let margin = [20, 350, 30, 60];
+ const showSlider = configs && configs.slider;
+ if (showSlider) {
+ //margin = [20, 145, 30, 138];
+ margin = [20, 335, 80, 138];
+ }
+ return (
+
+
+
+
+
+
+ {
+ showSlider ? [
+
,
+
+ ] : null
+ }
+
+
+ );
+ }
+}
+
+export default TimeAbnValueLineChart;
\ No newline at end of file
diff --git a/web/client/src/sections/install/components/TrendModal.jsx b/web/client/src/sections/install/components/TrendModal.jsx
new file mode 100644
index 0000000..6bda8f3
--- /dev/null
+++ b/web/client/src/sections/install/components/TrendModal.jsx
@@ -0,0 +1,288 @@
+
+import React, { useState, useEffect,useRef } from "react";
+import { connect } from "react-redux";
+import moment from 'moment';
+import { Form, Tooltip, Select, Checkbox, Modal, Input, Button, Switch, DatePicker, Row } from "@douyinfe/semi-ui";
+import {IconLineChartStroked} from "@douyinfe/semi-icons"
+import { TimeAbnValueLineChart } from '../components/TimeAbnValueLineChart';
+import PropTypes from 'prop-types';
+
+
+
+
+
+const TrendModal = (props) => {
+ const { modalData,dispatch,actions,sensorId, closeModal, visible,project } = props
+ const{install}=actions
+ const [currStartTime, setCurrStartTime] = useState(moment().add('days', -1).format('YYYY-MM-DD HH:mm:ss'))
+ const [currEndTime, setcurrEndTime] = useState(moment().format('YYYY-MM-DD HH:mm:ss'))
+ const form = useRef()
+ //初始化
+ useEffect(() => {
+ }, [])
+ const checkPoint = (rule, value) => {
+ const pattern = /^[1-9]*[1-9][0-9]*$/;
+ if (pattern.test(value) && value != 1) {
+ return true
+ }
+ return false
+ }
+ const checkNumber = (rule, val) => {
+ const strRegex = /^-?\d+(\.\d+)?$/
+ if (strRegex.test(val)) {
+ return true
+ }
+ return false
+
+
+
+ }
+ const btnFormSubmit = () => {
+ let data = modalData
+ form.current.validate().then(value=>{
+ let paramJson = {
+ "thr_burr": Number(value.burr),//毛刺阈值
+ "win_med": Number(value.ws),//滑动中值
+ "win_avg": Number(value.rc),//滑动均值
+ "thr_der": Number(value.dv), //导数阈值
+ "win_grad": Number(value.pn),//渐变点个数
+ "thr_grad": Number(value.gv),//渐变阈值
+ "days_Last": Number(value.timeRange),//分析时长
+ }
+ let pushData = {
+ station: data.sensorId,//测点
+ factorId: data.factorId,
+ itemId: data.itemIndex,
+ abnType: 3,//异常趋势
+ enabled: value.checked,
+ params: paramJson
+ };
+ dispatch(install.editAbnParams(data.id,pushData)).then(res => {
+ if (res.success) {
+ const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',')
+ dispatch(install.getAbnParamList({ sensorId:id}))
+ closeModal()
+ }
+ })
+ })
+ }
+ const checkInterger = (rule, val) => {
+ // if(val){
+ const strRegex = /^[1-9]*[1-9][0-9]*$/
+ if (strRegex.test(val)) {
+ return true
+ }
+ return false
+ // }
+
+ };
+ const timeOnChange = (dates, dateStrings) => {
+ if (dates.length == 0) {
+ setCurrStartTime(null)
+ setCurrEndTime(null)
+ return Notification.warning({
+ content: `请选择时间`,
+ duration: 3,
+ })
+ }
+ setCurrStartTime(dates[0].format('YYYY-MM-DD HH:mm:ss'))
+ setCurrEndTime(dates[1].format('YYYY-MM-DD HH:mm:ss'))
+ }
+ //数据对比
+ // const dataCompare = () => {
+ // this.props.form.validateFields((err, values) => {
+ // if (!err) {
+ // let paramJson = {
+ // "thr_burr": values.burr,//毛刺阈值
+ // "win_med": values.ws,//滑动中值
+ // "win_avg": values.rc,//滑动均值
+ // "thr_der": values.dv, //导数阈值
+ // "win_grad": values.pn,//渐变点个数
+ // "thr_grad": values.gv,//渐变阈值
+ // "days_Last": values.timeRange,//分析时长
+ // }
+ // let data = {
+ // station: this.props.modalData.stationId,
+ // factorId: this.props.modalData.factorId,
+ // itemId: this.props.modalData.itemId,
+ // abnType: 'trend',//算法类型
+ // enabled: true,
+ // params: paramJson
+ // };
+ // let start = this.state.currStartTime;
+ // let end = this.state.currEndTime;
+ // this.props.dispatch(getItemAbnResult_tr(this.props.structId, start, end, data)).then(res => {
+ // })
+ // }
+ // });
+ // }
+
+ // let originalData = ['还没查询'], calcArray = [], key, itemName, start, end;
+ // let stationsData = [];
+ // if (abnItemCompData) {
+ // originalData = abnItemCompData.stationData;
+ // calcArray = abnItemCompData.resultArray;
+ // key = abnItemCompData.itemKey;
+ // itemName = abnItemCompData.itemName;
+ // start = originalData && originalData.length > 0 ? originalData[0].time : '';
+ // end = originalData && originalData.length > 0 ? originalData[originalData.length - 1].time : '';
+
+ // for (let i = 0; i < originalData.length; i++) {
+ // let one = { name: itemName + "(单位:" + abnItemCompData.unit + ")", value: originalData[i][key], time: originalData[i].time };
+ // stationsData.push(one);
+ // }
+ // if (calcArray) {
+ // let preLineData = calcArray.calcPreprocess;
+ // let abnTrends = calcArray.calcFinal;
+ // for (let j = 0; j < preLineData.length; j++) {
+ // let one = { name: "预处理+滑动均值后数据", value: preLineData[j].value, time: preLineData[j].time };
+ // stationsData.push(one);
+ // }
+ // for (let t = 0; t < abnTrends.length; t++) {
+ // let name = abnTrends[t].startTime + "至" + abnTrends[t].endTime + abnTrends[t].des + ",渐变差值:" + abnTrends[t].value.toFixed(2) + abnItemCompData.unit;
+ // let start = { name: name, value: abnTrends[t].startValue, time: abnTrends[t].startTime };
+ // let end = { name: name, value: abnTrends[t].endValue, time: abnTrends[t].endTime };
+ // stationsData.push(start);
+ // stationsData.push(end);
+ // }
+ // }
+ // }
+ TrendModal.propTypes = {
+ visible: PropTypes.bool.isRequired,
+ closeModal: PropTypes.func.isRequired
+ };
+
+ return
+
+
+
+
]}>
+
+
+ ;
+}
+
+function mapStateToProps(state) {
+ const { abnItemState_tr, global} = state;
+ return {
+ abnItemCompData: abnItemState_tr.items,
+ actions:global.actions
+ }
+}
+
+export default connect(mapStateToProps)(TrendModal)
diff --git a/web/client/src/sections/install/components/burrModal.jsx b/web/client/src/sections/install/components/burrModal.jsx
new file mode 100644
index 0000000..bab3140
--- /dev/null
+++ b/web/client/src/sections/install/components/burrModal.jsx
@@ -0,0 +1,214 @@
+import React, { useRef,useState,useEffect } from "react";
+import { connect } from "react-redux";
+import moment from 'moment';
+import { Form, Tooltip, Select, Checkbox, Modal, Input, Button, Switch, DatePicker, Row, IconLineChartStroked } from "@douyinfe/semi-ui";
+import {IconInfoCircle} from "@douyinfe/semi-icons"
+import { TimeAbnValueLineChart } from '../components/TimeAbnValueLineChart';
+import PropTypes from 'prop-types';
+
+const BurrModal = (props) => {
+ const form = useRef()
+ const {dispatch, modalData, closeModal, visible, structId,item,actions,sensorId,project } = props
+ const [currStartTime, setCurrStartTime] = useState(moment().add('days', -1).format('YYYY-MM-DD HH:mm:ss'))
+ const [currEndTime, setcurrEndTime] = useState(moment().format('YYYY-MM-DD HH:mm:ss'))
+ const{install}=actions
+ useEffect(()=>{
+ // form.current.setValues({stationName: modalData.sensorName,
+ // factorName: modalData.factorName + "/" + modalData.item,
+ // burr: modalData.params.thr_burr,
+ // isEnable: modalData.enabled})
+ },[])
+ //表单类
+ const btnFormSubmit = () => {
+ let data = modalData;
+ form.current.validate().then(value=>{
+ let paramJson = { "thr_burr": Number(value.burr) };
+ let pushData = {
+ station: data.sensorId,//测点
+ factorId: data.factorId,
+ itemId: data.itemIndex,
+ abnType: 2,//毛刺
+ enabled: value.checked,
+ params: paramJson
+ }
+ dispatch(install.editAbnParams(data.id,pushData)).then(res => {
+ if (res.success) {
+ const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',')
+ dispatch(install.getAbnParamList({ sensorId:id}))
+ closeModal()
+ }
+ })
+ // this.props.dispatch(editAbnParams(data.id, pushData)).then(_ => {
+ // this.props.dispatch(getAbnParamList(this.props.structId)).then(() => {
+ // this.props.closeModal();
+ // });
+ // });
+
+ })
+ }
+const checkInterger = (rule, value, callback) => {
+ if (!value) {
+ callback();
+ } else {
+ const pattern = /^[1-9]*[1-9][0-9]*$/;
+ if (pattern.test(value)) {
+ callback();
+ } else {
+ callback(new Error('请输入正整数'));
+ }
+ }
+ };
+const timeOnChange = (dates, dateStrings) => {
+ if (dates.length == 0) {
+ setCurrStartTime(null)
+ setcurrEndTime(null)
+ return message.warning('请选择时间');
+ }
+ setCurrStartTime(dates[0].format('YYYY-MM-DD HH:mm:ss'))
+ setcurrEndTime(dates[1].format('YYYY-MM-DD HH:mm:ss'))
+ }
+ //数据对比
+// const dataCompare = () => {
+// form.current.validate().then(res=>{
+// if (res.success) {
+// let paramJson = { "thr_burr": values.burr }
+// let data = {
+// station: modalData.stationId,
+// factorId: modalData.factorId,
+// itemId: modalData.itemId,
+// abnType: 'burr',//算法类型
+// enabled: true,
+// params: paramJson
+// };
+// let start = this.state.currStartTime
+// let end = this.state.currEndTime
+
+// // this.props.dispatch(getItemAbnResult_burr(this.props.structId, start, end, data)).then(res => {
+// // })
+// }
+// })
+// }
+const checkNumber = (rule, val) => {
+ const strRegex = /^-?\d+(\.\d+)?$/
+ if (strRegex.test(val)) {
+ return true
+ }
+ return false
+
+
+
+}
+ // let originalData = ['还没查询'], calcArray = [], key, itemName, start, end;
+ // let stationsData = [];
+ // if (abnItemCompData) {
+ // originalData = abnItemCompData.stationData;
+ // calcArray = abnItemCompData.resultArray;
+ // key = abnItemCompData.itemKey;
+ // itemName = abnItemCompData.itemName;
+ // start = originalData && originalData.length > 0 ? originalData[0].time : '';
+ // end = originalData && originalData.length > 0 ? originalData[originalData.length - 1].time : '';
+ // for (let i = 0; i < originalData.length; i++) {
+ // let one = { name: itemName + "(单位:" + abnItemCompData.unit + ")", value: originalData[i][key], time: originalData[i].time };
+ // stationsData.push(one);
+ // }
+ // for (let j = 0; j < calcArray.length; j++) {
+ // let one = {
+ // name: "毛刺点:" + calcArray[j].time + "(突变大小:" + calcArray[j].burr.toFixed(2) + abnItemCompData.unit + ")"
+ // , value: calcArray[j].value, time: calcArray[j].time
+ // };
+ // stationsData.push(one);
+ // }
+ // }
+ BurrModal.propTypes = {
+ visible: PropTypes.bool.isRequired,
+ closeModal: PropTypes.func.isRequired
+ }
+ return (<>
+
+
+
+ ]}>
+
+
+
+ >)
+
+}
+
+
+
+
+
+
+
+function mapStateToProps(state) {
+ const { abnItemState_burr,global } = state;
+ return {
+ abnItemCompData: abnItemState_burr.items,
+ actions:global.actions,
+ }
+}
+
+export default connect(mapStateToProps)(BurrModal)
diff --git a/web/client/src/sections/install/components/interruptModal.jsx b/web/client/src/sections/install/components/interruptModal.jsx
new file mode 100644
index 0000000..775c25b
--- /dev/null
+++ b/web/client/src/sections/install/components/interruptModal.jsx
@@ -0,0 +1,187 @@
+import React, { useRef,useState,useEffect } from "react";
+import { connect } from "react-redux";
+import moment from 'moment';
+import { Form, Tooltip, Select, Checkbox, Modal, Input, Button, Switch, DatePicker, Row } from "@douyinfe/semi-ui";
+import { IconLineChartStroked } from "@douyinfe/semi-icons"
+import { TimeAbnValueLineChart } from '../components/TimeAbnValueLineChart'
+import PropTypes from 'prop-types';
+
+const InterruptModal = (props) => {
+ const form = useRef()
+ const {modalData,actions,dispatch, closeModal, visible, structId,item,sensorId,project}=props
+ const {install}=actions
+ const [currStartTime, setCurrStartTime] = useState(moment().add('days', -1).format('YYYY-MM-DD HH:mm:ss'))
+ const [currEndTime, setcurrEndTime] = useState(moment().format('YYYY-MM-DD HH:mm:ss'))
+
+
+ //表单类
+ const btnFormSubmit = () => {
+ let data = modalData
+ form.current.validate().then(value=>{
+ let paramJson = { "thr_int": Number(value.thr_int) }
+ let pushData = {
+ station: data.sensorId,//测点
+ factorId: data.factorId,
+ itemId: null,
+ abnType: 1,//中断
+ enabled: value.checked,
+ params: paramJson
+ }
+ dispatch(install.editAbnParams(data.id,pushData)).then(res => {
+ if (res.success) {
+ const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',')
+ dispatch(install.getAbnParamList({ sensorId:id}))
+ closeModal()
+ }
+ })
+ // this.props.dispatch(editAbnParams(data.id, pushData)).then(_ => {
+ // this.props.dispatch(getAbnParamList(this.props.structId)).then(() => {
+ // this.props.closeModal();
+ // });
+ // });
+
+ })
+ // this.props.form.validateFieldsAndScroll((err, values) => {
+
+ // });
+ }
+
+
+ const checkInterger = (rule, val) => {
+ // if(val){
+ const strRegex = /^[1-9]*[1-9][0-9]*$/
+ if (strRegex.test(val)) {
+ return true
+ }
+ return false
+ // }
+
+};
+ const timeOnChange = (dates, dateStrings) => {
+ if (dates.length == 0) {
+ setCurrStartTime(null)
+ setcurrEndTime(null)
+ return message.warning('请选择时间');
+ }
+ setCurrStartTime(dates[0].format('YYYY-MM-DD HH:mm:ss'))
+ setcurrEndTime(dates[1].format('YYYY-MM-DD HH:mm:ss'))
+ }
+ //数据对比
+ // const dataCompare = () => {
+ // form.current.validate().then(_=>{
+ // let paramJson = { "thr_int": values.interrupt }
+ // let data = {
+ // station: modalData.stationId,
+ // factorId: modalData.factorId,
+ // itemId: modalData.itemId,
+ // abnType: 'interrupt',//算法类型
+ // enabled: true,
+ // params: paramJson
+ // };
+ // let start = this.state.currStartTime;
+ // let end = this.state.currEndTime;
+ // this.props.dispatch(getItemAbnResult_int(this.props.structId, start, end, data)).then(res => {
+ // })
+ // })
+ // }
+ // let originalData = ['还没查询'], calcArray = [], key, itemName, start, end;
+ // let stationsData = [];
+ // if (abnItemCompData) {
+ // originalData = abnItemCompData.stationData;
+ // calcArray = abnItemCompData.resultArray;
+ // key = abnItemCompData.itemKey;
+ // itemName = abnItemCompData.itemName;
+ // start = originalData && originalData.length > 0 ? originalData[0].time : '';
+ // end = originalData && originalData.length > 0 ? originalData[originalData.length - 1].time : '';
+ // for (let i = 0; i < originalData.length; i++) {
+ // let one = { name: itemName + "(单位:" + abnItemCompData.unit + ")", value: originalData[i][key], time: originalData[i].time };
+ // stationsData.push(one);
+ // }
+ // for (let j = 0; j < calcArray.length; j++) {
+ // let one = { name: "中断点:" + calcArray[j].time + "(中断时长:" + calcArray[j].hour.toFixed(2) + "h)", value: calcArray[j].value, time: calcArray[j].time };
+ // stationsData.push(one);
+ // }
+ // }
+
+// InterruptModal.propTypes = {
+// visible: PropTypes.bool.isRequired,
+// closeModal: PropTypes.func.isRequired
+// }
+ return
+
+
+
+
]}>
+
+
+
+
+}
+
+
+function mapStateToProps(state) {
+ const { global } = state;
+ return {
+ // abnItemCompData: abnItemState_int.items,
+ // abnItemCompData:[],
+ actions:global.actions
+ }
+}
+
+export default connect(mapStateToProps)(InterruptModal)
diff --git a/web/client/src/sections/install/components/theme.jsx b/web/client/src/sections/install/components/theme.jsx
new file mode 100644
index 0000000..0298e64
--- /dev/null
+++ b/web/client/src/sections/install/components/theme.jsx
@@ -0,0 +1,474 @@
+import G2 from 'g2';
+
+const DEFAULT_COLOR = '#ffea00';
+const FONT_FAMILY = '"Microsoft YaHei", "微软雅黑", "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", SimSun, "sans-serif"';
+const theme = {
+ defaultColor: DEFAULT_COLOR, // 默认主题色
+ plotCfg: {
+ margin: [20, 80, 60, 80]
+ },
+ facetCfg: {
+ type: 'rect',
+ margin: 10,
+ facetTitle: {
+ titleOffset: 16,
+ colDimTitle: {
+ title: {
+ fontSize: 14,
+ textAlign: 'center',
+ fill: '#999'
+ }
+ },
+ colTitle: {
+ title: {
+ fontSize: 12,
+ textAlign: 'center',
+ fill: '#999'
+ }
+ },
+ rowTitle: {
+ title: {
+ fontSize: 12,
+ textAlign: 'center',
+ rotate: 90,
+ fill: '#999'
+ }
+ },
+ rowDimTitle: {
+ title: {
+ fontSize: 12,
+ textAlign: 'center',
+ rotate: 90,
+ fill: '#999'
+ }
+ }
+ }
+ },
+ binWidth: 0.03, // bin 统计的默认值
+ fontFamily: FONT_FAMILY,
+ colors: {
+ 'default': ['#ffea00', '#0de3ff', '#fe8501', '#00ff36', '#0097ff', '#fe4c9b', '#9bff00', '#bd67ff', '#f6ba33', '#978cff'],
+ intervalStack: ['#ffea00', '#0de3ff', '#fe8501', '#00ff36', '#0097ff', '#fe4c9b', '#9bff00', '#bd67ff', '#f6ba33', '#978cff']
+ },
+ shapes: {
+ point: ['hollowCircle', 'hollowSquare', 'hollowDiamond', 'hollowBowtie', 'hollowTriangle',
+ 'hollowHexagon', 'cross', 'tick', 'plus', 'hyphen', 'line'],
+ line: ['line', 'dash', 'dot'],
+ area: ['area']
+ },
+ hues: ['red', 'yellow', 'green', 'blue', 'orange', 'purple', 'pink', 'brown', 'white', 'gray', 'black'],
+ axis: {
+ top: {
+ position: 'top',
+ titleOffset: 30,
+ title: {
+ fontSize: 12,
+ fill: '#999'
+ },
+ labels: {
+ label: {
+ fill: '#a2adc2',
+ fontSize: 12
+ }
+ },
+ tickLine: {
+ lineWidth: 1,
+ stroke: '#ccc',
+ value: 5
+ }
+ },
+ bottom: {
+ position: 'bottom',
+ titleOffset: 45,
+ labelOffset: 20,
+ title: {
+ fontSize: 12,
+ textAlign: 'center',
+ fill: '#999'
+ },
+ labels: {
+ label: {
+ fill: '#a2adc2',
+ fontSize: 12
+ }
+ },
+ line: {
+ lineWidth: 1,
+ stroke: '#ccc'
+ },
+ tickLine: {
+ lineWidth: 1,
+ stroke: '#ccc',
+ value: 5
+ }
+ },
+ left: {
+ position: 'left',
+ titleOffset: 60,
+ labelOffset: 13,
+ title: {
+ fontSize: 12,
+ fill: '#999'
+ },
+ labels: {
+ label: {
+ fill: '#a2adc2'
+ }
+ },
+ line: {
+ lineWidth: 1,
+ stroke: '#ccc'
+ },
+ tickLine: {
+ lineWidth: 1,
+ stroke: '#ccc',
+ value: 5
+ },
+ grid: {
+ line: {
+ stroke: '#495f7a',
+ lineWidth: 1,
+ lineDash: [2, 2]
+ }
+ }
+ },
+ right: {
+ position: 'right',
+ titleOffset: 60,
+ labelOffset: 13,
+ title: {
+ fontSize: 12,
+ fill: '#999'
+ },
+ labels: {
+ label: {
+ fill: '#a2adc2'
+ }
+ },
+ line: {
+ lineWidth: 1,
+ stroke: '#ccc'
+ },
+ tickLine: {
+ lineWidth: 1,
+ stroke: '#ccc',
+ value: 5
+ }
+ },
+ circle: {
+ labelOffset: 5,
+ line: {
+ lineWidth: 1,
+ stroke: '#ccc'
+ },
+ grid: {
+ line: {
+ stroke: '#d9d9d9',
+ lineWidth: 1,
+ lineDash: [1, 3]
+ }
+ },
+ labels: {
+ label: {
+ fill: '#a2adc2'
+ }
+ }
+ },
+ gauge: {
+ grid: null,
+ labelOffset: 5,
+ tickLine: {
+ lineWidth: 1,
+ value: -20,
+ stroke: '#ccc'
+ },
+ subTick: 5,
+ labels: {
+ label: {
+ fill: '#a2adc2'
+ }
+ }
+ },
+ clock: {
+ grid: null,
+ labelOffset: 5,
+ tickLine: {
+ lineWidth: 1,
+ value: -20,
+ stroke: '#C0D0E0'
+ },
+ subTick: 5,
+ labels: {
+ label: {
+ fill: '#a2adc2'
+ }
+ }
+ },
+ radius: {
+ titleOffset: 45,
+ labels: {
+ label: {
+ fill: '#a2adc2'
+ }
+ },
+ line: {
+ lineWidth: 1,
+ stroke: '#ccc'
+ },
+ grid: {
+ line: {
+ stroke: '#d9d9d9',
+ lineWidth: 1,
+ lineDash: [2, 2]
+ },
+ type: 'circle'
+ }
+ },
+ helix: {
+ grid: null,
+ labels: {
+ label: null
+ },
+ line: {
+ lineWidth: 1,
+ stroke: '#ccc'
+ },
+ tickLine: {
+ lineWidth: 1,
+ value: 5,
+ stroke: '#ccc'
+ }
+ }
+ },
+ labels: {
+ offset: 14,
+ label: {
+ fill: '#666',
+ fontSize: 12
+ }
+ },
+ treemapLabels: {
+ offset: 10,
+ label: {
+ fill: '#fff',
+ fontSize: 14,
+ textBaseline: 'top',
+ fontStyle: 'bold'
+ }
+ },
+ innerLabels: {
+ label: {
+ fill: '#fff',
+ fontSize: 12
+ }
+ }, // 在theta坐标系下的饼图文本内部的样式
+ thetaLabels: {
+ labelLine: {
+ lineWidth: 1
+ },
+ labelHeight: 14,
+ offset: 30
+ }, // 在theta坐标系下的饼图文本的样式
+ legend: {
+ right: {
+ position: 'right',
+ back: null,
+ title: {
+ fill: '#a2adc2'
+ },
+ spacingX: 10,
+ spacingY: 12,
+ markerAlign: 'center',
+ wordSpaceing: 12,
+ word: {
+ fill: '#a2adc2'
+ },
+ width: 20,
+ height: 156
+ },
+ left: {
+ position: 'left',
+ back: null,
+ title: {
+ fill: '#a2adc2'
+ },
+ spacingX: 10,
+ spacingY: 12,
+ markerAlign: 'center',
+ wordSpaceing: 12,
+ word: {
+ fill: '#a2adc2'
+ },
+ width: 20,
+ height: 156
+ },
+ top: {
+ position: 'top',
+ title: null,
+ back: null,
+ spacingX: 16,
+ spacingY: 10,
+ markerAlign: 'center',
+ wordSpaceing: 12,
+ word: {
+ fill: '#a2adc2'
+ },
+ width: 156,
+ height: 20
+ },
+ bottom: {
+ position: 'bottom',
+ title: null,
+ back: null,
+ spacingX: 16,
+ spacingY: 10,
+ markerAlign: 'center',
+ wordSpaceing: 12,
+ word: {
+ fill: '#a2adc2'
+ },
+ width: 156,
+ height: 20
+ }
+ },
+ tooltip: {
+ crosshairs: false,
+ offset: 15,
+ crossLine: {
+ stroke: '#666'
+ },
+ wordSpaceing: 6,
+ markerCfg: {
+ symbol: 'circle',
+ radius: 3
+ }
+ },
+ activeShape: {
+ point: {
+ radius: 5,
+ fillOpacity: 0.7
+ },
+ hollowPoint: {
+ lineWidth: 2,
+ radius: 4
+ },
+ interval: {
+ fillOpacity: 0.7
+ },
+ hollowInterval: {
+ lineWidth: 2
+ },
+ area: {
+ fillOpacity: 0.85
+ },
+ hollowArea: {
+ lineWidth: 2
+ },
+ line: {
+ lineWidth: 2
+ },
+ polygon: {
+ fillOpacity: 0.75
+ }
+ }, // 图形激活时,鼠标移动到上面
+ shape: {
+ point: {
+ lineWidth: 1,
+ fill: DEFAULT_COLOR,
+ radius: 4
+ },
+ hollowPoint: {
+ fill: '#fff',
+ lineWidth: 1,
+ stroke: DEFAULT_COLOR,
+ radius: 3
+ },
+ interval: {
+ lineWidth: 0,
+ fill: DEFAULT_COLOR,
+ fillOpacity: 0.85
+ },
+ pie: {
+ lineWidth: 1,
+ stroke: '#fff'
+ },
+ hollowInterval: {
+ fill: '#fff',
+ stroke: DEFAULT_COLOR,
+ fillOpacity: 0,
+ lineWidth: 1
+ },
+ area: {
+ lineWidth: 0,
+ fill: DEFAULT_COLOR,
+ fillOpacity: 0.6
+ },
+ polygon: {
+ lineWidth: 0,
+ fill: DEFAULT_COLOR,
+ fillOpacity: 1
+ },
+ hollowPolygon: {
+ fill: '#fff',
+ stroke: DEFAULT_COLOR,
+ fillOpacity: 0,
+ lineWidth: 1
+ },
+ hollowArea: {
+ fill: '#fff',
+ stroke: DEFAULT_COLOR,
+ fillOpacity: 0,
+ lineWidth: 1
+ },
+ line: {
+ stroke: DEFAULT_COLOR,
+ lineWidth: 1,
+ fill: null
+ }
+ },
+ guide: {
+ text: {
+ fill: '#666',
+ fontSize: 12
+ },
+ line: {
+ stroke: DEFAULT_COLOR,
+ lineDash: [0, 2, 2]
+ },
+ rect: {
+ lineWidth: 0,
+ fill: DEFAULT_COLOR,
+ fillOpacity: 0.1
+ },
+ tag: {
+ line: {
+ stroke: DEFAULT_COLOR,
+ lineDash: [0, 2, 2]
+ },
+ text: {
+ fill: '#666',
+ fontSize: 12,
+ textAlign: 'center'
+ },
+ rect: {
+ lineWidth: 0,
+ fill: DEFAULT_COLOR,
+ fillOpacity: 0.1
+ }
+ },
+ html: {
+ align: 'cc'
+ }
+ },
+ tooltipMarker: {
+ fill: '#fff',
+ symbol: 'circle',
+ lineWidth: 2,
+ stroke: DEFAULT_COLOR,
+ radius: 4
+ } // 提示信息在折线图、区域图上形成点的样式
+};
+
+var Theme = G2.Util.mix(true, {}, G2.Theme, theme);
+
+G2.Global.setTheme(Theme);
\ No newline at end of file
diff --git a/web/client/src/sections/install/containers/AbnRecognize.jsx b/web/client/src/sections/install/containers/AbnRecognize.jsx
new file mode 100644
index 0000000..86b647c
--- /dev/null
+++ b/web/client/src/sections/install/containers/AbnRecognize.jsx
@@ -0,0 +1,106 @@
+'use strict';
+
+import React, { } from 'react';
+import { connect } from 'react-redux';
+import { Row,Col, Card,Icon } from '@douyinfe/semi-ui';
+import { IconSearch,IconLineChartStroked,IconStar,IconUser} from '@douyinfe/semi-icons';
+import Trend from './trend';
+import Burr from './Burr';
+import Interrupt from './Interrupt';
+
+const AbnRecognize = (props) => {
+ // const {abnCompareData}=props
+ const {calcMethod,abnCompareData,factorId,structId,strunctChange,itName,sensorId,project}=props
+ // let originalData = ['还没查询'], calcArray = [], key, itemName, start, end;
+ // let stationsData = [];
+ // if (abnCompareData &&calcMethod == abnCompareData.method) {
+ // originalData = abnCompareData.stationData;
+ // calcArray = abnCompareData.resultArray;
+ // key = abnCompareData.itemKey;
+ // itemName = abnCompareData.itemName;
+ // start = originalData && originalData.length > 0 ? originalData[0].time : '';
+ // end = originalData && originalData.length > 0 ? originalData[originalData.length - 1].time : '';
+ // for (let i = 0; i < originalData.length; i++) {
+ // let one = { name: itemName + "(单位:" + abnCompareData.unit + ")", value: originalData[i][key], time: originalData[i].time };
+ // stationsData.push(one);
+ // }
+ // if (calcMethod == 'interrupt') {
+ // for (let j = 0; j < calcArray.length; j++) {
+ // let one = { name: "中断点:" + calcArray[j].time + "(中断时长:" + calcArray[j].hour.toFixed(2) + "h)", value: calcArray[j].value, time: calcArray[j].time };
+ // stationsData.push(one);
+ // }
+ // } else if (calcMethod == 'burr') {
+ // for (let j = 0; j < calcArray.length; j++) {
+ // let one = {
+ // name: "毛刺点:" + calcArray[j].time + "(突变大小:" + calcArray[j].burr.toFixed(2) + abnCompareData.unit + ")",
+ // value: calcArray[j].value, time: calcArray[j].time
+ // };
+ // stationsData.push(one);
+ // }
+ // } else {
+ // if (calcArray) {
+ // let preLineData = calcArray.calcPreprocess;
+ // let abnTrends = calcArray.calcFinal;
+ // for (let j = 0; j < preLineData.length; j++) {
+ // let one = { name: "预处理+滑动均值后数据", value: preLineData[j].value, time: preLineData[j].time };
+ // stationsData.push(one);
+ // }
+ // for (let t = 0; t < abnTrends.length; t++) {
+ // let name = abnTrends[t].startTime + "至" + abnTrends[t].endTime + abnTrends[t].des + ",渐变差值:" + abnTrends[t].value.toFixed(2) + abnCompareData.unit;
+ // let start = { name: name, value: abnTrends[t].startValue, time: abnTrends[t].startTime };
+ // let end = { name: name, value: abnTrends[t].endValue, time: abnTrends[t].endTime };
+ // stationsData.push(start);
+ // stationsData.push(end);
+ // }
+ // }
+ // }
+ // }
+
+
+const renderContent=()=> {
+ const SubContent = {
+ 'interrupt': ,
+ 'burr': ,
+ 'trend': ,
+ };
+ return SubContent[calcMethod];
+}
+
+
+
+return (<>
+
+ {/*
+
+ 数据对比}>
+
+
+ {originalData && originalData[0] == '还没查询' ?
+
输入参数,点击数据对比展示数据对比图(默认显示选择的第一个传感器)
+ :
+ originalData && originalData[0] != '还没查询' && originalData.length > 0 ?
+
+ :
+
没有查询到任何有效数据!
}
+
+
+
+
+
*/}
+ {renderContent()}
+
+>)
+}
+
+
+
+function mapStateToProps(state) {
+ const { auth, global, OrganizationDeps } = state;
+ return {
+ // loading: OrganizationDeps.isRequesting,
+ // user: auth.user,
+ // actions: global.actions,
+ };
+}
+export default connect(mapStateToProps)(AbnRecognize)
\ No newline at end of file
diff --git a/web/client/src/sections/install/containers/burr.jsx b/web/client/src/sections/install/containers/burr.jsx
new file mode 100644
index 0000000..6ad9459
--- /dev/null
+++ b/web/client/src/sections/install/containers/burr.jsx
@@ -0,0 +1,267 @@
+'use strict';
+
+import React, { useState,useRef } from 'react';
+import { connect } from 'react-redux';
+import { Row, Col, Button, Input, Table, Card, Switch, Popconfirm, Tag,Notification, Form } from '@douyinfe/semi-ui';
+import BurrModal from '../components/burrModal';
+import { useEffect } from 'react';
+
+const Burr = (props) => {
+ const {factorId,abnParam,structId,dispatch,actions,itName,sensorId,project}=props
+ const form = useRef()
+ const [selectedRowKeys, setSelectedRowKeys] = useState([])
+ const [filterFunc, setFilterFunc] = useState({})
+ const [modalVisible, setModalVisible] = useState(false)
+ const [bvBatch, setBvBatch] = useState('')
+ const [modalData, setModalData] = useState(null)
+ const {install}=actions
+ const filterSet = { sensorName: null, factorName: null }
+
+ useEffect(()=>{
+ },[])
+ const onSelectChange = (selectedRowKeys) => {
+ setSelectedRowKeys(selectedRowKeys);
+ }
+ const compareAndEdit = (e, record) => {
+ setModalVisible(true)
+ setModalData(record)
+ }
+ const modalCancel = e => {
+ setModalVisible(false)
+ };
+ const removeItem = e => {
+ dispatch(install.deleteAbnParams(e)).then(res=>{
+ if(res.success){
+ const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',')
+ dispatch(install.getAbnParamList({ sensorId:id}))
+ }
+ })
+ };
+ const handleInputChange = e => {
+ if (e != null) {
+ const value = e
+ let func = (ep => (s => (s.sensorName).search(ep) > -1))(value);
+ filterSet.sensorName = func;
+ func = (ep => (s => (s.factorName).search(ep) > -1))(value);
+ filterSet.factorName = func;
+ } else {
+ filterSet.sensorName = null;
+ filterSet.factorName = null;
+ }
+ setFilterFunc(filterSet)
+ }
+ //批量启用or禁用
+ const onSwitchChange = (e) => {
+ if (selectedRowKeys.length != 0) {
+ let ids = selectedRowKeys.join(',');
+ let data = {
+ enabled: e,
+ use: 'switch'
+ };
+ //批量启用or禁用逻辑
+ dispatch(install.batchCfgAbnParams(ids,data)).then(res=>{
+ if(res.success){
+ const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',')
+ dispatch(install.getAbnParamList({ sensorId:id }))
+ }
+ })
+ }
+ }
+ //批量删除
+ const batchDelete = () => {
+ if (selectedRowKeys.length != 0) {
+ let ids = selectedRowKeys.join(',');
+ dispatch(install.deleteAbnParams(ids)).then(res=>{
+ if(res.success){
+ const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',')
+ dispatch(install.getAbnParamList({ sensorId:id }))
+ }
+ })
+
+ // this.props.dispatch(deleteAbnParams(ids)).then(_ => {
+ // this.props.dispatch(getAbnParamList(this.props.structId));
+ // });
+ } else {
+ Notification.warning({
+ content: '您尚未勾选任何参数配置!',
+ duration: 3,
+ })
+ }
+ }
+ //批量保存
+ const batchSave = () => {
+ form.current.validate().then(res=>{
+ let dataSource = abnParam.filter(a => a.abnType == 2 && a.factorId == factorId)
+ if (selectedRowKeys.length != 0) {
+ let ids = selectedRowKeys.join(',');
+ let data = {
+ paramJson: { "thr_burr": bvBatch },
+ use: 'notSwitch',
+ };
+ //批量保存逻辑
+ dispatch(install.batchCfgAbnParams(ids,data)).then(res=>{
+ if(res.success){
+ const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',')
+ dispatch(install.getAbnParamList({ sensorId:id }))
+ }
+ })
+ } else if (dataSource.length != 0) {
+ Notification.warning({
+ content: '您尚未勾选任何参数配置!',
+ duration: 3,
+ })
+ }
+ })
+ }
+ const batchBurr = (value) => {
+ setBvBatch(value)
+ }
+ const checkNumber = (rule, val) => {
+ const strRegex = /^-?\d+(\.\d+)?$/
+ if (strRegex.test(val)) {
+ return true
+ }
+ return false
+
+
+
+ }
+ let dataSource = abnParam.filter(a => a.abnType == 2 && a.factorId == factorId)
+ const rowSelection = {
+ selectedRowKeys: selectedRowKeys,
+ onChange: onSelectChange,
+ };
+ let filterData = dataSource
+ let flag = false
+ let keyArr = []
+ let tmpds = []
+ let dataPush = []
+ Object.keys(filterFunc).forEach(key => {
+ const filter = filterFunc[key]
+ filterData = dataSource
+ if (filter != null) {
+ flag = true
+ filterData = filterData.filter(filter)
+ if (filterData.length > 0) {
+ filterData.map(s => {
+ if (keyArr.indexOf(s.id) == -1) {
+ keyArr.push(s.id)
+ dataPush.push(s)
+ }
+ })
+ }
+ }
+ })
+ tmpds = flag ? dataPush.sort((a, b) => a.id - b.id) : dataSource.sort((a, b) => a.id - b.id)
+ let columns = [
+ {
+ title: "测点位置",
+ dataIndex: "sensorName",
+ key: "sensorName",
+ width: '20%',
+ },
+ {
+ title: "监测项",
+ dataIndex: "factorName",
+ key: "factorName",
+ width: '20%',
+ render: (text, record) => (
+ record.factorName + "/" + itName
+ ),
+ },
+ {
+ title: "毛刺阈值",
+ dataIndex: "params",
+ key: "params",
+ width: '20%',
+ render: text => text.thr_burr
+ },
+ {
+ title: "启用状态",
+ dataIndex: "enabled",
+ key: "enabled",
+ width: '20%',
+ render: (text, record) => (
+ record.enabled ? 已启用 : 已禁用
+ )
+ },
+ {
+ title: "操作",
+ key: "action",
+ width: '20%',
+ render: (text, record) => (
+
+
+
+ {removeItem(record.id) }}>
+
+
+
+ ),
+ }
+ ]
+
+ return (
+
+
+ {modalVisible ? : ''}
+
+ )
+
+}
+
+
+
+function mapStateToProps(state) {
+ const { abnParam ,global} = state;
+ // let isRequesting = abnParamState?.isRequesting;
+
+ return {
+ isRequesting: false,//请求状态
+ abnParam: abnParam?.data||[],
+ actions:global.actions
+ }
+}
+export default connect(mapStateToProps)(Burr)
+
diff --git a/web/client/src/sections/install/containers/cacl.jsx b/web/client/src/sections/install/containers/cacl.jsx
new file mode 100644
index 0000000..c035f37
--- /dev/null
+++ b/web/client/src/sections/install/containers/cacl.jsx
@@ -0,0 +1,496 @@
+'use strict'
+
+import React, { useEffect, useState, useRef } from 'react';
+import { connect } from 'react-redux';
+import { Row, Form, Col, Select, Button, Notification } from '@douyinfe/semi-ui';
+import moment from 'moment';
+import AbnRecognize from './AbnRecognize'
+const methodType = {
+ 'interrupt': 1,
+ 'burr': 2,
+ 'trend': 3,
+}
+const methodDes = {
+ 'interrupt': "中断",
+ 'burr': "毛刺",
+ 'trend': "异常趋势",
+}
+
+const Calc = (props) => {
+ const { actions, dispatch, pepProjectId, abnMethods, abnParam } = props
+ const { install, data } = actions
+ const [structId, setStructId] = useState(null)
+ const [methodType, setMethodType] = useState('interrupt')
+ const [loading, setLoading] = useState(false)
+ const [structList, setStructList] = useState([])//结构物筛选列表
+ const [project, setProject] = useState('')//project(eg:{projcet:'nbjj'})
+ const [reCalcFactorId, setReCalcFactorId] = useState(null);
+ const [msg, setMsg] = useState('')
+ const [factosList, setFactorsList] = useState([])//监测因素列表
+ const [factorId, setFactorId] = useState([])
+ const [sensorList, setSensorList] = useState([])//设备列表
+ const [sensorId, setSensorId] = useState([])//设备id'
+ const [itemId, setItemId] = useState(null)//监测项索引
+ const [itemName, setItemName] = useState('')//检测项name
+ const form = useRef()
+ const form2 = useRef()
+ const method = {
+ 'interrupt': 1,
+ 'burr': 2,
+ 'trend': 3,
+ }
+ //初始化
+ useEffect(() => {
+ // getData()
+ dispatch(install.getAbnMethods())
+ }, [])
+ useEffect(() => {
+ if(project){
+ const id = [...sensorId?.map(item=>project+'-'+item), -1].join(',')
+ dispatch(install.getAbnParamList({ sensorId: id }))
+ }
+ }, [sensorId, factorId,project])
+
+ //监听变化
+ useEffect(() => {
+ setStructList([])
+ form.current.reset()
+ form2.current.reset()
+ form2.current.setValue('method', methodType)
+ getData()
+ }, [pepProjectId])
+ const checkInterger = (rule, val) => {
+ // if(val){
+ const strRegex = /^[1-9]*[1-9][0-9]*$/
+ if (strRegex.test(val)) {
+ return true
+ }
+
+ return false
+ // }
+
+ };
+ const checkNumber = (rule, val) => {
+ const strRegex = /^-?\d+(\.\d+)?$/
+ if (strRegex.test(val)) {
+ return true
+ }
+ return false
+
+
+
+ }
+ const checkPoint = (rule, value) => {
+ const pattern = /^[1-9]*[1-9][0-9]*$/;
+ if (pattern.test(value) && value != 1) {
+ return true
+ }
+ return false
+ }
+ const getJson = (values) => {
+ let paramJson;
+ switch (methodType) {
+ case 'interrupt':
+ return paramJson = { "thr_int": Number(values.iv) };
+ case 'burr':
+ return paramJson = { "thr_burr": Number(values.bv1) }
+ case 'trend':
+ return paramJson = {
+ "thr_burr": Number(values.bv2),//毛刺阈值
+ "win_med": Number(values.ws),//滑动中值窗口
+ "win_avg": Number(values.rc),//滑动均值窗口
+ "win_grad": Number(values.pn),//渐变点个数
+ "thr_grad": Number(values.gv),//渐变阈值
+ "thr_der": Number(values.dv),//导数阈值
+ "days_Last": Number(values.time),//时间跨度
+ }
+ }
+ }
+ const getStationstoSave = () => {
+ let toSave = [], toSaveName = [], notToSave = [], notToSaveName = []
+ let selectIds = sensorId
+ for (let i = 0; i < selectIds.length; i++) {
+ let seStaName = sensorList.find(a => a.value == selectIds[i]).label
+ let cfg
+ if (methodType == "interrupt") {
+ cfg = abnParam.find(a => a.abnType == 1 && a.factorId == factorId
+ && a.sensorId == selectIds[i])
+ } else {
+ let type = methodType == "burr" ? 2 : 3
+ cfg = abnParam.find(a => a.abnType == type && a.factorId == factorId
+ && a.sensorId == selectIds[i])
+ }
+ if (!cfg) {
+ toSave.push(selectIds[i])
+ toSaveName.push(seStaName)
+ } else {
+ notToSave.push(selectIds[i])
+ notToSaveName.push(seStaName)
+ }
+ }
+ return {
+ toSave: toSave, toSaveName: toSaveName,
+ notToSave: notToSave, notToSaveName: notToSaveName
+ };
+ }
+ //保存配置
+ const handleSave = () => {
+ setMsg('')
+ form2.current.validate().then(res => {
+ if (sensorId.length != 0) {
+ let ids = getStationstoSave()
+ if (ids.toSave.length != 0) {
+ let paramJson = getJson(res)
+ let data = {
+ sensorId:ids.toSave,//测点
+ sensorName: ids.toSaveName,
+ factorId: factorId,
+ factorName: factosList.find(a => a.value == factorId).label,
+ abnType: method[methodType],//算法类型
+ enabled: true,
+ params: paramJson,
+ itemId: itemId,
+
+ }
+ let pushData = data.sensorId.map(d => {
+ return {
+ sensorId: project+'-'+d,//测点
+ sensorName: sensorList.find(a => a.value == d).label,
+ factorId: factorId,
+ factorName: factosList.find(a => a.value == factorId).label,
+ abnType: method[methodType],//算法类型
+ enabled: true,
+ params: paramJson,
+ itemId: methodType == 'interrupt' ? null : itemId
+
+ }
+ })
+ dispatch(install.addAbnParam(pushData)).then(res => {
+ if (res.success) {
+ const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',')
+ dispatch(install.getAbnParamList({ sensorId: id }))
+ }
+ })
+ if (ids.notToSave.length != 0) {
+ let fact = methodType == 'interrupt' ? "因素" : "子项"
+ Notification.warning({
+ content: `已存在传感器: ${ids.notToSaveName.join(',')} 在当前监测${fact}下的${methodDes[methodType]}参数配置,本次保存的传感器为:${ids.toSaveName.join(',')}`,
+ duration: 3,
+ })
+ }
+ } else {
+ let sg = methodType == 'interrupt' ? "因素下的中断"
+ : methodType == 'burr' ? "子项下的毛刺" : "子项下的异常趋势"
+ Notification.warning({
+ content: `已存在选定测点在当前监测 ${sg} 参数配置,本次无可保存的参数配置`,
+ duration: 3,
+ })
+ }
+ } else {
+ Notification.warning({
+ content: `请选择测点`,
+ duration: 3,
+ })
+ return false
+ }
+
+ })
+ }
+ //数据对比
+ const dataCompare = () => {
+ // this.setState({ msg: '' });
+ // this.props.form.validateFields((err, values) => {
+ // if (!err) {
+ // if (this.props.stationsValue.length != 0) {
+ // let paramJson = this.getJson(values);
+ // let data = {
+ // station: this.props.stationsValue[0],//第一个测点
+ // factorId: this.props.factorId,
+ // itemId: this.props.itemId,
+ // abnType: this.props.methodType,//算法类型
+ // enabled: true,
+ // params: paramJson
+ // };
+ // this.props.dataCompara(data);
+ // } else {
+ // message.error('请选择测点!');
+ // return false;
+ // }
+ // }
+ // });
+ }
+
+ //结构物筛选发生变化回调函数
+ const reCalcFactorChange = (value) => {
+ setItemName(factosList?.find(item => item.value === value[0])?.children?.find(p => p.value === value[1])?.label)
+ setItemId(value[1])
+ // form2.current.setValue('point', '')
+ setFactorId(value[0])
+ }
+ //点位筛选发生改变的回调函数
+ const pointChange = (value) => {
+ setSensorId(value)
+ // setProject(structList.find(item => item.value == structId)?.project)
+ }
+ //监听结构物变化,查询监测因素
+ useEffect(() => {
+ setLoading(true);
+ let queryParams = { structId, cacl: 1 }
+ if (structId) {
+ dispatch(data.getFactors({ ...queryParams })).then(res => {
+ if (res.success) {
+ const list = res.payload.data?.map(item => {
+ const itemList = item.ItemNames && item.ItemNames.split(',') || []
+ let child = []
+ itemList && itemList.length && itemList.forEach((i, index) => {
+ child.push({ label: i, value: index })
+ })
+ return {
+ label: item.Name,
+ value: item.FactorID,
+ children: child
+
+ }
+
+ })
+ form2.current.setValue('factor', [list[0]?.value, list[0]?.children[0]?.value])
+ setFactorsList(list)
+ setFactorId(list[0]?.value)
+ setItemId(list[0]?.children[0]?.value)
+ setItemName(list[0]?.children[0]?.label)
+ setLoading(false)
+ }
+ })
+ }
+
+ }, [structId])
+ useEffect(() => {
+ let queryParams = { structId, SafetyFactorTypeId: factorId }
+ if (factorId && structId) {
+ dispatch(data.getSensors({ ...queryParams })).then(res => {
+ if (res.success) {
+ const list = res.payload.data?.map(item => {
+ return {
+ value: item.SensorId,
+ label: item.SensorLocationDescription,
+ }
+ })
+ form2.current.setValue('point', [list[0]?.value])
+ setSensorId([list[0]?.value])
+ setSensorList(list)
+ setLoading(false);
+ setProject(res.payload.data[0].Project)
+ }
+ })
+ }
+ }, [factorId])
+
+
+
+
+ //获取结构物函数
+ const getData = (queryParams = { pomsProjectId: pepProjectId }) => {
+ setLoading(true);
+ dispatch(data.getProjectAllStructures({ ...queryParams })).then(res => {
+ if (res.success) {
+ const uniqueIds = new Set();
+ const list = res.payload.data?.filter(item => {
+ const duplicate = uniqueIds.has(item.strucId);
+ uniqueIds.add(item.strucId);
+ return !duplicate
+ })?.map(item => ({
+ value: item.strucId,
+ label: item.strucName,
+ project: item.Project,
+ }))
+ form.current.setValue('struct', list[0]?.value)
+ setStructId(list[0]?.value)
+ setStructList(list)
+ setLoading(false)
+ }
+ }
+
+ )
+ }
+ const changeMethod = (value) => {
+ setMethodType(value)
+ }
+ const clearFactorAndSensor = () => {
+ form2.current.setValue('point', '')
+ form2.current.setValue('factor', '')
+ }
+ const structChange = (value) => {
+ setStructId(value)
+ clearFactorAndSensor()
+ }
+
+
+ const renderParamsConfig = () => {
+ switch (methodType) {
+ case 'interrupt':
+ return (
+
+
+
+
+ );
+ case 'burr':
+ return (
+
+
+
+
+ );
+ case 'trend':
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+ }
+
+ return (<>
+
+
+
+ >)
+}
+
+
+
+function mapStateToProps(state) {
+ const { auth, global, OrganizationDeps, abnMethods, abnParam } = state;
+ return {
+ loading: OrganizationDeps.isRequesting,
+ user: auth.user,
+ actions: global.actions,
+ pepProjectId: global.pepProjectId,
+ abnMethods: abnMethods?.data || [],
+ abnParam: abnParam?.data || []
+ };
+}
+export default connect(mapStateToProps)(Calc);
+
+
diff --git a/web/client/src/sections/install/containers/index.js b/web/client/src/sections/install/containers/index.js
index afbe8a0..4cd2313 100644
--- a/web/client/src/sections/install/containers/index.js
+++ b/web/client/src/sections/install/containers/index.js
@@ -4,5 +4,5 @@ import Roles from './roles';
import System from './system';
import Setup from './setup';
import Set from './set';
-
-export { Roles, System, Setup, Set };
\ No newline at end of file
+import Cacl from './cacl'
+export { Roles, System, Setup, Set,Cacl }
\ No newline at end of file
diff --git a/web/client/src/sections/install/containers/interrupt.jsx b/web/client/src/sections/install/containers/interrupt.jsx
new file mode 100644
index 0000000..7ca3832
--- /dev/null
+++ b/web/client/src/sections/install/containers/interrupt.jsx
@@ -0,0 +1,266 @@
+'use strict';
+
+import React, { useRef, useState, useEffect } from 'react';
+import { connect } from 'react-redux';
+import { Row, Col, Button, Input, Table, Card, Switch, Popconfirm, Tag, Notification, Form } from '@douyinfe/semi-ui';
+import InterruptModal from '../components/InterruptModal';
+import '../style.less'
+
+
+const Interrupt = (props) => {
+ const {dispatch, abnParam,structId,factorId,actions,sensorId,project} = props
+ const form = useRef()
+ const [selectedRowKeys, setSelectedRowKeys] = useState([])
+ const [filterFunc, setFilterFunc] = useState({})
+ const [modalVisible, setModalVisible] = useState(false)
+ const [ivBatch, setIvBatch] = useState('')
+ const [modalData, setModalData] = useState(null)
+ const filterSet = { sensorName: null, factorName: null }
+ const {install}=actions
+ const onSelectChange = (selectedRowKeys) => {
+ setSelectedRowKeys(selectedRowKeys);
+ }
+ const compareAndEdit = (e, record) => {
+ setModalVisible(true)
+ setModalData(record)
+ }
+ const modalCancel = e => {
+ setModalVisible(false)
+ }
+ const removeItem = e => {
+ dispatch(install.deleteAbnParams(e)).then(res=>{
+ if(res.success){
+ const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',')
+ dispatch(install.getAbnParamList({ sensorId:id}))
+ }
+ })
+ }
+ const handleInputChange = e => {
+ if (e!= null) {
+ const value = e
+ let func = (ep => (s => (s.sensorName).search(ep) > -1))(value);
+ filterSet.sensorName = func
+ func = (ep => (s => (s.factorName).search(ep) > -1))(value);
+ filterSet.factorName = func
+ } else {
+ filterSet.sensorName = null
+ filterSet.factorName = null
+ }
+ setFilterFunc(filterSet)
+ }
+ //批量启用or禁用
+ const onSwitchChange = (e) => {
+ if (selectedRowKeys.length != 0) {
+ let ids = selectedRowKeys.join(',')
+ let data = {
+ enabled: e,
+ use: 'switch'
+ }
+ dispatch(install.batchCfgAbnParams(ids,data)).then(res=>{
+ if(res.success){
+ const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',')
+ dispatch(install.getAbnParamList({ sensorId:id }))
+ }
+ })
+
+ }
+ }
+ //批量删除
+ const batchDelete = () => {
+ if (selectedRowKeys.length != 0) {
+ let ids = selectedRowKeys.join(',')
+ dispatch(install.deleteAbnParams(ids)).then(res=>{
+ if(res.success){
+ const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',')
+ dispatch(install.getAbnParamList({ sensorId:id}))
+ }
+ })
+ } else {
+ Notification.warning({
+ content: '您尚未勾选任何参数配置!',
+ duration: 3,
+ })
+ }
+ }
+ //批量保存
+ const batchSave = () => {
+ form.current.validate().then(_ => {
+ let dataSource = abnParam.filter(a => a.abnType == 1 && a.factorId == factorId)
+ if (selectedRowKeys.length != 0) {
+ let ids = selectedRowKeys.join(',');
+ let data = {
+ paramJson: { "thr_int": ivBatch },
+ use: 'notSwitch',
+ }
+
+ dispatch(install.batchCfgAbnParams(ids,data)).then(res=>{
+ if(res.success){
+ const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',')
+ dispatch(install.getAbnParamList({ sensorId:id }))
+ }
+ })
+ } else if (dataSource.length != 0) {
+ Notification.warning({
+ content: '您尚未勾选任何参数配置!',
+ duration: 3,
+ })
+ }
+ })
+ }
+ const batchInterrupt = (value) => {
+ setIvBatch(value)
+ }
+ const checkInterger = (rule, val) => {
+ // if(val){
+ const strRegex = /^[1-9]*[1-9][0-9]*$/
+ if (strRegex.test(val)) {
+ return true
+ }
+ return false
+ // }
+
+ };
+ let dataSource = abnParam.filter(a => a.abnType == 1 && a.factorId == factorId)
+ const rowSelection = {
+ selectedRowKeys: selectedRowKeys,
+ onChange: onSelectChange,
+ };
+ let filterData = dataSource
+ let flag = false
+ let keyArr = []
+ let tmpds = []
+ let dataPush = []
+ Object.keys(filterFunc).forEach(key => {
+ const filter = filterFunc[key]
+ filterData = dataSource
+ if (filter != null) {
+ flag = true
+ filterData = filterData.filter(filter)
+ if (filterData.length > 0) {
+ filterData.map(s => {
+ if (keyArr.indexOf(s.id) == -1) {
+ keyArr.push(s.id)
+ dataPush.push(s)
+ }
+ })
+ }
+ }
+ })
+ tmpds = flag ? dataPush.sort((a, b) => a.id - b.id) : dataSource.sort((a, b) => a.id - b.id);
+ let columns = [
+ {
+ title: "测点位置",
+ dataIndex: "sensorName",
+ key: "stationName",
+ width: '20%',
+ },
+ {
+ title: "监测因素",
+ dataIndex: "factorName",
+ key: "factorName",
+ width: '20%',
+ },
+ {
+ title: "中断阈值",
+ dataIndex: "params",
+ key: "params",
+ width: '20%',
+ render: text => text.thr_int
+ // render: text => {
+ // return ()
+ // }
+ },
+ {
+ title: "启用状态",
+ dataIndex: "enabled",
+ key: "enabled",
+ width: '20%',
+ render: (text, record) => (
+ record.enabled ? 已启用 : 已禁用
+ )
+ },
+ {
+ title: "操作",
+ key: "action",
+ width: '20%',
+ render: (text, record) => (
+
+
+
+ { removeItem(record.id) }}>
+
+
+
+ ),
+ }
+ ]
+
+ return (
+
+
+ {modalVisible ? : ''}
+
+ )
+}
+
+
+
+
+
+
+
+
+
+
+
+
+function mapStateToProps(state) {
+ const { abnParam,global } = state;
+ // let isRequesting = abnParamState?.isRequesting;
+
+ return {
+ isRequesting: false,//请求状态
+ abnParam: abnParam?.data || [],
+ actions:global.actions,
+ }
+}
+export default connect(mapStateToProps)(Interrupt)
diff --git a/web/client/src/sections/install/containers/trend.jsx b/web/client/src/sections/install/containers/trend.jsx
new file mode 100644
index 0000000..3fb64a5
--- /dev/null
+++ b/web/client/src/sections/install/containers/trend.jsx
@@ -0,0 +1,425 @@
+'use strict';
+
+import React, { Component, useRef,useState } from 'react';
+import { connect } from 'react-redux';
+import { Row, Col, Button, Input, Table, Card, Switch, Popconfirm, Tag, Notification, Form, Select } from '@douyinfe/semi-ui'
+import {IconPlus} from "@douyinfe/semi-icons"
+import TrendModal from '../components/TrendModal';
+
+
+
+const Trend = (props) => {
+ const { abnParam, factorId,itName,actions,dispatch,sensorId,project } = props
+ const form = useRef()
+ const [selectedRowKeys, setSelectedRowKeys] = useState([])
+ const [filterFunc, setFilterFunc] = useState({})
+ const [modalVisible, setModalVisible] = useState(false)
+ const [showBatchConfig, setShowBatchConfig] = useState(false)
+ const [timeRange, setTimeRange] = useState('')
+ const [bvBatch, setBvBatch] = useState('')
+ const [winSize, setWinSize] = useState('')
+ const [reCoef, setReCoef] = useState('')
+ const [deValue, setDeValue] = useState('')
+ const [graPoint, setGraPoint] = useState('')
+ const [graValue, setGraValue] = useState('')
+ const [modalData, setModalData] = useState(null)
+ const filterSet = { sensorName: null, factorName: null }
+ const {install}=actions
+ const onSelectChange = (selectedRowKeys) => {
+ setSelectedRowKeys(selectedRowKeys)
+ }
+ const compareAndEdit = (e, record) => {
+ setModalVisible(true)
+ setModalData(record)
+ }
+ const modalCancel = e => {
+ setModalVisible(false)
+ };
+ const removeItem = e => {
+ dispatch(install.deleteAbnParams(e)).then(res=>{
+ if(res.success){
+ const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',')
+ dispatch(install.getAbnParamList({ sensorId:id}))
+ }
+ })
+ }
+ const handleInputChange = e => {
+ if (e != null) {
+ const value = e
+ let func = (ep => (s => (s.sensorName).search(ep) > -1))(value)
+ filterSet.sensorName = func
+ func = (ep => (s => (s.factorName).search(ep) > -1))(value)
+ filterSet.factorName = func
+ } else {
+ filterSet.sensorName = null
+ filterSet.factorName = null
+ }
+ setFilterFunc(filterSet)
+ }
+ //批量启用or禁用
+ const onSwitchChange = (e) => {
+ if (selectedRowKeys.length != 0) {
+ let ids = selectedRowKeys.join(',');
+ let data = {
+ enabled: e,
+ use: 'switch'
+ };
+ //启用和禁用接口
+ dispatch(install.batchCfgAbnParams(ids,data)).then(res=>{
+ if(res.success){
+ const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',')
+ dispatch(install.getAbnParamList({ sensorId:id}))
+ }
+ })
+ }
+ }
+ //批量删除
+ const batchDelete = () => {
+ if (selectedRowKeys.length != 0) {
+ let ids = selectedRowKeys.join(',');
+ //删除接口
+ dispatch(install.deleteAbnParams(ids)).then(res=>{
+ if(res.success){
+ const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',')
+ dispatch(install.getAbnParamList({ sensorId:id }))
+ }
+ })
+ } else {
+ Notification.warning({
+ content: '您尚未勾选任何参数配置!',
+ duration: 3,
+ }) }
+ }
+ //批量保存
+ const batchSave = () => {
+ form.current.validate().then(value=>{
+ let dataSource = abnParam.filter(a => a.abnType == 3 && a.factorId == factorId)
+ if (selectedRowKeys.length != 0) {
+ let ids = selectedRowKeys.join(',')
+ let data = {
+ paramJson: {
+ "thr_der": deValue, //导数阈值
+ "win_avg": reCoef,//滑动均值
+ "win_med": winSize,//滑动中值
+ "thr_burr": bvBatch,//毛刺阈值
+ "thr_grad": graValue,//渐变阈值
+ "win_grad": graPoint,//渐变点个数
+ "days_Last": timeRange//分析时长
+ },
+ use: 'notSwitch',
+ };
+
+ dispatch(install.batchCfgAbnParams(ids,data)).then(res=>{
+ if(res.success){
+ const id=[...sensorId?.map(item=>project+'-'+item), -1].join(',')
+ dispatch(install.getAbnParamList({ sensorId:id}))
+ }
+ })
+ } else if (dataSource.length != 0) {
+ Notification.warning({
+ content: '您尚未勾选任何参数配置!',
+ duration: 3,
+ })
+ }
+ })
+ }
+ const iconClick = () => {
+ if (!showBatchConfig) {
+ setShowBatchConfig(true);
+ } else {
+ setShowBatchConfig(false);
+ }
+ }
+ const rangeChange = (value) => {
+ setTimeRange(value)
+ }
+ const bvValueBatch = (value) => {
+ setBvBatch(value)
+ }
+ const winSizeBatch = (value) => {
+ setWinSize(value)
+ }
+ const reCoefBatch = (value) => {
+ setReCoef(value)
+ }
+ const deValueBatch = (value) => {
+ setDeValue(value)
+ }
+ const graPointBatch = (value) => {
+ setGraPoint(value)
+ }
+ const graValueBatch = (value) => {
+ setGraValue(value)
+ }
+ const checkInterger = (rule, val) => {
+ // if(val){
+ const strRegex = /^[1-9]*[1-9][0-9]*$/
+ if (strRegex.test(val)) {
+ return true
+ }
+ return false
+ // }
+
+ };
+ const checkPoint = (rule, value) => {
+ const pattern = /^[1-9]*[1-9][0-9]*$/;
+ if (pattern.test(value) && value != 1) {
+ return true
+ }
+ return false
+ }
+ const checkNumber = (rule, val) => {
+ const strRegex = /^-?\d+(\.\d+)?$/
+ if (strRegex.test(val)) {
+ return true
+ }
+ return false
+
+
+
+ }
+
+ let dataSource = abnParam.filter(a => a.abnType == 3 && a.factorId == factorId)
+ const rowSelection = {
+ selectedRowKeys: selectedRowKeys,
+ onChange: onSelectChange,
+ };
+ let filterData = dataSource;
+ let flag = false;
+ let keyArr = [];
+ let tmpds = [];
+ let dataPush = [];
+ Object.keys(filterFunc).forEach(key => {
+ const filter = filterFunc[key];
+ filterData = dataSource;
+ if (filter != null) {
+ flag = true;
+ filterData = filterData.filter(filter)
+ if (filterData.length > 0) {
+ filterData.map(s => {
+ if (keyArr.indexOf(s.id) == -1) {
+ keyArr.push(s.id);
+ dataPush.push(s);
+ }
+ })
+ }
+ }
+ })
+ tmpds = flag ? dataPush.sort((a, b) => a.id - b.id) : dataSource.sort((a, b) => a.id - b.id);
+ let columns = [
+ {
+ title: "测点位置",
+ dataIndex: "sensorName",
+ key: "sensorName",
+ width: '9%'
+ },
+ {
+ title: "监测项",
+ dataIndex: "factorName",
+ key: "factorName",
+ width: '10%',
+ render: (text, record) => (
+ record.factorName + "/" + itName
+ ),
+ },
+ {
+ title: "分析时段",
+ dataIndex: "params",
+ key: "params1",
+ width: '9%',
+ render: text => text.days_Last + "个月"
+ },
+ {
+ title: "毛刺阈值",
+ dataIndex: "params",
+ key: "params2",
+ width: '9%',
+ render: text => text.thr_burr
+ },
+ {
+ title: "窗口数",
+ dataIndex: "params",
+ key: "params3",
+ width: '9%',
+ render: text => text.win_med
+ },
+ {
+ title: "回归系数",
+ dataIndex: "params",
+ key: "params4",
+ width: '9%',
+ render: text => text.win_avg
+ },
+ {
+ title: "导数阈值",
+ dataIndex: "params",
+ key: "params5",
+ width: '9%',
+ render: text => text.thr_der
+ },
+ {
+ title: "渐变点个数",
+ dataIndex: "params",
+ key: "params6",
+ width: '9%',
+ render: text => text.win_grad
+ },
+ {
+ title: "渐变阈值",
+ dataIndex: "params",
+ key: "params7",
+ width: '9%',
+ render: text => text.thr_grad
+ },
+ {
+ title: "启用状态",
+ dataIndex: "enabled",
+ key: "enabled",
+ width: '9%',
+ render: (text, record) => (
+ record.enabled ? 已启用 : 已禁用
+ )
+ },
+ {
+ title: "操作",
+ key: "action",
+ width: '9%',
+ render: (text, record) => (
+
+
+
+ { removeItem(record.id) }}>
+
+
+
+ ),
+ }
+ ]
+ return (
+
+
+ {modalVisible ? : ''}
+
+ );
+}
+
+function mapStateToProps(state) {
+ const { abnParam,global } = state
+ // let isRequesting = abnParamState?.isRequesting;
+
+ return {
+ isRequesting: false,//请求状态
+ abnParam: abnParam?.data||[],
+ actions:global.actions
+ }
+}
+export default connect(mapStateToProps)(Trend)
+
diff --git a/web/client/src/sections/install/nav-item.jsx b/web/client/src/sections/install/nav-item.jsx
index ea09e55..fa4eddd 100644
--- a/web/client/src/sections/install/nav-item.jsx
+++ b/web/client/src/sections/install/nav-item.jsx
@@ -25,6 +25,15 @@ export function getNavItem (user, dispatch) {
items: [{
itemKey: 'system', to: '/install/mapping/system', text: '系统映射'
}]
+ },
+ {
+ itemKey: 'data',
+ text: '数据计算',
+ icon: ,
+ to: '/install/data/cacl',
+ items: [{
+ itemKey: 'cacl', to: '/install/data/cacl', text: '异常数据识别'
+ }]
},
// {
// itemKey: 'order ',
diff --git a/web/client/src/sections/install/routes.js b/web/client/src/sections/install/routes.js
index e983d58..c7eae43 100644
--- a/web/client/src/sections/install/routes.js
+++ b/web/client/src/sections/install/routes.js
@@ -1,5 +1,6 @@
+
'use strict';
-import { Roles, System, Setup, Set } from './containers';
+import { Roles, System, Setup, Set,Cacl } from './containers/index.js';
export default [
{
@@ -30,7 +31,17 @@ export default [
component: System,
breadcrumb: '系统映射',
}]
- }, {
+ },{
+ path: '/data',
+ key: 'data',
+ breadcrumb: '数据计算',
+ childRoutes: [{
+ path: '/cacl',
+ key: 'cacl',
+ component: Cacl,
+ breadcrumb: '异常数据识别',
+ }]
+ }, {
path: '/order',
key: 'order',
breadcrumb: '工单管理',
diff --git a/web/client/src/sections/install/style.less b/web/client/src/sections/install/style.less
index aa91f88..3e338a7 100644
--- a/web/client/src/sections/install/style.less
+++ b/web/client/src/sections/install/style.less
@@ -23,4 +23,5 @@
.separator{
display: none;
}
-}
\ No newline at end of file
+}
+
diff --git a/web/client/src/utils/webapi.js b/web/client/src/utils/webapi.js
index 2597668..8d3f82f 100644
--- a/web/client/src/utils/webapi.js
+++ b/web/client/src/utils/webapi.js
@@ -64,7 +64,7 @@ export const ApiTable = {
getVcmpAuth: 'vcmp/auth', // 获取视频平台应用鉴权token
getAlarmVideoExceptionType: 'alarm/video/exceptionType', //查询视频设备类型
- // 数据查询
+ // 数据监测
getDataContinuityType: 'data/continuity/type_list',
getDataContinuity: 'data/continuity',
getDataContinuityDetail: 'data/continuity/{continuityId}/detail',
@@ -174,7 +174,21 @@ export const ApiTable = {
//获取设备信息
getThingMessages: 'getThingMessages',
//下发配置(批量单个)
- distributeConfiguration: 'distributeConfiguration'
+ distributeConfiguration: 'distributeConfiguration',
+ //监测数据相关接口
+ getProjectAllStructures: "project/allStructures", //获取绑定项目下结构物
+ getFactors:'structure/factors' ,//结构物下的监测因素
+ getSensors:'structure/factors/sensors', //检测因素下的设备数据
+ getMonitorData:'structure/factors/sensors/data',//获取监测数据
+ //异常数据配置相关接口
+ getAbnMethods:'abnormal/methods' ,//获取计算方式
+ getAbnParamList:'struct/abnormal/params',//获取异常参数配置
+ addAbnParamList:'struct/abnormal/params',//新增异常参数配置
+ batchSwitch:'batch/switch/{ids}',//批量启用or禁用
+ deleteAbnParams: 'delete/abnormal/params/{id}',//删除配置
+ editAbnParams: 'edit/abnormal/params/{id}',//修改配置
+ getAbnTaskResult: 'struct/{structId}/abnTask/result/{start}/{end}',//
+
};
// 项企的接口
diff --git a/web/package.json b/web/package.json
index 57d43b7..ba23dff 100644
--- a/web/package.json
+++ b/web/package.json
@@ -66,6 +66,9 @@
"file-saver": "^2.0.5",
"fs-attachment": "^1.0.0",
"fs-web-server-scaffold": "^1.0.6",
+ "g2": "^2.3.13",
+ "g2-plugin-slider": "^1.2.1",
+ "g2-react": "^1.3.2",
"koa-better-http-proxy": "^0.2.5",
"koa-proxy": "^1.0.0-alpha.3",
"koa-view": "^2.1.4",