feat(): 电文逻辑完善

This commit is contained in:
Allenxy
2025-09-09 21:58:20 +08:00
parent 4f0f8a1ebd
commit 74e87ce652
38 changed files with 1124 additions and 401 deletions

View File

@@ -1,15 +1,10 @@
package com.fizz.business.comm.OPC; package com.fizz.business.comm.OPC;
import com.fizz.business.constants.enums.OpcMessageType;
import com.fizz.business.domain.msg.AppMeasureMessage;
import com.kangaroohy.milo.model.ReadWriteEntity;
import com.kangaroohy.milo.service.MiloService; import com.kangaroohy.milo.service.MiloService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists; import org.apache.commons.compress.utils.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner; import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.List; import java.util.List;

View File

@@ -2,7 +2,7 @@ package com.fizz.business.constants;
import com.fizz.business.constants.enums.DeviceEnum; import com.fizz.business.constants.enums.DeviceEnum;
import static com.fizz.business.constants.enums.DeviceEnum.DISC; import static com.fizz.business.constants.enums.DeviceEnum.INS;
public class CommonConstants { public class CommonConstants {
@@ -50,7 +50,7 @@ public class CommonConstants {
public static final String SEG_STRIP_LEN = "SEG_STRIP_LEN"; public static final String SEG_STRIP_LEN = "SEG_STRIP_LEN";
//卷取机前一个设备idx //卷取机前一个设备idx
public static final DeviceEnum BEFORE_TR_IDX = DISC; public static final DeviceEnum BEFORE_TR_IDX = INS;
// 工艺规程点位代码 // 工艺规程点位代码
public static final String SETUP_POINT_CODE = "SETUP_POINT_CODE"; public static final String SETUP_POINT_CODE = "SETUP_POINT_CODE";

View File

@@ -5,57 +5,52 @@ import com.fasterxml.jackson.annotation.JsonValue;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import java.util.HashMap; import java.util.*;
import java.util.Map;
@Getter @Getter
@AllArgsConstructor @AllArgsConstructor
/**
* 细粒度设备枚举按你给出的17台设备
* position 为基准位置单位m用于和 stripLocation 比较。
* paramFields 列出该设备需要采样/缓存的字段名(与 AppMeasure*Message 中字段名对应)。
*/
public enum DeviceEnum { public enum DeviceEnum {
POR1(0, "1#开卷机"), POR1(0, "1#开卷机", 0.0, Arrays.asList("tensionPorBr1", "stripSpeed")),
POR2(1, "2#开卷机"), POR2(1, "2#开卷机", 1.0, Arrays.asList("tensionPorBr2", "stripSpeed")),
WELDER(2, "焊机"), WELDER(2, "焊机", 5.0, Arrays.asList("weldStatus")),
BR1(3, "1#张力辊"), ENL1(3, "入口活套1", 10.0, Arrays.asList("celLength", "celCapacity", "tensionCel")),
CEL(4, "入口活套"), ENL2(4, "入口活套2", 12.0, Arrays.asList("celLength", "celCapacity", "tensionCel")),
BR2(5, "2#张力辊"), CLEAN(5, "清洗段", 20.0, Arrays.asList("cleaningVoltage", "cleaningCurrent", "alkaliConcentration", "alkaliTemperature")),
ALKALI(6, "碱洗"), FUR1(6, "退火炉-预热段", 30.0, Arrays.asList("phfExitStripTemp","potTemperature","gasConsumption")),
DRYING(7, "热烘干"), FUR2(7, "退火炉-加热段", 40.0, Arrays.asList("rtfExitStripTemp","zincPotPower")),
BR4(8, "4#张力辊"), FUR3(8, "退火炉-冷却段", 50.0, Arrays.asList("jcsExitStripTemp","coolingTowerStripTemp")),
RTF(9, "加热炉"), FUR4(9, "退火炉-均衡段", 60.0, Arrays.asList("scsExitStripTemp")),
SF1(10, "均热炉1"), TM(10, "光整机", 70.0, Arrays.asList("tensionBr5Tm", "stripSpeedTmExit")),
EHF(11, "EHF"), TL(11, "拉矫机", 80.0, Arrays.asList("tlElongation", "tensionTlBr7")),
SF2(12, "均热炉2"), COAT(12, "后处理段", 90.0, Arrays.asList(
CTF(13, "CTF"), "avrCoatingWeightTop","stdCoatingWeightTop","maxCoatingWeightTop","minCoatingWeightTop",
SF3(14, "均热炉3"), "avrCoatingWeightBottom","stdCoatingWeightBottom","maxCoatingWeightBottom","minCoatingWeightBottom",
RJC(15, "控冷段"), "airKnifePressure","airKnifeFlow","airKnifeGap","stripSpeedTmExit","tensionBr5Tm",
AJC(16, "快冷段"), "tensionTmBr6","tensionBr5Br6","tmMask","tmElongation","rollForceOperator","rollForceDrive",
BR5(17, "5#张力辊"), "motorTorque","bendingForce","antiCrimpingRollMesh","billyRollMesh",
BR6(18, "6#张力辊"), "tensionTlBr7","tensionBr6Br7","tlFlag","tlElongation","levelingUnit1Mesh","levelingUnit2Mesh",
CXL(19, "出口活套"), "antiCrossBowUnitMesh","tensionBr7Br8","stripSpeedAfp","stripTempAfp"
BR7(20, "7#张力辊"), )),
BR8(21, "8#张力辊"), CXL1(13, "出口活套1", 100.0, Arrays.asList("cxlLength", "cxlCapacity", "tensionCxl")),
COAT(22, "涂机"), CXL2(14, "出口活套2", 102.0, Arrays.asList("cxlLength", "cxlCapacity", "tensionCxl")),
DISC(23, "圆盘剪"), INS(15, "检查站", 110.0, Arrays.asList("inspectionStatus")),
TR1(24, "1#卷取机"), TR(16, "卷取机", 120.0, Arrays.asList("coilLength", "speedExitSection", "tensionBr9Tr")),
TR2(25, "2#卷取机"); EXC(17, "卸卷小车", 999999.0, Collections.emptyList()),
WEIGHT(18, "称重鞍座", 999999.0, Collections.emptyList());
private final int idx;
private final Integer idx;
private final String desc; private final String desc;
private static final Map<String, DeviceEnum> MAP = new HashMap<>(); private final double basePosition;
private final List<String> paramFields;
private static final Map<String, DeviceEnum> NAME_MAP = new HashMap<>();
static { static {
for (DeviceEnum e : DeviceEnum.values()) { for (DeviceEnum d : values()) NAME_MAP.put(d.name(), d);
MAP.put(e.getValue(), e);
}
}
@JsonCreator(mode = JsonCreator.Mode.DELEGATING)
public static DeviceEnum getByValue(String value) {
return MAP.get(value);
}
@JsonValue
public String getValue() {
return this.name();
} }
public static DeviceEnum fromName(String name) { return NAME_MAP.get(name); }
} }

View File

@@ -57,8 +57,7 @@ public enum L1OperateMatEnum implements IEnum<String>, IOperateMat<L1OperateMatF
@Override @Override
public void operate(L1OperateMatForm form) { public void operate(L1OperateMatForm form) {
PRODUCING.syncPlanStatus(form.getPlanId()); PRODUCING.syncPlanStatus(form.getPlanId());
// 钢卷上线时, 缓存工艺规程
// BeanFactory.getBean(RedisCacheManager.class).setCoilSetup(form.getPlanId());
WebSocketUtil.sendSignalMsg(form); WebSocketUtil.sendSignalMsg(form);
} }
}, },
@@ -66,8 +65,6 @@ public enum L1OperateMatEnum implements IEnum<String>, IOperateMat<L1OperateMatF
@Override @Override
public void operate(L1OperateMatForm form) { public void operate(L1OperateMatForm form) {
PRODUCT.syncPlanStatus(form.getPlanId()); PRODUCT.syncPlanStatus(form.getPlanId());
// 删除工艺规程
BeanFactory.getBean(RedisCacheManager.class).delCoilSetup(form.getEntryMatId());
WebSocketUtil.sendSignalMsg(form); WebSocketUtil.sendSignalMsg(form);
} }
}, },
@@ -78,7 +75,7 @@ public enum L1OperateMatEnum implements IEnum<String>, IOperateMat<L1OperateMatF
CrmPdiPlanService planClient = BeanFactory.getBean(CrmPdiPlanService.class); CrmPdiPlanService planClient = BeanFactory.getBean(CrmPdiPlanService.class);
CrmPdiPlanVO plan = planClient.getByCoilIdAndOperId(form.getEntryMatId()); CrmPdiPlanVO plan = planClient.getByCoilIdAndOperId(form.getEntryMatId());
Assert.notNull(plan, "计划[{}]不存在", plan.getId()); Assert.notNull(plan, "计划[{}]不存在", plan.getId());
Assert.isTrue(status.contains(plan.getStatus()), "当前状态[{}]不支持甩尾", plan.getStatus());
MatmapUtil.clearMatmap(form.getPorIdx()); MatmapUtil.clearMatmap(form.getPorIdx());
WebSocketUtil.sendSignalMsg(form); WebSocketUtil.sendSignalMsg(form);
WebSocketUtil.sendMatmapMsg(); WebSocketUtil.sendMatmapMsg();

View File

