Przeglądaj źródła

feat(exam): implement setting of hardware device exposure values in examination, including kv, ma, ms, mas, etc.

sw 4 tygodni temu
rodzic
commit
3c9e512eff

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

@@ -83,3 +83,83 @@ const getAprExposureParams = async (
 };
 
 export { getAprDetails, getAprExposureParams };
+
+interface DeviceActionMessage {
+  deviceUri: string;
+  reqName: string;
+  reqParam: string;
+  reqTransaction: string;
+  reqClientID: string;
+}
+
+const pathOfGenerator = 'DIOS/DEVICE/Generator';
+
+const sendDeviceAction = async (reqName: string) => {
+  const message: DeviceActionMessage = {
+    deviceUri: pathOfGenerator,
+    reqName,
+    reqParam: `{"P0":"default"}`,
+    reqTransaction: '',
+    reqClientID: '',
+  };
+
+  try {
+    await axiosInstance.post('/auth/device/action', message);
+  } catch (error) {
+    console.error(`[${reqName}] Failed to send device action:`, error);
+    throw error;
+  }
+};
+
+const incKv = async () => {
+  await sendDeviceAction('IncParam_KV');
+};
+
+const decKv = async () => {
+  await sendDeviceAction('DecParam_KV');
+};
+
+const incMas = async () => {
+  await sendDeviceAction('IncParam_MAS');
+};
+
+const decMas = async () => {
+  await sendDeviceAction('DecParam_MAS');
+};
+
+const incMa = async () => {
+  await sendDeviceAction('IncParam_MA');
+};
+
+const decMa = async () => {
+  await sendDeviceAction('DecParam_MA');
+};
+
+const incMs = async () => {
+  await sendDeviceAction('IncParam_MS');
+};
+
+const decMs = async () => {
+  await sendDeviceAction('DecParam_MS');
+};
+
+const incDensity = async () => {
+  await sendDeviceAction('IncParam_Density');
+};
+
+const decDensity = async () => {
+  await sendDeviceAction('DecParam_Density');
+};
+
+export {
+  incKv,
+  decKv,
+  incMas,
+  decMas,
+  incMa,
+  decMa,
+  incMs,
+  decMs,
+  incDensity,
+  decDensity,
+};

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

