BjkjDevicePullService.java 34.7 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848
package com.iot.scheduler.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.iot.scheduler.model.DeviceToken;
import com.iot.scheduler.model.QxDeviceInfo;
import com.iot.scheduler.model.QxDeviceInfoDetail;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.util.UriComponentsBuilder;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 博晶科技(滁州)有限公司应用数据同步
 */
@Slf4j
@Service
public class BjkjDevicePullService {

    @Value("${bjkj.iot.organizeId:}")
    private String iotOrganizeId;
    @Value("${bjkj.iot.profileId}")
    private String iotProfileId;
    @Value("${bjkj.iot.deviceProfileId}")
    private String iotDeviceProfileId;
    @Value("${bjkj.iot.userName:}")
    private String iotUserName;
    @Value("${bjkj.iot.password:}")
    private String iotPassword;
    @Value("${bjkj.iot.tokenUrl}")
    private String iotTokenUrl;
    @Value("${bjkj.iot.infoUrl}")
    private String iotDeviceInfoUrl;
    @Value("${bjkj.iot.detailUrl}")
    private String iotDeviceDetailUrl;

    @Value("${device.token.url}")
    private String deviceTokenUrl;
    @Value("${bjkj.device.token.userName}")
    private String deviceUserName;
    @Value("${bjkj.device.token.password}")
    private String devicePassword;
    @Value("${device.info.url}")
    private String deviceInfoUrl;
    @Value("${device.detail.url}")
    private String deviceDetailUrl;

