Ver Fonte

大屏上下线数据展示

day01-init há 5 meses atrás
pai
commit
b256d895d8

+ 12 - 0
src/main/java/com/jeesite/modules/mes/dao/MesProductRecordDao.java

@@ -6,7 +6,9 @@ import com.jeesite.modules.mes.entity.MesProduct;
 import com.jeesite.modules.mes.entity.MesProductRecord;
 import org.apache.ibatis.annotations.Param;
 
+import java.util.Date;
 import java.util.List;
+import java.util.Map;
 
 /**
  * 生产记录DAO接口
@@ -26,4 +28,14 @@ public interface MesProductRecordDao extends CrudDao<MesProductRecord> {
 
     List<MesProductRecord> selectByOprno(MesProductRecord mesProductRecord);
 
+    /**
+     * 按小时统计指定产线、工位前缀下 content=OK 的条数(用于大屏每小时上下线)
+     */
+    List<Map<String, Object>> countHourlyOkByLineAndOprnoPrefix(
+            @Param("lineSn") String lineSn,
+            @Param("oprnoPrefix") String oprnoPrefix,
+            @Param("dayStart") Date dayStart,
+            @Param("dayEnd") Date dayEnd,
+            @Param("filterLineSn") boolean filterLineSn);
+
 }

+ 47 - 0
src/main/java/com/jeesite/modules/mes/service/MesProductRecordService.java

@@ -1253,4 +1253,51 @@ public class MesProductRecordService extends CrudService<MesProductRecordDao, Me
 		query.getSqlMap().getWhere().and("oprno", QueryType.LIKE, fOprno);
 		return findList(query);
 	}
+
+	/**
+	 * 大屏:当日按小时统计上线/下线工位 OK 条数(24 个小时与前端 01–24 时对应:索引 0 = 0 点,…,23 = 23 点)
+	 * lineSn 为空时不按产线过滤。
+	 */
+	public Map<String, Object> getHourlyUpDownOkStats(String lineSn, String onlineOprno, String offlineOprno,
+													  java.util.Date dayStart, java.util.Date dayEnd) {
+		boolean filterLineSn = StringUtils.isNotBlank(lineSn);
+		List<Map<String, Object>> upRows = mesProductRecordDao.countHourlyOkByLineAndOprnoPrefix(
+				lineSn, onlineOprno, dayStart, dayEnd, filterLineSn);
+		List<Map<String, Object>> downRows = mesProductRecordDao.countHourlyOkByLineAndOprnoPrefix(
+				lineSn, offlineOprno, dayStart, dayEnd, filterLineSn);
+		int[] up = new int[24];
+		int[] down = new int[24];
+		fillHourlyCounts(upRows, up);
+		fillHourlyCounts(downRows, down);
+		int maxVal = 10;
+		for (int v : up) {
+			maxVal = Math.max(maxVal, v);
+		}
+		for (int v : down) {
+			maxVal = Math.max(maxVal, v);
+		}
+		Map<String, Object> ret = MapUtils.newHashMap();
+		ret.put("up", up);
+		ret.put("down", down);
+		ret.put("max", maxVal);
+		return ret;
+	}
+
+	private void fillHourlyCounts(List<Map<String, Object>> rows, int[] bucket) {
+		if (rows == null) {
+			return;
+		}
+		for (Map<String, Object> row : rows) {
+			Object hro = row.get("hr");
+			Object cnto = row.get("cnt");
+			if (hro == null || cnto == null) {
+				continue;
+			}
+			int hr = ((Number) hro).intValue();
+			int cnt = ((Number) cnto).intValue();
+			if (hr >= 0 && hr < 24) {
+				bucket[hr] = cnt;
+			}
+		}
+	}
 }

+ 59 - 0
src/main/java/com/jeesite/modules/mes/web/MesProductController.java

@@ -4,6 +4,8 @@ import java.text.DateFormat;
 import java.text.DecimalFormat;
 import java.text.SimpleDateFormat;
 import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
 import java.util.*;
 import javax.servlet.http.HttpServletRequest;