@@ -0,0 +1,262 @@
+/**
+ * 响应用户的操作,不需要组件传递参数,这些操作对应的方法有:
+ *
+1. 增加KV
+2. 减少KV
+3. 增加MAS
+4. 减少MAS
+5. 增加MA
+6. 减少MA
+7. 增加MS
+8. 减少MS
+9. 增加Density
+10. 减少Density
+ * 以上方法内部逻辑是向服务发送设置曝光参数,KV MA MS MAS;间接调用api method,通过 调用 @/src/API/exam/APRActions.ts  中定义的方法完成调用,并不直接调用api
+ * 接收device mqtt发来的新参数。device mqtt会触发事件 emitter.emit('NEW_KV',parsedMessage.CONTEXT); 这是接收KV新值的,还有对应的其他新曝光参数的新值
+ * 把 device mqtt 发来的新参数传递给 @/src/states/exam/aprSlice.ts  。 dispatch thunk的 action,有fullfilled pending rejected ,比如 incMS.fulfilled ;考虑 device mqtt 发来新曝光参数会超时,超时的情况下向 aprSLice发送 rejected 。
+ */
+
+import {
+  incKv as incKvFromAction,
+  decKv as decKvFromAction,
+  incMas as incMasFromAction,
+  decMas as decMasFromAction,
+  incMa as incMaFromAction,
+  decMa as decMaFromAction,
+  incMs as incMsFromAction,
+  decMs as decMsFromAction,
+  incDensity as incDensityFromAction,
+  decDensity as decDensityFromAction,
+} from '../../API/exam/APRActions';
+import emitter from '../../utils/eventEmitter';
+import {
+  incKV,
+  decKV,
+  incMAS,
+  decMAS,
+  incMA,
+  decMA,
+  incMS,
+  decMS,
+  incDensity,
+  decDensity,
+} from '../../states/exam/aprSlice';
+import store from '../../states/store';
+
+class ParaSettingCoordinator {
+  constructor() {
+    this.dispatch = store.dispatch;
+    this.initializeEventListeners();
+  }
+  dispatch;
+
+  initializeEventListeners() {
+    emitter.on('NEW_KV', (newValue) => this.handleNewKV(newValue));
+    emitter.on('NEW_MAS', (newValue) => this.handleNewMAS(newValue));
+    emitter.on('NEW_MA', (newValue) => this.handleNewMA(newValue));
+    emitter.on('NEW_MS', (newValue) => this.handleNewMS(newValue));
+    emitter.on('NEW_DENSITY', (newValue) => this.handleNewDensity(newValue));
+  }
+
+  handleNewKV(newValue) {
+    this.dispatch({
+      type: incKV.fulfilled.type,
+      payload: newValue,
+      meta: { arg: newValue, requestId: '' },
+    });
+  }
+
+  handleNewMAS(newValue) {
+    this.dispatch({
+      type: incMAS.fulfilled.type,
+      payload: newValue,
+      meta: { arg: newValue, requestId: '' },
+    });
+  }
+
+  handleNewMA(newValue) {
+    this.dispatch({
+      type: incMA.fulfilled.type,
+      payload: newValue,
+      meta: { arg: newValue, requestId: '' },
+    });
+  }
+
+  handleNewMS(newValue) {
+    this.dispatch({
+      type: incMS.fulfilled.type,
+      payload: newValue,
+      meta: { arg: newValue, requestId: '' },
+    });
+  }
+
+  handleNewDensity(newValue) {
+    this.dispatch({
+      type: incDensity.fulfilled.type,
+      payload: newValue,
+      meta: { arg: newValue, requestId: '' },
+    });
+  }
+
+  increaseKV() {
+    this.dispatch({
+      type: incKV.pending.type,
+      meta: { arg: 0, requestId: '' },
+    });
+    try {
+      incKvFromAction();
+    } catch (error) {
+      this.dispatch({
+        type: incKV.rejected.type,
+        payload: error,
+        meta: { arg: 0, requestId: '' },
+      });
+    }
+  }
+
+  decreaseKV() {
+    this.dispatch({
+      type: decKV.pending.type,
+      meta: { arg: 0, requestId: '' },
+    });
+    try {
+      decKvFromAction();
+    } catch (error) {
+      this.dispatch({
+        type: decKV.rejected.type,
+        payload: error,
+        meta: { arg: 0, requestId: '' },
+      });
+    }
+  }
+
+  increaseMAS() {
+    this.dispatch({
+      type: incMAS.pending.type,
+      meta: { arg: 0, requestId: '' },
+    });
+    try {
+      incMasFromAction();
+    } catch (error) {
+      this.dispatch({
+        type: incMAS.rejected.type,
+        payload: error,
+        meta: { arg: 0, requestId: '' },
+      });
+    }
+  }
+
+  decreaseMAS() {
+    this.dispatch({
+      type: decMAS.pending.type,
+      meta: { arg: 0, requestId: '' },
+    });
+    try {
+      decMasFromAction();
+    } catch (error) {
+      this.dispatch({
+        type: decMAS.rejected.type,
+        payload: error,
+        meta: { arg: 0, requestId: '' },
+      });
+    }
+  }
+
+  increaseMA() {
+    this.dispatch({
+      type: incMA.pending.type,
+      meta: { arg: 0, requestId: '' },
+    });
+    try {
+      incMaFromAction();
+    } catch (error) {
+      this.dispatch({
+        type: incMA.rejected.type,
+        payload: error,
+        meta: { arg: 0, requestId: '' },
+      });
+    }
+  }
+
+  decreaseMA() {
+    this.dispatch({
+      type: decMA.pending.type,
+      meta: { arg: 0, requestId: '' },
+    });
+    try {
+      decMaFromAction();
+    } catch (error) {
+      this.dispatch({
+        type: decMA.rejected.type,
+        payload: error,
+        meta: { arg: 0, requestId: '' },
+      });
+    }
+  }
+
+  increaseMS() {
+    this.dispatch({
+      type: incMS.pending.type,
+      meta: { arg: 0, requestId: '' },
+    });
+    try {
+      incMsFromAction();
+    } catch (error) {
+      this.dispatch({
+        type: incMS.rejected.type,
+        payload: error,
+        meta: { arg: 0, requestId: '' },
+      });
+    }
+  }
+
+  decreaseMS() {
+    this.dispatch({
+      type: decMS.pending.type,
+      meta: { arg: 0, requestId: '' },
+    });
+    try {
+      decMsFromAction();
+    } catch (error) {
+      this.dispatch({
+        type: decMS.rejected.type,
+        payload: error,
+        meta: { arg: 0, requestId: '' },
+      });
+    }
+  }
+
+  increaseDensity() {
+    this.dispatch({
+      type: incDensity.pending.type,
+      meta: { arg: 0, requestId: '' },
+    });
+    try {
+      incDensityFromAction();
+    } catch (error) {
+      this.dispatch({
+        type: incDensity.rejected.type,
+        payload: error,
+        meta: { arg: 0, requestId: '' },
+      });
+    }
+  }
+
+  decreaseDensity() {
+    this.dispatch({
+      type: decDensity.pending.type,
+      meta: { arg: 0, requestId: '' },
+    });
+    try {
+      decDensityFromAction();
+    } catch (error) {
+      this.dispatch({
+        type: decDensity.rejected.type,
+        payload: error,
+        meta: { arg: 0, requestId: '' },
+      });
+    }
+  }
+}
+
+export default new ParaSettingCoordinator();

