Compare commits

..

13 Commits

25 changed files with 585 additions and 159 deletions

View File

@@ -115,6 +115,12 @@
<version>3.1.4.0.6.15</version>
</dependency>
<dependency>
<groupId>com.github.xingshuangs</groupId>
<artifactId>iot-communication</artifactId>
<version>1.5.5</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>com.alibaba</groupId>-->

View File

@@ -86,7 +86,7 @@ public class MessageSubscriptionRunner implements ApplicationRunner {
EntryMovementMessage msg =new EntryMovementMessage();
writeMessage( msg,entryMoveIds);
log.info("接收入口移动信号:从 {} 移动到 {} ", msg.getMaterialPlaceSource(), msg.getMaterialPlaceDestination());
logDataService.logInfo("TRACK","Received entry movement signal: from {} to {}", msg.getMaterialPlaceSource(), msg.getMaterialPlaceDestination());
logDataService.logInfo("TRACK","Received entry movement signal: from " + msg.getMaterialPlaceSource() + " to " + msg.getMaterialPlaceDestination());
opcReceiverHandler.onMessageReceived(OpcMessageType.ENTRY_MOVEMENT,msg);
} catch (Exception e) {
}
@@ -98,7 +98,7 @@ public class MessageSubscriptionRunner implements ApplicationRunner {
ExitCutMessage msg = new ExitCutMessage();
writeMessage(msg,exitCutIds);
log.info("接收到出口剪切信号:剪切类型 {},剪切长度{} ", msg.getCutType().toString(), msg.getCutLength());
logDataService.logInfo("TRACK","Received exit cut signal: cut type {}, cut length {}", msg.getCutType().toString(), msg.getCutLength());
logDataService.logInfo("TRACK","Received exit cut signal: cut type " + msg.getCutType().toString() + ", cut length " + msg.getCutLength());
opcReceiverHandler.onMessageReceived(OpcMessageType.EXIT_CUT,msg);
} catch (Exception e) {
}
@@ -110,7 +110,7 @@ public class MessageSubscriptionRunner implements ApplicationRunner {
ExitMovementMessage msg = new ExitMovementMessage();
writeMessage( msg,exitMoveIds);
log.info("接收出口移动信号:从 {} 移动到 {} ", msg.getExSrc(), msg.getExDesc());
logDataService.logInfo("TRACK","Received exit movement signal: from {} to {}", msg.getExSrc(), msg.getExDesc());
logDataService.logInfo("TRACK","Received exit movement signal: from " + msg.getExSrc() + " to " + msg.getExDesc());
opcReceiverHandler.onMessageReceived(OpcMessageType.EXIT_MOVEMENT,msg);
} catch (Exception e) {
}
@@ -122,7 +122,7 @@ public class MessageSubscriptionRunner implements ApplicationRunner {
ExitMeasureMessage msg = new ExitMeasureMessage();
writeMessage(msg,exitMeasureIds);
log.info("接收出口称重信号:重量 {} ", msg.getWeight());
logDataService.logInfo("TRACK","Received exit weight signal: weight {}", msg.getWeight());
logDataService.logInfo("TRACK","Received exit weight signal: weight " + msg.getWeight());
opcReceiverHandler.onMessageReceived(OpcMessageType.EXIT_MEASURE,msg);
} catch (Exception e) {
}

View File

