Coverage for src/pytribeam/insertable_devices.py: 0%

248 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2026-06-16 18:30 +0000

1#!/usr/bin/python3 

2""" 

3Insertable Devices Module 

4========================== 

5 

6This module contains functions for managing and controlling insertable devices in the microscope, including detectors, EBSD, and EDS systems. 

7 

8Functions 

9--------- 

10detector_insertable(microscope: tbt.Microscope, detector: tbt.DetectorType) -> bool 

11 Determine whether or not the built-in microscope detector is insertable and return its state. 

12 

13detector_state(microscope: tbt.Microscope, detector: tbt.DetectorType) -> tbt.RetractableDeviceState 

14 Determine the state of the detector, only valid if the detector is insertable. 

15 

16detectors_will_collide(microscope: tbt.Microscope, detector_to_insert: tbt.DetectorType) -> bool 

17 Determine if a collision may occur when inserting the specified detector. 

18 

19device_access(microscope: tbt.Microscope) -> tbt.ViewQuad 

20 Switch to the upper-left quadrant and assign the electron beam as the active device. 

21 

22insert_EBSD(microscope: tbt.Microscope) -> bool 

23 Insert the EBSD camera into the microscope. 

24 

25insert_EDS(microscope: tbt.Microscope) -> bool 

26 Insert the EDS camera into the microscope. 

27 

28insert_detector(microscope: tbt.Microscope, detector: tbt.DetectorType, time_delay_s: float = 0.5) -> bool 

29 Insert the selected detector into the microscope. 

30 

31retract_all_devices(microscope: tbt.Microscope, enable_EBSD: bool, enable_EDS: bool) -> bool 

32 Retract all insertable devices, including microscope detectors and EBSD/EDS detectors if integrated. 

33 

34connect_EBSD() -> tbt.RetractableDeviceState 

35 Connect to the EBSD system and retrieve the camera status. 

36 

37retract_EBSD(microscope: tbt.Microscope) -> bool 

38 Retract the EBSD camera from the microscope. 

39 

40connect_EDS() -> tbt.RetractableDeviceState 

41 Connect to the EDS system and retrieve the camera status. 

42 

43retract_EDS(microscope: tbt.Microscope) -> bool 

44 Retract the EDS detector from the microscope. 

45 

46retract_device(microscope: tbt.Microscope, detector: tbt.DetectorType) -> bool 

47 Retract the specified detector from the microscope. 

48 

49CCD_pause(microscope: tbt.Microscope, quad: tbt.ViewQuad = tbt.ViewQuad.LOWER_RIGHT) -> bool 

50 Pause the CCD camera, typically used after device or stage movement. 

51 

52CCD_view(microscope: tbt.Microscope, quad: tbt.ViewQuad = tbt.ViewQuad.LOWER_RIGHT) -> bool 

53 Visualize detector or stage movement for the user using the CCD camera. 

54 

55specimen_current(microscope: tbt.Microscope, hfw_mm=Constants.specimen_current_hfw_mm, delay_s=Constants.specimen_current_delay_s) -> float 

56 Measure the specimen current using the electron beam and return the value in nA. 

57""" 

58 

59# Default python modules 

60# from functools import singledispatch 

61import time 

62import warnings 

63 

64# 3rd party module 

65 

66# Local scripts 

67import pytribeam.constants as cs 

68from pytribeam.constants import Constants 

69import pytribeam.image as img 

70 

71try: 

72 from pytribeam.laser import tfs_laser as external 

73except: 

74 pass 

75import pytribeam.types as tbt 

76 

77 

78def detector_insertable( 

79 microscope: tbt.Microscope, 

80 detector: tbt.DetectorType, 

81) -> bool: 

82 """ 

83 Determine whether or not the built-in microscope detector is insertable and return its state. 

84 

85 This function checks if the specified detector is being read by Autoscript and if it is insertable. 

86 

87 Parameters 

88 ---------- 

89 microscope : tbt.Microscope 

90 The microscope object for which to check the detector. 

91 detector : tbt.DetectorType 

92 The type of the detector to check. 

93 

94 Returns 

95 ------- 

96 bool 

97 True if the detector is insertable, False otherwise. 

98 

99 Warnings 

100 -------- 

101 UserWarning 

102 If the detector type is invalid for the currently selected device or if the detector is not found on the system. 

103 """ 