+ 56 - 0
src/domain/mqttServiceForDevice.ts

@@ -35,6 +35,10 @@ interface MqttMessage {
 const MQTT_TOPIC_2 = 'CCOS/DEVICE/Generator/Notify/GENERATORSYNCSTATE'; //发生器
 const MQTT_TOPIC = 'CCOS/DEVICE/Detector/Notify/XrayON';
 const MQTT_TOPIC_DETECTOR = 'CCOS/DEVICE/Detector/Notify/DetectorStatus'; //探测器
+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 options = {
   clean: true,
@@ -104,6 +108,38 @@ const startListening = () => {
         );
       }
     });
+    mqttClient.subscribe(MQTT_KV, (err) => {
+      if (err) {
+        console.error(
+          '[mqttServiceForDevice] Failed to subscribe to topic MQTT_KV',
+          err
+        );
+      }
+    });
+    mqttClient.subscribe(MQTT_MAS, (err) => {
+      if (err) {
+        console.error(
+          '[mqttServiceForDevice] Failed to subscribe to topic MQTT_MAS',
+          err
+        );
+      }
+    });
+    mqttClient.subscribe(MQTT_MA, (err) => {
+      if (err) {
+        console.error(
+          '[mqttServiceForDevice] Failed to subscribe to topic MQTT_MA',
+          err
+        );
+      }
+    });
+    mqttClient.subscribe(MQTT_MS, (err) => {
+      if (err) {
+        console.error(
+          '[mqttServiceForDevice] Failed to subscribe to topic MQTT_MS',
+          err
+        );
+      }
+    });
   });
 
   mqttClient.on('error', (error) => {
@@ -123,6 +159,26 @@ const startListening = () => {
       const parsedMessage: MqttMessage = JSON.parse(message.toString());
       handleMqttMessageFromDetector(parsedMessage);
     }
+    if (topic === MQTT_KV) {
+      const parsedMessage: MqttMessage = JSON.parse(message.toString());
+      console.log(`从设备发来新的KV值 ${parsedMessage.CONTEXT}`);
+      emitter.emit('NEW_KV', parsedMessage.CONTEXT);
+    }
+    if (topic === MQTT_MAS) {
+      const parsedMessage: MqttMessage = JSON.parse(message.toString());
+      console.log(`从设备发来新的MAS值 ${parsedMessage.CONTEXT}`);
+      emitter.emit('NEW_MAS', parsedMessage.CONTEXT);
+    }
+    if (topic === MQTT_MA) {
+      const parsedMessage: MqttMessage = JSON.parse(message.toString());
+      console.log(`从设备发来新的MA值 ${parsedMessage.CONTEXT}`);
+      emitter.emit('NEW_MA', parsedMessage.CONTEXT);
+    }
+    if (topic === MQTT_MS) {
+      const parsedMessage: MqttMessage = JSON.parse(message.toString());
+      console.log(`从设备发来新的MS值 ${parsedMessage.CONTEXT}`);
+      emitter.emit('NEW_MS', parsedMessage.CONTEXT);
+    }
   });
 };
 

+ 40 - 3
src/pages/exam/ContentAreaLarge.tsx

@@ -33,6 +33,7 @@ import BodyPositionDetail from './components/BodyPositionDetail';
 import { AppDispatch } from '@/states/store';
 import { useRef } from 'react';
 import Icon from '@/components/Icon';
