Browse Source

feat:工艺主框架

master
白凤吉 1 week ago
parent
commit
67e5b0f91a
  1. 29
      src/main/java/com/iflytop/colortitration/app/controller/CraftsController.java
  2. 148
      src/main/java/com/iflytop/colortitration/app/core/crafts/CraftsDispatcher.java
  3. 32
      src/main/java/com/iflytop/colortitration/app/core/crafts/CraftsJob.java
  4. 15
      src/main/java/com/iflytop/colortitration/app/core/state/DeviceState.java
  5. 2
      src/main/java/com/iflytop/colortitration/app/service/DeviceCommandService.java
  6. 60
      src/main/java/com/iflytop/colortitration/common/service/CraftsService.java

29
src/main/java/com/iflytop/colortitration/app/controller/CraftsController.java

@ -65,21 +65,20 @@ public class CraftsController {
return Result.success();
}
//
// @Operation(summary = "暂停执行工艺")
// @PostMapping("/pause")
// public Result<String> pauseCrafts(@Valid @RequestBody CraftsPauseDto pauseCraftsDto) {
// craftsService.pauseCrafts(pauseCraftsDto.getHeatId());
// return Result.success();
// }
//
// @Operation(summary = "恢复执行工艺")
// @PostMapping("/resume")
// public Result<String> resumeCrafts(@Valid @RequestBody CraftsResumeDTO resumeCraftsDto) {
// craftsService.resumeCrafts(resumeCraftsDto.getHeatId());
// return Result.success();
// }
//
@Operation(summary = "暂停执行工艺")
@PostMapping("/pause")
public Result<?> pauseCrafts() {
craftsService.pause();
return Result.success();
}
@Operation(summary = "恢复执行工艺")
@PostMapping("/resume")
public Result<?> resumeCrafts() {
craftsService.resume();
return Result.success();
}
// @Operation(summary = "停止执行工艺")
// @PostMapping("/stop")
// public Result<String> stopCrafts(@Valid @RequestBody CraftsStopDTO stopCraftsDto) {

148
src/main/java/com/iflytop/colortitration/app/core/crafts/CraftsDispatcher.java

@ -0,0 +1,148 @@
package com.iflytop.colortitration.app.core.crafts;
import com.iflytop.colortitration.app.common.enums.MultipleModuleCode;
import com.iflytop.colortitration.app.core.state.DeviceState;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* 动态为每个滴定位模块创建独立队列和工作线程
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class CraftsDispatcher {
private final BlockingQueue<CraftsJob> jobQueue = new LinkedBlockingQueue<>();
private ExecutorService executor;
private final AtomicInteger threadCounter = new AtomicInteger(0);
private final ReentrantLock pauseLock = new ReentrantLock();
private final Condition pauseCondition = pauseLock.newCondition();
private final DeviceState deviceState;
@PostConstruct
public void init() {
MultipleModuleCode[] modules = MultipleModuleCode.values();
int totalThreads = modules.length;
executor = Executors.newFixedThreadPool(totalThreads, r -> {
int id = threadCounter.incrementAndGet();
return new Thread(r, "CraftsWorker-" + id);
});
for (int i = 0; i < totalThreads; i++) {
executor.submit(createWorker());
}
log.info("已初始化{}个工作线程来处理滴定任务。", totalThreads);
}
/**
* 创建消费线程每个线程从队列中取任务并执行
*/
private Runnable createWorker() {
return () -> {
while (true) {
try {
CraftsJob job = jobQueue.take();
job.executeSteps(this);
log.info("试管编号 {} 的任务执行成功。", job.getTubeNum());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.warn("工作线程被中断,停止执行。");
return;
} catch (Exception e) {
log.error("处理任务时发生错误: {}", e.getMessage(), e);
}
}
};
}
/**
* 将新的待执行工艺添加至队列
*/
public void addCraftsJob(CraftsJob craftsJob) {
try {
jobQueue.put(craftsJob);
log.info("试管编号 {} 的任务已添加到队列中。", craftsJob.getTubeNum());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.error("添加试管编号 {} 的任务时发生中断。", craftsJob.getTubeNum());
}
}
/**
* 获取所有待执行的任务
*/
public BlockingQueue<CraftsJob> getAllJobs() {
return jobQueue;
}
/**
* 根据试管编号删除任务
*/
public void removeCraftsJob(int tubeNum) {
boolean removed = false;
for (CraftsJob job : jobQueue) {
if (job.getTubeNum() == tubeNum) {
jobQueue.remove(job); // 删除任务
removed = true;
log.info("试管编号 {} 的任务已从队列中删除。", tubeNum);
}
}
if (!removed) {
log.warn("未找到试管编号 {} 的任务进行删除。", tubeNum);
}
}
/**
* 如果暂停线程将进入等待状态
*
* @throws InterruptedException 如果线程被中断
*/
public void waitIfPaused() throws InterruptedException {
pauseLock.lock();
try {
while (deviceState.isPaused()) {
// 如果已暂停当前线程将被挂起直到调用 notifyAll() 唤醒
pauseCondition.await();
}
} finally {
pauseLock.unlock();
}
}
/**
* 暂停所有任务
*/
public void pause() {
pauseLock.lock();
try {
deviceState.setPaused(true);
log.info("所有任务已暂停。");
} finally {
pauseLock.unlock();
}
}
/**
* 恢复所有任务
*/
public void resume() {
pauseLock.lock();
try {
deviceState.setPaused(false);
pauseCondition.signalAll(); // 唤醒所有等待的线程
log.info("所有任务已恢复。");
} finally {
pauseLock.unlock();
}
}
}

32
src/main/java/com/iflytop/colortitration/app/core/crafts/CraftsJob.java