104 # check if the detector is being read by Autoscript 

105 try: 

106 # make requested detector the active detector 

107 microscope.detector.type.value = detector.value 

108 except: 

109 warnings.warn( 

110 f"""Warning. Invalid detector type of "{detector.value}" for currently selected device  

111 of "{tbt.Device(microscope.imaging.get_active_device()).value}" or detector not found on this system. 

112 Detector will be assumed to not be insertable.""" 

113 ) 

114 return False 

115 

116 # check if it is insertable 

117 try: 

118 state = microscope.detector.state 

119 if state == tbt.RetractableDeviceState.STATIONARY.value: 

120 return False 

121 return True 

122 except Exception: 

123 return False 

124 

125 

126def detector_state( 

127 microscope: tbt.Microscope, 

128 detector: tbt.DetectorType, 

129) -> tbt.RetractableDeviceState: 

130 """ 

131 Determine the state of the detector, only valid if the detector is insertable. 

132 

133 This function checks if the specified detector is insertable and returns its state. 

134 

135 Parameters 

136 ---------- 

137 microscope : tbt.Microscope 

138 The microscope object for which to check the detector state. 

139 detector : tbt.DetectorType 

140 The type of the detector to check. 

141 

142 Returns 

143 ------- 

144 tbt.RetractableDeviceState 

145 The state of the detector if it is insertable, None otherwise. 

146 """ 

147 # check if the detector is being read by Autoscriptdevice_access(microscope) 

148 # try: 

149 # return tbt.RetractableDeviceState(microscope.detector.state) 

150 # except Exception: 

151 # return tbt.RetractableDeviceState.STATIONARY 

152 if not detector_insertable( 

153 microscope=microscope, 

154 detector=detector, 

155 ): 

156 return tbt.RetractableDeviceState.STATIONARY 

157 return tbt.RetractableDeviceState(microscope.detector.state) 

158 

159 

160def detectors_will_collide( 

161 microscope: tbt.Microscope, 

162 detector_to_insert: tbt.DetectorType, 

163) -> bool: 

164 """ 

165 Determine if a collision may occur when inserting the specified detector. 

166 

167 This function checks if inserting the specified detector will cause a collision with any other detectors. 

168 

169 Parameters 

170 ---------- 

171 microscope : tbt.Microscope 

172 The microscope object for which to check for potential collisions. 

173 detector_to_insert : tbt.DetectorType 

174 The type of the detector to insert. 

175 

176 Returns 

177 ------- 

178 bool 

179 True if a collision may occur, False otherwise. 

180 """ 

181 device_retracted = tbt.RetractableDeviceState.RETRACTED.value 

182 for detector_combo in Constants.detector_collisions: 

183 if detector_to_insert in detector_combo: 

184 for detector in detector_combo: 

185 if detector == detector_to_insert: 

186 continue 

187 if detector == tbt.DetectorType.EDS: 

188 if external.EDS_CameraStatus() != device_retracted: 

189 return True 

190 elif detector == tbt.DetectorType.EBSD: 

191 if external.EBSD_CameraStatus() != device_retracted: 

192 return True 

193 else: 

194 state = detector_state(microscope=microscope, detector=detector) 

195 if state.value != device_retracted: 

196 return True 

197 return False 

198 

199 

200def device_access(microscope: tbt.Microscope) -> tbt.ViewQuad: 

201 """ 

202 Switch to the upper-left quadrant and assign the electron beam as the active device. 

203 

204 This function switches the view to the upper-left quadrant and assigns the electron beam as the active device, which is the only device with access to insertable devices like the CBS/ABS detector. Other devices, like the ion beam, CCD, or Nav-Cam, do not have CBS/ABS access. 

205 

206 Parameters 

207 ---------- 

208 microscope : tbt.Microscope 

209 The microscope object for which to switch the view and assign the active device. 

210 

211 Returns 

212 ------- 

213 tbt.ViewQuad 

214 The upper-left quadrant view. 

215 """ 

