31 changed files with 763 additions and 553 deletions
-
11pom.xml
-
2src/main/java/a8k/OS.java
-
3src/main/java/a8k/app/controler/api/v1/app/state/AppFlagStateMgrController.java
-
2src/main/java/a8k/app/controler/api/v1/app/state/DeviceStateController.java
-
97src/main/java/a8k/app/controler/api/v1/app/ws/AppWebSocketEndpointMgr.java
-
78src/main/java/a8k/app/factory/BiLisDoubleTrackFrameFactory.java
-
1src/main/java/a8k/app/interceptor/RefreshAccessInterceptor.java
-
258src/main/java/a8k/app/service/DeviceVirtualStateMgrService.java
-
48src/main/java/a8k/app/service/data/AppUserMgrService.java
-
7src/main/java/a8k/app/service/engineer/state/EngineerModeStateMgrService.java
-
4src/main/java/a8k/app/service/mainctrl/AppDeviceInitCtrlService.java
-
4src/main/java/a8k/app/service/module/SamplePreProcessModule.java
-
2src/main/java/a8k/app/service/setting/AppSettingsMgrService.java
-
94src/main/java/a8k/app/service/statemgr/AppFlagStateMgr.java
-
104src/main/java/a8k/app/service/statemgr/ConsumablesMgrService.java
-
47src/main/java/a8k/app/service/statemgr/DeviceWorkStateMgrService.java
-
11src/main/java/a8k/app/service/statemgr/GStateMgrService.java
-
51src/main/java/a8k/app/service/statemgr/IncubationPlateStateMgr.java
-
14src/main/java/a8k/app/service/statemgr/OptScanModuleStateMgr.java
-
52src/main/java/a8k/app/service/statemgr/PreReactionStateMgr.java
-
208src/main/java/a8k/app/service/statemgr/TubeStateMgr.java
-
40src/main/java/a8k/app/type/DeviceWarningFlagStateGroup.java
-
4src/main/java/a8k/app/type/GState.java
-
2src/main/java/a8k/app/type/a8k/state/Tube.java
-
4src/main/java/a8k/app/type/a8k/state/TubeHolder.java
-
12src/main/java/a8k/app/type/appevent/AppLoginEvent.java
-
2src/main/java/a8k/app/type/lisprotocol/BiLisDoubleTrackFrame.java
-
10src/main/java/a8k/app/type/lisprotocol/BiLisDoubleTrackFrameBuildContext.java
-
22src/main/java/a8k/extui/page/debug/P12TueStateDebugPage.java
-
88src/test/java/a8k/app/factory/BiLisDoubleTrackFrameFactoryTest.java
-
34代码说明/20240718.txt
@ -0,0 +1,258 @@ |
|||
package a8k.app.service; |
|||
|
|||
|
|||
import a8k.app.channel.iflytophald.type.protocol.A8kEcode; |
|||
import a8k.app.constant.AppConstant; |
|||
import a8k.app.factory.FakeA8kConsumableContainerFactory; |
|||
import a8k.app.factory.FakeAppErrorFactory; |
|||
import a8k.app.factory.ZAppPromptFactory; |
|||
import a8k.app.service.utils.ProjInfoUtils; |
|||
import a8k.app.type.AppFlagKey; |
|||
import a8k.app.type.DeviceWarningFlagStateGroup; |
|||
import a8k.app.type.PreReactionGrid; |
|||
import a8k.app.type.PreReactionGridGroup; |
|||
import a8k.app.type.a8k.BloodType; |
|||
import a8k.app.type.a8k.ConsumableGroup; |
|||
import a8k.app.type.a8k.LittleBottleConsumableType; |
|||
import a8k.app.type.a8k.proj.ProjBriefInfo; |
|||
import a8k.app.type.a8k.state.*; |
|||
import a8k.app.type.a8k.state.enumtype.IncubationSubTankState; |
|||
import a8k.app.type.a8k.state.enumtype.TubeHolderState; |
|||
import a8k.app.type.a8k.state.enumtype.TubeState; |
|||
import a8k.app.type.error.AppError; |
|||
import a8k.app.utils.ZList; |
|||
import cn.hutool.core.util.ObjectUtil; |
|||
import lombok.RequiredArgsConstructor; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import java.util.Date; |
|||
|
|||
@Component |
|||
@RequiredArgsConstructor |
|||
@Slf4j |
|||
public class DeviceVirtualStateMgrService { |
|||
|
|||
|
|||
/** |
|||
* 获取虚拟孵育盘状态 |
|||
* @return 虚拟孵育盘状态 |
|||
*/ |
|||
synchronized public IncubationPlate getVirtualIncubationPlateState() { |
|||
IncubationPlate incubationPlate = new IncubationPlate(); |
|||
int i = 0; |
|||
for (IncubationSubTank subtank : incubationPlate.subtanks) { |
|||
i++; |
|||
switch (i) { |
|||
case 1 -> subtank.state = IncubationSubTankState.RESERVED; |
|||
case 2 -> subtank.state = IncubationSubTankState.WAITING_FOR_DROP; |
|||
case 3 -> subtank.state = IncubationSubTankState.INCUBATING; |
|||
case 4 -> subtank.state = IncubationSubTankState.ERROR; |
|||
case 5 -> subtank.state = IncubationSubTankState.INCUBATION_COMPLETE; |
|||
default -> subtank.state = IncubationSubTankState.EMPTY; |
|||
} |
|||
|
|||
subtank.sampleInfo = new SampleInfo( |
|||
"250109_001E01", 0, false, false, BloodType.WHOLE_BLOOD, "B3A7KK8DKF", "250109_001E" |
|||
); |
|||
|
|||
|
|||
subtank.sampleInfo.bloodType = BloodType.WHOLE_BLOOD; |
|||
subtank.sampleInfo.sampleBarcode = "112334455667"; |
|||
subtank.sampleInfo.userid = "250109_001E"; |
|||
subtank.projInfo = new ProjBriefInfo(); |
|||
subtank.projInfo.projId = 1; |
|||
subtank.projInfo.projName = "hsCRP"; |
|||
subtank.projInfo.projShortName = "CA"; |
|||
subtank.projInfo.color = "#DC143C"; |
|||
subtank.sampleInfo.sampleId = "250109_001E01"; |
|||
subtank.projId = 1; |
|||
subtank.lotId = "CA123456"; |
|||
subtank.isEmergency = i == 2; |
|||
if (subtank.state.equals(IncubationSubTankState.ERROR)) { |
|||
subtank.error = new AppError(A8kEcode.PROJ_CARD_ERROR_WRONG_UNSUPPORTED, ""); |
|||
subtank.errorInfo = ZAppPromptFactory.buildAppPrompt(subtank.error); |
|||
} |
|||
subtank.startIncubatedTime = new Date().getTime(); |
|||
subtank.incubatedTimeSec = 3 * 60; |
|||
subtank.remainTimeSec = 3 * 60; |
|||
} |
|||
return incubationPlate; |
|||
} |
|||
|
|||
|
|||
static void addProjInfoToTube(Tube tube, Integer projId) { |
|||
ProjBriefInfo projBriefInfo = FakeA8kConsumableContainerFactory.buildFakeProjBriefInfo(projId); |
|||
tube.getProjInfo().add(projBriefInfo); |
|||
tube.getProjIds().add(projBriefInfo.projId); |
|||
} |
|||
|
|||
static Tube createFakeTube(Integer pos) { |
|||
Tube tube = new Tube(pos); |
|||
tube.setSampleId("250109_001E0" + pos); |
|||
tube.setBloodType(BloodType.WHOLE_BLOOD); |
|||
tube.setSampleBarcode("112334455667"); |
|||
tube.setUserid(String.format("250109_%dE", pos)); |
|||
|
|||
switch (pos) { |
|||
case 1 -> tube.setState(TubeState.EMPTY); |
|||
case 2 -> { |
|||
tube.setState(TubeState.ERROR); |
|||
tube.setError(FakeAppErrorFactory.buildAEConsumeNotEnoughError()); |
|||
tube.setErrorInfo(ZAppPromptFactory.buildAppPrompt(tube.getError())); |
|||
|
|||
addProjInfoToTube(tube, 1); |
|||
addProjInfoToTube(tube, 2); |
|||
|
|||
} |
|||
case 3, 4, 5, 6 -> { |
|||
tube.setState(TubeState.PROCESS_COMPLETE); |
|||
addProjInfoToTube(tube, 1); |
|||
addProjInfoToTube(tube, 2); |
|||
addProjInfoToTube(tube, 3); |
|||
} |
|||
case 7 -> { |
|||
tube.setState(TubeState.PROCESSING); |
|||
addProjInfoToTube(tube, 1); |
|||
addProjInfoToTube(tube, 2); |
|||
addProjInfoToTube(tube, 3); |
|||
} |
|||
case 8, 9, 10 -> { |
|||
tube.setState(TubeState.TO_BE_PROCESSED); |
|||
addProjInfoToTube(tube, 1); |
|||
addProjInfoToTube(tube, 2); |
|||
} |
|||
} |
|||
tube.setIsHighTube(pos % 2 == 0); //假设偶数位置为高位管 |
|||
|
|||
return tube; |
|||
} |
|||
|
|||
public TubeHolder getVirtualTubeHolderState() { |
|||
TubeHolder tubeHolder = new TubeHolder(); |
|||
tubeHolder.setTubes(new Tube[]{ |
|||
createFakeTube(1), |
|||
createFakeTube(2), |
|||
createFakeTube(3), |
|||
createFakeTube(4), |
|||
createFakeTube(5), |
|||
createFakeTube(6), |
|||
createFakeTube(7), |
|||
createFakeTube(8), |
|||
createFakeTube(9), |
|||
createFakeTube(10) |
|||
}); |
|||
tubeHolder.setState(TubeHolderState.PROCESSING); |
|||
return tubeHolder; |
|||
} |
|||
|
|||
public EmergencyTubePos getVirtualEmergencyTube() { |
|||
var tubePosState = new EmergencyTubePos(); |
|||
tubePosState.tube = createFakeTube(0); |
|||
tubePosState.tube.setPos(0); |
|||
tubePosState.tube.setIsEmergency(true); |
|||
return tubePosState; |
|||
} |
|||
|
|||
|
|||
public PreReactionGridGroup getFakePreReactionGridGroup(ConsumableGroup group) { |
|||
PreReactionGridGroup gridGroup = new PreReactionGridGroup(group); |
|||
gridGroup.group = group; |
|||
if (group.off % 2 == 0) { |
|||
gridGroup.installed = true; |
|||
gridGroup.hasSomeGridInReacting = true; |
|||
gridGroup.hasSomeGridReactedCompleted = true; |
|||
} else { |
|||
gridGroup.installed = false; |
|||
gridGroup.hasSomeGridInReacting = false; |
|||
gridGroup.hasSomeGridReactedCompleted = false; |
|||
} |
|||
gridGroup.consumableType = LittleBottleConsumableType.BufferSolution; |
|||
var projBuildInInfo = FakeA8kConsumableContainerFactory.buildFakeProjBuildInInfo(group.off); |
|||
gridGroup.projBriefInfo = ProjInfoUtils.buildProjBrefInfo(projBuildInInfo); |
|||
gridGroup.version = 10; |
|||
for (int i = 0; i < AppConstant.CONSUMABLE_NUM; i++) { |
|||
|
|||
gridGroup.grids.get(i).projId = projBuildInInfo.projId; |
|||
gridGroup.grids.get(i).projBuildinInfo = projBuildInInfo; |
|||
gridGroup.grids.get(i).projExtInfoCard = null; |
|||
|
|||
switch (i / 5) { |
|||
case 0 -> { |
|||
gridGroup.grids.get(i).state = PreReactionGrid.State.UNINSTALL; |
|||
} |
|||
case 1 -> { |
|||
gridGroup.grids.get(i).state = PreReactionGrid.State.USED; |
|||
} |
|||
|
|||
case 2 -> { |
|||
gridGroup.grids.get(i).state = PreReactionGrid.State.REACTION_COMPLETED; |
|||
gridGroup.grids.get(i).sampleInfo.userid = "UID123"; |
|||
} |
|||
case 3 -> { |
|||
gridGroup.grids.get(i).state = PreReactionGrid.State.REACTING; |
|||
gridGroup.grids.get(i).sampleInfo.userid = "UID123"; |
|||
gridGroup.grids.get(i).reactionRemainingTime = 3 * 60L; |
|||
} |
|||
case 4 -> { |
|||
gridGroup.grids.get(i).state = PreReactionGrid.State.TO_BE_USED; |
|||
} |
|||
} |
|||
} |
|||
return gridGroup; |
|||
} |
|||
|
|||
public SensorState getVirtualSensorState() { |
|||
SensorState sensorState = new SensorState(); |
|||
sensorState.setPboxTemperature(24); |
|||
sensorState.setIncubateBoxTemperature(25); |
|||
sensorState.setWasteBinFullFlag(false); |
|||
return sensorState; |
|||
} |
|||
|
|||
// |
|||
// VIRTUAL_OPERATION |
|||
// |
|||
public DeviceWarningFlagStateGroup getDeviceFlagStateVirtualStateInitVal(DeviceWarningFlagStateGroup deviceWarningFlagState) { |
|||
|
|||
DeviceWarningFlagStateGroup virtualWarningState = ObjectUtil.clone(deviceWarningFlagState); //初始化时复制一份状态版本号 |
|||
virtualWarningState.version = 10000; |
|||
|
|||
//设置VirtualState初始值 |
|||
var consumeNotEnoughState = virtualWarningState.find(AppFlagKey.ConsumeNotEnoughState); |
|||
consumeNotEnoughState.state = true; // |
|||
consumeNotEnoughState.errorDetailInfo = FakeAppErrorFactory.buildAEConsumeNotEnoughError(); |
|||
consumeNotEnoughState.errorPromptInfo = ZAppPromptFactory.buildAppPrompt(consumeNotEnoughState.errorDetailInfo); |
|||
|
|||
var tipNotEnoughState = virtualWarningState.find(AppFlagKey.TipNotEnoughState); |
|||
tipNotEnoughState.state = true; // |
|||
tipNotEnoughState.errorDetailInfo = FakeAppErrorFactory.buildError(A8kEcode.APPE_TIP_NOT_ENOUGH); |
|||
tipNotEnoughState.errorPromptInfo = ZAppPromptFactory.buildAppPrompt(tipNotEnoughState.errorDetailInfo); |
|||
|
|||
var infeedExceptionState = virtualWarningState.find(AppFlagKey.InfeedExceptionState); |
|||
infeedExceptionState.state = true; // |
|||
infeedExceptionState.errorDetailInfo = FakeAppErrorFactory.buildAEHardwareError(); |
|||
infeedExceptionState.errorPromptInfo = ZAppPromptFactory.buildAppPrompt(infeedExceptionState.errorDetailInfo); |
|||
|
|||
var outfeedAreaFullState = virtualWarningState.find(AppFlagKey.OutfeedAreaFullState); |
|||
outfeedAreaFullState.state = true; // |
|||
outfeedAreaFullState.errorDetailInfo = FakeAppErrorFactory.buildError(A8kEcode.APPE_OUTFEED_AREA_IS_FULL); |
|||
outfeedAreaFullState.errorPromptInfo = ZAppPromptFactory.buildAppPrompt(outfeedAreaFullState.errorDetailInfo); |
|||
|
|||
var wasteBinFullState = virtualWarningState.find(AppFlagKey.WasteBinFull); |
|||
wasteBinFullState.state = true; // |
|||
wasteBinFullState.errorDetailInfo = FakeAppErrorFactory.buildError(A8kEcode.APPE_WAST_BIN_IS_FULL); |
|||
wasteBinFullState.errorPromptInfo = ZAppPromptFactory.buildAppPrompt(wasteBinFullState.errorDetailInfo); |
|||
|
|||
if (AppFlagKey.InfeedPPSFlag.enabled) |
|||
virtualWarningState.find(AppFlagKey.InfeedPPSFlag).state = true; //入料区光电 |
|||
if (AppFlagKey.OutfeedPPSFlag.enabled) |
|||
virtualWarningState.find(AppFlagKey.OutfeedPPSFlag).state = false; //入料区光电 |
|||
if (AppFlagKey.TubeholderChannelPPSFlag.enabled) |
|||
virtualWarningState.find(AppFlagKey.TubeholderChannelPPSFlag).state = true; //入料区光电 |
|||
if (AppFlagKey.PlateBoxLidPPSState.enabled) |
|||
virtualWarningState.find(AppFlagKey.PlateBoxLidPPSState).state = false; //入料区光电 |
|||
return virtualWarningState; |
|||
|
|||
} |
|||
} |
@ -0,0 +1,40 @@ |
|||
package a8k.app.type; |
|||
|
|||
import a8k.app.service.statemgr.AppFlagStateMgr; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
|
|||
public class DeviceWarningFlagStateGroup implements java.io.Serializable { |
|||
public List<AppFlagStateMgr.AppFlagState> states = new ArrayList<>(); |
|||
public Integer version = 0; //状态版本号 |
|||
|
|||
// 别删,被前端使用 |
|||
public Boolean isHasWarningTriggerFlag() { |
|||
for (AppFlagStateMgr.AppFlagState state : states) { |
|||
if (state.flagType.equals(AppFlagType.WarningFlag) && state.state) { |
|||
return true; //如果有任意一个状态为true,则返回true |
|||
} |
|||
} |
|||
return false; //如果没有任意一个状态为true,则返回false |
|||
} |
|||
|
|||
public Boolean isHasErrorTriggerFlag() { |
|||
for (AppFlagStateMgr.AppFlagState state : states) { |
|||
if (state.flagType.equals(AppFlagType.ErrorFlag) && state.state) { |
|||
return true; //如果有任意一个状态为true,则返回true |
|||
} |
|||
} |
|||
return false; //如果没有任意一个状态为true,则返回false |
|||
} |
|||
|
|||
public AppFlagStateMgr.AppFlagState find(AppFlagKey appFlagKey) { |
|||
for (AppFlagStateMgr.AppFlagState state : states) { |
|||
if (state.keyName.equals(appFlagKey)) { |
|||
return state; |
|||
} |
|||
} |
|||
// AppFlagStateMgr.log.error("find: Unknown DeviceWarningFlagState: {}", appFlagKey.name()); |
|||
return null; //如果没有找到,返回null |
|||
} |
|||
} |
@ -0,0 +1,12 @@ |
|||
package a8k.app.type.appevent; |
|||
|
|||
import a8k.app.dao.type.db.AppUser; |
|||
|
|||
public class AppLoginEvent extends AppEvent { |
|||
public AppUser user; |
|||
|
|||
public AppLoginEvent(AppUser user) { |
|||
super(AppLoginEvent.class.getSimpleName()); |
|||
this.user = user; |
|||
} |
|||
} |
@ -0,0 +1,10 @@ |
|||
package a8k.app.type.lisprotocol; |
|||
|
|||
public class BiLisDoubleTrackFrameBuildContext { |
|||
public BiLisDoubleTrackFrame frame; |
|||
public byte[] frameBytes; |
|||
public String frameContentStr; |
|||
public char[] checksum; |
|||
public Boolean buildSuccess = true; |
|||
|
|||
} |
@ -0,0 +1,88 @@ |
|||
package a8k.app.factory; |
|||
|
|||
import a8k.app.constant.AppConstant; |
|||
import a8k.app.type.lisprotocol.BiLisDoubleTrackFrameBuildContext; |
|||
import a8k.app.utils.ByteArrayUtils; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.junit.jupiter.api.Test; |
|||
|
|||
import static org.junit.jupiter.api.Assertions.*; |
|||
|
|||
@Slf4j |
|||
class BiLisDoubleTrackFrameFactoryTest { |
|||
|
|||
@Test |
|||
void createRFrame() { |
|||
|
|||
} |
|||
|
|||
@Test |
|||
void createRFrameBytes() { |
|||
} |
|||
|
|||
@Test |
|||
void createOFrame() { |
|||
} |
|||
|
|||
@Test |
|||
void createQFrameBytes() { |
|||
BiLisDoubleTrackFrameBuildContext cxt = BiLisDoubleTrackFrameFactory.createQFrameBytes("A8000", "123456789"); |
|||
log.info("frameBytes {}", ByteArrayUtils.toByteString(cxt.frameBytes)); |
|||
log.info("frameContent {}",cxt.frameContentStr); |
|||
log.info("checksum {} {}", cxt.checksum[0], cxt.checksum[1]); |
|||
|
|||
|
|||
assertNotNull(cxt); |
|||
assertTrue(cxt.buildSuccess); |
|||
assertEquals("Q|A8000|^123456789|\r", cxt.frameContentStr); |
|||
} |
|||
|
|||
@Test |
|||
void serialize() { |
|||
} |
|||
|
|||
@Test |
|||
void createBiLisDoubleTrackFrame() { |
|||
} |
|||
|
|||
@Test |
|||
void serializeFrameContent() { |
|||
} |
|||
|
|||
@Test |
|||
void removeTrailingPipes() { |
|||
String str = "O|A5000p|1111||^NT-proBNP^^|||20220401112632\r"; |
|||
String result = BiLisDoubleTrackFrameFactory.removeTrailingPipes(str); |
|||
assertEquals("O|A5000p|1111||^NT-proBNP^^|||20220401112632\r", result); |
|||
|
|||
str = "O|A5000p|1111||^NT-proBNP^^|||20220401112632||||\r"; |
|||
result = BiLisDoubleTrackFrameFactory.removeTrailingPipes(str); |
|||
assertEquals("O|A5000p|1111||^NT-proBNP^^|||20220401112632|\r", result); |
|||
} |
|||
|
|||
@Test |
|||
void computeChecksum() { |
|||
char[] checksum = BiLisDoubleTrackFrameFactory.computeChecksum("O|A5000p|1111||^NT-proBNP^^|||20220401112632\r"); |
|||
assertEquals(2, checksum.length); |
|||
assertEquals('0', checksum[0]); |
|||
assertEquals('2', checksum[1]); |
|||
} |
|||
|
|||
@Test |
|||
void intToHexChars() { |
|||
char[] chars; |
|||
chars = BiLisDoubleTrackFrameFactory.intToHexChars(0x23); |
|||
assertEquals(2, chars.length); |
|||
assertEquals('2', chars[0]); |
|||
assertEquals('3', chars[1]); |
|||
|
|||
chars = BiLisDoubleTrackFrameFactory.intToHexChars(0x0A); |
|||
assertEquals(2, chars.length); |
|||
assertEquals('0', chars[0]); |
|||
assertEquals('a', chars[1]); |
|||
} |
|||
|
|||
@Test |
|||
void strArraySafeGet() { |
|||
} |
|||
} |
@ -1,34 +0,0 @@ |
|||
|
|||
``` |
|||
1.添加下面几种数据类型,每种类型都对应一种json,json的格式已经在对应的类中给出 |
|||
Pos3d("Pos3d"),// |
|||
Pos2d("Pos2d"),// |
|||
Bool("Bool"),// |
|||
Int("Int"),// |
|||
String("String"),// |
|||
LargeBottleBufferPosInfo("LargeBottleBufferPosInfo"),// |
|||
Plates2dCodeScanPosInfo("Plates2dCodeScanPosInfo"),// |
|||
SmallBottleBufferPosInfo("SmallBottleBufferPosInfo"),// |
|||
TipPickUpPosInfo("TipPickUpPosInfo"),// |
|||
TubeSamplePos("TubeSamplePos"),// |
|||
Float("Float"); |
|||
2.Hbot控制涉及到参数的访问和更改写在,HbotControlParamsService |
|||
3.反应板夹搬运(板夹出仓,转盘转动...,勾取反应板)相关参数的访问和更改写在 ReactionPlatesTransmitCtrlParamService |
|||
4.样本预处理(脱毛盖帽摇匀)涉及到参数的访问和更改写在 SamplesPreProcessModuleCtrlParamsService |
|||
|
|||
5.所有硬件控制涉及到的参数都写在HardwareServiceSetting数据库中,数据库中的条目索引,由key+ServiceName作为索引标识。 |
|||
|
|||
6.之前写的进出料控制,也改成类似的风格。(这个我没有改) |
|||
7.删除掉试管类型数据库,试管类型信息存储在A8kTubeType枚举中。试管架间距,参数也放在MotorTubeRackMoveCtrlServiceParameterDao中,(我没有删,你改完之后再删) |
|||
|
|||
``` |
|||
|
|||
``` |
|||
1. 添加AppWarningNotifyEvent,前端接收到该事件,弹窗显示dispalyInfo即可 |
|||
2. 添加AppStepNotifyEvent,前端收到该事件,在页面的信息栏显示dispalyInfo即可。 |
|||
``` |
|||
|
|||
``` |
|||
regIndex 换成枚举类型 |
|||
A8kCanBusService添加,read_reg,get_reg 方法 |
|||
``` |
Write
Preview
Loading…
Cancel
Save
Reference in new issue