@@ -1,20 +1,15 @@
package com.fizz.business.constants.enums; package com.fizz.business.constants.enums;
import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.IEnum; import com.baomidou.mybatisplus.annotation.IEnum;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue; import com.fasterxml.jackson.annotation.JsonValue;
import com.fizz.business.domain.HalfReturn; import com.fizz.business.domain.HalfReturn;
import com.fizz.business.domain.Segment;
import com.fizz.business.dto.CoilHeadDTO;
import com.fizz.business.dto.ExitCoilInfoDTO; import com.fizz.business.dto.ExitCoilInfoDTO;
import com.fizz.business.dto.MatmapDTO; import com.fizz.business.dto.MatmapDTO;
import com.fizz.business.form.ChangePlanStatusForm; import com.fizz.business.form.ChangePlanStatusForm;
import com.fizz.business.form.WebOperateMatForm; import com.fizz.business.form.WebOperateMatForm;
import com.fizz.business.mapper.HalfReturnMapper; import com.fizz.business.mapper.HalfReturnMapper;
import com.fizz.business.mapper.SegmentMapper;
import com.fizz.business.service.CrmPdiPlanService; import com.fizz.business.service.CrmPdiPlanService;
import com.fizz.business.service.PdoExCoilService; import com.fizz.business.service.PdoExCoilService;
import com.fizz.business.service.TrackService; import com.fizz.business.service.TrackService;
@@ -24,14 +19,16 @@ import com.fizz.business.utils.ErrorDataException;
import com.fizz.business.utils.MatmapUtil; import com.fizz.business.utils.MatmapUtil;
import com.fizz.business.utils.WebSocketUtil; import com.fizz.business.utils.WebSocketUtil;
import com.fizz.business.vo.CrmPdiPlanVO; import com.fizz.business.vo.CrmPdiPlanVO;
import com.fizz.business.vo.PdiPlanVO;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.*; import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/** /**
* 钢卷操作 * 钢卷操作
@@ -53,7 +50,6 @@ public enum WebOperateMatEnum implements IEnum<String>, IOperateMat<WebOperateMa
if (MatmapUtil.already(porCoil)) { if (MatmapUtil.already(porCoil)) {
ONLINE.syncPlanStatus(form.getPlanId()); ONLINE.syncPlanStatus(form.getPlanId());
ONLINE.replaceVirtualPlan(form, porCoil);
WebSocketUtil.sendSignalMsg(form, true); WebSocketUtil.sendSignalMsg(form, true);
// 如果开卷机和焊机的钢卷相同,则将计划状态变更为生产中 // 如果开卷机和焊机的钢卷相同,则将计划状态变更为生产中
MatmapDTO welderCoil = MatmapUtil.getMatmap(DeviceEnum.WELDER.getIdx()); MatmapDTO welderCoil = MatmapUtil.getMatmap(DeviceEnum.WELDER.getIdx());
@@ -250,52 +246,4 @@ public enum WebOperateMatEnum implements IEnum<String>, IOperateMat<WebOperateMa
WebSocketUtil.sendMatmapMsg(); WebSocketUtil.sendMatmapMsg();
} }
private void replaceVirtualPlan(WebOperateMatForm form, MatmapDTO porCoil) {
// 更新matmap
List<MatmapDTO> matmapList = MatmapUtil.getMatmapList();
matmapList.forEach(matmap -> {
if (Objects.equals(matmap.getMatId(), porCoil.getMatId())) {
MatmapUtil.setMatmap(matmap.getPosIdx(), form.getEntryMatId(), form.getPlanId(), form.getPlanNo());
}
});
RedisCacheManager redisCacheManager = BeanFactory.getBean(RedisCacheManager.class);
// 更新headList
List<CoilHeadDTO> headList = redisCacheManager.getHeadList();
headList.forEach(head -> {
if (Objects.equals(head.getMatId(), porCoil.getMatId())) {
head.setMatId(form.getEntryMatId());
head.setPlanNo(form.getPlanNo());
head.setPlanId(form.getPlanId());
head.setPorIdx(form.getPorIdx());
}
});
redisCacheManager.setHeadList(headList);
// 更新prevHead
CoilHeadDTO prevHead = redisCacheManager.getPrevHead();
if (Objects.nonNull(prevHead) && Objects.equals(prevHead.getMatId(), porCoil.getMatId())) {
prevHead.setMatId(form.getEntryMatId());
prevHead.setPlanNo(form.getPlanNo());
prevHead.setPlanId(form.getPlanId());
prevHead.setPorIdx(form.getPorIdx());
redisCacheManager.setPrevHead(prevHead);
}
// 更新mergeHead
CoilHeadDTO mergeHead = redisCacheManager.getMergeHead();
if (Objects.nonNull(mergeHead) && Objects.equals(mergeHead.getMatId(), porCoil.getMatId())) {
mergeHead.setMatId(form.getEntryMatId());
mergeHead.setPlanNo(form.getPlanNo());
mergeHead.setPlanId(form.getPlanId());
prevHead.setPorIdx(form.getPorIdx());
redisCacheManager.setMergeHead(mergeHead);
}
// 更新segment
SegmentMapper segmentMapper = BeanFactory.getBean(SegmentMapper.class);
LambdaUpdateWrapper<Segment> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(Segment::getEntryMatId, porCoil.getMatId());
updateWrapper.eq(Segment::getPlanId, porCoil.getPlanId());
updateWrapper.set(Segment::getEntryMatId, form.getEntryMatId());
updateWrapper.set(Segment::getPlanId, form.getPlanId());
segmentMapper.update(new Segment(), updateWrapper);
}
} }

View File

@@ -21,10 +21,10 @@ public class CrmPdoExcoilController {
@Resource @Resource
private CrmPdoExcoilService crmPdoExcoilService; private CrmPdoExcoilService crmPdoExcoilService;
@GetMapping("/get/{excoilid}/{operid}") @GetMapping("/get/{excoilid}")
@Operation(summary ="查询实绩") @Operation(summary ="查询实绩")
public R<CrmPdoExcoil> getByExcoilIdAndOperId(@PathVariable String excoilid, @PathVariable Integer operid) { public R<CrmPdoExcoil> getByExcoilId(@PathVariable String excoilid) {
return R.ok(crmPdoExcoilService.getByExcoilIdAndOperId(excoilid, operid)); return R.ok(crmPdoExcoilService.getByExcoilId(excoilid));
} }
@PostMapping("/add") @PostMapping("/add")

View File

@@ -0,0 +1,52 @@
package com.fizz.business.controller;
import com.fizz.business.service.CrmPdoExcoilService;
import com.fizz.business.vo.ReportDetailVO;
import com.fizz.business.vo.ReportSummaryVO;
import com.ruoyi.common.annotation.Anonymous;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
@RestController
@RequestMapping("/api/report")
@Tag(name= "报表接口",description = "报表接口")
@Anonymous
public class ReportController {
@Resource
private CrmPdoExcoilService crmPdoExcoilService;
@GetMapping("/summary")
@Operation(summary = "生产实绩报表-汇总信息查询")
public ReportSummaryVO getReportSummary(
@RequestParam(required = false) String groupNo,
@RequestParam(required = false) String shiftNo,
@RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime startTime,
@RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime endTime
) {
return crmPdoExcoilService.getReportSummary(groupNo, shiftNo, startTime, endTime);
}
@GetMapping("/details")
@Operation(summary = "生产实绩报表-产出明细查询")
public List<ReportDetailVO> getReportDetails(
@RequestParam(required = false) String groupNo,
@RequestParam(required = false) String shiftNo,
@RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime startTime,
@RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime endTime
) {
return crmPdoExcoilService.getReportDetails(groupNo, shiftNo, startTime, endTime);
}
}

View File

@@ -21,7 +21,7 @@ public class AppMeasureEntryMessage extends OpcMessage {
@Schema(description = "钢带位置 (m)0 表示头部在参考点 (Welder)") @Schema(description = "钢带位置 (m)0 表示头部在参考点 (Welder)")
private BigDecimal stripLocation; private BigDecimal stripLocation;
@Schema(description = "支付卷1 或 2") @Schema(description = "开卷机1 或 2")
private Integer payOffReelNumber; private Integer payOffReelNumber;
@Schema(description = "钢带张力 POR BR1 (daN)") @Schema(description = "钢带张力 POR BR1 (daN)")

View File

@@ -21,13 +21,13 @@ public class AppMeasureExitMessage extends OpcMessage {
@Schema(description = "钢带张力 BR8 BR9 (daN)") @Schema(description = "钢带张力 BR8 BR9 (daN)")
private BigDecimal tensionBr8Br9; private BigDecimal tensionBr8Br9;
@Schema(description = "口活套位置(m)") @Schema(description = "口活套位置(m)")
private BigDecimal cxlLength; private BigDecimal cxlLength;
@Schema(description = "口活套百分比(m)") @Schema(description = "口活套百分比(m)")
private BigDecimal cxlCapacity; private BigDecimal cxlCapacity;
@Schema(description = "口活套张力(m)") @Schema(description = "口活套张力(m)")
private BigDecimal tensionCxl ; private BigDecimal tensionCxl ;
@Schema(description = "涂油标志 (0=no, 1=yes)") @Schema(description = "涂油标志 (0=no, 1=yes)")

View File

@@ -1,5 +1,11 @@
package com.fizz.business.domain.msg; package com.fizz.business.domain.msg;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class AppMeasureMessage extends OpcMessage { public class AppMeasureMessage extends OpcMessage {
AppMeasureEntryMessage appMeasureEntryMessage; AppMeasureEntryMessage appMeasureEntryMessage;

View File

@@ -1,6 +1,7 @@
package com.fizz.business.domain.msg; package com.fizz.business.domain.msg;
import com.fizz.business.constants.enums.ExitCutTypeEnum;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
@@ -16,7 +17,7 @@ public class ExitCutMessage extends OpcMessage {
private Integer counter; private Integer counter;
@Schema(description = "剪切类型0=split, 1=weldSeam, 2=stripBreak") @Schema(description = "剪切类型0=split, 1=weldSeam, 2=stripBreak")
private Integer cutType; private ExitCutTypeEnum cutType;
@Schema(description = "剪切时卷取长度 (m)") @Schema(description = "剪切时卷取长度 (m)")
private BigDecimal cutLength; private BigDecimal cutLength;

View File

@@ -14,8 +14,8 @@ public class ExitMovementMessage extends OpcMessage {
private Integer counter; private Integer counter;
@Schema(description = "Material Place Source") @Schema(description = "Material Place Source")
private Integer materialPlaceSource; private Integer exSrc;
@Schema(description = "Material Place Destination") @Schema(description = "Material Place Destination")
private Integer materialPlaceDestination; private Integer exDesc;
} }

View File

@@ -15,6 +15,7 @@ public class ExitCoilInfoDTO implements Serializable {
private double startPos; private double startPos;
private double endPos; private double endPos;
private double exitCutLength; private double exitCutLength;
private double outerDiameter;
private String entryMatId; private String entryMatId;
private String exitMatId; private String exitMatId;
private boolean separateFlag; private boolean separateFlag;

View File

@@ -1,17 +1,31 @@
package com.fizz.business.dto; package com.fizz.business.dto;
import lombok.Data;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import java.io.Serializable; import java.io.Serializable;
import java.math.BigDecimal;
@Getter @Data
@Setter
public class SegValue implements Serializable { public class SegValue implements Serializable {
private double max; private BigDecimal max;
private double min; private BigDecimal min;
private double avg; private BigDecimal avg;
private double std; private BigDecimal std;
private int cnt; private int cnt;
private double sum; private BigDecimal sum;
public void add(BigDecimal value) {
this.sum = this.sum.add(value);
this.cnt++;
}
public BigDecimal getAverage() {
if (cnt == 0) {
return BigDecimal.ZERO;
}
return sum.divide(new BigDecimal(cnt), 2, BigDecimal.ROUND_HALF_UP);
}
} }

View File

@@ -1,26 +1,57 @@
package com.fizz.business.dto; package com.fizz.business.dto;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data; import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Data @Data
public class SegmentDTO { @TableName("cpl_segment_total")
public class SegmentDTO implements Serializable {
private boolean first; @TableId(type = IdType.AUTO)
private Long planId; private Long id;
private String planNo;
private String coilID; @TableField("en_coil_id")
private String enCoilID; private String enCoilID;
private String exCoilID;
//MATID_Type matId; @TableField("pickling_count")
private int segNo; private Integer picklingCount;
private double segLen;
private double startPos;//位于带钢的起始位置 @TableField("seg_no")
private double endPos; private Integer segNo;
private double headPos;//位于全线的位置
private double tailPos; // 以下字段用于业务逻辑,不存入数据库
@TableField(exist = false)
private BigDecimal headPos;
@TableField(exist = false)
private BigDecimal tailPos;
// 以下字段存入数据库
@TableField("start_pos")
private BigDecimal startPos;
@TableField("end_pos")
private BigDecimal endPos;
@TableField("seg_len")
private BigDecimal segLen;
@TableField("total_values_json")
private String totalValuesJson;
// 以下 Map 用于业务逻辑,不存入数据库
@TableField(exist = false)
private Map<String, SegValue> totalValues = new ConcurrentHashMap<>();
@TableField(exist = false)
private Map<String, SegValue> L3values = new ConcurrentHashMap<>();
// 两个未使用的字段,可以根据需要保留或移除
@TableField(exist = false)
private int headPosNum; private int headPosNum;
@TableField(exist = false)
private int tailPosNum; private int tailPosNum;
private Map<String, SegValue> segValMap;
} }

View File

@@ -2,8 +2,30 @@ package com.fizz.business.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.fizz.business.domain.CrmPdoExcoil; import com.fizz.business.domain.CrmPdoExcoil;
import com.fizz.business.vo.ReportDetailVO;
import com.fizz.business.vo.ReportSummaryVO;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.time.LocalDateTime;
import java.util.List;
@Mapper @Mapper
public interface CrmPdoExcoilMapper extends BaseMapper<CrmPdoExcoil> { public interface CrmPdoExcoilMapper extends BaseMapper<CrmPdoExcoil> {
ReportSummaryVO getReportSummary(
@Param("groupNo") String groupNo,
@Param("shiftNo") String shiftNo,
@Param("startTime") LocalDateTime startTime,
@Param("endTime") LocalDateTime endTime
);
List<ReportDetailVO> getReportDetails(
@Param("groupNo") String groupNo,
@Param("shiftNo") String shiftNo,
@Param("startTime") LocalDateTime startTime,
@Param("endTime") LocalDateTime endTime
);
} }

View File

@@ -3,6 +3,7 @@ package com.fizz.business.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.fizz.business.domain.PdoStripvalue; import com.fizz.business.domain.PdoStripvalue;
import com.fizz.business.domain.Segment; import com.fizz.business.domain.Segment;
import com.fizz.business.dto.SegmentDTO;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
@@ -15,7 +16,7 @@ import org.apache.ibatis.annotations.Param;
* @since 2023-05-17 * @since 2023-05-17
*/ */
@Mapper @Mapper
public interface SegmentMapper extends BaseMapper<Segment> { public interface SegmentMapper extends BaseMapper<SegmentDTO> {
PdoStripvalue getStripValue(@Param("entryMatId") String entryMatId, @Param("startPos") double startPos, @Param("endPos") double endPos); PdoStripvalue getStripValue(@Param("entryMatId") String entryMatId, @Param("startPos") double startPos, @Param("endPos") double endPos);

View File

@@ -5,11 +5,15 @@ import com.baomidou.mybatisplus.extension.service.IService;
import com.fizz.business.domain.CrmPdoExcoil; import com.fizz.business.domain.CrmPdoExcoil;
import com.fizz.business.domain.PdoExcoil; import com.fizz.business.domain.PdoExcoil;
import com.fizz.business.form.CrmPdoExcoilForm; import com.fizz.business.form.CrmPdoExcoilForm;
import com.fizz.business.vo.ReportDetailVO;
import com.fizz.business.vo.ReportSummaryVO;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List; import java.util.List;
public interface CrmPdoExcoilService extends IService<CrmPdoExcoil> { public interface CrmPdoExcoilService extends IService<CrmPdoExcoil> {
CrmPdoExcoil getByExcoilIdAndOperId(String excoilid, Integer operid); CrmPdoExcoil getByExcoilId(String excoilid);
boolean addCrmPdoExcoil(CrmPdoExcoil crmPdoExcoil); boolean addCrmPdoExcoil(CrmPdoExcoil crmPdoExcoil);
@@ -20,5 +24,11 @@ public interface CrmPdoExcoilService extends IService<CrmPdoExcoil> {
List<CrmPdoExcoil> listAll(CrmPdoExcoilForm form); List<CrmPdoExcoil> listAll(CrmPdoExcoilForm form);
Long getNumber(String matId,String planId,Integer subNumber); Long getNumber(String matId,String planId,Integer subNumber);
ReportSummaryVO getReportSummary(String groupNo, String shiftNo, LocalDateTime startTime, LocalDateTime endTime);
List<ReportDetailVO> getReportDetails(String groupNo, String shiftNo, LocalDateTime startTime, LocalDateTime endTime);
void updateExitCoilActualWeight(String exitMatId, BigDecimal weight);
} }

View File

@@ -1,57 +0,0 @@
package com.fizz.business.service.client;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fizz.business.constants.enums.LimitItemCode;
import com.fizz.business.domain.DeviceDefine;
import com.fizz.business.dto.PointConfigDTO;
import com.fizz.business.mapper.DeviceDefineMapper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 本地缓存管理
*
* @author chenhao
* @date 2023/05/15
*/
@Component
public class LocalCacheManager {
private static DeviceDefineMapper deviceDefineMapper;
@Autowired
public void setDeviceDefineMapper(DeviceDefineMapper deviceDefineMapper) {
LocalCacheManager.deviceDefineMapper = deviceDefineMapper;
}
public static LinkedHashMap<String, DeviceDefine> DEVICE_DEFINE_MAP = Maps.newLinkedHashMap();
@PostConstruct
public static void init() {
DEVICE_DEFINE_MAP = getDeviceDefine();
}
public static LinkedHashMap<String, DeviceDefine> getDeviceDefine() {
List<DeviceDefine> list = deviceDefineMapper.selectList(new LambdaQueryWrapper<DeviceDefine>().orderByAsc(DeviceDefine::getPosIdx));
return list.stream().collect(Collectors.toMap(DeviceDefine::getPositionNameEn, it -> it, (k1, k2) -> k1, LinkedHashMap::new));
}
}

View File

@@ -116,14 +116,14 @@ public class RedisCacheManager {
if (segmentList.isEmpty()) { if (segmentList.isEmpty()) {
segmentRedisTemplate.opsForList().rightPush(COIL_SEG_LIST_KEY, segment); segmentRedisTemplate.opsForList().rightPush(COIL_SEG_LIST_KEY, segment);
log.info("create new segment, coilId=[{}], segNo={}, startPos={}, endPos={}, segLen={}", log.info("create new segment, coilId=[{}], segNo={}, startPos={}, endPos={}, segLen={}",
segment.getCoilID(), segment.getSegNo(), segment.getStartPos(), segment.getEndPos(), segment.getSegLen()); segment.getEnCoilID(), segment.getSegNo(), segment.getStartPos(), segment.getEndPos(), segment.getSegLen());
} else { } else {
Map<String, String> exists = segmentList.stream().collect(Collectors.toMap(s -> s.getCoilID() + "-" + s.getSegNo() + "-" + s.getPlanId(), s -> s.getCoilID() + s.getSegNo() + s.getPlanId(), (k1, k2) -> k1)); Map<String, String> exists = segmentList.stream().collect(Collectors.toMap(s -> s.getEnCoilID() + "-" + s.getSegNo(), s -> s.getEnCoilID() + s.getSegNo() , (k1, k2) -> k1));
String key = segment.getCoilID() + "-" + segment.getSegNo() + "-" + segment.getPlanId(); String key = segment.getEnCoilID() + "-" + segment.getSegNo() ;
if (!exists.containsKey(key)) { if (!exists.containsKey(key)) {
segmentRedisTemplate.opsForList().rightPush(COIL_SEG_LIST_KEY, segment); segmentRedisTemplate.opsForList().rightPush(COIL_SEG_LIST_KEY, segment);
log.info("create new segment, coilId=[{}], segNo={}, startPos={}, endPos={}, segLen={}", log.info("create new segment, coilId=[{}], segNo={}, startPos={}, endPos={}, segLen={}",
segment.getCoilID(), segment.getSegNo(), segment.getStartPos(), segment.getEndPos(), segment.getSegLen()); segment.getEnCoilID(), segment.getSegNo(), segment.getStartPos(), segment.getEndPos(), segment.getSegLen());
} }
} }
} }

View File

@@ -1,19 +1,45 @@
package com.fizz.business.service.hanle; package com.fizz.business.service.hanle;
import com.fizz.business.anno.OpcMessageHandlerType; import com.fizz.business.anno.OpcMessageHandlerType;
import com.fizz.business.constants.enums.DeviceEnum;
import com.fizz.business.constants.enums.OpcMessageType; import com.fizz.business.constants.enums.OpcMessageType;
import com.fizz.business.domain.msg.AppMeasureExitMessage; import com.fizz.business.domain.msg.*;
import com.fizz.business.domain.msg.AppMeasureMessage;
import com.fizz.business.service.OpcMessageHandler; import com.fizz.business.service.OpcMessageHandler;
import com.fizz.business.service.strip.SegmentTrackerService;
import com.fizz.business.service.strip.StripPositionService;
import com.fizz.business.utils.WebSocketUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.math.BigDecimal;
@Component @Component
@OpcMessageHandlerType(OpcMessageType.APP_MEASURE) @OpcMessageHandlerType(OpcMessageType.APP_MEASURE)
@RequiredArgsConstructor
public class AppMeasureHandler implements OpcMessageHandler<AppMeasureMessage> { public class AppMeasureHandler implements OpcMessageHandler<AppMeasureMessage> {
private final SegmentTrackerService tracker;
@Override @Override
public void handle(AppMeasureMessage message) { public void handle(AppMeasureMessage message) {
// 处理出口段测量逻辑 if (message == null) return;
System.out.println("处理 APP_MEASURE_EXIT 消息: " + message);
AppMeasureEntryMessage entry = message.getAppMeasureEntryMessage();
AppMeasureFurnaceMessage furnace = message.getAppMeasureFurnaceMessage();
AppMeasureCoatMessage coat = message.getAppMeasureCoatMessage();
AppMeasureExitMessage exit = message.getAppMeasureExitMessage();
WebSocketUtil.sendMeasureMsg(message);
// 2) extract key fields
String coilId = entry == null ? "UNKNOWN" : entry.getEntryCoilId();
BigDecimal lengthAtWelder = entry == null || entry.getStripLocation() == null ? BigDecimal.ZERO : entry.getStripLocation();
// 3) delegate core processing to tracker
tracker.handleMeasure(coilId, lengthAtWelder, entry, furnace, coat, exit);
// 调用新的方法来跟踪钢卷头部位置,并处理快照和 matmap 更新
// 使用焊机长度作为最接近的钢卷头部位置
tracker.trackCoilHeadPosition(coilId, lengthAtWelder, entry, exit);
} }
} }

View File

@@ -5,10 +5,12 @@ import com.fizz.business.constants.enums.L1OperateMatEnum;
import com.fizz.business.constants.enums.OpcMessageType; import com.fizz.business.constants.enums.OpcMessageType;
import com.fizz.business.domain.CrmPdiPlan; import com.fizz.business.domain.CrmPdiPlan;
import com.fizz.business.domain.msg.EntryMovementMessage; import com.fizz.business.domain.msg.EntryMovementMessage;
import com.fizz.business.dto.MatmapDTO;
import com.fizz.business.form.L1OperateMatForm; import com.fizz.business.form.L1OperateMatForm;
import com.fizz.business.service.CrmPdiPlanService; import com.fizz.business.service.CrmPdiPlanService;
import com.fizz.business.service.OpcMessageHandler; import com.fizz.business.service.OpcMessageHandler;
import com.fizz.business.service.TrackService; import com.fizz.business.service.TrackService;
import com.fizz.business.utils.MatmapUtil;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -52,6 +54,17 @@ public class EntryMovementHandler implements OpcMessageHandler<EntryMovementMess
} else { } else {
log.error("未找到可上卷的钢卷计划!"); log.error("未找到可上卷的钢卷计划!");
} }
}else if (action.contains("甩尾")){
MatmapDTO matmap = MatmapUtil.getMatmap(src);
log.info("获取到甩尾信息: " + matmap.getMatId());
trackService.l1OperateMat(L1OperateMatForm.builder()
.entryMatId(matmap.getMatId())
.operation(L1OperateMatEnum.PAY_OVER)
.planId(matmap.getPlanId())
.porIdx(src)
.build());
} }
} else { } else {
log.error("未识别的组合: SRC=" + src + ", DES=" + des); log.error("未识别的组合: SRC=" + src + ", DES=" + des);

View File

@@ -1,18 +1,61 @@
package com.fizz.business.service.hanle; package com.fizz.business.service.hanle;
import com.fizz.business.anno.OpcMessageHandlerType; import com.fizz.business.anno.OpcMessageHandlerType;
import com.fizz.business.constants.enums.L1OperateMatEnum;
import com.fizz.business.constants.enums.OpcMessageType; import com.fizz.business.constants.enums.OpcMessageType;
import com.fizz.business.domain.msg.ExitCutMessage; import com.fizz.business.domain.msg.ExitCutMessage;
import com.fizz.business.dto.ExitCoilInfoDTO;
import com.fizz.business.dto.MatmapDTO;
import com.fizz.business.form.L1OperateMatForm;
import com.fizz.business.service.OpcMessageHandler; import com.fizz.business.service.OpcMessageHandler;
import com.fizz.business.service.PdoExCoilService;
import com.fizz.business.service.TrackService;
import com.fizz.business.utils.MatmapUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import static com.fizz.business.constants.enums.DeviceEnum.TR;
@Component @Component
@Slf4j
@RequiredArgsConstructor
@OpcMessageHandlerType(OpcMessageType.EXIT_CUT) @OpcMessageHandlerType(OpcMessageType.EXIT_CUT)
public class ExitCutHandler implements OpcMessageHandler<ExitCutMessage> { public class ExitCutHandler implements OpcMessageHandler<ExitCutMessage> {
// 注入 PdoExCoilService
private final PdoExCoilService pdoExCoilService;
private final TrackService trackService;
@Override @Override
public void handle(ExitCutMessage message) { public void handle(ExitCutMessage message) {
// 出口剪切逻辑处理 // 1. 获取卷取机 (TR) 上的物料信息
System.out.println("处理 EXIT_CUT 消息: " + message); MatmapDTO trMatmap = MatmapUtil.getMatmap(TR.getIdx());
if (trMatmap == null) {
log.error("卷取机s.");
return;
}
// 2. 根据剪切类型,生成产出卷信息
ExitCoilInfoDTO exitCoilInfo = pdoExCoilService.genExitCoilInfo(trMatmap, message.getCutType());
// 3. 补充 ExitCutMessage 中的长度和外径信息
exitCoilInfo.setExitCutLength(message.getCutLength().doubleValue());
exitCoilInfo.setOuterDiameter(message.getOuterDiameter().doubleValue());
// 4. 调用服务进行产出卷的持久化
pdoExCoilService.saveExCoil(exitCoilInfo);
if (exitCoilInfo.isLastFlag()) {
trackService.l1OperateMat(L1OperateMatForm.builder()
.trIdx(TR.getIdx())
.entryMatId(trMatmap.getMatId())
.planId(trMatmap.getPlanId())
.operation(L1OperateMatEnum.PRODUCT)
.build());
}
log.info("成功处理 EXIT_CUT 消息,已保存成品卷: " + exitCoilInfo.getExitMatId());
} }
} }

View File

@@ -3,17 +3,34 @@ package com.fizz.business.service.hanle;
import com.fizz.business.anno.OpcMessageHandlerType; import com.fizz.business.anno.OpcMessageHandlerType;
import com.fizz.business.constants.enums.OpcMessageType; import com.fizz.business.constants.enums.OpcMessageType;
import com.fizz.business.domain.msg.ExitMeasureMessage; import com.fizz.business.domain.msg.ExitMeasureMessage;
import com.fizz.business.service.CrmPdoExcoilService;
import com.fizz.business.service.OpcMessageHandler; import com.fizz.business.service.OpcMessageHandler;
import com.fizz.business.utils.MatmapUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import static com.fizz.business.constants.enums.DeviceEnum.TR;
@Component @Component
@OpcMessageHandlerType(OpcMessageType.EXIT_MEASURE) @OpcMessageHandlerType(OpcMessageType.EXIT_MEASURE)
@RequiredArgsConstructor
public class ExitMeasureHandler implements OpcMessageHandler<ExitMeasureMessage> { public class ExitMeasureHandler implements OpcMessageHandler<ExitMeasureMessage> {
private final CrmPdoExcoilService crmPdoExcoilService;
@Override @Override
public void handle(ExitMeasureMessage message) { public void handle(ExitMeasureMessage message) {
// 出口称重逻辑处理
System.out.println("处理 EXIT_MEASURE 消息: " + message); System.out.println("处理 EXIT_MEASURE 消息: " + message);
// 1. 获取卷取机 (TR) 上的成品卷号
String exitMatId = MatmapUtil.getMatId(TR.getIdx());
if (exitMatId == null) {
System.err.println("TR device has no coil. Cannot process ExitMeasureMessage.");
return;
}
// 2. 调用服务层更新成品卷的实际重量
crmPdoExcoilService.updateExitCoilActualWeight(exitMatId, message.getWeight());
} }
} }

View File

@@ -1,18 +1,46 @@
package com.fizz.business.service.hanle; package com.fizz.business.service.hanle;
import cn.hutool.core.util.ObjectUtil;
import com.fizz.business.anno.OpcMessageHandlerType; import com.fizz.business.anno.OpcMessageHandlerType;
import com.fizz.business.constants.enums.OpcMessageType; import com.fizz.business.constants.enums.OpcMessageType;
import com.fizz.business.domain.msg.ExitMovementMessage; import com.fizz.business.domain.msg.ExitMovementMessage;
import com.fizz.business.dto.MatmapDTO;
import com.fizz.business.service.OpcMessageHandler; import com.fizz.business.service.OpcMessageHandler;
import com.fizz.business.utils.MatmapUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Component @Component
@Slf4j
@OpcMessageHandlerType(OpcMessageType.EXIT_MOVEMENT) @OpcMessageHandlerType(OpcMessageType.EXIT_MOVEMENT)
@RequiredArgsConstructor
public class ExitMovementHandler implements OpcMessageHandler<ExitMovementMessage> { public class ExitMovementHandler implements OpcMessageHandler<ExitMovementMessage> {
@Override @Override
public void handle(ExitMovementMessage message) { public void handle(ExitMovementMessage message) {
// 出口移动逻辑处理 System.out.printf("处理 EXIT_MOVEMENT 消息: %d -> %d%n", message.getExSrc(), message.getExDesc());
System.out.println("处理 EXIT_MOVEMENT 消息: " + message);
// 1. 获取源位置的物料信息
int srcIndex = message.getExSrc() -1 ;
MatmapDTO srcMatmap = MatmapUtil.getMatmap(srcIndex);
if (ObjectUtil.isNull(srcMatmap) || ObjectUtil.isNull(srcMatmap.getMatId())) {
log.error("源位置 {} 没有物料信息,跳过移动处理。", srcIndex);
// 如果源位置没有物料,则可能是一次无效的移动,直接返回
return;
}
// 2. 清空源位置的物料信息
MatmapUtil.clearMatmap(srcIndex);
log.info("成功清空源位置 {} 的物料信息。{}", srcIndex,srcMatmap.getMatId());
// 3. 将物料信息设置到目标位置
int destIndex = message.getExDesc() - 1;
MatmapUtil.setMatmap(destIndex, srcMatmap.getMatId(), srcMatmap.getPlanId(), srcMatmap.getPlanNo());
log.info("成功设置目标位置 {} 的物料信息为钢卷 {}", destIndex, srcMatmap.getMatId());
} }
} }

View File

@@ -8,24 +8,31 @@ import com.fizz.business.domain.PdoExcoil;
import com.fizz.business.form.CrmPdoExcoilForm; import com.fizz.business.form.CrmPdoExcoilForm;
import com.fizz.business.mapper.CrmPdoExcoilMapper; import com.fizz.business.mapper.CrmPdoExcoilMapper;
import com.fizz.business.service.CrmPdoExcoilService; import com.fizz.business.service.CrmPdoExcoilService;
import com.fizz.business.vo.ReportDetailVO;
import com.fizz.business.vo.ReportSummaryVO;
import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List; import java.util.List;
import java.util.Objects;
@Service @Service
@Slf4j
public class CrmPdoExcoilServiceImpl extends ServiceImpl<CrmPdoExcoilMapper, CrmPdoExcoil> implements CrmPdoExcoilService { public class CrmPdoExcoilServiceImpl extends ServiceImpl<CrmPdoExcoilMapper, CrmPdoExcoil> implements CrmPdoExcoilService {
/** /**
* 根据退出卷ID和操作员ID查询记录 * 根据退出卷ID和操作员ID查询记录
* *
* @param excoilid 退出卷ID * @param excoilid 退出卷ID
* @param operid 操作员ID
* @return 查询到的CrmPdoExcoil对象 * @return 查询到的CrmPdoExcoil对象
*/ */
public CrmPdoExcoil getByExcoilIdAndOperId(String excoilid, Integer operid) { public CrmPdoExcoil getByExcoilId(String excoilid) {
QueryWrapper<CrmPdoExcoil> queryWrapper = new QueryWrapper<>(); QueryWrapper<CrmPdoExcoil> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("exit_coilid", excoilid).eq("operid", operid); queryWrapper.eq("exit_coilid", excoilid);
return this.getOne(queryWrapper); return this.getOne(queryWrapper);
} }
@@ -98,5 +105,51 @@ public class CrmPdoExcoilServiceImpl extends ServiceImpl<CrmPdoExcoilMapper, Crm
return baseMapper.selectCount(queryWrapper); return baseMapper.selectCount(queryWrapper);
} }
@Override
public ReportSummaryVO getReportSummary(String groupNo, String shiftNo, LocalDateTime startTime, LocalDateTime endTime) {
return baseMapper.getReportSummary(groupNo, shiftNo, startTime, endTime);
}
@Override
public List<ReportDetailVO> getReportDetails(String groupNo, String shiftNo, LocalDateTime startTime, LocalDateTime endTime) {
return baseMapper.getReportDetails(groupNo, shiftNo, startTime, endTime);
}
/**
* 【新增实现】
* 负责更新产出卷的实际重量和状态。
*/
@Override
@Transactional
public void updateExitCoilActualWeight(String exitMatId, BigDecimal actualWeight) {
// 1. 根据成品卷号查询记录
CrmPdoExcoil excoil = this.getByExcoilId(exitMatId);
if (excoil == null) {
log.error("未找到成品卷 {} 的数据库记录,无法更新重量。", exitMatId);
return;
}
// 2. 检查当前状态是否为 "UNWEIGHT" (未称重)
// 确保我们只更新未处理过的卷
if (Objects.equals(excoil.getStatus(), "UNWEIGHT")) {
// 3. 更新实际重量和状态
excoil.setActualWeight(actualWeight.doubleValue());
excoil.setStatus("COMPLETED"); // 或其他最终状态,如 "WEIGHED"
// 4. 调用 CRM 服务进行数据库更新
boolean success = this.updateCrmPdoExcoil(excoil);
if (success) {
log.info("成品卷 {} 的实际重量已成功更新为 {} kg状态变为 {}.",
exitMatId, actualWeight, excoil.getStatus());
} else {
log.error("更新成品卷 {} 失败。", exitMatId);
}
} else {
log.warn("成品卷 {} 的状态不是 'UNWEIGHT',当前状态为 '{}',跳过重量更新。",
exitMatId, excoil.getStatus());
}
}
} }