216 img.set_view( 

217 microscope=microscope, 

218 quad=tbt.ViewQuad.UPPER_LEFT, 

219 ) 

220 img.set_beam_device( 

221 microscope=microscope, 

222 device=tbt.Device.ELECTRON_BEAM, 

223 ) 

224 return True 

225 

226 

227def insert_EBSD( 

228 microscope: tbt.Microscope, 

229) -> bool: 

230 """ 

231 Insert the EBSD camera into the microscope. 

232 

233 This function connects to the EBSD system, checks for potential collisions with other detectors, and inserts the EBSD camera if it is not already inserted. It raises an error if the EBSD camera cannot be inserted. 

234 

235 Parameters 

236 ---------- 

237 microscope : tbt.Microscope 

238 The microscope object for which to insert the EBSD camera. 

239 

240 Returns 

241 ------- 

242 bool 

243 True if the EBSD camera is successfully inserted. 

244 

245 Raises 

246 ------ 

247 SystemError 

248 If a collision may occur with another detector, if the EBSD camera is in an error state, if the EBSD mapping is not idle, or if the EBSD camera cannot be inserted. 

249 """ 

250 connect_EBSD() 

251 if detectors_will_collide( 

252 microscope=microscope, 

253 detector_to_insert=tbt.DetectorType.EBSD, 

254 ): 

255 raise SystemError( 

256 f"""Error. Cannot insert EBSD which may collide with another detector. 

257 Disallowed detector combinations are: {Constants.detector_collisions}""" 

258 ) 

259 ebsd_cam_status = tbt.RetractableDeviceState(external.EBSD_CameraStatus()) 

260 map_status = tbt.MapStatus(external.EBSD_MappingStatus()) 

261 if ebsd_cam_status == tbt.RetractableDeviceState.ERROR: 

262 raise SystemError("Error, EDS Camera in error state, workflow stopped.") 

263 if map_status != tbt.MapStatus.IDLE: 

264 raise SystemError( 

265 f'Error, EBSD mapping not in "{tbt.MapStatus.IDLE.value}" state.' 

266 ) 

267 if ebsd_cam_status != tbt.RetractableDeviceState.INSERTED: 

268 print("\tInserting EBSD Camera...") 

269 # TODO change to constants 

270 minutes_to_wait = 3 

271 timeout = minutes_to_wait * 60 # seconds 

272 waittime = 4 # seconds 

273 CCD_view(microscope=microscope) 

274 # Oxford Inst requires 2 inserts 

275 while True: 

276 external.EBSD_InsertCamera() # inserted state 

277 if ( 

278 external.EBSD_CameraStatus() 

279 == tbt.RetractableDeviceState.INSERTED.value 

280 ): 

281 break 

282 time.sleep(waittime) 

283 timeout = timeout - waittime 

284 if timeout < 1: 

285 warnings.warn("Warning: EBSD insert timeout. Trying to continue...") 

286 break 

287 CCD_pause(microscope=microscope) 

288 

289 new_ebsd_cam_status = tbt.RetractableDeviceState(external.EBSD_CameraStatus()) 

290 if new_ebsd_cam_status == tbt.RetractableDeviceState.INSERTED: 

291 print("\tEBSD Camera inserted") 

292 return True 

293 raise SystemError( 

294 f'EBSDS Camera is not inserted, currently in "{new_ebsd_cam_status}" state' 

295 ) 

296 

297 

298def insert_EDS( 

299 microscope: tbt.Microscope, 

300) -> bool: 

301 """ 

302 Insert the EDS camera into the microscope. 

303 

304 This function connects to the EDS system, checks for potential collisions with other detectors, and inserts the EDS camera if it is not already inserted. It raises an error if the EDS camera cannot be inserted. 

305 

306 Parameters 

307 ---------- 

308 microscope : tbt.Microscope 

309 The microscope object for which to insert the EDS camera. 

310 

311 Returns 

312 ------- 

313 bool 

314 True if the EDS camera is successfully inserted. 

315 

316 Raises 

317 ------ 

318 SystemError 

319 If a collision may occur with another detector, if the EDS camera is in an error state, if the EDS mapping is not idle, or if the EDS camera cannot be inserted. 

320 """ 

321 connect_EDS() 

322 if detectors_will_collide( 

323 microscope=microscope, 

324 detector_to_insert=tbt.DetectorType.EDS, 

325 ): 

326 raise SystemError( 

327 f"""Error. Cannot insert EDS while CBS not in "Retracted" state.  

328 CBS detector currently in "{detector_state(microscope=microscope, detector=tbt.DetectorType.CBS).value}" state.""" 

329 ) 

330 eds_cam_status = tbt.RetractableDeviceState(external.EDS_CameraStatus()) 

331 map_status = tbt.MapStatus(external.EDS_MappingStatus()) 

332 if eds_cam_status == tbt.RetractableDeviceState.ERROR: 

333 raise SystemError("Error, EDS Camera in error state, workflow stopped.") 

334 if map_status != tbt.MapStatus.IDLE: 

335 raise SystemError( 

336 f'Error, EDS mapping not in "{tbt.MapStatus.IDLE.value}" state.' 

337 ) 

338 if eds_cam_status != tbt.RetractableDeviceState.INSERTED: 

339 print("\tInserting EDS Camera...") 

340 CCD_view(microscope=microscope) 

341 external.EDS_InsertCamera() 

342 CCD_pause(microscope=microscope) 

343 

344 new_eds_cam_status = tbt.RetractableDeviceState(external.EDS_CameraStatus()) 

345 if new_eds_cam_status == tbt.RetractableDeviceState.INSERTED: 

346 print("\tEDS Camera inserted") 

347 return True 

348 raise SystemError( 

349 f'EDS Camera is not inserted, currently in "{new_eds_cam_status}" state' 

350 ) 

351 

352 

353def insert_detector( 

354 microscope: tbt.Microscope, 

355 detector: tbt.DetectorType, 

356 time_delay_s: float = 0.5, 

357) -> bool: 

358 """ 

359 Insert the selected detector into the microscope. 

360 

361 This function ensures the specified detector is the active one, confirms it is insertable, and inserts it if it is not already inserted. It raises an error if the detector cannot be inserted. 

362 

363 Parameters 

364 ---------- 

365 microscope : tbt.Microscope 

366 The microscope object for which to insert the detector. 

367 detector : tbt.DetectorType 

368 The type of the detector to insert. 

369 time_delay_s : float, optional 

370 The time delay in seconds after inserting the detector (default is 0.5 seconds). 

371 

372 Returns 

373 ------- 

374 bool 

375 True if the detector is successfully inserted. 

376 

377 Raises 

378 ------ 

379 ValueError 

380 If the detector is not insertable. 

381 SystemError 

382 If a collision may occur with another detector or if the detector cannot be inserted. 

383 """ 

384 # ensure detector is the active one 

385 microscope.detector.type.value = detector.value 

386 # confirm detector is insertable 

387 try: 

388 state = microscope.detector.state 

389 except: 

390 raise ValueError(f"{detector.value} detector is not insertable.") 

391 if state == tbt.RetractableDeviceState.RETRACTED.value: 

392 if detectors_will_collide( 

393 microscope=microscope, 

394 detector_to_insert=detector, 

395 ): 

396 raise SystemError( 

397 f"""Error. Cannot insert {detector.value} which may collide with another detector. 

398 Disallowed detector combinations are: {Constants.detector_collisions}""" 

399 ) 

400 

401 print(f"\tInserting {detector.value} detector...") 

402 CCD_view(microscope=microscope) 

403 microscope.detector.insert() 

404 time.sleep(time_delay_s) 

405 CCD_pause(microscope=microscope) 

406 if microscope.detector.state == tbt.RetractableDeviceState.INSERTED.value: 

407 print(f"\t\t{detector.value} detector inserted.") 

408 return True 

