Commit debf3249 authored by wanghao's avatar wanghao

1 时间校准 和 实时AD 处理 流程功能开发。

2 实时AD 范围值 界面维护功能开发。
3 上传MES 地址 界面维护功能实现。
parent ee9a8ec3
package com.zehong.web.controller.equipment;
import com.zehong.common.core.controller.BaseController;
import com.zehong.common.core.domain.AjaxResult;
import com.zehong.common.core.page.TableDataInfo;
import com.zehong.common.utils.poi.ExcelUtil;
import com.zehong.system.domain.SysRealTimeAdRange;
import com.zehong.system.service.ISysRealTimeAdRangeService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
/**
* 实时AD范围维护Controller
*
* @author zehong
* @date 2025-11-22
*/
@RestController
@RequestMapping("/system/realAdRange")
public class SysRealTimeAdRangeController extends BaseController
{
@Resource
private ISysRealTimeAdRangeService sysRealTimeAdRangeService;
/**
* 查询实时AD范围维护列表
*/
@GetMapping("/list")
public TableDataInfo list(SysRealTimeAdRange sysRealTimeAdRange)
{
startPage();
List<SysRealTimeAdRange> list = sysRealTimeAdRangeService.selectSysRealTimeAdRangeList(sysRealTimeAdRange);
return getDataTable(list);
}
/**
* 获取实时AD范围维护列表
*/
@GetMapping("/getTopOne")
public AjaxResult getTopOne() {
return sysRealTimeAdRangeService.getTopOne();
}
/**
* 导出实时AD范围维护列表
*/
@GetMapping("/export")
public AjaxResult export(SysRealTimeAdRange sysRealTimeAdRange)
{
List<SysRealTimeAdRange> list = sysRealTimeAdRangeService.selectSysRealTimeAdRangeList(sysRealTimeAdRange);
ExcelUtil<SysRealTimeAdRange> util = new ExcelUtil<SysRealTimeAdRange>(SysRealTimeAdRange.class);
return util.exportExcel(list, "实时AD范围维护数据");
}
/**
* 获取实时AD范围维护详细信息
*/
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
return AjaxResult.success(sysRealTimeAdRangeService.selectSysRealTimeAdRangeById(id));
}
/**
* 新增实时AD范围维护
*/
@PostMapping
public AjaxResult add(@RequestBody SysRealTimeAdRange sysRealTimeAdRange)
{
return AjaxResult.success(sysRealTimeAdRangeService.insertSysRealTimeAdRange(sysRealTimeAdRange));
}
/**
* 修改实时AD范围维护
*/
@PutMapping
public AjaxResult edit(@RequestBody SysRealTimeAdRange sysRealTimeAdRange)
{
return toAjax(sysRealTimeAdRangeService.updateSysRealTimeAdRange(sysRealTimeAdRange));
}
/**
* 删除实时AD范围维护
*/
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids)
{
return toAjax(sysRealTimeAdRangeService.deleteSysRealTimeAdRangeByIds(ids));
}
}
......@@ -96,6 +96,14 @@ public class TStoreyInfoController extends BaseController
return tStoreyInfoService.handleBlanking(command);
}
/**
* 处理再老化
*/
@GetMapping(value = "/handleReAgine")
public AjaxResult handleReAgine(TStoreyInfo tStoreyInfo) {
return tStoreyInfoService.handleReAgine(tStoreyInfo);
}
/**
* 处理直接消 blanking
*/
......
package com.zehong.web.controller.system;
import java.util.List;
import com.zehong.system.netty.handler.NettyUdpServerHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
......@@ -77,6 +79,23 @@ public class SysConfigController extends BaseController
return AjaxResult.success(configService.selectConfigByKey(configKey));
}
/**
* 获取上传mes地址
*/
@GetMapping(value = "/getUploadMesAddress")
public AjaxResult getUploadMesAddress() {
return AjaxResult.success(configService.directSelectConfigByKey(NettyUdpServerHandler.UPLOAD_MES_ADDRESS));
}
/**
* 修改上传mes地址
*/
@PostMapping(value = "/updateUploadMesAddress")
public AjaxResult updateUploadMesAddress(@RequestBody SysConfig config) {
return toAjax(configService.updateUploadMesAddress(config));
}
/**
* 新增参数配置
*/
......
package com.zehong.system.domain;
import com.zehong.common.core.domain.BaseEntity;
/**
* @author lenovo
* @date 2025/11/22
* @description MES设备对象
*/
public class MesDeviceDomain extends BaseEntity {
private static final long serialVersionUID = 1L;
/** 主板码 */
private String motherboardCode;
/**
* 零点AD值
*/
private String zeroAdValue;
/**
*零点状态 0-未校准 1-已校准
*/
private String zeroStatus;
/**
* 校准AD值
*/
private String calibratedAdValue;
/**
* 校准状态
*/
private String calibrationStatus;
/**
* 浓度
*/
private String concentration;
/**
* 实时AD
*/
private Integer realTimeAd;
/**
* 实时状态 0-异常;1-正常
*/
private String realTimeStatus;
public String getMotherboardCode() {
return motherboardCode;
}
public void setMotherboardCode(String motherboardCode) {
this.motherboardCode = motherboardCode;
}
public String getZeroAdValue() {
return zeroAdValue;
}
public void setZeroAdValue(String zeroAdValue) {
this.zeroAdValue = zeroAdValue;
}
public String getZeroStatus() {
return zeroStatus;
}
public void setZeroStatus(String zeroStatus) {
this.zeroStatus = zeroStatus;
}
public String getCalibratedAdValue() {
return calibratedAdValue;
}
public void setCalibratedAdValue(String calibratedAdValue) {
this.calibratedAdValue = calibratedAdValue;
}
public String getCalibrationStatus() {
return calibrationStatus;
}
public void setCalibrationStatus(String calibrationStatus) {
this.calibrationStatus = calibrationStatus;
}
public String getConcentration() {
return concentration;
}
public void setConcentration(String concentration) {
this.concentration = concentration;
}
public Integer getRealTimeAd() {
return realTimeAd;
}
public void setRealTimeAd(Integer realTimeAd) {
this.realTimeAd = realTimeAd;
}
public String getRealTimeStatus() {
return realTimeStatus;
}
public void setRealTimeStatus(String realTimeStatus) {
this.realTimeStatus = realTimeStatus;
}
}
......@@ -95,20 +95,46 @@ public class PalletDeviceBinding extends BaseEntity
private String writeTimeStatus;
/**
* 零点校准AD
* 零点AD
*/
private String adjustmentZeroAd;
/**
* 零点校准AD
* 零点状态 0-异常;1-正常
*/
private String zeroStatus;
/**
* 校准AD
*/
private String calibrationAd;
/**
* 零点状态 0-异常;1-正常
*/
private String calibrationAdStatus;
/**
* 浓度
*/
private String concentration;
/**
* 运行时间状态 0-失败;1-成功
*/
private String runTimeStatus;
/**
* 实时AD
*/
private Integer realTimeAd;
/**
* 实时状态 0-异常;1-正常
*/
private String realTimeStatus;
public String getStatus() {
return status;
}
......@@ -277,6 +303,46 @@ public class PalletDeviceBinding extends BaseEntity
this.number = number;
}
public String getRunTimeStatus() {
return runTimeStatus;
}
public void setRunTimeStatus(String runTimeStatus) {
this.runTimeStatus = runTimeStatus;
}
public Integer getRealTimeAd() {
return realTimeAd;
}
public void setRealTimeAd(Integer realTimeAd) {
this.realTimeAd = realTimeAd;
}
public String getRealTimeStatus() {
return realTimeStatus;
}
public void setRealTimeStatus(String realTimeStatus) {
this.realTimeStatus = realTimeStatus;
}
public String getZeroStatus() {
return zeroStatus;
}
public void setZeroStatus(String zeroStatus) {
this.zeroStatus = zeroStatus;
}
public String getCalibrationAdStatus() {
return calibrationAdStatus;
}
public void setCalibrationAdStatus(String calibrationAdStatus) {
this.calibrationAdStatus = calibrationAdStatus;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
......
package com.zehong.system.domain;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.zehong.common.annotation.Excel;
import com.zehong.common.core.domain.BaseEntity;
/**
* 实时AD范围维护对象 t_sys_real_time_ad_range
*
* @author zehong
* @date 2025-11-22
*/
public class SysRealTimeAdRange extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 主键ID */
private Long id;
/** 实时AD最小值 */
@Excel(name = "实时AD最小值")
private Long realTimeAdMin;
/** 实时AD最大值 */
@Excel(name = "实时AD最大值")
private Long realTimeAdMax;
public void setId(Long id)
{
this.id = id;
}
public Long getId()
{
return id;
}
public void setRealTimeAdMin(Long realTimeAdMin)
{
this.realTimeAdMin = realTimeAdMin;
}
public Long getRealTimeAdMin()
{
return realTimeAdMin;
}
public void setRealTimeAdMax(Long realTimeAdMax)
{
this.realTimeAdMax = realTimeAdMax;
}
public Long getRealTimeAdMax()
{
return realTimeAdMax;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("realTimeAdMin", getRealTimeAdMin())
.append("realTimeAdMax", getRealTimeAdMax())
.toString();
}
}
......@@ -38,6 +38,8 @@ public interface PalletDeviceBindingMapper
public List<PalletDeviceBinding> getAllExcludeUnbindingTimeByTrayId(Long trayId);
public List<PalletDeviceBinding> listByTrayCode(String trayCode);
public int countErrorByTrayId(Long trayId);
public int countByTrayId(Long trayId);
......@@ -70,6 +72,8 @@ public interface PalletDeviceBindingMapper
public int batchUpdateDeviceCode(@Param("palletDeviceBindingList") List<PalletDeviceBinding> palletDeviceBindingList);
public int batchUpdateAdAndStatus(@Param("palletDeviceBindingList") List<PalletDeviceBinding> palletDeviceBindingList);
public int batchUpdateDeviceCodeAndUnbindingTime(@Param("palletDeviceBindingList") List<PalletDeviceBinding> palletDeviceBindingList);
public int unbindAllDevice(Long trayId);
......
......@@ -50,6 +50,8 @@ public interface SysConfigMapper
*/
public int updateConfig(SysConfig config);
public int directUpdateConfigByKey(SysConfig config);
/**
* 删除参数配置
*
......
package com.zehong.system.mapper;
import java.util.List;
import com.zehong.system.domain.SysRealTimeAdRange;
/**
* 实时AD范围维护Mapper接口
*
* @author zehong
* @date 2025-11-22
*/
public interface SysRealTimeAdRangeMapper
{
/**
* 查询实时AD范围维护
*
* @param id 实时AD范围维护ID
* @return 实时AD范围维护
*/
public SysRealTimeAdRange selectSysRealTimeAdRangeById(Long id);
/**
* 查询实时AD范围维护列表
*
* @param sysRealTimeAdRange 实时AD范围维护
* @return 实时AD范围维护集合
*/
public List<SysRealTimeAdRange> selectSysRealTimeAdRangeList(SysRealTimeAdRange sysRealTimeAdRange);
public SysRealTimeAdRange getTopOne();
/**
* 新增实时AD范围维护
*
* @param sysRealTimeAdRange 实时AD范围维护
* @return 结果
*/
public int insertSysRealTimeAdRange(SysRealTimeAdRange sysRealTimeAdRange);
/**
* 修改实时AD范围维护
*
* @param sysRealTimeAdRange 实时AD范围维护
* @return 结果
*/
public int updateSysRealTimeAdRange(SysRealTimeAdRange sysRealTimeAdRange);
/**
* 删除实时AD范围维护
*
* @param id 实时AD范围维护ID
* @return 结果
*/
public int deleteSysRealTimeAdRangeById(Long id);
/**
* 批量删除实时AD范围维护
*
* @param ids 需要删除的数据ID
* @return 结果
*/
public int deleteSysRealTimeAdRangeByIds(Long[] ids);
}
package com.zehong.system.netty.handler;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.zehong.common.utils.StringUtils;
import com.zehong.common.utils.http.HttpUtils;
import com.zehong.system.domain.MesDeviceDomain;
import com.zehong.system.domain.PalletDeviceBinding;
import com.zehong.system.domain.RobotArmCommand;
import com.zehong.system.service.IPalletDeviceBindingService;
import com.zehong.system.service.IRobotArmCommandService;
import com.zehong.system.service.ISysConfigService;
import com.zehong.system.service.websocket.RobotArmWebSocketHandler;
import com.zehong.system.udp.RobotArmMessageParser;
import io.netty.buffer.ByteBuf;
......@@ -19,14 +27,12 @@ import javax.annotation.Resource;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
import java.io.*;
import java.util.Date;
/**
* @author lenovo
* @date 2025/7/31
......@@ -40,6 +46,9 @@ public class NettyUdpServerHandler extends SimpleChannelInboundHandler<DatagramP
// 日志文件保存目录
// 写法1:使用双反斜杠
private static final String LOG_DIR = "D:\\BaiduNetdiskDownload\\udp_message_logs\\";
// 上传MES地址的key
public static final String UPLOAD_MES_ADDRESS = "uploadMesAddress";
// 日期格式器,用于生成文件名和日志时间
private static final SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat("yyyyMMdd");
private static final SimpleDateFormat TIME_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
......@@ -58,6 +67,12 @@ public class NettyUdpServerHandler extends SimpleChannelInboundHandler<DatagramP
@Resource
private IRobotArmCommandService robotArmCommandService;
@Resource
private IPalletDeviceBindingService palletDeviceBindingService;
@Resource
private ISysConfigService sysConfigService;
/**
* 接收UDP消息
*/
......@@ -161,7 +176,6 @@ public class NettyUdpServerHandler extends SimpleChannelInboundHandler<DatagramP
* 处理校准消息
*/
private void handleCalibrationMessage(ChannelHandlerContext ctx, DatagramPacket packet, String message) {
// TODO: 实现校准消息的具体处理逻辑
log.info("收到校准消息: {}", message);
// 示例:解析校准数据
......@@ -174,9 +188,79 @@ public class NettyUdpServerHandler extends SimpleChannelInboundHandler<DatagramP
io.netty.buffer.Unpooled.copiedBuffer(responseBytes),
packet.sender()));
// TODO: 添加具体的校准业务逻辑
// 例如:更新校准状态、保存校准数据、通知前端等
// 1 根据信息拿到点位信息 假如说1L是传过来的
List<PalletDeviceBinding> palletDeviceBindings = initAdAndStatus(message);
// 2 解析数据上传MES 返回是否成功
Boolean aBoolean = initMesDataAndUpload(palletDeviceBindings);
// 3 三次失败后 记录到本系统,之后有时间再手动上传
if(!aBoolean) {
}
}
/**
* 初始化MES数据并上传
*/
private Boolean initMesDataAndUpload(List<PalletDeviceBinding> palletDeviceBindings) {
List<MesDeviceDomain> mesDeviceDomains = new ArrayList<>();
for (PalletDeviceBinding palletDeviceBinding : palletDeviceBindings) {
MesDeviceDomain mesDeviceDomain = new MesDeviceDomain();
// 主板码
mesDeviceDomain.setMotherboardCode(palletDeviceBinding.getDeviceCode());
// 零点AD
mesDeviceDomain.setZeroAdValue(palletDeviceBinding.getAdjustmentZeroAd());
// 零点AD状态
mesDeviceDomain.setZeroStatus(palletDeviceBinding.getZeroStatus());
// 标定AD
mesDeviceDomain.setCalibratedAdValue(palletDeviceBinding.getCalibrationAd());
// 标定AD状态
mesDeviceDomain.setCalibrationStatus(palletDeviceBinding.getCalibrationAdStatus());
// 浓度
mesDeviceDomain.setConcentration(palletDeviceBinding.getConcentration());
// 实时AD
mesDeviceDomain.setRealTimeAd(palletDeviceBinding.getRealTimeAd());
// 实时状态
mesDeviceDomain.setRealTimeStatus(palletDeviceBinding.getRealTimeStatus());
mesDeviceDomains.add(mesDeviceDomain);
}
String mesUploadAddress = sysConfigService.directSelectConfigByKey(UPLOAD_MES_ADDRESS);
if(StringUtils.isNotBlank(mesUploadAddress)) {
String result = HttpUtils.sendPost(mesUploadAddress, JSON.toJSONString(mesDeviceDomains));
if(StringUtils.isBlank(result)) {
return false;
}
JSONObject jsonObject = JSON.parseObject(result);
if(jsonObject.getInteger("code") == 200) {
return true;
}
}
return false;
}
/**
* 根据托盘码查询托盘绑定的设备列
*/
private List<PalletDeviceBinding> initAdAndStatus(String message) {
List<PalletDeviceBinding> palletDeviceBindings = palletDeviceBindingService.listByTrayCode("1L");
// 根据message 处理 零点AD 零点AD状态 标定AD 标定AD状态
// 批量更新
palletDeviceBindingService.batchUpdateAdAndStatus(palletDeviceBindings);
return palletDeviceBindings;
}
/**
* 仅处理已完成的指令
*/
......
......@@ -31,6 +31,10 @@ public interface IPalletDeviceBindingService
public List<PalletDeviceBinding> getAllExcludeUnbindingTimeByTrayId(Long trayId);
public List<PalletDeviceBinding> listByTrayCode(String trayCode);
public void batchUpdateAdAndStatus(List<PalletDeviceBinding> palletDeviceBindingList);
/**
* 新增托盘绑定的设备列
*
......
......@@ -26,6 +26,8 @@ public interface ISysConfigService
*/
public String selectConfigByKey(String configKey);
public String directSelectConfigByKey(String configKey);
/**
* 查询参数配置列表
*
......@@ -50,6 +52,8 @@ public interface ISysConfigService
*/
public int updateConfig(SysConfig config);
public int updateUploadMesAddress(SysConfig sysConfig);
/**
* 批量删除参数信息
*
......
package com.zehong.system.service;
import java.util.List;
import com.zehong.common.core.domain.AjaxResult;
import com.zehong.system.domain.SysRealTimeAdRange;
/**
* 实时AD范围维护Service接口
*
* @author zehong
* @date 2025-11-22
*/
public interface ISysRealTimeAdRangeService
{
/**
* 查询实时AD范围维护
*
* @param id 实时AD范围维护ID
* @return 实时AD范围维护
*/
public SysRealTimeAdRange selectSysRealTimeAdRangeById(Long id);
/**
* 查询实时AD范围维护列表
*
* @param sysRealTimeAdRange 实时AD范围维护
* @return 实时AD范围维护集合
*/
public List<SysRealTimeAdRange> selectSysRealTimeAdRangeList(SysRealTimeAdRange sysRealTimeAdRange);
public AjaxResult getTopOne();
/**
* 新增实时AD范围维护
*
* @param sysRealTimeAdRange 实时AD范围维护
* @return 结果
*/
public Long insertSysRealTimeAdRange(SysRealTimeAdRange sysRealTimeAdRange);
/**
* 修改实时AD范围维护
*
* @param sysRealTimeAdRange 实时AD范围维护
* @return 结果
*/
public int updateSysRealTimeAdRange(SysRealTimeAdRange sysRealTimeAdRange);
/**
* 批量删除实时AD范围维护
*
* @param ids 需要删除的实时AD范围维护ID
* @return 结果
*/
public int deleteSysRealTimeAdRangeByIds(Long[] ids);
/**
* 删除实时AD范围维护信息
*
* @param id 实时AD范围维护ID
* @return 结果
*/
public int deleteSysRealTimeAdRangeById(Long id);
}
......@@ -60,6 +60,8 @@ public interface ITStoreyInfoService
public AjaxResult handleBlanking(String command);
public AjaxResult handleReAgine(TStoreyInfo tStoreyInfo);
public AjaxResult handleDirectBlanking(String command);
/**
......
......@@ -65,6 +65,22 @@ public class PalletDeviceBindingServiceImpl implements IPalletDeviceBindingServi
return palletDeviceBindingMapper.getAllExcludeUnbindingTimeByTrayId(trayId);
}
/**
* 根据托盘码查询托盘绑定的设备列
*
* @param trayCode 托盘码
* @return 列表
*/
@Override
public List<PalletDeviceBinding> listByTrayCode(String trayCode) {
return palletDeviceBindingMapper.listByTrayCode(trayCode);
}
@Override
public void batchUpdateAdAndStatus(List<PalletDeviceBinding> palletDeviceBindingList) {
palletDeviceBindingMapper.batchUpdateAdAndStatus(palletDeviceBindingList);
}
/**
* 新增托盘绑定的设备列
*
......
......@@ -3,7 +3,9 @@ package com.zehong.system.service.impl;
import java.util.Collection;
import java.util.List;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.Resource;
import com.zehong.system.netty.handler.NettyUdpServerHandler;
import org.springframework.stereotype.Service;
import com.zehong.common.annotation.DataSource;
import com.zehong.common.constant.Constants;
......@@ -25,10 +27,10 @@ import com.zehong.system.service.ISysConfigService;
@Service
public class SysConfigServiceImpl implements ISysConfigService
{
@Autowired
@Resource
private SysConfigMapper configMapper;
@Autowired
@Resource
private RedisCache redisCache;
/**
......@@ -80,6 +82,26 @@ public class SysConfigServiceImpl implements ISysConfigService
return StringUtils.EMPTY;
}
/**
* 根据键名查询参数配置信息-直接查询不经过redis
*
* @param configKey 键名
* @return 参数键值
*/
@Override
public String directSelectConfigByKey(String configKey) {
if(StringUtils.isBlank(configKey)) {
return StringUtils.EMPTY;
}
SysConfig config = new SysConfig();
config.setConfigKey(configKey);
SysConfig sysConfig = configMapper.selectConfig(config);
if(sysConfig != null) {
return sysConfig.getConfigValue();
}
return StringUtils.EMPTY;
}
/**
* 查询参数配置列表
*
......@@ -126,6 +148,17 @@ public class SysConfigServiceImpl implements ISysConfigService
return row;
}
/**
* 修改上传地址
*
* @return 结果
*/
@Override
public int updateUploadMesAddress(SysConfig sysConfig) {
sysConfig.setConfigKey(NettyUdpServerHandler.UPLOAD_MES_ADDRESS);
return configMapper.directUpdateConfigByKey(sysConfig);
}
/**
* 批量删除参数信息
*
......
package com.zehong.system.service.impl;
import com.zehong.common.core.domain.AjaxResult;
import com.zehong.system.domain.SysRealTimeAdRange;
import com.zehong.system.mapper.SysRealTimeAdRangeMapper;
import com.zehong.system.service.ISysRealTimeAdRangeService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
/**
* 实时AD范围维护Service业务层处理
*
* @author zehong
* @date 2025-11-22
*/
@Service
public class SysRealTimeAdRangeServiceImpl implements ISysRealTimeAdRangeService
{
@Resource
private SysRealTimeAdRangeMapper sysRealTimeAdRangeMapper;
/**
* 查询实时AD范围维护
*
* @param id 实时AD范围维护ID
* @return 实时AD范围维护
*/
@Override
public SysRealTimeAdRange selectSysRealTimeAdRangeById(Long id)
{
return sysRealTimeAdRangeMapper.selectSysRealTimeAdRangeById(id);
}
/**
* 查询实时AD范围维护列表
*
* @param sysRealTimeAdRange 实时AD范围维护
* @return 实时AD范围维护
*/
@Override
public List<SysRealTimeAdRange> selectSysRealTimeAdRangeList(SysRealTimeAdRange sysRealTimeAdRange)
{
return sysRealTimeAdRangeMapper.selectSysRealTimeAdRangeList(sysRealTimeAdRange);
}
@Override
public AjaxResult getTopOne() {
return AjaxResult.success(sysRealTimeAdRangeMapper.getTopOne());
}
/**
* 新增实时AD范围维护
*
* @param sysRealTimeAdRange 实时AD范围维护
* @return 结果
*/
@Override
public Long insertSysRealTimeAdRange(SysRealTimeAdRange sysRealTimeAdRange)
{
sysRealTimeAdRangeMapper.insertSysRealTimeAdRange(sysRealTimeAdRange);
return sysRealTimeAdRange.getId();
}
/**
* 修改实时AD范围维护
*
* @param sysRealTimeAdRange 实时AD范围维护
* @return 结果
*/
@Override
public int updateSysRealTimeAdRange(SysRealTimeAdRange sysRealTimeAdRange)
{
return sysRealTimeAdRangeMapper.updateSysRealTimeAdRange(sysRealTimeAdRange);
}
/**
* 批量删除实时AD范围维护
*
* @param ids 需要删除的实时AD范围维护ID
* @return 结果
*/
@Override
public int deleteSysRealTimeAdRangeByIds(Long[] ids)
{
return sysRealTimeAdRangeMapper.deleteSysRealTimeAdRangeByIds(ids);
}
/**
* 删除实时AD范围维护信息
*
* @param id 实时AD范围维护ID
* @return 结果
*/
@Override
public int deleteSysRealTimeAdRangeById(Long id)
{
return sysRealTimeAdRangeMapper.deleteSysRealTimeAdRangeById(id);
}
}
......@@ -560,6 +560,39 @@ public class TStoreyInfoServiceImpl implements ITStoreyInfoService
return AjaxResult.success();
}
/**
* 重新老化
* @param tStoreyInfo c
* @return r
*/
@Override
public AjaxResult handleReAgine(TStoreyInfo tStoreyInfo) {
// CheckPowerOnCommandEvent 方法里面 会 重新开始记录老化时间
String storeyCode = tStoreyInfo.getfStoreyCode();
String[] parts = storeyCode.split("-");
String equitmentCode = parts[0];
int registerOffset = Integer.parseInt(parts[1]);
TEquipmentInfo tEquipmentInfo = equipmentInfoMapper.selectTEquipmentInfoByCode(equitmentCode);
if(tEquipmentInfo == null) {
return AjaxResult.error("无此设备信息!!!");
}
String powerOutageIp = tEquipmentInfo.getfPowerOutageIp();
Integer powerOutagePort = tEquipmentInfo.getfPowerOutagePort();
eventPublisher.publishEvent(new CheckPowerOnCommandEvent(
this,
equitmentCode,
powerOutageIp,
powerOutagePort,
registerOffset,
registerOffset - 1
));
return AjaxResult.success();
}
/**
* 发送下料指令
* @return r
......@@ -573,26 +606,55 @@ public class TStoreyInfoServiceImpl implements ITStoreyInfoService
if(notCompletedCommand > 0) {
return AjaxResult.error("有未完成指令,请稍后再试");
} else {
// 根据指令 去查 老化层
TStoreyInfo tStoreyInfo = tStoreyInfoMapper.selectByBlankingCommand(command);
if(tStoreyInfo == null) {
return AjaxResult.error("无此指令对应的老化层信息");
}
TTrayInfo tTrayInfo = tTrayInfoMapper.selectTTrayInfoByStoreyCode(tStoreyInfo.getfStoreyCode());
if(checkStoreyInfoPower(tStoreyInfo)) {
TTrayInfo tTrayInfo = tTrayInfoMapper.selectTTrayInfoByStoreyCode(tStoreyInfo.getfStoreyCode());
//还得去把没执行的 定时任务删掉,否则会导致 再次 发送下料指令
try {
deviceTaskScheduler.cleanExistingTasks(tStoreyInfo.getfStoreyId());
} catch (SchedulerException e) {
return AjaxResult.error("定时任务异常,请联系管理员");
}
//还得去把没执行的 定时任务删掉,否则会导致 再次 发送下料指令
try {
deviceTaskScheduler.cleanExistingTasks(tStoreyInfo.getfStoreyId());
} catch (SchedulerException e) {
return AjaxResult.error("定时任务异常,请联系管理员");
}
if(tTrayInfo != null) {
robotArmCommandService.generateBlankingCommand(tTrayInfo.getfTrayCode(), tStoreyInfo.getfStoreyCode(), command);
if(tTrayInfo != null) {
robotArmCommandService.generateBlankingCommand(tTrayInfo.getfTrayCode(), tStoreyInfo.getfStoreyCode(), command);
}
} else {
robotArmCommandService.sendCommand(command);
}
}
return AjaxResult.success();
}
/**
* 批量处理
* @param tStoreyInfo c
* @return r
*/
private Boolean checkStoreyInfoPower(TStoreyInfo tStoreyInfo) {
TEquipmentInfo tEquipmentInfo = equipmentInfoMapper.selectTEquipmentInfoById(tStoreyInfo.getfEquipmentId());
String registerOffset = tStoreyInfo.getfStoreyCode().split("-")[1];
try {
ModbusMaster master = Modbus4jUtils.getMaster(tEquipmentInfo.getfPowerOutageIp(), tEquipmentInfo.getfPowerOutagePort());
int i = Integer.parseInt(registerOffset);
int registerOffsetInt = i - 1;
return Modbus4jUtils.readCoilStatus(master, 1, registerOffsetInt);
} catch (ModbusInitException | ModbusTransportException | ErrorResponseException e) {
throw new RuntimeException(e);
}
}
/**
* 批量开灯
* @return r
......
......@@ -15,19 +15,20 @@ import com.zehong.system.domain.TStoreyInfo;
import com.zehong.system.mapper.PalletDeviceBindingMapper;
import com.zehong.system.mapper.TStoreyInfoMapper;
import com.zehong.system.modbus.handler.ModbusResultHandler;
import com.zehong.system.modbus.handler.dto.DeviceStatusReaderDto;
import com.zehong.system.modbus.util.Modbus4jUtils;
import com.zehong.system.service.ITEquipmentAlarmDataService;
import org.apache.commons.lang3.StringUtils;
import org.quartz.*;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Calendar;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;
......@@ -45,7 +46,6 @@ public class DeviceCommunicationJob implements Job {
// -------------------------- 常量配置(统一管理,避免魔法值)--------------------------
// 超时控制:必须小于Cron周期(假设Cron为5分钟,这里留1分钟缓冲)
private static final int TOTAL_TASK_TIMEOUT_SEC = 240; // 任务总超时:4分钟
private static final int SINGLE_PORT_TIMEOUT_SEC = 60; // 单个端口超时:1分钟
private static final int SINGLE_DEVICE_TIMEOUT_SEC = 10; // 单个设备超时:10秒
// Modbus配置:取消内置重试,统一用自定义重试
private static final int MODBUS_CONN_TIMEOUT_MS = 3000; // 连接超时:3秒
......
......@@ -64,12 +64,13 @@ public class DeviceTaskScheduler {
// 2. 清理可能的残留任务
cleanExistingTasks(fStoreyId);
// 3. 创建核心任务
// 1. 创建3个端口专属任务(501:5分钟后,502:10分钟后,503:15分钟后)
// createPortSpecificCommJobs(fStoreyId);
// 3. 创建5分钟开始读写时间的job
createHourlyCommunicationJob(fStoreyId);
// 4. 创建71小时 执行的任务
prepareFinalExecutionJob(fStoreyId);
// 5. 创建72小时 最终任务
createFinalExecutionJob(fStoreyId, fPowerOutageIp, fPowerOutagePort);
checkTaskStatus(fStoreyId);
......@@ -262,6 +263,45 @@ public class DeviceTaskScheduler {
log.info("通信任务[{}]创建成功,下次执行:{}", jobId, nextFireTime);
}
/**
* 创建71 小时 执行的任务逻辑
*/
private void prepareFinalExecutionJob(Long fStoreyId) throws SchedulerException {
String jobId = "PREPARE_" + fStoreyId;
JobKey jobKey = new JobKey(jobId, JOB_GROUP);
TriggerKey triggerKey = new TriggerKey(jobId + "_TRIGGER", TRIGGER_GROUP);
// 先删除旧的触发器和任务(彻底清理)
if (scheduler.checkExists(triggerKey)) {
scheduler.unscheduleJob(triggerKey);
}
if (scheduler.checkExists(jobKey)) {
scheduler.deleteJob(jobKey);
}
JobDetail job = JobBuilder.newJob(PrepareFinalExecutionJob.class)
.withIdentity(jobKey)
.usingJobData("fStoreyId", fStoreyId.toString())
.storeDurably(false)
.build();
Date executeTime = Date.from(Instant.now().plus(10, ChronoUnit.MINUTES));
// 关键修复:使用StartAt而不是StartNow
SimpleTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity(triggerKey)
.forJob(jobKey)
.withDescription("设备" + fStoreyId + "最终任务触发器,触发时间是:" + executeTime)
.startAt(executeTime)
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withMisfireHandlingInstructionFireNow() // 错过立即执行
.withRepeatCount(0)) // 仅一次
.build();
Date nextFireTime = scheduler.scheduleJob(job, trigger);
log.info("通信任务[{}]创建成功,下次执行:{}", jobId, nextFireTime);
}
/**
* 2. 创建15分钟后执行的最终任务(保持原逻辑,优化超时)
*/
......@@ -280,7 +320,7 @@ public class DeviceTaskScheduler {
.requestRecovery(true)
.build();
Date executeTime = Date.from(Instant.now().plus(7, ChronoUnit.MINUTES));
Date executeTime = Date.from(Instant.now().plus(15, ChronoUnit.MINUTES));
SimpleTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity(triggerKey)
.forJob(jobKey)
......
package com.zehong.system.task;
import com.serotonin.modbus4j.ModbusFactory;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.ip.IpParameters;
import com.serotonin.modbus4j.ip.tcp.TcpMaster;
import com.serotonin.modbus4j.msg.ModbusResponse;
import com.serotonin.modbus4j.msg.ReadHoldingRegistersRequest;
import com.serotonin.modbus4j.msg.ReadHoldingRegistersResponse;
import com.zehong.system.domain.PalletDeviceBinding;
import com.zehong.system.domain.SysRealTimeAdRange;
import com.zehong.system.domain.TEquipmentAlarmData;
import com.zehong.system.domain.TStoreyInfo;
import com.zehong.system.mapper.PalletDeviceBindingMapper;
import com.zehong.system.mapper.SysRealTimeAdRangeMapper;
import com.zehong.system.mapper.TStoreyInfoMapper;
import com.zehong.system.modbus.handler.ModbusResultHandler;
import com.zehong.system.modbus.util.Modbus4jUtils;
import com.zehong.system.service.ITEquipmentAlarmDataService;
import org.apache.commons.lang3.StringUtils;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
/**
* @author lenovo
* @date 2025/11/21
* @description 71 小时后执行的任务
*/
@Component
@DisallowConcurrentExecution // 禁止同一任务并行执行(必须保留)
public class PrepareFinalExecutionJob implements Job {
private static final Logger log = LoggerFactory.getLogger(DeviceCommunicationJob.class);
// -------------------------- 常量配置(统一管理,避免魔法值)--------------------------
// 超时控制:必须小于Cron周期(假设Cron为5分钟,这里留1分钟缓冲)
private static final int TOTAL_TASK_TIMEOUT_SEC = 240; // 任务总超时:4分钟
private static final int SINGLE_DEVICE_TIMEOUT_SEC = 10; // 单个设备超时:10秒
// Modbus配置:取消内置重试,统一用自定义重试
private static final int MODBUS_CONN_TIMEOUT_MS = 3000; // 连接超时:3秒
private static final int CUSTOM_RETRY_TIMES = 2; // 自定义重试次数:1次
// Modbus寄存器配置
private static final int REG_START_ADDR = 0;
private static final int REG_READ_COUNT = 10;
// 全局线程池 - 避免重复创建
private static final ExecutorService GLOBAL_DEVICE_EXECUTOR = new ThreadPoolExecutor(
50, 100, 60, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(500),
r -> new Thread(r, "prepare-global-modbus-device"),
new ThreadPoolExecutor.CallerRunsPolicy()
);
// 工厂(单例)
private static final ModbusFactory modbusFactory = new ModbusFactory();
// -------------------------- 依赖注入 --------------------------
@Resource
private ITEquipmentAlarmDataService alarmDataService;
@Resource
private TStoreyInfoMapper tStoreyInfoMapper;
@Resource
private PalletDeviceBindingMapper palletDeviceBindingMapper;
@Resource
private SysRealTimeAdRangeMapper sysRealTimeAdRangeMapper;
// -------------------------- 核心执行逻辑 --------------------------
@Override
public void execute(JobExecutionContext context) {
String storeyIdStr = getStoreyIdFromContext(context);
long startTime = System.currentTimeMillis();
try {
TStoreyInfo storeyInfo = validateAndGetStoreyInfo(storeyIdStr);
// 并行处理3个端口
List<CompletableFuture<Void>> portFutures = Arrays.asList(
processPort(storeyInfo, 501, Arrays.asList(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)),
processPort(storeyInfo, 502, Arrays.asList(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)),
processPort(storeyInfo, 503, Arrays.asList(55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72))
);
// 等待所有端口完成,带总超时
CompletableFuture<Void> allPorts = CompletableFuture.allOf(
portFutures.toArray(new CompletableFuture[0])
);
allPorts.get(TOTAL_TASK_TIMEOUT_SEC, TimeUnit.SECONDS);
log.info("任务执行成功: fStoreyId={}, 耗时={}ms",
storeyIdStr, System.currentTimeMillis() - startTime);
} catch (TimeoutException e) {
log.warn("任务执行超时: fStoreyId={}", storeyIdStr);
recordAlarm(null, storeyIdStr, "任务执行超时");
} catch (Exception e) {
log.error("任务执行异常: fStoreyId={}", storeyIdStr, e);
recordAlarm(null, storeyIdStr, "任务执行异常: " + e.getMessage());
}
}
/**
* 处理单个端口的所有设备
*/
private CompletableFuture<Void> processPort(TStoreyInfo storeyInfo, int port, List<Integer> deviceIds) {
return CompletableFuture.runAsync(() -> {
String ip = storeyInfo.getfIp();
String storeyIdStr = storeyInfo.getfStoreyId().toString();
log.info("开始端口通信: ip={}, port={}, 设备数={}", ip, port, deviceIds.size());
AtomicInteger errorCount = new AtomicInteger(0);
// 并行处理该端口的所有设备
List<CompletableFuture<Boolean>> deviceFutures = deviceIds.stream()
.map(deviceId -> processDeviceWithWrite(ip, port, deviceId, errorCount))
.collect(Collectors.toList());
try {
// 等待该端口所有设备完成
CompletableFuture<Void> allDevices = CompletableFuture.allOf(
deviceFutures.toArray(new CompletableFuture[0])
);
// 端口超时 = 设备数 * 单设备超时 / 并发因子
int portTimeout = Math.max(30, deviceIds.size() * SINGLE_DEVICE_TIMEOUT_SEC / 5);
allDevices.get(portTimeout, TimeUnit.SECONDS);
} catch (TimeoutException e) {
log.warn("端口{}通信超时: ip={}, fStoreyId={}", port, ip, storeyIdStr);
recordAlarm(storeyInfo, "端口" + port + "通信超时");
} catch (Exception e) {
log.error("端口{}通信异常: ip={}, fStoreyId={}", port, ip, storeyIdStr, e);
}
if (errorCount.get() > 0) {
log.warn("端口{}部分设备失败: 失败数={}, fStoreyId={}",
port, errorCount.get(), storeyIdStr);
}
log.info("端口通信完成: ip={}, port={}, fStoreyId={}", ip, port, storeyIdStr);
}, GLOBAL_DEVICE_EXECUTOR);
}
/**
* 处理单个设备(读取 + 条件写入)
*/
private CompletableFuture<Boolean> processDeviceWithWrite(String ip, int port, int deviceId, AtomicInteger errorCount) {
return CompletableFuture.supplyAsync(() -> {
ModbusMaster master = null;
PalletDeviceBinding binding = null;
try {
// 1. 读取设备数据
int[] result = readDeviceWithRetry(ip, port, deviceId);
// 2. 查询设备绑定信息
binding = palletDeviceBindingMapper.selectByTrayIdAndIndex(ip, deviceId);
if (binding == null) {
log.warn("未找到设备绑定: ip={}, deviceId={}", ip, deviceId);
recordAlarm(null, "ip:" + ip + ",port:" + port + ",deviceId:" + deviceId, "未找到设备绑定");
errorCount.incrementAndGet();
return false;
}
// 3. 更新设备状态
binding.setStatus(String.valueOf(result[1]));
// 3. 更新浓度值
binding.setConcentration(String.valueOf(result[0]));
// 4. 更新AD值
int realAd = result[2];
binding.setRealTimeAd(realAd);
SysRealTimeAdRange topOne = sysRealTimeAdRangeMapper.getTopOne();
if (topOne != null && realAd >= topOne.getRealTimeAdMin() && realAd <= topOne.getRealTimeAdMax()) {
binding.setRealTimeStatus("1");
} else {
binding.setRealTimeStatus("0");
}
// 4. 条件写入时间
if (result[1] == 1 || result[1] == 3 || result[1] == 4) {
// 重用之前的master连接进行写操作
master = createModbusMaster(ip, port);
checkAndUpdateTime(master, deviceId, binding);
} else {
binding.setStatus(result[1] +"");
}
// 5. 更新数据库
palletDeviceBindingMapper.updatePalletDeviceBinding(binding);
log.debug("设备{}处理完成: ip={}, port={}, status={}", deviceId, ip, port, result[1]);
return true;
} catch (Exception e) {
log.error("设备{}处理异常: ip={}, port={}", deviceId, ip, port, e);
if(binding == null) {
binding = palletDeviceBindingMapper.selectByTrayIdAndIndex(ip, deviceId);
}
binding.setStatus("5");
binding.setWriteTimeStatus("0");
binding.setRealTimeStatus("0");
palletDeviceBindingMapper.updatePalletDeviceBinding(binding);
errorCount.incrementAndGet();
return false;
} finally {
destroyModbusMaster(master, deviceId);
}
}, GLOBAL_DEVICE_EXECUTOR);
}
/**
* 验证参数并获取设备信息(参数错误直接抛出异常)
*/
private TStoreyInfo validateAndGetStoreyInfo(String storeyIdStr) {
if (StringUtils.isBlank(storeyIdStr)) {
log.error("fStoreyId参数为空");
return null;
}
try {
Long storeyId = Long.parseLong(storeyIdStr);
TStoreyInfo storeyInfo = tStoreyInfoMapper.selectTStoreyInfoById(storeyId);
if (storeyInfo == null || StringUtils.isBlank(storeyInfo.getfIp())) {
log.error("设备信息无效: fStoreyId={}", storeyIdStr);
return null;
}
return storeyInfo;
} catch (NumberFormatException e) {
log.error("fStoreyId格式错误: {}", storeyIdStr);
return null;
}
}
/**
* 带重试的设备读取
*/
private int[] readDeviceWithRetry(String ip, int port, int deviceId) {
ModbusMaster master = null;
int[] lastResult = null; // 用于记录最后一次读取的结果(无论是否满足停止条件)
try {
// 只创建一次ModbusMaster,循环内复用
master = createModbusMaster(ip, port);
for (int retry = 0; retry <= CUSTOM_RETRY_TIMES; retry++) {
try {
// 执行读取操作,获取本次结果
int[] currentResult = readDeviceRegisters(master, deviceId);
// 更新最后一次结果(无论是否满足停止条件,都记录)
lastResult = currentResult;
// 检查停止条件,如果满足则提前返回(无需等到重试耗尽)
if (ModbusResultHandler.createDefaultStopCondition().test(currentResult)) {
log.info("设备{}第{}次读取成功(满足条件): ip={}, port={}",
deviceId, retry + 1, ip, port);
return currentResult;
}
// 未满足条件且不是最后一次重试,休眠后继续
if (retry < CUSTOM_RETRY_TIMES) {
log.info("设备{}第{}次读取未满足条件,准备重试: ip={}, port={}",
deviceId, retry + 1, ip, port);
Thread.sleep(200);
}
} catch (Exception e) {
// 本次读取发生异常,记录日志但不中断重试(继续下一次)
log.warn("设备{}第{}次读取发生异常: ip={}, port={}",
deviceId, retry + 1, ip, port, e);
// 如果是最后一次重试,异常时lastResult可能为null(需后续处理)
}
}
// 循环结束(重试耗尽),此时lastResult为最后一次的结果(可能是正常读取但不满足条件,或null)
log.info("设备{}重试次数耗尽,返回最后一次结果: ip={}, port={}",
deviceId, ip, port);
} catch (Exception e) {
// 捕获创建ModbusMaster或休眠时的异常(非读取操作的异常)
log.error("设备{}连接创建或休眠失败: ip={}, port={}",
deviceId, ip, port, e);
throw new RuntimeException("设备连接或操作异常", e);
} finally {
// 无论结果如何,最终销毁连接
destroyModbusMaster(master, deviceId);
}
// 处理最后一次结果可能为null的情况(例如所有重试都异常)
if (lastResult == null) {
throw new RuntimeException("设备所有读取尝试均失败(无有效结果)");
}
return lastResult;
}
/**
* 检查并更新时间
*/
private void checkAndUpdateTime(ModbusMaster master, int deviceId,PalletDeviceBinding binding) {
try {
int[] timeRegisters = Modbus4jUtils.readDeviceRegisters(master, deviceId);
if (timeRegisters.length < 9) {
log.warn("设备{}时间寄存器读取失败或数据长度不足", deviceId);
binding.setWriteTimeStatus("0");
binding.setRealTimeStatus("0");
recordAlarmByBinding(binding, "设备时间寄存器读取失败");
return;
}
int deviceYear = timeRegisters[4];
int deviceMonth = timeRegisters[5];
int deviceDay = timeRegisters[6];
int deviceHour = timeRegisters[7];
int deviceMinute = timeRegisters[8];
log.info("设备{}当前时间: {}-{}-{} {}:{}",
deviceId, deviceYear, deviceMonth, deviceDay, deviceHour, deviceMinute);
// 2. 获取系统当前时间
Calendar systemCal = Calendar.getInstance();
int systemYear = systemCal.get(Calendar.YEAR);
int systemMonth = systemCal.get(Calendar.MONTH) + 1; // Calendar月份从0开始
int systemDay = systemCal.get(Calendar.DATE);
int systemHour = systemCal.get(Calendar.HOUR_OF_DAY);
int systemMinute = systemCal.get(Calendar.MINUTE);
log.info("系统当前时间: {}-{}-{} {}:{}",
systemYear, systemMonth, systemDay, systemHour, systemMinute);
// 3. 比较时间差异
if (isTimeConsistent(deviceYear, deviceMonth, deviceDay, deviceHour, deviceMinute,
systemYear, systemMonth, systemDay, systemHour, systemMinute)) {
// 时间一致
handleTimeConsistent(deviceId, binding);
} else {
// 时间不一致
handleTimeInconsistent(master, deviceId, binding, systemCal);
}
} catch (ModbusTransportException e) {
binding.setWriteTimeStatus("0");
binding.setRealTimeStatus("0");
log.error("设备{}时间写入异常", deviceId, e);
recordAlarmByBinding(binding, "设备时间写入异常: " + e.getMessage());
}
}
/**
* 判断设备时间与系统时间是否一致(差异在2分钟内)
* 考虑跨年、跨月等边界情况
*/
private boolean isTimeConsistent(int deviceYear, int deviceMonth, int deviceDay,
int deviceHour, int deviceMinute,
int systemYear, int systemMonth, int systemDay,
int systemHour, int systemMinute) {
// 1. 首先检查年份差异(考虑跨年)
if (Math.abs(deviceYear - systemYear) > 1) {
log.debug("年份差异超过1年: 设备{}年 vs 系统{}年", deviceYear, systemYear);
return false;
}
// 2. 转换为总分钟数进行比较(考虑跨年、跨月、跨日)
long deviceTotalMinutes = calculateTotalMinutes(deviceYear, deviceMonth, deviceDay, deviceHour, deviceMinute);
long systemTotalMinutes = calculateTotalMinutes(systemYear, systemMonth, systemDay, systemHour, systemMinute);
long minuteDiff = Math.abs(deviceTotalMinutes - systemTotalMinutes);
log.debug("时间差异比较: 设备分钟数={}, 系统分钟数={}, 差异={}分钟",
deviceTotalMinutes, systemTotalMinutes, minuteDiff);
// 3. 差异在2分钟内认为一致
return minuteDiff <= 2;
}
/**
* 处理时间一致的情况
*/
private void handleTimeConsistent(int deviceId, PalletDeviceBinding binding) {
log.info("设备{}时间与系统时间一致,无需更新", deviceId);
binding.setWriteTimeStatus("1"); // 时间状态正常
log.info("设备{}时间检查完成:时间一致", deviceId);
}
/**
* 处理时间不一致的情况 - 更新设备时间
*/
private void handleTimeInconsistent(ModbusMaster master, int deviceId,
PalletDeviceBinding binding, Calendar systemCal) {
log.info("设备{}时间与系统时间不一致,开始更新时间", deviceId);
try {
// 1 如果时间状态为空,则写入时间
if(binding.getWriteTimeStatus() == null) {
int year = systemCal.get(Calendar.YEAR);
int month = systemCal.get(Calendar.MONTH) + 1;
int day = systemCal.get(Calendar.DATE);
int hour = systemCal.get(Calendar.HOUR_OF_DAY);
int minute = systemCal.get(Calendar.MINUTE);
// 写入时间寄存器
boolean success;
success = Modbus4jUtils.writeRegister(master, deviceId, 4, (short) year);
success = success && Modbus4jUtils.writeRegister(master, deviceId, 5, (short) month);
success = success && Modbus4jUtils.writeRegister(master, deviceId, 6, (short) day);
success = success && Modbus4jUtils.writeRegister(master, deviceId, 7, (short) hour);
success = success && Modbus4jUtils.writeRegister(master, deviceId, 8, (short) minute);
if (success) {
binding.setRecordYear(String.valueOf(year));
binding.setRecordMonth(String.valueOf(month));
binding.setRecordDate(String.valueOf(day));
binding.setRecordHour(String.valueOf(hour));
binding.setRecordMinute(String.valueOf(minute));
binding.setWriteTimeStatus("1");
log.info("设备{}时间更新成功: {}-{}-{} {}:{}",
deviceId, year, month, day, hour, minute);
} else {
binding.setWriteTimeStatus("0");
log.error("设备{}时间更新失败", deviceId);
recordAlarmByBinding(binding, "设备时间更新失败");
}
// 2. 如果时间状态为1,则更新 时间运行状态 为异常 状态
} else if("1".equals(binding.getWriteTimeStatus())) {
log.info("设备{}时间状态为1,则更新 时间运行状态 为异常 状态", deviceId);
binding.setRunTimeStatus("0");
} // 3. 写时间状态 异常状态;不管
} catch (Exception e) {
binding.setWriteTimeStatus("0");
log.error("设备{}时间更新异常", deviceId, e);
recordAlarmByBinding(binding, "设备时间更新异常: " + e.getMessage());
}
}
/**
* 计算从参考时间点(如2000年)开始的总分钟数
* 用于准确比较时间差异,考虑跨年跨月
*/
private long calculateTotalMinutes(int year, int month, int day, int hour, int minute) {
// 使用2000-01-01 00:00:00作为参考点
Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, 2000);
cal.set(Calendar.MONTH, Calendar.JANUARY);
cal.set(Calendar.DAY_OF_MONTH, 1);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
long baseTimeInMillis = cal.getTimeInMillis();
// 设置目标时间
cal.set(Calendar.YEAR, year);
cal.set(Calendar.MONTH, month - 1); // 转换为Calendar月份(0-based)
cal.set(Calendar.DAY_OF_MONTH, day);
cal.set(Calendar.HOUR_OF_DAY, hour);
cal.set(Calendar.MINUTE, minute);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
long targetTimeInMillis = cal.getTimeInMillis();
// 返回总分钟数
return (targetTimeInMillis - baseTimeInMillis) / (1000 * 60);
}
/**
* 写入当前时间到设备
*/
private void writeCurrentTimeToDevice(ModbusMaster master, int deviceId,
PalletDeviceBinding binding) {
try {
Calendar cal = Calendar.getInstance();
int year = cal.get(Calendar.YEAR);
int month = cal.get(Calendar.MONTH) + 1;
int day = cal.get(Calendar.DATE);
int hour = cal.get(Calendar.HOUR_OF_DAY);
int minute = cal.get(Calendar.MINUTE);
// 写入时间寄存器
boolean success;
success = Modbus4jUtils.writeRegister(master, deviceId, 4, (short) year);
success &= Modbus4jUtils.writeRegister(master, deviceId, 5, (short) month);
success &= Modbus4jUtils.writeRegister(master, deviceId, 6, (short) day);
success &= Modbus4jUtils.writeRegister(master, deviceId, 7, (short) hour);
success &= Modbus4jUtils.writeRegister(master, deviceId, 8, (short) minute);
if (success) {
binding.setRecordYear(String.valueOf(year));
binding.setRecordMonth(String.valueOf(month));
binding.setRecordDate(String.valueOf(day));
binding.setRecordHour(String.valueOf(hour));
binding.setRecordMinute(String.valueOf(minute));
log.debug("设备{}时间写入成功", deviceId);
} else {
binding.setWriteTimeStatus("0");
recordAlarmByBinding(binding, "设备时间写入失败");
}
} catch (Exception e) {
binding.setWriteTimeStatus("0");
log.error("设备{}时间写入异常", deviceId, e);
recordAlarmByBinding(binding, "设备时间写入异常: " + e.getMessage());
}
}
// -------------------------- Modbus工具方法(显式抛出异常)--------------------------
/**
* 创建Modbus连接(取消内置重试,统一自定义重试)
*/
private ModbusMaster createModbusMaster(String ip, int port) throws ModbusInitException {
IpParameters params = new IpParameters();
params.setHost(ip);
params.setPort(port);
TcpMaster master = (TcpMaster) modbusFactory.createTcpMaster(params, true);
master.setTimeout(MODBUS_CONN_TIMEOUT_MS);
master.setRetries(0);
master.init();
return master;
}
/**
* 读取设备寄存器(异常直接抛出)
*/
private int[] readDeviceRegisters(ModbusMaster master, int deviceId) throws ModbusTransportException {
ReadHoldingRegistersRequest request = Modbus4jUtils.getReadHoldingRegistersRequest(
deviceId, REG_START_ADDR, REG_READ_COUNT);
ModbusResponse response = master.send(request);
if (!(response instanceof ReadHoldingRegistersResponse)) {
log.info("无效Modbus响应类型:" + response.getClass().getName() + ",deviceId=" + deviceId);
}
assert response instanceof ReadHoldingRegistersResponse;
ReadHoldingRegistersResponse regResp = (ReadHoldingRegistersResponse) response;
short[] signedVals = regResp.getShortData();
int[] unsignedVals = new int[signedVals.length];
for (int i = 0; i < signedVals.length; i++) {
unsignedVals[i] = signedVals[i] & 0xFFFF; // 转换为无符号整数
}
log.info("设备寄存器读取结果:deviceId={},值={}", deviceId, Arrays.toString(unsignedVals));
return unsignedVals;
}
/**
* 销毁Modbus连接(反射失败直接抛出异常,显式暴露问题)
*/
private void destroyModbusMaster(ModbusMaster master, int deviceId) {
if (master != null) {
try {
master.destroy();
} catch (Exception e) {
log.debug("设备{}: ModbusMaster销毁异常", deviceId, e);
}
}
}
// -------------------------- 辅助方法(日志/告警)--------------------------
/**
* 从JobContext中获取fStoreyId(失败返回unknown)
*/
private String getStoreyIdFromContext(JobExecutionContext context) {
try {
JobDataMap data = context.getJobDetail().getJobDataMap();
return data != null ? data.getString("fStoreyId") : "unknown";
} catch (Exception e) {
log.error("从JobContext获取fStoreyId失败", e);
return "unknown";
}
}
/**
* 记录告警(兼容设备信息为空的场景)
*/
private void recordAlarm(TStoreyInfo storeyInfo, String equipmentCode, String alarmData) {
try {
TEquipmentAlarmData alarm = new TEquipmentAlarmData();
alarm.setfAlarmType("03"); // 老化层告警
alarm.setfEquipmentCode(storeyInfo != null ? storeyInfo.getfStoreyCode() : equipmentCode);
alarm.setfAlarmData(alarmData);
alarm.setfCreateTime(new Date());
alarmDataService.insertTEquipmentAlarmData(alarm);
log.debug("告警记录成功:设备编码={},内容={}", alarm.getfEquipmentCode(), alarmData);
} catch (Exception e) {
log.error("告警记录失败:设备编码={},内容={}", equipmentCode, alarmData, e);
}
}
/**
* 简化告警记录(设备信息非空时)
*/
private void recordAlarm(TStoreyInfo storeyInfo, String alarmData) {
recordAlarm(storeyInfo, storeyInfo != null ? storeyInfo.getfStoreyCode() : "unknown", alarmData);
}
/**
* 统一告警记录(修复字段错误,确保写入成功)
*/
private void recordAlarmByBinding(PalletDeviceBinding binding, String alarmMsg) {
String equipmentCode = binding != null ? binding.getDeviceCode() : "unknown";
recordAlarmByBingding(equipmentCode, alarmMsg);
}
private void recordAlarmByBingding(String equipmentCode, String alarmMsg) {
try {
TEquipmentAlarmData alarm = new TEquipmentAlarmData();
alarm.setfAlarmType("04"); // 04.点位告警
alarm.setfEquipmentCode(equipmentCode);
alarm.setfAlarmData(alarmMsg);
alarm.setfCreateTime(new Date()); // 修复字段错误:用fCreateTime而非createTime
alarmDataService.insertTEquipmentAlarmData(alarm);
log.debug("告警记录成功:equipmentCode={}, msg={}", equipmentCode, alarmMsg);
} catch (Exception e) {
log.error("告警记录失败:equipmentCode={}, msg={}", equipmentCode, alarmMsg, e);
}
}
}
......@@ -24,8 +24,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="writeTimeStatus" column="f_write_time_status" />
<result property="adjustmentZeroAd" column="f_adjustment_zero_ad" />
<result property="zeroStatus" column="f_zero_status" />
<result property="calibrationAd" column="f_calibration_ad" />
<result property="calibrationAdStatus" column="f_calibration_status" />
<result property="concentration" column="f_concentration" />
<result property="runTimeStatus" column="f_run_time_status" />
<result property="realTimeAd" column="f_real_time_ad" />
<result property="realTimeStatus" column="f_real_time_ad_status" />
</resultMap>
<sql id="selectPalletDeviceBindingVo">
......@@ -48,8 +55,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
palDeviceBinding.f_record_minute,
palDeviceBinding.f_write_time_status,
palDeviceBinding.f_adjustment_zero_ad,
palDeviceBinding.f_zero_status,
palDeviceBinding.f_calibration_ad,
palDeviceBinding.f_concentration
palDeviceBinding.f_calibration_status,
palDeviceBinding.f_concentration,
palDeviceBinding.f_run_time_status,
palDeviceBinding.f_real_time_ad,
palDeviceBinding.f_real_time_ad_status
from t_pallet_device_binding palDeviceBinding
left join t_tray_info trayInfo on trayInfo.f_tray_id = palDeviceBinding.f_tray_id
</sql>
......@@ -83,13 +95,17 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="createTime != null "> and palDeviceBinding.f_create_time = #{createTime}</if>
<if test="status != null and status != '' "> and palDeviceBinding.f_status = #{status}</if>
</where>
order by palDeviceBinding.f_index asc
order by trayInfo.f_tray_code ,palDeviceBinding.f_index asc
</select>
<select id="getAllExcludeUnbindingTimeByTrayId" parameterType="long" resultMap="PalletDeviceBindingResult">
<include refid="selectPalletDeviceBindingVo"/>
where palDeviceBinding.f_tray_id = #{trayId}
order by palDeviceBinding.f_index asc
</select>
<select id="listByTrayCode" parameterType="string" resultMap="PalletDeviceBindingResult">
<include refid="selectPalletDeviceBindingVo"/>
where trayInfo.f_tray_code = #{trayCode}
</select>
<select id="selectPalletDeviceBindingById" parameterType="Long" resultMap="PalletDeviceBindingResult">
<include refid="selectPalletDeviceBindingVo"/>
......@@ -114,8 +130,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
f_record_minute ,
f_write_time_status,
f_adjustment_zero_ad,
f_zero_status,
f_calibration_ad,
f_concentration
f_calibration_status,
f_concentration,
f_run_time_status,
f_real_time_ad,
f_real_time_ad_status
from t_pallet_device_binding palDeviceBinding where palDeviceBinding.f_tray_id = (
SELECT
trayInfo.f_tray_id
......@@ -151,8 +172,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
f_record_minute = null,
f_write_time_status = null,
f_adjustment_zero_ad = null,
f_zero_status = null,
f_calibration_ad = null,
f_concentration = null
f_calibration_status = null,
f_concentration = null,
f_calibration_status = null,
f_run_time_status = null,
f_real_time_ad = null,
f_real_time_ad_status = null
where f_tray_id = #{trayId}
</update>
<insert id="insertPalletDeviceBinding" parameterType="PalletDeviceBinding" useGeneratedKeys="true" keyProperty="palletDeviceBindingId">
......@@ -194,8 +221,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
f_record_minute = null,
f_write_time_status = null,
f_adjustment_zero_ad = null,
f_zero_status = null,
f_calibration_ad = null,
f_concentration = null
f_calibration_status = null,
f_concentration = null,
f_run_time_status = null,
f_real_time_ad = null,
f_real_time_ad_status = null
where f_pallet_device_binding_id = #{palletDeviceBindingId}
</update>
<update id="updatePalletDeviceBinding" parameterType="PalletDeviceBinding">
......@@ -220,8 +252,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="recordMinute != null">f_record_minute = #{recordMinute},</if>
<if test="writeTimeStatus != null">f_write_time_status = #{writeTimeStatus},</if>
<if test="adjustmentZeroAd != null">f_adjustment_zero_ad = #{adjustmentZeroAd},</if>
<if test="zeroStatus != null">f_zero_status = #{zeroStatus},</if>
<if test="calibrationAd != null">f_calibration_ad = #{calibrationAd},</if>
<if test="concentration != null">f_concentration = #{concentration},</if>
<if test="calibrationAdStatus != null">f_calibration_status = #{calibrationAdStatus},</if>
<if test="runTimeStatus != null">f_run_time_status = #{runTimeStatus},</if>
<if test="realTimeAd != null">f_real_time_ad = #{realTimeAd},</if>
<if test="realTimeStatus != null">f_real_time_status = #{realTimeStatus},</if>
</trim>
where f_pallet_device_binding_id = #{palletDeviceBindingId}
</update>
......@@ -235,6 +272,18 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
WHERE f_pallet_device_binding_id = #{item.palletDeviceBindingId}
</foreach>
</update>
<update id="batchUpdateAdAndStatus" parameterType="list">
<foreach collection="palletDeviceBindingList" item="item" index="index" separator=";">
UPDATE t_pallet_device_binding
<trim prefix="SET" suffixOverrides=",">
<if test="item.adjustmentZeroAd != null">f_adjustment_zero_ad = #{item.adjustmentZeroAd},</if>
<if test="item.zeroStatus == null">f_zero_status = #{item.zeroStatus},</if>
<if test="item.calibrationAd != null">f_calibration_ad = #{item.calibrationAd},</if>
<if test="item.calibrationAdStatus != null">f_calibration_status = #{item.calibrationAdStatus},</if>
</trim>
where f_pallet_device_binding_id = #{item.palletDeviceBindingId}
</foreach>
</update>
<update id="batchUpdateDeviceCodeAndUnbindingTime" parameterType="list">
<foreach collection="palletDeviceBindingList" item="item" index="index" separator=";">
UPDATE t_pallet_device_binding
......@@ -248,8 +297,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
f_record_minute = null,
f_write_time_status = null,
f_adjustment_zero_ad = null,
f_zero_status = null,
f_calibration_ad = null,
f_calibration_status = null,
f_concentration = null,
f_run_time_status = null,
f_real_time_ad = null,
f_real_time_status = null
<if test="item.deviceCode != null">f_device_code = #{item.deviceCode},</if>
</trim>
WHERE f_pallet_device_binding_id = #{item.palletDeviceBindingId}
......@@ -267,8 +321,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
f_record_minute = null,
f_write_time_status = null,
f_adjustment_zero_ad = null,
f_zero_status = null,
f_calibration_ad = null,
f_concentration = null
f_calibration_status = null,
f_concentration = null,
f_run_time_status = null,
f_real_time_ad = null,
f_real_time_status = null
where f_tray_id = #{trayId}
</update>
......
......@@ -97,6 +97,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</set>
where config_id = #{configId}
</update>
<update id="directUpdateConfigByKey" parameterType="SysConfig">
update sys_config set config_value = #{configValue} where config_key = #{configKey}
</update>
<delete id="deleteConfigById" parameterType="Long">
delete from sys_config where config_id = #{configId}
......
<?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.zehong.system.mapper.SysRealTimeAdRangeMapper">
<resultMap type="SysRealTimeAdRange" id="SysRealTimeAdRangeResult">
<result property="id" column="f_id" />
<result property="realTimeAdMin" column="f_real_time_ad_min" />
<result property="realTimeAdMax" column="f_real_time_ad_max" />
</resultMap>
<sql id="selectSysRealTimeAdRangeVo">
select f_id, f_real_time_ad_min, f_real_time_ad_max from t_sys_real_time_ad_range
</sql>
<select id="selectSysRealTimeAdRangeList" parameterType="SysRealTimeAdRange" resultMap="SysRealTimeAdRangeResult">
<include refid="selectSysRealTimeAdRangeVo"/>
<where>
<if test="realTimeAdMin != null "> and f_real_time_ad_min = #{realTimeAdMin}</if>
<if test="realTimeAdMax != null "> and f_real_time_ad_max = #{realTimeAdMax}</if>
</where>
</select>
<select id="getTopOne" resultMap="SysRealTimeAdRangeResult">
<include refid="selectSysRealTimeAdRangeVo"/>
limit 1
</select>
<select id="selectSysRealTimeAdRangeById" parameterType="Long" resultMap="SysRealTimeAdRangeResult">
<include refid="selectSysRealTimeAdRangeVo"/>
where f_id = #{id}
</select>
<insert id="insertSysRealTimeAdRange" parameterType="SysRealTimeAdRange" useGeneratedKeys="true" keyProperty="id">
insert into t_sys_real_time_ad_range
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="realTimeAdMin != null">f_real_time_ad_min,</if>
<if test="realTimeAdMax != null">f_real_time_ad_max,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="realTimeAdMin != null">#{realTimeAdMin},</if>
<if test="realTimeAdMax != null">#{realTimeAdMax},</if>
</trim>
</insert>
<update id="updateSysRealTimeAdRange" parameterType="SysRealTimeAdRange">
update t_sys_real_time_ad_range
<trim prefix="SET" suffixOverrides=",">
<if test="realTimeAdMin != null">f_real_time_ad_min = #{realTimeAdMin},</if>
<if test="realTimeAdMax != null">f_real_time_ad_max = #{realTimeAdMax},</if>
</trim>
where f_id = #{id}
</update>
<delete id="deleteSysRealTimeAdRangeById" parameterType="Long">
delete from t_sys_real_time_ad_range where f_id = #{id}
</delete>
<delete id="deleteSysRealTimeAdRangeByIds" parameterType="String">
delete from t_sys_real_time_ad_range where f_id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
</mapper>
\ No newline at end of file
......@@ -205,6 +205,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="fAgingEndTime != null">f_aging_end_time = #{fAgingEndTime},</if>
<if test="fAgingEndTime == null">f_aging_end_time = null,</if>
</trim>
where f_storey_id = #{fStoreyId}
</update>
<update id="unbindByCode" parameterType="TStoreyInfo">
......
import request from '@/utils/request'
// 查询实时AD范围维护列表
export function listRange(query) {
return request({
url: '/system/realAdRange/list',
method: 'get',
params: query
})
}
// 查询实时AD范围维护详细
export function getRange(id) {
return request({
url: '/system/realAdRange/' + id,
method: 'get'
})
}
export function getTopOne() {
return request({
url: '/system/realAdRange/getTopOne',
method: 'get'
})
}
// 新增实时AD范围维护
export function addRange(data) {
return request({
url: '/system/realAdRange',
method: 'post',
data: data
})
}
// 修改实时AD范围维护
export function updateRange(data) {
return request({
url: '/system/realAdRange',
method: 'put',
data: data
})
}
// 删除实时AD范围维护
export function delRange(id) {
return request({
url: '/system/realAdRange/' + id,
method: 'delete'
})
}
// 导出实时AD范围维护
export function exportRange(query) {
return request({
url: '/system/realAdRange/export',
method: 'get',
params: query
})
}
......@@ -69,7 +69,7 @@ export function PowerOn(query) {
})
}
export function PowerOutage( query) {
export function PowerOutage(query) {
return request({
url: '/storey/powerOutage',
method: 'get',
......@@ -83,13 +83,21 @@ export function feeding(command) {
method: 'get'
})
}
export function blanking(command) {
export function blanking(command) {
return request({
url: '/storey/handleBlanking/' + command,
method: 'get'
})
}
export function reAgine(query) {
return request({
url: '/storey/handleReAgine/',
method: 'get',
params: query
})
}
export function directBlanking(command) {
return request({
url: '/storey/handleDirectBlanking/' + command,
......
......@@ -25,6 +25,20 @@ export function getConfigKey(configKey) {
})
}
export function getUploadMesAddress() {
return request({
url: '/system/config/getUploadMesAddress',
method: 'get'
})
}
export function updateUploadMesAddress(data) {
return request({
url: '/system/config/updateUploadMesAddress/',
method: 'post',
data: data
})
}
// 新增参数配置
export function addConfig(data) {
return request({
......@@ -66,4 +80,4 @@ export function exportConfig(query) {
method: 'get',
params: query
})
}
\ No newline at end of file
}
<template>
<div class="app-container">
<el-card shadow="hover" style="max-width: 600px; margin: 0 auto;">
<div slot="header" class="clearfix">
<span>实时AD范围维护</span>
</div>
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px" class="mt-4">
<el-form-item label="实时AD最小值" prop="realTimeAdMin">
<el-input
v-model.number="form.realTimeAdMin"
type="number"
placeholder="请输入实时AD最小值 (0-65535)"
:min="0"
:max="65535"
class="w-80"
/>
</el-form-item>
<el-form-item label="实时AD最大值" prop="realTimeAdMax">
<el-input
v-model.number="form.realTimeAdMax"
type="number"
placeholder="请输入实时AD最大值 (0-65535)"
:min="0"
:max="65535"
class="w-80"
/>
</el-form-item>
<el-form-item>
<el-button
type="primary"
@click="handleSave"
:loading="loading"
>
<i class="el-icon-check"></i> 保存设置
</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</template>
<script>
import { updateRange ,getTopOne,addRange} from "@/api/realAdRange/realAdRange";
export default {
name: "RealTimeAdRange",
data() {
return {
// 加载状态
loading: false,
// 表单数据
form: {
realTimeAdMin: null,
realTimeAdMax: null
},
// 表单校验规则
rules: {
realTimeAdMin: [
{ required: true, message: "实时AD最小值不能为空", trigger: "blur" },
{ type: "integer", message: "实时AD最小值必须为整数", trigger: "blur" },
{
validator: (rule, value, callback) => {
if (value < 0) {
callback(new Error("实时AD最小值不能小于0"));
} else if (value > 65535) {
callback(new Error("实时AD最小值不能大于65535"));
} else {
callback();
}
},
trigger: "blur"
}
],
realTimeAdMax: [
{ required: true, message: "实时AD最大值不能为空", trigger: "blur" },
{ type: "integer", message: "实时AD最大值必须为整数", trigger: "blur" },
{
validator: (rule, value, callback) => {
if (value < 0) {
callback(new Error("实时AD最大值不能小于0"));
} else if (value > 65535) {
callback(new Error("实时AD最大值不能大于65535"));
} else if (this.form.realTimeAdMin !== null && value <= this.form.realTimeAdMin) {
callback(new Error("实时AD最大值必须大于最小值"));
} else {
callback();
}
},
trigger: "blur"
}
]
}
};
},
created() {
// 初始化时加载数据
this.fetchData();
},
methods: {
/**
* 加载AD范围数据
*/
fetchData() {
this.loading = true;
getTopOne().then(response => {
// 假设接口返回的数据结构是 { realTimeAdMin: xxx, realTimeAdMax: xxx }
this.form = { ...response.data };
}).catch(error => {
this.$message.error("加载AD范围数据失败: " + (error.message || "未知错误"));
}).finally(() => {
this.loading = false;
});
},
/**
* 保存设置
*/
handleSave() {
this.$refs["formRef"].validate(valid => {
if (valid) {
this.loading = true;
if(this.form.id) {
updateRange(this.form).then(response => {
this.$message.success("保存成功");
}).catch(error => {
this.$message.error("保存失败: " + (error.message || "未知错误"));
}).finally(() => {
this.loading = false;
});
} else {
addRange(this.form).then(response => {
this.form.id = response.data;
this.$message.success("保存成功");
}).catch(error => {
this.$message.error("保存失败: " + (error.message || "未知错误"));
}).finally(() => {
this.loading = false;
});
}
}
});
},
}
};
</script>
<style scoped>
.el-card {
border-radius: 12px;
border: 1px solid #ebeef5;
}
.el-card__header {
background-color: #f5f7fa;
border-bottom: 1px solid #ebeef5;
}
.el-form-item {
margin-bottom: 20px;
}
.w-80 {
width: 240px;
}
.mt-4 {
margin-top: 16px;
}
</style>
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="实时AD最小值" prop="realTimeAdMin">
<el-input
v-model="queryParams.realTimeAdMin"
placeholder="请输入实时AD最小值"
clearable
size="small"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="实时AD最大值" prop="realTimeAdMax">
<el-input
v-model="queryParams.realTimeAdMax"
placeholder="请输入实时AD最大值"
clearable
size="small"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAdd"
v-hasPermi="['system:range:add']"
>新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-edit"
size="mini"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['system:range:edit']"
>修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['system:range:remove']"
>删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
size="mini"
:loading="exportLoading"
@click="handleExport"
v-hasPermi="['system:range:export']"
>导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="rangeList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="主键ID" align="center" prop="id" />
<el-table-column label="实时AD最小值" align="center" prop="realTimeAdMin" />
<el-table-column label="实时AD最大值" align="center" prop="realTimeAdMax" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['system:range:edit']"
>修改</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-hasPermi="['system:range:remove']"
>删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 添加或修改实时AD范围维护对话框 -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="实时AD最小值" prop="realTimeAdMin">
<el-input v-model="form.realTimeAdMin" placeholder="请输入实时AD最小值" />
</el-form-item>
<el-form-item label="实时AD最大值" prop="realTimeAdMax">
<el-input v-model="form.realTimeAdMax" placeholder="请输入实时AD最大值" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm">确 定</el-button>
<el-button @click="cancel">取 消</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { listRange, getRange, delRange, addRange, updateRange, exportRange } from "@/api/realAdRange/realAdRange";
export default {
name: "Range",
components: {
},
data() {
return {
// 遮罩层
loading: true,
// 导出遮罩层
exportLoading: false,
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 实时AD范围维护表格数据
rangeList: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
realTimeAdMin: null,
realTimeAdMax: null
},
// 表单参数
form: {},
// 表单校验
rules: {
realTimeAdMin: [
{ required: true, message: "实时AD最小值不能为空", trigger: "blur" }
],
realTimeAdMax: [
{ required: true, message: "实时AD最大值不能为空", trigger: "blur" }
]
}
};
},
created() {
this.getList();
},
methods: {
/** 查询实时AD范围维护列表 */
getList() {
this.loading = true;
listRange(this.queryParams).then(response => {
this.rangeList = response.rows;
this.total = response.total;
this.loading = false;
});
},
// 取消按钮
cancel() {
this.open = false;
this.reset();
},
// 表单重置
reset() {
this.form = {
id: null,
realTimeAdMin: null,
realTimeAdMax: null
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
// 多选框选中数据
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id)
this.single = selection.length!==1
this.multiple = !selection.length
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加实时AD范围维护";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const id = row.id || this.ids
getRange(id).then(response => {
this.form = response.data;
this.open = true;
this.title = "修改实时AD范围维护";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
if (this.form.id != null) {
updateRange(this.form).then(response => {
this.msgSuccess("修改成功");
this.open = false;
this.getList();
});
} else {
addRange(this.form).then(response => {
this.msgSuccess("新增成功");
this.open = false;
this.getList();
});
}
}
});
},
/** 删除按钮操作 */
handleDelete(row) {
const ids = row.id || this.ids;
this.$confirm('是否确认删除实时AD范围维护编号为"' + ids + '"的数据项?', "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(function() {
return delRange(ids);
}).then(() => {
this.getList();
this.msgSuccess("删除成功");
}).catch(() => {});
},
/** 导出按钮操作 */
handleExport() {
const queryParams = this.queryParams;
this.$confirm('是否确认导出所有实时AD范围维护数据项?', "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
this.exportLoading = true;
return exportRange(queryParams);
}).then(response => {
this.download(response.msg);
this.exportLoading = false;
}).catch(() => {});
}
}
};
</script>
......@@ -63,12 +63,16 @@
<div class="device-code">{{ scope.row.deviceCode }}</div>
</template>
</el-table-column>
<el-table-column label="行" prop="row" align="center" width="80" />
<el-table-column label="列" prop="col" align="center" width="80" />
<el-table-column label="位置编号" prop="number" align="center" width="150" />
<!-- <el-table-column label="行" prop="row" align="center" width="80" />-->
<!-- <el-table-column label="列" prop="col" align="center" width="80" />-->
<el-table-column label="位置编号" prop="number" align="center" width="120" >
<template slot-scope="scope">
<div class="device-code">{{ scope.row.number }}</div>
</template>
</el-table-column>
<el-table-column label="绑定时间" align="center" width="150">
<template slot-scope="scope">
<div class="binding-time">{{ scope.row.bindingTime ? formatDate(scope.row.bindingTime) : '-' }}</div>
<div class="device-code">{{ scope.row.bindingTime ? formatDate(scope.row.bindingTime) : '-' }}</div>
</template>
</el-table-column>
<el-table-column label="状态" align="center" width="120">
......@@ -82,51 +86,71 @@
</el-tag>
</template>
</el-table-column>
<el-table-column label="浓度" prop="concentration" align="center" width="100">
<el-table-column label="写时间状态" align="center" width="150">
<template slot-scope="scope">
<div class="concentration">{{ scope.row.concentration || '-' }}</div>
<span v-if="scope.row.writeTimeStatus === '1'" class="write-success">成功</span>
<span v-if="scope.row.writeTimeStatus === '0'" class="write-failed">失败</span>
<span v-else class="write-unknown">-</span>
</template>
</el-table-column>
<el-table-column label="写时间状态" align="center" width="120">
<el-table-column label="时间运行状态" align="center" width="150">
<template slot-scope="scope">
<span v-if="scope.row.writeTimeStatus === '1'" class="write-success">成功</span>
<span v-if="scope.row.writeTimeStatus === '0'" class="write-failed">失败</span>
<span v-if="scope.row.runTimeStatus === '1'" class="write-success">成功</span>
<span v-if="scope.row.runTimeStatus === '0'" class="write-failed">失败</span>
<span v-else class="write-unknown">-</span>
</template>
</el-table-column>
<el-table-column label="零点校准AD" align="center" width="120">
<el-table-column label="浓度" prop="concentration" align="center" width="100">
<template slot-scope="scope">
<div class="adjustment-zero">{{ scope.row.adjustmentZeroAd || '-' }}</div>
<div class="device-code">{{ scope.row.concentration || '-' }}</div>
</template>
</el-table-column>
<el-table-column label="零点AD" align="center" width="150">
<template slot-scope="scope">
<div class="device-code">{{ scope.row.adjustmentZeroAd || '-' }}</div>
</template>
</el-table-column>
<el-table-column label="校准AD" align="center" width="120">
<template slot-scope="scope">
<div class="calibration-ad">{{ scope.row.calibrationAd || '-' }}</div>
<div class="device-code">{{ scope.row.calibrationAd || '-' }}</div>
</template>
</el-table-column>
<el-table-column label="实时AD" align="center" width="120">
<template slot-scope="scope">
<div class="device-code">{{ scope.row.realTimeAd || '-' }}</div>
</template>
</el-table-column>
<el-table-column label="实时AD状态" align="center" width="150">
<template slot-scope="scope">
<span v-if="scope.row.realTimeStatus === '1'" class="write-success">正常</span>
<span v-if="scope.row.realTimeStatus === '0'" class="write-failed">异常</span>
<span v-else class="write-unknown">-</span>
</template>
</el-table-column>
<el-table-column label="通信年" prop="recordYear" align="center" width="100">
<template slot-scope="scope">
<div class="record-year">{{ scope.row.recordYear || '-' }}</div>
<div class="device-code">{{ scope.row.recordYear || '-' }}</div>
</template>
</el-table-column>
<el-table-column label="通信月" prop="recordMonth" align="center" width="100">
<template slot-scope="scope">
<div class="record-month">{{ scope.row.recordMonth || '-' }}</div>
<div class="device-code">{{ scope.row.recordMonth || '-' }}</div>
</template>
</el-table-column>
<el-table-column label="通信日" prop="recordDate" align="center" width="100">
<template slot-scope="scope">
<div class="record-date">{{ scope.row.recordDate || '-' }}</div>
<div class="device-code">{{ scope.row.recordDate || '-' }}</div>
</template>
</el-table-column>
<el-table-column label="通信时" prop="recordHour" align="center" width="100">
<template slot-scope="scope">
<div class="record-hour">{{ scope.row.recordHour || '-' }}</div>
<div class="device-code">{{ scope.row.recordHour || '-' }}</div>
</template>
</el-table-column>
<el-table-column label="通信分" prop="recordMinute" align="center" width="100">
<template slot-scope="scope">
<div class="record-minute">{{ scope.row.recordMinute || '-' }}</div>
<div class="device-code">{{ scope.row.recordMinute || '-' }}</div>
</template>
</el-table-column>
</el-table>
......
......@@ -113,18 +113,18 @@
<el-table-column label="上料指令" align="center" prop="feedingCommand" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
>修改</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
>删除</el-button>
<!-- <el-button-->
<!-- size="mini"-->
<!-- type="text"-->
<!-- icon="el-icon-edit"-->
<!-- @click="handleUpdate(scope.row)"-->
<!-- >修改</el-button>-->
<!-- <el-button-->
<!-- size="mini"-->
<!-- type="text"-->
<!-- icon="el-icon-delete"-->
<!-- @click="handleDelete(scope.row)"-->
<!-- >删除</el-button>-->
<el-button
size="mini"
type="text"
......@@ -146,12 +146,18 @@
size="mini"
type="text"
@click="handleBlanking(scope.row)"
>生成下料命令</el-button>
>下料</el-button>
<el-button
size="mini"
type="text"
@click="handleDirectBlanking(scope.row)"
>直接下料</el-button>
@click="handleReAgine(scope.row)"
>重新老化</el-button>
<!-- <el-button-->
<!-- size="mini"-->
<!-- type="text"-->
<!-- @click="handleDirectBlanking(scope.row)"-->
<!-- >直接下料</el-button>-->
</template>
</el-table-column>
</el-table>
......@@ -216,7 +222,7 @@
import {
listStorey, getStorey, delStorey, addStorey, updateStorey,
exportStorey, PowerOn, PowerOutage,
feeding, blanking, directBlanking
feeding, blanking, directBlanking,reAgine
} from "@/api/storey/storey";
import {sendHomeCommand, sendStopCommand} from "@/api/robotArm/robotArmCommand";
......@@ -410,6 +416,20 @@ export default {
this.msgSuccess("下料成功");
}).catch(() => {});
},
/** 重新老化 */
handleReAgine(row) {
const fIp = row.fIp;
this.$confirm('是否确认重新老化IP为"' + fIp + '"的设备?', "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(function() {
return reAgine(row);
}).then(() => {
this.getList();
this.msgSuccess("重新老化开始");
}).catch(() => {});
},
// 下料
handleBlanking(row) {
const fIp = row.fIp;
......
<template>
<div class="app-container">
<el-card shadow="hover" style="max-width: 1000px; margin: 0 auto;">
<div slot="header" class="clearfix">
<span>上传MES地址维护</span>
</div>
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px" class="mt-4">
<el-form-item label="MES地址" prop="mesAddress">
<el-input
v-model="form.msg"
placeholder="请输入上传MES地址"
style="width:700px"
/>
</el-form-item>
<el-form-item>
<el-button
type="primary"
@click="handleSave"
:loading="loading"
>
<i class="el-icon-check"></i> 保存设置
</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</template>
<script>
import { getUploadMesAddress, updateUploadMesAddress } from "@/api/system/config";
export default {
name: "MesAddressConfig",
data() {
return {
// 加载状态
loading: false,
// 表单数据
form: {
msg: ""
},
// 表单校验规则
rules: {
msg: [
{ required: true, message: "MES地址不能为空", trigger: "blur" },
{
validator: (rule, value, callback) => {
if (value && !this.isValidUrl(value)) {
callback(new Error("请输入有效的URL地址"));
} else {
callback();
}
},
trigger: "blur"
}
]
}
};
},
created() {
// 初始化时加载数据
this.fetchData();
},
methods: {
/**
* URL格式验证
*/
isValidUrl(url) {
try {
new URL(url);
return true;
} catch (e) {
return false;
}
},
/**
* 加载MES地址数据
*/
fetchData() {
this.loading = true;
getUploadMesAddress().then(response => {
if (response.msg) {
this.form = { ...response };
}
}).catch(error => {
this.$message.error("加载MES地址失败: " + (error.message || "未知错误"));
}).finally(() => {
this.loading = false;
});
},
/**
* 保存设置
*/
handleSave() {
this.$refs["formRef"].validate(valid => {
if (valid) {
this.loading = true;
let obj = {
configValue: this.form.msg
};
updateUploadMesAddress(obj).then(response => {
this.$message.success("保存成功");
}).catch(error => {
this.$message.error("保存失败: " + (error.message || "未知错误"));
}).finally(() => {
this.loading = false;
});
}
});
},
}
};
</script>
<style scoped>
.el-card {
border-radius: 12px;
border: 1px solid #ebeef5;
}
.el-card__header {
background-color: #f5f7fa;
border-bottom: 1px solid #ebeef5;
}
.el-form-item {
margin-bottom: 20px;
}
.w-80 {
width: 240px;
}
.mt-4 {
margin-top: 16px;
}
</style>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment