From 5a6a6c94d0c02732e0be3410d126d652acb8ad10 Mon Sep 17 00:00:00 2001
From: zhang <zc857179121@qq.com>
Date: 星期三, 11 三月 2026 08:46:44 +0800
Subject: [PATCH] 1
---
zy-acs-hex/src/main/java/com/zy/acs/hex/controller/TestController.java | 7
zy-acs-cv/src/main/java/com/zy/asrs/service/JobService.java | 2
zy-acs-cv/src/main/java/com/zy/asrs/service/impl/JobServiceImpl.java | 4
zy-acs-hex/pom.xml | 6
zy-acs-hex/src/main/webapp/views/dashboard.html | 354 +++++++++++++++++++++++++
zy-acs-hex/src/main/java/com/zy/acs/hex/HexApplication.java | 2
zy-acs-hex/src/main/java/com/zy/acs/hex/utils/HttpGo.java | 322 +++++++++++++++++++++++
zy-acs-cv/src/main/java/com/zy/core/operation/handler/SendTaskOperationHandler.java | 4
zy-acs-cv/src/main/java/com/zy/asrs/service/impl/WrkLastnoServiceImpl.java | 2
zy-acs-cv/src/main/resources/mapper/JobMapper.xml | 1
zy-acs-hex/src/main/java/com/zy/acs/hex/influxdb/task/InfluxDbScheduler.java | 85 ++++++
component/component-Influxdb/src/main/java/com/zy/component/influxdb/service/InfluxDBService.java | 2
/dev/null | 11
zy-acs-hex/src/main/resources/application.yml | 2
zy-acs-cv/src/main/java/com/zy/core/operation/handler/FakeUserOperationHandler.java | 2
zy-acs-cv/src/main/java/com/zy/asrs/service/impl/DevpServiceImpl.java | 2
zy-acs-cv/src/main/java/com/zy/asrs/service/impl/WmsMainServiceImpl.java | 7
zy-acs-cv/src/main/resources/application.yml | 4
zy-acs-cv/src/main/java/com/zy/core/operation/handler/AppleLocOperationHandler.java | 14
19 files changed, 802 insertions(+), 31 deletions(-)
diff --git a/component/component-Influxdb/src/main/java/com/zy/component/influxdb/service/InfluxDBService.java b/component/component-Influxdb/src/main/java/com/zy/component/influxdb/service/InfluxDBService.java
index eaf78eb..14f96b2 100644
--- a/component/component-Influxdb/src/main/java/com/zy/component/influxdb/service/InfluxDBService.java
+++ b/component/component-Influxdb/src/main/java/com/zy/component/influxdb/service/InfluxDBService.java
@@ -29,8 +29,6 @@
@Autowired
private InfluxDBClient influxDBClient;
-
-
/**
* 鍐欏叆鏁版嵁
*
diff --git a/zy-acs-cv/src/main/java/com/zy/asrs/service/JobService.java b/zy-acs-cv/src/main/java/com/zy/asrs/service/JobService.java
index 419ece4..852fb03 100644
--- a/zy-acs-cv/src/main/java/com/zy/asrs/service/JobService.java
+++ b/zy-acs-cv/src/main/java/com/zy/asrs/service/JobService.java
@@ -12,7 +12,7 @@
Job getJobByBarcodeAndJobSts(String barcode, Integer jobSts);
- Job getJobByBarcode(String barcode);
+ Job getJobByBarcode(String barcode, Integer jobSts);
Job getJobByJobNo(Integer jobNo);
diff --git a/zy-acs-cv/src/main/java/com/zy/asrs/service/impl/DevpServiceImpl.java b/zy-acs-cv/src/main/java/com/zy/asrs/service/impl/DevpServiceImpl.java
index b88729f..1d97e5b 100644
--- a/zy-acs-cv/src/main/java/com/zy/asrs/service/impl/DevpServiceImpl.java
+++ b/zy-acs-cv/src/main/java/com/zy/asrs/service/impl/DevpServiceImpl.java
@@ -19,6 +19,8 @@
if (de != null) {
de.setSqlData(devp);
baseMapper.updateById(de);
+ }else {
+ baseMapper.insert(devp);
}
}
}
diff --git a/zy-acs-cv/src/main/java/com/zy/asrs/service/impl/JobServiceImpl.java b/zy-acs-cv/src/main/java/com/zy/asrs/service/impl/JobServiceImpl.java
index 1d42622..0be56be 100644
--- a/zy-acs-cv/src/main/java/com/zy/asrs/service/impl/JobServiceImpl.java
+++ b/zy-acs-cv/src/main/java/com/zy/asrs/service/impl/JobServiceImpl.java
@@ -41,8 +41,8 @@
}
@Override
- public Job getJobByBarcode(String barcode) {
- return baseMapper.getJobByBarcode(barcode);
+ public Job getJobByBarcode(String barcode, Integer jobSts) {
+ return baseMapper.getJobByBarcodeAndJobSts(barcode,jobSts);
}
@Override
diff --git a/zy-acs-cv/src/main/java/com/zy/asrs/service/impl/WmsMainServiceImpl.java b/zy-acs-cv/src/main/java/com/zy/asrs/service/impl/WmsMainServiceImpl.java
index b0c825b..85bfb20 100644
--- a/zy-acs-cv/src/main/java/com/zy/asrs/service/impl/WmsMainServiceImpl.java
+++ b/zy-acs-cv/src/main/java/com/zy/asrs/service/impl/WmsMainServiceImpl.java
@@ -74,4 +74,11 @@
}
return null;
}
+
+ public static void main(String[] args) {
+ String s= "{\"msg\":\"Success\",\"code\":200,\"data\":{\"locNo\":\"A102400201\",\"batchNo\":\"TK2603104428\",\"taskNo\":\"TK2603104428\"}}";
+ //System.out.println(JSON.parseObject(s).getString("data"));
+ ApplyInRepsonseDto applyInRepsonseDto = JSONObject.parseObject(JSON.parseObject(s).getString("data"), ApplyInRepsonseDto.class);
+ System.out.println(applyInRepsonseDto);
+ }
}
diff --git a/zy-acs-cv/src/main/java/com/zy/asrs/service/impl/WrkLastnoServiceImpl.java b/zy-acs-cv/src/main/java/com/zy/asrs/service/impl/WrkLastnoServiceImpl.java
index bd9288b..4b15263 100644
--- a/zy-acs-cv/src/main/java/com/zy/asrs/service/impl/WrkLastnoServiceImpl.java
+++ b/zy-acs-cv/src/main/java/com/zy/asrs/service/impl/WrkLastnoServiceImpl.java
@@ -37,7 +37,7 @@
do {
workNo = workNo >= eNo ? sNo : workNo + 1;
- } while (jobService.getJobByJobNo(workNo) == null);
+ } while (jobService.getJobByJobNo(workNo) != null);
if (workNo > 0) {
wrkLastno.setWrkNo(workNo);
diff --git a/zy-acs-cv/src/main/java/com/zy/core/operation/handler/AppleLocOperationHandler.java b/zy-acs-cv/src/main/java/com/zy/core/operation/handler/AppleLocOperationHandler.java
index 7d4c74b..50f8376 100644
--- a/zy-acs-cv/src/main/java/com/zy/core/operation/handler/AppleLocOperationHandler.java
+++ b/zy-acs-cv/src/main/java/com/zy/core/operation/handler/AppleLocOperationHandler.java
@@ -85,18 +85,21 @@
}
// 9991鏄┖鏉匡紝9992鏄弧鏉�
if (staProtocol.getWorkNo() >= 9991 && staProtocol.getWorkNo() <= 9992) {
- Job job = jobService.getJobByBarcode(barcode);
+ Job job = jobService.getJobByBarcode(barcode ,ConveyorStateType.INBOUND.getStatus());
// 鐢宠鍏ュ簱
if (job == null || (job != null && job.getJobSts() == ConveyorStateType.CLEARSIGNAL.getStatus())) {
ApplyInRepsonseDto locOfWms = wmsMainService.getLocOfWms(applyIn(barcode, inSta.getStaNo() + "", staProtocol));
if (locOfWms != null) {
- staProtocol.setWorkNo(job.getJobNo());
+ Integer workNo = getWorkNo();
+ staProtocol.setWorkNo(workNo);
staProtocol.setStaNo(inSta.getTargetSta());
if (MessageQueue.offer(SlaveType.Devp, devp.getId(), new Task(TaskType.WRITE, staProtocol))) {
- if (!jobService.insert(initJob(locOfWms, barcode, inSta.getTargetSta() + ""))) {
+ if (!jobService.insert(initJob(locOfWms, barcode, workNo,inSta.getTargetSta() + ""))) {
throw new CoolException("鏇存柊杈撻�佺嚎浠诲姟澶辫触," + " - " + staProtocol.getWorkNo());
}
log.info("鍏ュ簱鍓嶈繘锛歿},{}", staProtocol.getWorkNo(), inSta.getTargetSta());
+ }else {
+ log.info("涓嬪彂澶辫触锛歿},{}", staProtocol.getWorkNo(), inSta.getTargetSta());
}
} else {
log.info("WMS鏈繑鍥炲簱浣嶄俊鎭紝鏉$爜锛歿},绔欑偣锛歿}", barcode, inSta.getStaNo());
@@ -112,14 +115,15 @@
}
}
- private Job initJob(ApplyInRepsonseDto locOfWms, String barcode, String staNo) {
+
+ private Job initJob(ApplyInRepsonseDto locOfWms, String barcode,Integer workNo, String staNo) {
Job job = new Job();
job.setLoc(locOfWms.getLocNo());
job.setTaskNo(locOfWms.getTaskNo());
job.setBatchNo(locOfWms.getBatchNo());
job.setBarcode(barcode);
job.setStaNo(staNo);
- job.setJobNo(getWorkNo());
+ job.setJobNo(workNo);
job.setJobSts(ConveyorStateType.INBOUND.getStatus());
job.setWmsTime(new Date());
return job;
diff --git a/zy-acs-cv/src/main/java/com/zy/core/operation/handler/FakeUserOperationHandler.java b/zy-acs-cv/src/main/java/com/zy/core/operation/handler/FakeUserOperationHandler.java
index 6c402da..e7c32c1 100644
--- a/zy-acs-cv/src/main/java/com/zy/core/operation/handler/FakeUserOperationHandler.java
+++ b/zy-acs-cv/src/main/java/com/zy/core/operation/handler/FakeUserOperationHandler.java
@@ -60,7 +60,7 @@
return;
}
if (staProtocol.getWorkNo() > 0 && staProtocol.isAutoing()) {
- Job jobByWorkNo = jobService.getJobByJobNo(staProtocol.getWorkNo());
+ Job jobByWorkNo = jobService.getJobByJobNoAndJobSts(staProtocol.getWorkNo(),ConveyorStateType.OUTBOUND.getStatus());
if (jobByWorkNo != null && jobByWorkNo.getJobSts() == ConveyorStateType.OUTBOUND.getStatus()) {
staProtocol.setWorkNo(9992);
staProtocol.setStaNo(1005);
diff --git a/zy-acs-cv/src/main/java/com/zy/core/operation/handler/SendTaskOperationHandler.java b/zy-acs-cv/src/main/java/com/zy/core/operation/handler/SendTaskOperationHandler.java
index 760106d..81d9c40 100644
--- a/zy-acs-cv/src/main/java/com/zy/core/operation/handler/SendTaskOperationHandler.java
+++ b/zy-acs-cv/src/main/java/com/zy/core/operation/handler/SendTaskOperationHandler.java
@@ -73,8 +73,8 @@
continue;
}
if (staProtocol.isAutoing()) {
- Job job = jobService.getJobByJobNo(staProtocol.getWorkNo());
- if (job != null && job.getJobSts() == ConveyorStateType.INBOUND.getStatus()) {
+ Job job = jobService.getJobByJobNoAndJobSts(staProtocol.getWorkNo(),ConveyorStateType.INBOUND.getStatus());
+ if (job != null ) {
if (ctuMainService.sendTask(process(job))) {
job.setJobSts(ConveyorStateType.SENDTASK.getStatus());
job.setRcsTime(new Date());
diff --git a/zy-acs-cv/src/main/resources/application.yml b/zy-acs-cv/src/main/resources/application.yml
index 42e55ff..0a4f054 100644
--- a/zy-acs-cv/src/main/resources/application.yml
+++ b/zy-acs-cv/src/main/resources/application.yml
@@ -10,7 +10,7 @@
datasource:
driver-class-name: com.mysql.jdbc.Driver
#url: jdbc:mysql://127.0.0.1:3306/rcs_ctu_stable?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
- url: jdbc:mysql://192.168.133.173:3306/cv?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
+ url: jdbc:mysql://127.0.0.1:3306/cv?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: xltys1995
mvc:
@@ -18,7 +18,7 @@
logging:
file:
- path: stock/out/cv/logs
+ path: /stock/out/cv/logs
wms:
url: 10.10.10.220:8081
diff --git a/zy-acs-cv/src/main/resources/mapper/JobMapper.xml b/zy-acs-cv/src/main/resources/mapper/JobMapper.xml
index b91bc3b..79235ed 100644
--- a/zy-acs-cv/src/main/resources/mapper/JobMapper.xml
+++ b/zy-acs-cv/src/main/resources/mapper/JobMapper.xml
@@ -61,6 +61,7 @@
select *
from cv_job
where barcode = #{barcode}
+ and job_sts = #{jobSts}
order by id desc limit 1
</select>
diff --git a/zy-acs-hex/pom.xml b/zy-acs-hex/pom.xml
index f23f868..0a39584 100644
--- a/zy-acs-hex/pom.xml
+++ b/zy-acs-hex/pom.xml
@@ -64,6 +64,12 @@
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
+
+ <dependency>
+ <groupId>com.squareup.okhttp3</groupId>
+ <artifactId>okhttp</artifactId>
+ <version>4.12.0</version>
+ </dependency>
</dependencies>
<build>
diff --git a/zy-acs-hex/src/main/java/com/zy/acs/hex/HexApplication.java b/zy-acs-hex/src/main/java/com/zy/acs/hex/HexApplication.java
index 41c2135..d779c50 100644
--- a/zy-acs-hex/src/main/java/com/zy/acs/hex/HexApplication.java
+++ b/zy-acs-hex/src/main/java/com/zy/acs/hex/HexApplication.java
@@ -4,7 +4,9 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
+import org.springframework.scheduling.annotation.EnableScheduling;
+@EnableScheduling
@ComponentScan(basePackages = {"com.zy.component", "com.zy.acs"})
@SpringBootApplication
public class HexApplication {
diff --git a/zy-acs-hex/src/main/java/com/zy/acs/hex/controller/TestController.java b/zy-acs-hex/src/main/java/com/zy/acs/hex/controller/TestController.java
index 11f1f56..ba77e21 100644
--- a/zy-acs-hex/src/main/java/com/zy/acs/hex/controller/TestController.java
+++ b/zy-acs-hex/src/main/java/com/zy/acs/hex/controller/TestController.java
@@ -1,7 +1,7 @@
package com.zy.acs.hex.controller;
+import com.zy.acs.common.domain.mq.DeviceMessage;
import com.zy.acs.hex.constant.RabbitConstant;
-import com.zy.acs.hex.domain.Device;
import com.zy.component.influxdb.service.InfluxDBService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
@@ -31,10 +31,9 @@
*/
@GetMapping(value = "/test1")
public void sendTest1() {
- Device device = new Device();
+ DeviceMessage device = new DeviceMessage("121212121212");
//device.setEvent("online");
//device.setDeviceId("123");
- device.setProtocol("212121212121212");
String router = RabbitConstant.ROUTING_KEY_UP.replaceFirst("\\*", "123").replaceFirst("\\*", "online");
rabbitTemplate.convertAndSend(RabbitConstant.TOPIC_EXCHANGE, router, device);
}
@@ -69,7 +68,7 @@
@GetMapping(value = "/query2")
@ResponseBody
public Object queryTest2() {
- return influxDBService.queryPoints("select * from device order by time desc limit 10", Device.class);
+ return influxDBService.queryPoints("select * from device order by time desc limit 10", DeviceMessage.class);
}
}
diff --git a/zy-acs-hex/src/main/java/com/zy/acs/hex/domain/Device.java b/zy-acs-hex/src/main/java/com/zy/acs/hex/domain/Device.java
deleted file mode 100644
index 2b07977..0000000
--- a/zy-acs-hex/src/main/java/com/zy/acs/hex/domain/Device.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.zy.acs.hex.domain;
-
-import lombok.Data;
-
-import java.io.Serializable;
-
-@Data
-public class Device implements Serializable {
- private String protocol;
-
-}
diff --git a/zy-acs-hex/src/main/java/com/zy/acs/hex/influxdb/task/InfluxDbScheduler.java b/zy-acs-hex/src/main/java/com/zy/acs/hex/influxdb/task/InfluxDbScheduler.java
new file mode 100644
index 0000000..903e203
--- /dev/null
+++ b/zy-acs-hex/src/main/java/com/zy/acs/hex/influxdb/task/InfluxDbScheduler.java
@@ -0,0 +1,85 @@
+package com.zy.acs.hex.influxdb.task;
+
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.zy.acs.hex.utils.HttpGo;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import java.io.IOException;
+import java.time.Duration;
+import java.util.HashMap;
+import java.util.Map;
+
+@Slf4j
+@Component
+public class InfluxDbScheduler {
+
+
+ @Value("${influxdb3.createDatabaseUrl}")
+ private String createDatabaseUrl;
+
+
+ @Value("${influxdb3.database}")
+ private String databaseName;
+
+ @Value("${influxdb3.token}")
+ private String token;
+
+ @Value("${influxdb3.retention-period}")
+ private String retentionPeriod;
+
+
+ private static Long timeoutSeconds = 30L;
+
+ private HttpGo http;
+
+ @PostConstruct
+ public void init() {
+ this.http = HttpGo.builder()
+ .connectTimeout(Duration.ofSeconds(timeoutSeconds))
+ .readTimeout(Duration.ofSeconds(timeoutSeconds))
+ .build();
+ createDatabase();
+ }
+
+
+ public void createDatabase() {
+ // headers
+ Map<String, String> headers = new HashMap<>();
+ headers.put("Authorization", "Bearer " + token);
+ headers.put("Content-Type", "application/json;charset=UTF-8");
+ try {
+ HttpGo.HttpResponse response = this.http.get(createDatabaseUrl + "?format=json", headers, null);
+ if (!isExist(response.body())) {
+ Map<String, String> parames = new HashMap<>();
+ parames.put("db", databaseName);
+ parames.put("retention-period", retentionPeriod);
+ HttpGo.HttpResponse postResponse = this.http.postJson(createDatabaseUrl, headers, JSON.toJSONString(parames));
+ log.info("鏄惁鍒涘缓鏁版嵁搴擄細{}", postResponse);
+ }else {
+ log.info("鏁版嵁搴擄細{}", response.body());
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ }
+
+ private boolean isExist(String databases) {
+ JSONArray objects = JSON.parseArray(databases);
+ for (Object object : objects) {
+ JSONObject obj = (JSONObject) object;
+ if (obj.getString("iox::database").equals(databaseName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+}
diff --git a/zy-acs-hex/src/main/java/com/zy/acs/hex/utils/HttpGo.java b/zy-acs-hex/src/main/java/com/zy/acs/hex/utils/HttpGo.java
new file mode 100644
index 0000000..a0d27f5
--- /dev/null
+++ b/zy-acs-hex/src/main/java/com/zy/acs/hex/utils/HttpGo.java
@@ -0,0 +1,322 @@
+package com.zy.acs.hex.utils;
+
+import lombok.extern.slf4j.Slf4j;
+import okhttp3.*;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.security.SecureRandom;
+import java.security.cert.X509Certificate;
+import java.time.Duration;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Minimal OkHttp wrapper: GET / POST only.
+ *
+ * - fluent API (get / postJson / postForm)
+ * - default singleton instance (thread-safe)
+ * - per-request headers + default headers
+ * - simple response wrapper with tookMs
+ * - optional trust-all SSL (ONLY for internal test)
+ */
+@Slf4j
+public final class HttpGo {
+
+ private static final MediaType JSON = MediaType.get("application/json; charset=utf-8");
+ private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
+
+ private final OkHttpClient client;
+ private final Map<String, String> defaultHeaders;
+
+ private HttpGo(OkHttpClient client, Map<String, String> defaultHeaders) {
+ this.client = Objects.requireNonNull(client, "client");
+ this.defaultHeaders = defaultHeaders == null
+ ? Collections.emptyMap()
+ : Collections.unmodifiableMap(new LinkedHashMap<>(defaultHeaders));
+ }
+
+ /** Shared default instance (safe SSL by default). */
+ public static HttpGo defaults() {
+ return Holder.DEFAULT;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ // ===================== GET =====================
+
+ public HttpResponse get(String url) throws IOException {
+ return get(url, null, null);
+ }
+
+ public HttpResponse get(String url, Map<String, String> queryParams) throws IOException {
+ return get(url, null, queryParams);
+ }
+
+ public HttpResponse get(String url, Map<String, String> headers, Map<String, String> queryParams) throws IOException {
+ HttpUrl parsed = HttpUrl.parse(url);
+ if (parsed == null) throw new IllegalArgumentException("Invalid url: " + url);
+
+ HttpUrl.Builder ub = parsed.newBuilder();
+ if (queryParams != null) {
+ queryParams.forEach((k, v) -> {
+ if (k != null && v != null) ub.addQueryParameter(k, v);
+ });
+ }
+
+ Request.Builder rb = new Request.Builder().url(ub.build()).get();
+ applyHeaders(rb, headers);
+ return execute(rb.build());
+ }
+
+ // ===================== POST =====================
+
+ /** POST JSON string payload (null/blank -> "{}"). */
+ public HttpResponse postJson(String url, String json) throws IOException {
+ return postJson(url, null, json);
+ }
+
+ /** POST JSON string payload (null/blank -> "{}"). */
+ public HttpResponse postJson(String url, Map<String, String> headers, String json) throws IOException {
+ String payload = (json == null || json.trim().isEmpty()) ? "{}" : json;
+ RequestBody body = RequestBody.create(payload, JSON);
+
+ Request.Builder rb = new Request.Builder().url(url).post(body);
+ applyHeaders(rb, headers);
+
+ // ensure content-type unless caller overrides
+ if (rb.build().header("Content-Type") == null) {
+ rb.header("Content-Type", "application/json; charset=utf-8");
+ }
+
+ return execute(rb.build());
+ }
+
+ /** POST x-www-form-urlencoded fields. */
+ public HttpResponse postForm(String url, Map<String, String> formFields) throws IOException {
+ return postForm(url, null, formFields);
+ }
+
+ /** POST x-www-form-urlencoded fields. */
+ public HttpResponse postForm(String url, Map<String, String> headers, Map<String, String> formFields) throws IOException {
+ FormBody.Builder fb = new FormBody.Builder(DEFAULT_CHARSET);
+ if (formFields != null) {
+ formFields.forEach((k, v) -> {
+ if (k != null && v != null) fb.add(k, v);
+ });
+ }
+
+ Request.Builder rb = new Request.Builder().url(url).post(fb.build());
+ applyHeaders(rb, headers);
+ return execute(rb.build());
+ }
+
+ // ===================== Internals =====================
+
+ private HttpResponse execute(Request request) throws IOException {
+ long start = System.nanoTime();
+ try (Response resp = client.newCall(request).execute()) {
+ long tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
+
+ String body = null;
+ ResponseBody rb = resp.body();
+ if (rb != null) body = rb.string(); // one-shot
+
+ return new HttpResponse(resp.code(), resp.headers(), body, tookMs);
+ } catch (IOException e) {
+ log.error("HttpGo request failed: {} {}", request.method(), request.url(), e);
+ throw e;
+ }
+ }
+
+ private void applyHeaders(Request.Builder rb, Map<String, String> headers) {
+ // defaults first, then per-request overrides
+ if (defaultHeaders != null) {
+ defaultHeaders.forEach((k, v) -> {
+ if (k != null && v != null) rb.header(k, v);
+ });
+ }
+ if (headers != null) {
+ headers.forEach((k, v) -> {
+ if (k != null && v != null) rb.header(k, v);
+ });
+ }
+ }
+
+ private static final class Holder {
+ private static final HttpGo DEFAULT = HttpGo.builder().build();
+ }
+
+ // ===================== Response =====================
+
+ public static final class HttpResponse {
+ private final int statusCode;
+ private final Headers headers;
+ private final String body;
+ private final long tookMs;
+
+ public HttpResponse(int statusCode, Headers headers, String body, long tookMs) {
+ this.statusCode = statusCode;
+ this.headers = headers == null ? new Headers.Builder().build() : headers;
+ this.body = body;
+ this.tookMs = tookMs;
+ }
+
+ public int statusCode() { return statusCode; }
+ public Headers headers() { return headers; }
+ public String body() { return body; }
+ public long tookMs() { return tookMs; }
+
+ public boolean is2xx() {
+ return statusCode >= 200 && statusCode < 300;
+ }
+
+ public String header(String name) {
+ return headers.get(name);
+ }
+
+ @Override
+ public String toString() {
+ return "HttpResponse{status=" + statusCode + ", tookMs=" + tookMs
+ + ", bodyLen=" + (body == null ? 0 : body.length()) + "}";
+ }
+ }
+
+ // ===================== Builder =====================
+
+ public static final class Builder {
+ private Duration connectTimeout = Duration.ofSeconds(10);
+ private Duration readTimeout = Duration.ofSeconds(20);
+ private Duration writeTimeout = Duration.ofSeconds(20);
+ private boolean trustAllSsl = false;
+
+ private final Map<String, String> defaultHeaders = new LinkedHashMap<>();
+
+ public Builder defaultHeader(String name, String value) {
+ if (name != null && value != null) defaultHeaders.put(name, value);
+ return this;
+ }
+
+ public Builder connectTimeout(Duration d) {
+ if (d != null) connectTimeout = d;
+ return this;
+ }
+
+ public Builder readTimeout(Duration d) {
+ if (d != null) readTimeout = d;
+ return this;
+ }
+
+ public Builder writeTimeout(Duration d) {
+ if (d != null) writeTimeout = d;
+ return this;
+ }
+
+ /** Trust ALL certificates. ONLY for internal testing/self-signed endpoints. */
+ public Builder trustAllSsl(boolean enable) {
+ this.trustAllSsl = enable;
+ return this;
+ }
+
+ public HttpGo build() {
+ OkHttpClient.Builder cb = new OkHttpClient.Builder()
+ .connectTimeout(connectTimeout.toMillis(), TimeUnit.MILLISECONDS)
+ .readTimeout(readTimeout.toMillis(), TimeUnit.MILLISECONDS)
+ .writeTimeout(writeTimeout.toMillis(), TimeUnit.MILLISECONDS);
+
+ if (trustAllSsl) {
+ TrustAll trustAll = new TrustAll();
+ cb.sslSocketFactory(trustAll.sslSocketFactory, trustAll.trustManager)
+ .hostnameVerifier((hostname, session) -> true);
+ }
+
+ return new HttpGo(cb.build(), defaultHeaders);
+ }
+ }
+
+ // ===================== Trust-all SSL helper =====================
+
+ private static final class TrustAll {
+ final X509TrustManager trustManager;
+ final SSLSocketFactory sslSocketFactory;
+
+ TrustAll() {
+ try {
+ this.trustManager = new X509TrustManager() {
+ @Override public void checkClientTrusted(X509Certificate[] chain, String authType) { }
+ @Override public void checkServerTrusted(X509Certificate[] chain, String authType) { }
+ @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
+ };
+
+ SSLContext sslContext = SSLContext.getInstance("TLS");
+ sslContext.init(null, new TrustManager[]{trustManager}, new SecureRandom());
+ this.sslSocketFactory = sslContext.getSocketFactory();
+ } catch (Exception e) {
+ throw new IllegalStateException("Failed to init trust-all SSL", e);
+ }
+ }
+ }
+
+ // ======================== tools ========================
+ public String buildUrl(String host, Integer port, String path) {
+ return buildUrl(host, port, path, false);
+ }
+
+ public String buildUrl(String host, Integer port, String path, boolean ssl) {
+ String p = (path == null) ? "" : (path.startsWith("/") ? path : ("/" + path));
+ return (ssl ? "https" : "http") + "://" + host + ":" + port + p;
+ }
+
+ // ===================== Demo (main) =====================
+
+ public static void main(String[] args) throws Exception {
+ HttpGo http = HttpGo.builder()
+ .connectTimeout(Duration.ofSeconds(8))
+ .readTimeout(Duration.ofSeconds(15))
+ .defaultHeader("User-Agent", "HttpGo/1.0")
+ // .trustAllSsl(true) // ONLY if you really need it
+ .build();
+
+ // 1) GET with query params + per-request headers
+ String getUrl = "https://httpbin.org/get";
+
+ Map<String, String> query = new HashMap<>();
+ query.put("q", "vincent");
+ query.put("page", "1");
+
+ Map<String, String> headers = new HashMap<>();
+ headers.put("X-Trace-Id", "trace-001");
+
+ HttpResponse r1 = http.get(getUrl, headers, query);
+ System.out.println("GET status=" + r1.statusCode() + ", tookMs=" + r1.tookMs());
+ System.out.println(r1.body());
+
+ // 2) POST JSON
+ String postUrl = "https://httpbin.org/post";
+ String json = "{\"name\":\"Vincent\",\"role\":\"engineer\"}";
+
+ Map<String, String> postHeaders = new HashMap<>();
+ postHeaders.put("X-Trace-Id", "trace-002");
+
+ HttpResponse r2 = http.postJson(postUrl, postHeaders, json);
+ System.out.println("POST(JSON) status=" + r2.statusCode() + ", tookMs=" + r2.tookMs());
+ System.out.println(r2.body());
+
+ // 3) POST Form
+ Map<String, String> form = new HashMap<>();
+ form.put("username", "vincent");
+ form.put("password", "123456");
+
+ HttpResponse r3 = http.postForm(postUrl, null, form);
+ System.out.println("POST(Form) status=" + r3.statusCode() + ", tookMs=" + r3.tookMs());
+ System.out.println(r3.body());
+ }
+
+}
\ No newline at end of file
diff --git a/zy-acs-hex/src/main/resources/application.yml b/zy-acs-hex/src/main/resources/application.yml
index 16f7a1c..0a41bf9 100644
--- a/zy-acs-hex/src/main/resources/application.yml
+++ b/zy-acs-hex/src/main/resources/application.yml
@@ -30,3 +30,5 @@
#token: apiv3_Jx1SvmBMV_kikGhc4eZJQbeGmNYN7KX1GdpoR9MClkKzMxSJ0MPKM_O2Xt3o1hVyRikMmlxZ_h9zfy6ybC5Idg
database: rcs
token: apiv3_116RKycNhxbf62Nys4zthC05aRD-aidzhEpEpLtsFuedhJTaCtVklNrzHs9LHxBWMuzDclBHVgToGoQuWGiIIA
+ retention-period: 30d
+ createDatabaseUrl: ${influxdb3.url}/api/v3/configure/database
diff --git a/zy-acs-hex/src/main/webapp/views/dashboard.html b/zy-acs-hex/src/main/webapp/views/dashboard.html
new file mode 100644
index 0000000..ac20308
--- /dev/null
+++ b/zy-acs-hex/src/main/webapp/views/dashboard.html
@@ -0,0 +1,354 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>CTU灏忚溅鏁版嵁鐩戞帶</title>
+ <script src="https://cdn.tailwindcss.com"></script>
+ <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
+ <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.8/dist/chart.umd.min.js"></script>
+ <script>
+ tailwind.config = {
+ theme: {
+ extend: {
+ colors: {
+ primary: '#3b82f6',
+ secondary: '#10b981',
+ warning: '#f59e0b',
+ danger: '#ef4444',
+ dark: '#1e293b',
+ },
+ fontFamily: {
+ inter: ['Inter', 'sans-serif'],
+ },
+ }
+ }
+ }
+ </script>
+ <style type="text/tailwindcss">
+ @layer utilities {
+ .content-auto {
+ content-visibility: auto;
+ }
+ .card-shadow {
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+ }
+ .animate-pulse-slow {
+ animation: pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite;
+ }
+ }
+ </style>
+</head>
+<body class="bg-gray-50 font-inter">
+ <div class="min-h-screen">
+ <!-- 椤堕儴瀵艰埅鏍� -->
+ <header class="bg-white card-shadow sticky top-0 z-10">
+ <div class="container mx-auto px-4 py-3 flex justify-between items-center">
+ <div class="flex items-center space-x-2">
+ <i class="fa fa-car text-primary text-2xl"></i>
+ <h1 class="text-xl font-bold text-dark">CTU灏忚溅鏁版嵁鐩戞帶</h1>
+ </div>
+ <div class="flex items-center space-x-4">
+ <div class="relative">
+ <span class="absolute inset-y-0 left-0 flex items-center pl-3">
+ <i class="fa fa-search text-gray-400"></i>
+ </span>
+ <input type="text" placeholder="鎼滅储璁惧..." class="pl-10 pr-4 py-2 rounded-lg border border-gray-300 focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent">
+ </div>
+ <button class="bg-primary text-white px-4 py-2 rounded-lg hover:bg-blue-600 transition-colors">
+ <i class="fa fa-refresh mr-1"></i> 鍒锋柊
+ </button>
+ </div>
+ </div>
+ </header>
+
+ <!-- 涓诲唴瀹瑰尯 -->
+ <main class="container mx-auto px-4 py-6">
+ <!-- 鐘舵�佹瑙� -->
+ <section class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
+ <div class="bg-white rounded-lg p-4 card-shadow">
+ <div class="flex items-center justify-between">
+ <div>
+ <p class="text-gray-500 text-sm">鎬昏澶囨暟</p>
+ <h3 class="text-2xl font-bold text-dark">24</h3>
+ </div>
+ <div class="w-12 h-12 rounded-full bg-blue-100 flex items-center justify-center">
+ <i class="fa fa-microchip text-primary text-xl"></i>
+ </div>
+ </div>
+ </div>
+ <div class="bg-white rounded-lg p-4 card-shadow">
+ <div class="flex items-center justify-between">
+ <div>
+ <p class="text-gray-500 text-sm">鍦ㄧ嚎璁惧</p>
+ <h3 class="text-2xl font-bold text-secondary">18</h3>
+ </div>
+ <div class="w-12 h-12 rounded-full bg-green-100 flex items-center justify-center">
+ <i class="fa fa-check-circle text-secondary text-xl"></i>
+ </div>
+ </div>
+ </div>
+ <div class="bg-white rounded-lg p-4 card-shadow">
+ <div class="flex items-center justify-between">
+ <div>
+ <p class="text-gray-500 text-sm">绂荤嚎璁惧</p>
+ <h3 class="text-2xl font-bold text-danger">6</h3>
+ </div>
+ <div class="w-12 h-12 rounded-full bg-red-100 flex items-center justify-center">
+ <i class="fa fa-exclamation-circle text-danger text-xl"></i>
+ </div>
+ </div>
+ </div>
+ <div class="bg-white rounded-lg p-4 card-shadow">
+ <div class="flex items-center justify-between">
+ <div>
+ <p class="text-gray-500 text-sm">浠婃棩鏁版嵁閲�</p>
+ <h3 class="text-2xl font-bold text-warning">1.2k</h3>
+ </div>
+ <div class="w-12 h-12 rounded-full bg-yellow-100 flex items-center justify-center">
+ <i class="fa fa-database text-warning text-xl"></i>
+ </div>
+ </div>
+ </div>
+ </section>
+
+ <!-- 鏁版嵁鍥捐〃 -->
+ <section class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
+ <!-- 涓婅鏁版嵁鍥捐〃 -->
+ <div class="bg-white rounded-lg p-4 card-shadow">
+ <div class="flex justify-between items-center mb-4">
+ <h2 class="text-lg font-semibold text-dark">涓婅鏁版嵁瓒嬪娍</h2>
+ <div class="flex space-x-2">
+ <button class="px-3 py-1 text-sm bg-blue-100 text-primary rounded-md">灏忔椂</button>
+ <button class="px-3 py-1 text-sm bg-gray-100 text-gray-600 rounded-md">澶�</button>
+ <button class="px-3 py-1 text-sm bg-gray-100 text-gray-600 rounded-md">鍛�</button>
+ </div>
+ </div>
+ <div class="h-64">
+ <canvas id="upDataChart"></canvas>
+ </div>
+ </div>
+ <!-- 涓嬭鏁版嵁鍥捐〃 -->
+ <div class="bg-white rounded-lg p-4 card-shadow">
+ <div class="flex justify-between items-center mb-4">
+ <h2 class="text-lg font-semibold text-dark">涓嬭鏁版嵁瓒嬪娍</h2>
+ <div class="flex space-x-2">
+ <button class="px-3 py-1 text-sm bg-blue-100 text-primary rounded-md">灏忔椂</button>
+ <button class="px-3 py-1 text-sm bg-gray-100 text-gray-600 rounded-md">澶�</button>
+ <button class="px-3 py-1 text-sm bg-gray-100 text-gray-600 rounded-md">鍛�</button>
+ </div>
+ </div>
+ <div class="h-64">
+ <canvas id="downDataChart"></canvas>
+ </div>
+ </div>
+ </section>
+
+ <!-- 璁惧鏁版嵁琛ㄦ牸 -->
+ <section class="bg-white rounded-lg p-4 card-shadow">
+ <div class="flex justify-between items-center mb-4">
+ <h2 class="text-lg font-semibold text-dark">CTU灏忚溅鏁版嵁鍒楄〃</h2>
+ <div class="flex space-x-2">
+ <button class="px-3 py-1 text-sm bg-gray-100 text-gray-600 rounded-md">
+ <i class="fa fa-filter mr-1"></i> 绛涢��
+ </button>
+ <button class="px-3 py-1 text-sm bg-gray-100 text-gray-600 rounded-md">
+ <i class="fa fa-download mr-1"></i> 瀵煎嚭
+ </button>
+ </div>
+ </div>
+ <div class="overflow-x-auto">
+ <table class="min-w-full divide-y divide-gray-200">
+ <thead>
+ <tr>
+ <th class="px-6 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">璁惧ID</th>
+ <th class="px-6 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">璁惧鍚嶇О</th>
+ <th class="px-6 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">鐘舵��</th>
+ <th class="px-6 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">涓婅鏁版嵁</th>
+ <th class="px-6 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">涓嬭鏁版嵁</th>
+ <th class="px-6 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">鏈�鍚庨�氫俊</th>
+ <th class="px-6 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">鎿嶄綔</th>
+ </tr>
+ </thead>
+ <tbody class="bg-white divide-y divide-gray-200" id="deviceTableBody">
+ <!-- 琛ㄦ牸鏁版嵁灏嗛�氳繃JavaScript鍔ㄦ�佺敓鎴� -->
+ </tbody>
+ </table>
+ </div>
+ <div class="flex justify-between items-center mt-4">
+ <p class="text-sm text-gray-500">鏄剧ず 1-10 鏉★紝鍏� 24 鏉�</p>
+ <div class="flex space-x-1">
+ <button class="px-3 py-1 text-sm bg-gray-100 text-gray-600 rounded-md disabled:opacity-50" disabled>涓婁竴椤�</button>
+ <button class="px-3 py-1 text-sm bg-primary text-white rounded-md">1</button>
+ <button class="px-3 py-1 text-sm bg-gray-100 text-gray-600 rounded-md">2</button>
+ <button class="px-3 py-1 text-sm bg-gray-100 text-gray-600 rounded-md">3</button>
+ <button class="px-3 py-1 text-sm bg-gray-100 text-gray-600 rounded-md">涓嬩竴椤�</button>
+ </div>
+ </div>
+ </section>
+
+ <!-- 瀹炴椂鏁版嵁鏇存柊 -->
+ <section class="bg-white rounded-lg p-4 card-shadow mt-6">
+ <h2 class="text-lg font-semibold text-dark mb-4">瀹炴椂鏁版嵁鏇存柊</h2>
+ <div class="h-40 overflow-y-auto p-2 border border-gray-200 rounded-lg" id="realtimeData">
+ <!-- 瀹炴椂鏁版嵁灏嗛�氳繃JavaScript鍔ㄦ�佺敓鎴� -->
+ </div>
+ </section>
+ </main>
+
+ <!-- 椤佃剼 -->
+ <footer class="bg-white card-shadow mt-6 py-4">
+ <div class="container mx-auto px-4 text-center text-gray-500 text-sm">
+ <p>漏 2026 鐗╄仈缃戣澶囨暟鎹洃鎺х郴缁� | 鐗堟湰 1.0.0</p>
+ </div>
+ </footer>
+ </div>
+
+ <script>
+ // 妯℃嫙CTU灏忚溅鏁版嵁
+ const devices = [
+ { id: 'CTU-001', name: '閰嶉�佸皬杞�1鍙�', status: 'online', upData: '2.4KB', downData: '0.8KB', lastComm: '2鍒嗛挓鍓�' },
+ { id: 'CTU-002', name: '閰嶉�佸皬杞�2鍙�', status: 'online', upData: '1.8KB', downData: '0.5KB', lastComm: '5鍒嗛挓鍓�' },
+ { id: 'CTU-003', name: '宸℃灏忚溅1鍙�', status: 'offline', upData: '0KB', downData: '0KB', lastComm: '2灏忔椂鍓�' },
+ { id: 'CTU-004', name: '閰嶉�佸皬杞�3鍙�', status: 'online', upData: '3.2KB', downData: '1.2KB', lastComm: '1鍒嗛挓鍓�' },
+ { id: 'CTU-005', name: '宸℃灏忚溅2鍙�', status: 'online', upData: '1.5KB', downData: '0.6KB', lastComm: '3鍒嗛挓鍓�' },
+ { id: 'CTU-006', name: '閰嶉�佸皬杞�4鍙�', status: 'offline', upData: '0KB', downData: '0KB', lastComm: '5灏忔椂鍓�' },
+ { id: 'CTU-007', name: '宸℃灏忚溅3鍙�', status: 'online', upData: '2.1KB', downData: '0.9KB', lastComm: '4鍒嗛挓鍓�' },
+ { id: 'CTU-008', name: '閰嶉�佸皬杞�5鍙�', status: 'online', upData: '2.8KB', downData: '1.1KB', lastComm: '2鍒嗛挓鍓�' },
+ { id: 'CTU-009', name: '宸℃灏忚溅4鍙�', status: 'offline', upData: '0KB', downData: '0KB', lastComm: '1澶╁墠' },
+ { id: 'CTU-010', name: '閰嶉�佸皬杞�6鍙�', status: 'online', upData: '1.9KB', downData: '0.7KB', lastComm: '6鍒嗛挓鍓�' }
+ ];
+
+ // 鐢熸垚琛ㄦ牸鏁版嵁
+ function generateTableData() {
+ const tbody = document.getElementById('deviceTableBody');
+ tbody.innerHTML = '';
+ devices.forEach(device => {
+ const row = document.createElement('tr');
+ row.innerHTML = `
+ <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">${device.id}</td>
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${device.name}</td>
+ <td class="px-6 py-4 whitespace-nowrap">
+ <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full ${device.status === 'online' ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'}">
+ ${device.status === 'online' ? '鍦ㄧ嚎' : '绂荤嚎'}
+ </span>
+ </td>
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${device.upData}</td>
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${device.downData}</td>
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${device.lastComm}</td>
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
+ <button class="text-primary hover:text-blue-700 mr-3">
+ <i class="fa fa-eye"></i>
+ </button>
+ <button class="text-warning hover:text-yellow-700 mr-3">
+ <i class="fa fa-edit"></i>
+ </button>
+ <button class="text-danger hover:text-red-700">
+ <i class="fa fa-trash"></i>
+ </button>
+ </td>
+ `;
+ tbody.appendChild(row);
+ });
+ }
+
+ // 鍒濆鍖栧浘琛�
+ function initCharts() {
+ // 涓婅鏁版嵁鍥捐〃
+ const upCtx = document.getElementById('upDataChart').getContext('2d');
+ const upDataChart = new Chart(upCtx, {
+ type: 'line',
+ data: {
+ labels: ['00:00', '03:00', '06:00', '09:00', '12:00', '15:00', '18:00', '21:00'],
+ datasets: [{
+ label: '涓婅鏁版嵁 (KB)',
+ data: [12, 19, 15, 25, 22, 30, 28, 35],
+ borderColor: '#3b82f6',
+ backgroundColor: 'rgba(59, 130, 246, 0.1)',
+ tension: 0.4,
+ fill: true
+ }]
+ },
+ options: {
+ responsive: true,
+ maintainAspectRatio: false,
+ plugins: {
+ legend: {
+ display: false
+ }
+ },
+ scales: {
+ y: {
+ beginAtZero: true
+ }
+ }
+ }
+ });
+
+ // 涓嬭鏁版嵁鍥捐〃
+ const downCtx = document.getElementById('downDataChart').getContext('2d');
+ const downDataChart = new Chart(downCtx, {
+ type: 'line',
+ data: {
+ labels: ['00:00', '03:00', '06:00', '09:00', '12:00', '15:00', '18:00', '21:00'],
+ datasets: [{
+ label: '涓嬭鏁版嵁 (KB)',
+ data: [5, 8, 6, 12, 10, 15, 13, 18],
+ borderColor: '#10b981',
+ backgroundColor: 'rgba(16, 185, 129, 0.1)',
+ tension: 0.4,
+ fill: true
+ }]
+ },
+ options: {
+ responsive: true,
+ maintainAspectRatio: false,
+ plugins: {
+ legend: {
+ display: false
+ }
+ },
+ scales: {
+ y: {
+ beginAtZero: true
+ }
+ }
+ }
+ });
+ }
+
+ // 妯℃嫙瀹炴椂鏁版嵁鏇存柊
+ function simulateRealtimeData() {
+ const realtimeContainer = document.getElementById('realtimeData');
+ const messages = [
+ 'CTU-001 閰嶉�佸皬杞�1鍙�: 杩愯涓紝浣嶇疆: A1鍖�',
+ 'CTU-002 閰嶉�佸皬杞�2鍙�: 寰呮満涓紝浣嶇疆: B2鍖�',
+ 'CTU-004 閰嶉�佸皬杞�3鍙�: 鍏呯數涓紝鐢甸噺: 85%',
+ 'CTU-005 宸℃灏忚溅2鍙�: 宸℃涓紝宸插畬鎴�3/5浠诲姟',
+ 'CTU-007 宸℃灏忚溅3鍙�: 寰呮満涓紝浣嶇疆: C3鍖�',
+ 'CTU-008 閰嶉�佸皬杞�5鍙�: 杩愯涓紝浣嶇疆: D4鍖�'
+ ];
+
+ setInterval(() => {
+ const message = messages[Math.floor(Math.random() * messages.length)];
+ const timestamp = new Date().toLocaleTimeString();
+ const div = document.createElement('div');
+ div.className = 'py-1 border-b border-gray-100';
+ div.innerHTML = `<span class="text-gray-500 text-xs">${timestamp}</span> <span class="text-gray-900">${message}</span>`;
+ realtimeContainer.prepend(div);
+ // 闄愬埗鏄剧ず鏉℃暟
+ if (realtimeContainer.children.length > 20) {
+ realtimeContainer.removeChild(realtimeContainer.lastChild);
+ }
+ }, 2000);
+ }
+
+ // 椤甸潰鍔犺浇瀹屾垚鍚庡垵濮嬪寲
+ document.addEventListener('DOMContentLoaded', function() {
+ generateTableData();
+ initCharts();
+ simulateRealtimeData();
+ });
+ </script>
+</body>
+</html>
\ No newline at end of file
--
Gitblit v1.9.1