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