Browse Source

feat:定量预充

master
白凤吉 2 days ago
parent
commit
2f488d8cd5
  1. 61
      src/main/java/com/iflytop/handacid/app/controller/PreFillController.java
  2. 5
      src/main/java/com/iflytop/handacid/app/core/listener/BleGamepadEventListener.java
  3. 2
      src/main/java/com/iflytop/handacid/app/core/state/RemoteControlState.java
  4. 1
      src/main/java/com/iflytop/handacid/app/scheduled/BleGamepadStateScheduledTask.java
  5. 129
      src/main/java/com/iflytop/handacid/app/service/ChannelCtrlService.java
  6. 14
      src/main/java/com/iflytop/handacid/common/mapper/PreFillMapper.java
  7. 27
      src/main/java/com/iflytop/handacid/common/model/entity/PreFill.java
  8. 17
      src/main/java/com/iflytop/handacid/common/service/PreFillService.java
  9. 11
      src/main/resources/sql/init.sql

61
src/main/java/com/iflytop/handacid/app/controller/PreFillController.java

@ -0,0 +1,61 @@
package com.iflytop.handacid.app.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.iflytop.handacid.common.base.BasePageQuery;
import com.iflytop.handacid.common.model.entity.PreFill;
import com.iflytop.handacid.common.model.entity.User;
import com.iflytop.handacid.common.result.PageResult;
import com.iflytop.handacid.common.result.Result;
import com.iflytop.handacid.common.service.FormulationService;
import com.iflytop.handacid.common.service.PreFillService;
import com.iflytop.handacid.common.service.SolutionService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
/**
* 预充设定
*/
@Tag(name = "\uD83D\uDCA6预充设定")
@RestController
@RequestMapping("/api/pre-fill")
@RequiredArgsConstructor
@Slf4j
public class PreFillController {
private final PreFillService preFillService;
@Operation(summary = "分页查询用预充设定")
@PostMapping("/page")
public PageResult<PreFill> getPage(@RequestBody BasePageQuery query) {
Page<PreFill> page = new Page<>(query.getPageNum(), query.getPageSize());
return PageResult.success(preFillService.page(page));
}
@Operation(summary = "添加预充设定")
@PostMapping("")
public Result<String> add(@Valid @RequestBody PreFill preFill) {
return preFillService.save(preFill) ? Result.success() : Result.failed();
}
@Operation(summary = "修改预充设定")
@PutMapping("")
public Result<String> update(@Valid @RequestBody PreFill preFill) {
return preFillService.updateById(preFill) ? Result.success() : Result.failed();
}
@Operation(summary = "删除预充设定")
@DeleteMapping("/{ids}")
public Result<String> delete(@Parameter(description = "ID,多个用逗号分隔") @PathVariable String ids) {
boolean success = preFillService.removeBatchByIds(
Arrays.stream(ids.split(",")).map(Long::valueOf).toList()
);
return success ? Result.success() : Result.failed();
}
}

5
src/main/java/com/iflytop/handacid/app/core/listener/BleGamepadEventListener.java

@ -1,6 +1,7 @@
package com.iflytop.handacid.app.core.listener; package com.iflytop.handacid.app.core.listener;
import com.iflytop.handacid.app.core.state.DeviceState; import com.iflytop.handacid.app.core.state.DeviceState;
import com.iflytop.handacid.app.scheduled.BleGamepadStateScheduledTask;
import com.iflytop.handacid.app.service.ChannelCtrlService; import com.iflytop.handacid.app.service.ChannelCtrlService;
import com.iflytop.handacid.common.service.AuditRecordService; import com.iflytop.handacid.common.service.AuditRecordService;
import com.iflytop.handacid.hardware.service.AppEventBusService; import com.iflytop.handacid.hardware.service.AppEventBusService;
@ -24,6 +25,7 @@ public class BleGamepadEventListener {
private final AppEventBusService eventBus; private final AppEventBusService eventBus;
private final ChannelCtrlService channelCtrlService; private final ChannelCtrlService channelCtrlService;
private final DeviceState deviceState; private final DeviceState deviceState;
private final BleGamepadStateScheduledTask bleGamepadStateScheduledTask;
@PostConstruct @PostConstruct
synchronized public void init() { synchronized public void init() {
@ -65,9 +67,12 @@ public class BleGamepadEventListener {
} else if (CmdId.event_ble_gamepad_connected.equals(cmdId)) { } else if (CmdId.event_ble_gamepad_connected.equals(cmdId)) {
log.info("蓝牙手柄 连接成功"); log.info("蓝牙手柄 连接成功");
deviceState.getRemoteControlState().setConnected(true); deviceState.getRemoteControlState().setConnected(true);
bleGamepadStateScheduledTask.fetchTrayState();
} else if (CmdId.event_ble_gamepad_disconnected.equals(cmdId)) { } else if (CmdId.event_ble_gamepad_disconnected.equals(cmdId)) {
log.info("蓝牙手柄 连接断开"); log.info("蓝牙手柄 连接断开");
deviceState.getRemoteControlState().setConnected(false); deviceState.getRemoteControlState().setConnected(false);
deviceState.getRemoteControlState().setBatteryLevel(-1);
deviceState.getRemoteControlState().setCharging(false);
} }
} }

