Pārlūkot izejas kodu

feat: 实现曝光模式切换功能并集成设备API下发

- 在 APRActions.ts 中导出 setExposureModeFromAction API
- 在 aprSlice.ts 中添加 setExposureModeThunk 异步thunk和状态处理
- 在 ParaSettingCoordinator 中实现 setExposureMode 方法和MQTT事件监听
- 在 mqttServiceForDevice.ts 中添加NEW_EXPOSURE_MODE事件触发
- 更新三个UI组件使用受控组件模式,确保只在设备确认后才更新显示
- 利用React受控组件特性实现优雅的API-first更新流程

改动文件:
- src/API/exam/APRActions.ts
- src/states/exam/aprSlice.ts
- src/domain/exam/paraSettingCoordinator.ts
- src/domain/mqttServiceForDevice.ts
- src/pages/exam/ContentAreaLarge.tsx
- src/pages/exam/ContentAreaMedium.tsx
- src/pages/exam/ContentAreaSmall.tsx
sw 4 dienas atpakaļ
vecāks
revīzija
d538756603

+ 24 - 0
src/API/exam/APRActions.ts

@@ -183,6 +183,29 @@ const SetAPR = async (reqParam: string) => {
   }
 };
 
+const setExposureModeFromAction = async (reqParam: string) => {
+  const message: DeviceActionMessage = {
+    deviceUri: pathOfGenerator,
+    reqName: 'SetValue_TECHMODE',
+    reqParam,
+    reqTransaction: '',
+    reqClientID: '',
+  };
+
+  try {
+    console.info(`[SetValue_TECHMODE][开始调用曝光模式API] 参数是 ${reqParam} `);
+    const response = await axiosInstance.post('/auth/device/action', message);
+    if (response.data.code !== '0x000000') {
+      throw new Error(
+        `[调用切换曝光模式API] Error code: ${response.data.code}, Description: ${response.data.description}`
+      );
+    }
+  } catch (error) {
+    console.error(`[SetValue_TECHMODE][切换曝光模式调用api失败] Failed to send device action:`, error);
+    throw error;
+  }
+};
+
 export {
   incKv,
   decKv,
@@ -197,4 +220,5 @@ export {
   incThickness,
   decThickness,
   SetAPR,
+  setExposureModeFromAction,
 };

+ 53 - 0
src/domain/exam/paraSettingCoordinator.ts

@@ -32,6 +32,7 @@ import {
   decDensity as decDensityFromAction,
   incThickness as incThicknessFromAction,
   decThickness as decThicknessFromAction,
+  setExposureModeFromAction,
 } from '../../API/exam/APRActions';
 import emitter from '../../utils/eventEmitter';
 import {
@@ -47,6 +48,7 @@ import {
   decDensity,
   incThickness,
   decThickness,
+  setExposureModeThunk,
 } from '../../states/exam/aprSlice';
 import store from '../../states/store';
 
@@ -66,6 +68,9 @@ class ParaSettingCoordinator {
     emitter.on('NEW_THICKNESS', (newValue) =>
       this.handleNewThickness(newValue)
     );
+    emitter.on('NEW_EXPOSURE_MODE', (newValue) =>
+      this.handleNewExposureMode(newValue)
+    );
   }
 
   handleNewKV(newValue) {
@@ -117,6 +122,54 @@ class ParaSettingCoordinator {
     });
   }
 
+  handleNewExposureMode(newValue) {
+    // 将数字转换为字符串: 0 -> 'time', 1 -> 'mAs'
+    const mode = newValue === 0 ? 'time' : 'mAs';
+    console.info('有新的曝光模式从设备端到来。handleNewExposureMode', newValue, '转换为:', mode);
+    this.dispatch({
+      type: setExposureModeThunk.fulfilled.type,
+      payload: mode,
+      meta: { arg: mode, requestId: '' },
+    });
+  }
+
+  setExposureMode(mode: string) {
+    console.info('[ParaSettingCoordinator] 切换曝光模式:', mode);
+
+    // 1. Dispatch pending
+    this.dispatch({
+      type: setExposureModeThunk.pending.type,
+      meta: { arg: mode, requestId: '' },
+    });
+
+    // 2. 调用API
+    try {
+      // 将UI字符串转换为API需要的数字: 'mAs' -> 1, 'time' -> 0
+      let reqParam;
+      if (mode === 'mAs') {
+        // 构造参数对象
+        reqParam = JSON.stringify({
+          P0: "1"
+        });
+      } else if (mode === 'time') {
+        // 构造参数对象
+        reqParam = JSON.stringify({
+          P0: "0"
+        });
+      }
+
+      console.info('[ParaSettingCoordinator] 下发曝光模式参数:', reqParam);
+      setExposureModeFromAction(reqParam);
+    } catch (error) {
+      console.error('[ParaSettingCoordinator] 切换曝光模式失败:', error);
+      this.dispatch({
+        type: setExposureModeThunk.rejected.type,
+        payload: error,
+        meta: { arg: mode, requestId: '' },
+      });
+    }
+  }
+
   increaseKV() {
     this.dispatch({
       type: incKV.pending.type,

+ 6 - 0
src/domain/mqttServiceForDevice.ts

@@ -39,6 +39,7 @@ const MQTT_KV = 'CCOS/DEVICE/Generator/Notify/KV'; //KV值
 const MQTT_MAS = 'CCOS/DEVICE/Generator/Notify/MAS';
 const MQTT_MA = 'CCOS/DEVICE/Generator/Notify/MA';
 const MQTT_MS = 'CCOS/DEVICE/Generator/Notify/MS';
+const MQTT_TECHMODE = 'CCOS/DEVICE/Generator/Notify/TECHMODE';
 const MQTT_ERRORLIST_NOTIFY = 'CCOS/DEVICE/Generator/Notify/ErrorList';
 
 interface ErrorListMessage {
@@ -248,6 +249,11 @@ const startListening = () => {
       console.log(`从设备发来新的MS值 ${parsedMessage.CONTEXT}`);
       emitter.emit('NEW_MS', parsedMessage.CONTEXT);
     }
+    if (topic === MQTT_TECHMODE) {
+      const parsedMessage: MqttMessage = JSON.parse(message.toString());
+      console.log(`从设备发来新的曝光模式值 ${parsedMessage.CONTEXT}`);
+      emitter.emit('NEW_EXPOSURE_MODE', parsedMessage.CONTEXT);
+    }
 
     if (topic === MQTT_ERRORLIST_NOTIFY) {
       const parsedMessage: ErrorListMessage = JSON.parse(message.toString());

+ 1 - 2
src/pages/exam/ContentAreaLarge.tsx

@@ -28,7 +28,6 @@ import {
   setWorkstation,
   setThickness,
   setIsAECEnabled,
-  setCurrentExposureMode,
 } from '../../states/exam/aprSlice';
 import BodyPositionList from './components/BodyPositionList';
 import BodyPositionDetail from './components/BodyPositionDetail';
@@ -81,7 +80,7 @@ const ContentAreaLarge = () => {
   };
 
   const handleExposureModeChange = (value: string) => {
-    dispatch(setCurrentExposureMode(value));
+    ParaSettingCoordinator.setExposureMode(value);
   };
 
   const handleResetParameters = async () => {

+ 2 - 2
src/pages/exam/ContentAreaMedium.tsx

@@ -23,9 +23,9 @@ import { useSelector, useDispatch } from 'react-redux';
 import { RootState } from '@/states/store';
 import {
   setAprConfig,
-  setCurrentExposureMode,
   setIsAECEnabled,
 } from '@/states/exam/aprSlice';
+import ParaSettingCoordinator from '@/domain/exam/paraSettingCoordinator';
 
 const ContentAreaMedium = () => {
   const dispatch = useDispatch();
@@ -38,7 +38,7 @@ const ContentAreaMedium = () => {
   );
 
   const handleExposureModeChange = (value: string) => {
-    dispatch(setCurrentExposureMode(value));
+    ParaSettingCoordinator.setExposureMode(value);
   };
 
   const handleAECChange = (checked: boolean) => {

+ 2 - 2
src/pages/exam/ContentAreaSmall.tsx

@@ -24,9 +24,9 @@ import { useSelector, useDispatch } from 'react-redux';
 import { RootState } from '@/states/store';
 import {
   setAprConfig,
-  setCurrentExposureMode,
   setIsAECEnabled,
 } from '@/states/exam/aprSlice';
+import ParaSettingCoordinator from '@/domain/exam/paraSettingCoordinator';
 
 const ContentAreaSmall = ({ className }: { className?: string }) => {
   const dispatch = useDispatch();
@@ -39,7 +39,7 @@ const ContentAreaSmall = ({ className }: { className?: string }) => {
   );
 
   const handleExposureModeChange = (value: string) => {
-    dispatch(setCurrentExposureMode(value));
+    ParaSettingCoordinator.setExposureMode(value);
   };
 
   const handleAECChange = (checked: boolean) => {

+ 17 - 0
src/states/exam/aprSlice.ts

@@ -186,6 +186,19 @@ const aprSlice = createSlice({
       .addCase(decThickness.rejected, (state, action) => {
         console.error('Failed to decrease Thickness', action.error);
       })
+      .addCase(setExposureModeThunk.pending, (state) => {
+        console.log('Setting exposure mode...');
+        state.isPending = true;
+      })
+      .addCase(setExposureModeThunk.fulfilled, (state, action) => {
+        console.log('Exposure mode set successfully:', action.payload);
+        state.currentExposureMode = action.payload;
+        state.isPending = false;
+      })
+      .addCase(setExposureModeThunk.rejected, (state, action) => {
+        console.error('Failed to set exposure mode', action.error);
+        state.isPending = false;
+      })
       .addCase(setSelectedBodyPosition, (state, action: PayloadAction<ExtendedBodyPosition | null>) => {
         console.log('APR Extra Reducer triggered for setSelectedBodyPosition action');
         const selectedBodyPosition = action.payload;
@@ -417,6 +430,10 @@ export const decThickness = createAsyncThunk<number, number>('apr/decThickness',
   return amount;
 });
 
+export const setExposureModeThunk = createAsyncThunk<string, string>('apr/setExposureMode', async (mode: string) => {
+  return mode;
+});
+
 export const {
   setAprConfig,
   setBodysize,