Sfoglia il codice sorgente

计算剩余圈数,处理多线程死锁

wukai 1 anno fa
parent
commit
a190675d07

+ 127 - 0
ruoyi-admin/src/main/java/com/ruoyi/biz/controller/TwinPanHeadInfoController.java

@@ -0,0 +1,127 @@
+package com.ruoyi.biz.controller;
+
+import java.util.List;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.ModelMap;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import com.ruoyi.common.annotation.Log;
+import com.ruoyi.common.enums.BusinessType;
+import com.ruoyi.biz.domain.TwinPanHeadInfo;
+import com.ruoyi.biz.service.ITwinPanHeadInfoService;
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.common.core.page.TableDataInfo;
+
+/**
+ * 设备盘头信息Controller
+ * 
+ * @author ruoyi
+ * @date 2024-05-29
+ */
+@Controller
+@RequestMapping("/biz/panHead")
+public class TwinPanHeadInfoController extends BaseController
+{
+    private String prefix = "biz/panHead";
+
+    @Autowired
+    private ITwinPanHeadInfoService twinPanHeadInfoService;
+
+    @RequiresPermissions("biz:panHead:view")
+    @GetMapping()
+    public String panHead()
+    {
+        return prefix + "/panHead";
+    }
+
+    /**
+     * 查询设备盘头信息列表
+     */
+    @RequiresPermissions("biz:panHead:list")
+    @PostMapping("/list")
+    @ResponseBody
+    public TableDataInfo list(TwinPanHeadInfo twinPanHeadInfo)
+    {
+        startPage();
+        List<TwinPanHeadInfo> list = twinPanHeadInfoService.selectTwinPanHeadInfoList(twinPanHeadInfo);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出设备盘头信息列表
+     */
+    @RequiresPermissions("biz:panHead:export")
+    @Log(title = "设备盘头信息", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    @ResponseBody
+    public AjaxResult export(TwinPanHeadInfo twinPanHeadInfo)
+    {
+        List<TwinPanHeadInfo> list = twinPanHeadInfoService.selectTwinPanHeadInfoList(twinPanHeadInfo);
+        ExcelUtil<TwinPanHeadInfo> util = new ExcelUtil<TwinPanHeadInfo>(TwinPanHeadInfo.class);
+        return util.exportExcel(list, "设备盘头信息数据");
+    }
+
+    /**
+     * 新增设备盘头信息
+     */
+    @GetMapping("/add")
+    public String add()
+    {
+        return prefix + "/add";
+    }
+
+    /**
+     * 新增保存设备盘头信息
+     */
+    @RequiresPermissions("biz:panHead:add")
+    @Log(title = "设备盘头信息", businessType = BusinessType.INSERT)
+    @PostMapping("/add")
+    @ResponseBody
+    public AjaxResult addSave(TwinPanHeadInfo twinPanHeadInfo)
+    {
+        return toAjax(twinPanHeadInfoService.insertTwinPanHeadInfo(twinPanHeadInfo));
+    }
+
+    /**
+     * 修改设备盘头信息
+     */
+    @RequiresPermissions("biz:panHead:edit")
+    @GetMapping("/edit/{infoId}")
+    public String edit(@PathVariable("infoId") Long infoId, ModelMap mmap)
+    {
+        TwinPanHeadInfo twinPanHeadInfo = twinPanHeadInfoService.selectTwinPanHeadInfoByInfoId(infoId);
+        mmap.put("twinPanHeadInfo", twinPanHeadInfo);
+        return prefix + "/edit";
+    }
+
+    /**
+     * 修改保存设备盘头信息
+     */
+    @RequiresPermissions("biz:panHead:edit")
+    @Log(title = "设备盘头信息", businessType = BusinessType.UPDATE)
+    @PostMapping("/edit")
+    @ResponseBody
+    public AjaxResult editSave(TwinPanHeadInfo twinPanHeadInfo)
+    {
+        return toAjax(twinPanHeadInfoService.updateTwinPanHeadInfo(twinPanHeadInfo));
+    }
+
+    /**
+     * 删除设备盘头信息
+     */
+    @RequiresPermissions("biz:panHead:remove")
+    @Log(title = "设备盘头信息", businessType = BusinessType.DELETE)
+    @PostMapping( "/remove")
+    @ResponseBody
+    public AjaxResult remove(String ids)
+    {
+        return toAjax(twinPanHeadInfoService.deleteTwinPanHeadInfoByInfoIds(ids));
+    }
+}

+ 180 - 0
ruoyi-admin/src/main/java/com/ruoyi/biz/domain/TwinPanHeadInfo.java

@@ -0,0 +1,180 @@
+package com.ruoyi.biz.domain;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.common.annotation.Excel;
+import com.ruoyi.common.core.domain.BaseEntity;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+import java.util.Date;
+
+/**
+ * 设备盘头信息对象 twin_pan_head_info
+ *
+ * @author ruoyi
+ * @date 2024-05-29
+ */
+@ApiModel(value = "ITwinPanHeadInfo", description = "设备盘头信息")
+public class TwinPanHeadInfo extends BaseEntity {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 信息ID
+     */
+    private Long infoId;
+
+    /**
+     * 设备ID
+     */
+    @Excel(name = "设备ID")
+    @ApiModelProperty("设备ID")
+    private Long deviceId;
+
+    /**
+     * 盘头号
+     */
+    @Excel(name = "盘头号")
+    @ApiModelProperty("盘头号")
+    private Long phNum;
+
+    /**
+     * 盘头最大圈数
+     */
+    @Excel(name = "盘头最大圈数")
+    @ApiModelProperty("盘头最大圈数")
+    private Long phMax;
+
+    /**
+     * 最大圈数记录时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "最大圈数记录时间", width = 30, dateFormat = "yyyy-MM-dd")
+    @ApiModelProperty("最大圈数记录时间")
+    private Date recordTime;
+
+    /**
+     * 创建人
+     */
+    @Excel(name = "创建人")
+    @ApiModelProperty("创建人")
+    private String createdBy;
+
+    /**
+     * 创建时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "创建时间", width = 30, dateFormat = "yyyy-MM-dd")
+    @ApiModelProperty("创建时间")
+    private Date createdTime;
+
+    /**
+     * 更新人
+     */
+    @Excel(name = "更新人")
+    @ApiModelProperty("更新人")
+    private String updatedBy;
+
+    /**
+     * 更新时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "更新时间", width = 30, dateFormat = "yyyy-MM-dd")
+    @ApiModelProperty("更新时间")
+    private Date updatedTime;
+
+    public void setInfoId(Long infoId) {
+        this.infoId = infoId;
+    }
+
+    public Long getInfoId() {
+        return infoId;
+    }
+
+    public void setDeviceId(Long deviceId) {
+        this.deviceId = deviceId;
+    }
+
+    public Long getDeviceId() {
+        return deviceId;
+    }
+
+    public void setPhNum(Long phNum) {
+        this.phNum = phNum;
+    }
+
+    public Long getPhNum() {
+        return phNum;
+    }
+
+    public void setPhMax(Long phMax) {
+        this.phMax = phMax;
+    }
+
+    public Long getPhMax() {
+        return phMax;
+    }
+
+    public void setRecordTime(Date recordTime) {
+        this.recordTime = recordTime;
+    }
+
+    public Date getRecordTime() {
+        return recordTime;
+    }
+
+    public void setCreatedBy(String createdBy) {
+        this.createdBy = createdBy;
+    }
+
+    public String getCreatedBy() {
+        return createdBy;
+    }
+
+    public void setCreatedTime(Date createdTime) {
+        this.createdTime = createdTime;
+    }
+
+    public Date getCreatedTime() {
+        return createdTime;
+    }
+
+    public void setUpdatedBy(String updatedBy) {
+        this.updatedBy = updatedBy;
+    }
+
+    public String getUpdatedBy() {
+        return updatedBy;
+    }
+
+    public void setUpdatedTime(Date updatedTime) {
+        this.updatedTime = updatedTime;
+    }
+
+    public Date getUpdatedTime() {
+        return updatedTime;
+    }
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
+                .append("infoId", getInfoId())
+                .append("deviceId", getDeviceId())
+                .append("phNum", getPhNum())
+                .append("phMax", getPhMax())
+                .append("recordTime", getRecordTime())
+                .append("createdBy", getCreatedBy())
+                .append("createdTime", getCreatedTime())
+                .append("updatedBy", getUpdatedBy())
+                .append("updatedTime", getUpdatedTime())
+                .append("remark", getRemark())
+                .toString();
+    }
+
+    public Object[] toArray() {
+//        DEVICE_ID, PH_NUM,PH_MAX,RECORD_TIME
+        Object[] objects = {this.deviceId, this.phNum, this.phMax, this.recordTime};
+        return objects;
+    }
+}

+ 61 - 0
ruoyi-admin/src/main/java/com/ruoyi/biz/mapper/TwinPanHeadInfoMapper.java

@@ -0,0 +1,61 @@
+package com.ruoyi.biz.mapper;
+
+import java.util.List;
+import com.ruoyi.biz.domain.TwinPanHeadInfo;
+
+/**
+ * 设备盘头信息Mapper接口
+ * 
+ * @author ruoyi
+ * @date 2024-05-29
+ */
+public interface TwinPanHeadInfoMapper 
+{
+    /**
+     * 查询设备盘头信息
+     * 
+     * @param infoId 设备盘头信息主键
+     * @return 设备盘头信息
+     */
+    public TwinPanHeadInfo selectTwinPanHeadInfoByInfoId(Long infoId);
+
+    /**
+     * 查询设备盘头信息列表
+     * 
+     * @param twinPanHeadInfo 设备盘头信息
+     * @return 设备盘头信息集合
+     */
+    public List<TwinPanHeadInfo> selectTwinPanHeadInfoList(TwinPanHeadInfo twinPanHeadInfo);
+
+    /**
+     * 新增设备盘头信息
+     * 
+     * @param twinPanHeadInfo 设备盘头信息
+     * @return 结果
+     */
+    public int insertTwinPanHeadInfo(TwinPanHeadInfo twinPanHeadInfo);
+
+    /**
+     * 修改设备盘头信息
+     * 
+     * @param twinPanHeadInfo 设备盘头信息
+     * @return 结果
+     */
+    public int updateTwinPanHeadInfo(TwinPanHeadInfo twinPanHeadInfo);
+
+    /**
+     * 删除设备盘头信息
+     * 
+     * @param infoId 设备盘头信息主键
+     * @return 结果
+     */
+    public int deleteTwinPanHeadInfoByInfoId(Long infoId);
+
+    /**
+     * 批量删除设备盘头信息
+     * 
+     * @param infoIds 需要删除的数据主键集合
+     * @return 结果
+     */
+    public int deleteTwinPanHeadInfoByInfoIds(String[] infoIds);
+}

+ 61 - 0
ruoyi-admin/src/main/java/com/ruoyi/biz/service/ITwinPanHeadInfoService.java

@@ -0,0 +1,61 @@
+package com.ruoyi.biz.service;
+
+import java.util.List;
+import com.ruoyi.biz.domain.TwinPanHeadInfo;
+
+/**
+ * 设备盘头信息Service接口
+ * 
+ * @author ruoyi
+ * @date 2024-05-29
+ */
+public interface ITwinPanHeadInfoService 
+{
+    /**
+     * 查询设备盘头信息
+     * 
+     * @param infoId 设备盘头信息主键
+     * @return 设备盘头信息
+     */
+    public TwinPanHeadInfo selectTwinPanHeadInfoByInfoId(Long infoId);
+
+    /**
+     * 查询设备盘头信息列表
+     * 
+     * @param twinPanHeadInfo 设备盘头信息
+     * @return 设备盘头信息集合
+     */
+    public List<TwinPanHeadInfo> selectTwinPanHeadInfoList(TwinPanHeadInfo twinPanHeadInfo);
+
+    /**
+     * 新增设备盘头信息
+     * 
+     * @param twinPanHeadInfo 设备盘头信息
+     * @return 结果
+     */
+    public int insertTwinPanHeadInfo(TwinPanHeadInfo twinPanHeadInfo);
+
+    /**
+     * 修改设备盘头信息
+     * 
+     * @param twinPanHeadInfo 设备盘头信息
+     * @return 结果
+     */
+    public int updateTwinPanHeadInfo(TwinPanHeadInfo twinPanHeadInfo);
+
+    /**
+     * 批量删除设备盘头信息
+     * 
+     * @param infoIds 需要删除的设备盘头信息主键集合
+     * @return 结果
+     */
+    public int deleteTwinPanHeadInfoByInfoIds(String infoIds);
+
+    /**
+     * 删除设备盘头信息信息
+     * 
+     * @param infoId 设备盘头信息主键
+     * @return 结果
+     */
+    public int deleteTwinPanHeadInfoByInfoId(Long infoId);
+}

+ 47 - 3
ruoyi-admin/src/main/java/com/ruoyi/biz/service/impl/AsyncServiceImpl.java

@@ -4,6 +4,7 @@ import cn.hutool.json.JSONArray;
 import cn.hutool.json.JSONObject;
 import com.ruoyi.biz.domain.TwinCalc2hr;
 import com.ruoyi.biz.domain.TwinDevice;
+import com.ruoyi.biz.domain.TwinPanHeadInfo;
 import com.ruoyi.biz.domain.TwinRecordAlarms;
 import com.ruoyi.biz.service.IIotService;
 import javafx.util.Pair;
@@ -104,6 +105,7 @@ public class AsyncServiceImpl {
 
         calc2hrList.add(calc2hr);
         List<Pair<String, Long>> alarmRecord = (List<Pair<String, Long>>) map.get("alarmRecord");
+
         String strStop = "stop";
         String strAlarm = "alarm";
         alarmRecord.forEach(pair -> {
@@ -121,8 +123,13 @@ public class AsyncServiceImpl {
             recordAlarmsList.add(recordAlarms);
         });
 
+        List<TwinPanHeadInfo> panHeadInfo = (List<TwinPanHeadInfo>) map.get("panHead");
+        panHeadInfo.forEach(info -> info.setDeviceId(twinDevice.getId()));
+
+
         result.put("calc", calc2hrList);
         result.put("record", recordAlarmsList);
+        result.put("panHead", panHeadInfo);
         return new AsyncResult<>(result);
     }
 
@@ -136,9 +143,11 @@ public class AsyncServiceImpl {
             "Alarm_unit_9", "Alarm_unit_10", "Alarm_unit_11", "Alarm_unit_12", "Alarm_unit_13",
             "Alarm_unit_14", "Alarm_unit_15", "Alarm_unit_16", "Alarm_unit_17", "Alarm_unit_18",
             "Alarm_unit_19", "Alarm_unit_20", "Alarm_unit_21", "Alarm_unit_22", "Alarm_unit_23",
-            "Alarm_unit_24", "Alarm_unit_25", "Alarm_unit_26", "Alarm_unit_27", "Capacity_data_33"
+            "Alarm_unit_24", "Alarm_unit_25", "Alarm_unit_26", "Alarm_unit_27", "Capacity_data_33",
+            "Capacity_data_15", "Capacity_data_16", "Capacity_data_17", "Capacity_data_18", "Capacity_data_19"
     };
 
+
     /**
      * 字段列表,方便查找位置
      */
@@ -160,8 +169,10 @@ public class AsyncServiceImpl {
     public Map<String, Object> calc(String table, long startTime, long endTime) {
         String sql = "select %s from %s where time>%s and time <=%s";
         sql = String.format(sql, Arrays.stream(fields).collect(Collectors.joining(",")), table, startTime, endTime);
+        long s = System.currentTimeMillis();
         JSONObject jsonObject = iotService.query(sql);
-
+        long e = System.currentTimeMillis();
+        log.info("接口耗时:{}ms,table:{},time:{}", e - s, table, new Date(endTime));
         JSONObject data = jsonObject.getJSONObject("data");
         JSONArray values = data.getJSONArray("values");
         JSONArray timestamps = data.getJSONArray("timestamps");
@@ -174,6 +185,8 @@ public class AsyncServiceImpl {
         float[] total = new float[12];
         //告警数据统计
         boolean[] lastAlarms = new boolean[26];
+        //上一个时间点盘头数据
+        int[] lastPanHead = new int[5];
         //最后一个值为int型,需要单独计算
         int[] alarmsTimes = new int[27];
         //米克重
@@ -189,18 +202,23 @@ public class AsyncServiceImpl {
         }
         //停机和告警时间记录
         List<Pair<String, Long>> alarmRecord = new ArrayList<>();
+        //盘头记录
+        List<TwinPanHeadInfo> panHeadInfo = new ArrayList<>();
 
         for (int i = 0; i < timestamps.size(); i++) {
             JSONArray da = values.getJSONArray(i);
             //0-data2,1-data37,2-data38,3-data39,4-data42,5-data-43,6data-44
             //当前时间数据
             float[] curr = {da.getFloat(0), da.getFloat(1), da.getFloat(2), da.getFloat(3), da.getFloat(4), da.getFloat(5), da.getFloat(6)};
+            int[] currPan = {da.getInt(fieldList.indexOf("Capacity_data_15")), da.getInt(fieldList.indexOf("Capacity_data_16")), da.getInt(fieldList.indexOf("Capacity_data_17")),
+                    da.getInt(fieldList.indexOf("Capacity_data_18")), da.getInt(fieldList.indexOf("Capacity_data_19"))};
             int curr48 = da.getInt(7);
 
             if (i == 0) {
                 //第一次数据是上次最后一条,只做记录用,不做处理
                 first = curr.clone();
                 last = curr.clone();
+                lastPanHead = currPan.clone();
                 lastMkz = da.getInt(8);
                 lastFk = da.getFloat(9);
                 last48 = curr48;
@@ -209,8 +227,10 @@ public class AsyncServiceImpl {
                 }
                 continue;
             }
-
+            //计算电量
             calcKwh(da, total);
+            //计算盘头
+            calcPan(currPan, lastPanHead, panHeadInfo, timestamps.getLong(i));
 
             for (int j = 0; j < first.length; j++) {
                 //如果当前值为小于上一个,且上一个值不为0,则计算
@@ -255,6 +275,7 @@ public class AsyncServiceImpl {
 
             //复制数组,设置last值为当前值
             last = curr.clone();
+            lastPanHead = currPan.clone();
             lastMkz = da.getInt(8);
             lastFk = da.getFloat(9);
             for (int j = 0; j < lastAlarms.length; j++) {
@@ -272,10 +293,33 @@ public class AsyncServiceImpl {
         result.put("stop", stopMap);
         result.put("alarms", alarmsTimes);
         result.put("alarmRecord", alarmRecord);
+        result.put("panHead", panHeadInfo);
         return result;
     }
 
     /**
+     * 计算盘头信息
+     * 数组 分别是GB1-GB5
+     *
+     * @param currPan     当前剩余圈数
+     * @param lastPanHead 上一条记录剩余圈数
+     * @param panHeadInfo info
+     * @param time        时间戳
+     */
+    private void calcPan(int[] currPan, int[] lastPanHead, List<TwinPanHeadInfo> panHeadInfo, Long time) {
+        //如果当前记录大于上一条记录,则证明是重新叫料了,需要记录下当前值
+        for (int i = 0; i < currPan.length; i++) {
+            if (currPan[i] > lastPanHead[i]) {
+                TwinPanHeadInfo info = new TwinPanHeadInfo();
+                info.setRecordTime(new Date(time));
+                info.setPhNum((long) (i + 1));
+                info.setPhMax((long) currPan[i]);
+                panHeadInfo.add(info);
+            }
+        }
+    }
+
+    /**
      * 能耗计算
      * total[9].总电量  total[10].A班电量  total[11].B班电量
      *

+ 0 - 3
ruoyi-admin/src/main/java/com/ruoyi/biz/service/impl/IotServiceImpl.java

@@ -35,7 +35,6 @@ public class IotServiceImpl implements IIotService {
      */
     @Override
     public JSONObject query(String sql) {
-        Date s = new Date();
         String uri = this.uri + "/api/cnv-device/data-point/timeseries/query";
         JSONObject object = new JSONObject();
         object.set("sql", sql);
@@ -44,8 +43,6 @@ public class IotServiceImpl implements IIotService {
         request.header("Blade-Auth", "bearer " + tokenService.getToken());
         request.body(object.toString());
         JSONObject result = IotTools.getData(request);
-        Date e = new Date();
-        log.info("接口耗时:{}ms\t", (e.getTime() - s.getTime()));
         return result;
     }
 

+ 49 - 21
ruoyi-admin/src/main/java/com/ruoyi/biz/service/impl/TaskServiceImpl.java

@@ -2,6 +2,7 @@ package com.ruoyi.biz.service.impl;
 
 import com.ruoyi.biz.domain.TwinCalc2hr;
 import com.ruoyi.biz.domain.TwinDevice;
+import com.ruoyi.biz.domain.TwinPanHeadInfo;
 import com.ruoyi.biz.domain.TwinRecordAlarms;
 import com.ruoyi.biz.service.ITaskService;
 import com.ruoyi.biz.service.ITwinCalc2hrService;
@@ -22,6 +23,8 @@ import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
 /**
  * 计算任务
@@ -137,31 +140,19 @@ public class TaskServiceImpl implements ITaskService {
         int period = ldt.getHour() / 2 + 1;
         List<Object[]> calc2hrList = new ArrayList<>();
         List<Object[]> recordAlarmsList = new ArrayList<>();
+        List<Object[]> panList = new ArrayList<>();
         Date s = new Date();
         //为了避免多线程同时获取token导致重复执行,先执行一次获取token
         iotTokenService.getToken();
         List<TwinDevice> list = deviceService.selectTwinDeviceList(new TwinDevice());
-        List<Future<Map<String, List<?>>>> futureList = new ArrayList<>();
-        for (int i = 0; i < list.size(); i++) {
-            TwinDevice twinDevice = list.get(i);
-            futureList.add(asyncService.process(twinDevice, date, startTime, endTime, period));
-        }
-        try {
-            for (Future<Map<String, List<?>>> future : futureList) {
-                Map<String, List<?>> map = future.get();
-                List<TwinCalc2hr> calc2hrs = (List<TwinCalc2hr>) map.get("calc");
-                List<TwinRecordAlarms> recordAlarms = (List<TwinRecordAlarms>) map.get("record");
-                for (TwinCalc2hr calc2hr : calc2hrs) {
-                    calc2hrList.add(calc2hr.toArray());
-                }
-                for (TwinRecordAlarms alarms : recordAlarms) {
-                    recordAlarmsList.add(alarms.toArray());
-                }
+        List<TwinDevice> errList = exec(list, date, startTime, endTime, period, calc2hrList, recordAlarmsList, panList);
+        //重试2次
+        int repTimes = 1;
+        while (errList.size() != 0) {
+            errList = exec(list, date, startTime, endTime, period, calc2hrList, recordAlarmsList, panList);
+            if (repTimes++ > 1) {
+                break;
             }
-        } catch (InterruptedException e) {
-            throw new RuntimeException(e);
-        } catch (ExecutionException e) {
-            throw new RuntimeException(e);
         }
 
         Date d = new Date();
@@ -175,7 +166,44 @@ public class TaskServiceImpl implements ITaskService {
                 " VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
         jdbcTemplate.batchUpdate(sql, calc2hrList);
 
-        sql = "INSERT INTO TWIN_RECORD_ALARMS (DEVICE_ID, ALARM_TYPE,ALARM_CODE,DATA_TIME) VALUES (?,?,?,?)";
+        sql = "INSERT INTO TWIN_RECORD_ALARMS (DEVICE_ID,ALARM_TYPE,ALARM_CODE,DATA_TIME) VALUES (?,?,?,?)";
         jdbcTemplate.batchUpdate(sql, recordAlarmsList);
+
+        sql = "INSERT INTO TWIN_PAN_HEAD_INFO (DEVICE_ID,PH_NUM,PH_MAX,RECORD_TIME) VALUES (?,?,?,?)";
+        jdbcTemplate.batchUpdate(sql, panList);
+    }
+
+
+    private List<TwinDevice> exec(List<TwinDevice> list, Date date, Long startTime, Long endTime, int period, List<Object[]> calc2hrList, List<Object[]> recordAlarmsList, List<Object[]> panList) {
+        List<Future<Map<String, List<?>>>> futureList = new ArrayList<>();
+        for (int i = 0; i < list.size(); i++) {
+            TwinDevice twinDevice = list.get(i);
+            futureList.add(asyncService.process(twinDevice, date, startTime, endTime, period));
+        }
+
+        List<TwinDevice> errList = new ArrayList<>();
+        try {
+            for (Future<Map<String, List<?>>> future : futureList) {
+                // 任务完成后获取结果
+                try {
+                    Map<String, List<?>> map = future.get(10L, TimeUnit.SECONDS);
+                    List<TwinCalc2hr> calc2hrs = (List<TwinCalc2hr>) map.get("calc");
+                    List<TwinRecordAlarms> recordAlarms = (List<TwinRecordAlarms>) map.get("record");
+                    List<TwinPanHeadInfo> panHeadInfoList = (List<TwinPanHeadInfo>) map.get("panHead");
+
+                    calc2hrs.forEach(calc2hr -> calc2hrList.add(calc2hr.toArray()));
+                    recordAlarms.forEach(alarm -> recordAlarmsList.add(alarm.toArray()));
+                    panHeadInfoList.forEach(pan -> panList.add(pan.toArray()));
+                } catch (TimeoutException e) {
+                    errList.add(list.get(futureList.indexOf(future)));
+                }
+            }
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        } catch (ExecutionException e) {
+            throw new RuntimeException(e);
+        }
+
+        return errList;
     }
 }

+ 94 - 0
ruoyi-admin/src/main/java/com/ruoyi/biz/service/impl/TwinPanHeadInfoServiceImpl.java

@@ -0,0 +1,94 @@
+package com.ruoyi.biz.service.impl;
+
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.ruoyi.biz.mapper.TwinPanHeadInfoMapper;
+import com.ruoyi.biz.domain.TwinPanHeadInfo;
+import com.ruoyi.biz.service.ITwinPanHeadInfoService;
+import com.ruoyi.common.core.text.Convert;
+
+/**
+ * 设备盘头信息Service业务层处理
+ * 
+ * @author ruoyi
+ * @date 2024-05-29
+ */
+@Service
+public class TwinPanHeadInfoServiceImpl implements ITwinPanHeadInfoService 
+{
+    @Autowired
+    private TwinPanHeadInfoMapper twinPanHeadInfoMapper;
+
+    /**
+     * 查询设备盘头信息
+     * 
+     * @param infoId 设备盘头信息主键
+     * @return 设备盘头信息
+     */
+    @Override
+    public TwinPanHeadInfo selectTwinPanHeadInfoByInfoId(Long infoId)
+    {
+        return twinPanHeadInfoMapper.selectTwinPanHeadInfoByInfoId(infoId);
+    }
+
+    /**
+     * 查询设备盘头信息列表
+     * 
+     * @param twinPanHeadInfo 设备盘头信息
+     * @return 设备盘头信息
+     */
+    @Override
+    public List<TwinPanHeadInfo> selectTwinPanHeadInfoList(TwinPanHeadInfo twinPanHeadInfo)
+    {
+        return twinPanHeadInfoMapper.selectTwinPanHeadInfoList(twinPanHeadInfo);
+    }
+
+    /**
+     * 新增设备盘头信息
+     * 
+     * @param twinPanHeadInfo 设备盘头信息
+     * @return 结果
+     */
+    @Override
+    public int insertTwinPanHeadInfo(TwinPanHeadInfo twinPanHeadInfo)
+    {
+        return twinPanHeadInfoMapper.insertTwinPanHeadInfo(twinPanHeadInfo);
+    }
+
+    /**
+     * 修改设备盘头信息
+     * 
+     * @param twinPanHeadInfo 设备盘头信息
+     * @return 结果
+     */
+    @Override
+    public int updateTwinPanHeadInfo(TwinPanHeadInfo twinPanHeadInfo)
+    {
+        return twinPanHeadInfoMapper.updateTwinPanHeadInfo(twinPanHeadInfo);
+    }
+
+    /**
+     * 批量删除设备盘头信息
+     * 
+     * @param infoIds 需要删除的设备盘头信息主键
+     * @return 结果
+     */
+    @Override
+    public int deleteTwinPanHeadInfoByInfoIds(String infoIds)
+    {
+        return twinPanHeadInfoMapper.deleteTwinPanHeadInfoByInfoIds(Convert.toStrArray(infoIds));
+    }
+
+    /**
+     * 删除设备盘头信息信息
+     * 
+     * @param infoId 设备盘头信息主键
+     * @return 结果
+     */
+    @Override
+    public int deleteTwinPanHeadInfoByInfoId(Long infoId)
+    {
+        return twinPanHeadInfoMapper.deleteTwinPanHeadInfoByInfoId(infoId);
+    }
+}

+ 3 - 3
ruoyi-admin/src/main/java/com/ruoyi/biz/tools/IotTools.java

@@ -25,7 +25,7 @@ public class IotTools {
      */
     public static JSONObject getData(HttpRequest request) {
         //连接超时时间 5秒
-        request.setConnectionTimeout(50000);
+        request.setConnectionTimeout(5000);
         HttpResponse execute;
         int times = 1;
         while (true) {
@@ -41,8 +41,8 @@ public class IotTools {
                     throw new RuntimeException("重试5次未获取到数据!!!");
                 }
                 try {
-                    Thread.sleep(1000);
-                    //暂停1秒一直重试
+                    Thread.sleep(500);
+                    //暂停0.5秒一直重试
                 } catch (InterruptedException ignored) {
                 }
             }

+ 97 - 0
ruoyi-admin/src/main/resources/mapper/biz/TwinPanHeadInfoMapper.xml

@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.biz.mapper.TwinPanHeadInfoMapper">
+    
+    <resultMap type="TwinPanHeadInfo" id="TwinPanHeadInfoResult">
+        <result property="infoId"    column="INFO_ID"    />
+        <result property="deviceId"    column="DEVICE_ID"    />
+        <result property="phNum"    column="PH_NUM"    />
+        <result property="phMax"    column="PH_MAX"    />
+        <result property="recordTime"    column="RECORD_TIME"    />
+        <result property="createdBy"    column="CREATED_BY"    />
+        <result property="createdTime"    column="CREATED_TIME"    />
+        <result property="updatedBy"    column="UPDATED_BY"    />
+        <result property="updatedTime"    column="UPDATED_TIME"    />
+        <result property="remark"    column="REMARK"    />
+    </resultMap>
+
+    <sql id="selectTwinPanHeadInfoVo">
+        select INFO_ID, DEVICE_ID, PH_NUM, PH_MAX, RECORD_TIME, CREATED_BY, CREATED_TIME, UPDATED_BY, UPDATED_TIME, REMARK from twin_pan_head_info
+    </sql>
+
+    <select id="selectTwinPanHeadInfoList" parameterType="TwinPanHeadInfo" resultMap="TwinPanHeadInfoResult">
+        <include refid="selectTwinPanHeadInfoVo"/>
+        <where>  
+            <if test="deviceId != null "> and DEVICE_ID = #{deviceId}</if>
+            <if test="phNum != null "> and PH_NUM = #{phNum}</if>
+            <if test="phMax != null "> and PH_MAX = #{phMax}</if>
+            <if test="recordTime != null "> and RECORD_TIME = #{recordTime}</if>
+            <if test="createdBy != null  and createdBy != ''"> and CREATED_BY = #{createdBy}</if>
+            <if test="createdTime != null "> and CREATED_TIME = #{createdTime}</if>
+            <if test="updatedBy != null  and updatedBy != ''"> and UPDATED_BY = #{updatedBy}</if>
+            <if test="updatedTime != null "> and UPDATED_TIME = #{updatedTime}</if>
+            <if test="remark != null  and remark != ''"> and REMARK = #{remark}</if>
+        </where>
+    </select>
+    
+    <select id="selectTwinPanHeadInfoByInfoId" parameterType="Long" resultMap="TwinPanHeadInfoResult">
+        <include refid="selectTwinPanHeadInfoVo"/>
+        where INFO_ID = #{infoId}
+    </select>
+        
+    <insert id="insertTwinPanHeadInfo" parameterType="TwinPanHeadInfo" useGeneratedKeys="true" keyProperty="infoId">
+        insert into twin_pan_head_info
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="deviceId != null">DEVICE_ID,</if>
+            <if test="phNum != null">PH_NUM,</if>
+            <if test="phMax != null">PH_MAX,</if>
+            <if test="recordTime != null">RECORD_TIME,</if>
+            <if test="createdBy != null">CREATED_BY,</if>
+            <if test="createdTime != null">CREATED_TIME,</if>
+            <if test="updatedBy != null">UPDATED_BY,</if>
+            <if test="updatedTime != null">UPDATED_TIME,</if>
+            <if test="remark != null">REMARK,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="deviceId != null">#{deviceId},</if>
+            <if test="phNum != null">#{phNum},</if>
+            <if test="phMax != null">#{phMax},</if>
+            <if test="recordTime != null">#{recordTime},</if>
+            <if test="createdBy != null">#{createdBy},</if>
+            <if test="createdTime != null">#{createdTime},</if>
+            <if test="updatedBy != null">#{updatedBy},</if>
+            <if test="updatedTime != null">#{updatedTime},</if>
+            <if test="remark != null">#{remark},</if>
+         </trim>
+    </insert>
+
+    <update id="updateTwinPanHeadInfo" parameterType="TwinPanHeadInfo">
+        update twin_pan_head_info
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="deviceId != null">DEVICE_ID = #{deviceId},</if>
+            <if test="phNum != null">PH_NUM = #{phNum},</if>
+            <if test="phMax != null">PH_MAX = #{phMax},</if>
+            <if test="recordTime != null">RECORD_TIME = #{recordTime},</if>
+            <if test="createdBy != null">CREATED_BY = #{createdBy},</if>
+            <if test="createdTime != null">CREATED_TIME = #{createdTime},</if>
+            <if test="updatedBy != null">UPDATED_BY = #{updatedBy},</if>
+            <if test="updatedTime != null">UPDATED_TIME = #{updatedTime},</if>
+            <if test="remark != null">REMARK = #{remark},</if>
+        </trim>
+        where INFO_ID = #{infoId}
+    </update>
+
+    <delete id="deleteTwinPanHeadInfoByInfoId" parameterType="Long">
+        delete from twin_pan_head_info where INFO_ID = #{infoId}
+    </delete>
+
+    <delete id="deleteTwinPanHeadInfoByInfoIds" parameterType="String">
+        delete from twin_pan_head_info where INFO_ID in 
+        <foreach item="infoId" collection="array" open="(" separator="," close=")">
+            #{infoId}
+        </foreach>
+    </delete>
+
+</mapper>

+ 108 - 0
ruoyi-admin/src/main/resources/templates/biz/panHead/add.html

@@ -0,0 +1,108 @@
+<!DOCTYPE html>
+<html lang="zh" xmlns:th="http://www.thymeleaf.org" >
+<head>
+    <th:block th:include="include :: header('新增设备盘头信息')" />
+    <th:block th:include="include :: datetimepicker-css" />
+</head>
+<body class="white-bg">
+    <div class="wrapper wrapper-content animated fadeInRight ibox-content">
+        <form class="form-horizontal m" id="form-panHead-add">
+            <div class="form-group">    
+                <label class="col-sm-3 control-label">设备ID:</label>
+                <div class="col-sm-8">
+                    <input name="deviceId" class="form-control" type="text">
+                </div>
+            </div>
+            <div class="form-group">    
+                <label class="col-sm-3 control-label">盘头号:</label>
+                <div class="col-sm-8">
+                    <input name="phNum" class="form-control" type="text">
+                </div>
+            </div>
+            <div class="form-group">    
+                <label class="col-sm-3 control-label">盘头最大圈数:</label>
+                <div class="col-sm-8">
+                    <input name="phMax" class="form-control" type="text">
+                </div>
+            </div>
+            <div class="form-group">    
+                <label class="col-sm-3 control-label">最大圈数记录时间:</label>
+                <div class="col-sm-8">
+                    <div class="input-group date">
+                        <input name="recordTime" class="form-control" placeholder="yyyy-MM-dd" type="text">
+                        <span class="input-group-addon"><i class="fa fa-calendar"></i></span>
+                    </div>
+                </div>
+            </div>
+            <div class="form-group">    
+                <label class="col-sm-3 control-label">创建人:</label>
+                <div class="col-sm-8">
+                    <input name="createdBy" class="form-control" type="text">
+                </div>
+            </div>
+            <div class="form-group">    
+                <label class="col-sm-3 control-label">创建时间:</label>
+                <div class="col-sm-8">
+                    <div class="input-group date">
+                        <input name="createdTime" class="form-control" placeholder="yyyy-MM-dd" type="text">
+                        <span class="input-group-addon"><i class="fa fa-calendar"></i></span>
+                    </div>
+                </div>
+            </div>
+            <div class="form-group">    
+                <label class="col-sm-3 control-label">更新人:</label>
+                <div class="col-sm-8">
+                    <input name="updatedBy" class="form-control" type="text">
+                </div>
+            </div>
+            <div class="form-group">    
+                <label class="col-sm-3 control-label">更新时间:</label>
+                <div class="col-sm-8">
+                    <div class="input-group date">
+                        <input name="updatedTime" class="form-control" placeholder="yyyy-MM-dd" type="text">
+                        <span class="input-group-addon"><i class="fa fa-calendar"></i></span>
+                    </div>
+                </div>
+            </div>
+            <div class="form-group">
+                <label class="col-sm-3 control-label">备注:</label>
+                <div class="col-sm-8">
+                    <textarea name="remark" class="form-control"></textarea>
+                </div>
+            </div>
+        </form>
+    </div>
+    <th:block th:include="include :: footer" />
+    <th:block th:include="include :: datetimepicker-js" />
+    <script th:inline="javascript">
+        var prefix = ctx + "biz/panHead"
+        $("#form-panHead-add").validate({
+            focusCleanup: true
+        });
+
+        function submitHandler() {
+            if ($.validate.form()) {
+                $.operate.save(prefix + "/add", $('#form-panHead-add').serialize());
+            }
+        }
+
+        $("input[name='recordTime']").datetimepicker({
+            format: "yyyy-mm-dd",
+            minView: "month",
+            autoclose: true
+        });
+
+        $("input[name='createdTime']").datetimepicker({
+            format: "yyyy-mm-dd",
+            minView: "month",
+            autoclose: true
+        });
+
+        $("input[name='updatedTime']").datetimepicker({
+            format: "yyyy-mm-dd",
+            minView: "month",
+            autoclose: true
+        });
+    </script>
+</body>
+</html>

+ 109 - 0
ruoyi-admin/src/main/resources/templates/biz/panHead/edit.html

@@ -0,0 +1,109 @@
+<!DOCTYPE html>
+<html lang="zh" xmlns:th="http://www.thymeleaf.org" >
+<head>
+    <th:block th:include="include :: header('修改设备盘头信息')" />
+    <th:block th:include="include :: datetimepicker-css" />
+</head>
+<body class="white-bg">
+    <div class="wrapper wrapper-content animated fadeInRight ibox-content">
+        <form class="form-horizontal m" id="form-panHead-edit" th:object="${twinPanHeadInfo}">
+            <input name="infoId" th:field="*{infoId}" type="hidden">
+            <div class="form-group">    
+                <label class="col-sm-3 control-label">设备ID:</label>
+                <div class="col-sm-8">
+                    <input name="deviceId" th:field="*{deviceId}" class="form-control" type="text">
+                </div>
+            </div>
+            <div class="form-group">    
+                <label class="col-sm-3 control-label">盘头号:</label>
+                <div class="col-sm-8">
+                    <input name="phNum" th:field="*{phNum}" class="form-control" type="text">
+                </div>
+            </div>
+            <div class="form-group">    
+                <label class="col-sm-3 control-label">盘头最大圈数:</label>
+                <div class="col-sm-8">
+                    <input name="phMax" th:field="*{phMax}" class="form-control" type="text">
+                </div>
+            </div>
+            <div class="form-group">    
+                <label class="col-sm-3 control-label">最大圈数记录时间:</label>
+                <div class="col-sm-8">
+                    <div class="input-group date">
+                        <input name="recordTime" th:value="${#dates.format(twinPanHeadInfo.recordTime, 'yyyy-MM-dd')}" class="form-control" placeholder="yyyy-MM-dd" type="text">
+                        <span class="input-group-addon"><i class="fa fa-calendar"></i></span>
+                    </div>
+                </div>
+            </div>
+            <div class="form-group">    
+                <label class="col-sm-3 control-label">创建人:</label>
+                <div class="col-sm-8">
+                    <input name="createdBy" th:field="*{createdBy}" class="form-control" type="text">
+                </div>
+            </div>
+            <div class="form-group">    
+                <label class="col-sm-3 control-label">创建时间:</label>
+                <div class="col-sm-8">
+                    <div class="input-group date">
+                        <input name="createdTime" th:value="${#dates.format(twinPanHeadInfo.createdTime, 'yyyy-MM-dd')}" class="form-control" placeholder="yyyy-MM-dd" type="text">
+                        <span class="input-group-addon"><i class="fa fa-calendar"></i></span>
+                    </div>
+                </div>
+            </div>
+            <div class="form-group">    
+                <label class="col-sm-3 control-label">更新人:</label>
+                <div class="col-sm-8">
+                    <input name="updatedBy" th:field="*{updatedBy}" class="form-control" type="text">
+                </div>
+            </div>
+            <div class="form-group">    
+                <label class="col-sm-3 control-label">更新时间:</label>
+                <div class="col-sm-8">
+                    <div class="input-group date">
+                        <input name="updatedTime" th:value="${#dates.format(twinPanHeadInfo.updatedTime, 'yyyy-MM-dd')}" class="form-control" placeholder="yyyy-MM-dd" type="text">
+                        <span class="input-group-addon"><i class="fa fa-calendar"></i></span>
+                    </div>
+                </div>
+            </div>
+            <div class="form-group">
+                <label class="col-sm-3 control-label">备注:</label>
+                <div class="col-sm-8">
+                    <textarea name="remark" class="form-control">[[*{remark}]]</textarea>
+                </div>
+            </div>
+        </form>
+    </div>
+    <th:block th:include="include :: footer" />
+    <th:block th:include="include :: datetimepicker-js" />
+    <script th:inline="javascript">
+        var prefix = ctx + "biz/panHead";
+        $("#form-panHead-edit").validate({
+            focusCleanup: true
+        });
+
+        function submitHandler() {
+            if ($.validate.form()) {
+                $.operate.save(prefix + "/edit", $('#form-panHead-edit').serialize());
+            }
+        }
+
+        $("input[name='recordTime']").datetimepicker({
+            format: "yyyy-mm-dd",
+            minView: "month",
+            autoclose: true
+        });
+
+        $("input[name='createdTime']").datetimepicker({
+            format: "yyyy-mm-dd",
+            minView: "month",
+            autoclose: true
+        });
+
+        $("input[name='updatedTime']").datetimepicker({
+            format: "yyyy-mm-dd",
+            minView: "month",
+            autoclose: true
+        });
+    </script>
+</body>
+</html>

+ 146 - 0
ruoyi-admin/src/main/resources/templates/biz/panHead/panHead.html

@@ -0,0 +1,146 @@
+<!DOCTYPE html>
+<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
+<head>
+    <th:block th:include="include :: header('设备盘头信息列表')" />
+</head>
+<body class="gray-bg">
+     <div class="container-div">
+        <div class="row">
+            <div class="col-sm-12 search-collapse">
+                <form id="formId">
+                    <div class="select-list">
+                        <ul>
+                            <li>
+                                <label>设备ID:</label>
+                                <input type="text" name="deviceId"/>
+                            </li>
+                            <li>
+                                <label>盘头号:</label>
+                                <input type="text" name="phNum"/>
+                            </li>
+                            <li>
+                                <label>盘头最大圈数:</label>
+                                <input type="text" name="phMax"/>
+                            </li>
+                            <li>
+                                <label>最大圈数记录时间:</label>
+                                <input type="text" class="time-input" placeholder="请选择最大圈数记录时间" name="recordTime"/>
+                            </li>
+                            <li>
+                                <label>创建人:</label>
+                                <input type="text" name="createdBy"/>
+                            </li>
+                            <li>
+                                <label>创建时间:</label>
+                                <input type="text" class="time-input" placeholder="请选择创建时间" name="createdTime"/>
+                            </li>
+                            <li>
+                                <label>更新人:</label>
+                                <input type="text" name="updatedBy"/>
+                            </li>
+                            <li>
+                                <label>更新时间:</label>
+                                <input type="text" class="time-input" placeholder="请选择更新时间" name="updatedTime"/>
+                            </li>
+                            <li>
+                                <a class="btn btn-primary btn-rounded btn-sm" onclick="$.table.search()"><i class="fa fa-search"></i>&nbsp;搜索</a>
+                                <a class="btn btn-warning btn-rounded btn-sm" onclick="$.form.reset()"><i class="fa fa-refresh"></i>&nbsp;重置</a>
+                            </li>
+                        </ul>
+                    </div>
+                </form>
+            </div>
+
+            <div class="btn-group-sm" id="toolbar" role="group">
+                <a class="btn btn-success" onclick="$.operate.add()" shiro:hasPermission="biz:panHead:add">
+                    <i class="fa fa-plus"></i> 添加
+                </a>
+                <a class="btn btn-primary single disabled" onclick="$.operate.edit()" shiro:hasPermission="biz:panHead:edit">
+                    <i class="fa fa-edit"></i> 修改
+                </a>
+                <a class="btn btn-danger multiple disabled" onclick="$.operate.removeAll()" shiro:hasPermission="biz:panHead:remove">
+                    <i class="fa fa-remove"></i> 删除
+                </a>
+                <a class="btn btn-warning" onclick="$.table.exportExcel()" shiro:hasPermission="biz:panHead:export">
+                    <i class="fa fa-download"></i> 导出
+                </a>
+            </div>
+            <div class="col-sm-12 select-table table-striped">
+                <table id="bootstrap-table"></table>
+            </div>
+        </div>
+    </div>
+    <th:block th:include="include :: footer" />
+    <script th:inline="javascript">
+        var editFlag = [[${@permission.hasPermi('biz:panHead:edit')}]];
+        var removeFlag = [[${@permission.hasPermi('biz:panHead:remove')}]];
+        var prefix = ctx + "biz/panHead";
+
+        $(function() {
+            var options = {
+                url: prefix + "/list",
+                createUrl: prefix + "/add",
+                updateUrl: prefix + "/edit/{id}",
+                removeUrl: prefix + "/remove",
+                exportUrl: prefix + "/export",
+                modalName: "设备盘头信息",
+                columns: [{
+                    checkbox: true
+                },
+                {
+                    field: 'infoId',
+                    title: '信息ID',
+                    visible: false
+                },
+                {
+                    field: 'deviceId',
+                    title: '设备ID'
+                },
+                {
+                    field: 'phNum',
+                    title: '盘头号'
+                },
+                {
+                    field: 'phMax',
+                    title: '盘头最大圈数'
+                },
+                {
+                    field: 'recordTime',
+                    title: '最大圈数记录时间'
+                },
+                {
+                    field: 'createdBy',
+                    title: '创建人'
+                },
+                {
+                    field: 'createdTime',
+                    title: '创建时间'
+                },
+                {
+                    field: 'updatedBy',
+                    title: '更新人'
+                },
+                {
+                    field: 'updatedTime',
+                    title: '更新时间'
+                },
+                {
+                    field: 'remark',
+                    title: '备注'
+                },
+                {
+                    title: '操作',
+                    align: 'center',
+                    formatter: function(value, row, index) {
+                        var actions = [];
+                        actions.push('<a class="btn btn-success btn-xs ' + editFlag + '" href="javascript:void(0)" onclick="$.operate.edit(\'' + row.infoId + '\')"><i class="fa fa-edit"></i>编辑</a> ');
+                        actions.push('<a class="btn btn-danger btn-xs ' + removeFlag + '" href="javascript:void(0)" onclick="$.operate.remove(\'' + row.infoId + '\')"><i class="fa fa-remove"></i>删除</a>');
+                        return actions.join('');
+                    }
+                }]
+            };
+            $.table.init(options);
+        });
+    </script>
+</body>
+</html>

+ 8 - 4
ruoyi-admin/src/test/java/com/jjt/DataPF.java

@@ -1,5 +1,8 @@
 package com.jjt;
 
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.LoggerContext;
 import cn.hutool.json.JSONArray;
 import cn.hutool.json.JSONObject;
 import com.ruoyi.RuoYiApplication;
@@ -12,6 +15,7 @@ import com.ruoyi.biz.service.ITwinDeviceService;
 import com.ruoyi.biz.service.impl.AsyncServiceImpl;
 import javafx.util.Pair;
 import org.junit.jupiter.api.Test;
+import org.slf4j.impl.StaticLoggerBinder;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.scheduling.annotation.EnableAsync;
@@ -102,10 +106,10 @@ public class DataPF {
 //        for (int i = 11; i <= 12; i++) {
 //            taskService.calc("2024-05-26", i);
 //        }
-        for (int i = 22; i <= 27; i++) {
-            taskService.calc("2024-05-" + i);
-
-        }
+//        for (int i = 23; i <= 27; i++) {
+//            taskService.calc("2024-05-" + i);
+//        }
+        taskService.calc("2024-05-28");
     }
 
     @Test