2
src/main/java/com/iflytop/handacid/app/core/state/RemoteControlState.java

@ -19,7 +19,7 @@ public class RemoteControlState {
private volatile boolean connected = false; private volatile boolean connected = false;
@Schema(description = "当前电量(0-100%)") @Schema(description = "当前电量(0-100%)")
private volatile double batteryLevel = 0;
private volatile double batteryLevel = -1;
@Schema(description = "是否正在充电") @Schema(description = "是否正在充电")
private volatile boolean charging = false; private volatile boolean charging = false;

1
src/main/java/com/iflytop/handacid/app/scheduled/BleGamepadStateScheduledTask.java

@ -21,7 +21,6 @@ public class BleGamepadStateScheduledTask {
@Scheduled(fixedRate = 30 * 1000) @Scheduled(fixedRate = 30 * 1000)
public void fetchTrayState() { public void fetchTrayState() {
try { try {
if (!deviceState.isVirtual()) { if (!deviceState.isVirtual()) {
boolean is_connect = bleGamepadDriver.is_connected(BleGamepadMid.BleGamePad); boolean is_connect = bleGamepadDriver.is_connected(BleGamepadMid.BleGamePad);
deviceState.getRemoteControlState().setConnected(is_connect); deviceState.getRemoteControlState().setConnected(is_connect);

129
src/main/java/com/iflytop/handacid/app/service/ChannelCtrlService.java

@ -14,10 +14,8 @@ import com.iflytop.handacid.app.core.state.DeviceState;
import com.iflytop.handacid.common.model.entity.AuditRecord; import com.iflytop.handacid.common.model.entity.AuditRecord;
import com.iflytop.handacid.common.model.entity.Channel; import com.iflytop.handacid.common.model.entity.Channel;
import com.iflytop.handacid.common.model.entity.Formulation; import com.iflytop.handacid.common.model.entity.Formulation;
import com.iflytop.handacid.common.service.AuditRecordService;
import com.iflytop.handacid.common.service.ChannelService;
import com.iflytop.handacid.common.service.FormulationService;
import com.iflytop.handacid.common.service.SystemConfigService;
import com.iflytop.handacid.common.model.entity.PreFill;
import com.iflytop.handacid.common.service.*;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -41,6 +39,7 @@ public class ChannelCtrlService {
private final AuditRecordService auditRecordService; private final AuditRecordService auditRecordService;
private final SystemConfigService systemConfigService; private final SystemConfigService systemConfigService;
private final ChannelService channelService; private final ChannelService channelService;
private final PreFillService preFillService;
/** /**
* 开始加液 * 开始加液
@ -68,14 +67,28 @@ public class ChannelCtrlService {
if (SolutionAddMode.AUTO.equals(deviceState.getMode())) {//自动模式 if (SolutionAddMode.AUTO.equals(deviceState.getMode())) {//自动模式
while (!deviceState.isSolutionAddStop()) { while (!deviceState.isSolutionAddStop()) {
handleSolutionAdd(channelStateList); handleSolutionAdd(channelStateList);
handleCalculateActualDispensedVolume(channelStateList);
for (ChannelState channelState : channelStateList) {//与缓存的位置比较计算加液量
Formulation formulation = formulationService.lambdaQuery()
.eq(Formulation::getSolutionId, channelState.getSolutionId())
.eq(Formulation::getConcentration, channelState.getConcentration())
.eq(Formulation::getVolume, channelState.getTargetVolume())
.one();
handleCalculateActualDispensedVolume(channelState, formulation.getVolume(), formulation.getRevolutions());
}
if (!deviceState.isSolutionAddStop()) { if (!deviceState.isSolutionAddStop()) {
Thread.sleep(deviceState.getDelay() * 1000L); Thread.sleep(deviceState.getDelay() * 1000L);
} }
} }
} else if (SolutionAddMode.CLICK.equals(deviceState.getMode())) {//点动模式 } else if (SolutionAddMode.CLICK.equals(deviceState.getMode())) {//点动模式
handleSolutionAdd(channelStateList); handleSolutionAdd(channelStateList);
handleCalculateActualDispensedVolume(channelStateList);
for (ChannelState channelState : channelStateList) {//与缓存的位置比较计算加液量
Formulation formulation = formulationService.lambdaQuery()
.eq(Formulation::getSolutionId, channelState.getSolutionId())
.eq(Formulation::getConcentration, channelState.getConcentration())
.eq(Formulation::getVolume, channelState.getTargetVolume())
.one();
handleCalculateActualDispensedVolume(channelState, formulation.getVolume(), formulation.getRevolutions());
}
} }
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
@ -124,23 +137,34 @@ public class ChannelCtrlService {
return; return;
} }
try { try {
List<CommandFuture> valveOpenDeviceCommandFutureList = new ArrayList<>();
for (ChannelState channelState : channelStateList) { for (ChannelState channelState : channelStateList) {
channelState.setStateCode(ChannelStateCode.PRE); channelState.setStateCode(ChannelStateCode.PRE);
DeviceCommand valveOpenDeviceCommand = getValveOpenCommandByChannel(channelState.getChannelCode());//打开阀门
valveOpenDeviceCommandFutureList.add(deviceCommandService.sendCommand(valveOpenDeviceCommand));
} }
CommandUtil.wait(valveOpenDeviceCommandFutureList);
List<CommandFuture> commandFutureList = new ArrayList<>(); List<CommandFuture> commandFutureList = new ArrayList<>();
for (ChannelState channelState : channelStateList) { for (ChannelState channelState : channelStateList) {
//打开阀门
DeviceCommand valveOpenDeviceCommand = getValveOpenCommandByChannel(channelState.getChannelCode());
CommandFuture valveOpenCommandFuture = deviceCommandService.sendCommand(valveOpenDeviceCommand);
CommandUtil.wait(valveOpenCommandFuture);
DeviceCommand deviceCommand = getPumpForwardRotateCommandByChannel(channelState.getChannelCode());
pumpPositionCache(channelState);
PreFill preFill = preFillService.lambdaQuery()
.eq(PreFill::getSolutionId, channelState.getSolutionId())
.eq(PreFill::getConcentration, channelState.getConcentration())
.one();
DeviceCommand deviceCommand = getPumpMoveByCommandByChannel(channelState.getChannelCode(), preFill.getRevolutions());
commandFutureList.add(deviceCommandService.sendCommand(deviceCommand)); commandFutureList.add(deviceCommandService.sendCommand(deviceCommand));
AuditRecord auditRecord = new AuditRecord(deviceState.getCurrentUser().getId(), deviceState.getCurrentUser().getNickname(), channelState.getSolutionId(),
channelState.getSolutionName(), channelState.getConcentration(), channelState.getChannelCode().name(), channelState.getTargetVolume());
auditRecordService.saveOrUpdate(auditRecord);
} }
CommandUtil.wait(commandFutureList); CommandUtil.wait(commandFutureList);
for (ChannelState channelState : channelStateList) {//与缓存的位置比较计算加液量
PreFill preFill = preFillService.lambdaQuery()
.eq(PreFill::getSolutionId, channelState.getSolutionId())
.eq(PreFill::getConcentration, channelState.getConcentration())
.one();
handleCalculateActualDispensedVolume(channelState, preFill.getVolume(), preFill.getConcentration());
}
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@ -282,8 +306,11 @@ public class ChannelCtrlService {
List<CommandFuture> pumpMoveByCommandFutureList = new ArrayList<>(); List<CommandFuture> pumpMoveByCommandFutureList = new ArrayList<>();
for (ChannelState channelState : channelStateList) { for (ChannelState channelState : channelStateList) {
pumpPositionCache(channelState); pumpPositionCache(channelState);
Formulation formulation = formulationService.getOne(new LambdaQueryWrapper<Formulation>().eq(Formulation::getSolutionId, channelState.getSolutionId())
.eq(Formulation::getConcentration, channelState.getConcentration()).eq(Formulation::getVolume, channelState.getTargetVolume()).last("limit 1"));
Formulation formulation = formulationService.lambdaQuery()
.eq(Formulation::getSolutionId, channelState.getSolutionId())
.eq(Formulation::getConcentration, channelState.getConcentration())
.eq(Formulation::getVolume, channelState.getTargetVolume())
.one();
DeviceCommand deviceCommand = getPumpMoveByCommandByChannel(channelState.getChannelCode(), formulation.getRevolutions()); DeviceCommand deviceCommand = getPumpMoveByCommandByChannel(channelState.getChannelCode(), formulation.getRevolutions());
pumpMoveByCommandFutureList.add(deviceCommandService.sendCommand(deviceCommand)); pumpMoveByCommandFutureList.add(deviceCommandService.sendCommand(deviceCommand));
} }
@ -302,33 +329,29 @@ public class ChannelCtrlService {
} }
/** /**
* 处理实际加液计算
* 处理实际加液计算并更新
*/ */
private void handleCalculateActualDispensedVolume(List<ChannelState> channelStateList) throws Exception {
for (ChannelState channelState : channelStateList) {//与缓存的位置比较计算加液量
DeviceCommand currentPositionDeviceCommand = getPumpPositionCommandByChannel(channelState.getChannelCode());
CommandFuture currentPositionCommandFuture = deviceCommandService.sendCommand(currentPositionDeviceCommand);
CommandUtil.wait(currentPositionCommandFuture);
Double currentPosition = currentPositionCommandFuture.getResponseResult().getJSONObject("data").getDouble("position");
Formulation formulation = formulationService.getOne(new LambdaQueryWrapper<Formulation>().eq(Formulation::getSolutionId, channelState.getSolutionId())
.eq(Formulation::getConcentration, channelState.getConcentration()).eq(Formulation::getVolume, channelState.getTargetVolume()).last("limit 1"));
double dispensedVolume = calculateActualSolutionDispensedVolume(channelState.getPumpPositionCache(), currentPosition, formulation);
double currentVolume = channelState.getCurrentVolume() - dispensedVolume;//剩余
log.info("旧位置与新位置:{},{}", channelState.getPumpPositionCache(), currentPosition);
log.info("实际加液量:{}", dispensedVolume);
BigDecimal bd = BigDecimal.valueOf(currentVolume);
double roundedCurrentVolume = bd.setScale(2, RoundingMode.HALF_UP).doubleValue();//剩余保留2位
if (roundedCurrentVolume < 0) {
roundedCurrentVolume = 0;
}
channelState.setCurrentVolume(roundedCurrentVolume);
Channel channel = channelService.getOne(new LambdaQueryWrapper<>(new Channel()).eq(Channel::getCode, channelState.getChannelCode()));
channel.setCurrentVolume(roundedCurrentVolume);
channelService.updateById(channel);
AuditRecord auditRecord = new AuditRecord(deviceState.getCurrentUser().getId(), deviceState.getCurrentUser().getNickname(), channelState.getSolutionId(),
channelState.getSolutionName(), formulation.getConcentration(), channelState.getChannelCode().name(), dispensedVolume);//记录审计
auditRecordService.saveOrUpdate(auditRecord);
private void handleCalculateActualDispensedVolume(ChannelState channelState, double volume, double revs) throws Exception {
DeviceCommand currentPositionDeviceCommand = getPumpPositionCommandByChannel(channelState.getChannelCode());
CommandFuture currentPositionCommandFuture = deviceCommandService.sendCommand(currentPositionDeviceCommand);
CommandUtil.wait(currentPositionCommandFuture);
Double currentPosition = currentPositionCommandFuture.getResponseResult().getJSONObject("data").getDouble("position");
double dispensedVolume = calculateActualSolutionDispensedVolume(channelState.getPumpPositionCache(), currentPosition, volume, revs);
double currentVolume = channelState.getCurrentVolume() - dispensedVolume;//剩余
log.info("旧位置与新位置:{},{}", channelState.getPumpPositionCache(), currentPosition);
log.info("实际加液量:{}", dispensedVolume);
BigDecimal bd = BigDecimal.valueOf(currentVolume);
double roundedCurrentVolume = bd.setScale(2, RoundingMode.HALF_UP).doubleValue();//剩余保留2位
if (roundedCurrentVolume < 0) {
roundedCurrentVolume = 0;
} }
channelState.setCurrentVolume(roundedCurrentVolume);
Channel channel = channelService.getOne(new LambdaQueryWrapper<>(new Channel()).eq(Channel::getCode, channelState.getChannelCode()));
channel.setCurrentVolume(roundedCurrentVolume);
channelService.updateById(channel);
AuditRecord auditRecord = new AuditRecord(deviceState.getCurrentUser().getId(), deviceState.getCurrentUser().getNickname(), channelState.getSolutionId(),
channelState.getSolutionName(), channelState.getConcentration(), channelState.getChannelCode().name(), dispensedVolume);//记录审计
auditRecordService.saveOrUpdate(auditRecord);
} }
/** /**
@ -336,29 +359,17 @@ public class ChannelCtrlService {
* *
* @param beforePosition 加液前旋转圈数 * @param beforePosition 加液前旋转圈数
* @param afterPosition 加液后旋转圈数 * @param afterPosition 加液后旋转圈数
* @param formulation 配方对象包含指定加液量及其对应的旋转圈数
* - volume 目标加液量
* - revolutions 目标加液对应的旋转圈数
* @return double 实际出液量
* @param volume 配方加液量
* @param revs 配方加液对应的旋转圈数
*/ */
private double calculateActualSolutionDispensedVolume(Double beforePosition, Double afterPosition, Formulation formulation) {
if (beforePosition == null || afterPosition == null) {
throw new IllegalArgumentException("加液前/后位置参数不能为空");
}
if (formulation == null
|| formulation.getVolume() == null
|| formulation.getRevolutions() == null) {
throw new IllegalArgumentException("Formulation 或其字段 volume/revolutions 不能为空");
}
private double calculateActualSolutionDispensedVolume(double beforePosition, double afterPosition, double volume, double revs) {
Integer pumpConversionFactor = systemConfigService.getValueByKeyToInteger(SystemConfigKey.PUMP_CONVERSION_FACTOR); Integer pumpConversionFactor = systemConfigService.getValueByKeyToInteger(SystemConfigKey.PUMP_CONVERSION_FACTOR);
double deltaRevolutions = (((afterPosition - beforePosition) / pumpConversionFactor)); double deltaRevolutions = (((afterPosition - beforePosition) / pumpConversionFactor));
double targetVolume = formulation.getVolume();
double targetRevs = formulation.getRevolutions();
if (targetRevs == 0) {
if (revs == 0) {
throw new IllegalArgumentException("Formulation.revolutions 不能为 0"); throw new IllegalArgumentException("Formulation.revolutions 不能为 0");
} }
// 实际出液量 = 差值圈数 * (目标加液量 ÷ 目标圈数) // 实际出液量 = 差值圈数 * (目标加液量 ÷ 目标圈数)
BigDecimal bd = BigDecimal.valueOf(deltaRevolutions * (targetVolume / targetRevs));
BigDecimal bd = BigDecimal.valueOf(deltaRevolutions * (volume / revs));
return bd.setScale(2, RoundingMode.HALF_UP).doubleValue(); return bd.setScale(2, RoundingMode.HALF_UP).doubleValue();
} }

14
src/main/java/com/iflytop/handacid/common/mapper/PreFillMapper.java

@ -0,0 +1,14 @@
package com.iflytop.handacid.common.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.iflytop.handacid.common.model.entity.Formulation;
import com.iflytop.handacid.common.model.entity.PreFill;
import org.apache.ibatis.annotations.Mapper;
/**
* 预充持久层接口
*/
@Mapper
public interface PreFillMapper extends BaseMapper<PreFill> {
}

27
src/main/java/com/iflytop/handacid/common/model/entity/PreFill.java

@ -0,0 +1,27 @@
package com.iflytop.handacid.common.model.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.iflytop.handacid.common.base.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@TableName("pre_fill")
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(description = "预充配置")
public class PreFill extends BaseEntity {
@Schema(description = "加液量(mL)")
private Double volume;
@Schema(description = "溶液ID")
private Long solutionId;
@Schema(description = "溶液浓度")
private Double concentration;
@Schema(description = "对应转数")
private Double revolutions;
}

17
src/main/java/com/iflytop/handacid/common/service/PreFillService.java

@ -0,0 +1,17 @@
package com.iflytop.handacid.common.service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.iflytop.handacid.common.mapper.FormulationMapper;
import com.iflytop.handacid.common.mapper.PreFillMapper;
import com.iflytop.handacid.common.model.entity.Formulation;
import com.iflytop.handacid.common.model.entity.PreFill;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
/**
* 预充
*/
@Service
@RequiredArgsConstructor
public class PreFillService extends ServiceImpl<PreFillMapper, PreFill> {
}

11
src/main/resources/sql/init.sql

@ -61,6 +61,17 @@ CREATE TABLE IF NOT EXISTS formulation (
update_time TEXT DEFAULT CURRENT_TIMESTAMP update_time TEXT DEFAULT CURRENT_TIMESTAMP
); );
--
CREATE TABLE IF NOT EXISTS pre_fill (
id INTEGER PRIMARY KEY AUTOINCREMENT,
volume REAL,--
solution_id INTEGER,--id
concentration REAL,--
revolutions REAL,--
create_time TEXT DEFAULT CURRENT_TIMESTAMP,
update_time TEXT DEFAULT CURRENT_TIMESTAMP
);
-- 溶液 -- 溶液
CREATE TABLE IF NOT EXISTS solution ( CREATE TABLE IF NOT EXISTS solution (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,

Loading…
Cancel
Save