@@ -294,6 +296,63 @@ public class MesProductController extends BaseController {
 	public String screen3(Model model) {
 		return "modules/mes/mesScreen4";
 	}
+	@RequestMapping(value = {"screen5"})
+	public String screen5(Model model) {
+		return "modules/mes/mesScreen5";
+	}
+
+	@RequestMapping(value = {"screen6"})
+	public String screen6(Model model) {
+		return "modules/mes/mesScreen6";
+	}
+
+	/**
+	 * 大屏:按小时统计 mes_product_record 中上线/下线工位的 OK 条数(与前端「每小时上下线数据分析」对接)。
+	 * <p>请求参数:</p>
+	 * <ul>
+	 *   <li>onlineOprno:上线工位号前缀,必填(如 OP120)</li>
+	 *   <li>offlineOprno:下线工位号前缀,必填(如 OP190)</li>
+	 *   <li>lineSn:产线编号,可选;不传或空则不过滤产线(全部产线汇总)</li>
+	 *   <li>queryDate:统计日期 yyyy-MM-dd,可选,默认当天</li>
+	 * </ul>
+	 * <p>返回 data:up、down 各 24 个 int(下标 0–23 对应 0–23 时),max 为纵轴建议最大值。</p>
+	 */
+	@RequestMapping(value = "hourlyUpDownOkData")
+	@ResponseBody
+	public CommonResp<Map<String, Object>> hourlyUpDownOkData(HttpServletRequest request) {
+		CommonResp<Map<String, Object>> resp = new CommonResp<>();
+		String lineSn = trimRequestParam(request.getParameter("lineSn"));
+		String onlineOprno = trimRequestParam(request.getParameter("onlineOprno"));
+		String offlineOprno = trimRequestParam(request.getParameter("offlineOprno"));
+		String queryDate = request.getParameter("queryDate");
+		if (StringUtils.isBlank(onlineOprno) || StringUtils.isBlank(offlineOprno)) {
+			resp.setResult(Global.FALSE);
+			resp.setMessage("onlineOprno、offlineOprno 不能为空");
+			return resp;
+		}
+		try {
+			LocalDate day = StringUtils.isNotBlank(queryDate)
+					? LocalDate.parse(queryDate.trim(), DateTimeFormatter.ISO_LOCAL_DATE)
+					: LocalDate.now();
+			java.time.LocalDateTime startLdt = day.atStartOfDay();
+			java.time.LocalDateTime endLdt = day.plusDays(1).atStartOfDay();
+			Date dayStart = Date.from(startLdt.atZone(ZoneId.systemDefault()).toInstant());
+			Date dayEnd = Date.from(endLdt.atZone(ZoneId.systemDefault()).toInstant());
+			Map<String, Object> data = mesProductRecordService.getHourlyUpDownOkStats(
+					lineSn, onlineOprno, offlineOprno, dayStart, dayEnd);
+			resp.setData(data);
+			resp.setResult(Global.TRUE);
+		} catch (Exception e) {
+			logger.error(e.getMessage(), e);
+			resp.setResult(Global.FALSE);
+			resp.setMessage("查询失败:" + e.getMessage());
+		}
+		return resp;
+	}
+
+	private static String trimRequestParam(String s) {
+		return s == null ? "" : s.trim();
+	}
 
 	@RequestMapping(value = "alarmData")
 	@ResponseBody

+ 15 - 0
src/main/resources/mappings/modules/mes/MesProductRecordDao.xml

@@ -49,4 +49,19 @@
         update mes_product_record set content =#{content}
         where sn = #{sn} and oprno = #{oprno} and line_sn = #{lineSn} and craft = #{craft}
     </update>
+
+    <!-- 按小时统计 OK 条数:oprno 以前缀匹配(与 listData LIKE 一致),filterLineSn=false 时不按产线过滤 -->
+    <select id="countHourlyOkByLineAndOprnoPrefix" resultType="java.util.HashMap">
+        SELECT HOUR(a.update_date) AS hr, COUNT(1) AS cnt
+        FROM mes_product_record a
+        WHERE a.update_date <![CDATA[ >= ]]> #{dayStart}
+          AND a.update_date <![CDATA[ < ]]> #{dayEnd}
+          AND UPPER(TRIM(a.content)) = 'OK'
+          AND a.oprno LIKE CONCAT(#{oprnoPrefix}, '%')
+        <if test="filterLineSn == true">
+          AND a.line_sn = #{lineSn}
+        </if>
+        GROUP BY HOUR(a.update_date)
+        ORDER BY HOUR(a.update_date)
+    </select>
 </mapper>

+ 83 - 0
src/main/resources/views/modules/mes/mesScreen3.html

@@ -785,6 +785,88 @@
 	// ====== 各窗口测试数据 ======
 	var mockData = {};
 
+	/**
+	 * 总览页:汇总 mesScreen4 / 5 / 6 三条产线每小时上下线 OK 数(与各分屏工位、lineSn 一致)
+	 */
+	var HOURLY_API_CFG_LIST = [
+		{ lineSn: 'MP', onlineOprno: 'OP120', offlineOprno: 'OP190' },
+		{ lineSn: 'XT', onlineOprno: 'OP010',  offlineOprno: 'OP180' },
+		{ lineSn: 'ZP', onlineOprno: 'OP190', offlineOprno: 'OP250' }
+	];
+
+	function buildHourLabels24() {
+		var hourLabels = [];
+		for (var i = 1; i <= 24; i++) {
+			hourLabels.push(pad(i));
+		}
+		return hourLabels;
+	}
+
+	function updateHourlyUpDownByApi() {
+		var upSum = [], downSum = [], h;
+		for (h = 0; h < 24; h++) {
+			upSum[h] = 0;
+			downSum[h] = 0;
+		}
+		var total = HOURLY_API_CFG_LIST.length;
+		var finished = 0;
+
+		function applyAggregatedChart() {
+			var maxVal = 10;
+			for (h = 0; h < 24; h++) {
+				maxVal = Math.max(maxVal, upSum[h], downSum[h]);
+			}
+			mockData.hourlyData = {
+				labels: buildHourLabels24(),
+				up: upSum,
+				down: downSum,
+				max: maxVal
+			};
+			drawAllCharts();
+		}
+
+		function onOneRequestDone() {
+			finished++;
+			if (finished >= total) {
+				applyAggregatedChart();
+			}
+		}
+
+		function mergeHourlyResp(d) {
+			if (!d || !d.up || !d.down || d.up.length < 24 || d.down.length < 24) {
+				return;
+			}
+			for (h = 0; h < 24; h++) {
+				upSum[h] += Number(d.up[h]) || 0;
+				downSum[h] += Number(d.down[h]) || 0;
+			}
+		}
+
+		for (var i = 0; i < HOURLY_API_CFG_LIST.length; i++) {
+			(function(cfg) {
+				$.ajax({
+					url: '${ctx}/mes/mesProduct/hourlyUpDownOkData',
+					type: 'POST',
+					dataType: 'json',
+					data: {
+						lineSn: cfg.lineSn,
+						onlineOprno: cfg.onlineOprno,
+						offlineOprno: cfg.offlineOprno
+					},
+					success: function(resp) {
+						if (resp && resp.result === 'true' && resp.data) {
+							mergeHourlyResp(resp.data);
+						}
+						onOneRequestDone();
+					},
+					error: function() {
+						onOneRequestDone();
+					}
+				});
+			})(HOURLY_API_CFG_LIST[i]);
+		}
+	}
+
 	function generateMockData() {
 		// 近7日日期标签
 		var now = new Date();
@@ -1656,6 +1738,7 @@
 		updateDeviceAlarmAlertsByApi();
 		renderFaultRank();
 		renderAlerts();
+		updateHourlyUpDownByApi();
 		drawAllCharts();
 	}
 

+ 37 - 0
src/main/resources/views/modules/mes/mesScreen4.html

@@ -392,6 +392,42 @@
 
 	// ====== 测试数据 ======
 	var mockData = {};
+	/** 每小时上下线:mesProduct/hourlyUpDownOkData(本页 lineSn=MP,上线 OP120 / 下线 OP190) */
+	var HOURLY_API_CFG = { lineSn: 'MP', onlineOprno: 'OP120', offlineOprno: 'OP190' };
+	function buildHourLabels24() {
+		var hourLabels = [];
+		for (var i = 1; i <= 24; i++) {
+			hourLabels.push(pad(i));
+		}
+		return hourLabels;
+	}
+	function updateHourlyUpDownByApi() {
+		$.ajax({
+			url: '${ctx}/mes/mesProduct/hourlyUpDownOkData',
+			type: 'POST',
+			dataType: 'json',
+			data: {
+				lineSn: HOURLY_API_CFG.lineSn,
+				onlineOprno: HOURLY_API_CFG.onlineOprno,
+				offlineOprno: HOURLY_API_CFG.offlineOprno
+			},
+			success: function(resp) {
+				if (resp && resp.result === 'true' && resp.data) {
+					var d = resp.data;
+					if (!d.up || !d.down || d.up.length < 24 || d.down.length < 24) {
+						return;
+					}
+					mockData.hourly = {
+						labels: buildHourLabels24(),
+						up: d.up,
+						down: d.down,
+						max: typeof d.max === 'number' ? d.max : 100
+					};
+					drawAllCharts();
+				}
+			}
+		});
+	}
 	function generateMockData() {
 		// 最近7日日期
 		var now = new Date();
@@ -1073,6 +1109,7 @@
 		updateDeviceCountByApi();
 		updateActualProductionByApi();
 		updateDeviceAlarmAlertsByApi();
+		updateHourlyUpDownByApi();
 		drawAllCharts();
 	}