    final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    @Resource
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 设备同步主方法
     * @param useRemoteData true: 从远程接口获取设备数据; false: 使用本地设备列表
     */
    public void pullDeviceAndPushToIot(boolean useRemoteData) {
        List<QxDeviceInfo> qxDeviceInfos = new ArrayList<>();
        List<QxDeviceInfoDetail> qxAddDeviceInfoDetails = new ArrayList<>();
        Map<String, QxDeviceInfoDetail> qxDeviceInfoDetailMap = new HashMap<>();

        if (useRemoteData) {
            // 从远程接口获取设备数据
            String deviceResult = getDeviceInfo();
            if (StringUtils.isBlank(deviceResult)) {
                log.error("获取远程设备信息失败");
                return;
            }

            Map<String, Object> deviceInfos = JSON.parseObject(deviceResult, new TypeReference<>() {});
            JSONArray deviceInfoList = (JSONArray) deviceInfos.get("data");

            for (Object o : deviceInfoList) {
                JSONObject deviceInfoJson = (JSONObject) o;
                QxDeviceInfo qxDeviceInfo = new QxDeviceInfo();
                qxDeviceInfo.setDeviceType("DIRECT_CONNECTION");
                qxDeviceInfo.setTransportType("DEFAULT");
                qxDeviceInfo.setOrganizationId(iotOrganizeId);
                qxDeviceInfo.setDeviceProfileId(iotDeviceProfileId);
                qxDeviceInfo.setProfileId(iotProfileId);
                qxDeviceInfo.setLabel("生产设备");

                //设备名称
                String deviceName = deviceInfoJson.getString("deviceName");
                qxDeviceInfo.setName(deviceName);
                qxDeviceInfo.setBrand(deviceName);

                //序列号
                String dtuSn = deviceInfoJson.getString("dtuSn");
                qxDeviceInfo.setSn(dtuSn);

                DeviceToken deviceToken = new DeviceToken();
                deviceToken.setCredentialsType("ACCESS_TOKEN");
                deviceToken.setCredentialsId(dtuSn);
                deviceToken.setCredentialsValue(dtuSn);
                qxDeviceInfo.setDeviceToken(deviceToken);
                qxDeviceInfos.add(qxDeviceInfo);

                //有序列号直接获取灯信息
                if (StringUtils.isNotBlank(dtuSn)) {
                    String deviceInfoDetails = getDeviceInfoDetail(dtuSn);
                    if (StringUtils.isBlank(deviceInfoDetails)) {
                        continue;
                    }

                    Map<String, Object> deviceInfoDetailMap = JSON.parseObject(deviceInfoDetails, new TypeReference<>() {});
                    JSONArray deviceInfoDetailList = (JSONArray) deviceInfoDetailMap.get("data");
                    if (CollectionUtils.isEmpty(deviceInfoDetailList)) {
                        continue;
                    }

                    JSONObject deviceInfoDetailJson = (JSONObject) deviceInfoDetailList.get(0);
                    //灯详情数据
                    //序列号
                    String dtuSnDetail = deviceInfoDetailJson.getString("dtuSn");
                    //开始时间
                    String startTime = deviceInfoDetailJson.getString("startTime");
                    QxDeviceInfoDetail qxDeviceInfoDetail = new QxDeviceInfoDetail();
                    qxDeviceInfoDetail.setAlarm(0);
                    //灯状态(0:灭灯,1:红,2:黄,3:绿,4:蓝)
                    Integer lampState = deviceInfoDetailJson.getInteger("lampState");
                    switch (lampState) {
                        case 0:
                            qxDeviceInfoDetail.setStatus("OFF");
                            break;
                        case 1:
                            qxDeviceInfoDetail.setStatus("ERROR");
                            qxDeviceInfoDetail.setAlarm(1);
                            break;
                        case 2:
                            qxDeviceInfoDetail.setStatus("STAND");
                            break;
                        case 3:
                            qxDeviceInfoDetail.setStatus("RUN");
                            break;
                        default:
                            continue;
                    }

                    if (StringUtils.isNotBlank(startTime)) {
                        try {
                            qxDeviceInfoDetail.setStartTime(dateFormat.parse(startTime));
                        } catch (Exception e) {
                            log.error("时间格式出错");
                            qxDeviceInfoDetail.setStartTime(new Date());
                        }
                    } else {
                        qxDeviceInfoDetail.setStartTime(new Date());
                    }

                    qxDeviceInfoDetail.setDtuSn(dtuSn);
                    qxDeviceInfoDetailMap.put(dtuSn, qxDeviceInfoDetail);
                    qxAddDeviceInfoDetails.add(qxDeviceInfoDetail);
                }
            }
        } else {
            // 使用本地设备列表
            List<String> deviceNameList = getDeviceNameList();
            int totalDevices = deviceNameList.size();

            // 按照 8:1:1 的比例分配状态
            int runCount = (int) Math.round(totalDevices * 0.8);
            int standCount = (int) Math.round(totalDevices * 0.1);
            int offCount = totalDevices - runCount - standCount;

            // 创建状态列表
            List<String> statusList = new ArrayList<>();
            for (int i = 0; i < runCount; i++) {
                statusList.add("RUN");
            }
            for (int i = 0; i < standCount; i++) {
                statusList.add("STAND");
            }
            for (int i = 0; i < offCount; i++) {
                statusList.add("OFF");
            }

            // 打乱状态顺序,使设备状态随机分布
            Collections.shuffle(statusList);

            // 获取当前日期作为序列号前缀
            String datePrefix = "BJKJ20260401";

            for (int i = 0; i < totalDevices; i++) {
                String deviceName = deviceNameList.get(i);
                String status = statusList.get(i);

                // 生成序列号:BJKJ20260401 + 三位数序列号(从001开始)
                String serialNumber = datePrefix + String.format("%03d", (i + 1));

                QxDeviceInfo qxDeviceInfo = new QxDeviceInfo();
                qxDeviceInfo.setDeviceType("DIRECT_CONNECTION");
                qxDeviceInfo.setTransportType("DEFAULT");
                qxDeviceInfo.setOrganizationId(iotOrganizeId);
                qxDeviceInfo.setDeviceProfileId(iotDeviceProfileId);
                qxDeviceInfo.setProfileId(iotProfileId);
                qxDeviceInfo.setLabel("生产设备");
                qxDeviceInfo.setName(deviceName);
                qxDeviceInfo.setBrand(deviceName);
                qxDeviceInfo.setSn(serialNumber);

                DeviceToken deviceToken = new DeviceToken();
                deviceToken.setCredentialsType("ACCESS_TOKEN");
                deviceToken.setCredentialsId(serialNumber);
                deviceToken.setCredentialsValue(serialNumber);
                qxDeviceInfo.setDeviceToken(deviceToken);
                qxDeviceInfos.add(qxDeviceInfo);

                // 创建设备详情(遥测数据)
                QxDeviceInfoDetail qxDeviceInfoDetail = new QxDeviceInfoDetail();
                qxDeviceInfoDetail.setAlarm(0);
                qxDeviceInfoDetail.setStatus(status);
                qxDeviceInfoDetail.setStartTime(new Date());
                qxDeviceInfoDetail.setDtuSn(serialNumber);
                qxDeviceInfoDetailMap.put(serialNumber, qxDeviceInfoDetail);
                qxAddDeviceInfoDetails.add(qxDeviceInfoDetail);

                log.debug("生成设备 - 名称: {}, 序列号: {}, 状态: {}", deviceName, serialNumber, status);
            }

            log.info("设备状态分配统计 - 总数: {}, RUN: {}, STAND: {}, OFF: {}",
                    totalDevices, runCount, standCount, offCount);
        }

        // 上传数据到IoT平台
        uploadToIotPlatform(qxDeviceInfos, qxAddDeviceInfoDetails);
    }

