From 868e3568d7e8d4073b72755c6122b4f859a29e3a Mon Sep 17 00:00:00 2001 From: dashixiong Date: Tue, 24 Mar 2026 15:01:50 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B5=8B=E9=87=8F=E7=82=B9=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ktg-admin/src/main/resources/application.yml | 10 + .../com/ktg/mes/md/config/InfluxDbConfig.java | 72 ++++ .../controller/MdMeasurePointController.java | 118 +++++++ .../com/ktg/mes/md/domain/MdMeasurePoint.java | 209 ++++++++++++ .../md/domain/MeasurePointHistoryItem.java | 32 ++ .../mes/md/mapper/MdMeasurePointMapper.java | 25 ++ .../md/service/IMdMeasurePointService.java | 30 ++ .../impl/MdMeasurePointInfluxService.java | 316 ++++++++++++++++++ .../impl/MdMeasurePointServiceImpl.java | 119 +++++++ .../mapper/md/MdMeasurePointMapper.xml | 149 +++++++++ 10 files changed, 1080 insertions(+) create mode 100644 ktg-mes/src/main/java/com/ktg/mes/md/config/InfluxDbConfig.java create mode 100644 ktg-mes/src/main/java/com/ktg/mes/md/controller/MdMeasurePointController.java create mode 100644 ktg-mes/src/main/java/com/ktg/mes/md/domain/MdMeasurePoint.java create mode 100644 ktg-mes/src/main/java/com/ktg/mes/md/domain/MeasurePointHistoryItem.java create mode 100644 ktg-mes/src/main/java/com/ktg/mes/md/mapper/MdMeasurePointMapper.java create mode 100644 ktg-mes/src/main/java/com/ktg/mes/md/service/IMdMeasurePointService.java create mode 100644 ktg-mes/src/main/java/com/ktg/mes/md/service/impl/MdMeasurePointInfluxService.java create mode 100644 ktg-mes/src/main/java/com/ktg/mes/md/service/impl/MdMeasurePointServiceImpl.java create mode 100644 ktg-mes/src/main/resources/mapper/md/MdMeasurePointMapper.xml diff --git a/ktg-admin/src/main/resources/application.yml b/ktg-admin/src/main/resources/application.yml index d826712..d498460 100644 --- a/ktg-admin/src/main/resources/application.yml +++ b/ktg-admin/src/main/resources/application.yml @@ -139,3 +139,13 @@ minio: accessKey: minioadmin secretKey: minioadmin bucketName: lx-mes + +influxdb: + enabled: true + url: http://146.56.245.38:8086 + org: influxdb + bucket: mes + token: -okZX8Re-PY3tEPV1_e7w-u9bvZlGnoMwpIEKjTQnpaZ3GyVJM9U72WLb5hNAQIDlCNU0NlfgoFQA__mhyUAxw== + measurement: device_point + timeout-millis: 10000 + diff --git a/ktg-mes/src/main/java/com/ktg/mes/md/config/InfluxDbConfig.java b/ktg-mes/src/main/java/com/ktg/mes/md/config/InfluxDbConfig.java new file mode 100644 index 0000000..85f933a --- /dev/null +++ b/ktg-mes/src/main/java/com/ktg/mes/md/config/InfluxDbConfig.java @@ -0,0 +1,72 @@ +package com.ktg.mes.md.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Component +@ConfigurationProperties(prefix = "influxdb") +public class InfluxDbConfig { + private boolean enabled; + private String url; + private String org; + private String bucket; + private String token; + private String measurement; + private Integer timeoutMillis = 10000; + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getOrg() { + return org; + } + + public void setOrg(String org) { + this.org = org; + } + + public String getBucket() { + return bucket; + } + + public void setBucket(String bucket) { + this.bucket = bucket; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + + public String getMeasurement() { + return measurement; + } + + public void setMeasurement(String measurement) { + this.measurement = measurement; + } + + public Integer getTimeoutMillis() { + return timeoutMillis; + } + + public void setTimeoutMillis(Integer timeoutMillis) { + this.timeoutMillis = timeoutMillis; + } +} diff --git a/ktg-mes/src/main/java/com/ktg/mes/md/controller/MdMeasurePointController.java b/ktg-mes/src/main/java/com/ktg/mes/md/controller/MdMeasurePointController.java new file mode 100644 index 0000000..1c8a789 --- /dev/null +++ b/ktg-mes/src/main/java/com/ktg/mes/md/controller/MdMeasurePointController.java @@ -0,0 +1,118 @@ +package com.ktg.mes.md.controller; + +import com.ktg.common.annotation.Log; +import com.ktg.common.constant.UserConstants; +import com.ktg.common.core.controller.BaseController; +import com.ktg.common.core.domain.AjaxResult; +import com.ktg.common.core.page.TableDataInfo; +import com.ktg.common.enums.BusinessType; +import com.ktg.common.utils.poi.ExcelUtil; +import com.ktg.mes.md.domain.MdMeasurePoint; +import com.ktg.mes.md.domain.MeasurePointHistoryItem; +import com.ktg.mes.md.service.IMdMeasurePointService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletResponse; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/mes/md/measurepoint") +public class MdMeasurePointController extends BaseController { + @Autowired + private IMdMeasurePointService mdMeasurePointService; + + @GetMapping("/list") + public TableDataInfo list(MdMeasurePoint mdMeasurePoint) { + startPage(); + List list = mdMeasurePointService.selectMdMeasurePointList(mdMeasurePoint); + return getDataTable(list); + } + + @GetMapping("/listAll") + public AjaxResult listAll() { + MdMeasurePoint query = new MdMeasurePoint(); + query.setEnableFlag("Y"); + return AjaxResult.success(mdMeasurePointService.selectMdMeasurePointList(query)); + } + + @PreAuthorize("@ss.hasPermi('mes:md:measurepoint:export')") + @Log(title = "测量点", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, MdMeasurePoint mdMeasurePoint) { + List list = mdMeasurePointService.selectMdMeasurePointList(mdMeasurePoint); + ExcelUtil util = new ExcelUtil(MdMeasurePoint.class); + util.exportExcel(response, list, "测量点数据"); + } + + @PreAuthorize("@ss.hasPermi('mes:md:measurepoint:query')") + @GetMapping("/{pointId}") + public AjaxResult getInfo(@PathVariable("pointId") Long pointId) { + return AjaxResult.success(mdMeasurePointService.selectMdMeasurePointByPointId(pointId)); + } + + @PreAuthorize("@ss.hasPermi('mes:md:measurepoint:query')") + @GetMapping("/latest") + public AjaxResult latest(@RequestParam("pointCode") String pointCode) { + return AjaxResult.success(mdMeasurePointService.queryLatestValue(pointCode)); + } + + @PreAuthorize("@ss.hasPermi('mes:md:measurepoint:query')") + @GetMapping("/history") + public AjaxResult history(@RequestParam("pointCode") String pointCode, + @RequestParam(value = "range", required = false, defaultValue = "24h") String range, + @RequestParam(value = "interval", required = false, defaultValue = "5m") String interval, + @RequestParam(value = "startTime", required = false) String startTime, + @RequestParam(value = "endTime", required = false) String endTime) { + List history = mdMeasurePointService.queryHistory(pointCode, range, interval, startTime, endTime); + MdMeasurePoint point = mdMeasurePointService.queryLatestValue(pointCode); + Map result = new LinkedHashMap(); + result.put("point", point); + result.put("records", history); + return AjaxResult.success(result); + } + + @PreAuthorize("@ss.hasPermi('mes:md:measurepoint:add')") + @Log(title = "测量点", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody MdMeasurePoint mdMeasurePoint) { + if (UserConstants.NOT_UNIQUE.equals(mdMeasurePointService.checkPointCodeUnique(mdMeasurePoint))) { + return AjaxResult.error("测量点编码已存在!"); + } + if (UserConstants.NOT_UNIQUE.equals(mdMeasurePointService.checkPointNameUnique(mdMeasurePoint))) { + return AjaxResult.error("测量点名称已存在!"); + } + return toAjax(mdMeasurePointService.insertMdMeasurePoint(mdMeasurePoint)); + } + + @PreAuthorize("@ss.hasPermi('mes:md:measurepoint:edit')") + @Log(title = "测量点", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody MdMeasurePoint mdMeasurePoint) { + if (UserConstants.NOT_UNIQUE.equals(mdMeasurePointService.checkPointCodeUnique(mdMeasurePoint))) { + return AjaxResult.error("测量点编码已存在!"); + } + if (UserConstants.NOT_UNIQUE.equals(mdMeasurePointService.checkPointNameUnique(mdMeasurePoint))) { + return AjaxResult.error("测量点名称已存在!"); + } + return toAjax(mdMeasurePointService.updateMdMeasurePoint(mdMeasurePoint)); + } + + @PreAuthorize("@ss.hasPermi('mes:md:measurepoint:remove')") + @Log(title = "测量点", businessType = BusinessType.DELETE) + @DeleteMapping("/{pointIds}") + public AjaxResult remove(@PathVariable Long[] pointIds) { + return toAjax(mdMeasurePointService.deleteMdMeasurePointByPointIds(pointIds)); + } +} diff --git a/ktg-mes/src/main/java/com/ktg/mes/md/domain/MdMeasurePoint.java b/ktg-mes/src/main/java/com/ktg/mes/md/domain/MdMeasurePoint.java new file mode 100644 index 0000000..02fa6a7 --- /dev/null +++ b/ktg-mes/src/main/java/com/ktg/mes/md/domain/MdMeasurePoint.java @@ -0,0 +1,209 @@ +package com.ktg.mes.md.domain; + +import com.ktg.common.annotation.Excel; +import com.ktg.common.core.domain.BaseEntity; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +public class MdMeasurePoint extends BaseEntity { + private static final long serialVersionUID = 1L; + + private Long pointId; + + @Excel(name = "测量点编码") + private String pointCode; + + @Excel(name = "测量点名称") + private String pointName; + + private Long workshopId; + + @Excel(name = "车间编码") + private String workshopCode; + + @Excel(name = "车间名称") + private String workshopName; + + @Excel(name = "设备编码") + private String deviceCode; + + @Excel(name = "节点编码") + private String nodeCode; + + @Excel(name = "字段名") + private String fieldKey; + + @Excel(name = "标签条件") + private String tagJson; + + @Excel(name = "单位") + private String unit; + + @Excel(name = "保留位数") + private Integer precisionDigit; + + @Excel(name = "排序") + private Integer sortNum; + + @Excel(name = "是否启用") + private String enableFlag; + + private String latestValue; + + private String latestTime; + + public Long getPointId() { + return pointId; + } + + public void setPointId(Long pointId) { + this.pointId = pointId; + } + + public String getPointCode() { + return pointCode; + } + + public void setPointCode(String pointCode) { + this.pointCode = pointCode; + } + + public String getPointName() { + return pointName; + } + + public void setPointName(String pointName) { + this.pointName = pointName; + } + + public Long getWorkshopId() { + return workshopId; + } + + public void setWorkshopId(Long workshopId) { + this.workshopId = workshopId; + } + + public String getWorkshopCode() { + return workshopCode; + } + + public void setWorkshopCode(String workshopCode) { + this.workshopCode = workshopCode; + } + + public String getWorkshopName() { + return workshopName; + } + + public void setWorkshopName(String workshopName) { + this.workshopName = workshopName; + } + + public String getDeviceCode() { + return deviceCode; + } + + public void setDeviceCode(String deviceCode) { + this.deviceCode = deviceCode; + } + + public String getNodeCode() { + return nodeCode; + } + + public void setNodeCode(String nodeCode) { + this.nodeCode = nodeCode; + } + + public String getFieldKey() { + return fieldKey; + } + + public void setFieldKey(String fieldKey) { + this.fieldKey = fieldKey; + } + + public String getTagJson() { + return tagJson; + } + + public void setTagJson(String tagJson) { + this.tagJson = tagJson; + } + + public String getUnit() { + return unit; + } + + public void setUnit(String unit) { + this.unit = unit; + } + + public Integer getPrecisionDigit() { + return precisionDigit; + } + + public void setPrecisionDigit(Integer precisionDigit) { + this.precisionDigit = precisionDigit; + } + + public Integer getSortNum() { + return sortNum; + } + + public void setSortNum(Integer sortNum) { + this.sortNum = sortNum; + } + + public String getEnableFlag() { + return enableFlag; + } + + public void setEnableFlag(String enableFlag) { + this.enableFlag = enableFlag; + } + + public String getLatestValue() { + return latestValue; + } + + public void setLatestValue(String latestValue) { + this.latestValue = latestValue; + } + + public String getLatestTime() { + return latestTime; + } + + public void setLatestTime(String latestTime) { + this.latestTime = latestTime; + } + + @Override + public String toString() { + return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE) + .append("pointId", getPointId()) + .append("pointCode", getPointCode()) + .append("pointName", getPointName()) + .append("workshopId", getWorkshopId()) + .append("workshopCode", getWorkshopCode()) + .append("workshopName", getWorkshopName()) + .append("deviceCode", getDeviceCode()) + .append("nodeCode", getNodeCode()) + .append("fieldKey", getFieldKey()) + .append("tagJson", getTagJson()) + .append("unit", getUnit()) + .append("precisionDigit", getPrecisionDigit()) + .append("sortNum", getSortNum()) + .append("enableFlag", getEnableFlag()) + .append("latestValue", getLatestValue()) + .append("latestTime", getLatestTime()) + .append("remark", getRemark()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .toString(); + } +} diff --git a/ktg-mes/src/main/java/com/ktg/mes/md/domain/MeasurePointHistoryItem.java b/ktg-mes/src/main/java/com/ktg/mes/md/domain/MeasurePointHistoryItem.java new file mode 100644 index 0000000..03c7bed --- /dev/null +++ b/ktg-mes/src/main/java/com/ktg/mes/md/domain/MeasurePointHistoryItem.java @@ -0,0 +1,32 @@ +package com.ktg.mes.md.domain; + +import java.math.BigDecimal; + +public class MeasurePointHistoryItem { + private String time; + private BigDecimal value; + + public MeasurePointHistoryItem() { + } + + public MeasurePointHistoryItem(String time, BigDecimal value) { + this.time = time; + this.value = value; + } + + public String getTime() { + return time; + } + + public void setTime(String time) { + this.time = time; + } + + public BigDecimal getValue() { + return value; + } + + public void setValue(BigDecimal value) { + this.value = value; + } +} diff --git a/ktg-mes/src/main/java/com/ktg/mes/md/mapper/MdMeasurePointMapper.java b/ktg-mes/src/main/java/com/ktg/mes/md/mapper/MdMeasurePointMapper.java new file mode 100644 index 0000000..0262f41 --- /dev/null +++ b/ktg-mes/src/main/java/com/ktg/mes/md/mapper/MdMeasurePointMapper.java @@ -0,0 +1,25 @@ +package com.ktg.mes.md.mapper; + +import com.ktg.mes.md.domain.MdMeasurePoint; + +import java.util.List; + +public interface MdMeasurePointMapper { + MdMeasurePoint selectMdMeasurePointByPointId(Long pointId); + + MdMeasurePoint selectMdMeasurePointByPointCode(String pointCode); + + List selectMdMeasurePointList(MdMeasurePoint mdMeasurePoint); + + MdMeasurePoint checkPointCodeUnique(MdMeasurePoint mdMeasurePoint); + + MdMeasurePoint checkPointNameUnique(MdMeasurePoint mdMeasurePoint); + + int insertMdMeasurePoint(MdMeasurePoint mdMeasurePoint); + + int updateMdMeasurePoint(MdMeasurePoint mdMeasurePoint); + + int deleteMdMeasurePointByPointId(Long pointId); + + int deleteMdMeasurePointByPointIds(Long[] pointIds); +} diff --git a/ktg-mes/src/main/java/com/ktg/mes/md/service/IMdMeasurePointService.java b/ktg-mes/src/main/java/com/ktg/mes/md/service/IMdMeasurePointService.java new file mode 100644 index 0000000..453f211 --- /dev/null +++ b/ktg-mes/src/main/java/com/ktg/mes/md/service/IMdMeasurePointService.java @@ -0,0 +1,30 @@ +package com.ktg.mes.md.service; + +import com.ktg.mes.md.domain.MdMeasurePoint; +import com.ktg.mes.md.domain.MeasurePointHistoryItem; + +import java.util.List; + +public interface IMdMeasurePointService { + MdMeasurePoint selectMdMeasurePointByPointId(Long pointId); + + MdMeasurePoint selectMdMeasurePointByPointCode(String pointCode); + + List selectMdMeasurePointList(MdMeasurePoint mdMeasurePoint); + + String checkPointCodeUnique(MdMeasurePoint mdMeasurePoint); + + String checkPointNameUnique(MdMeasurePoint mdMeasurePoint); + + int insertMdMeasurePoint(MdMeasurePoint mdMeasurePoint); + + int updateMdMeasurePoint(MdMeasurePoint mdMeasurePoint); + + int deleteMdMeasurePointByPointIds(Long[] pointIds); + + int deleteMdMeasurePointByPointId(Long pointId); + + MdMeasurePoint queryLatestValue(String pointCode); + + List queryHistory(String pointCode, String range, String interval, String startTime, String endTime); +} diff --git a/ktg-mes/src/main/java/com/ktg/mes/md/service/impl/MdMeasurePointInfluxService.java b/ktg-mes/src/main/java/com/ktg/mes/md/service/impl/MdMeasurePointInfluxService.java new file mode 100644 index 0000000..31962ec --- /dev/null +++ b/ktg-mes/src/main/java/com/ktg/mes/md/service/impl/MdMeasurePointInfluxService.java @@ -0,0 +1,316 @@ +package com.ktg.mes.md.service.impl; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.ktg.mes.md.config.InfluxDbConfig; +import com.ktg.mes.md.domain.MdMeasurePoint; +import com.ktg.mes.md.domain.MeasurePointHistoryItem; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +@Service +public class MdMeasurePointInfluxService { + private static final Logger log = LoggerFactory.getLogger(MdMeasurePointInfluxService.class); + + @Autowired + private InfluxDbConfig influxDbConfig; + + public MdMeasurePoint fillLatestValue(MdMeasurePoint point) { + if (point == null) { + return null; + } + List> rows = executeFlux(buildLatestFlux(point)); + if (!rows.isEmpty()) { + Map row = rows.get(0); + point.setLatestValue(formatValue(row.get("_value"), point.getPrecisionDigit())); + point.setLatestTime(row.get("_time")); + } + return point; + } + + public List queryHistory(MdMeasurePoint point, String range, String interval, String startTime, String endTime) { + List result = new ArrayList(); + if (point == null) { + return result; + } + List> rows = executeFlux(buildHistoryFlux(point, range, interval, startTime, endTime)); + for (Map row : rows) { + if (StringUtils.isBlank(row.get("_time")) || StringUtils.isBlank(row.get("_value"))) { + continue; + } + try { + result.add(new MeasurePointHistoryItem(row.get("_time"), new BigDecimal(row.get("_value")))); + } catch (Exception ignored) { + } + } + return result; + } + + private String buildLatestFlux(MdMeasurePoint point) { + StringBuilder builder = new StringBuilder(); + builder.append("from(bucket: \"").append(escapeFlux(influxDbConfig.getBucket())).append("\")"); + builder.append("\n |> range(start: -30d)"); + builder.append("\n |> filter(fn: (r) => r._measurement == \"").append(escapeFlux(influxDbConfig.getMeasurement())).append("\")"); + appendFilter(builder, point); + builder.append("\n |> keep(columns: [\"_time\", \"_value\"])"); + builder.append("\n |> sort(columns:[\"_time\"], desc: true)"); + builder.append("\n |> limit(n: 1)"); + return builder.toString(); + } + + private String buildHistoryFlux(MdMeasurePoint point, String range, String interval, String startTime, String endTime) { + StringBuilder builder = new StringBuilder(); + builder.append("from(bucket: \"").append(escapeFlux(influxDbConfig.getBucket())).append("\")"); + if (StringUtils.isNotBlank(startTime) && StringUtils.isNotBlank(endTime)) { + builder.append("\n |> range(start: time(v: \"").append(escapeFlux(normalizeFluxTime(startTime))).append("\"), stop: time(v: \"").append(escapeFlux(normalizeFluxTime(endTime))).append("\"))"); + } else { + builder.append("\n |> range(start: ").append(normalizeRange(range)).append(")"); + } + builder.append("\n |> filter(fn: (r) => r._measurement == \"").append(escapeFlux(influxDbConfig.getMeasurement())).append("\")"); + appendFilter(builder, point); + if (StringUtils.isNotBlank(interval)) { + builder.append("\n |> aggregateWindow(every: ").append(interval).append(", fn: mean, createEmpty: false)"); + } + builder.append("\n |> keep(columns: [\"_time\", \"_value\"])"); + builder.append("\n |> sort(columns:[\"_time\"], desc: false)"); + return builder.toString(); + } + + private void appendFilter(StringBuilder builder, MdMeasurePoint point) { + appendFieldFilter(builder, point.getFieldKey()); + Map tags = buildTagMap(point); + for (Map.Entry entry : tags.entrySet()) { + if (StringUtils.isBlank(entry.getKey()) || StringUtils.isBlank(entry.getValue())) { + continue; + } + appendTagFilter(builder, entry.getKey(), entry.getValue()); + } + } + + private Map buildTagMap(MdMeasurePoint point) { + Map tags = new LinkedHashMap(); + if (StringUtils.isNotBlank(point.getTagJson())) { + try { + JSONObject jsonObject = JSON.parseObject(point.getTagJson()); + for (String key : jsonObject.keySet()) { + Object value = jsonObject.get(key); + if (value != null) { + tags.put(key, String.valueOf(value)); + } + } + } catch (Exception ignored) { + } + } + if (tags.isEmpty()) { + if (StringUtils.isNotBlank(point.getDeviceCode())) { + tags.put("device_id", point.getDeviceCode()); + } + if (StringUtils.isNotBlank(point.getNodeCode())) { + tags.put("point_code", point.getNodeCode()); + } + } + return tags; + } + + private void appendFieldFilter(StringBuilder builder, String fieldKey) { + builder.append("\n |> filter(fn: (r) => r._field == \"value\")"); + } + + private void appendTagFilter(StringBuilder builder, String tagKeyExpression, String tagValue) { + builder.append("\n |> filter(fn: (r) => r[\"").append(escapeFlux(tagKeyExpression)).append("\"] == \"") + .append(escapeFlux(tagValue)).append("\")"); + } + + private List> executeFlux(String flux) { + List> rows = new ArrayList>(); + if (!influxDbConfig.isEnabled() + || StringUtils.isBlank(influxDbConfig.getUrl()) + || StringUtils.isBlank(influxDbConfig.getOrg()) + || StringUtils.isBlank(influxDbConfig.getBucket()) + || StringUtils.isBlank(influxDbConfig.getToken())) { + log.warn("InfluxDB query skipped because configuration is incomplete or disabled. enabled={}, url={}, org={}, bucket={}", + influxDbConfig.isEnabled(), + influxDbConfig.getUrl(), + influxDbConfig.getOrg(), + influxDbConfig.getBucket()); + return rows; + } + + HttpURLConnection connection = null; + try { + String url = influxDbConfig.getUrl(); + if (url.endsWith("/")) { + url = url.substring(0, url.length() - 1); + } + URL queryUrl = new URL(url + "/api/v2/query?org=" + URLEncoder.encode(influxDbConfig.getOrg(), "UTF-8")); + connection = (HttpURLConnection) queryUrl.openConnection(); + connection.setRequestMethod("POST"); + connection.setConnectTimeout(influxDbConfig.getTimeoutMillis()); + connection.setReadTimeout(influxDbConfig.getTimeoutMillis()); + connection.setDoOutput(true); + connection.setRequestProperty("Authorization", "Token " + influxDbConfig.getToken()); + connection.setRequestProperty("Content-Type", "application/vnd.flux"); + connection.setRequestProperty("Accept", "application/csv"); + + try (OutputStream outputStream = connection.getOutputStream()) { + outputStream.write(flux.getBytes(StandardCharsets.UTF_8)); + } + + int code = connection.getResponseCode(); + InputStream inputStream = code >= 200 && code < 300 ? connection.getInputStream() : connection.getErrorStream(); + if (inputStream == null) { + log.warn("InfluxDB query returned empty response stream. httpCode={}, flux={}", code, flux); + return rows; + } + String csv = readAll(inputStream); + if (code < 200 || code >= 300) { + log.warn("InfluxDB query failed. httpCode={}, response={}, flux={}", code, csv, flux); + return rows; + } + return parseCsv(csv); + } catch (Exception e) { + log.warn("InfluxDB query exception. message={}, flux={}", e.getMessage(), flux, e); + return rows; + } finally { + if (connection != null) { + connection.disconnect(); + } + } + } + + private List> parseCsv(String csv) { + List> list = new ArrayList>(); + if (StringUtils.isBlank(csv)) { + return list; + } + BufferedReader reader = new BufferedReader(new InputStreamReader(new java.io.ByteArrayInputStream(csv.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8)); + String headerLine = null; + try { + String line; + while ((line = reader.readLine()) != null) { + if (StringUtils.isBlank(line) || line.startsWith("#")) { + continue; + } + if (headerLine == null) { + headerLine = line; + continue; + } + List headers = splitCsvLine(headerLine); + List values = splitCsvLine(line); + Map row = new LinkedHashMap(); + for (int i = 0; i < headers.size() && i < values.size(); i++) { + row.put(headers.get(i), values.get(i)); + } + if (StringUtils.isNotBlank(row.get("_value"))) { + list.add(row); + } + } + } catch (Exception ignored) { + } + return list; + } + + private List splitCsvLine(String line) { + List values = new ArrayList(); + if (line == null) { + return values; + } + StringBuilder builder = new StringBuilder(); + boolean inQuotes = false; + for (int i = 0; i < line.length(); i++) { + char current = line.charAt(i); + if (current == '"') { + if (inQuotes && i + 1 < line.length() && line.charAt(i + 1) == '"') { + builder.append('"'); + i++; + } else { + inQuotes = !inQuotes; + } + continue; + } + if (current == ',' && !inQuotes) { + values.add(builder.toString()); + builder.setLength(0); + continue; + } + builder.append(current); + } + values.add(builder.toString()); + return values; + } + + private String readAll(InputStream inputStream) throws Exception { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int len; + while ((len = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, len); + } + return new String(outputStream.toByteArray(), StandardCharsets.UTF_8); + } + + private String normalizeRange(String range) { + if (StringUtils.isBlank(range)) { + return "-24h"; + } + return range.startsWith("-") ? range : "-" + range; + } + + private String escapeFlux(String value) { + return value == null ? "" : value.replace("\\", "\\\\").replace("\"", "\\\""); + } + + private String formatValue(String value, Integer precisionDigit) { + if (StringUtils.isBlank(value)) { + return null; + } + try { + BigDecimal decimal = new BigDecimal(value); + if (precisionDigit != null && precisionDigit >= 0) { + decimal = decimal.setScale(precisionDigit, RoundingMode.HALF_UP); + } + return decimal.stripTrailingZeros().toPlainString(); + } catch (Exception ignored) { + return value; + } + } + + private String normalizeFluxTime(String value) { + if (StringUtils.isBlank(value)) { + return value; + } + try { + return OffsetDateTime.parse(value).format(DateTimeFormatter.ISO_OFFSET_DATE_TIME); + } catch (Exception ignored) { + } + try { + return java.time.LocalDateTime.parse(value, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + .atOffset(ZoneOffset.ofHours(8)) + .format(DateTimeFormatter.ISO_OFFSET_DATE_TIME); + } catch (Exception ignored) { + } + return value; + } +} diff --git a/ktg-mes/src/main/java/com/ktg/mes/md/service/impl/MdMeasurePointServiceImpl.java b/ktg-mes/src/main/java/com/ktg/mes/md/service/impl/MdMeasurePointServiceImpl.java new file mode 100644 index 0000000..ad323b4 --- /dev/null +++ b/ktg-mes/src/main/java/com/ktg/mes/md/service/impl/MdMeasurePointServiceImpl.java @@ -0,0 +1,119 @@ +package com.ktg.mes.md.service.impl; + +import com.ktg.common.constant.UserConstants; +import com.ktg.common.utils.DateUtils; +import com.ktg.mes.md.domain.MdMeasurePoint; +import com.ktg.mes.md.domain.MdWorkshop; +import com.ktg.mes.md.domain.MeasurePointHistoryItem; +import com.ktg.mes.md.mapper.MdMeasurePointMapper; +import com.ktg.mes.md.service.IMdMeasurePointService; +import com.ktg.mes.md.service.IMdWorkshopService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +@Service +public class MdMeasurePointServiceImpl implements IMdMeasurePointService { + @Autowired + private MdMeasurePointMapper mdMeasurePointMapper; + + @Autowired + private IMdWorkshopService mdWorkshopService; + + @Autowired + private MdMeasurePointInfluxService mdMeasurePointInfluxService; + + @Override + public MdMeasurePoint selectMdMeasurePointByPointId(Long pointId) { + MdMeasurePoint point = mdMeasurePointMapper.selectMdMeasurePointByPointId(pointId); + return mdMeasurePointInfluxService.fillLatestValue(point); + } + + @Override + public MdMeasurePoint selectMdMeasurePointByPointCode(String pointCode) { + MdMeasurePoint point = mdMeasurePointMapper.selectMdMeasurePointByPointCode(pointCode); + return mdMeasurePointInfluxService.fillLatestValue(point); + } + + @Override + public List selectMdMeasurePointList(MdMeasurePoint mdMeasurePoint) { + List list = mdMeasurePointMapper.selectMdMeasurePointList(mdMeasurePoint); + for (MdMeasurePoint point : list) { + mdMeasurePointInfluxService.fillLatestValue(point); + } + return list; + } + + @Override + public String checkPointCodeUnique(MdMeasurePoint mdMeasurePoint) { + MdMeasurePoint point = mdMeasurePointMapper.checkPointCodeUnique(mdMeasurePoint); + Long pointId = mdMeasurePoint.getPointId() == null ? -1L : mdMeasurePoint.getPointId(); + if (point != null && !pointId.equals(point.getPointId())) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + @Override + public String checkPointNameUnique(MdMeasurePoint mdMeasurePoint) { + MdMeasurePoint point = mdMeasurePointMapper.checkPointNameUnique(mdMeasurePoint); + Long pointId = mdMeasurePoint.getPointId() == null ? -1L : mdMeasurePoint.getPointId(); + if (point != null && !pointId.equals(point.getPointId())) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + @Override + public int insertMdMeasurePoint(MdMeasurePoint mdMeasurePoint) { + fillWorkshopInfo(mdMeasurePoint); + mdMeasurePoint.setCreateTime(DateUtils.getNowDate()); + return mdMeasurePointMapper.insertMdMeasurePoint(mdMeasurePoint); + } + + @Override + public int updateMdMeasurePoint(MdMeasurePoint mdMeasurePoint) { + fillWorkshopInfo(mdMeasurePoint); + mdMeasurePoint.setUpdateTime(DateUtils.getNowDate()); + return mdMeasurePointMapper.updateMdMeasurePoint(mdMeasurePoint); + } + + @Override + public int deleteMdMeasurePointByPointIds(Long[] pointIds) { + return mdMeasurePointMapper.deleteMdMeasurePointByPointIds(pointIds); + } + + @Override + public int deleteMdMeasurePointByPointId(Long pointId) { + return mdMeasurePointMapper.deleteMdMeasurePointByPointId(pointId); + } + + @Override + public MdMeasurePoint queryLatestValue(String pointCode) { + return selectMdMeasurePointByPointCode(pointCode); + } + + @Override + public List queryHistory(String pointCode, String range, String interval, String startTime, String endTime) { + MdMeasurePoint point = mdMeasurePointMapper.selectMdMeasurePointByPointCode(pointCode); + if (point == null) { + return new ArrayList(); + } + return mdMeasurePointInfluxService.queryHistory(point, range, interval, startTime, endTime); + } + + private void fillWorkshopInfo(MdMeasurePoint mdMeasurePoint) { + if (mdMeasurePoint.getWorkshopId() == null) { + mdMeasurePoint.setWorkshopCode(null); + mdMeasurePoint.setWorkshopName(null); + return; + } + MdWorkshop workshop = mdWorkshopService.selectMdWorkshopByWorkshopId(mdMeasurePoint.getWorkshopId()); + if (workshop != null) { + mdMeasurePoint.setWorkshopCode(workshop.getWorkshopCode()); + mdMeasurePoint.setWorkshopName(workshop.getWorkshopName()); + } + } +} diff --git a/ktg-mes/src/main/resources/mapper/md/MdMeasurePointMapper.xml b/ktg-mes/src/main/resources/mapper/md/MdMeasurePointMapper.xml new file mode 100644 index 0000000..bc1ee1b --- /dev/null +++ b/ktg-mes/src/main/resources/mapper/md/MdMeasurePointMapper.xml @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + select point_id, point_code, point_name, workshop_id, workshop_code, workshop_name, + device_code, node_code, field_key, tag_json, unit, + precision_digit, sort_num, enable_flag, remark, create_by, create_time, update_by, update_time + from md_measure_point + + + + + + + + + + + + + + insert into md_measure_point + + point_code, + point_name, + workshop_id, + workshop_code, + workshop_name, + device_code, + node_code, + field_key, + tag_json, + unit, + precision_digit, + sort_num, + enable_flag, + remark, + create_by, + create_time, + update_by, + update_time, + + + #{pointCode}, + #{pointName}, + #{workshopId}, + #{workshopCode}, + #{workshopName}, + #{deviceCode}, + #{nodeCode}, + #{fieldKey}, + #{tagJson}, + #{unit}, + #{precisionDigit}, + #{sortNum}, + #{enableFlag}, + #{remark}, + #{createBy}, + #{createTime}, + #{updateBy}, + #{updateTime}, + + + + + update md_measure_point + + point_code = #{pointCode}, + point_name = #{pointName}, + workshop_id = #{workshopId}, + workshop_code = #{workshopCode}, + workshop_name = #{workshopName}, + device_code = #{deviceCode}, + node_code = #{nodeCode}, + field_key = #{fieldKey}, + tag_json = #{tagJson}, + unit = #{unit}, + precision_digit = #{precisionDigit}, + sort_num = #{sortNum}, + enable_flag = #{enableFlag}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = #{updateTime}, + + where point_id = #{pointId} + + + + delete from md_measure_point where point_id = #{pointId} + + + + delete from md_measure_point where point_id in + + #{pointId} + + +