Bläddra i källkod

1、一键开启、一键关闭组装设备对接多功能卡参数
2、定时开关机,任务下发,组装多功能卡所需参数
3、任务状态结束,同步更新任务主表状态

lihao16 5 månader sedan
förälder
incheckning
c32a192aa6

+ 3 - 0
smsb-modules/smsb-device/src/main/java/com/inspur/device/domain/constants/DeviceConstants.java

@@ -57,4 +57,7 @@ public class DeviceConstants {
     /** 设备多功能卡上报信息 */
     public static final String REDIS_DEVICE_MULTI_CARD_KEY = "global:device:multi:card:";
 
+    /** 设备多功能卡 定时开亮屏配置 */
+    public static final String REDIS_DEVICE_MULTI_CARD_SCHEDULE = "global:device:multi:card:schedule:";
+
 }

+ 3 - 0
smsb-modules/smsb-device/src/main/java/com/inspur/device/domain/constants/DeviceTaskConstants.java

@@ -44,6 +44,9 @@ public class DeviceTaskConstants {
     /** 电源一键关闭 */
     public static final Integer DEVICE_TASK_POWER_OFF = 1012;
 
+    /** 电源定时任务 */
+    public static final Integer DEVICE_TASK_POWER_SCHEDULE = 1013;
+
     /** 设备任务类型 end */
 
     /** 设备任务状态 begin */

+ 9 - 0
smsb-modules/smsb-device/src/main/java/com/inspur/device/mapper/SmsbDevicePowerScheduleMapper.java

@@ -2,8 +2,11 @@ package com.inspur.device.mapper;
 
 import com.inspur.device.domain.SmsbDevicePowerSchedule;
 import com.inspur.device.domain.vo.SmsbDevicePowerScheduleVo;
+import org.apache.ibatis.annotations.Param;
 import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
 
+import java.util.List;
+
 /**
  * 多功能卡电源定时开关Mapper接口
  *
@@ -12,4 +15,10 @@ import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
  */
 public interface SmsbDevicePowerScheduleMapper extends BaseMapperPlus<SmsbDevicePowerSchedule, SmsbDevicePowerScheduleVo> {
 
+    /**
+     * 查询当前设备 所有生效的配置
+     * @param deviceId
+     * @return
+     */
+    List<SmsbDevicePowerScheduleVo> selectInUseVoListByDeviceId(@Param("deviceId") Long deviceId);
 }

+ 9 - 0
smsb-modules/smsb-device/src/main/java/com/inspur/device/service/ISmsbDevicePowerScheduleService.java

@@ -1,7 +1,9 @@
 package com.inspur.device.service;
 
+import cn.hutool.json.JSONObject;
 import com.inspur.device.domain.bo.SmsbDevicePowerScheduleBo;
 import com.inspur.device.domain.vo.SmsbDevicePowerScheduleVo;
+import com.inspur.device.domain.vo.SmsbDeviceVo;
 import org.dromara.common.core.domain.R;
 import org.dromara.common.mybatis.core.page.PageQuery;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
@@ -73,4 +75,11 @@ public interface ISmsbDevicePowerScheduleService {
      * @return
      */
     List<SmsbDevicePowerScheduleVo> getListByDevice(Long deviceId);
+
+    /**
+     * 根据设备ID 组装多功能卡定时配置
+     * @param smsbDeviceVo
+     * @return
+     */
+    JSONObject createDeviceScheduleCron(SmsbDeviceVo smsbDeviceVo);
 }

+ 126 - 0
smsb-modules/smsb-device/src/main/java/com/inspur/device/service/impl/SmsbDevicePowerScheduleServiceImpl.java

@@ -1,16 +1,21 @@
 package com.inspur.device.service.impl;
 
+import cn.hutool.json.JSONArray;
+import cn.hutool.json.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.inspur.device.domain.SmsbDevicePowerSchedule;
 import com.inspur.device.domain.bo.SmsbDevicePowerScheduleBo;
+import com.inspur.device.domain.constants.DeviceConstants;
 import com.inspur.device.domain.vo.SmsbDevicePowerScheduleVo;
+import com.inspur.device.domain.vo.SmsbDeviceVo;
 import com.inspur.device.mapper.SmsbDevicePowerScheduleMapper;
 import com.inspur.device.service.ISmsbDevicePowerScheduleService;
 import lombok.RequiredArgsConstructor;
 import org.dromara.common.core.domain.R;
 import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.StringUtils;
 import org.dromara.common.mybatis.core.page.PageQuery;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.common.satoken.utils.LoginHelper;
@@ -66,6 +71,127 @@ public class SmsbDevicePowerScheduleServiceImpl implements ISmsbDevicePowerSched
         return baseMapper.selectVoList(lqw);
     }
 