    /**
     * 获取IoT平台Token并上传设备数据
     */
    private void uploadToIotPlatform(List<QxDeviceInfo> qxDeviceInfos, List<QxDeviceInfoDetail> qxAddDeviceInfoDetails) {
        // 获取IoT平台Token
        Map<String, String> qxParam = new HashMap<>(2);
        qxParam.put("username", iotUserName);
        qxParam.put("password", iotPassword);

        HttpPost qxHttpPost = new HttpPost(iotTokenUrl);
        String qxResult = sendPost(qxHttpPost, JSON.toJSONString(qxParam));
        if (StringUtils.isBlank(qxResult)) {
            log.error("获取IoT平台Token失败");
            return;
        }

        Map<String, Object> qxRes = JSON.parseObject(qxResult, new TypeReference<Map<String, Object>>() {});
        String qxAccessToken = (String) qxRes.get("token");
        if (StringUtils.isBlank(qxAccessToken)) {
            log.error("IoT平台Token为空");
            return;
        }

        BasicHeader qxAuthorization = new BasicHeader("X-Authorization", "Bearer " + qxAccessToken);

        // 批量上传设备信息
        if (!CollectionUtils.isEmpty(qxDeviceInfos)) {
            HttpPost qxDeviceInfoPost = new HttpPost(iotDeviceInfoUrl);
            qxDeviceInfoPost.addHeader(qxAuthorization);

            int successCount = 0;
            int failCount = 0;

            for (QxDeviceInfo qxDeviceInfo : qxDeviceInfos) {
                try {
                    String syncDeviceInfo = sendPost(qxDeviceInfoPost, JSON.toJSONString(qxDeviceInfo));

                    if (StringUtils.isNotBlank(syncDeviceInfo)) {
                        Map<String, Object> result = JSON.parseObject(syncDeviceInfo, new TypeReference<Map<String, Object>>() {});
                        if (result.containsKey("id") || (result.containsKey("success") && (Boolean) result.get("success"))) {
                            successCount++;
                            log.info("设备上传成功 - 名称: {}, 序列号: {}", qxDeviceInfo.getName(), qxDeviceInfo.getSn());
                        } else {
                            failCount++;
                            log.error("设备上传失败 - 名称: {}, 序列号: {}, 返回: {}",
                                    qxDeviceInfo.getName(), qxDeviceInfo.getSn(), syncDeviceInfo);
                        }
                    } else {
                        failCount++;
                        log.error("设备上传返回为空 - 名称: {}, 序列号: {}", qxDeviceInfo.getName(), qxDeviceInfo.getSn());
                    }
                } catch (Exception e) {
                    failCount++;
                    log.error("设备上传异常 - 名称: {}, 序列号: {}", qxDeviceInfo.getName(), qxDeviceInfo.getSn(), e);
                }
            }

            log.info("设备上传完成 - 成功: {}, 失败: {}", successCount, failCount);
        }

        // 批量上传设备详情(遥测数据)
        if (!CollectionUtils.isEmpty(qxAddDeviceInfoDetails)) {
            int successCount = 0;
            int failCount = 0;

            for (QxDeviceInfoDetail qxDeviceInfoDetail : qxAddDeviceInfoDetails) {
                try {
                    String qxDeviceInfoDetailUrlStr = iotDeviceDetailUrl + qxDeviceInfoDetail.getDtuSn() + "/telemetry";
                    HttpPost qxDeviceInfoDetailPost = new HttpPost(qxDeviceInfoDetailUrlStr);
                    qxDeviceInfoDetailPost.addHeader(qxAuthorization);

                    String syncDeviceInfoDetail = sendPost(qxDeviceInfoDetailPost, JSON.toJSONString(qxDeviceInfoDetail));
                    successCount++;
                    log.debug("设备详情上传成功 - 序列号: {}, 状态: {}",
                            qxDeviceInfoDetail.getDtuSn(), qxDeviceInfoDetail.getStatus());
                } catch (Exception e) {
                    failCount++;
                    log.error("设备详情上传异常 - 序列号: {}", qxDeviceInfoDetail.getDtuSn(), e);
                }
            }

            log.info("设备详情上传完成 - 成功: {}, 失败: {}", successCount, failCount);
        }
    }

