diff --git a/src/main/java/com/iflytop/gd/app/controller/CraftsController.java b/src/main/java/com/iflytop/gd/app/controller/CraftsController.java index 951883a..7022339 100644 --- a/src/main/java/com/iflytop/gd/app/controller/CraftsController.java +++ b/src/main/java/com/iflytop/gd/app/controller/CraftsController.java @@ -4,11 +4,10 @@ import com.iflytop.gd.app.model.dto.PauseCraftsDto; import com.iflytop.gd.app.model.dto.ResumeCraftsDto; import com.iflytop.gd.app.model.dto.StartCraftsDTO; import com.iflytop.gd.app.model.dto.StopCraftsDto; -import com.iflytop.gd.app.service.CraftsStepService; -import com.iflytop.gd.common.result.Result; -import com.iflytop.gd.common.result.ResultCode; import com.iflytop.gd.app.model.entity.Crafts; import com.iflytop.gd.app.service.CraftsService; +import com.iflytop.gd.common.result.Result; +import com.iflytop.gd.common.result.ResultCode; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; @@ -25,7 +24,6 @@ import java.util.List; @Slf4j public class CraftsController { private final CraftsService craftsService; - private final CraftsStepService craftsStepService; @Operation(summary = "根据矿石id获取工艺列表") @GetMapping("/list/{oresId}") @@ -73,7 +71,7 @@ public class CraftsController { @Operation(summary = "开始执行工艺") @PostMapping("/start") public Result startCrafts(@RequestBody StartCraftsDTO startCraftsDTO) { - boolean isSuccess = craftsStepService.startCrafts(startCraftsDTO.getCraftId(), startCraftsDTO.getHeatId()); + boolean isSuccess = craftsService.startCrafts(startCraftsDTO.getCraftId(), startCraftsDTO.getHeatId()); if (isSuccess) { return Result.success(); } @@ -83,21 +81,21 @@ public class CraftsController { @Operation(summary = "暂停执行工艺") @PostMapping("/pause") public Result pauseCrafts(@RequestBody PauseCraftsDto pauseCraftsDto) { - craftsStepService.pauseCrafts(pauseCraftsDto.getHeatId()); + craftsService.pauseCrafts(pauseCraftsDto.getHeatId()); return Result.success(); } @Operation(summary = "恢复执行工艺") @PostMapping("/resume") public Result resumeCrafts(@RequestBody ResumeCraftsDto resumeCraftsDto) { - craftsStepService.resumeCrafts(resumeCraftsDto.getHeatId()); + craftsService.resumeCrafts(resumeCraftsDto.getHeatId()); return Result.success(); } @Operation(summary = "停止执行工艺") @PostMapping("/stop") public Result stopCrafts(@RequestBody StopCraftsDto stopCraftsDto) { - boolean isSuccess = craftsStepService.stopCrafts(stopCraftsDto.getHeatId()); + boolean isSuccess = craftsService.stopCrafts(stopCraftsDto.getHeatId()); if (isSuccess) { return Result.success(); } diff --git a/src/main/java/com/iflytop/gd/app/core/CraftsContext.java b/src/main/java/com/iflytop/gd/app/core/CraftsContext.java index d4fa82d..9a7596b 100644 --- a/src/main/java/com/iflytop/gd/app/core/CraftsContext.java +++ b/src/main/java/com/iflytop/gd/app/core/CraftsContext.java @@ -1,13 +1,12 @@ package com.iflytop.gd.app.core; import cn.hutool.json.JSONUtil; -import com.iflytop.gd.common.constant.WebSocketMessageType; import com.iflytop.gd.app.common.enums.CraftEvents; import com.iflytop.gd.app.common.enums.CraftStates; import com.iflytop.gd.app.model.bo.CraftsStep; -import com.iflytop.gd.app.service.CraftsStepService; import com.iflytop.gd.app.model.entity.Crafts; import com.iflytop.gd.app.service.WebSocketService; +import com.iflytop.gd.common.constant.WebSocketMessageType; import lombok.Getter; import org.springframework.messaging.Message; import org.springframework.messaging.support.MessageBuilder; @@ -27,13 +26,11 @@ public class CraftsContext implements Runnable { private final List craftsStepList; private final StateMachine sm; private final WebSocketService ws; - private final CraftsStepService craftsStepService; private int currentIndex = 0; - public CraftsContext(String heatId, Crafts craft, StateMachineFactory factory, CraftsStepService craftsStepService, WebSocketService ws) { + public CraftsContext(String heatId, Crafts craft, StateMachineFactory factory, WebSocketService ws) { this.heatId = heatId; this.craftsStepList = JSONUtil.parseArray(craft.getSteps()).toList(CraftsStep.class); - this.craftsStepService = craftsStepService; this.ws = ws; this.sm = factory.getStateMachine(heatId); @@ -46,8 +43,7 @@ public class CraftsContext implements Runnable { Map dataMap = new HashMap<>(); dataMap.put("heatId", heatId); dataMap.put("event", to.getId()); - dataMap.put("index", currentIndex); - ws.push(WebSocketMessageType.CRAFTS, dataMap); + ws.push(WebSocketMessageType.CRAFTS_STATE, dataMap); } }); } @@ -82,7 +78,12 @@ public class CraftsContext implements Runnable { } } - private boolean executeStep(CraftsStep step) { + private boolean executeStep(CraftsStep step) throws InterruptedException { + Map dataMap = new HashMap<>(); + dataMap.put("heatId", heatId); + dataMap.put("currentStep", step.getMethod()); + ws.push(WebSocketMessageType.CRAFTS_STEP, dataMap); + Thread.sleep(3000); // TODO: 调用 craftsStepService 或 device 服务执行具体命令 return true; } diff --git a/src/main/java/com/iflytop/gd/app/model/bo/CraftsStep.java b/src/main/java/com/iflytop/gd/app/model/bo/CraftsStep.java index 159222b..23ef71f 100644 --- a/src/main/java/com/iflytop/gd/app/model/bo/CraftsStep.java +++ b/src/main/java/com/iflytop/gd/app/model/bo/CraftsStep.java @@ -1,8 +1,10 @@ package com.iflytop.gd.app.model.bo; import cn.hutool.json.JSONObject; +import lombok.Data; +@Data public class CraftsStep { - String name; - JSONObject params; + private String method; + private JSONObject params; } diff --git a/src/main/java/com/iflytop/gd/app/service/CraftsService.java b/src/main/java/com/iflytop/gd/app/service/CraftsService.java index 90cbe1e..b3ca695 100644 --- a/src/main/java/com/iflytop/gd/app/service/CraftsService.java +++ b/src/main/java/com/iflytop/gd/app/service/CraftsService.java @@ -2,21 +2,102 @@ package com.iflytop.gd.app.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.iflytop.gd.app.common.enums.CraftEvents; +import com.iflytop.gd.app.common.enums.CraftStates; +import com.iflytop.gd.app.core.CraftsContext; import com.iflytop.gd.app.mapper.CraftsMapper; import com.iflytop.gd.app.model.entity.Crafts; +import jakarta.annotation.PostConstruct; import lombok.RequiredArgsConstructor; +import org.springframework.statemachine.config.StateMachineFactory; import org.springframework.stereotype.Service; import java.util.Arrays; import java.util.List; +import java.util.concurrent.*; import java.util.stream.Collectors; /** - * 工艺 + * 工艺执行管理服务,每个 heatId 对应一个独立线程执行任务 */ @Service @RequiredArgsConstructor -public class CraftsService extends ServiceImpl{ +public class CraftsService extends ServiceImpl { + private final StateMachineFactory stateMachineFactory; + private final WebSocketService webSocketService; + + private ExecutorService executor; + + private final ConcurrentHashMap contextMap = new ConcurrentHashMap<>(); + private final ConcurrentHashMap> futureMap = new ConcurrentHashMap<>(); + + @PostConstruct + public void init() { + this.executor = Executors.newCachedThreadPool(); + } + + /** + * 开始执行工艺 + */ + public synchronized boolean startCrafts(Long craftId, String heatId) { + if (futureMap.containsKey(heatId)) { // 已有任务在执行,不重复启动 + return false; + } + Crafts craft = this.getById(craftId); + if (craft == null) { + return false; + } + CraftsContext ctx = new CraftsContext(heatId, craft, stateMachineFactory, webSocketService); + CompletableFuture cf = CompletableFuture.runAsync(() -> { + try { + ctx.run(); + } finally { + contextMap.remove(heatId); + futureMap.remove(heatId); + } + }, executor); + contextMap.put(heatId, ctx); + futureMap.put(heatId, cf); + return true; + } + + /** + * 暂停执行工艺 + */ + public synchronized void pauseCrafts(String heatId) { + CraftsContext ctx = contextMap.get(heatId); + if (ctx != null) { + ctx.pause(); + } + } + + /** + * 恢复执行工艺 + */ + public synchronized void resumeCrafts(String heatId) { + CraftsContext ctx = contextMap.get(heatId); + if (ctx != null) { + ctx.resume(); + } + } + + /** + * 停止执行工艺 + */ + public synchronized boolean stopCrafts(String heatId) { + CraftsContext ctx = contextMap.get(heatId); + Future future = futureMap.get(heatId); + if (ctx != null && future != null) { + ctx.stop(); + future.cancel(true); + contextMap.remove(heatId); + futureMap.remove(heatId); + return true; + } + return false; + } + + // TODO: 如需提供查询当前执行状态,可添加 getStatus(heatId) 方法,返回 ctx.getCurrentIndex(), ctx.getSm().getState().getId(), ctx.getRemainingSteps() 等 public List selectAllByOresId(Long oresId) { return this.baseMapper.selectAllByOresId(oresId); @@ -40,5 +121,4 @@ public class CraftsService extends ServiceImpl{ .collect(Collectors.toList()); return this.removeByIds(ids); } - } diff --git a/src/main/java/com/iflytop/gd/app/service/CraftsStepService.java b/src/main/java/com/iflytop/gd/app/service/CraftsStepService.java deleted file mode 100644 index 5dae5ca..0000000 --- a/src/main/java/com/iflytop/gd/app/service/CraftsStepService.java +++ /dev/null @@ -1,100 +0,0 @@ -package com.iflytop.gd.app.service; - -import com.iflytop.gd.app.common.enums.CraftEvents; -import com.iflytop.gd.app.common.enums.CraftStates; -import com.iflytop.gd.app.core.CraftsContext; -import com.iflytop.gd.app.model.entity.Crafts; -import jakarta.annotation.PostConstruct; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.statemachine.config.StateMachineFactory; -import org.springframework.stereotype.Service; - -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; - -/** - * 设备步骤操作 - */ -@Slf4j -@Service -@RequiredArgsConstructor -public class CraftsStepService { - private final CraftsService craftsService; - private final StateMachineFactory stateMachineFactory; - private final WebSocketService webSocketService; - - private ExecutorService executor; - - private final ConcurrentHashMap contextMap = new ConcurrentHashMap<>(); - private final ConcurrentHashMap> futureMap = new ConcurrentHashMap<>(); - - @PostConstruct - public void init() { - this.executor = Executors.newCachedThreadPool(); - } - - /** - * 开始执行工艺 - */ - public synchronized boolean startCrafts(Long craftId, String heatId) { - if (futureMap.containsKey(heatId)) {// 已有任务在执行,不重复启动 - return false; - } - Crafts craft = craftsService.getById(craftId); - if (craft == null) { - return false; - } - CraftsContext ctx = new CraftsContext( - heatId, - craft, - stateMachineFactory, - this, - webSocketService - ); - Future future = executor.submit(ctx); - contextMap.put(heatId, ctx); - futureMap.put(heatId, future); - return true; - } - - /** - * 暂停执行工艺 - */ - public synchronized void pauseCrafts(String heatId) { - CraftsContext ctx = contextMap.get(heatId); - if (ctx != null) { - ctx.pause(); - } - } - - /** - * 恢复执行工艺 - */ - public synchronized void resumeCrafts(String heatId) { - CraftsContext ctx = contextMap.get(heatId); - if (ctx != null) { - ctx.resume(); - } - } - - /** - * 停止执行工艺 - */ - public synchronized boolean stopCrafts(String heatId) { - CraftsContext ctx = contextMap.get(heatId); - Future future = futureMap.get(heatId); - if (ctx != null && future != null) { - ctx.stop(); - future.cancel(true); - contextMap.remove(heatId); - futureMap.remove(heatId); - return true; - } - return false; - } - - // TODO: 如需提供查询当前执行状态,可在此类添加 getStatus(heatId) 方法,返回 ctx.getCurrentIndex(), ctx.getSm().getState().getId(), ctx.getRemainingSteps() 等 -} diff --git a/src/main/java/com/iflytop/gd/common/constant/WebSocketMessageType.java b/src/main/java/com/iflytop/gd/common/constant/WebSocketMessageType.java index 08a1dd3..b443181 100644 --- a/src/main/java/com/iflytop/gd/common/constant/WebSocketMessageType.java +++ b/src/main/java/com/iflytop/gd/common/constant/WebSocketMessageType.java @@ -13,10 +13,16 @@ public class WebSocketMessageType { * 设备指令反馈 */ public static final String CMD = "cmd"; + + /** + * 工艺执行步骤反馈 + */ + public static final String CRAFTS_STEP = "crafts_step"; + /** - * 工艺执行反馈 + * 工艺执行状态反馈 */ - public static final String CRAFTS = "crafts"; + public static final String CRAFTS_STATE = "crafts_state"; /** * 容器剩余状态 */