@ -0,0 +1,32 @@
package com.iflytop.colortitration.app.core.crafts;
import com.iflytop.colortitration.app.common.enums.MultipleModuleCode;
import com.iflytop.colortitration.common.model.entity.Crafts;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.util.List;
@Slf4j
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Data
public class CraftsJob {
private int tubeNum;
private List<MultipleModuleCode> selectedModules;
private Crafts crafts;
public void executeSteps(CraftsDispatcher dispatcher) {
log.info("执行工艺开始,试管{}", tubeNum);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.info("执行工艺结束,试管{}", tubeNum);
}
}

15
src/main/java/com/iflytop/colortitration/app/core/state/DeviceState.java

@ -17,6 +17,9 @@ import java.util.Map;
@Component
@JsonIgnoreProperties(value = {"advisors", "frozen", "preFiltered", "proxyTargetClass", "targetSource", "exposeProxy", "advisorCount", "proxiedInterfaces", "targetClass"})
public class DeviceState {
@Schema(description = "当前设备是否暂停")
private volatile boolean paused = false;
@Schema(description = "滴定模块")
private final Map<MultipleModuleCode, TitrationModuleState> titrationModuleStateMap = new HashMap<>();
@ -27,22 +30,22 @@ public class DeviceState {
private final Map<Integer, TubeState> trayTubeStateMap = new HashMap<>();
@Schema(description = "托盘1是否存在,true 存在 false不存在")
private boolean trayExist1 = false;
private volatile boolean trayExist1 = false;
@Schema(description = "托盘2是否存在,true 存在 false不存在")
private boolean trayExist2 = false;
private volatile boolean trayExist2 = false;
@Schema(description = "虚拟模式,true为虚拟")
private boolean virtual = false;
private volatile boolean virtual = false;
@Schema(description = "初始化状态,true初始化完毕")
private boolean initComplete = false;
private volatile boolean initComplete = false;
@Schema(description = "自检状态,true自检完毕")
private boolean selfTest = false;
private volatile boolean selfTest = false;
@Schema(description = "是否是急停状态,true为急停")
private boolean emergencyStop = false;
private volatile boolean emergencyStop = false;
@Schema(description = "当前登录用户")
private User currentUser;

2
src/main/java/com/iflytop/colortitration/app/service/DeviceCommandService.java

@ -6,6 +6,7 @@ import com.iflytop.colortitration.app.common.constant.CommandStatus;
import com.iflytop.colortitration.app.core.command.CommandFuture;
import com.iflytop.colortitration.app.core.command.CyclicNumberGenerator;
import com.iflytop.colortitration.app.core.command.DeviceCommand;
import com.iflytop.colortitration.app.core.state.DeviceState;
import com.iflytop.colortitration.app.websocket.server.DebugGenerator;
import com.iflytop.colortitration.app.websocket.server.WebSocketSender;
import com.iflytop.colortitration.hardware.HardwareService;
@ -27,6 +28,7 @@ import java.util.concurrent.LinkedBlockingQueue;
public class DeviceCommandService {
private final HardwareService hardwareService;
private final WebSocketSender webSocketService;
private final DeviceState deviceState;
/**
* 需要等待加液区空闲的龙门架机械臂指令
*/

60
src/main/java/com/iflytop/colortitration/common/service/CraftsService.java

@ -3,15 +3,21 @@ package com.iflytop.colortitration.common.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.iflytop.colortitration.app.common.enums.TitrationStatus;
import com.iflytop.colortitration.app.core.crafts.CraftsDispatcher;
import com.iflytop.colortitration.app.core.crafts.CraftsJob;
import com.iflytop.colortitration.app.core.state.DeviceState;
import com.iflytop.colortitration.app.core.state.TubeState;
import com.iflytop.colortitration.app.model.dto.SetCraftsDTO;
import com.iflytop.colortitration.common.mapper.CraftsMapper;
import com.iflytop.colortitration.common.model.entity.Crafts;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* 工艺接口服务
*/
@ -19,7 +25,12 @@ import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class CraftsService extends ServiceImpl<CraftsMapper, Crafts> {
private final CraftsDispatcher craftsDispatcher;
private final DeviceState deviceState;
private final ReentrantLock lock = new ReentrantLock(true);
private final Condition condition = lock.newCondition();
@Getter
private volatile boolean paused = false;
public Crafts findByName(String name) {
return this.getOne(new LambdaQueryWrapper<>(new Crafts()).eq(Crafts::getName, name));
@ -36,9 +47,58 @@ public class CraftsService extends ServiceImpl<CraftsMapper, Crafts> {
tubeState.setCraftsId(crafts.getId());
tubeState.setCraftsName(crafts.getName());
tubeState.setTitrationModuleCodes(startCraftsDTO.getModuleCodes());
CraftsJob job = new CraftsJob();
job.setTubeNum(tubeNum);
job.setCrafts(crafts);
job.setSelectedModules(startCraftsDTO.getModuleCodes());
craftsDispatcher.addCraftsJob(job);
}
}
}
/**
* 暂停所有自动滴定任务
*/
public void pause() {
lock.lock();
try {
paused = true;
log.info("Titration paused");
} finally {
lock.unlock();
}
}
/**
* 恢复所有自动滴定任务
*/
public void resume() {
lock.lock();
try {
paused = false;
condition.signalAll();
log.info("Titration resumed");
} finally {
lock.unlock();
}
}
/**
* 在各处理步骤前调用以挂起当前线程直到恢复
*
* @throws InterruptedException 如果线程被中断
*/
public void pauseIfNeeded() throws InterruptedException {
lock.lock();
try {
while (paused) {
log.debug("Titration is paused, waiting...");
condition.await();
}
} finally {
lock.unlock();
}
}
}
Loading…
Cancel
Save