+import ParaSettingCoordinator from '@/domain/exam/paraSettingCoordinator';
 
 const ContentAreaLarge = () => {
   const dispatch = useDispatch<AppDispatch>();
@@ -127,6 +128,13 @@ const ContentAreaLarge = () => {
             onChange={(value) =>
               dispatch(setAprConfig({ ...aprConfig, mA: value ?? 0 }))
             }
+            onStep={(value, info) => {
+              if (info.type === 'up') {
+                ParaSettingCoordinator.increaseMA();
+              } else {
+                ParaSettingCoordinator.decreaseMA();
+              }
+            }}
           />
           <InputNumber
             placeholder="ms"
@@ -135,6 +143,13 @@ const ContentAreaLarge = () => {
             onChange={(value) =>
               dispatch(setAprConfig({ ...aprConfig, ms: value ?? 0 }))
             }
+            onStep={(value, info) => {
+              if (info.type === 'up') {
+                ParaSettingCoordinator.increaseMS();
+              } else {
+                ParaSettingCoordinator.decreaseMS();
+              }
+            }}
           />
           <InputNumber
             placeholder="mAs"
@@ -143,14 +158,29 @@ const ContentAreaLarge = () => {
             onChange={(value) =>
               dispatch(setAprConfig({ ...aprConfig, mAs: value ?? 0 }))
             }
+            onStep={(value, info) => {
+              if (info.type === 'up') {
+                ParaSettingCoordinator.increaseMAS();
+              } else {
+                ParaSettingCoordinator.decreaseMAS();
+              }
+            }}
           />
           <InputNumber
             placeholder="KV"
             style={{ width: '100%', marginBottom: 8 }}
             value={aprConfig.kV ?? undefined}
-            onChange={(value) =>
-              dispatch(setAprConfig({ ...aprConfig, kV: value ?? 0 }))
-            }
+            // onChange={(value) =>
+            //   dispatch(setAprConfig({ ...aprConfig, kV: value ?? 0 }))
+            // }
+
+            onStep={(value, info) => {
+              if (info.type === 'up') {
+                ParaSettingCoordinator.increaseKV();
+              } else {
+                ParaSettingCoordinator.decreaseKV();
+              }
+            }}
           />
           <InputNumber
             placeholder="density"
@@ -159,6 +189,13 @@ const ContentAreaLarge = () => {
             onChange={(value) =>
               dispatch(setAprConfig({ ...aprConfig, AECDensity: value ?? 0 }))
             }
+            onStep={(value, info) => {
+              if (info.type === 'up') {
+                ParaSettingCoordinator.increaseDensity();
+              } else {
+                ParaSettingCoordinator.decreaseDensity();
+              }
+            }}
           />
         </div>
         <Flex align="center" justify="start" gap="middle" wrap>

+ 152 - 2
src/states/exam/aprSlice.ts

@@ -1,4 +1,5 @@
-import { createSlice, PayloadAction, Middleware } from '@reduxjs/toolkit';
+/* eslint-disable */
+import { createSlice, PayloadAction, Middleware, createAsyncThunk } from '@reduxjs/toolkit';
 import { AprConfig, getAprExposureParams } from '../../API/exam/APRActions';
 import { workstationIdFromWorkstation } from '../workstation';
 
@@ -8,6 +9,10 @@ interface AprState {
   workstation: string;
   isAECEnabled: boolean;
   currentExposureMode: string;
+  /**
+   * 是否正在设置中
+   */
+  isPending:boolean;
 }
 
 const initialState: AprState = {
@@ -29,6 +34,7 @@ const initialState: AprState = {
   workstation: '',
   isAECEnabled: false,
   currentExposureMode: '',
+  isPending:false
 };
 
 const aprSlice = createSlice({
@@ -51,8 +57,111 @@ const aprSlice = createSlice({
       state.currentExposureMode = action.payload;
     },
   },
+  extraReducers: (builder) => {
+    builder
+      .addCase(incKV.pending, (state) => {
+        console.log('Increasing kV...');
+      })
+      .addCase(incKV.fulfilled, (state, action) => {
+        console.log('kV increased successfully');
+        state.aprConfig.kV = action.payload;
+      })
+      .addCase(incKV.rejected, (state, action) => {
+        console.error('Failed to increase kV', action.error);
+      })
+      .addCase(decKV.pending, (state) => {
+        console.log('Decreasing kV...');
+      })
+      .addCase(decKV.fulfilled, (state, action) => {
+        console.log('kV decreased successfully');
+        state.aprConfig.kV = action.payload;
+      })
+      .addCase(decKV.rejected, (state, action) => {
+        console.error('Failed to decrease kV', action.error);
+      })
+      .addCase(incMA.pending, (state) => {
+        console.log('Increasing mA...');
+      })
+      .addCase(incMA.fulfilled, (state, action) => {
+        console.log('mA increased successfully');
+        state.aprConfig.mA = action.payload;
+      })
+      .addCase(incMA.rejected, (state, action) => {
+        console.error('Failed to increase mA', action.error);
+      })
+      .addCase(decMA.pending, (state) => {
+        console.log('Decreasing mA...');
+      })
+      .addCase(decMA.fulfilled, (state, action) => {
+        console.log('mA decreased successfully');
+        state.aprConfig.mA = action.payload;
+      })
+      .addCase(decMA.rejected, (state, action) => {
+        console.error('Failed to decrease mA', action.error);
+      })
+      .addCase(incMAS.pending, (state) => {
+        console.log('Increasing mAs...');
+      })
+      .addCase(incMAS.fulfilled, (state, action) => {
+        console.log('mAs increased successfully');
+        state.aprConfig.mAs = action.payload;
+      })
+      .addCase(incMAS.rejected, (state, action) => {
+        console.error('Failed to increase mAs', action.error);
+      })
+      .addCase(decMAS.pending, (state) => {
+        console.log('Decreasing mAs...');
+      })
+      .addCase(decMAS.fulfilled, (state, action) => {
+        console.log('mAs decreased successfully');
+        state.aprConfig.mAs = action.payload;
+      })
+      .addCase(decMAS.rejected, (state, action) => {
+        console.error('Failed to decrease mAs', action.error);
+      })
+      .addCase(incMS.pending, (state) => {
+        console.log('Increasing ms...');
+      })
+      .addCase(incMS.fulfilled, (state, action) => {
+        console.log('ms increased successfully');
+        state.aprConfig.ms = action.payload;
+      })
+      .addCase(incMS.rejected, (state, action) => {
+        console.error('Failed to increase ms', action.error);
+      })
+      .addCase(decMS.pending, (state) => {
+        console.log('Decreasing ms...');
+      })
+      .addCase(decMS.fulfilled, (state, action) => {
+        console.log('ms decreased successfully');
+        state.aprConfig.ms = action.payload;
+      })
+      .addCase(decMS.rejected, (state, action) => {
+        console.error('Failed to decrease ms', action.error);
+      })
+      .addCase(incDensity.pending, (state) => {
+        console.log('Increasing Density...');
+      })
+      .addCase(incDensity.fulfilled, (state, action) => {
+        console.log('Density increased successfully');
+        state.aprConfig.AECDensity = action.payload;
+      })
+      .addCase(incDensity.rejected, (state, action) => {
+        console.error('Failed to increase Density', action.error);
+      })
+      .addCase(decDensity.pending, (state) => {
+        console.log('Decreasing Density...');
+      })
+      .addCase(decDensity.fulfilled, (state, action) => {
+        console.log('Density decreased successfully');
+        state.aprConfig.AECDensity = action.payload;
+      })
+      .addCase(decDensity.rejected, (state, action) => {
+        console.error('Failed to decrease Density', action.error);
+      });
+  },
 });
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
+ 
 const aprMiddleware: Middleware = (store) => (next) => (action: any) => {
   if (
     action.type === aprSlice.actions.setBodysize.type ||
@@ -87,6 +196,46 @@ const aprMiddleware: Middleware = (store) => (next) => (action: any) => {
   }
   return next(action);
 };
+ 
+export const incKV = createAsyncThunk<number, number>('apr/incKV', async (amount: number) => {
+  return amount;
+});
+
+export const decKV = createAsyncThunk<number, number>('apr/decKV', async (amount: number) => {
+  return amount;
+});
+
+export const incMA = createAsyncThunk<number, number>('apr/incMA', async (amount: number) => {
+  return amount;
+});
+
+export const decMA = createAsyncThunk<number, number>('apr/decMA', async (amount: number) => {
+  return amount;
+});
+
+export const incMAS = createAsyncThunk<number, number>('apr/incMAS', async (amount: number) => {
+  return amount;
+});
+
+export const decMAS = createAsyncThunk<number, number>('apr/decMAS', async (amount: number) => {
+  return amount;
+});
+
+export const incMS = createAsyncThunk<number, number>('apr/incMS', async (amount: number) => {
+  return amount;
+});
+
+export const decMS = createAsyncThunk<number, number>('apr/decMS', async (amount: number) => {
+  return amount;
+});
+
+export const incDensity = createAsyncThunk<number, number>('apr/incDensity', async (amount: number) => {
+  return amount;
+});
+
+export const decDensity = createAsyncThunk<number, number>('apr/decDensity', async (amount: number) => {
+  return amount;
+});
 
 export const {
   setAprConfig,
@@ -95,5 +244,6 @@ export const {
   setIsAECEnabled,
   setCurrentExposureMode,
 } = aprSlice.actions;
+
 export default aprSlice.reducer;
 export { aprMiddleware };