    /**
     * 获取设备名称列表
     */
    private List<String> getDeviceNameList() {
        return Arrays.asList(
                "组装机_1106100002",
                "组装机撕膜检改造_1106100003",
                "自动标签打印机_2213140002",
                "喷码机_1111190002",
                "喷码机皮带线_1301110005",
                "喷码滚轮线_1301100002",
                "FI检查流水线(6M)_1301110003",
                "流水线(9M)_1301110001",
                "自动Tape贴附机_1110180001",
                "客户码及易撕贴贴附机_2213140003",
                "真空打包机_1401110002",
                "AG膜贴膜机_1110170001",
                "贴合线AG贴膜机装机_1110170002",
                "贴合线AG贴膜机装机_1110170003",
                "脱泡机_1110170004",
                "OCR贴合机_1110160001",
                "橱窗式老化炉_1202040001",
                "出货膜贴膜机_1110170002",
                "抽屉式老化炉_1202040004",
                "抽屉式老化炉_1202040005",
                "凌云AOI_1201020002",
                "点灯机_1202070001",
                "点灯机_1202070002",
                "点灯机_1202070003",
                "点灯机_1202070004",
                "点灯机_1202070005",
                "点灯机_1202070006",
                "点灯机_1202070007",
                "点灯机_1202070008",
                "点灯机_1202070009",
                "5点亮度治具_1202080001",
                "OTP治具_1202080004",
                "OTP治具_1202080005",
                "OTP治具_1202080006",
                "OTP治具_1202080007",
                "半自动OCA软对硬贴合机_1110150001",
                "半自动OCA硬对硬贴合机_1110150002",
                "半自动脱泡机_1107110002",
                "2.5次元_1203010002",
                "膜厚测量仪_1203020001",
                "水滴角测试仪_1203030001",
                "条码打印机/型号:斑马105SL 分_2213150001",
                "条码打印机/型号:斑马105SL 分_2213150003",
                "日置数字照度计_1203040001",
                "无尘室专用吸尘器_1401200001",
                "能量计/UV_1203050001",
                "测温仪/日本图技_1203060001",
                "锥入度仪_1203070001",
                "缠膜机_1401180001",
                "玻璃二位蚀刻码扫码治具_2211130001",
                "防爆柜_1401100001",
                "重锤式表面电阻测试仪_1203080001",
                "手持式热熔打包机_1401190001",
                "手持式热熔打包机_1401190002",
                "三坐标_1203090001",
                "背光点胶组装机_1106100004",
                "双联屏AOI_1201020004",
                "显微镜_1203100001",
                "烤箱_1202040006",
                "上板机_1301010001",
                "镭雕机_1101030001",
                "0.8m接驳台_1301030001",
                "印刷机_1102040001",
                "印刷机_1102040002",
                "0.5m接驳台_1301030002",
                "0.5m接驳台_1301030003",
                "移载机_1301080001",
                "0.5m双轨接驳台_1301030004",
                "SPI_1201010001",
                "1.2m双规筛选接驳台_1301030005",
                "1.2m双规接驳台_1301030006",
                "贴片机_1101010001",
                "贴片机_1101010002",
                "贴片机_1101010003",
                "贴片机_1101010004",
                "0.5m双轨接驳台_1301030007",
                "0.5m移载机_1301080002",
                "1m接驳台_1301030008",
                "转角机_1301030001",
                "0.5m接驳台_1301030009",
                "0.85m单轨伸缩过道_1301070001",
                "0.85m单轨伸缩过道_1301070002",
                "0.5m接驳台_1301030010",
                "转角机_1301030002",
                "0.5m接驳台_1301030011",
                "0.5m移载机_130108003",
                "0.5m双轨接驳台_1301030012",
                "回流焊_1103050001",
                "0.5m双轨接驳台_1301030013",
                "0.5m移载机_1301080004",
                "0.5m双轨接驳台_1301030014",
                "双轨储板机_1301050001",
                "0.5m双轨接驳台_1301030015",
                "AOI_1201030001",
                "0.5m双轨接驳台_1301030016",
                "0.5m移载机_1301080005",
                "0.5m接驳台_1301030017",
                "0.8m单轨筛选接驳台_1301030018",
                "0.5m接驳台_1301030019",
                "转角机_1301030003",
                "0.7m接驳台_1301030020",
                "0.85m单轨伸缩过道_1301070003",
                "翻板机_1301040001",
                "0.7m接驳台_1301030021",
                "转角机_1301030004",
                "1m接驳台_1301030022",
                "0.8米接驳台_1301030023",
                "1.2米接驳台_1301030024",
                "选择焊_1103050002",
                "0.8m接驳台_1301030025",
                "ICT测试_1202050001",
                "0.8m接驳台_1301030026",
                "双轨储板机_1301050002",
                "0.8m接驳台_1301030027",
                "分板机_1104060001",
                "MPM印刷机_1102040003",
                "德律SPI_1201010002",
                "德律AOI_1201030002",
                "多功能贴片机_1101010005",
                "回流焊_1103050002",
                "德律AOI_1201030003",
                "离线分板机_1104060002",
                "离线镭雕机_1101030002",
                "离线ICT测试_1202050002",
                "冲压机_1104060003",
                "叠板上板机_1301010002",
                "1米接驳台_1301030028",
                "1米接驳台_1301030029",
                "1米接驳台_1301030030",
                "1米接驳台_1301030031",
                "1米接驳台_1301030032",
                "储板机(冷却)_1301050003",
                "PCBA烧录屏蔽罩安装_1202080001",
                "扬声器安装_1106100001",
                "PCBA安装/FPC检测_1202060002",
                "中控PCBA第三次烧录&后壳安装_1202060003",
                "卡爪固定_1105070002",
                "总成AOI EOL测试_1202060001",
                "支架/散热硅胶安装_1105070001",
                "后壳固定_1105090001",
                "提升机_1301090001",
                "Plasma清洁_1109140001",
                "点胶_1111170001",
                "TLCM与支架贴合_1110150003",
                "后壳组装_1105090002",
                "提升机_1301090002",
                "UWB测试夹具_1101030002",
                "UWB测试夹具_1101030003",
                "UWB测试夹具_1101030004",
                "UWB测试夹具_1101030005",
                "UWB测试夹具_1101030006",
                "HY11测试夹具_1111100001",
                "HY11测试夹具_1111100002",
                "离线ICT_1202050003",
                "离线烧录_1111170001",
                "离线3D X-Ray_1202090001",
                "脱泡机_1107110003",
                "半自动OCA贴合机_1110150003",
                "半自动背光点胶组装机_1106100005",
                "热拆解返修_1106100006",
                "清胶返修_1106100007",
                "切割返修_1106100008",
                "展威切割机_1104060005",
                "展威切割机_1104060006",
                "喷码机_1111190004",
                "钢网清洗机_1401010001",
                "钢网检查机_1401010002",
                "干燥箱_1401010003",
                "干燥箱_1401030002",
                "干燥箱_1401030003",
                "干燥箱_1401030004",
                "点料机_1401060001",
                "点料机_1401060002",
                "锡膏搅拌机_1401070001",
                "抽真空机_1401110001",
                "电烙铁_1401140001",
                "炉温测试仪_1401150001",
                "应力测试仪_1202060001",
                "Tray清洗机_001",
                "喷淋工位_002",
                "抖动1工位_003",
                "抖动2工位_004",
                "抖动3工位_005",
                "烘干1工位_006",
                "烘干2工位_007",
                "烘干3工位_008",
                "烘干4工位_009",
                "大尺寸Tary水洗机_010",
                "喷淋1工位_011",
                "喷淋2工位_012",
                "喷淋3工位_013",
                "烘干1工位_014",
                "烘干2工位_015",
                "烘干3工位_016",
                "烘干4工位_017",
                "烘干5工位_018",
                "风刀背板清洗机_024",
                "水洗背板清洗机_025",
                "喷淋1工位_026",
                "喷淋2工位_027",
                "喷淋3工位_028",
                "烘干1工位_029",
                "烘干2工位_030",
                "烘干3工位_031",
                "烘干4工位_032",
                "烘干5工位_033",
                "烘干6工位_034",
                "胶框清洗机_039",
                "超声波工位_040",
                "喷淋1工位_041",
                "喷淋2工位_042",
                "烘干1工位_043",
                "烘干2工位_044",
                "烘干3工位_045",
                "烘干4工位_046",
                "烘干5工位_047",
                "三合一小尺寸膜片清洗机_051",
                "三合一大尺寸膜片清洗机_052",
                "膜片单清设备_053",
                "单工位AOI_054",
                "离线背板贴胶机_055",
                "离线LGP组装设备_056",
                "胶条1工位_057",
                "胶条2工位_058",
                "滚压1工位_059",
                "胶条3工位_060",
                "滚压2工位_061",
                "反射片组装工位_062",
                "覆膜工位_063",
                "离线胶框贴胶机_064",
                "离线背板侧壁贴胶机_065",
                "背板侧壁胶工位_066",
                "LB导热胶工位_067",
                "反射片固定胶工位_068",
                "覆膜机_069",
                "泡棉贴胶机_070",
                "胶条1工位_071",
                "胶条2工位_072",
                "胶条3工位_073",
                "胶条4工位_074",
                "划压工位_075",
                "撕膜1工位_076",
                "撕膜2工位_077",
                "称重工位_078",
                "背板贴胶机_079",
                "膜片组装机_080",
                "扩散工位_081",
                "增光工位_082",
                "DBEF工位_083",
                "防窥膜工位_084",
                "覆膜机_085",
                "背板贴胶机_086",
                "膜片组装机_087",
                "扩散工位_088",
                "增光工位_089",
                "DBEF工位_090",
                "防窥膜工位_091",
                "膜移检测工位_092",
                "扣框工位_093",
                "胶框保压工位_094",
                "前加工设备_095",
                "PCB工位_096",
                "标签+小黑胶工位_097",
                "翻转工位_098",
                "堵孔1工位_099",
                "堵孔2工位_100",
                "灯条侧边胶工位_101",
                "导热胶工位_102",
                "反射片固定胶工位_103",
                "泡棉贴胶机_104",
                "胶条1工位_105",
                "胶条2工位_106",
                "胶条3工位_107",
                "胶条4工位_108",
                "划压工位_109",
                "半成品组装_110",
                "LB保压工位_111",
                "胶条1工位_112",
                "滚压1工位_113",
                "胶条2工位_114",
                "滚压2工位_115",
                "胶条3工位_116",
                "滚压3工位_117",
                "胶条4工位_118",
                "撕膜工位_119",
                "反射片贴合工位_120",
                "导光板组装工位_121",
                "CCD检查+搬运工位_122",
                "膜间胶1工位_123",
                "扩散工位_124",
                "膜间胶2工位_125",
                "增光工位_126",
                "膜间胶3工位_127",
                "DBEF工位_128",
                "膜间胶4工位_129",
                "防窥膜工位_130",
                "膜移检测工位_131",
                "扣框工位_132",
                "包边机_133",
                "胶条1工位_134",
                "胶条2工位_135",
                "胶条3工位_136",
                "胶条4工位_137",
                "撕膜1工位_138",
                "撕膜2工位_139",
                "PET工位_140",
                "覆膜机_141",
                "自动AOI_142",
                "AVI_143",
                "半自动CCD检查设备_144",
                "大尺寸CCD检查设备_145",
                "喷码机_146",
                "螺纹柱检查设备_147"
        );
    }

