11<template >
2- <BasePage >
3- <BaseSection
4- title="Device setup"
5- subtitle="Initial configuration of your OpenScan device."
2+ <BasePage content-class="col-12 col-md-10 col-lg-8">
3+ <div class =" q-mb-lg" >
4+ <div class =" text-h5" >Device setup</div >
5+ <div class =" text-body2 text-grey-7" >
6+ Initial configuration of your OpenScan device.
7+ </div >
8+ </div >
9+ <BaseWizard
10+ v-model =" activeStepId "
11+ :steps =" steps "
12+ finish-label="Finish setup"
13+ next-label="Next"
14+ back-label="Back"
15+ @finish =" handleFinishSetup "
616 >
7- <BaseWizard
8- v-model =" activeStepId "
9- :steps =" steps "
10- finish-label="Finish setup"
11- next-label="Next"
12- back-label="Back"
13- @finish =" handleFinishSetup "
14- >
1517 <template #default =" { step } " >
1618 <div v-if =" step.id === 'connection'" >
1719 <div class =" text-subtitle1 q-mb-sm" >Device configuration</div >
4345 </BaseList >
4446 </div >
4547 </div >
48+ <div v-else-if =" step.id === 'rotor-direction'" >
49+ <div class =" text-subtitle1 q-mb-sm" >Rotor direction</div >
50+ <p class =" q-mb-md" >
51+ Use the buttons below to move the rotor slightly up or down and confirm the movement matches the labels.
52+ </p >
53+ <div class =" rotor-controls q-mb-md" >
54+ <BaseButtonSecondary
55+ icon="keyboard_arrow_up"
56+ label="Move up"
57+ :loading =" rotorMoveAction === ' up' "
58+ :disable =" isRotorControlDisabled "
59+ @click =" handleRotorMove (' up' )"
60+ />
61+ <BaseButtonSecondary
62+ icon="keyboard_arrow_down"
63+ label="Move down"
64+ :loading =" rotorMoveAction === ' down' "
65+ :disable =" isRotorControlDisabled "
66+ @click =" handleRotorMove (' down' )"
67+ />
68+ </div >
69+ <div class =" text-body2 q-mb-sm" >
70+ Does the rotor move in the direction shown on the buttons? If yes, click "Next". If not, click "Reverse direction".
71+ </div >
72+ <div class =" rotor-reverse" >
73+ <BaseButtonPrimary
74+ outline
75+ icon="swap_vert"
76+ label="Reverse direction"
77+ :loading =" isReversingRotorDirection "
78+ :disable =" isReverseDisabled "
79+ @click =" handleReverseRotorDirection "
80+ />
81+ </div >
82+ <div class =" text-body2 text-grey-7 q-mt-sm text-center" >
83+ Current direction: {{ rotorDirectionLabel }}
84+ </div >
85+ </div >
4686 <div v-else-if =" step.id === 'hardware'" >
4787 <div class =" text-subtitle1 q-mb-sm" >Rotor position</div >
4888 <p >Verify that the initial rotor position is correct as in the picture.</p >
74114 />
75115 <BaseButtonSecondary
76116 icon="flip"
77- label="Mirror"
117+ label="Mirror vertically "
78118 :loading =" orientationAction === ' mirror' "
79119 :disable =" isOrientationUpdating "
80120 size="md"
136176 </div >
137177 </div >
138178 </template >
139- </BaseWizard >
140- </BaseSection >
179+ </BaseWizard >
141180 </BasePage >
142181</template >
143182
@@ -146,7 +185,6 @@ import { computed, onMounted, ref } from 'vue'
146185import { useQuasar } from ' quasar'
147186import { useRouter } from ' vue-router'
148187import BasePage from ' components/base/BasePage.vue'
149- import BaseSection from ' components/base/BaseSection.vue'
150188import BaseWizard from ' components/base/BaseWizard.vue'
151189import BaseList from ' components/base/BaseList.vue'
152190import BaseListItem from ' components/base/BaseListItem.vue'
@@ -157,11 +195,19 @@ import motorPositionImage from 'src/assets/openscan_motor_position.jpg'
157195import { useDeviceStore } from ' src/stores/device'
158196import CameraFastPreview from ' components/camera/CameraFastPreview.vue'
159197import { useCameraStore } from ' src/stores/camera'
160- import { listConfigFiles , setConfigFile , updateCameraNameSettings , type DeviceConfigRequest } from ' src/generated/api'
198+ import {
199+ listConfigFiles ,
200+ moveMotorByDegree ,
201+ setConfigFile ,
202+ updateCameraNameSettings ,
203+ updateMotorNameSettings ,
204+ type DeviceConfigRequest
205+ } from ' src/generated/api'
161206import { apiClient } from ' src/services/apiClient'
162207
163208const steps = [
164209 { id: ' connection' , label: ' Model' , caption: ' Select the device model.' },
210+ { id: ' rotor-direction' , label: ' Rotor Direction' , caption: ' Verify motor direction.' },
165211 { id: ' hardware' , label: ' Rotor Position' , caption: ' Verify initial motor position.' },
166212 { id: ' orientation' , label: ' Camera Orientation' , caption: ' Adjust camera orientation.' },
167213 { id: ' test-scan' , label: ' Finish' , caption: ' ' }
@@ -194,6 +240,34 @@ const isNextDisabled = computed(
194240 isApplyingConfig .value
195241)
196242
243+ const ROTOR_MOTOR_NAME = ' rotor'
244+ const rotorMoveAction = ref <' up' | ' down' | null >(null )
245+ const isReversingRotorDirection = ref (false )
246+
247+ const rotorMotor = computed (() => deviceStore .device ?.motors ?.[ROTOR_MOTOR_NAME ] ?? null )
248+ const rotorDirection = computed < 1 | - 1 | null > (() => {
249+ const value = rotorMotor .value ?.settings ?.direction
250+ return value === 1 || value === - 1 ? value : null
251+ })
252+ const rotorDirectionLabel = computed (() => {
253+ if (rotorDirection .value === 1 ) return ' Forward (1)'
254+ if (rotorDirection .value === - 1 ) return ' Reverse (-1)'
255+ return ' Unknown'
256+ })
257+
258+ const isRotorControlDisabled = computed (
259+ () =>
260+ rotorMoveAction .value !== null ||
261+ isReversingRotorDirection .value ||
262+ deviceStore .status !== ' open'
263+ )
264+ const isReverseDisabled = computed (
265+ () =>
266+ isReversingRotorDirection .value ||
267+ deviceStore .status !== ' open' ||
268+ rotorDirection .value === null
269+ )
270+
197271const orientationCamera = computed (() => {
198272 const options = cameraStore .cameraOptions
199273 if (! options .length ) return null
@@ -218,7 +292,25 @@ async function loadConfigs() {
218292 try {
219293 const data = await listConfigFiles <true >({ client: apiClient , throwOnError: true })
220294 const configs = (data as any )?.configs ?? []
221- configOptions .value = configs as DeviceConfigFile []
295+ const filtered = (configs as DeviceConfigFile []).filter (
296+ (config ) => config .filename !== ' device_config.json'
297+ )
298+
299+ filtered .sort ((a , b ) => {
300+ const shieldA = (a .shield ?? ' ' ).toLowerCase ()
301+ const shieldB = (b .shield ?? ' ' ).toLowerCase ()
302+ if (shieldA !== shieldB ) return shieldB .localeCompare (shieldA )
303+
304+ const nameA = (a .name ?? a .filename ).toLowerCase ()
305+ const nameB = (b .name ?? b .filename ).toLowerCase ()
306+ return nameA .localeCompare (nameB )
307+ })
308+
309+ configOptions .value = filtered
310+
311+ if (selectedConfigPath .value && ! filtered .some ((c ) => c .path === selectedConfigPath .value )) {
312+ selectedConfigPath .value = null
313+ }
222314 } catch (error ) {
223315 console .error (' Failed to load device configurations' , error )
224316 $q .notify ({ type: ' negative' , message: ' Failed to load device configurations' })
@@ -255,6 +347,55 @@ async function handleNext(goNext: () => void) {
255347 goNext ()
256348}
257349
350+ async function handleRotorMove(direction : ' up' | ' down' ) {
351+ if (rotorMoveAction .value !== null ) {
352+ return
353+ }
354+
355+ rotorMoveAction .value = direction
356+ const degrees = direction === ' up' ? - 10 : 10
357+
358+ try {
359+ await deviceStore .ensureConnected ()
360+ await moveMotorByDegree <true >({
361+ client: apiClient ,
362+ throwOnError: true ,
363+ path: { motor_name: ROTOR_MOTOR_NAME },
364+ body: { degrees }
365+ })
366+ } catch (error ) {
367+ console .error (' Failed to move rotor motor' , error )
368+ $q .notify ({ type: ' negative' , message: ' Failed to move rotor motor' })
369+ } finally {
370+ rotorMoveAction .value = null
371+ }
372+ }
373+
374+ async function handleReverseRotorDirection() {
375+ if (isReversingRotorDirection .value || rotorDirection .value === null ) {
376+ return
377+ }
378+
379+ const nextDirection = rotorDirection .value === 1 ? - 1 : 1
380+ isReversingRotorDirection .value = true
381+
382+ try {
383+ await deviceStore .ensureConnected ()
384+ await updateMotorNameSettings <true >({
385+ client: apiClient ,
386+ throwOnError: true ,
387+ path: { name: ROTOR_MOTOR_NAME },
388+ body: { direction: nextDirection }
389+ })
390+ await deviceStore .refreshFromRest ()
391+ } catch (error ) {
392+ console .error (' Failed to reverse rotor direction' , error )
393+ $q .notify ({ type: ' negative' , message: ' Failed to reverse rotor direction' })
394+ } finally {
395+ isReversingRotorDirection .value = false
396+ }
397+ }
398+
258399const ROTATE_RIGHT_MAP: Record <number , number > = {
259400 1 : 6 ,
260401 6 : 3 ,
@@ -271,10 +412,10 @@ const MIRROR_MAP: Record<number, number> = {
271412 2 : 1 ,
272413 3 : 4 ,
273414 4 : 3 ,
274- 5 : 8 ,
275- 8 : 5 ,
276- 6 : 7 ,
277- 7 : 6
415+ 5 : 6 ,
416+ 6 : 5 ,
417+ 7 : 8 ,
418+ 8 : 7
278419}
279420
280421function getSafeOrientationFlag(): number | null {
@@ -347,6 +488,17 @@ async function handleFinishSetup() {
347488 margin : 0 auto ;
348489}
349490
491+ .rotor-controls {
492+ display : flex ;
493+ justify-content : center ;
494+ gap : 8px ;
495+ }
496+
497+ .rotor-reverse {
498+ display : flex ;
499+ justify-content : center ;
500+ }
501+
350502.rotor-image {
351503 display : block ;
352504 max-width : 100% ;
0 commit comments