View File

@@ -98,7 +98,7 @@ public class PdoExCoilServiceImpl implements PdoExCoilService {
// 产出实际入库 // 产出实际入库
crmPdoExcoilService.addCrmPdoExcoil(pdoExCoilDTO); crmPdoExcoilService.addCrmPdoExcoil(pdoExCoilDTO);
// 打包segment // 打包segment
pdoStripValueService.treatStripValues(exitCoil); // pdoStripValueService.treatStripValues(exitCoil);
return pdoExCoilDTO; return pdoExCoilDTO;
} }

View File

@@ -0,0 +1,61 @@
package com.fizz.business.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fizz.business.dto.SegmentDTO;
import com.fizz.business.mapper.SegmentMapper;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
@Service
public class SegmentService {
@Resource
private SegmentMapper segmentMapper;
private final ObjectMapper objectMapper = new ObjectMapper(); // 用于JSON转换
/**
* 保存完整的段数据,包含插入和更新逻辑。
* 对应 C++ 的 saveTotalSegment() 方法。
*
* @param segment 待保存的段数据对象
*/
public void saveTotalSegment(SegmentDTO segment) {
if (segment == null || segment.getEnCoilID() == null || segment.getEnCoilID().isEmpty()) {
System.err.println("警告: Coil ID 为空,无法保存数据。");
return;
}
// 1. 将动态的 Map 数据转换为 JSON 字符串
Map<String, BigDecimal> totalValuesAvg = new HashMap<>();
segment.getTotalValues().forEach((key, segValue) -> {
// 这里将 key 拼接上 "_avg" 以示区分,如果需要也可以不加
totalValuesAvg.put(key + "_avg", segValue.getAverage());
});
try {
// 使用 Jackson ObjectMapper 将 Map 转换为 JSON 字符串
String jsonString = objectMapper.writeValueAsString(totalValuesAvg);
segment.setTotalValuesJson(jsonString);
} catch (JsonProcessingException e) {
System.err.println("转换 JSON 失败: " + e.getMessage());
return;
}
// 2. 先执行删除,再插入,以避免重复数据(与 C++ 逻辑保持一致)
QueryWrapper<SegmentDTO> deleteWrapper = new QueryWrapper<>();
deleteWrapper.eq("en_coil_id", segment.getEnCoilID())
.eq("seg_no", segment.getSegNo());
segmentMapper.delete(deleteWrapper);
// 3. 执行插入操作MyBatis-Plus 会自动处理 totalValuesJson 字段的保存
segmentMapper.insert(segment);
System.out.println("段数据保存成功。钢卷号: " + segment.getEnCoilID() + ", 段号: " + segment.getSegNo());
}
}