+    @Override
+    public JSONObject createDeviceScheduleCron(SmsbDeviceVo smsbDeviceVo) {
+        // 查询当前设备 所有生效的配置
+        List<SmsbDevicePowerScheduleVo> powerScheduleVoList = baseMapper.selectInUseVoListByDeviceId(smsbDeviceVo.getId());
+        if (CollectionUtils.isEmpty(powerScheduleVoList)) {
+            return null;
+        }
+        JSONObject result = new JSONObject();
+        // 1 sn
+        result.set("sn",smsbDeviceVo.getSerialNumber());
+        // 2 taskInfo
+        JSONObject taskInfo = new JSONObject();
+        // 2-1 type
+        taskInfo.set("type","FUNCTIONPOWER");
+        // 2-2 source 任务的发布来源
+        JSONObject source = new JSONObject();
+        // 2-2-1 type
+        source.set("type",1);
+        // 2-2-2 platform
+        source.set("platform",1);
+        taskInfo.set("source",source);
+        // 2-3 data
+        JSONArray data = new JSONArray();
+        JSONObject oneData = new JSONObject();
+        // 2-3-1 enable
+        oneData.set("enable",true);
+        // 2-3-2 portIndex
+        oneData.set("portIndex",1);
+        // 2-3-3 connectIndex
+        oneData.set("connectIndex",1);
+        // 2-3-4 commands
+        JSONArray commands = new JSONArray();
+        for (SmsbDevicePowerScheduleVo powerScheduleVo : powerScheduleVoList) {
+            // 一条记录生成两个command 开屏和息屏
+            JSONObject onCommands = createOneCommands(powerScheduleVo, DeviceConstants.DEVICE_POWER_ON);
+            JSONObject offCommands = createOneCommands(powerScheduleVo, DeviceConstants.DEVICE_POWER_OFF);
+            if (null != onCommands) {
+                commands.add(onCommands);
+            }
+            if (null != offCommands) {
+                commands.add(offCommands);
+            }
+        }
+        oneData.set("commands",commands);
+        data.add(oneData);
+        taskInfo.set("data",data);
+        result.set("taskInfo",taskInfo);
+        return result;
+    }
+
+    private JSONObject createOneCommands(SmsbDevicePowerScheduleVo powerScheduleVo, String powerType) {
+        if (powerType.equals(DeviceConstants.DEVICE_POWER_ON) && StringUtils.isEmpty(powerScheduleVo.getPowerOnTime())) {
+            return null;
+        }
+        if (powerType.equals(DeviceConstants.DEVICE_POWER_OFF) && StringUtils.isEmpty(powerScheduleVo.getPowerOffTime())) {
+            return null;
+        }
+        JSONObject oneCommands = new JSONObject();
+        JSONArray conditions = new JSONArray();
+        for (int i = 0 ; i < 8 ; i++) {
+            JSONObject oneConditions = new JSONObject();
+            String cronStr = "";
+            Integer action = 0;
+            if (powerType.equals(DeviceConstants.DEVICE_POWER_ON)) {
+                action = 0;
+                cronStr = convertTimeToDailyCron(powerScheduleVo.getPowerOnTime());
+            }else {
+                action = 1;
+                cronStr = convertTimeToDailyCron(powerScheduleVo.getPowerOffTime());
+            }
+            JSONArray cron = new JSONArray();
+            cron.add(cronStr);
+            oneConditions.set("cron",cron);
+            oneConditions.set("action",action);
+            oneConditions.set("type","power");
+            oneConditions.set("startTime",powerScheduleVo.getStartDate());
+            oneConditions.set("endTime",powerScheduleVo.getEndDate());
+            oneConditions.set("enable",true);
+            oneConditions.set("powerIndex",i);
+            oneConditions.set("flag","abc");
+            conditions.add(oneConditions);
+        }
+        oneCommands.set("conditions",conditions);
+        return oneCommands;
+    }
+
+    /**
+     * 将时间字符串转换为每天执行的 Cron 表达式
+     * @param timeStr 时间字符串,格式:HH:mm:ss
+     * @return Cron 表达式,如:"10 40 20 * * ?"
+     * @throws IllegalArgumentException 如果时间格式不正确
+     */
+    private String convertTimeToDailyCron(String timeStr) {
+        if (timeStr == null || timeStr.isEmpty()) {
+            throw new IllegalArgumentException("时间字符串不能为空");
+        }
+        // 分割时、分、秒
+        String[] parts = timeStr.split(":");
+        if (parts.length != 3) {
+            throw new IllegalArgumentException("时间格式必须为 HH:mm:ss");
+        }
+        try {
+            int hour = Integer.parseInt(parts[0]);
+            int minute = Integer.parseInt(parts[1]);
+            int second = Integer.parseInt(parts[2]);
+            // 验证时分秒范围
+            validateTimePart(hour, 0, 23, "小时");
+            validateTimePart(minute, 0, 59, "分钟");
+            validateTimePart(second, 0, 59, "秒");
+            // 构建 Cron 表达式:秒 分 时 * * ?
+            return String.format("%d %d %d * * ?", second, minute, hour);
+        } catch (NumberFormatException e) {
+            throw new IllegalArgumentException("时分秒必须为数字", e);
+        }
+    }
+    private void validateTimePart(int value, int min, int max, String partName) {
+        if (value < min || value > max) {
+            throw new IllegalArgumentException(partName + "范围必须是 " + min + "-" + max);
+        }
+    }
+
     /**
      * 查询符合条件的多功能卡电源定时开关列表
      *

+ 5 - 0
smsb-modules/smsb-device/src/main/java/com/inspur/device/service/impl/SmsbDeviceTaskServiceImpl.java

@@ -89,6 +89,11 @@ public class SmsbDeviceTaskServiceImpl implements ISmsbDeviceTaskService {
         deviceTaskDetail.setTaskStatus(taskStatus);
         deviceTaskDetail.setTenantId(smsbDeviceTask.getTenantId());
         smsbDeviceTaskDetailMapper.insert(deviceTaskDetail);
+        // 任务执行完成,更新任务主表为完成
+        if (taskStatus.equals(DeviceTaskConstants.DEVICE_TASK_STATUS_END)) {
+            smsbDeviceTask.setIsEnd(1);
+            baseMapper.updateById(smsbDeviceTask);
+        }
         return R.ok();
     }
 

+ 13 - 0
smsb-modules/smsb-device/src/main/resources/mapper/device/SmsbDevicePowerScheduleMapper.xml

@@ -4,4 +4,17 @@
     "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="com.inspur.device.mapper.SmsbDevicePowerScheduleMapper">
 
+    <select id="selectInUseVoListByDeviceId" parameterType="Long" resultType="com.inspur.device.domain.vo.SmsbDevicePowerScheduleVo">
+        SELECT
+            *
+        FROM
+            smsb_device_power_schedule
+        WHERE
+            device_id = #{deviceId}
+            AND STR_TO_DATE( CONCAT( start_date, ' 00:00:00' ), '%Y-%m-%d %H:%i:%s' ) <![CDATA[ <= ]]> NOW()
+            AND STR_TO_DATE( CONCAT( end_date, ' 23:59:59' ), '%Y-%m-%d %H:%i:%s' ) <![CDATA[ >= ]]> NOW()
+            AND STATUS = 1
+            ORDER BY create_time desc
+    </select>
+
 </mapper>

+ 72 - 6
smsb-modules/smsb-netty/src/main/java/com/inspur/netty/controller/DeviceController.java

@@ -1,10 +1,13 @@
 package com.inspur.netty.controller;
 
 import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.hutool.json.JSONArray;
+import cn.hutool.json.JSONObject;
 import com.inspur.device.domain.bo.SmsbOtaRecordBo;
 import com.inspur.device.domain.constants.DeviceConstants;
 import com.inspur.device.domain.constants.DeviceTaskConstants;
 import com.inspur.device.domain.vo.SmsbDeviceVo;
+import com.inspur.device.service.ISmsbDevicePowerScheduleService;
 import com.inspur.device.service.ISmsbDeviceService;
 import com.inspur.device.service.ISmsbDeviceTaskService;
 import com.inspur.device.service.ISmsbOtaRecordService;
@@ -18,12 +21,10 @@ import org.dromara.common.idempotent.annotation.RepeatSubmit;
 import org.dromara.common.log.annotation.Log;
 import org.dromara.common.log.enums.BusinessType;
 import org.dromara.common.redis.utils.RedisUtils;
-import org.dromara.common.satoken.utils.LoginHelper;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
-import java.time.Duration;
 import java.util.List;
 
 /**
@@ -45,6 +46,9 @@ public class DeviceController {
     @Autowired
     private ISmsbDeviceTaskService deviceTaskService;
 
+    @Autowired
+    private ISmsbDevicePowerScheduleService smsbDevicePowerScheduleService;
+
     /**
      * 重启设备
      *
@@ -134,6 +138,31 @@ public class DeviceController {
         return isSend ? R.ok() : R.fail("长连接发送失败,设备长连接已断开");
     }
 
+    /**
+     * 多功能卡定时任务下发
+     *
+     * @param deviceId
+     * @return
+     */
+    @GetMapping("/scheduleSet/{deviceId}")
+    public R<String> scheduleSet(@PathVariable("deviceId") Long deviceId) {
+        // 1 查询设备信息
+        SmsbDeviceVo deviceVo = smsbDeviceService.getDeviceCacheById(deviceId);
+        if (deviceVo == null) {
+            return R.fail("设备不存在");
+        }
+        // 2 组装多功能卡命令
+        JSONObject scheduleCron = smsbDevicePowerScheduleService.createDeviceScheduleCron(deviceVo);
+        RedisUtils.setCacheObject(DeviceConstants.REDIS_DEVICE_MULTI_CARD_SCHEDULE + deviceVo.getIdentifier(),scheduleCron);
+        // 3 任务中心创建任务
+        String taskParam = scheduleCron.toString();
+        deviceTaskService.createNewDeviceTask(DeviceTaskConstants.DEVICE_TASK_POWER_SCHEDULE, deviceVo, taskParam);
+        // 4 组装待机命令 发送长连接
+        String standbyCmd = deviceVo.getIdentifier() + PushMessageType.CONTROL_MULTI_CARD_SCHEDULE.getValue() + NettyConstants.DATA_PACK_SEPARATOR;
+        boolean isSend = PushMsgUtil.sendV2(deviceVo.getIdentifier(), standbyCmd);
+        return isSend ? R.ok() : R.fail("长连接发送失败,设备长连接已断开");
+    }
+
     /**
      * 音量设置
      *
@@ -197,20 +226,57 @@ public class DeviceController {
         // 2 任务中心创建任务
         String taskParam = "";
         Integer taskType = 0;
+        String powerSetCmd = "";
         if (DeviceConstants.DEVICE_POWER_ON.equals(type)) {
-            taskParam = PushMessageType.CONTROL_POWER_ON.getValue();
+            taskParam = createPowerSetParam(deviceVo,type);
             taskType = DeviceTaskConstants.DEVICE_TASK_POWER_ON;
+            powerSetCmd = deviceVo.getIdentifier() + PushMessageType.CONTROL_POWER_ON.getValue() + NettyConstants.DATA_PACK_SEPARATOR;
         }else {
-            taskParam = PushMessageType.CONTROL_POWER_OFF.getValue();
+            taskParam = createPowerSetParam(deviceVo,type);
             taskType = DeviceTaskConstants.DEVICE_TASK_POWER_OFF;
+            powerSetCmd = deviceVo.getIdentifier() + PushMessageType.CONTROL_POWER_OFF.getValue() + NettyConstants.DATA_PACK_SEPARATOR;
         }
         deviceTaskService.createNewDeviceTask(taskType, deviceVo, taskParam);
         // 3 组装待机命令 发送长连接
-        String standbyCmd = deviceVo.getIdentifier() + taskParam + NettyConstants.DATA_PACK_SEPARATOR;
-        boolean isSend = PushMsgUtil.sendV2(deviceVo.getIdentifier(), standbyCmd);
+        boolean isSend = PushMsgUtil.sendV2(deviceVo.getIdentifier(), powerSetCmd);
         return isSend ? R.ok() : R.fail("长连接发送失败,设备长连接已断开");
     }
 
+    private String createPowerSetParam(SmsbDeviceVo deviceVo, String type) {
+        JSONObject resultJson = new JSONObject();
+        resultJson.set("sn",deviceVo.getSerialNumber());
+        JSONObject data = new JSONObject();
+        data.set("type","FUNCTIONPOWER");
+        JSONObject source = new JSONObject();
+        source.set("type",1);
+        source.set("platform",1);
+        data.set("source",source);
+        JSONArray dataArray = new JSONArray();
+        JSONObject oneData = new JSONObject();
+        JSONArray conditions = createConditions(type);
+        oneData.set("conditions",conditions);
+        dataArray.add(oneData);
+        data.set("data",dataArray);
+        resultJson.set("data",data);
+        return resultJson.toString();
+    }
+
+    private JSONArray createConditions(String type) {
+        JSONArray conditions = new JSONArray();
+        for (int i = 0;i<8 ;i++){
+            JSONObject condition = new JSONObject();
+            if (type.equals(DeviceConstants.DEVICE_POWER_ON)){
+                condition.set("action",0);
+            }else {
+                condition.set("action",1);
+            }
+            condition.set("type","屏体电源");
+            condition.set("powerIndex",i);
+            conditions.add(condition);
+        }
+        return conditions;
+    }
+
     /**
      * 截屏
      *

+ 7 - 2
smsb-modules/smsb-netty/src/main/java/com/inspur/netty/message/push/PushMessageType.java

@@ -85,12 +85,17 @@ public enum PushMessageType {
     /**
      * 一键电源开启
      */