    // 保留原方法以保持兼容性
    public void pullDeviceAndPushToIot() {
        pullDeviceAndPushToIot(true);
    }

    public void pullDeviceAndPushToIot2() {
        pullDeviceAndPushToIot(false);
    }

    public String getDeviceInfoDetail(String dtuSn) {
        String accessToken = getAccessToken();
        Map<String, String> dtuSnOb = new HashMap<>(1);
        dtuSnOb.put("dtuSn", dtuSn);
        Map<String, String> headerMap = new HashMap<>(1);
        headerMap.put("Authorization", "Bearer " + accessToken);
        String deviceInfoDetail = sendRequestGet(deviceDetailUrl, dtuSnOb, headerMap);
        if (StringUtils.isBlank(deviceInfoDetail)) {
            return null;
        }

        Map<String, Object> deviceInfoDetailMap = JSON.parseObject(deviceInfoDetail,
                new TypeReference<>() {
                });
        Integer deviceInfoDetailCode = (Integer) deviceInfoDetailMap.get("code");
        if (deviceInfoDetailCode != 200) {
            return null;
        }

        JSONArray deviceInfoDetailList = (JSONArray) deviceInfoDetailMap.get("data");
        if (CollectionUtils.isEmpty(deviceInfoDetailList)) {
            return null;
        }

        JSONObject deviceInfoDetailJson = (JSONObject) deviceInfoDetailList.get(0);
        //灯详情数据
        //灯状态(0:灭灯,1:红,2:黄,3:绿,4:蓝)
        Integer lampState = deviceInfoDetailJson.getInteger("lampState");
        if (lampState == null) {
            return null;
        }

        return deviceInfoDetail;
    }

    public String getDeviceInfo() {
        String accessToken = getAccessToken();
        // 初始化headerMap并设置Authorization
        Map<String, String> headerMap = new HashMap<>(1);
        headerMap.put("Authorization", "Bearer " + accessToken);

        Map<String, String> paramsMap = new HashMap<>();
        paramsMap.put("groupName", "默认组织");

        // 第一次请求设备信息
        String deviceResult = sendRequestGet(deviceInfoUrl, paramsMap, headerMap);

        // 检查设备信息是否为空
        if (StringUtils.isBlank(deviceResult)) {
            return null;
        }

        // 解析设备信息
        Map<String, Object> deviceInfos = JSON.parseObject(deviceResult, new TypeReference<>() {
        });
        Integer deviceInfoCode = (Integer) deviceInfos.get("code");

        // 如果code不为200,可能是accessToken失效,重新获取token并重试
        if (deviceInfoCode != 200) {
            accessToken = getAccessToken();
            if (StringUtils.isEmpty(accessToken)) {
                return null;
            }

            // 更新headerMap中的Authorization
            headerMap.put("Authorization", "Bearer " + accessToken);

            // 第二次请求设备信息
            deviceResult = sendRequestGet(deviceInfoUrl, paramsMap, headerMap);
            if (StringUtils.isBlank(deviceResult)) {
                return null;
            }

            // 重新解析设备信息
            deviceInfos = JSON.parseObject(deviceResult, new TypeReference<>() {
            });
            deviceInfoCode = (Integer) deviceInfos.get("code");

            // 如果第二次请求仍然失败,返回错误信息
            if (deviceInfoCode != 200) {
                return null;
            }
        }

        // 返回成功的设备信息
        return deviceResult;
    }