409 elif state == tbt.RetractableDeviceState.INSERTED.value: 

410 print(f"\t{detector.value} detector is already inserted.") 

411 return True 

412 raise SystemError( 

413 f'Cannot insert {detector.value} detector, current detector state is "{state}".' 

414 ) 

415 

416 

417def retract_all_devices( 

418 microscope: tbt.Microscope, 

419 enable_EBSD: bool, 

420 enable_EDS: bool, 

421) -> bool: 

422 # TODO come up with better system for enable_EBSD_EDS 

423 """ 

424 Retract all insertable devices, including microscope detectors and EBSD/EDS detectors if integrated. 

425 

426 This function retracts all insertable devices, first retracting microscope detectors and then retracting EBSD/EDS detectors if they are integrated and enabled. 

427 

428 Parameters 

429 ---------- 

430 microscope : tbt.Microscope 

431 The microscope object for accessing the Autoscript API. 

432 enable_EBSD : bool 

433 Whether to enable retraction of the EBSD detector. 

434 enable_EDS : bool 

435 Whether to enable retraction of the EDS detector. 

436 

437 Returns 

438 ------- 

439 bool 

440 True if all devices are successfully retracted. 

441 

442 Raises 

443 ------ 

444 None 

445 """ 

446 print("\tRetracting devices, do not interact with xTUI during this process...") 

447 initial_view = tbt.ViewQuad(microscope.imaging.get_active_view()) 

448 device_access(microscope) 

449 

450 for detector in microscope.detector.type.available_values: 

451 detector = tbt.DetectorType(detector) # overwrite 

452 state = detector_state( 

453 microscope=microscope, 

454 detector=detector, 

455 ) 

456 if ( 

457 state is not tbt.RetractableDeviceState.STATIONARY 

458 and state is not tbt.RetractableDeviceState.RETRACTED 

459 ): 

460 # if (state is not None) and (state != tbt.RetractableDeviceState.RETRACTED): 

461 retract_device( 

462 microscope=microscope, 

463 detector=detector, 

464 ) 

465 

466 # EBSD/EDS detectors: 

467 try: 

468 external 

469 except NameError: 

470 pass 

471 print("\t\tLaser API not imported, EBSD and EDS detectors are unavailable") 

472 else: 

473 if enable_EBSD: 

474 retract_EBSD(microscope=microscope) 

475 if enable_EDS: 

476 retract_EDS(microscope=microscope) 

477 # else: 

478 # warnings.warn( 

479 # "\t\tWarning: EBSD and EDS device control API is available but not being used." 

480 # ) 

481 

482 # reset initial settings: 

483 img.set_view( 

484 microscope=microscope, 

485 quad=initial_view, 

486 ) 

487 print("\t\tAll available and enabled devices retracted.") 

488 return True 

489 

490 

491def connect_EBSD() -> tbt.RetractableDeviceState: 

492 """ 

493 Connect to the EBSD system and retrieve the camera status. 

494 

495 This function attempts to connect to the EBSD system and retrieve the camera status. It raises a ConnectionError if the connection fails. 

496 

497 Returns 

498 ------- 

499 tbt.RetractableDeviceState 

500 The status of the EBSD camera. 

501 

502 Raises 

503 ------ 

504 ConnectionError 

505 If the EBSD control is not connected. 

506 """ 

507 try: 

508 status = external.EBSD_CameraStatus() 

509 except: 

510 raise ConnectionError( 

511 """EBSD control not connected, "Laser Control" from ThermoFisher must be open.  

512 Try closing Laser Control, restarting EBSD/EDS software, then opening Laser Control again.""" 

513 ) 

514 return tbt.RetractableDeviceState(status) 

515 

516 

517def retract_EBSD(microscope: tbt.Microscope) -> bool: 

518 """ 

519 Retract the EBSD camera from the microscope. 

520 

521 This function connects to the EBSD system, checks the camera status, and retracts the EBSD camera if it is not already retracted. It raises an error if the EBSD camera cannot be retracted. 

522 

523 Parameters 

524 ---------- 

525 microscope : tbt.Microscope 

526 The microscope object for which to retract the EBSD camera. 

527 

528 Returns 

529 ------- 

530 bool 

531 True if the EBSD camera is successfully retracted. 

532 

533 Raises 

534 ------ 

535 SystemError 

536 If the EBSD camera is in an error state, if the EBSD mapping is not completed, or if the EBSD camera retraction fails. 

537 """ 

538 connect_EBSD() 

539 ebsd_status = tbt.RetractableDeviceState(external.EBSD_CameraStatus()) 

540 if ebsd_status == tbt.RetractableDeviceState.ERROR: 

541 raise SystemError( 

542 "EBSD Camera in error state, workflow stopped. Check EBSD/EDS software or restart laser control" 

543 ) 

544 if ebsd_status != tbt.RetractableDeviceState.RETRACTED: 

545 print( 

546 '\t\t\tEBSD Camera Retraction requested, please wait for "mapping complete" verification...' 

547 ) 

548 map_status = tbt.MapStatus(external.EBSD_MappingStatus()) 

549 # first check if mapping is finished properly 

550 minutes_to_wait = 5 # TODO set constant 

551 timeout = minutes_to_wait * 60 # seconds #TODO 

552 cameraokconfirmations = 3 # synchronization issue with EDAX, try to get map completed 3x before continuing 

553 waittime = 10 # seconds 

554 if map_status == tbt.MapStatus.ACTIVE: 

555 print("\t\t\tEBSD mapping currently active, waiting for mapping to finish") 

556 while True: 

557 current_map_status = tbt.MapStatus(external.EBSD_MappingStatus()) 

558 if current_map_status != tbt.MapStatus.ACTIVE: 

559 cameraokconfirmations = cameraokconfirmations - 1 

560 waittime = 3 # shorten wait time, polling 3x to see if mapping was really completed. 

561 time.sleep(waittime) 

562 timeout = timeout - waittime 

563 if cameraokconfirmations < 1: 

564 delay = minutes_to_wait * 60 - timeout 

565 print(f"\t\t\t\tEBSD mapping finished. Delay of {delay} seconds") 

566 break 

567 if timeout < 1: 

568 warnings.warn( 

569 "\t\t\tWarning, EBSD mapping timeout. Trying to continue..." 

570 ) 

571 break 

572 CCD_view(microscope=microscope) 

573 print("\t\t\tEBSD Camera retracting...") 

574 external.EBSD_RetractCamera() 

575 time.sleep(1) 

576 CCD_pause(microscope=microscope) 

577 current_ebsd_status = tbt.RetractableDeviceState(external.EBSD_CameraStatus()) 

578 if current_ebsd_status != tbt.RetractableDeviceState.RETRACTED: 

579 raise SystemError("Error, EBSD Camera retraction failed, workflow stopped.") 

580 print("\t\tEBSD Camera retracted") 

581 return True 

582 

583 

584def connect_EDS() -> tbt.RetractableDeviceState: 

585 """ 

586 Connect to the EDS system and retrieve the camera status. 

587 

588 This function attempts to connect to the EDS system and retrieve the camera status. It raises a ConnectionError if the connection fails. 

589 

590 Returns 

591 ------- 

592 tbt.RetractableDeviceState 

593 The status of the EDS camera. 

594 

595 Raises 

596 ------ 

597 ConnectionError 

598 If the EDS control is not connected. 

599 """ 

600 try: 

601 status = external.EDS_CameraStatus() 

602 except: 

603 raise ConnectionError( 

604 """EDS control not connected, "Laser Control" from ThermoFisher must be open.  

605 Try closing Laser Control, restarting EBSD/EDS software, then opening Laser Control again.""" 

606 ) 

607 return tbt.RetractableDeviceState(status) 

608 

609 

610def retract_EDS(microscope: tbt.Microscope) -> bool: 

611 """ 

612 Retract the EDS detector from the microscope. 

613 

614 This function connects to the EDS system, checks the camera status, and retracts the EDS camera if it is not already retracted. It raises an error if the EDS camera cannot be retracted. 

615 

616 Parameters 

617 ---------- 

618 microscope : tbt.Microscope 

619 The microscope object for which to retract the EDS camera. 

620 

621 Returns 

622 ------- 

623 bool 

624 True if the EDS camera is successfully retracted. 

625 

626 Raises 

627 ------ 

628 SystemError 

629 If the EDS camera is in an error state or if the EDS camera retraction fails. 

630 """ 

631 # print(f"\t\tRetracting EDS detector") 

632 connect_EDS() 

633 eds_status = tbt.RetractableDeviceState(external.EDS_CameraStatus()) 

634 if eds_status == tbt.RetractableDeviceState.ERROR: 

635 raise SystemError( 

636 "EDS Camera in error state, workflow stopped. Check EBSD/EDS software or restart laser control" 

637 ) 

638 if eds_status != tbt.RetractableDeviceState.RETRACTED: 

639 print("\t\t\tEDS Camera retracting...") 

640 CCD_view(microscope=microscope) 

641 external.EDS_RetractCamera() 

642 time.sleep(1) 

643 if ( 

644 tbt.RetractableDeviceState(external.EDS_CameraStatus()) 

645 != tbt.RetractableDeviceState.RETRACTED 

646 ): 

647 raise SystemError("Error, EDS Camera retraction failed, workflow stopped.") 

648 CCD_pause(microscope=microscope) 

649 print("\t\tEDS Camera retracted") 

650 return True 

651 

652 

653def retract_device(microscope: tbt.Microscope, detector: tbt.DetectorType) -> bool: 

654 """ 

655 Retract the specified detector from the microscope. 

656 

657 This function ensures the specified detector is the active one, retracts it, and checks its state. It raises an error if the detector cannot be retracted. 

658 

659 Parameters 

660 ---------- 

661 microscope : tbt.Microscope 

662 The microscope object for which to retract the detector. 

663 detector : tbt.DetectorType 

664 The type of the detector to retract. 

665 

666 Returns 

667 ------- 

668 bool 

669 True if the detector is successfully retracted. 

670 

671 Raises 

672 ------ 

673 SystemError 

674 If the detector cannot be retracted. 

675 """ 

676 CCD_view(microscope=microscope) 

677 print(f"\t\tRetracting {detector.value} detector") 

678 microscope.detector.type.value = detector.value 

679 microscope.detector.retract() 

680 state = tbt.RetractableDeviceState(microscope.detector.state) 

681 if state != tbt.RetractableDeviceState.RETRACTED: 

682 raise SystemError( 

683 f"{detector.value} detector not retracted, current detector state is {detector_state.value}" 

684 ) 

685 print(f"\t\t{detector.value} detector retracted") 

686 CCD_pause(microscope=microscope) 

687 

688 return True 

689 

690 

691def CCD_pause( 

692 microscope: tbt.Microscope, 

693 quad: tbt.ViewQuad = tbt.ViewQuad.LOWER_RIGHT, 

694) -> bool: 

695 """ 

696 Pause the CCD camera, typically used after device or stage movement. 

697 

698 This function pauses the CCD camera by switching to the specified quadrant, setting the beam device to the CCD camera, and stopping the acquisition. It restores the initial view afterward. 

699 

700 Parameters 

701 ---------- 

702 microscope : tbt.Microscope 

703 The microscope object for which to pause the CCD camera. 

704 quad : tbt.ViewQuad, optional 

705 The quadrant to switch to before pausing the CCD camera (default is tbt.ViewQuad.LOWER_RIGHT). 

706 

707 Returns 

708 ------- 

709 bool 

710 True if the CCD camera is successfully paused. 

711 

712 Warnings 

713 -------- 

714 UserWarning 

715 If the CCD camera is not installed on the microscope. 

716 """ 

717 initial_view = tbt.ViewQuad(microscope.imaging.get_active_view()) 

718 img.set_view(microscope=microscope, quad=quad) 

719 try: 

720 img.set_beam_device(microscope=microscope, device=tbt.Device.CCD_CAMERA) 

721 except: 

722 warnings.warn("CCD camera is not installed on this microscope.") 

723 else: 

724 microscope.imaging.stop_acquisition() 

725 finally: 

726 microscope.imaging.set_active_view(initial_view.value) 

727 return True 

728 

729 

730def CCD_view( 

731 microscope: tbt.Microscope, 

732 quad: tbt.ViewQuad = tbt.ViewQuad.LOWER_RIGHT, 

733) -> bool: 

734 """ 

735 Visualize detector or stage movement for the user using the CCD camera. 

736 

737 This function visualizes detector or stage movement by switching to the specified quadrant, setting the beam device to the CCD camera, and starting the acquisition. It restores the initial view afterward. 

738 

739 Parameters 

740 ---------- 

741 microscope : tbt.Microscope 

742 The microscope object for which to visualize the movement. 

743 quad : tbt.ViewQuad, optional 

744 The quadrant to switch to before visualizing the movement (default is tbt.ViewQuad.LOWER_RIGHT). 

745 

746 Returns 

747 ------- 

748 bool 

749 True if the CCD camera is successfully used for visualization. 

750 

751 Warnings 

752 -------- 

753 UserWarning 

754 If the CCD camera is not installed on the microscope. 

755 """ 

756 initial_view = tbt.ViewQuad(microscope.imaging.get_active_view()) 

757 img.set_view(microscope=microscope, quad=quad) 

758 try: 

759 img.set_beam_device(microscope=microscope, device=tbt.Device.CCD_CAMERA) 

760 except: 

761 warnings.warn("CCD camera is not installed on this microscope.") 

762 else: 

763 microscope.imaging.start_acquisition() 

764 finally: 

765 microscope.imaging.set_active_view(initial_view.value) 

766 return True 

767 

768 

769def specimen_current( 

770 microscope: tbt.Microscope, 

771 hfw_mm=Constants.specimen_current_hfw_mm, 

772 delay_s=Constants.specimen_current_delay_s, 

773) -> float: 

774 """ 

775 Measure the specimen current using the electron beam and return the value in nA. 

776 

777 This function sets the beam device to the electron beam, adjusts the horizontal field width (HFW) and detector, starts the acquisition, and measures the specimen current. It then resets the detector and HFW to their initial values. 

778 

779 Parameters 

780 ---------- 

781 microscope : tbt.Microscope 

782 The microscope object for which to measure the specimen current. 

783 hfw_mm : float, optional 

784 The horizontal field width in millimeters (default is Constants.specimen_current_hfw_mm). 

785 delay_s : float, optional 

786 The delay in seconds before measuring the specimen current (default is Constants.specimen_current_delay_s). 

787 

788 Returns 

789 ------- 

790 float 

791 The measured specimen current in nA. 

792 """ 

793 img.set_beam_device( 

794 microscope=microscope, 

795 device=tbt.Device.ELECTRON_BEAM, 

796 ) 

797 initial_hfw_m = microscope.beams.electron_beam.horizontal_field_width.value 

798 initial_detector = tbt.DetectorType(microscope.detector.type.value) 

799 

800 img.detector_type(microscope=microscope, detector=tbt.DetectorType.ETD) 

801 img.beam_hfw( 

802 beam=tbt.ElectronBeam(settings=tbt.BeamSettings()), 

803 microscope=microscope, 

804 hfw_mm=hfw_mm, 

805 ) 

806 microscope.imaging.start_acquisition() 

807 time.sleep(delay_s) 

808 current_na = microscope.state.specimen_current.value * cs.Conversions.A_TO_NA 

809 microscope.imaging.stop_acquisition() 

810 

811 # reset detector and hfw 

812 microscope.beams.electron_beam.horizontal_field_width.value = initial_hfw_m 

813 img.detector_type(microscope=microscope, detector=initial_detector) 

814 img.beam_hfw( 

815 beam=tbt.ElectronBeam(settings=tbt.BeamSettings()), 

816 microscope=microscope, 

817 hfw_mm=initial_hfw_m * cs.Conversions.M_TO_MM, 

818 ) 

819 

820 return current_na