| | |
| | | @Autowired |
| | | private InfluxDBClient influxDBClient; |
| | | |
| | | |
| | | |
| | | /** |
| | | * 写入数据 |
| | | * |
| | |
| | | |
| | | Job getJobByBarcodeAndJobSts(String barcode, Integer jobSts); |
| | | |
| | | Job getJobByBarcode(String barcode); |
| | | Job getJobByBarcode(String barcode, Integer jobSts); |
| | | |
| | | Job getJobByJobNo(Integer jobNo); |
| | | |
| | |
| | | if (de != null) { |
| | | de.setSqlData(devp); |
| | | baseMapper.updateById(de); |
| | | }else { |
| | | baseMapper.insert(devp); |
| | | } |
| | | } |
| | | } |
| | |
| | | } |
| | | |
| | | @Override |
| | | public Job getJobByBarcode(String barcode) { |
| | | return baseMapper.getJobByBarcode(barcode); |
| | | public Job getJobByBarcode(String barcode, Integer jobSts) { |
| | | return baseMapper.getJobByBarcodeAndJobSts(barcode,jobSts); |
| | | } |
| | | |
| | | @Override |
| | |
| | | } |
| | | 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); |
| | | } |
| | | } |
| | |
| | | |
| | | do { |
| | | workNo = workNo >= eNo ? sNo : workNo + 1; |
| | | } while (jobService.getJobByJobNo(workNo) == null); |
| | | } while (jobService.getJobByJobNo(workNo) != null); |
| | | |
| | | if (workNo > 0) { |
| | | wrkLastno.setWrkNo(workNo); |
| | |
| | | } |
| | | // 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()); |
| | |
| | | } |
| | | } |
| | | |
| | | 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; |
| | |
| | | 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); |
| | |
| | | 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()); |
| | |
| | | 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: |
| | |
| | | |
| | | logging: |
| | | file: |
| | | path: stock/out/cv/logs |
| | | path: /stock/out/cv/logs |
| | | |
| | | wms: |
| | | url: 10.10.10.220:8081 |
| | |
| | | select * |
| | | from cv_job |
| | | where barcode = #{barcode} |
| | | and job_sts = #{jobSts} |
| | | order by id desc limit 1 |
| | | </select> |
| | | |
| | |
| | | <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> |
| | |
| | | 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 { |
| | |
| | | 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; |
| | |
| | | */ |
| | | @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); |
| | | } |
| | |
| | | @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); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | 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; |
| | | } |
| | | |
| | | |
| | | } |
| New file |
| | |
| | | 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()); |
| | | } |
| | | |
| | | } |
| | |
| | | #token: apiv3_Jx1SvmBMV_kikGhc4eZJQbeGmNYN7KX1GdpoR9MClkKzMxSJ0MPKM_O2Xt3o1hVyRikMmlxZ_h9zfy6ybC5Idg |
| | | database: rcs |
| | | token: apiv3_116RKycNhxbf62Nys4zthC05aRD-aidzhEpEpLtsFuedhJTaCtVklNrzHs9LHxBWMuzDclBHVgToGoQuWGiIIA |
| | | retention-period: 30d |
| | | createDatabaseUrl: ${influxdb3.url}/api/v3/configure/database |
| New file |
| | |
| | | <!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> |