@@ -4,6 +4,7 @@ import com.fizz.business.domain.msg.OpcMessage;
import com.fizz.business.domain.msg.PdiSetup;
import com.kangaroohy.milo.model.ReadWriteEntity;
import com.kangaroohy.milo.service.MiloService;
import com.fizz.business.service.LogDataService;
import lombok.extern.log4j.Log4j2;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;
@@ -25,6 +26,9 @@ public class OpcMessageSend {
@Resource
private MiloService miloService;
@Resource
private LogDataService logDataService;
private void sendPdiSetup(PdiSetup pdiSetup) {
try {
List<ReadWriteEntity> entities = getWriteEntities(pdiSetup, pdiSetupIds);
@@ -111,43 +115,6 @@ public class OpcMessageSend {
}
}
/**
* 批量写数据方法:通过字段名向多个点位写入数据
* @param fieldDataMap 字段名和值的映射key 为 fieldNamevalue 为要写入的值
* @return 是否全部写入成功
*/
public boolean batchWriteDataByFieldName(Map<String, Object> fieldDataMap) {
try {
if (fieldDataMap == null || fieldDataMap.isEmpty()) {
log.warn("批量写数据:数据映射为空,跳过写入");
return false;
}
List<ReadWriteEntity> entities = new ArrayList<>();
for (Map.Entry<String, Object> entry : fieldDataMap.entrySet()) {
String fieldName = entry.getKey();
String identifier = findIdentifierByFieldName(fieldName);
if (identifier == null) {
log.warn("未找到字段名对应的 OPC 节点路径fieldName={},跳过该字段", fieldName);
continue;
}
entities.add(ReadWriteEntity.builder()
.identifier(identifier)
.value(entry.getValue())
.build());
}
if (entities.isEmpty()) {
log.warn("批量写数据:没有有效的字段映射,跳过写入");
return false;
}
miloService.writeToOpcUa(entities);
log.info("批量写入 OPC 数据成功,共 {} 个点位", entities.size());
return true;
} catch (Exception e) {
log.error("批量写入 OPC 数据失败,原因:{}", e.getMessage(), e);
return false;
}
}
/**
* 批量写数据方法:向多个点位写入数据(直接使用节点路径)
* @param dataMap 点位标识符和值的映射key 为 identifiervalue 为要写入的值
@@ -155,10 +122,6 @@ public class OpcMessageSend {
*/
public boolean batchWriteData(Map<String, Object> dataMap) {
try {
if (dataMap == null || dataMap.isEmpty()) {
log.warn("批量写数据:数据映射为空,跳过写入");
return false;
}
List<ReadWriteEntity> entities = new ArrayList<>();
for (Map.Entry<String, Object> entry : dataMap.entrySet()) {
entities.add(ReadWriteEntity.builder()
@@ -167,10 +130,10 @@ public class OpcMessageSend {
.build());
}
miloService.writeToOpcUa(entities);
log.info("批量写入 OPC 数据成功,共 {} 个点位", entities.size());
logDataService.logInfo("WRITE","批量写入 OPC 数据成功,共"+ entities.size()+ "个点位");
return true;
} catch (Exception e) {
log.error("批量写入 OPC 数据失败,原因:{}", e.getMessage(), e);
logDataService.logInfo("WRITE","批量写入 OPC 数据失败,原因:"+ e.getMessage()+","+ e);
return false;
}
}

View File

@@ -14,38 +14,58 @@ import java.util.*;
@Getter
public enum DeviceEnum {
// === Entry section / 入口段 ===
POR1(0, "Uncoiler #1", 0.0, SectionType.ENTRY, SourceType.ENTRY, Arrays.asList("tensionPorBr1", "stripSpeed")),
POR2(1, "Uncoiler #2", 0.0, SectionType.ENTRY, SourceType.ENTRY, Arrays.asList("tensionPorBr2", "stripSpeed")),
POR1(0, "Uncoiler #1", 0.0, SectionType.ENTRY, SourceType.ENTRY, Arrays.asList("tensionPorBr1", "stripSpeed","tensionBr45Br6")),
POR2(1, "Uncoiler #2", 0.0, SectionType.ENTRY, SourceType.ENTRY, Arrays.asList("tensionPorBr2", "stripSpeed","tensionBr45Br6")),
WELDER(2, "Welder", 4.98, SectionType.ENTRY, SourceType.ENTRY, Arrays.asList("weldStatus")),
ENL1(3, "Entry Looper #1", 19.04, SectionType.PROCESS, SourceType.ENTRY, Arrays.asList("celLength", "celCapacity", "tensionCel")),
ENL2(4, "Entry Looper #2", 167.09, SectionType.PROCESS, SourceType.ENTRY, Arrays.asList("celLength", "celCapacity", "tensionCel")),
ENL3(5, "Entry Looper #3", 198.19, SectionType.PROCESS, SourceType.ENTRY, Arrays.asList("celLength", "celCapacity", "tensionCel")),
ENL1(3, "Entry Looper #1", 19.041, SectionType.PROCESS, SourceType.ENTRY, Arrays.asList("celLength", "celCapacity", "tensionCel")),
ENL2(4, "Entry Looper #2", 167.091, SectionType.PROCESS, SourceType.ENTRY, Arrays.asList("celLength", "celCapacity", "tensionCel")),
ENL3(5, "Entry Looper #3", 198.191, SectionType.PROCESS, SourceType.ENTRY, Arrays.asList("celLength", "celCapacity", "tensionCel","tensionBr45Br6")),
// === Process section / 工艺段 ===
CLEAN(6, "Cleaning Section", 264.803, SectionType.PROCESS, SourceType.FURNACE, Arrays.asList("cleaningVoltage", "cleaningCurrent", "alkaliConcentration", "alkaliTemperature")),
FUR1(7, "Annealing Furnace - Preheating", 302.837, SectionType.PROCESS, SourceType.FURNACE, Arrays.asList("phfExitStripTemp", "potTemperature", "gasConsumption")),
FUR2(8, "Annealing Furnace - Heating", 381.057, SectionType.PROCESS, SourceType.FURNACE, Arrays.asList("rtfExitStripTemp", "zincPotPower")),
FUR3(9, "Annealing Furnace - Cooling", 416.837, SectionType.PROCESS, SourceType.FURNACE, Arrays.asList("jcsExitStripTemp", "coolingTowerStripTemp")),
FUR4(10, "Annealing Furnace - Equalizing", 432.16, SectionType.PROCESS, SourceType.FURNACE, Arrays.asList("scsExitStripTemp")),
POT(11, "Zinc Pot", 442.994, SectionType.PROCESS, SourceType.COAT, Arrays.asList("scsExitStripTemp")),
TOWER(12, "Cooling Tower", 563.594, SectionType.PROCESS, SourceType.COAT, Arrays.asList("scsExitStripTemp")),
TM(13, "Temper Mill", 586.529, SectionType.PROCESS, SourceType.COAT, Arrays.asList("tensionBr5Tm", "stripSpeedTmExit")),
TL(14, "Tension Leveler", 612.909, SectionType.PROCESS, SourceType.COAT, Arrays.asList("tlElongation", "tensionTlBr7")),
COAT(15, "Post-treatment Section", 712.699, SectionType.PROCESS, SourceType.COAT, Arrays.asList(
FUR1(7, "Annealing Furnace - Preheating", 302.837, SectionType.PROCESS, SourceType.FURNACE, Arrays.asList(
"phFurnaceTemperatureActual",
"nof1FurnaceTemperatureActual", "nof2FurnaceTemperatureActual", "nof3FurnaceTemperatureActual", "nof4FurnaceTemperatureActual",
"nofPlateTemperatureActual",
"nofFurnacePressureActual"
)),
FUR2(8, "Annealing Furnace - Heating", 381.057, SectionType.PROCESS, SourceType.FURNACE, Arrays.asList(
"rtf1FurnaceTemperatureActual", "rtf2FurnaceTemperatureActual",
"rtfPlateTemperatureActual",
"rtfFurnacePressureActual"
)),
FUR3(9, "Annealing Furnace - Cooling", 398.947, SectionType.PROCESS, SourceType.FURNACE, Arrays.asList(
"jcf1FurnaceTemperatureActual",
"jcfFan1ActualSpeed", "jcfFan2ActualSpeed", "jcfFan3ActualSpeed", "jcfFan4ActualSpeed"
)),
FUR4(10, "Annealing Furnace - Equalizing", 414.27, SectionType.PROCESS, SourceType.FURNACE, Arrays.asList(
"lthFurnaceTemperatureActual",
"tdsFurnaceTemperatureActual",
"lbzFurnaceTemperatureActual",
"tdsPlateTemperatureActual",
"tdsFurnacePressureActual"
)),
POT(11, "Zinc Pot", 425.104, SectionType.PROCESS, SourceType.COAT, Arrays.asList("potTemperature")),
TOWER(12, "Cooling Tower", 545.659, SectionType.PROCESS, SourceType.COAT, Arrays.asList("coolingTowerTemperature")),
TM(13, "Temper Mill", 568.639, SectionType.PROCESS, SourceType.COAT, Arrays.asList("tensionBr5Tm", "stripSpeedTmExit")),
TL(14, "Tension Leveler", 595.019, SectionType.PROCESS, SourceType.COAT, Arrays.asList("tlElongation", "tensionTlBr7")),
COAT(15, "Post-treatment Section", 694.809, SectionType.PROCESS, SourceType.COAT, Arrays.asList(
"avrCoatingWeightTop","stdCoatingWeightTop","maxCoatingWeightTop","minCoatingWeightTop",
"avrCoatingWeightBottom","stdCoatingWeightBottom","maxCoatingWeightBottom","minCoatingWeightBottom",
"airKnifePressure","airKnifeFlow","airKnifeGap","stripSpeedTmExit","tensionBr8Tm",
"tensionTmBr9","tensionBr8Br9","tmMask","tmElongation","rollForceOperator","rollForceDrive",
"motorTorque","bendingForce","antiCrimpingRollMesh","billyRollMesh",
"tensionTlBr10Br11","tensionBr9toBr10Br11","tlFlag","tlElongation","levelingUnit1Mesh","levelingUnit2Mesh",
"antiCrossBowUnitMesh","tensionBr10Br11toBr12","stripSpeedAfp","stripTempAfp"
"antiCrossBowUnitMesh","tensionBr10Br11Br12","stripSpeedAfp","stripTempAfp",
"potTemperature","coolingTowerTemperature","potPower"
)),
// === Exit section / 出口段 ===
CXL1(16, "Exit Looper #1", 720.709, SectionType.EXIT, SourceType.EXIT, Arrays.asList("cxlLength", "cxlCapacity", "tensionCxl")),
CXL2(17, "Exit Looper #2", 888.789, SectionType.EXIT, SourceType.EXIT, Arrays.asList("cxlLength", "cxlCapacity", "tensionCxl")),
CXL1(16, "Exit Looper #1", 702.819, SectionType.EXIT, SourceType.EXIT, Arrays.asList("cxlLength", "cxlCapacity", "tensionCxl")),
CXL2(17, "Exit Looper #2", 870.899, SectionType.EXIT, SourceType.EXIT, Arrays.asList("cxlLength", "cxlCapacity", "tensionCxl")),
// INS(18, "Inspection Station", 940.561, SectionType.EXIT, SourceType.EXIT, Arrays.asList("inspectionStatus")),
TR(18, "Recoiler", 952.819, SectionType.EXIT, SourceType.EXIT, Arrays.asList("coilLength", "speedExitSection", "tensionBr9Tr")),
TR(18, "Recoiler", 934.929, SectionType.EXIT, SourceType.EXIT, Arrays.asList("coilLength", "speedExitSection", "tensionBr9Tr")),
EXC(19, "Coil Car", 9999999.0, SectionType.EXIT, SourceType.EXIT, Collections.emptyList()),
WEIGHT(20, "Weighing Saddle", 9999999.0, SectionType.EXIT, SourceType.EXIT, Collections.emptyList());

View File

@@ -10,6 +10,7 @@ import com.fizz.business.service.CrmPdiPlanService;
import com.fizz.business.comm.OPC.OpcMessageSend;
import com.fizz.business.service.client.RedisCacheManager;
import com.fizz.business.service.impl.BeanFactory;
import com.fizz.business.service.strip.SegmentTrackerService;
import com.fizz.business.utils.MatmapUtil;
import com.fizz.business.utils.WebSocketUtil;
import com.fizz.business.vo.CrmPdiPlanVO;
@@ -40,6 +41,14 @@ public enum L1OperateMatEnum implements IEnum<String>, IOperateMat<L1OperateMatF
MatmapUtil.setMatmap(form.getPorIdx(), form.getEntryMatId(), form.getPlanId(), form.getPlanNo());
// 钢卷上线时, 缓存工艺规程
// BeanFactory.getBean(RedisCacheManager.class).setCoilSetup(form.getPlanId());
// ONLINE 时同步下发入口参数 + 张力下一设定值
try {
BeanFactory.getBean(SegmentTrackerService.class)
.sendAllPdiOnOnline(form.getEntryMatId(), form.getPorIdx());
} catch (Exception e) {
BeanFactory.getBean(com.fizz.business.service.LogDataService.class)
.logWarn("L1-ONLINE", "ONLINE下发异常, coilId={}, porIdx={}, err={}", form.getEntryMatId(), form.getPorIdx(), e.toString());
}
WebSocketUtil.sendSignalMsg(form);
WebSocketUtil.sendMatmapMsg();
}
@@ -80,11 +89,11 @@ public enum L1OperateMatEnum implements IEnum<String>, IOperateMat<L1OperateMatF
PAY_OVER("甩尾") {
@Override
public void operate(L1OperateMatForm form) {
ArrayList<String> status = Lists.newArrayList(PlanStatusEnum.PRODUCING.name(), PlanStatusEnum.PRODUCT.name());
// ArrayList<String> status = Lists.newArrayList(PlanStatusEnum.PRODUCING.name(), PlanStatusEnum.PRODUCT.name());
CrmPdiPlanService planClient = BeanFactory.getBean(CrmPdiPlanService.class);
CrmPdiPlanVO plan = planClient.getByCoilIdAndOperId(form.getEntryMatId());
Assert.notNull(plan, "计划[{}]不存在", plan.getId());
Assert.isTrue(status.contains(plan.getStatus()), "当前状态[{}]不支持甩尾", plan.getStatus());
// Assert.isTrue(status.contains(plan.getStatus()), "当前状态[{}]不支持甩尾", plan.getStatus());
MatmapUtil.clearMatmap(form.getPorIdx());
WebSocketUtil.sendSignalMsg(form);
WebSocketUtil.sendMatmapMsg();
@@ -122,10 +131,14 @@ public enum L1OperateMatEnum implements IEnum<String>, IOperateMat<L1OperateMatF
private void syncPlanStatus(String planId, String matId) {
CrmPdiPlanService planClient = BeanFactory.getBean(CrmPdiPlanService.class);
planClient.changeStatus(ChangePlanStatusForm.builder()
boolean ok = planClient.changeStatus(ChangePlanStatusForm.builder()
.operation(this.name())
.id(planId)
.matId(matId)
.build());
if (!ok) {
throw new IllegalStateException(String.format("计划状态同步失败, operation=%s, planId=%s, matId=%s", this.name(), planId, matId));
}
}
}

View File

@@ -3,6 +3,8 @@ package com.fizz.business.controller;
import com.fizz.business.domain.ProStoppage;
import com.fizz.business.form.ProStoppageForm;
import com.fizz.business.service.ProStoppageService;
import com.fizz.business.service.ProStoppageTypeService;
import com.fizz.business.domain.vo.ProStoppageTypeVO;
import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.core.domain.R;
import io.swagger.v3.oas.annotations.Operation;
@@ -22,6 +24,9 @@ public class ProStoppageController {
@Resource
private ProStoppageService proStoppageService;
@Resource
private ProStoppageTypeService proStoppageTypeService;
// @PostMapping("/add")
// @ApiOperation("新增停机记录")
// public R<Boolean> add(@RequestBody ProStoppage proStoppage) {
@@ -51,4 +56,15 @@ public class ProStoppageController {
public R<List<Object>> calc(@RequestBody ProStoppageForm form) {
return R.ok(proStoppageService.calc(form));
}
@GetMapping("/types")
@Operation(summary = "查询停机类型")
public R<List<ProStoppageTypeVO>> types() {
return R.ok(proStoppageTypeService.listAll().stream().map(item -> {
ProStoppageTypeVO vo = new ProStoppageTypeVO();
vo.setStopType(item.getStopType());
vo.setRemark(item.getRemark());
return vo;
}).collect(java.util.stream.Collectors.toList()));
}
}

View File

@@ -0,0 +1,24 @@
package com.fizz.business.domain;
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 java.time.LocalDateTime;
@Data
@TableName("pro_stoppage_type")
public class ProStoppageType {
@TableId(value = "STOP_TYPE", type = IdType.INPUT)
private Integer stopType;
@TableField("REMARK")
private String remark;
@TableField("INSDATE")
private LocalDateTime insdate;
}

View File

@@ -32,6 +32,9 @@ public class AppMeasureEntryMessage extends OpcMessage {
@Schema(description = "钢带张力 BR2 BR3 (daN)")
private BigDecimal tensionBr2Br3;
@Schema(description = "钢带张力 BR4/5 BR6 (daN)")
private BigDecimal tensionBr45Br6;
@Schema(description = "钢带速度 (m/min)")
private BigDecimal stripSpeed;
@@ -74,6 +77,4 @@ public class AppMeasureEntryMessage extends OpcMessage {
@Schema(description = "热风压力 (Pa)")
private BigDecimal hotAirPressure;
@Schema(description = "钢带张力 BR4/5 BR6 (daN)")
private BigDecimal bR4or5toBR6Tension;
}

View File

@@ -120,6 +120,56 @@ public class PdiSetup extends OpcMessage{
@Schema(description = "BR9-TR 张力")
private BigDecimal tensionBR9TR;
// 新增的张力设定值字段
@Schema(description = "开卷机1当前张力")
private BigDecimal tensionPor1Current;
@Schema(description = "开卷机1下一设定张力")
private BigDecimal tensionPor1Next;
@Schema(description = "开卷机2当前张力")
private BigDecimal tensionPor2Current;
@Schema(description = "开卷机2下一设定张力")
private BigDecimal tensionPor2Next;
@Schema(description = "入口活套当前张力")
private BigDecimal tensionEnLpCurrent;
@Schema(description = "入口活套下一设定张力")
private BigDecimal tensionEnLpNext;
@Schema(description = "清洗段当前张力")
private BigDecimal tensionCleaningCurrent;
@Schema(description = "清洗段下一设定张力")
private BigDecimal tensionCleaningNext;
@Schema(description = "炉区当前张力")
private BigDecimal tensionFuranceCurrent;
@Schema(description = "炉区下一设定张力")
private BigDecimal tensionFuranceNext;
@Schema(description = "镀后冷却当前张力")
private BigDecimal tensionGalvanizingCoolCurrent;
@Schema(description = "镀后冷却下一设定张力")
private BigDecimal tensionGalvanizingCoolNext;
@Schema(description = "光整机入口当前张力")
private BigDecimal tensionTmEntryCurrent;
@Schema(description = "光整机入口下一设定张力")
private BigDecimal tensionTmEntryNext;
@Schema(description = "光整机出口当前张力")
private BigDecimal tensionTmExitCurrent;
@Schema(description = "光整机出口下一设定张力")
private BigDecimal tensionTmExitNext;
@Schema(description = "拉矫机当前张力")
private BigDecimal tensionTlCurrent;
@Schema(description = "拉矫机下一设定张力")
private BigDecimal tensionTlNext;
@Schema(description = "涂敷段当前张力")
private BigDecimal tensionCoatingCurrent;
@Schema(description = "涂敷段下一设定张力")
private BigDecimal tensionCoatingNext;
@Schema(description = "出口活套当前张力")
private BigDecimal tensionExLpCurrent;
@Schema(description = "出口活套下一设定张力")
private BigDecimal tensionExLpNext;
@Schema(description = "卷取机当前张力")
private BigDecimal tensionTrCurrent;
@Schema(description = "卷取机下一设定张力")
private BigDecimal tensionTrNext;
@Schema(description = "涂油投入")
private Integer oilingFlag;
@Schema(description = "上表面涂油量")

View File

@@ -0,0 +1,33 @@
package com.fizz.business.domain.s7;
import com.github.xingshuangs.iot.common.enums.EDataType;
import com.github.xingshuangs.iot.protocol.s7.serializer.S7Variable;
import lombok.Data;
@Data
public class DB35501Data {
@S7Variable(address = "DB35501.0", type = EDataType.STRING, count = 20)
private String coilId;
@S7Variable(address = "DB35501.30", type = EDataType.FLOAT32)
private float entryCoilWeight;
@S7Variable(address = "DB35501.34", type = EDataType.FLOAT32)
private float entryCoilLength;
@S7Variable(address = "DB35501.38", type = EDataType.FLOAT32)
private float entryCoilWidth;
@S7Variable(address = "DB35501.42", type = EDataType.FLOAT32)
private float entryCoilThick;
@S7Variable(address = "DB35501.46", type = EDataType.FLOAT32)
private float entryCoilInnerDia;
@S7Variable(address = "DB35501.50", type = EDataType.FLOAT32)
private float entryCoilOuterDia;
@S7Variable(address = "DB35501.54", type = EDataType.BYTE)
private byte alloyCode;
}

View File

@@ -0,0 +1,39 @@
package com.fizz.business.domain.s7;
import com.github.xingshuangs.iot.common.enums.EDataType;
import com.github.xingshuangs.iot.protocol.s7.serializer.S7Variable;
import lombok.Data;
@Data
public class DB35502Data {
@S7Variable(address = "DB35502.114", type = EDataType.FLOAT32)
private float tensionPor1Next;
@S7Variable(address = "DB35502.122", type = EDataType.FLOAT32)
private float tensionPor2Next;
@S7Variable(address = "DB35502.130", type = EDataType.FLOAT32)
private float tensionEnLpNext;
@S7Variable(address = "DB35502.138", type = EDataType.FLOAT32)
private float tensionCleaningNext;
@S7Variable(address = "DB35502.162", type = EDataType.FLOAT32)
private float tensionGalvanizingCoolNext;
@S7Variable(address = "DB35502.266", type = EDataType.FLOAT32)
private float tensionExLpNext;
@S7Variable(address = "DB35502.232", type = EDataType.FLOAT32)
private float tensionTlNext;
@S7Variable(address = "DB35502.198", type = EDataType.FLOAT32)
private float tensionTmExitNext;
@S7Variable(address = "DB35502.146", type = EDataType.FLOAT32)
private float tensionFuranceNext;
@S7Variable(address = "DB35502.274", type = EDataType.FLOAT32)
private float tensionTrNext;
}

View File

@@ -0,0 +1,12 @@
package com.fizz.business.domain.vo;
import lombok.Data;
@Data
public class ProStoppageTypeVO {
private Integer stopType;
private String remark;
}

View File

@@ -0,0 +1,8 @@
package com.fizz.business.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.fizz.business.domain.ProStoppageType;
public interface ProStoppageTypeMapper extends BaseMapper<ProStoppageType> {
}

View File

@@ -28,7 +28,7 @@ public interface CrmPdiPlanService extends IService<CrmPdiPlan> {
*/
CrmPdiPlan getFirstUnProducedCoil();
void changeStatus(ChangePlanStatusForm build);
boolean changeStatus(ChangePlanStatusForm build);
/**
* 获取当前正在线上的钢卷优先级ONLINE > PRODUCING > READY/NEW

View File

@@ -0,0 +1,12 @@
package com.fizz.business.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.fizz.business.domain.ProStoppageType;
import java.util.List;
public interface ProStoppageTypeService extends IService<ProStoppageType> {
List<ProStoppageType> listAll();
}

View File

@@ -9,6 +9,7 @@ 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.LogDataService;
import com.fizz.business.service.OpcMessageHandler;
import com.fizz.business.service.PdoExCoilService;
import com.fizz.business.service.TrackService;
@@ -32,13 +33,15 @@ public class ExitCutHandler implements OpcMessageHandler<ExitCutMessage> {
private final TrackService trackService;
@Resource
private OpcReceiverHandler opcReceiverHandler;
private final LogDataService logDataService;
@Override
public void handle(ExitCutMessage message) {
// 1. 获取卷取机 (TR) 上的物料信息
MatmapDTO trMatmap = MatmapUtil.getMatmap(TR.getIdx());
if (trMatmap == null) {
log.error("卷取机不存在");
com.fizz.business.service.impl.BeanFactory.getBean(com.fizz.business.service.LogDataService.class)
.logInfo("EXIT_CUT", "卷取机不存在");
return;
}
@@ -53,24 +56,17 @@ public class ExitCutHandler implements OpcMessageHandler<ExitCutMessage> {
pdoExCoilService.saveExCoil(exitCoilInfo);
if (exitCoilInfo.isLastFlag()) {
logDataService.logInfo("TRACK", "当前trMatmap的值为 -> matId=" + trMatmap.getMatId() + ", planId=" + trMatmap.getPlanId());
trackService.l1OperateMat(L1OperateMatForm.builder()
.trIdx(TR.getIdx())
.entryMatId(trMatmap.getMatId())
.planId(trMatmap.getPlanId())
.entryMatId(exitCoilInfo.getEntryMatId())
.planId(exitCoilInfo.getPlanId())
.operation(L1OperateMatEnum.PRODUCT)
.build());
// wangyu 配合二级发一个甩尾信号 01全甩一下
EntryMovementMessage msg = new EntryMovementMessage();
msg.setCounter(message.getCounter());
msg.setMaterialPlaceSource(0);
msg.setMaterialPlaceDestination(200);
opcReceiverHandler.onMessageReceived(OpcMessageType.ENTRY_MOVEMENT,msg);
msg.setMaterialPlaceSource(1);
opcReceiverHandler.onMessageReceived(OpcMessageType.ENTRY_MOVEMENT,msg);
logDataService.logInfo("EXIT_CUT", "成功处理 EXIT_CUT 消息,已保存成品卷 -> exitMatId=" + exitCoilInfo.getExitMatId() + ", planId=" + exitCoilInfo.getPlanId() + ", entryMatId=" + trMatmap.getMatId());
}
log.info("成功处理 EXIT_CUT 消息,已保存成品卷: " + exitCoilInfo.getExitMatId());
}
}

View File

@@ -7,20 +7,26 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fizz.business.domain.CrmPdiPlan;
import com.fizz.business.form.ChangePlanStatusForm;
import com.fizz.business.constants.enums.PlanStatusEnum;
import com.fizz.business.form.PlanQueryForm;
import com.fizz.business.mapper.CrmPdiPlanMapper;
import com.fizz.business.service.CrmPdiPlanService;
import com.fizz.business.service.LogDataService;
import com.fizz.business.vo.CrmPdiPlanVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Objects;
@Slf4j
@Service
public class CrmPdiPlanServiceImpl extends ServiceImpl<CrmPdiPlanMapper, CrmPdiPlan> implements CrmPdiPlanService {
@javax.annotation.Resource
private LogDataService logDataService;
/**
* 根据卷ID和操作员ID查询单个记录
@@ -150,23 +156,61 @@ public class CrmPdiPlanServiceImpl extends ServiceImpl<CrmPdiPlanMapper, CrmPdiP
}
@Override
public void changeStatus(ChangePlanStatusForm build) {
public boolean changeStatus(ChangePlanStatusForm build) {
if (build == null) {
if (logDataService != null) {
logDataService.logInfo("PLAN", "changeStatus 参数为空");
}
return false;
}
QueryWrapper<CrmPdiPlan> wrapper = new QueryWrapper<>();
if (StrUtil.isBlank(build.getMatId())) {
logDataService.logInfo("PLAN", "changeStatus matId为空, planId=" + build.getId() + ", operation=" + build.getOperation());
return false;
}
wrapper.eq("coilid", build.getMatId());
wrapper.eq("planid", build.getId());
// planId 非空时matId + planId 精确匹配
// planId 为空时:仅用 matId 匹配coilid 唯一)
if (StrUtil.isNotBlank(build.getId())) {
wrapper.eq("planid", build.getId());
}
CrmPdiPlan pdiPlan = baseMapper.selectOne(wrapper);
if (pdiPlan == null) {
log.error("未找到ID为{}的计划记录", build.getId());
return;
logDataService.logInfo("PLAN", "未找到计划记录, matId=" + build.getMatId() + ", planId=" + build.getId() + ", operation=" + build.getOperation());
return false;
}
pdiPlan.setStatus(build.getOperation());
String oldStatus = pdiPlan.getStatus();
String newStatus = build.getOperation();
baseMapper.updateById(pdiPlan);
log.info("计划状态更新成功ID: {}, 新状态: {}", build.getId(), build.getOperation());
// 状态机保护:禁止已完成(PRODUCT)的计划回退到生产中(PRODUCING)
if (PlanStatusEnum.PRODUCT.name().equals(oldStatus) && PlanStatusEnum.PRODUCING.name().equals(newStatus)) {
logDataService.logWarn("PLAN", "拦截计划状态回退: matId=" + build.getMatId() + ", planId=" + build.getId() + ", " + oldStatus + " -> " + newStatus);
return true;
}
// 幂等:同状态重复更新直接放行(避免重复写库导致的误判)
if (Objects.equals(oldStatus, newStatus)) {
return true;
}
pdiPlan.setStatus(newStatus);
int rows = baseMapper.updateById(pdiPlan);
if (rows <= 0) {
logDataService.logInfo("PLAN", "计划状态更新失败, matId=" + build.getMatId() + ", planId=" + build.getId() + ", operation=" + newStatus);
return false;
}
logDataService.logInfo("PLAN", "计划状态更新成功, matId=" + build.getMatId() + ", planId=" + build.getId() + ", {} -> {}", oldStatus, newStatus);
return true;
}
/**

View File

@@ -77,9 +77,6 @@ public class LogDataServiceImpl extends ServiceImpl<LogDataMapper, LogData> impl
@Override
public void logInfo(String module, String content, Object... args) {
try {
if (args.length > 0) {
content = formatMessage(content, args);
}
saveLogData("INFORMATION", module, content);
} catch (Exception e) {
log.error("没存进去,没事,{}",e);
@@ -88,9 +85,6 @@ public class LogDataServiceImpl extends ServiceImpl<LogDataMapper, LogData> impl
@Override
public void logWarn(String module, String content, Object... args) {
if (args.length > 0) {
content = formatMessage(content, args);
}
saveLogData("WARN", module, content);
}
@@ -105,34 +99,4 @@ public class LogDataServiceImpl extends ServiceImpl<LogDataMapper, LogData> impl
save(logData);
}
private String formatMessage(String pattern, Object[] arguments) {
if (pattern == null) {
return null;
}
if (arguments == null || arguments.length == 0) {
return pattern;
}
StringBuilder sb = new StringBuilder();
int argIndex = 0;
int i = 0;
while (i < pattern.length()) {
char c = pattern.charAt(i);
if (c == '{' && i + 1 < pattern.length() && pattern.charAt(i + 1) == '}') {
if (argIndex < arguments.length) {
sb.append(String.valueOf(arguments[argIndex++]));
} else {
sb.append("{}");
}
i += 2;
} else {
sb.append(c);
i++;
}
}
return sb.toString();
}
}

View File

@@ -3,6 +3,7 @@ package com.fizz.business.service.impl;
import com.fizz.business.comm.OPC.OpcMessageSend;
import com.fizz.business.form.OpcBatchWriteDataForm;
import com.fizz.business.form.OpcWriteDataForm;
import com.fizz.business.service.LogDataService;
import com.fizz.business.service.OpcDataService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -22,6 +23,7 @@ import java.util.stream.Collectors;
public class OpcDataServiceImpl implements OpcDataService {
private final OpcMessageSend opcMessageSend;
private final LogDataService logDataService;
@Override
public boolean writeData(OpcWriteDataForm form) {
@@ -36,13 +38,44 @@ public class OpcDataServiceImpl implements OpcDataService {
@Override
public boolean batchWriteData(OpcBatchWriteDataForm form) {
try {
int rawSize = form.getDataList().size();
int nullItemCount = 0;
int nullFieldNameCount = 0;
int nullValueCount = 0;
StringBuilder nullValueFields = new StringBuilder();
for (int i = 0; i < form.getDataList().size(); i++) {
OpcWriteDataForm d = form.getDataList().get(i);
if (d == null) {
nullItemCount++;
continue;
}
if (d.getFieldName() == null) {
nullFieldNameCount++;
continue;
}
if (d.getValue() == null) {
nullValueCount++;
if (nullValueFields.length() < 1500) {
if (nullValueFields.length() > 0) {
nullValueFields.append(",");
}
nullValueFields.append(d.getFieldName());
}
}
}
Map<String, Object> fieldDataMap = form.getDataList().stream()
.filter(d -> d != null && d.getFieldName() != null)
.collect(Collectors.toMap(
OpcWriteDataForm::getFieldName,
OpcWriteDataForm::getValue,
(v1, v2) -> v2 // 如果有重复的 key保留后面的值
));
return opcMessageSend.batchWriteDataByFieldName(fieldDataMap);
return opcMessageSend.batchWriteData(fieldDataMap);
} catch (Exception e) {
log.error("批量写入 OPC 数据异常", e);
return false;

View File

@@ -45,7 +45,7 @@ public class ProStoppageServiceImpl extends ServiceImpl<ProStoppageMapper, ProSt
@Override
public boolean deleteProStoppage(Long stopid) {
return this.deleteProStoppage(stopid);
return this.removeById(stopid);
}
@Override

View File

@@ -0,0 +1,19 @@
package com.fizz.business.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fizz.business.domain.ProStoppageType;
import com.fizz.business.mapper.ProStoppageTypeMapper;
import com.fizz.business.service.ProStoppageTypeService;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class ProStoppageTypeServiceImpl extends ServiceImpl<ProStoppageTypeMapper, ProStoppageType> implements ProStoppageTypeService {
@Override
public List<ProStoppageType> listAll() {
return baseMapper.selectList(null);
}
}

View File

@@ -14,6 +14,7 @@ import com.fizz.business.form.L1OperateMatForm;
import com.fizz.business.form.WebOperateMatForm;
import com.fizz.business.service.CrmPdiPlanService;
import com.fizz.business.comm.OPC.OpcMessageSend;
import com.fizz.business.service.strip.SegmentTrackerService;
import com.fizz.business.service.ProMatmapService;
import com.fizz.business.service.TrackService;
import com.fizz.business.service.client.RedisCacheManager;
@@ -64,6 +65,10 @@ public class TrackServiceImpl implements TrackService {
proMatmapService.flushMatmap();
}
@Autowired
@org.springframework.context.annotation.Lazy
private SegmentTrackerService segmentTrackerService;
@Override
public void l1OperateMat(L1OperateMatForm form) {

View File

@@ -114,9 +114,10 @@ public class OpcMessageIdsManager {
entryLineMeasureIds.put("ns=2;s=ProcessCGL.PLCLine.EntryLineMeasure.rinseConductivity","rinseConductivity");
entryLineMeasureIds.put("ns=2;s=ProcessCGL.PLCLine.EntryLineMeasure.rinseTemperature","rinseTemperature");
entryLineMeasureIds.put("ns=2;s=ProcessCGL.PLCLine.EntryLineMeasure.dryingTemperature","dryingTemperature");
entryLineMeasureIds.put("ns=2;s=ProcessCGL.PLCLine.EntryLineMeasure.BR4or5toBR6Tension","tensionBr45Br6");
entryLineMeasureIds.put("ns=2;s=ProcessCGL.PLCLine.EntryLineMeasure.hotAirFlow","hotAirFlow");
entryLineMeasureIds.put("ns=2;s=ProcessCGL.PLCLine.EntryLineMeasure.hotAirPressure","hotAirPressure");
entryLineMeasureIds.put("ns=2;s=ProcessCGL.PLCLine.EntryLineMeasure.BR4or5toBR6Tension","bR4or5toBR6Tension");
}
public static void loadProcLineMeasureIds(){
@@ -406,6 +407,34 @@ public class OpcMessageIdsManager {
pdiSetupIds.put("ns=2;s=ProcessCGL.PLCLine.L2Setup.exitLength4", "exitLength4");
pdiSetupIds.put("ns=2;s=ProcessCGL.PLCLine.L2Setup.exitLength5", "exitLength5");
pdiSetupIds.put("ns=2;s=ProcessCGL.PLCLine.L2Setup.exitLength6", "exitLength6");
// 张力设定值LepServer TagName按 DB 地址与工艺语义对齐)
// DB35502 段Current/Next
pdiSetupIds.put("ns=2;s=ProcessCGL.PLCLine.L2Setup.1kjjtensionsetting", "tensionPor1Current");
pdiSetupIds.put("ns=2;s=ProcessCGL.PLCLine.L2Setup.1kjjtensionsettingnext", "tensionPor1Next");
pdiSetupIds.put("ns=2;s=ProcessCGL.PLCLine.L2Setup.2kjjtensionsetting", "tensionPor2Current");
pdiSetupIds.put("ns=2;s=ProcessCGL.PLCLine.L2Setup.2kjjtensionsettingnext", "tensionPor2Next");
pdiSetupIds.put("ns=2;s=ProcessCGL.PLCLine.L2Setup.rkhttensionsetting", "tensionEnLpCurrent");
pdiSetupIds.put("ns=2;s=ProcessCGL.PLCLine.L2Setup.rkhttensionsettingnext", "tensionEnLpNext");
pdiSetupIds.put("ns=2;s=ProcessCGL.PLCLine.L2Setup.qxdtensionsetting", "tensionCleaningCurrent");
pdiSetupIds.put("ns=2;s=ProcessCGL.PLCLine.L2Setup.qxdtensionsettingnext", "tensionCleaningNext");
pdiSetupIds.put("ns=2;s=ProcessCGL.PLCLine.L2Setup.thltensionsetting", "tensionFuranceCurrent");
pdiSetupIds.put("ns=2;s=ProcessCGL.PLCLine.L2Setup.thltensionsettingnext", "tensionFuranceNext");
pdiSetupIds.put("ns=2;s=ProcessCGL.PLCLine.L2Setup.dhlqdtensionsetting", "tensionGalvanizingCoolCurrent");
pdiSetupIds.put("ns=2;s=ProcessCGL.PLCLine.L2Setup.dhlqdtensionsettingnext", "tensionGalvanizingCoolNext");
pdiSetupIds.put("ns=2;s=ProcessCGL.PLCLine.L2Setup.gzjrktensionsetting", "tensionTmEntryCurrent");
pdiSetupIds.put("ns=2;s=ProcessCGL.PLCLine.L2Setup.gzjrktensionsettingnext", "tensionTmEntryNext");
pdiSetupIds.put("ns=2;s=ProcessCGL.PLCLine.L2Setup.gzjcktensionsetting", "tensionTmExitCurrent");
pdiSetupIds.put("ns=2;s=ProcessCGL.PLCLine.L2Setup.gzjcktensionsettingnext", "tensionTmExitNext");
pdiSetupIds.put("ns=2;s=ProcessCGL.PLCLine.L2Setup.ljjtensionsetting", "tensionTlCurrent");
pdiSetupIds.put("ns=2;s=ProcessCGL.PLCLine.L2Setup.ljjtensionsettingnext", "tensionTlNext");
pdiSetupIds.put("ns=2;s=ProcessCGL.PLCLine.L2Setup.dhdtensionsetting", "tensionCoatingCurrent");
pdiSetupIds.put("ns=2;s=ProcessCGL.PLCLine.L2Setup.dhdtensionsettingnext", "tensionCoatingNext");
pdiSetupIds.put("ns=2;s=ProcessCGL.PLCLine.L2Setup.ckhttensionsetting", "tensionExLpCurrent");
pdiSetupIds.put("ns=2;s=ProcessCGL.PLCLine.L2Setup.ckhttensionsettingnext", "tensionExLpNext");
pdiSetupIds.put("ns=2;s=ProcessCGL.PLCLine.L2Setup.sjjtensionsetting", "tensionTrCurrent");
pdiSetupIds.put("ns=2;s=ProcessCGL.PLCLine.L2Setup.sjjtensionsettingnext", "tensionTrNext");
// DB35501 段:原有张力字段(保持兼容)
pdiSetupIds.put("ns=2;s=ProcessCGL.PLCLine.L2Setup.tensionPorBR1", "tensionPorBR1");
pdiSetupIds.put("ns=2;s=ProcessCGL.PLCLine.L2Setup.tensionBR1BR2", "tensionBR1BR2");
pdiSetupIds.put("ns=2;s=ProcessCGL.PLCLine.L2Setup.tensionBR2BR3", "tensionBR2BR3");

View File

@@ -3,6 +3,7 @@ package com.fizz.business.service.strip;
import cn.hutool.json.JSONUtil;
import com.fizz.business.constants.enums.DeviceEnum;
import com.fizz.business.constants.enums.L1OperateMatEnum;
import com.fizz.business.domain.PdiSetups;
import com.fizz.business.domain.msg.AppMeasureCoatMessage;
import com.fizz.business.domain.msg.AppMeasureEntryMessage;
import com.fizz.business.domain.msg.AppMeasureExitMessage;
@@ -10,18 +11,27 @@ 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.domain.s7.DB35501Data;
import com.fizz.business.domain.s7.DB35502Data;
import com.fizz.business.form.L1OperateMatForm;
import com.fizz.business.service.CrmPdiPlanService;
import com.fizz.business.service.IPdiSetupService;
import com.fizz.business.service.LogDataService;
import com.fizz.business.service.ProMatmapService;
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 com.fizz.business.vo.CrmPdiPlanVO;
import com.github.xingshuangs.iot.protocol.s7.enums.EPlcType;
import com.github.xingshuangs.iot.protocol.s7.service.S7PLC;
import com.github.xingshuangs.iot.protocol.s7.serializer.S7Serializer;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.lang.reflect.Method;
import java.lang.reflect.Field;
import java.math.BigDecimal;
@@ -42,6 +52,40 @@ public class SegmentTrackerService {
private final LogDataService logDataService; // 注入新创建的段服务
private final ProMatmapService proMatmapService; // 注入新创建的段服务
private final CrmPdiPlanService crmPdiPlanService;
private final IPdiSetupService pdiSetupService;
private static final String PLC_IP = "192.168.0.223";
private static final int COIL_ID_MAX_LEN = 20;
private S7PLC s7PLC;
private S7Serializer s7Serializer;
@PostConstruct
public void initS7() {
// PLC机型是1500IP 192.168.0.223rack/slot 默认
this.s7PLC = new S7PLC(EPlcType.S1500, PLC_IP);
this.s7Serializer = S7Serializer.newInstance(s7PLC);
}
private String trimToLen(String s, int maxLen) {
if (s == null) {
return null;
}
return s.length() <= maxLen ? s : s.substring(0, maxLen);
}
private float toFloat(BigDecimal v) {
return v == null ? 0.0f : v.floatValue();
}
private byte toByte(String s) {
if (s == null || s.isEmpty()) {
return 0;
}
return (byte) s.charAt(0);
}
private static final long intervalTime = 10000; // 每 10 秒写一次
private int traceCount = 0;
private boolean firstMeasure = true;
@@ -79,7 +123,7 @@ public class SegmentTrackerService {
try {
trackCoilHeadPosition(coilId, entryLengthAtWelder, entry, exit);
} catch (Exception e) {
logDataService.logInfo("ERROR","coidId="+coilId+", e="+ e);
logDataService.logInfo("TRACK", "trackCoilHeadPosition异常, coilId=" + coilId + ", err=" + String.valueOf(e));
}
Long lastTime = lastLogTimeMap.get(coilId);
@@ -98,13 +142,14 @@ public class SegmentTrackerService {
firstMeasure = false;
listSegment.clear();
coilReachedDevices.remove(coilId);
coilReachedDevices.remove(coilId + "#POR1");
coilReachedDevices.remove(coilId + "#POR2");
long now1 = System.currentTimeMillis();
Long lastTime1 = lastNewCoilLogTimeMap.get(coilId);
if (lastTime1 == null || now1 - lastTime1 > intervalTime) {
lastNewCoilLogTimeMap.put(coilId, now1);
log.info("WELDER检测到新钢卷或初始化 -> CoilId: {}", coilId);
logDataService.logInfo("WELDER", "Detected new coil or initialized -> CoilId: " + coilId);
logDataService.logInfo("WELDER", "检测到新钢卷或初始化, CoilId=" + coilId);
}
} else {
weldDev = entryLengthAtWelder.subtract(weldLength);
@@ -127,8 +172,7 @@ public class SegmentTrackerService {
// 5. 完成日志
log.info("TRACK-END】CoilId: {}, 当前长度: {}, 已生成段数: {}", coilId, entryLengthAtWelder, coilSegNum);
logDataService.logInfo("TRACK", "Process completed -> CoilId: " + coilId + ", CurrentLength: " + entryLengthAtWelder + ", SegmentCount: " + coilSegNum);
logDataService.logInfo("TRACK", "处理完成, CoilId=" + coilId + ", 当前长度=" + entryLengthAtWelder + ", 已生成段数=" + coilSegNum);
}
@@ -144,7 +188,7 @@ public class SegmentTrackerService {
BigDecimal weldDev) {
if (listSegment.isEmpty()) {
// === 【处理开始日志】===
log.warn("TRACK-TREAT】,listSegment为空直接返回");
logDataService.logWarn("TRACK", "treatSeg: listSegment为空直接返回, coilSegNum=" + coilSegNum);
return;
}
@@ -152,9 +196,10 @@ public class SegmentTrackerService {
BigDecimal celLength = entry != null ? entry.getCelLength() : BigDecimal.ZERO;
BigDecimal cxlLength = exit != null ? exit.getCxlLength() : BigDecimal.ZERO;
// payOffReelNumber=1/2 代表当前跟踪的是哪一路开卷机入口。
// 这里做保护,避免 entry 为空或值异常导致误写 matmap。
// === 【处理开始日志】===
log.info("TRACK-TREAT】开始处理段数据共 {} 段weldDev={}celLength = {}cxlLength = {}。 ", listSegment.size(), weldDev, celLength, cxlLength);
logDataService.logInfo("TRACK", "Start processing segments: count=" + listSegment.size() + ", weldDev=" + weldDev);
logDataService.logInfo("TRACK", "开始处理段数据, count=" + listSegment.size() + ", weldDev=" + weldDev + ", celLength=" + celLength + ", cxlLength=" + cxlLength);
Iterator<SegmentDTO> iterator = listSegment.descendingIterator();
while (iterator.hasNext()) {
@@ -180,14 +225,12 @@ public class SegmentTrackerService {
if (segment.getHeadPos().compareTo(BigDecimal.valueOf(currentDevicePos)) > 0 &&
segment.getTailPos().compareTo(BigDecimal.valueOf(currentDevicePos)) < 0) {
log.info("【TRACK-TREAT】段 {} 进入设备区域 [{}],当前设备长度:{}", segment.getSegNo(), device.name(), currentDevicePos);
logDataService.logInfo("TRACK",
"Segment " + segment.getSegNo() + " entered device area [" + device.name() + "], devicePos=" + currentDevicePos);
"段进入设备区域, segNo={}, device={}, devicePos={}", segment.getSegNo(), device.name(), currentDevicePos);
double currentSpeed = getSpeedForDevice(device, entry, coat, exit);
if (currentSpeed > LOWSPEEDLIMIT) {
log.info("【TRACK-TREAT】段 {} 速度大于基准速度, 进入设备区域 [{}],当前速度:{}", segment.getSegNo(), device.name(), currentSpeed);
logDataService.logInfo("TRACK",
"Segment " + segment.getSegNo() + " entered device area [" + device.name() + "], speed=" + currentSpeed);
"段速度满足条件并进入设备区域, segNo={}, device={}, speed={}", segment.getSegNo(), device.name(), currentSpeed);
for (String fieldName : device.getParamFields()) {
Object message = getMessageForDevice(device, entry, furnace, coat, exit);
if (message != null) {
@@ -202,10 +245,9 @@ public class SegmentTrackerService {
double exitPlantPos = stripPositionService.calculate(DeviceEnum.TR, celLength, cxlLength);
if (segment.getTailPos().compareTo(BigDecimal.valueOf(exitPlantPos)) >= 0) {
log.info("【TRACK-END】钢卷 {} 的段号 {} 已离开产线,开始持久化数据{}。",
segment.getEnCoilID(), segment.getSegNo(), JSONUtil.toJsonStr(segment.getTotalValues()));
logDataService.logInfo("SEGMENT",
"Coil " + segment.getEnCoilID() + " segment " + segment.getSegNo() + " left the line, start persisting data.");
"段离开产线并开始持久化, coilId={}, segNo={}, values={}",
segment.getEnCoilID(), segment.getSegNo(), JSONUtil.toJsonStr(segment.getTotalValues()));
segmentService.saveTotalSegment(segment); // 调用服务进行持久化
iterator.remove();
}
@@ -364,14 +406,20 @@ public class SegmentTrackerService {
public void trackCoilHeadPosition(String coilId, BigDecimal headPos,
AppMeasureEntryMessage entry, AppMeasureExitMessage exit) {
log.warn(">>> trackCoilHeadPosition 当前线程{}", Thread.currentThread().getName());
logDataService.logInfo("MATMAP-TRACK", "trackCoilHeadPosition 当前线程=" + Thread.currentThread().getName());
if (LogRateLimiter.shouldLog("TRACK:" + coilId, 5000)) {
log.info("焊缝位置匹配逻辑,当前焊缝长度{}", headPos);
logDataService.logInfo("MATMAP-TRACK", "CoilId=" + coilId + "Weld position matching logic, current weld length ="+headPos);
logDataService.logInfo("MATMAP-TRACK", "焊缝位置匹配逻辑, coilId=" + coilId + ", weldLen=" + headPos);
}
Set<DeviceEnum> prevReached = coilReachedDevices.computeIfAbsent(coilId,
Integer payOffReelNumber = entry != null ? entry.getPayOffReelNumber() : null;
if (payOffReelNumber == null || payOffReelNumber < 1) {
logDataService.logWarn("MATMAP-TRACK", "trackCoilHeadPosition: invalid payOffReelNumber=" + payOffReelNumber + ", coilId=" + coilId);
return;
}
String reachKey = coilId + "#POR" + payOffReelNumber;
Set<DeviceEnum> prevReached = coilReachedDevices.computeIfAbsent(reachKey,
k -> Collections.newSetFromMap(new ConcurrentHashMap<>()));
if (LogRateLimiter.shouldLog("TRACK:" + coilId, 5000)) {
@@ -383,19 +431,50 @@ public class SegmentTrackerService {
for (DeviceEnum d : DeviceEnum.values()) {
double dynPos = stripPositionService.calculate(d, celLength, cxlLength);
log.info("焊缝位置匹配逻辑,当前焊缝长度{},当前计算的设备长度:{}", headPos, dynPos);
if (LogRateLimiter.shouldLog("TRACK:DYNPOS:" + coilId, 5000)) {
logDataService.logInfo("MATMAP-TRACK", "焊缝位置匹配, coilId=" + coilId + ", headPos=" + headPos + ", device=" + d.name() + ", dynPos=" + dynPos);
}
if (headPos.compareTo(BigDecimal.valueOf(dynPos)) >= 0 && !prevReached.contains(d)) {
boolean positionReached = headPos.compareTo(BigDecimal.valueOf(dynPos)) >= 0;
MatmapDTO matmap = MatmapUtil.getMatmap(entry.getPayOffReelNumber());
MatmapUtil.setMatId(d.getIdx(), coilId,matmap.getPlanId());
// --- 针对大辊缝时 stripLocation 更新延迟的补偿逻辑 ---
// 只要入口段速度 > 阈值,就认为已经开始进入生产状态,可以触发 WELDER 到达
boolean welderIsMoving = false;
if (d == DeviceEnum.WELDER && entry != null && entry.getStripSpeed() != null) {
if (entry.getStripSpeed().doubleValue() > LOWSPEEDLIMIT) {
welderIsMoving = true;
logDataService.logInfo("MATMAP-TRACK", "WELDER速度触发状态变更, coilId=" + coilId + ", speed=" + entry.getStripSpeed().doubleValue() + ", limit=" + LOWSPEEDLIMIT);
}
}
if ((positionReached || welderIsMoving) && !prevReached.contains(d)) {
MatmapDTO matmap = MatmapUtil.getMatmap(payOffReelNumber - 1);
// 关键点POR1/POR2 是两路入口,各自有独立的 matmap 槽位。
// 设备到达时只更新该入口对应的设备槽位,避免把另一入口(另一开卷机/活套)覆盖。
int targetIdx = d.getIdx();
if (payOffReelNumber == 1) {
// 仅在开卷机位置做 1/2 路隔离。
// 入口/出口活套已合并为单套ENL1/2/3、CXL1/2 为同一活套内不同测点,不再按入口分路。
if (d == DeviceEnum.POR2) {
continue;
}
} else if (payOffReelNumber == 2) {
if (d == DeviceEnum.POR1) {
continue;
}
}
MatmapUtil.setMatId(targetIdx, coilId, matmap.getPlanId());
if (d == DeviceEnum.WELDER) {
logDataService.logInfo("MATMAP-TRACK", "Coil reached welder, update plan status. coil=" + coilId + ", headPos=" + headPos);
trackService.l1OperateMat(L1OperateMatForm.builder()
.entryMatId(coilId)
.planId(matmap.getPlanId())
.porIdx(entry.getPayOffReelNumber())
.porIdx(entry.getPayOffReelNumber() - 1)
.operation(L1OperateMatEnum.PRODUCING)
.build());
} else {
@@ -410,6 +489,65 @@ public class SegmentTrackerService {
}
private PdiSetups loadPdiSetup(String coilId, String planId) {
if (planId == null) {
return null;
}
com.fizz.business.domain.PdiSetups query = new com.fizz.business.domain.PdiSetups();
query.setCoilid(coilId);
query.setPlanid(planId);
List<com.fizz.business.domain.PdiSetups> setups = pdiSetupService.selectPdiSetupList(query);
return (setups != null && !setups.isEmpty()) ? setups.get(0) : null;
}
/**
* NEW/READY -> ONLINE入口卷参数 + 全线张力“下一设定值”(Next) 一次性下发 (通过 S7 协议直连 PLC)
*/
public void sendAllPdiOnOnline(String coilId, Integer porIdx) {
try {
CrmPdiPlanVO plan = crmPdiPlanService.getByCoilIdAndOperId(coilId);
PdiSetups setup = loadPdiSetup(coilId, plan.getPlanid());
// 1. 组装 DB35501 数据 (入口卷参数)
DB35501Data d35501 = new DB35501Data();
d35501.setCoilId(trimToLen(coilId, COIL_ID_MAX_LEN));
d35501.setEntryCoilWeight(toFloat(plan.getEntryWeight()));
d35501.setEntryCoilLength(toFloat(plan.getEntryLength()));
d35501.setEntryCoilWidth(toFloat(plan.getEntryWidth()));
d35501.setEntryCoilThick(toFloat(plan.getEntryThick()));
d35501.setEntryCoilInnerDia(toFloat(plan.getEntryInnerDiameter()));
d35501.setEntryCoilOuterDia(toFloat(plan.getEntryOuterDiameter()));
d35501.setAlloyCode(toByte(plan.getSteelGrade()));
// 2. 组装 DB35502 数据 (全线张力 Next)
DB35502Data d35502 = new DB35502Data();
if (setup != null) {
if (porIdx != null && porIdx == 0) {
d35502.setTensionPor1Next(toFloat(setup.getPorTension()));
} else if (porIdx != null && porIdx == 1) {
d35502.setTensionPor2Next(toFloat(setup.getPorTension()));
}
d35502.setTensionEnLpNext(toFloat(setup.getCelTension()));
d35502.setTensionCleaningNext(toFloat(setup.getCleanTension()));
d35502.setTensionGalvanizingCoolNext(toFloat(setup.getPassivationTension()));
d35502.setTensionExLpNext(toFloat(setup.getCxlTension()));
d35502.setTensionTlNext(toFloat(setup.getLevelerEntryTension()));
d35502.setTensionTmExitNext(toFloat(setup.getStraightenerExitTension()));
d35502.setTensionFuranceNext(toFloat(setup.getFurTension()));
d35502.setTensionTrNext(toFloat(setup.getTrTension()));
}
// 3. 执行 S7 序列化写入
s7Serializer.write(d35501);
s7Serializer.write(d35502);
logDataService.logInfo("MATMAP-TRACK", "ONLINE S7下发完成(入口参数+张力Next), coilId=" + coilId + ", PLC=" + PLC_IP);
} catch (Exception e) {
log.error("S7下发异常: {}", e.getMessage(), e);
logDataService.logInfo("MATMAP-TRACK", "ONLINE S7下发失败, coilId=" + coilId + ", err=" + e.getMessage());
}
}
}

View File

@@ -6,7 +6,6 @@ import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.fizz.business.constants.CommonConstants;
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.DeviceChartDataDTO;
@@ -46,11 +45,13 @@ public class WebSocketUtil {
Map<String, WebSocketSession> clients = trackWsHandler.getClients().getOrDefault(type.name(), Maps.newConcurrentMap());
TextMessage message = new TextMessage(text);
clients.values().forEach(s -> {
try {
s.sendMessage(message);
synchronized (s) {
try {
s.sendMessage(message);
// log.info("[websocket]向客户端[{}]推送消息:{}", type + "-" + s.getId(), text);
} catch (IOException e) {
log.error("[websocket]向客户端[{}]推送消息异常:{}", type + "-" + s.getId(), text);
} catch (IOException e) {
log.error("[websocket]向客户端[{}]推送消息异常:{}", type + "-" + s.getId(), text);
}
}
});
}