View File

@@ -3,30 +3,23 @@ package com.fizz.business.service.impl;
import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.NumberUtil; import cn.hutool.core.util.NumberUtil;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fizz.business.constants.enums.DeviceEnum; import com.fizz.business.constants.enums.DeviceEnum;
import com.fizz.business.constants.enums.PlanStatusEnum; import com.fizz.business.constants.enums.PlanStatusEnum;
import com.fizz.business.constants.enums.WebOperateMatEnum; import com.fizz.business.constants.enums.WebOperateMatEnum;
import com.fizz.business.domain.DeviceDefine;
import com.fizz.business.domain.HalfReturn;
import com.fizz.business.dto.CoilHeadDTO; import com.fizz.business.dto.CoilHeadDTO;
import com.fizz.business.dto.CoilPositionDTO; import com.fizz.business.dto.CoilPositionDTO;
import com.fizz.business.dto.MatmapDTO; import com.fizz.business.dto.MatmapDTO;
import com.fizz.business.form.AdjustPosForm; import com.fizz.business.form.AdjustPosForm;
import com.fizz.business.form.L1OperateMatForm; import com.fizz.business.form.L1OperateMatForm;
import com.fizz.business.form.WebOperateMatForm; import com.fizz.business.form.WebOperateMatForm;
import com.fizz.business.mapper.HalfReturnMapper;
import com.fizz.business.mapper.SegmentMapper;
import com.fizz.business.service.CrmPdiPlanService; import com.fizz.business.service.CrmPdiPlanService;
import com.fizz.business.service.TrackService; import com.fizz.business.service.TrackService;
import com.fizz.business.service.client.LocalCacheManager;
import com.fizz.business.service.client.RedisCacheManager; import com.fizz.business.service.client.RedisCacheManager;
import com.fizz.business.utils.CalcUtil; import com.fizz.business.utils.CalcUtil;
import com.fizz.business.utils.CoilMeasUtil; import com.fizz.business.utils.CoilMeasUtil;
import com.fizz.business.utils.MatmapUtil; import com.fizz.business.utils.MatmapUtil;
import com.fizz.business.utils.WebSocketUtil; import com.fizz.business.utils.WebSocketUtil;
import com.fizz.business.vo.CrmPdiPlanVO; import com.fizz.business.vo.CrmPdiPlanVO;
import com.fizz.business.vo.HalfReturnInfoVO;
import com.fizz.business.vo.ReturnInfoVO; import com.fizz.business.vo.ReturnInfoVO;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -50,8 +43,7 @@ public class TrackServiceImpl implements TrackService {
@Autowired @Autowired
RedisCacheManager redisCacheManager; RedisCacheManager redisCacheManager;
@Autowired
SegmentMapper segmentMapper;
@Autowired @Autowired
CrmPdiPlanService crmPdiPlanService; CrmPdiPlanService crmPdiPlanService;
@@ -107,10 +99,10 @@ public class TrackServiceImpl implements TrackService {
*/ */
@Override @Override
public void adjustPosition(AdjustPosForm form) { public void adjustPosition(AdjustPosForm form) {
DeviceDefine targetDevice = LocalCacheManager.DEVICE_DEFINE_MAP.get(form.getTargetPos().name()); DeviceEnum targetDevice = DeviceEnum.fromName(form.getTargetPos().name());
Assert.notNull(targetDevice, "目标位置设备不存在"); Assert.notNull(targetDevice, "目标位置设备不存在");
DeviceDefine currentDevice = LocalCacheManager.DEVICE_DEFINE_MAP.get(form.getCurrentPos().name()); DeviceEnum currentDevice = DeviceEnum.fromName(form.getCurrentPos().name());
Assert.notNull(currentDevice, "当前位置设备不存在"); Assert.notNull(currentDevice, "当前位置设备不存在");
// 更新matmap // 更新matmap
@@ -129,16 +121,16 @@ public class TrackServiceImpl implements TrackService {
return true; return true;
} }
// 多个卷取机需要跳过1#卷取机 // 多个卷取机需要跳过1#卷取机
if (Objects.equals(endPos, DeviceEnum.TR2.getIdx()) if (Objects.equals(endPos, DeviceEnum.TR.getIdx())
&& Objects.equals(currPos, DeviceEnum.TR1.getIdx())) { ) {
return true; return true;
} }
return false; return false;
} }
private void refreshMatmap(DeviceDefine targetDevice, DeviceDefine currDevice, AdjustPosForm form) { private void refreshMatmap(DeviceEnum targetDevice, DeviceEnum currDevice, AdjustPosForm form) {
Integer targetPosIdx = targetDevice.getPosIdx(); Integer targetPosIdx = targetDevice.getIdx();
Integer currPosIdx = currDevice.getPosIdx(); Integer currPosIdx = currDevice.getIdx();
MatmapDTO target = MatmapUtil.getMatmap(targetPosIdx); MatmapDTO target = MatmapUtil.getMatmap(targetPosIdx);
MatmapDTO curr = MatmapUtil.getMatmap(currPosIdx); MatmapDTO curr = MatmapUtil.getMatmap(currPosIdx);
if (Objects.equals(curr.getMatId(), target.getMatId())) { if (Objects.equals(curr.getMatId(), target.getMatId())) {

View File

@@ -0,0 +1,309 @@
package com.fizz.business.service.strip;
import com.fizz.business.constants.enums.DeviceEnum;
import com.fizz.business.constants.enums.L1OperateMatEnum;
import com.fizz.business.domain.msg.AppMeasureCoatMessage;
import com.fizz.business.domain.msg.AppMeasureEntryMessage;
import com.fizz.business.domain.msg.AppMeasureExitMessage;
import com.fizz.business.domain.msg.AppMeasureFurnaceMessage;
import com.fizz.business.dto.MatmapDTO;
import com.fizz.business.dto.SegValue;
import com.fizz.business.dto.SegmentDTO;
import com.fizz.business.form.L1OperateMatForm;
import com.fizz.business.service.TrackService;
import com.fizz.business.service.impl.SegmentService;
import com.fizz.business.utils.MatmapUtil;
import com.fizz.business.utils.WebSocketUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.ConcurrentMap;
import static com.fizz.business.constants.enums.DeviceEnum.WELDER;
@Service
@Slf4j
@RequiredArgsConstructor
public class SegmentTrackerService {
private final StripPositionService stripPositionService;
private final TrackService trackService;
private final SegmentService segmentService; // 注入新创建的段服务
private int traceCount = 0;
private boolean firstMeasure = true;
private BigDecimal weldLength = BigDecimal.ZERO;
private int coilSegNum = 1;
private final Deque<SegmentDTO> listSegment = new ConcurrentLinkedDeque<>();
private static final double SEGSTRIPLEN = 2.0;
private static final int MAX_SEG_COUNT = 2000;
private final ConcurrentMap<String, Set<DeviceEnum>> coilReachedDevices = new ConcurrentHashMap<>();
private static final double LOWSPEEDLIMIT = 0.05;
/**
* 【主入口】
* 负责处理新的测量数据,生成新段,并对新到达的设备做一次性快照。
*/
public void handleMeasure(String coilId,
BigDecimal entryLengthAtWelder,
AppMeasureEntryMessage entry,
AppMeasureFurnaceMessage furnace,
AppMeasureCoatMessage coat,
AppMeasureExitMessage exit) {
// 1. 首次测量初始化或新卷判定
BigDecimal weldDev;
if (firstMeasure || entryLengthAtWelder.compareTo(weldLength) < 0) {
weldDev = entryLengthAtWelder; // 新卷偏移量等于当前长度
coilSegNum = 1;
weldLength = entryLengthAtWelder;
firstMeasure = false;
listSegment.clear(); // 清空旧段
coilReachedDevices.remove(coilId); // 清空旧钢卷的快照记录
log.info("New coil detected or initialized:{} " ,coilId);
} else {
weldDev = entryLengthAtWelder.subtract(weldLength);
}
// 更新焊机长度
weldLength = entryLengthAtWelder;
// trace 控制
traceCount++;
if (traceCount > 5) {
log.info(String.format("Trace: weldLen=%.2f, speed=%s",
entryLengthAtWelder, procStripSpeed(entry, furnace, exit)));
traceCount = 0;
}
// 2. 检查是否达到生成新段的条件
BigDecimal segThreshold = BigDecimal.valueOf(coilSegNum * SEGSTRIPLEN);
if (entryLengthAtWelder.compareTo(segThreshold) >= 0) {
// 2.1. 创建新的 SegmentDTO 并初始化基本位置
SegmentDTO newSeg = createNewSegment(coilId, entryLengthAtWelder);
// 2.3. 将新段添加到队列中
listSegment.addLast(newSeg);
if (listSegment.size() > MAX_SEG_COUNT) {
listSegment.removeFirst();
}
coilSegNum++;
}
// 3. 处理队列中所有已存在的段
treatSegAsync(entry, furnace, coat, exit, weldDev);
}
/**
* 【核心功能】
* 遍历所有已存在的段,更新其位置,并累积其在设备区域内的实时数据。
* 这部分逻辑只负责“移动”和“累积”。
*/
public void treatSeg(AppMeasureEntryMessage entry,
AppMeasureFurnaceMessage furnace,
AppMeasureCoatMessage coat,
AppMeasureExitMessage exit,
BigDecimal weldDev) {
if (listSegment.isEmpty()) {
return;
}
BigDecimal celLength = entry != null ? entry.getCelLength() : BigDecimal.ZERO;
BigDecimal cxlLength = exit != null ? exit.getCxlLength() : BigDecimal.ZERO;
Iterator<SegmentDTO> iterator = listSegment.descendingIterator();
while (iterator.hasNext()) {
SegmentDTO segment = iterator.next();
// 更新段在产线上的位置
segment.setHeadPos(segment.getHeadPos().add(weldDev));
segment.setTailPos(segment.getTailPos().add(weldDev));
// 遍历所有设备,累积段在设备区域内的数据
for (DeviceEnum device : DeviceEnum.values()) {
double currentDevicePos = stripPositionService.calculate(device, celLength, cxlLength);
// 判断段是否位于设备的“长区域”内
if (segment.getHeadPos().compareTo(BigDecimal.valueOf(currentDevicePos)) > 0 &&
segment.getTailPos().compareTo(BigDecimal.valueOf(currentDevicePos)) < 0) {
double currentSpeed = getSpeedForDevice(device, entry, furnace, exit);
if (currentSpeed > LOWSPEEDLIMIT) {
for (String fieldName : device.getParamFields()) {
Object message = getMessageForDevice(device, entry, furnace, coat, exit);
if (message != null) {
BigDecimal value = getFieldFromMessage(message, fieldName);
setSegValue(segment.getTotalValues(), fieldName, value);
}
}
}
}
}
// 出厂判断
double exitPlantPos = stripPositionService.calculate(DeviceEnum.TR, celLength, cxlLength);
if (segment.getTailPos().compareTo(BigDecimal.valueOf(exitPlantPos)) >= 0) {
System.out.println("钢卷 " + segment.getEnCoilID() + " 的段号 " + segment.getSegNo() + " 已离开产线,开始持久化数据。");
segmentService.saveTotalSegment(segment); // 调用服务进行持久化
iterator.remove();
}
}
}
// --- 新增的辅助方法,将逻辑封装起来 ---
/**
* 【异步方法】
* 异步处理队列中所有已存在的段,更新其位置和累积数据。
*/
@Async("taskExecutor") // 指定使用名为 "taskExecutor" 的线程池
public void treatSegAsync(AppMeasureEntryMessage entry,
AppMeasureFurnaceMessage furnace,
AppMeasureCoatMessage coat,
AppMeasureExitMessage exit,
BigDecimal weldDev) {
// 调用原有的同步方法
this.treatSeg(entry, furnace, coat, exit, weldDev);
}
/**
* 创建并初始化一个新的 SegmentDTO。
*/
private SegmentDTO createNewSegment(String coilId, BigDecimal entryLengthAtWelder) {
SegmentDTO seg = new SegmentDTO();
seg.setSegNo(coilSegNum);
seg.setEnCoilID(coilId);
if (coilSegNum == 1) {
// 第一个段的特殊处理
seg.setStartPos(BigDecimal.ZERO);
seg.setEndPos(entryLengthAtWelder);
seg.setSegLen(entryLengthAtWelder);
seg.setHeadPos(entryLengthAtWelder);
seg.setTailPos(BigDecimal.ZERO);
} else {
// 后续段的计算
SegmentDTO prev = listSegment.peekLast();
seg.setStartPos(prev.getEndPos());
seg.setEndPos(entryLengthAtWelder);
seg.setSegLen(seg.getEndPos().subtract(seg.getStartPos()));
// 这两个位置是全线绝对位置,后续会通过 weldDev 动态更新
seg.setHeadPos(entryLengthAtWelder);
seg.setTailPos(seg.getEndPos().subtract(seg.getSegLen()));
}
return seg;
}
// --- 辅助方法,与消息处理和反射相关 ---
private Object getMessageForDevice(DeviceEnum device, AppMeasureEntryMessage entry, AppMeasureFurnaceMessage furnace, AppMeasureCoatMessage coat, AppMeasureExitMessage exit) {
if (device.getDesc().contains("开卷机") || device.getDesc().contains("活套") || device.getDesc().contains("焊机")) {
return entry;
} else if (device.getDesc().contains("清洗段") || device.getDesc().contains("退火炉")) {
return furnace;
} else if (device.getDesc().contains("涂机")) {
return coat;
} else {
return exit;
}
}
private double getSpeedForDevice(DeviceEnum device, AppMeasureEntryMessage entry, AppMeasureFurnaceMessage furnace, AppMeasureExitMessage exit) {
if (device.getDesc().contains("开卷机") || device.getDesc().contains("活套") || device.getDesc().contains("焊机")) {
return entry != null && entry.getStripSpeed() != null ? entry.getStripSpeed().doubleValue() : 0.0;
} else if (device.getDesc().contains("清洗段") || device.getDesc().contains("退火炉")) {
return furnace != null && furnace.getStripSpeed() != null ? furnace.getStripSpeed().doubleValue() : 0.0;
} else {
return exit != null && exit.getSpeedExitSection() != null ? exit.getSpeedExitSection().doubleValue() : 0.0;
}
}
private double procStripSpeed(AppMeasureEntryMessage entry, AppMeasureFurnaceMessage furnace, AppMeasureExitMessage exit) {
if (entry != null && entry.getStripSpeed() != null) return entry.getStripSpeed().doubleValue();
if (furnace != null && furnace.getStripSpeed() != null) return furnace.getStripSpeed().doubleValue();
if (exit != null && exit.getSpeedExitSection() != null) return exit.getSpeedExitSection().doubleValue();
return 0.0;
}
private BigDecimal getFieldFromMessage(Object message, String fieldName) {
if (message == null || fieldName == null) return null;
try {
String getterName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
Method getter = message.getClass().getMethod(getterName);
Object value = getter.invoke(message);
if (value instanceof BigDecimal) {
return (BigDecimal) value;
} else if (value instanceof Number) {
return BigDecimal.valueOf(((Number) value).doubleValue());
} else {
return null;
}
} catch (Exception e) {
System.err.println("Error getting field " + fieldName + " from message: " + e.getMessage());
return null;
}
}
private void setSegValue(Map<String, SegValue> segValues, String key, BigDecimal value) {
if (value != null) {
segValues.computeIfAbsent(key, k -> new SegValue()).add(value);
}
}
/**
* 【新方法】
* 专门用于处理钢卷头部在设备间的移动和物料跟踪。
* 这部分逻辑包含了对 Redis/DB 的写入,最适合异步化。
*/
@Async("taskExecutor")
public void trackCoilHeadPosition(String coilId, BigDecimal headPos,
AppMeasureEntryMessage entry, AppMeasureExitMessage exit) {
Set<DeviceEnum> prevReached = coilReachedDevices.computeIfAbsent(coilId, k -> Collections.newSetFromMap(new ConcurrentHashMap<>()));
BigDecimal celLength = entry != null ? entry.getCelLength() : BigDecimal.ZERO;
BigDecimal cxlLength = exit != null ? exit.getCxlLength() : BigDecimal.ZERO;
for (DeviceEnum d : DeviceEnum.values()) {
double dynPos = stripPositionService.calculate(d, celLength, cxlLength);
// 判断钢卷的头部是否首次到达该设备
if (headPos.compareTo(BigDecimal.valueOf(dynPos)) >= 0) {
if (!prevReached.contains(d)) {
// 1. 如果是焊机,则调用 CRM 更新计划状态
if (d == WELDER) {
MatmapDTO matmap = MatmapUtil.getMatmap(WELDER.getIdx());
trackService.l1OperateMat(L1OperateMatForm.builder()
.entryMatId(coilId)
.planId(matmap.getPlanId())
.porIdx(entry.getPayOffReelNumber())
.operation(L1OperateMatEnum.PRODUCING)
.build());
}
// 2. 更新 Matmap
MatmapUtil.setMatId(d.getIdx(), coilId);
WebSocketUtil.sendMatmapMsg();
// 3. 标记为已到达,防止重复操作
prevReached.add(d);
}
}
}
}
}

View File

@@ -0,0 +1,68 @@
package com.fizz.business.service.strip;
import com.fizz.business.constants.enums.DeviceEnum;
import com.fizz.business.domain.msg.AppMeasureEntryMessage;
import com.fizz.business.domain.msg.AppMeasureExitMessage;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
/**
* 位置计算服务 —— 根据 entry.stripLocation焊机参考和活套长度动态判断设备的比较位置
*/
@Service
public class StripPositionService {
/**
* 计算设备在 entry 坐标系的“比较位置”
*
* 规则:
* - device.basePosition < ENL_MIN : 不加活套
* - ENL_MIN <= device.basePosition < CXL_MIN : 加 celLength
* - >= CXL_MIN : 加 cxlLength
*/
public double calculate(DeviceEnum device, BigDecimal celLength, BigDecimal cxlLength) {
double base = device.getBasePosition();
double cel = celLength == null ? 0.0 : celLength.doubleValue();
double cxl = cxlLength == null ? 0.0 : cxlLength.doubleValue();
double enlMin = findEnlMin();
double cxlMin = findCxlMin();
if (base < enlMin) {
return base;
} else if (base < cxlMin) {
return base + cel;
} else {
return base + cxl;
}
}
public double calculate(DeviceEnum device,AppMeasureEntryMessage entry,
AppMeasureExitMessage exit) {
BigDecimal cel = entry == null ? null : entry.getCelLength();
BigDecimal cxl = exit == null ? null : exit.getCxlLength();
return calculate(device, cel, cxl);
}
private double findEnlMin() {
double min = Double.MAX_VALUE;
for (DeviceEnum d : DeviceEnum.values()) {
if (d.name().startsWith("ENL")) {
min = Math.min(min, d.getBasePosition());
}
}
return min == Double.MAX_VALUE ? 0.0 : min;
}
private double findCxlMin() {
double min = Double.MAX_VALUE;
for (DeviceEnum d : DeviceEnum.values()) {
if (d.name().startsWith("CXL")) {
min = Math.min(min, d.getBasePosition());
}
}
return min == Double.MAX_VALUE ? Double.MAX_VALUE : min;
}
}

View File

@@ -1,23 +1,21 @@
package com.fizz.business.utils; package com.fizz.business.utils;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSON;
import com.fizz.business.constants.enums.DeviceEnum; import com.fizz.business.constants.enums.DeviceEnum;
import com.fizz.business.domain.DeviceDefine; import com.fizz.business.domain.DeviceDefine;
import com.fizz.business.dto.*; import com.fizz.business.dto.CoilHeadDTO;
import com.fizz.business.dto.CoilPositionDTO;
import com.fizz.business.dto.L1CoilLineMeasureDTO;
import com.fizz.business.dto.MatmapDTO;
import com.fizz.business.service.client.RedisCacheManager; import com.fizz.business.service.client.RedisCacheManager;
import com.fizz.business.service.impl.BeanFactory; import com.fizz.business.service.impl.BeanFactory;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.*; import java.util.List;
import java.util.stream.Collectors; import java.util.Objects;
import static com.fizz.business.constants.CommonConstants.CoilMeasure.BEFORE_TR_IDX; import static com.fizz.business.constants.CommonConstants.CoilMeasure.BEFORE_TR_IDX;
import static com.fizz.business.service.client.LocalCacheManager.DEVICE_DEFINE_MAP;
/** /**
@@ -67,9 +65,7 @@ public class CoilMeasUtil {
Integer trIdx = null; Integer trIdx = null;
if (trCode == 1) { if (trCode == 1) {
trIdx = DeviceEnum.TR1.getIdx(); trIdx = DeviceEnum.TR.getIdx();
} else if (trCode == 2) {
trIdx = DeviceEnum.TR2.getIdx();
} else { } else {
log.warn("W: invalid working TR, trCode={}", trCode); log.warn("W: invalid working TR, trCode={}", trCode);
} }
@@ -81,7 +77,7 @@ public class CoilMeasUtil {
} }
public static List<Integer> getTrIdxList() { public static List<Integer> getTrIdxList() {
return Lists.newArrayList(DeviceEnum.TR1.getIdx(), DeviceEnum.TR2.getIdx()); return Lists.newArrayList(DeviceEnum.TR.getIdx());
} }
/** /**
@@ -93,147 +89,4 @@ public class CoilMeasUtil {
return BEFORE_TR_IDX.getIdx(); return BEFORE_TR_IDX.getIdx();
} }
public static double initStripLocation(double stripLocation) {
if (stripLocation < 0) {
stripLocation = 0;
} else if (stripLocation > 25000) {
stripLocation = 25000;
}
return stripLocation;
}
public static DeviceDefine matchDevice(double posLen, double entryLoopLen, double exitLoopLen) {
DeviceDefine mCEL = DEVICE_DEFINE_MAP.get(DeviceEnum.CEL.name()); // 入口活套
DeviceDefine mCXL = DEVICE_DEFINE_MAP.get(DeviceEnum.CXL.name()); // 出口活套
for (DeviceDefine device : DEVICE_DEFINE_MAP.values()) {
Double start = device.getPositionLengthStart();
Double end = device.getPositionLengthEnd();
// 入口活套之后的设备需要加上entryLoopLen
if (device.getPosIdx() >= mCEL.getPosIdx()) {
// start = NumberUtil.add(start, entryLoopLen);
end = NumberUtil.add(end, entryLoopLen);
}
// 出口活套之后的设备还需要加上exitLoopLen
if (device.getPosIdx() >= mCXL.getPosIdx()) {
// start = NumberUtil.add(start, exitLoopLen);
end = NumberUtil.add(end, exitLoopLen);
}
if (posLen >= start && posLen < end) {
return device;
}
}
return null;
}
public static CoilPositionDTO buildCoilPos(L1CoilLineMeasureDTO measureDTO, List<CoilHeadDTO> headList) {
List<CoilPositionDTO.CoilStripLocation> coilStripLocationList = Lists.newArrayList();
Integer workPor = measureDTO.getWorkPor();
Integer workTr = measureDTO.getWorkTr();
CoilHeadDTO prevHead = BeanFactory.getBean(RedisCacheManager.class).getPrevHead();
if (Objects.nonNull(prevHead)) {
coilStripLocationList.add(
CoilPositionDTO.CoilStripLocation.builder()
.matId(prevHead.getMatId())
.planId(prevHead.getPlanId())
.planNo(prevHead.getPlanNo())
.porId(prevHead.getPorIdx())
.stripLocation(99999).build());
}
headList.forEach(head -> coilStripLocationList.add(
CoilPositionDTO.CoilStripLocation.builder()
.matId(head.getMatId())
.planId(head.getPlanId())
.planNo(head.getPlanNo())
.porId(head.getPorIdx())
.stripLocation(head.getPos()).build()));
// 两个开卷机时,需要设置另一个未运行的开卷机上的钢卷数据
Integer anotherPorIdx = getAnotherPorIdx(workPor);
if (Objects.nonNull(anotherPorIdx)) {
MatmapDTO matmap = MatmapUtil.getMatmap(anotherPorIdx);
coilStripLocationList.add(
CoilPositionDTO.CoilStripLocation.builder()
.matId(matmap.getMatId())
.planId(matmap.getPlanId())
.planNo(matmap.getPlanNo())
.porId(anotherPorIdx)
.stripLocation(0).build());
}
// 当前开卷机与焊机的卷号不同时,需要设置当前开卷机上的钢卷数据(这种情况只出现在只有一个开卷机运行的时候)
Integer porIdx = CoilMeasUtil.getPorIdx(workPor);
MatmapDTO welderCoil = MatmapUtil.getMatmap(DeviceEnum.WELDER.getIdx());
if (Objects.nonNull(porIdx) && MatmapUtil.already(welderCoil)) {
MatmapDTO porCoil = MatmapUtil.getMatmap(porIdx);
if (!Objects.equals(welderCoil.getMatId(), porCoil.getMatId())) {
coilStripLocationList.add(
CoilPositionDTO.CoilStripLocation.builder()
.matId(porCoil.getMatId())
.planId(porCoil.getPlanId())
.planNo(porCoil.getPlanNo())
.porId(porIdx)
.stripLocation(0).build());
}
}
return CoilPositionDTO.builder()
.coilStripLocationList(coilStripLocationList)
.entryLoopLen(measureDTO.getEntryLoopLength())
.exitLoopLen(measureDTO.getExitLoopLength())
.entryLoopPer(measureDTO.getEntryLoopPercent())
.exitLoopLPer(measureDTO.getExitLoopPercent())
.entrySpeed(measureDTO.getInSpeed())
.technologySpeed(measureDTO.getUnitSpeed())
.exitSpeed(measureDTO.getOutSpeed())
.porId(CoilMeasUtil.getPorIdx(workPor))
.trId(CoilMeasUtil.getTrIdx(workTr))
.build();
}
public static double getPorSpeed(L1CoilLineMeasureDTO measureDTO) {
return measureDTO.getUnitSpeed();
}
// public static List<String> getParamKey(String deviceName) {
// List<PointConfigDTO> list = DEVICE_PARAM_MAP.get(deviceName);
// if (CollUtil.isEmpty(list)) {
// return Collections.emptyList();
// }
// return list.stream().map(PointConfigDTO::getParam).collect(Collectors.toList());
// }
//
// public static String getParamKey(String deviceName, String paramDesc) {
// List<PointConfigDTO> list = DEVICE_PARAM_MAP.get(deviceName);
// if (CollUtil.isEmpty(list)) {
// return null;
// }
// List<String> keys = list.stream()
// .filter(p -> paramDesc.equals(p.getDesc()))
// .map(PointConfigDTO::getParam)
// .collect(Collectors.toList());
// if (CollUtil.isEmpty(keys)) {
// return null;
// }
// return keys.get(0);
// }
// public static List<String> getParamDesc(String deviceName) {
// List<PointConfigDTO> list = DEVICE_PARAM_MAP.get(deviceName);
// if (CollUtil.isEmpty(list)) {
// return Collections.emptyList();
// }
// return list.stream().map(PointConfigDTO::getDesc).collect(Collectors.toList());
// }
//
// public static LinkedHashMap<String, String> getParamMap(String deviceName) {
// List<PointConfigDTO> list = DEVICE_PARAM_MAP.get(deviceName);
// if (CollUtil.isEmpty(list)) {
// return Maps.newLinkedHashMap();
// }
//
// return list.stream().collect(Collectors.toMap(PointConfigDTO::getParam, PointConfigDTO::getDesc, (k1, k2) -> k1, LinkedHashMap::new));
// }
} }

View File

@@ -6,6 +6,8 @@ import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.fizz.business.constants.CommonConstants; import com.fizz.business.constants.CommonConstants;
import com.fizz.business.constants.enums.WsTypeEnum; import com.fizz.business.constants.enums.WsTypeEnum;
import com.fizz.business.domain.msg.AppMeasureEntryMessage;
import com.fizz.business.domain.msg.AppMeasureMessage;
import com.fizz.business.dto.CoilPositionDTO; import com.fizz.business.dto.CoilPositionDTO;
import com.fizz.business.dto.L1CoilLineMeasureDTO; import com.fizz.business.dto.L1CoilLineMeasureDTO;
import com.fizz.business.dto.MatmapDTO; import com.fizz.business.dto.MatmapDTO;
@@ -79,6 +81,13 @@ public class WebSocketUtil {
sendMessage(WsTypeEnum.track_measure, JSONUtil.toJsonStr(measureDTO)); sendMessage(WsTypeEnum.track_measure, JSONUtil.toJsonStr(measureDTO));
} }
public static void sendMeasureMsg(AppMeasureMessage measureDTO) {
sendMessage(WsTypeEnum.track_measure, JSONUtil.toJsonStr(measureDTO));
}
public static void sendMatmapMsg() { public static void sendMatmapMsg() {
List<MatmapDTO> list = MatmapUtil.getMatmapList(); List<MatmapDTO> list = MatmapUtil.getMatmapList();
if (CollUtil.isEmpty(list)) return; if (CollUtil.isEmpty(list)) return;

View File

@@ -0,0 +1,47 @@
package com.fizz.business.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@Schema(name = "ReportDetailVO", description = "钢卷明细信息")
public class ReportDetailVO {
@Schema(description = "成品卷号")
private String exitMatId;
@Schema(description = "原料卷号")
private String entryMatId;
@Schema(description = "班号")
private String groupNo;
@Schema(description = "组号")
private String shiftNo;
@Schema(description = "钢种")
private String steelGrade;
@Schema(description = "成品宽度")
private Double exitWidth;
@Schema(description = "成品长度")
private Double exitLength;
@Schema(description = "理论重量")
private Double theoryWeight;
@Schema(description = "实际重量")
private Double actualWeight;
@Schema(description = "成品厚度")
private Double exitThickness;
@Schema(description = "上线时间")
private LocalDateTime onlineTime;
@Schema(description = "结束时间")
private LocalDateTime endTime;
}

View File

@@ -0,0 +1,51 @@
package com.fizz.business.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(name = "ReportSummaryVO", description = "报表汇总统计结果")
public class ReportSummaryVO {
// 汇总指标
@Schema(description = "总成品宽度")
private Double totalExitWidth;
@Schema(description = "总成品长度")
private Double totalExitLength;
@Schema(description = "总理论重量")
private Double totalTheoryWeight;
@Schema(description = "总实际重量")
private Double totalActualWeight;
@Schema(description = "总成品厚度")
private Double totalExitThickness;
// 平均指标
@Schema(description = "平均成品宽度")
private Double avgExitWidth;
@Schema(description = "平均成品长度")
private Double avgExitLength;
@Schema(description = "平均理论重量")
private Double avgTheoryWeight;
@Schema(description = "平均实际重量")
private Double avgActualWeight;
@Schema(description = "平均成品厚度")
private Double avgExitThickness;
// 新增指标
@Schema(description = "钢卷总数")
private Long coilCount;
@Schema(description = "原料总重(去重计算)")
private Double totalEntryWeight;
@Schema(description = "成材率(成品实际重量/原料重量)")
private Double yieldRate;
}

View File

@@ -3,4 +3,139 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fizz.business.mapper.CrmPdoExcoilMapper"> <mapper namespace="com.fizz.business.mapper.CrmPdoExcoilMapper">
<select id="getReportSummary" resultType="com.fizz.business.vo.ReportSummaryVO">
SELECT
-- 总计
SUM(exit_width) AS totalExitWidth,
SUM(exit_length) AS totalExitLength,
SUM(theory_weight) AS totalTheoryWeight,
SUM(actual_weight) AS totalActualWeight,
SUM(exit_thickness) AS totalExitThickness,
-- 平均
AVG(exit_width) AS avgExitWidth,
AVG(exit_length) AS avgExitLength,
AVG(theory_weight) AS avgTheoryWeight,
AVG(actual_weight) AS avgActualWeight,
AVG(exit_thickness) AS avgExitThickness,
-- 总数
COUNT(DISTINCT exit_mat_id) AS coilCount,
-- 原料总重(去重 entry_mat_id
(SELECT SUM(t.entry_weight)
FROM (
SELECT entry_mat_id, MAX(entry_weight) AS entry_weight
FROM crm_pdo_excoil
WHERE del_flag = 0
<if test="groupNo != null and groupNo != ''">
AND group_no = #{groupNo}
</if>
<if test="shiftNo != null and shiftNo != ''">
AND shift_no = #{shiftNo}
</if>
<if test="startTime != null">
AND end_time <![CDATA[ >= ]]> #{startTime}
</if>
<if test="endTime != null">
AND end_time <![CDATA[ <= ]]> #{endTime}
</if>
GROUP BY entry_mat_id
) t
) AS totalEntryWeight,
-- 成材率
CASE
WHEN (SELECT SUM(tt.entry_weight)
FROM (
SELECT entry_mat_id, MAX(entry_weight) AS entry_weight
FROM crm_pdo_excoil
WHERE del_flag = 0
<if test="groupNo != null and groupNo != ''">
AND group_no = #{groupNo}
</if>
<if test="shiftNo != null and shiftNo != ''">
AND shift_no = #{shiftNo}
</if>
<if test="startTime != null">
AND end_time <![CDATA[ >= ]]> #{startTime}
</if>
<if test="endTime != null">
AND end_time <![CDATA[ <= ]]> #{endTime}
</if>
GROUP BY entry_mat_id
) tt
) > 0
THEN SUM(actual_weight) /
(SELECT SUM(tt.entry_weight)
FROM (
SELECT entry_mat_id, MAX(entry_weight) AS entry_weight
FROM crm_pdo_excoil
WHERE del_flag = 0
<if test="groupNo != null and groupNo != ''">
AND group_no = #{groupNo}
</if>
<if test="shiftNo != null and shiftNo != ''">
AND shift_no = #{shiftNo}
</if>
<if test="startTime != null">
AND end_time <![CDATA[ >= ]]> #{startTime}
</if>
<if test="endTime != null">
AND end_time <![CDATA[ <= ]]> #{endTime}
</if>
GROUP BY entry_mat_id
) tt
)
ELSE 0
END AS yieldRate
FROM crm_pdo_excoil
WHERE del_flag = 0
<if test="groupNo != null and groupNo != ''">
AND group_no = #{groupNo}
</if>
<if test="shiftNo != null and shiftNo != ''">
AND shift_no = #{shiftNo}
</if>
<if test="startTime != null">
AND end_time <![CDATA[ >= ]]> #{startTime}
</if>
<if test="endTime != null">
AND end_time <![CDATA[ <= ]]> #{endTime}
</if>
</select>
<select id="getReportDetails" resultType="com.fizz.business.vo.ReportDetailVO">
SELECT
exit_mat_id AS exitMatId,
entry_mat_id AS entryMatId,
group_no AS groupNo,
shift_no AS shiftNo,
steel_grade AS steelGrade,
exit_width AS exitWidth,
exit_length AS exitLength,
theory_weight AS theoryWeight,
actual_weight AS actualWeight,
exit_thickness AS exitThickness,
online_time AS onlineTime,
end_time AS endTime
FROM crm_pdo_excoil
WHERE del_flag = 0
<if test="groupNo != null and groupNo != ''">
AND group_no = #{groupNo}
</if>
<if test="shiftNo != null and shiftNo != ''">
AND shift_no = #{shiftNo}
</if>
<if test="startTime != null">
AND end_time <![CDATA[ >= ]]> #{startTime}
</if>
<if test="endTime != null">
AND end_time <![CDATA[ <= ]]> #{endTime}
</if>
ORDER BY end_time ASC
</select>
</mapper> </mapper>

View File

@@ -4,6 +4,7 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableAsync;
/** /**
* 启动程序 * 启动程序
@@ -11,6 +12,7 @@ import org.springframework.context.annotation.ComponentScan;
* @author ruoyi * @author ruoyi
*/ */
@ComponentScan(basePackages = {"com.ruoyi","com.fizz"}) @ComponentScan(basePackages = {"com.ruoyi","com.fizz"})
@EnableAsync // 启用异步处理
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class }) @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
public class RuoYiApplication public class RuoYiApplication
{ {

View File

@@ -74,9 +74,9 @@ spring:
port: 6379 port: 6379
# 数据库索引 # 数据库索引
database: 0 database: 0
# 密码 abcd1234 fe2b3cef78b74d3692909bdcbdf46331 KeLunPu123! # 密码 abcd1234 fe2b3cef78b74d3692909bdcbdf46331 KeLunPu123!
password: KeLunPu123! # password: KeLunPu123!
#password: password: abcd1234
# 连接超时时间 # 连接超时时间
timeout: 10s timeout: 10s
lettuce: lettuce:
@@ -140,10 +140,10 @@ xss:
# 匹配链接 # 匹配链接
urlPatterns: /system/*,/monitor/*,/tool/* urlPatterns: /system/*,/monitor/*,/tool/*
#rocketmq: #rocketmq:
#name-server: 127.0.0.1:9876 #name-server: 127.0.0.1:9876
#producer: #producer:
#group: test #group: test
springdoc: springdoc:
api-docs: api-docs: