Browse Source

update

tags/v0
zhaohe 8 months ago
parent
commit
2d07da1ca9
  1. 1
      .gitignore
  2. 7
      doc/a8k移液泵优化方案.md
  3. 355
      doc/代码框架.md
  4. 9
      doc/代码缩写.md
  5. 4
      src/main/java/a8k/a8kproj/A8kIdCardDataParseService.java
  6. 6
      src/main/java/a8k/controler/api/v1/app/data/A8kProjectInfoControler.java
  7. 144
      src/main/java/a8k/controler/filemgr/FileMgrController.java
  8. 6
      src/main/java/a8k/service/app/appdata/ProjInfoMgrService.java
  9. 7
      src/main/java/a8k/service/dao/ProjectBaseInfoDao.java
  10. 11
      src/main/java/a8k/service/test/MainflowCtrlTestService.java
  11. 11
      src/main/java/a8k/service/test/TestStateMgrService.java
  12. 75
      src/main/java/a8k/utils/ZCSVUtils.java
  13. 0
      src/main/resources/optdata/project_base_info.csv
  14. 81
      src/main/resources/templates/filemgr/index.html

1
.gitignore

@ -33,3 +33,4 @@ build/
### VS Code ###
.vscode/
runenv/
tmp/

7
doc/a8k移液泵优化方案.md

@ -8,11 +8,4 @@ TODO:
lld启动时,先不移动步进电机,先读取压力值,如果压力值大于记录值,说明有液体
否则,移动步进电机,直到压力值大于记录值
```

355
doc/代码框架.md

@ -1,355 +0,0 @@
/*
*
* while(!isWaitingForStop()){
* if(当前样本处理完成){
* if(入料口有样本){
* 扫描样本 && 判断耗材是否充足
* 如果耗材不足,
* 提示用户(抛出提示事件),
* 暂停设备(退出线程(抛出pause异常))
* 如果扫描样本有误
* 提示用户
* 暂停设备
* }
* }
* else{
* 处理样本
* }
* }
*
* try{
*
* }catch(PauseException e){
* //pauseSelf
* //抛出事件
* }
*
* if(event){
* pauseSampleProcessThread();
* }
*
*/
经验:
动作与赋值分开
当一组动作完全执行完之后,再赋值给状态,这样就可以使得动作在执行过程中任意时间点都可以被打断。
while(true){
TubeCfg cfg = getTubeCfg(i);
moveTubeHolderProcessPos
if(tubeType == xxx)
takeTube
shakeTube(times,degree)
takeHap
move
else if(){
...
}
pausePoint;
}
问题:
有两种暂停状态:
一种是复位势暂停。
一种是保持现状势暂停。(前提是循环中)
pause/stop
动作划分为状态机
scan{
.
.
.
.
}
状态机->enter
状态机->enter
{
IDLE
{
if(state.eq(wokring)){
state.changeTo(scanPrepare)
}
}
SCANPREPARE
{
enterState
exitState
if(ioTrigger){
moveTubeHolderTo
waitForIoTrigger
changeStateTo(scanTubeHolderType)
if(error){
changeStateToError
}
}
}
SCANHODLER
{
move
scan
}
SCAN
PAUSE
{
}
}
```
用户PAUSE --> 暂停,恢复,全局状态的变更。
遇到错误 --> 通知,记录,清空工作位。无需恢复。
状态机 -->
修改全局状态。
记录。
执行动作。
前端接口:
pause;
continue;
全局都有哪些状态?
1. 类似于配置,
初始化
运行过程中修改(前端提交)
2. 运行过程控制,记录当前设备运行到哪一步,以便执行下一步。
步骤与暂停之间的关系。
步骤与恢复?
步骤与停止之间的关系。
步骤
步骤
步骤
步骤
步骤
暂停
步骤
----------------------------------------------
每一步都有几种处理方法
1. firstEnter
checkCondition是否满足
2. finalExit
3. loop
4. pause
5. continue
waitting_for_take_sample
pause
stepPause;
stepContinue;
stop
协作?
全局状态
{
take_tip
take_xxx
put_xxx
waitting_for
onPause{
....
}
onStop{
...
}
pauseCondtion{
...
}
}
{
pause...
}
pause_point
```
```
服务的提供者,
服务的使用者,
相互配合呢?
triggerHasTakeSample
doNext
---处理---
| |
| --搬运--(等待)
| |
扫描与预处理(等待) 抛出事件。
机械臂
takeSampleEnd
----------------------------
暂停:
waitingForPause,不需要弹出,只有单次循环结束
停止:
break?
异常:
exception?
第一假如需要允许暂停,则需要将步骤进行拆分,暂停点作为一步。
机械动作的暂停,涉及到,暂停和暂停恢复,不能简单的在代码中添加一个pause就可以的。
协作
机械臂异常--> 中断 --> 退出。
扫描与预处理 --> break = WaittingForFlag(stop)
----------------------
急停:
不恢复--->threadStop-->触发线程异常
停止:
抛出异常
程序打断
打断当前状态,调用状态本身的onStop();
stop--->
停止:
不想用异常
stopPoint.
stopPoint.
stopPoint(if(isStopFlag) throwException)
onStop{
}
waittingForState(其他线程,用户,等触发的逻辑){}
取试管
if(stopFlag){
....
....
return;
}
if(stopFlag){
....
....
....
}
putTubeBak
...
...
...
...
...
..
stop()
公共区+相互等待
取样的时候,机械臂线程出现异常
机械臂退出
尝试一次复位
预处理线程
stop();
直接执行,抛出异常。
stop
stop
stop
isStoped
isStoped
isStoped
前端只触发状态的改变,而不直接触发具体动作。这样可以保证让某些接口可以重复调用而不出错。
stoped......
stoped......
stoped......
耗材不足
---> 暂停补充耗材
pause:
先抛出事件,再执行暂停动作
---------------------------------------------
1. 耗材不足导致的暂停(任何阶段,例如取耗材失败)
2. 用户信息获取失败导致的暂停(扫描阶段)
设备继续运行下
设备触发耗材不足事件
在触发点等待waitingForStop
如果是计数不对,则设置错误标志位。
如果耗材数量不足,则在处理当前试管前,触发stop
//
状态:
1. 满足自己运行的状态。
2. 满足其他服务运行的状态。
```
```
```

9
doc/代码缩写.md

@ -1,9 +0,0 @@
```
```

4
src/main/java/a8k/a8kproj/A8kIdCardDataParseService.java

@ -2,7 +2,7 @@ package a8k.a8kproj;
import a8k.hardware.type.a8kcanprotocol.A8kEcode;
import a8k.a8kproj.opttype.A8kOptX;
import a8k.service.dao.A8kProjInfoDao;
import a8k.service.dao.ProjectBaseInfoDao;
import a8k.service.dao.type.ProjectBaseInfo;
import a8k.service.dao.type.a8kidcard.A8kNormalFn;
import a8k.service.dao.type.a8kidcard.A8kPiecewiseFn;
@ -26,7 +26,7 @@ import java.util.Date;
public class A8kIdCardDataParseService {
@Resource
A8kProjInfoDao projInfoDao;
ProjectBaseInfoDao projInfoDao;
byte[] rawdata;

6
src/main/java/a8k/controler/api/v1/app/data/A8kProjectInfoControler.java

@ -1,6 +1,6 @@
package a8k.controler.api.v1.app.data;
import a8k.service.dao.A8kProjInfoDao;
import a8k.service.dao.ProjectBaseInfoDao;
import a8k.service.dao.type.ProjectBaseInfo;
import a8k.type.appret.ApiV1Ret;
import a8k.type.exception.AppException;
@ -23,12 +23,12 @@ import java.util.List;
public class A8kProjectInfoControler {
@Resource
A8kProjInfoDao a8kProjInfoDao;
ProjectBaseInfoDao projectBaseInfoDao;
@Operation(summary = "读取设备支持的所有项目")
@PostMapping("/getAll")
public ApiV1Ret<List<ProjectBaseInfo>> getAll() throws AppException {
return ApiV1Ret.success(a8kProjInfoDao.getAll());
return ApiV1Ret.success(projectBaseInfoDao.getAll());
}
}

144
src/main/java/a8k/controler/filemgr/FileMgrController.java

@ -1,144 +0,0 @@
//package a8k.controler.filemgr;
//
//
//import a8k.constant.FilePathConstant;
//import a8k.type.appret.ApiV1Ret;
//import io.swagger.v3.oas.annotations.tags.Tag;
//import lombok.extern.slf4j.Slf4j;
//import org.springframework.core.io.FileSystemResource;
//import org.springframework.core.io.Resource;
//import org.springframework.http.ContentDisposition;
//import org.springframework.http.HttpHeaders;
//import org.springframework.http.ResponseEntity;
//import org.springframework.stereotype.Controller;
//import org.springframework.web.bind.annotation.*;
//import org.springframework.web.multipart.MultipartFile;
//
//import java.io.File;
//import java.io.IOException;
//
//@Tag(name = "文件管理")
//@Slf4j
//@Controller
//@RequestMapping(value = "/files")
//public class FileMgrControler {
//
// @GetMapping("")
// public String index() {
// log.info("=======================================================================================");
// return "files/index.html";
// }
//
// @GetMapping("/download/{path}")
// @ResponseBody
// public ResponseEntity<Resource> download(@PathVariable String path) {
// String realpath = String.format(FilePathConstant.FILE_DOWNLOAD_PATH + "/%s", path);
// String contentDisposition = ContentDisposition
// .builder("attachment")
// .filename(path) // Use the original filename
// .build().toString();
// return ResponseEntity.ok()
// .header(HttpHeaders.CONTENT_DISPOSITION, contentDisposition)
// .body(new FileSystemResource(realpath));
// }
//
//
// @PostMapping("/upload")
// @ResponseBody
// public String handleFileUpload(@RequestParam("file") MultipartFile file) {
// String uploadDir = FilePathConstant.FILE_UPLOAD_PATH;
// File updatefilepath = new File(uploadDir);
// if (!updatefilepath.exists()) {
// updatefilepath.mkdirs();
// }
//
// try {
// file.transferTo(new File(updatefilepath.getAbsolutePath() + file.getOriginalFilename()));
// return "success";
// } catch (IOException e) {
// return "fail";
// }
// }
//
//}
// src/main/java/a8k/controller/FileMgrController.java
package a8k.controler.filemgr;
import a8k.service.app.appdata.FileMgrService;
import cn.hutool.core.net.URLDecoder;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.ContentDisposition;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
@Controller
@Slf4j
@RequestMapping("/filemgr")
public class FileMgrController {
@Resource
FileMgrService fileMgrService;
record FileGroup(String name, List<FileInfo> files) {
}
record FileInfo(String filePath, String name) {
}
FileGroup buidOptFileGroup() {
List<FileInfo> files = fileMgrService.getOptReportList().stream()
.map(fileName -> new FileInfo(fileMgrService.getOptReportFilePath(fileName), fileName))
.toList();
return new FileGroup("光学报告", files);
}
@GetMapping("")
public String fileDownloadPage(Model model) {
// Mock data for file groups and files
List<FileGroup> fileGroups = List.of(
buidOptFileGroup()
);
model.addAttribute("fileGroups", fileGroups);
return "filemgr/index.html";
}
@GetMapping("/download/**")
@ResponseBody
public ResponseEntity<org.springframework.core.io.Resource> download(HttpServletRequest request) {
String path = request.getRequestURI().substring("/filemgr/download/".length());
//转义地址中空格TODO:支持处理中文文件名
// path = path.replaceAll("%20", " ");
path = URLDecoder.decode(path, StandardCharsets.UTF_8);
String fileName = Paths.get(path).getFileName().toString();
log.info("Download file:{} path:{}", fileName, path);
File file = new File(path);
String contentDisposition = ContentDisposition
.builder("attachment")
.filename(fileName, StandardCharsets.UTF_8) // Use the original filename
.build().toString();
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, contentDisposition)
.body(new FileSystemResource(file.getAbsolutePath()));
}
}

6
src/main/java/a8k/service/app/appdata/ProjInfoMgrService.java

@ -2,7 +2,7 @@ package a8k.service.app.appdata;
import a8k.hardware.type.a8kcanprotocol.A8kEcode;
import a8k.service.dao.A8kProjExtInfoCardDao;
import a8k.service.dao.A8kProjInfoDao;
import a8k.service.dao.ProjectBaseInfoDao;
import a8k.service.dao.A8kProjOptConfigDao;
import a8k.service.dao.type.ProjExtInfoCard;
import a8k.type.exception.AppException;
@ -22,7 +22,7 @@ public class ProjInfoMgrService {
@Resource
A8kProjExtInfoCardDao a8KProjExtInfoCardDao;
@Resource
A8kProjInfoDao a8KProjInfoDao;
ProjectBaseInfoDao projectBaseInfoDao;
@Resource
A8kProjOptConfigDao a8KProjOptConfigDao;
@ -50,7 +50,7 @@ public class ProjInfoMgrService {
public ProjBuildinInfo getProjBuildInInfo(Integer projId) throws AppException {
ProjBuildinInfo projBuildinInfo = new ProjBuildinInfo();
projBuildinInfo.projBaseInfo = a8KProjInfoDao.findByProjId(projId);
projBuildinInfo.projBaseInfo = projectBaseInfoDao.findByProjId(projId);
projBuildinInfo.projOptInfos = a8KProjOptConfigDao.findByProjIndex(projId);
ZAppChecker.notNull(projBuildinInfo.projBaseInfo, A8kEcode.APPE_A8K_PROJ_UNSUPPORTED, "PROJ:%d IS NOT SUPPORTED", projId);

7
src/main/java/a8k/service/dao/A8kProjInfoDao.java → src/main/java/a8k/service/dao/ProjectBaseInfoDao.java

@ -10,7 +10,7 @@ import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
@Component
public class A8kProjInfoDao extends ZSqlite<ProjectBaseInfo> {
public class ProjectBaseInfoDao extends ZSqlite<ProjectBaseInfo> {
@Resource
JdbcTemplate jdbcTemplate;
@ -33,5 +33,10 @@ public class A8kProjInfoDao extends ZSqlite<ProjectBaseInfo> {
super.add(val);
}
public void loadCfg() {
//
// ProjectBaseInfo
}
}

11
src/main/java/a8k/service/test/MainflowCtrlTestService.java

@ -21,7 +21,7 @@ import a8k.service.app.devicectrl.driver.StepMotorCtrlDriver;
import a8k.service.app.devicectrl.driver.type.StepMotorMId;
import a8k.service.app.devicectrl.exdriver.MotorEnableExDriver;
import a8k.service.dao.A8kProjExtInfoCardDao;
import a8k.service.dao.A8kProjInfoDao;
import a8k.service.dao.ProjectBaseInfoDao;
import a8k.service.dao.A8kProjOptConfigDao;
import a8k.service.test.state.TestModeState;
import a8k.service.test.state.VirtualDevice;
@ -34,15 +34,12 @@ import a8k.type.tube_setting.TubeSetting;
import a8k.type.type.A8kTubeHolderType;
import a8k.type.type.BloodType;
import a8k.type.type.TipGroup;
import a8k.utils.ZJsonHelper;
import a8k.utils.ZList;
import com.fasterxml.jackson.databind.node.ObjectNode;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component
@ -82,7 +79,7 @@ public class MainflowCtrlTestService {
@Resource
A8kProjExtInfoCardDao a8KProjExtInfoCardDao;
@Resource
A8kProjInfoDao a8KProjInfoDao;
ProjectBaseInfoDao projectBaseInfoDao;
@Resource
A8kProjOptConfigDao a8KProjOptConfigDao;
@ -103,13 +100,13 @@ public class MainflowCtrlTestService {
void resetProjDB() {
a8KProjExtInfoCardDao.deleteAll();
a8KProjInfoDao.deleteAll();
projectBaseInfoDao.deleteAll();
a8KProjOptConfigDao.removeAll();
}
void addProjInfo(Class<?> projClass) {
a8KProjExtInfoCardDao.addIdCard(FakeProjInfoFactory.buildIDCardInfo(projClass));
a8KProjInfoDao.add(FakeProjInfoFactory.buildProjectInfo(projClass));
projectBaseInfoDao.add(FakeProjInfoFactory.buildProjectInfo(projClass));
a8KProjOptConfigDao.add(FakeProjInfoFactory.buildA8kProjOptConfigList(projClass));
}

11
src/main/java/a8k/service/test/TestStateMgrService.java

@ -2,7 +2,7 @@ package a8k.service.test;
import a8k.service.dao.A8kProjExtInfoCardDao;
import a8k.service.dao.A8kProjInfoDao;
import a8k.service.dao.ProjectBaseInfoDao;
import a8k.service.dao.A8kProjOptConfigDao;
import a8k.a8kproj.fakeproj.FakeProjInfoFactory;
import a8k.service.test.state.TestModeState;
@ -10,14 +10,11 @@ import a8k.service.test.state.VirtualDevice;
import a8k.type.ConsumableOneChRawResult;
import a8k.type.TubeHolderScanResult;
import a8k.type.TubesScanResult;
import a8k.type.tube_setting.TubeHolderSetting;
import a8k.type.type.A8kTubeHolderType;
import a8k.utils.ReactionPlate2DCodeHelper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class TestStateMgrService {
@ -31,11 +28,11 @@ public class TestStateMgrService {
@Resource
A8kProjOptConfigDao a8KProjOptConfigDao;
@Resource
A8kProjInfoDao a8KProjInfoDao;
ProjectBaseInfoDao projectBaseInfoDao;
public void addProjInfo(Class<?> projClass) {
a8KProjInfoDao.add(FakeProjInfoFactory.buildProjectInfo(projClass));
projectBaseInfoDao.add(FakeProjInfoFactory.buildProjectInfo(projClass));
a8KProjOptConfigDao.add(FakeProjInfoFactory.buildA8kProjOptConfigList(projClass));
}
@ -45,7 +42,7 @@ public class TestStateMgrService {
public void resetProjDB() {
a8KProjInfoDao.deleteAll();
projectBaseInfoDao.deleteAll();
a8KProjOptConfigDao.removeAll();
}

75
src/main/java/a8k/utils/ZCSVUtils.java

@ -0,0 +1,75 @@
package a8k.utils;
import a8k.service.dao.type.ProjectBaseInfo;
import cn.hutool.core.text.csv.CsvUtil;
import cn.hutool.core.text.csv.CsvWriter;
import lombok.extern.slf4j.Slf4j;
import java.io.File;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
@Slf4j
public class ZCSVUtils {
public static <T> void writeCSV(String filePath, Class<T> type, List<T> objects) {
File file = new File(filePath);
try (
CsvWriter csvWriter = CsvUtil.getWriter(file.getAbsoluteFile(), StandardCharsets.UTF_8);
) {
List<String> headers = new ArrayList<>();
var fields = type.getDeclaredFields();
for (var field : fields) {
headers.add(field.getName());
}
String[] headersArray = new String[headers.size()];
headers.toArray(headersArray);
csvWriter.write(headersArray);
for (var object : objects) {
String[] row = new String[fields.length];
for (int i = 0; i < fields.length; i++) {
fields[i].setAccessible(true);
row[i] = String.format("%s", fields[i].get(object));
}
log.info("row: {}", row);
csvWriter.write(row);
}
csvWriter.flush();
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
public static <T> List<T> readCSV(String filePath, Class<T> clazz) {
return null;
}
public static void main(String[] args) {
List<ProjectBaseInfo> objects = new ArrayList<>();
for (int i = 0; i < 10; i++) {
ProjectBaseInfo projectBaseInfo = new ProjectBaseInfo();
projectBaseInfo.projId = i;
projectBaseInfo.projName = "projName" + i;
projectBaseInfo.projShortName = "projShortName" + i;
projectBaseInfo.subProjNum = i;
projectBaseInfo.reactionTemperature = i;
projectBaseInfo.color = "color" + i;
objects.add(projectBaseInfo);
}
writeCSV("tmp/test.csv", ProjectBaseInfo.class, objects);
}
}

0
src/main/resources/optdata/project_base_info.csv

81
src/main/resources/templates/filemgr/index.html

@ -1,81 +0,0 @@
<!-- src/main/resources/templates/filemgr/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件管理</title>
<link href="/background/js/bootstrap.min.css" rel="stylesheet">
<style>
body {
padding: 20px;
}
h1 {
margin-bottom: 20px;
}
.file-group {
margin-bottom: 20px;
}
.file-group h2 {
margin-bottom: 10px;
}
.file-group .file {
margin-bottom: 5px;
}
.file-group .file button {
width: 100%;
}
.download-title {
font-size: 2em;
color: #007bff;
text-align: left;
margin-bottom: 30px;
}
</style>
</head>
<body>
<div class="container">
<h1 class="download-title">文件下载</h1>
<div th:each="group : ${fileGroups}" class="file-group">
<h2 th:text="${group.name}" class="text-primary"></h2>
<div th:each="file : ${group.files}" class="file">
<a th:href="@{'/filemgr/download/' + ${file.filePath}}" class="btn btn-outline-primary download-link">
<span th:text="${file.name}"></span>
</a>
</div>
</div>
</div>
<!-- Notification Modal -->
<div class="modal fade" id="notificationModal" tabindex="-1" role="dialog" aria-labelledby="notificationModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="notificationModalLabel">通知</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
文件下载成功
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
</div>
</div>
</div>
</div>
<script src="/background/js/jquery-3.5.1.min.js"></script>
<script src="/background/js/bootstrap.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
var downloadLinks = document.querySelectorAll('.download-link');
downloadLinks.forEach(function(link) {
link.addEventListener('click', function() {
$('#notificationModal').modal('show');
});
});
});
</script>
</body>
</html>
Loading…
Cancel
Save