Junjie
2026-04-21 2cd165b0fae397281801939182436837e0a55ad0
#堆垛机增加镭射值-V3.0.0.6
14个文件已修改
127 ■■■■■ 已修改文件
src/main/java/com/zy/asrs/controller/CrnController.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/controller/OpenController.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/domain/vo/CrnStateTableVo.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/core/model/protocol/CrnProtocol.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/core/network/entity/ZyCrnStatusEntity.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/core/network/fake/FakeConfigKeys.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/core/network/fake/ZyCrnFakeConnect.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/core/thread/impl/ZySiemensCrnThread.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/core/thread/impl/ZySiemensCrnV2Thread.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/sql/20260416_add_fake_advanced_config.sql 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/components/WatchCrnCard.js 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/static/js/watch/fakeOperationConfig.js 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/watch/fakeOperationConfig.html 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/controller/CrnController.java
@@ -82,6 +82,7 @@
            vo.setXduration(String.format("%.2f", crnProtocol.getXDuration()));    //  走行时长(H)
            vo.setYduration(String.format("%.2f", crnProtocol.getYDuration()));    //  升降时长(H)
            vo.setTaskReceive(crnProtocol.getTaskReceive());
            vo.setLaserValue(crnProtocol.getLaserValue());
            if (crnProtocol.getModeType().equals(CrnModeType.AUTO)) {
                vo.setDeviceStatus("AUTO");
src/main/java/com/zy/asrs/controller/OpenController.java
@@ -446,20 +446,45 @@
            return R.error("参数不能为空");
        }
        Set<String> allowKeys = FakeConfigKeys.ALL_KEYS;
        Map<String, String> normalizedMap = new LinkedHashMap<>();
        for (Map.Entry<String, Object> entry : param.getConfigMap().entrySet()) {
            String key = entry.getKey();
            if (!allowKeys.contains(key)) {
                return R.error("不支持的配置项: " + key);
            }
            String normalized = normalizeFakeConfigValue(key, entry.getValue());
            if (!configService.saveConfigValue(key, normalized)) {
                return R.error("保存失败: " + key);
            normalizedMap.put(key, normalizeFakeConfigValue(key, entry.getValue()));
        }
        validateFakeCrnLaserConfig(normalizedMap);
        for (Map.Entry<String, String> entry : normalizedMap.entrySet()) {
            if (!configService.saveConfigValue(entry.getKey(), entry.getValue())) {
                return R.error("保存失败: " + entry.getKey());
            }
        }
        configService.refreshSystemConfigCache();
        return getFakeConfig();
    }
    private void validateFakeCrnLaserConfig(Map<String, String> normalizedMap) {
        long bayMin = resolveFakeLongValue(normalizedMap, FakeConfigKeys.FAKE_CRN_BAY_MIN);
        long bayMax = resolveFakeLongValue(normalizedMap, FakeConfigKeys.FAKE_CRN_BAY_MAX);
        long laserMin = resolveFakeLongValue(normalizedMap, FakeConfigKeys.FAKE_CRN_LASER_MIN);
        long laserMax = resolveFakeLongValue(normalizedMap, FakeConfigKeys.FAKE_CRN_LASER_MAX);
        if (bayMax <= bayMin) {
            throw new CoolException("堆垛机 bay 最大值必须大于最小值");
        }
        if (laserMax <= laserMin) {
            throw new CoolException("堆垛机镭射最大值必须大于最小值");
        }
    }
    private long resolveFakeLongValue(Map<String, String> normalizedMap, String key) {
        String value = normalizedMap.get(key);
        if (value == null) {
            value = configService.getConfigValue(key, FakeConfigKeys.DEFAULTS.get(key));
        }
        return Long.parseLong(value);
    }
    private String normalizeFakeConfigValue(String key, Object rawValue) {
        if (rawValue == null) {
            throw new CoolException(key + " 参数不能为空");
src/main/java/com/zy/asrs/domain/vo/CrnStateTableVo.java
@@ -106,6 +106,9 @@
    private Integer taskReceive;
    // 镭射值
    private Integer laserValue;
    // 设备状态-用于前端显示
    private String deviceStatus = "OFFLINE";
src/main/java/com/zy/core/model/protocol/CrnProtocol.java
@@ -164,6 +164,11 @@
    public Integer taskReceive;
    /**
     * 镭射值
     */
    private Integer laserValue;
    /**
     * 日志采集时间
     */
    private Long deviceDataLog = System.currentTimeMillis();
src/main/java/com/zy/core/network/entity/ZyCrnStatusEntity.java
@@ -119,6 +119,11 @@
     */
    public Integer taskReceive;
    /**
     * 镭射值
     */
    private Integer laserValue;
    private Integer temp1;
    private Integer temp2;
src/main/java/com/zy/core/network/fake/FakeConfigKeys.java
@@ -20,6 +20,10 @@
    public static final String FAKE_CRN_LEVEL_STEP_DURATION_MS = "fakeCrnLevelStepDurationMs";
    public static final String FAKE_CRN_BAY_STEP_DURATION_MS = "fakeCrnBayStepDurationMs";
    public static final String FAKE_CRN_RESET_DURATION_MS = "fakeCrnResetDurationMs";
    public static final String FAKE_CRN_BAY_MIN = "fakeCrnBayMin";
    public static final String FAKE_CRN_BAY_MAX = "fakeCrnBayMax";
    public static final String FAKE_CRN_LASER_MIN = "fakeCrnLaserMin";
    public static final String FAKE_CRN_LASER_MAX = "fakeCrnLaserMax";
    public static final String FAKE_DUAL_CRN_COMMAND_WAIT_MS = "fakeDualCrnCommandWaitMs";
    public static final String FAKE_DUAL_CRN_TRANSFER_DURATION_MS = "fakeDualCrnTransferDurationMs";
@@ -58,6 +62,10 @@
        defaults.put(FAKE_CRN_LEVEL_STEP_DURATION_MS, "1000");
        defaults.put(FAKE_CRN_BAY_STEP_DURATION_MS, "1000");
        defaults.put(FAKE_CRN_RESET_DURATION_MS, "0");
        defaults.put(FAKE_CRN_BAY_MIN, "1");
        defaults.put(FAKE_CRN_BAY_MAX, "100");
        defaults.put(FAKE_CRN_LASER_MIN, "100");
        defaults.put(FAKE_CRN_LASER_MAX, "10000");
        defaults.put(FAKE_DUAL_CRN_COMMAND_WAIT_MS, "200");
        defaults.put(FAKE_DUAL_CRN_TRANSFER_DURATION_MS, "2000");
        defaults.put(FAKE_DUAL_CRN_PICK_DURATION_MS, "3000");
@@ -90,6 +98,10 @@
                FAKE_CRN_LEVEL_STEP_DURATION_MS,
                FAKE_CRN_BAY_STEP_DURATION_MS,
                FAKE_CRN_RESET_DURATION_MS,
                FAKE_CRN_BAY_MIN,
                FAKE_CRN_BAY_MAX,
                FAKE_CRN_LASER_MIN,
                FAKE_CRN_LASER_MAX,
                FAKE_DUAL_CRN_COMMAND_WAIT_MS,
                FAKE_DUAL_CRN_TRANSFER_DURATION_MS,
                FAKE_DUAL_CRN_PICK_DURATION_MS,
src/main/java/com/zy/core/network/fake/ZyCrnFakeConnect.java
@@ -21,10 +21,33 @@
    private long levelStepDurationMs() { return FakeConfigSupport.getLong(FakeConfigKeys.FAKE_CRN_LEVEL_STEP_DURATION_MS); }
    private long bayStepDurationMs() { return FakeConfigSupport.getLong(FakeConfigKeys.FAKE_CRN_BAY_STEP_DURATION_MS); }
    private long resetDurationMs() { return FakeConfigSupport.getLong(FakeConfigKeys.FAKE_CRN_RESET_DURATION_MS); }
    private long bayMin() { return FakeConfigSupport.getLong(FakeConfigKeys.FAKE_CRN_BAY_MIN); }
    private long bayMax() { return FakeConfigSupport.getLong(FakeConfigKeys.FAKE_CRN_BAY_MAX); }
    private long laserMin() { return FakeConfigSupport.getLong(FakeConfigKeys.FAKE_CRN_LASER_MIN); }
    private long laserMax() { return FakeConfigSupport.getLong(FakeConfigKeys.FAKE_CRN_LASER_MAX); }
    public ZyCrnFakeConnect(DeviceConfig deviceConfig) {
        this.deviceConfig = deviceConfig;
        this.crnStatus = JSON.parseObject(deviceConfig.getFakeInitStatus(), ZyCrnStatusEntity.class);
        if (this.crnStatus != null && this.crnStatus.getBay() != null) {
            updateLaserByBay(this.crnStatus.getBay());
        }
    }
    private void updateLaserByBay(int bay) {
        long bayMin = bayMin();
        long bayMax = bayMax();
        long laserMin = laserMin();
        long laserMax = laserMax();
        if (bayMax <= bayMin || laserMax <= laserMin) {
            this.crnStatus.setLaserValue((int) laserMin);
            return;
        }
        long normalizedBay = Math.max(bayMin, Math.min(bayMax, bay));
        double ratio = (double) (normalizedBay - bayMin) / (double) (bayMax - bayMin);
        long computed = Math.round(laserMin + ratio * (laserMax - laserMin));
        long clamped = Math.max(laserMin, Math.min(laserMax, computed));
        this.crnStatus.setLaserValue((int) clamped);
    }
    @Override
@@ -148,6 +171,7 @@
            for(int i = 0; i < moveLength; i++) {
                initSourcePosY++;
                this.crnStatus.setBay(initSourcePosY);
                updateLaserByBay(initSourcePosY);
                sleep(bayStepDurationMs());
                if (Thread.currentThread().isInterrupted()) {
                    return;
@@ -159,6 +183,7 @@
            for(int i = 0; i < moveLength; i++) {
                initSourcePosY--;
                this.crnStatus.setBay(initSourcePosY);
                updateLaserByBay(initSourcePosY);
                sleep(bayStepDurationMs());
                if (Thread.currentThread().isInterrupted()) {
                    return;
src/main/java/com/zy/core/thread/impl/ZySiemensCrnThread.java
@@ -171,6 +171,7 @@
        crnProtocol.setWalkPos(crnStatus.getWalkPos());
        crnProtocol.setLoaded(crnStatus.getLoaded());
        crnProtocol.setAlarm(crnStatus.getAlarm());
        crnProtocol.setLaserValue(crnStatus.getLaserValue());
        crnProtocol.setTemp1(crnStatus.getTemp1());
        crnProtocol.setTemp2(crnStatus.getTemp2());
        crnProtocol.setTemp3(crnStatus.getTemp3());
src/main/java/com/zy/core/thread/impl/ZySiemensCrnV2Thread.java
@@ -169,6 +169,7 @@
        crnProtocol.setWalkPos(crnStatus.getWalkPos());
        crnProtocol.setLoaded(crnStatus.getLoaded());
        crnProtocol.setAlarm(crnStatus.getAlarm());
        crnProtocol.setLaserValue(crnStatus.getLaserValue());
        crnProtocol.setTemp1(crnStatus.getTemp1());
        crnProtocol.setTemp2(crnStatus.getTemp2());
        crnProtocol.setTemp3(crnStatus.getTemp3());
src/main/resources/application.yml
@@ -1,6 +1,6 @@
# 系统版本信息
app:
  version: 3.0.0.5
  version: 3.0.0.6
  version-type: prd  # prd 或 dev
  i18n:
    default-locale: zh-CN
src/main/resources/sql/20260416_add_fake_advanced_config.sql
@@ -52,6 +52,26 @@
WHERE NOT EXISTS (SELECT 1 FROM sys_config WHERE code = 'fakeCrnResetDurationMs');
INSERT INTO sys_config(name, code, value, type, status, select_type)
SELECT '单工位堆垛机bay最小值', 'fakeCrnBayMin', '1', 1, 1, 'fake'
FROM dual
WHERE NOT EXISTS (SELECT 1 FROM sys_config WHERE code = 'fakeCrnBayMin');
INSERT INTO sys_config(name, code, value, type, status, select_type)
SELECT '单工位堆垛机bay最大值', 'fakeCrnBayMax', '100', 1, 1, 'fake'
FROM dual
WHERE NOT EXISTS (SELECT 1 FROM sys_config WHERE code = 'fakeCrnBayMax');
INSERT INTO sys_config(name, code, value, type, status, select_type)
SELECT '单工位堆垛机镭射最小值', 'fakeCrnLaserMin', '100', 1, 1, 'fake'
FROM dual
WHERE NOT EXISTS (SELECT 1 FROM sys_config WHERE code = 'fakeCrnLaserMin');
INSERT INTO sys_config(name, code, value, type, status, select_type)
SELECT '单工位堆垛机镭射最大值', 'fakeCrnLaserMax', '10000', 1, 1, 'fake'
FROM dual
WHERE NOT EXISTS (SELECT 1 FROM sys_config WHERE code = 'fakeCrnLaserMax');
INSERT INTO sys_config(name, code, value, type, status, select_type)
SELECT '双工位堆垛机互斥轮询间隔(毫秒)', 'fakeDualCrnCommandWaitMs', '200', 1, 1, 'fake'
FROM dual
WHERE NOT EXISTS (SELECT 1 FROM sys_config WHERE code = 'fakeDualCrnCommandWaitMs');
src/main/webapp/components/WatchCrnCard.js
@@ -218,6 +218,7 @@
        { label: "目标库位", value: this.orDash(item.locNo) },
        { label: "是否有物", value: this.orDash(item.loading) },
        { label: "任务接收", value: this.orDash(item.taskReceive) },
        { label: "镭射值", value: this.orDash(item.laserValue) },
        { label: "列", value: this.orDash(item.bay) },
        { label: "层", value: this.orDash(item.lev) },
        { label: "货叉定位", value: this.orDash(item.forkOffset) },
src/main/webapp/static/js/watch/fakeOperationConfig.js
@@ -32,12 +32,16 @@
        {
            key: 'crn',
            title: '单工位堆垛机',
            desc: '控制单工位堆垛机的取放货、Y 轴和 Z 轴节奏。',
            desc: '控制单工位堆垛机的取放货、Y 轴和 Z 轴节奏,并根据 bay 线性计算仿真镭射值。',
            items: [
                { code: 'fakeCrnFetchPutDurationMs', name: '取放货耗时', desc: '单次取货或放货动作耗时。', unit: 'ms', min: 0, max: 600000 },
                { code: 'fakeCrnBayStepDurationMs', name: 'Y轴每步耗时', desc: '堆垛机 bay 方向单步移动耗时。', unit: 'ms', min: 0, max: 600000 },
                { code: 'fakeCrnLevelStepDurationMs', name: 'Z轴每步耗时', desc: '堆垛机 level 方向单步移动耗时。', unit: 'ms', min: 0, max: 600000 },
                { code: 'fakeCrnResetDurationMs', name: '复位耗时', desc: '单工位堆垛机复位命令耗时。', unit: 'ms', min: 0, max: 600000 }
                { code: 'fakeCrnResetDurationMs', name: '复位耗时', desc: '单工位堆垛机复位命令耗时。', unit: 'ms', min: 0, max: 600000 },
                { code: 'fakeCrnBayMin', name: 'bay最小值', desc: '仿真镭射换算使用的 bay 最小边界。', min: 0, max: 100000 },
                { code: 'fakeCrnBayMax', name: 'bay最大值', desc: '仿真镭射换算使用的 bay 最大边界。', min: 0, max: 100000 },
                { code: 'fakeCrnLaserMin', name: '镭射最小值', desc: 'bay 到最小边界时对应的仿真镭射值。', min: 0, max: 1000000 },
                { code: 'fakeCrnLaserMax', name: '镭射最大值', desc: 'bay 到最大边界时对应的仿真镭射值。', min: 0, max: 1000000 }
            ]
        },
        {
@@ -180,6 +184,16 @@
                    }
                }, this);
            }, this);
            var bayMin = parseInt(payload.fakeCrnBayMin, 10);
            var bayMax = parseInt(payload.fakeCrnBayMax, 10);
            var laserMin = parseInt(payload.fakeCrnLaserMin, 10);
            var laserMax = parseInt(payload.fakeCrnLaserMax, 10);
            if (bayMax <= bayMin) {
                throw new Error('堆垛机 bay 最大值必须大于最小值');
            }
            if (laserMax <= laserMin) {
                throw new Error('堆垛机镭射最大值必须大于最小值');
            }
            return payload;
        },
        saveConfig: function () {
src/main/webapp/views/watch/fakeOperationConfig.html
@@ -190,7 +190,7 @@
            <div class="toolbar-left">
                <el-tag size="small" :type="statusInfo.isFake ? 'success' : 'info'">{{ statusInfo.isFake ? '仿真模式' : '非仿真模式' }}</el-tag>
                <el-tag size="small" :type="statusInfo.running ? 'danger' : 'warning'">{{ statusInfo.running ? '运行中' : '未运行' }}</el-tag>
                <span class="toolbar-tip">开关项保存为 Y/N,时间项单位均为毫秒。</span>
                <span class="toolbar-tip">开关项保存为 Y/N,时间项单位均为毫秒</span>
            </div>
            <div>
                <el-button size="small" @click="reloadData" :loading="loading">刷新</el-button>