-    CONTROL_POWER_ON("/power/on"),
+    CONTROL_POWER_ON("/multiCard/power/on"),
 
     /**
      * 一键电源关闭
      */
-    CONTROL_POWER_OFF("/power/off"),
+    CONTROL_POWER_OFF("/multiCard/power/off"),
+
+    /**
+     * 设置电源定时开关
+     */
+    CONTROL_MULTI_CARD_SCHEDULE("/multiCard/schedule"),
 
     /**
      * 检查OTA升级

+ 7 - 0
smsb-plus-ui/src/api/smsb/device/device.ts

@@ -144,6 +144,13 @@ export const powerSet = (id: string | number, type: string) => {
   });
 };
 
+export const scheduleSet = (deviceId: string | number) => {
+  return request({
+    url: '/netty/device/scheduleSet/' + deviceId,
+    method: 'get'
+  });
+};
+
 export const startStream = (id: string | number) => {
   return request({
     url: '/stream/start/' + id,

+ 9 - 2
smsb-plus-ui/src/views/smsb/device/index.vue

@@ -326,7 +326,7 @@
             <el-button :loading="buttonLoading" type="primary" @click="handlePower('on')">一键开启</el-button>
             <el-button :loading="buttonLoading" type="primary" @click="handlePower('off')">一键关闭</el-button>
             <el-button :loading="buttonLoading" type="primary" @click="handleSchedule">定时新增</el-button>
-            <!--            <el-button :loading="buttonLoading" type="primary" @click="handleRead">定时回读</el-button>-->
+            <el-button :loading="buttonLoading" type="primary" @click="handleScheduleSet">定时应用</el-button>
           </div>
           <el-divider border-style="double"/>
           <el-table :data="powerScheduleList">
@@ -460,7 +460,8 @@ import {
   startStream,
   stopStream,
   updateDevice,
-  volumeSet
+  volumeSet,
+  scheduleSet
 } from '@/api/smsb/device/device';
 import {DeviceForm, DeviceMultiCardVo, DeviceQuery, DeviceStatisticsVo, DeviceVO} from '@/api/smsb/device/device_type';
 import {DeviceManufacturerVO} from '@/api/smsb/device/deviceManufacturer_type';
@@ -697,6 +698,12 @@ const brightCancel = () => {
   brightDialog.visible = false;
   buttonLoading.value = false;
 }
+const handleScheduleSet = async () => {
+  buttonLoading.value = true;
+  const deviceId = deviceRunInfo.deviceId;
+  await scheduleSet(deviceId).finally(() => buttonLoading.value = false);
+  proxy?.$modal.msgSuccess("应用成功");
+}
 const handleSchedule = async () => {
   const deviceId = deviceRunInfo.deviceId;
   powerForm.value.dateRange = [];

+ 5 - 5
smsb-plus-ui/src/views/smsb/deviceTask/index.vue

@@ -41,19 +41,19 @@
           <el-table-column label="" align="left" v-if="true" width="10"/>
           <el-table-column label="任务ID" align="left" prop="id" v-if="true" width="180"/>
           <el-table-column label="设备ID" align="left" prop="deviceId" width="180"/>
-          <el-table-column label="设备名称" align="left" prop="deviceName"/>
-          <el-table-column label="唯一标识" align="left" prop="identifier" width="220"/>
-          <el-table-column label="任务类型" align="center" prop="taskType" width="150">
+          <el-table-column label="设备名称" align="left" prop="deviceName" width="220" :show-overflow-tooltip="true"/>
+          <el-table-column label="唯一标识" align="left" prop="identifier" width="200" :show-overflow-tooltip="true"/>
+          <el-table-column label="任务类型" align="center" prop="taskType" width="100">
             <template #default="scope">
               <dict-tag :options="smsb_device_task_type" :value="scope.row.taskType"/>
             </template>
           </el-table-column>
-          <el-table-column label="是否完成" align="center" prop="isEnd" width="150">
+          <el-table-column label="是否完成" align="center" prop="isEnd" width="100">
             <template #default="scope">
               <dict-tag :options="smsb_device_task_end" :value="scope.row.isEnd"/>
             </template>
           </el-table-column>
-          <el-table-column label="任务参数" align="left" prop="taskParam"/>
+          <el-table-column label="任务参数" align="left" prop="taskParam" :show-overflow-tooltip="true"/>
           <el-table-column label="创建时间" align="left" prop="createTime" width="160"/>
           <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="120">
             <template #default="scope">