    private String getAccessToken() {
        String accessToken = "";
        String redisKey = "bjkj_device_token";
        if (StringUtils.isNotBlank(redisTemplate.opsForValue().get(redisKey)) && redisTemplate.getExpire(redisKey) > 0) {
            return redisTemplate.opsForValue().get(redisKey);
        }

        Map<String, String> param = new HashMap<>(2);
        param.put("username", deviceUserName);
        param.put("password", devicePassword);
        HttpPost httpPost = new HttpPost(deviceTokenUrl);
        String result = sendPost(httpPost, JSON.toJSONString(param));
        if (StringUtils.isBlank(result)) {
            return accessToken;
        }

        Map<String, Object> res = JSON.parseObject(result, new TypeReference<>() {
        });

        Integer code = (Integer) res.get("code");
        if (code == 200) {
            JSONObject data = (JSONObject) res.get("data");
            accessToken = (String) data.get("token");
            redisTemplate.opsForValue().set(redisKey, accessToken, 3600, TimeUnit.SECONDS); // 一小时过期
        }

        return accessToken;
    }

    public static String sendRequestGet(String url, Map<String, String> params, Map<String, String> header) {
        //实例化httpclient
        CloseableHttpClient httpclient = HttpClients.createDefault();
        url = builderUrl(url, params);
        //请求结果
        String content = "";
        //实例化get方法
        HttpGet httpget = new HttpGet(url);
        if (!CollectionUtils.isEmpty(header)) {
            for (Map.Entry<String, String> entry : header.entrySet()) {
                httpget.setHeader(entry.getKey(), entry.getValue());
            }
        }

        try (CloseableHttpResponse response = httpclient.execute(httpget)) {

            //执行get方法
            if (response.getStatusLine().getStatusCode() == 200) {
                content = EntityUtils.toString(response.getEntity(), "UTF-8");
            }
        } catch (IOException e) {
            log.error("sendRequest---GET Error!", e);
        }
        return content;
    }

    private static String builderUrl(String url, Map<String, String> params) {
        UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromHttpUrl(url);
        if (!CollectionUtils.isEmpty(params)) {
            MultiValueMap<String, String> paramsValue = new LinkedMultiValueMap<>();
            for (Map.Entry<String, String> entry : params.entrySet()) {
                paramsValue.add(entry.getKey(), entry.getValue());
            }

            uriBuilder = uriBuilder.queryParams(paramsValue);
        }

        return uriBuilder.toUriString();
    }

    private String sendPost(HttpPost httpPost, String jsonData) {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        StringEntity entity = new StringEntity(jsonData, ContentType.create("application/json", Consts.UTF_8));
        httpPost.setEntity(entity);
        String result = null;
        try {
            CloseableHttpResponse execute = httpClient.execute(httpPost);
            HttpEntity res = execute.getEntity();
            InputStream is = res.getContent();
            int len;
            byte[] buf = new byte[128];
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            while ((len = is.read(buf)) != -1) {
                byteArrayOutputStream.write(buf, 0, len);
            }
            result = byteArrayOutputStream.toString();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }
}