Skip to content

Dod

DoD

Source code in dod/dod.py
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
class DoD: 
    def __init__(self, modules = 'None', ip = "172.21.72.187", port = 9999, supported_json = '/cds/group/pcds/pyps/apps/hutch-python/mfx/dod/supported.json'
):
        """
        Class definition of the DoD robot
        Parameters
        ----------
        modules : string
            Defines the optional modules of the robot. 
            Options: 'None', 'codi', , 
        ip = "172.21.72.187" , port = 9999, supported_json = "supported.json"            
        """
        from dod.DropsDriver import myClient
        from dod.JsonFileHandler import JsonFileHandler
        from dod.ServerResponse import ServerResponse

        import time

        # Create object 
        # pytest encourages this pattern, apologies.
        # ip = "172.21.72.187" #"172.21.148.101"
        # port = 9999
        # supported_json = '/cds/group/pcds/pyps/apps/hutch-python/mfx/dod/supported.json'

        # User input parameters: 
        # Safety parameters in hutch coordinate system. 
        # Note: hutch (x,y,z) = robot (x,-z, y) 
        # 
        self.y_min     = 10000 # minimum value in y.
        self.y_safety  = 50000 # value in y, above which the robot can only be in vertical configuration
        self.y_max     = 50000 # maximum value in y

        #Initialize safety regions for horizontal and vertical rotation: 
        self.forbidden_regions_horizontal = []
        self.forbidden_regions_vertical = []
        #minimum region: 
        self.set_forbidden_region(0, 300000, 0, self.y_min,rotation_state='both')
        #maximum region: 
        self.set_forbidden_region(0, 300000, self.y_max, 500000,rotation_state='both')
        #region where horizontal rotation is forbidden: 
        self.set_forbidden_region(0, 300000,  self.y_safety, self.y_max,rotation_state='horizontal')

        # Initializing the robot client that is used for communication
        self.client = myClient(ip=ip, port=port, supported_json=supported_json, reload=False)

        # create config parser handler
        json_handler = JsonFileHandler(supported_json)
        # load configs and launch web server
        json_handler.reload_endpoints()

        # Flag that can be used later on for safety aborts during task execution
        self.safety_abort = False
        if modules == 'codi': 
            from dod.codi import CoDI
            self.codi = CoDI()

        # Timing section
        from pcdsdevices.evr import Trigger
        self.delay = None

        # Trigger objects
        self.trigger_Xray = Trigger('MFX:LAS:EVR:01:TRIG7', name='trigger_X-ray_simulator')
        self.trigger_nozzle_1 = Trigger('MFX:LAS:EVR:01:TRIG2', name='trigger_nozzle_1')
        self.trigger_nozzle_2 = Trigger('MFX:LAS:EVR:01:TRIG3', name='trigger_nozzle_2')
        self.trigger_LED = Trigger('MFX:LAS:EVR:01:TRIG1', name='trigger_LED_array')

        # Timing parameter
        self.timing_Xray = self.trigger_Xray.ns_delay.get()
        self.timing_nozzle_1 = self.trigger_nozzle_1.ns_delay.get()
        self.timing_nozzle_2 = self.trigger_nozzle_2.ns_delay.get()
        self.timing_LED = self.trigger_LED.ns_delay.get()
        self.timing_delay_sciPulse = 60600
        self.timing_delay_LED = 1000 #delay of LED relative to X-ray timing
        self.timing_delay_reaction = 0
        self.timing_delay_nozzle_1 = self.timing_Xray - self.timing_nozzle_1 - self.timing_delay_sciPulse - self.timing_delay_reaction
        self.timing_delay_nozzle_2 = self.timing_Xray - self.timing_nozzle_2 - self.timing_delay_sciPulse - self.timing_delay_reaction


    def stop_task(self, verbose = True):
        """
        Stop task while running
        ** ISSUES **
        -  When stop task  is called, the Robot stays in "BUSY" Status.
        Parameters
        ----------
        verbose : boolean
            Defines whether the function returns the full output, or only the results
        Returns: 
        r : 
            status readback when aborted
        """
        r = self.client.connect("Test")
        self.safety_abort = False
        r = self.client.stop_task()
        r = self.client.disconnect()
        if verbose == True: 
            return r


    def clear_abort(self, verbose = True):
        """
        clear abort flag
        Parameters
        verbose : boolean
           Defines whether the function returns the full output, or only the results
        ----------
        Returns: 
        r : 
            status readback after error cleared
        '''
        """
        r = self.client.connect("Test")
        r = self.client.get_status()
        self.safety_abort = False
        rr = self.client.disconnect()

        if verbose == True: 
            return r


    # def clear_popup_window(self, verbose = True):
    #     """
    #     clears a popup window that might pop up on the robot gui
    #     Parameters
    #     verbose : boolean
    #        Defines whether the function returns the full output, or only the results
    #     ----------
    #     Returns: 
    #     r : 
    #         status readback after error cleared
    #     '''
    #     """
    #     r = self.client.connect("Test")
    #     r = self.client.get_status()

    #     self.client.close_dialog(reference, selection)

    #     if verbose == True: 
    #         return r


    def get_status(self, verbose = False):
        """
        returns the robot state

        Parameters
        verbose : boolean
           Defines whether the function returns the full output, or only the results
        ----------
        Returns: 
        r : dict
            different states of the robot
        """
        rr = self.client.connect("Test")
        r = self.client.get_status()
        # expected_keys = [
        #     'Position',
        #     'RunningTask',
        #     'Dialog',
        #     'LastProbe',
        #     'Humidity',
        #     'Temperature',
        #     'BathTemp',
        #     ]
        rr = self.client.disconnect()
        if verbose == True: 
            return r
        else: 
            return r.RESULTS


    def busy_wait(self, timeout):
        '''
            Busy wait untill timeout value is reached,
            timeout : sec
            returns true if timeout occured
        '''
        import time
        start = time.time()
        r = self.client.get_status()
        delta = 0

        while(r.STATUS['Status'] == "Busy"):
            if delta > timeout:
                return True

            time.sleep(0.1) #Wait a ms to stop spamming robot
            r = self.client.get_status()
            delta = time.time() - start    
        return False


    # def __del__(self):
    #     # close network connection
    #     self.client.conn.close()


    def get_task_details(self, task_name, verbose = False):
        """
            This gets the details of a task from the robot to see the scripted routines
            Parameters
            task_name : string
                Name of the task that we want to get
            verbose : boolean
                Defines whether the function returns the full output, or only the results
            ----------
            Returns: 
            r : 
                returns the robot tasks
        """
        rr = self.client.connect("Test")
        r = self.client.get_task_details(task_name)
        rr = self.client.disconnect()
        if verbose == True: 
            return r
        else: 
            return r.RESULTS


    def get_task_names(self, verbose = False):
        """
            This gets the names of available tasks from the robot
            Parameters
            task_name : string
                Name of the task that we want to get
            verbose : boolean
                Defines whether the function returns the full output, or only the results
            ----------
            Returns: 
            r : dict
                returns the robot tasks
        """
        # Check if reponse is not an empty array or any errors occured
        rr = self.client.connect("Test")
        r = self.client.get_task_names()
        rr = self.client.disconnect()
        if verbose == True: 
            return r
        else: 
            return r.RESULTS


    def get_current_position(self, verbose = False):
        '''
        Returns current robot position
        name and properties of the last selected position, together with the real current position coordinates
        Parameters

        verbose : boolean
                Defines whether the function returns the full output, or only the results
            ----------
        Returns: 
        r : 
            returns the current position.         
        # expected_keys = [
        #         'CurrentPosition',
        #         'Position',
        #         'PositionReal',
        #         ]
        '''
        rr = self.client.connect("Test")
        r = self.client.get_current_positions()
        rr = self.client.disconnect()
        if verbose == True: 
            return r
        else: 
            return r.RESULTS


    def get_nozzle_status(self, verbose = False):
        '''
        Returns current nozzle parameters position
        Parameters

        verbose : boolean
                Defines whether the function returns the full output, or only the results
        ----------
        Returns: 
        r : 
            returns the current nozzle parameters.         
        #         expected_keys = [
                "Activated Nozzles",
                "Selected Nozzles",
                "ID,Volt,Pulse,Freq,Volume",  # Intreseting? all one key
                "Dispensing",
                ]
        '''
        rr = self.client.connect("Test")
        r = self.client.get_nozzle_status()
        rr = self.client.disconnect()
        if verbose == True: 
            return r
        else: 
            return r.RESULTS


    def set_nozzle_dispensing(self, mode = "Off", verbose = False):
        '''
        Sets the dispensing, either "Free", "Triggered", or "Off"
        Parameters
        mode : string 
            either "free", "triggered", or "off"
        verbose : boolean
                Defines whether the function returns the full output, or only the results
        ----------
        Returns: 
        r : 
        '''
        rr = self.client.connect("Test")
        if mode == 'Free': 
            r = self.client.dispensing('Free')
        elif mode == 'Triggered':
            r = self.client.dispensing('Triggered')
        else: 
            #turns active nozzles off. Safer if all nozzles would be turned off
            r = self.client.dispensing('Off')
            for i in [1,2,3,4]: 
                r = self.client.select_nozzle(i)
                r = self.client.dispensing('Off')

        rr = self.client.disconnect()
        if verbose == True: 
            return r
        else: 
            return r.RESULTS


    def do_move(self, position, safety_test = False, verbose = False):
        '''
            Moves robot to new position

        Parameters
        position : string
            Position name to which the robot is supposed to move
        safety_test : Boolean
            question whether a safety test is to be performed or not
            True: Test will be performed
            False: Not performed
        verbose : boolean
                Defines whether the function returns the full output, or only the results
            ----------
        Returns: 
        r : current position
        '''

        r = self.client.connect("Test")
        r = self.client.get_current_positions()
        current_real_position = r.RESULTS['PositionReal']

        if safety_test == False:  
            r = self.client.move(position)
        else: 
            print('safety test of move has yet to be implemented')

        # WAIT FOR MOVEMENT TO BE DONE
        self.busy_wait(15)

        r = self.client.get_current_positions()
        new_real_position = r.RESULTS['PositionReal']

        rr = self.client.disconnect()
        if verbose == True: 
            return r
        else: 
            return r.RESULTS


    def move_x_abs(self, position_x, safety_test = False, verbose = False):
        '''
            Robot coordinate system!!!: 
            Moves robot to new absolute x position. 
            No safety test. 

        Parameters
        position : int
            Absolute position in um(?) to which the robot is supposed to move
        safety_test : Boolean
            question whether a safety test is to be performed or not
            True: Test will be performed
            False: Not performed
        verbose : boolean
                Defines whether the function returns the full output, or only the results
            ----------
        Returns: 
        r : current position
        '''

        r = self.client.connect("Test")
        r = self.client.get_current_positions()
        current_real_position = r.RESULTS['PositionReal']
        x_current, y_current, z_current = current_real_position

        if safety_test == False:  
            r = self.client.move_x(position_x)
        else: 
            print('safety test of move has yet to be implemented')
            if self.test_forbidden_region(position_x, y_current): 
                r = self.client.move_x(position_x)


        # WAIT FOR MOVEMENT TO BE DONE
        self.busy_wait(25)

        # r = self.client.get_current_positions()
        # new_real_position = r.RESULTS['PositionReal']

        rr = self.client.disconnect()
        if verbose == True: 
            return r
        else: 
            return r.RESULTS


    def move_y_abs(self, position_y, safety_test = False, verbose = False):
        '''
            Robot coordinate system!!!: 
            Moves robot to new absolute x position. 
            No safety test. 

        Parameters
        position : int
            Absolute position in um(?) to which the robot is supposed to move
        safety_test : Boolean
            question whether a safety test is to be performed or not
            True: Test will be performed
            False: Not performed
        verbose : boolean
                Defines whether the function returns the full output, or only the results
            ----------
        Returns: 
        r : current position
        '''

        r = self.client.connect("Test")
        r = self.client.get_current_positions()
        current_real_position = r.RESULTS['PositionReal']
        x_current, y_current, z_current = current_real_position

        if safety_test == False:  
            r = self.client.move_y(position_y)
        else: 
            print('safety test of move has yet to be implemented')
            if self.test_forbidden_region(x_current, position_y): 
                r = self.client.move_y(position_y)


        # WAIT FOR MOVEMENT TO BE DONE
        self.busy_wait(25)

        # r = self.client.get_current_positions()
        # new_real_position = r.RESULTS['PositionReal']

        rr = self.client.disconnect()
        if verbose == True: 
            return r
        else: 
            return r.RESULTS


    def move_z_abs(self, position_z, safety_test = False, verbose = False):
        '''
            Robot coordinate system!!!: 
            Moves robot to new absolute Z position. 
            No safety test. 

        Parameters
        position : int
            Absolute position in um(?) to which the robot is supposed to move
        safety_test : Boolean
            question whether a safety test is to be performed or not
            True: Test will be performed
            False: Not performed
        verbose : boolean
                Defines whether the function returns the full output, or only the results
            ----------
        Returns: 
        r : current position
        '''

        r = self.client.connect("Test")
        r = self.client.get_current_positions()
        current_real_position = r.RESULTS['PositionReal']
        x_current, y_current, z_current = current_real_position

        if safety_test == False:  
            r = self.client.move_z(position_z)
        else: 
            print('safety test of move has yet to be implemented')
            if self.test_forbidden_region(x_current, y_current): 
                r = self.client.move_z(position_z)


        # WAIT FOR MOVEMENT TO BE DONE
        self.busy_wait(25)

        # r = self.client.get_current_positions()
        # new_real_position = r.RESULTS['PositionReal']

        rr = self.client.disconnect()
        if verbose == True: 
            return r
        else: 
            return r.RESULTS


    def do_task(self, task_name, safety_check = False, verbose = False):
        '''
        Executes a task of the robot

        Parameters
        task_name : string
            task name which robot is supposed to perform
        safety_test : Boolean
            question whether a safety test is to be performed or not
            True: Test will be performed
            False: Not performed
        verbose : boolean
                Defines whether the function returns the full output, or only the results
            ----------
        Returns: 
        r : 
        '''
        import time

        rr = self.client.connect("Test")
        if safety_check == False: 
            r = self.client.execute_task(task_name)
        else: 
            print('safety check needs to be implemented')

        ## Wait for task to be done
        while(r.STATUS['Status'] == "Busy"):
            #Possible if loop is not enterd?
            time.sleep(0.5)
            r = self.client.get_status()
            if self.safety_abort == True: 
                r = self.client.stop_task()
                print('User aborted task execution')
                return r

        r = self.client.get_status()

        #Check if any error occured
        if r.ERROR_CODE == 0:
            print('no error')
        else: 
            print('error while performing task!')

        if verbose == True: 
            return r
        else: 
            return r.RESULTS


    def get_forbidden_region(self, rotation_state = 'both'): 
        """
        returns forbidden regions in the robot coordinate x y plane for end-point testing 
        defined via a rectangle in the x-y-plane. x_start<x_stop, y_start<y_stop
        returns the region depending on the rotation state, as some regions are forbidden only in one configuration

        Parameters
        rotation_state: string
            options are "horizontal" (nozzle sideways, base 90 degrees), "vertical" (nozzle vertical, base 0 degree), "both"
        ----------

        """

        #combine the lists depending on the rotation 
        if rotation_state == "vertical": 
            test_list_safety = self.forbidden_regions_vertical
        elif rotation_state == "horizontal": 
            test_list_safety = self.forbidden_regions_horizontal
        else:
            test_list_safety = self.forbidden_regions_horizontal + self.forbidden_regions_vertical

        return test_list_safety


    def set_forbidden_region(self, x_start, x_stop, y_start, y_stop, rotation_state = 'both'): 
        """
        set a forbidden region in the robot coordinate x y plane for end-point testing 
        defined via a rectangle in the x-y-plane. x_start<x_stop, y_start<y_stop
        set the region depending on the rotation state, as some regions are forbidden only in one configuration

        Parameters
        x_start : float
            x-position start 
        x_stop : float
            x-position stop
        y_start : float
            y-position start 
        y_stop : float
            y-position stop
        rotation_state: string
            options are "horizontal" (nozzle sideways, base 90 degrees), "vertical" (nozzle vertical, base 0 degree), "both"
        ----------

        """
        region_tuple = (min(x_start,x_stop), max(x_start,x_stop), min(y_start,y_stop), max(y_start,y_stop))
        if rotation_state == "horizontal": 
            self.forbidden_regions_horizontal.append(region_tuple)
        elif rotation_state == "vertical": 
            self.forbidden_regions_vertical.append(region_tuple)
        elif rotation_state == "both": 
            self.forbidden_regions_vertical.append(region_tuple)
            self.forbidden_regions_horizontal.append(region_tuple)
        else:
            print('invalid input of rotation state')


    def test_forbidden_region(self, x_test, y_test): 
        """
        tests if the end point of a motion is inside a forbidden region
        No testing of the path of a motion included!

        Parameters
        x_test : float
            x-position to test 
        y_test : float 
            y-position to test 

        ----------
        Returns: 
        safe_motion : bool
            boolean flag if endpoint of motion is safe or not
        """
        from dod.codi import CoDI_base
        # Get current rotation state
        pos_rot_base  = round(CoDI_base.wm(),0)

        #Initialize safe flag (True = safe)
        flag_safe_endpoint = True

        #combine the lists depending on the rotation 
        if pos_rot_base == 90: 
            test_list_safety = self.forbidden_regions_vertical
        elif pos_rot_base == 0:
            test_list_safety = self.forbidden_regions_horizontal
        else:
            test_list_safety = self.forbidden_regions_horizontal + self.forbidden_regions_vertical

        #Test for all regions if the end point is in the forbidden region
        for tuple_current in test_list_safety: 
            x_start, x_stop, y_start, y_stop = tuple_current
            if ((x_start < x_test) and (x_stop > x_test) and (y_start < y_test) and (y_stop > y_test)): 
                flag_safe_endpoint = flag_safe_endpoint and False
            else:
                flag_safe_endpoint = flag_safe_endpoint and True

        return flag_safe_endpoint


    def set_timing_update(self): 
        """
        updating the timing triggers according to the set relative and absolute timing values

        Parameters
               ----------
        Returns: 
        """
        # Nozzle 1
        self.timing_nozzle_1 = self.timing_Xray - self.timing_delay_nozzle_1 - self.timing_delay_sciPulse - self.timing_delay_reaction
        if self.timing_nozzle_1 < 0: 
            self.timing_nozzle_1 = self.timing_nozzle_1 + 1/120*1000000000
        self.trigger_nozzle_1.ns_delay.put(self.timing_nozzle_1)

        # Nozzle 2
        self.timing_nozzle_2 = self.timing_Xray - self.timing_delay_nozzle_2 - self.timing_delay_sciPulse - self.timing_delay_reaction
        if self.timing_nozzle_2 < 0: 
            self.timing_nozzle_2 = self.timing_nozzle_2 + 1/120*1000000000
        self.trigger_nozzle_2.ns_delay.put(self.timing_nozzle_2)

        # LED
        self.timing_LED = self.timing_Xray + self.timing_delay_LED
        self.trigger_LED.ns_delay.put(self.timing_LED)


    def set_timing_zero_nozzle(self, nozzle, timing_rel): 
        """
        Setting the time zero for the nozzles from the LED alignment in robot

        Parameters
        nozle : int
            nozzle number
        timing_rel : float
            relative delay in ns from robot alignment
        ----------
        Returns: 
        """
        if nozzle == 1: 
            self.timing_delay_nozzle_1 = timing_rel
        elif nozzle == 2: 
            self.timing_delay_nozzle_2 = timing_rel
        else: 
            print('no valid nozzle selected.')
            return

        # update timings
        self.set_timing_update()


    def set_timing_rel_LED(self, timing_rel): 
        """
        Setting the relative timing of the LED relative to the X-rays

        Parameters
        timing_rel : float
            relative delay in ns from robot alignment
        ----------
        Returns: 
        """
        self.timing_delay_LED = timing_rel #delay of LED relative to X-ray timing

        # update timings
        self.set_timing_update()


    def set_timing_rel_reaction(self, timing_rel): 
        """
        Setting the relative timing of the reaction relative to the X-rays

        Parameters
        timing_rel : float
            relative delay in ns from robot alignment
        ----------
        Returns: 
        """
        self.timing_delay_reaction = timing_rel #delay of LED relative to X-ray timing

        # update timings
        self.set_timing_update()


    def set_timing_abs_Xray(self, timing_abs): 
        """
        Setting the absolute timing of the X-rays for claculation purposes

        Parameters
        timing_abs : float
            abs timing in ns from robot alignment
        ----------
        Returns: 
        """
        self.timing_Xray = timing_abs #delay of LED relative to X-ray timing
        #self.trigger_Xray.ns_delay.put(self.timing_Xray)

        # update timings
        self.set_timing_update()


    def set_timing_relative_nozzle(self, nozzle, timing_rel): 
        """
        Changing the timing of the selected nozzle by a relative amount 

        Parameters
        nozle : int
            nozzle number
        timing_rel : float
            relative change in ns from robot alignment
        ----------
        Returns: 
        """
        if nozzle == 1: 
            current_timing = self.timing_delay_nozzle_1
            self.timing_delay_nozzle_1 = self.timing_delay_nozzle_1 + timing_rel
        elif nozzle == 2: 
            current_timing = self.timing_delay_nozzle_2
            self.timing_delay_nozzle_2 = self.timing_delay_nozzle_2 + timing_rel
        else: 
            print('no valid nozzle selected.')
            return

        # update timings
        self.set_timing_update()


    def logging_string(self):
        """
        Creating the string to post to the e-log. 

        Parameters
        ----------
        Returns: 
        post : string
            String with useful logging information for posting into the e-log 
        """
        post_str = ''

        # Info to be posted: 

        # Nozzle angles: 
        position = self.codi.get_CoDI_pos()
        position_str = 'Codi Information: \n CoDI data: name: ' + str(position[0]) + '\n rot_base: '+ str(position[1])+ '\n rot_left: '+ str(position[2]) + '\n rot_right: '+ str(position[3])+ '\n z-transl: '+ str(position[4]) 
        post_str = post_str + position_str +' \n '

        #Timings:
        post_str = post_str + 'Timing:' + ' \n '
        post_str = post_str + 'timing_Xray:' + str(self.timing_Xray) +' \n '
        post_str = post_str + 'timing_nozzle_1:' + str(self.timing_nozzle_1) +' \n '
        post_str = post_str + 'timing_nozzle_2:' + str(self.timing_nozzle_2) +' \n '
        post_str = post_str + 'timing_LED:' + str(self.timing_LED) +' \n '
        post_str = post_str + 'timing_delay_LED:' + str(self.timing_delay_LED) +' \n '
        post_str = post_str + 'timing_delay_reaction:' + str(self.timing_delay_reaction) +' \n '        
        post_str = post_str + 'timing_delay_nozzle_1:' + str(self.timing_delay_nozzle_1) +' \n '   
        post_str = post_str + 'timing_delay_nozzle_2:' + str(self.timing_delay_nozzle_2) +' \n '   

        return post_str

__init__(modules='None', ip='172.21.72.187', port=9999, supported_json='/cds/group/pcds/pyps/apps/hutch-python/mfx/dod/supported.json')

Class definition of the DoD robot Parameters


modules : string Defines the optional modules of the robot. Options: 'None', 'codi', , ip = "172.21.72.187" , port = 9999, supported_json = "supported.json"

Source code in dod/dod.py
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
    def __init__(self, modules = 'None', ip = "172.21.72.187", port = 9999, supported_json = '/cds/group/pcds/pyps/apps/hutch-python/mfx/dod/supported.json'
):
        """
        Class definition of the DoD robot
        Parameters
        ----------
        modules : string
            Defines the optional modules of the robot. 
            Options: 'None', 'codi', , 
        ip = "172.21.72.187" , port = 9999, supported_json = "supported.json"            
        """
        from dod.DropsDriver import myClient
        from dod.JsonFileHandler import JsonFileHandler
        from dod.ServerResponse import ServerResponse

        import time

        # Create object 
        # pytest encourages this pattern, apologies.
        # ip = "172.21.72.187" #"172.21.148.101"
        # port = 9999
        # supported_json = '/cds/group/pcds/pyps/apps/hutch-python/mfx/dod/supported.json'

        # User input parameters: 
        # Safety parameters in hutch coordinate system. 
        # Note: hutch (x,y,z) = robot (x,-z, y) 
        # 
        self.y_min     = 10000 # minimum value in y.
        self.y_safety  = 50000 # value in y, above which the robot can only be in vertical configuration
        self.y_max     = 50000 # maximum value in y

        #Initialize safety regions for horizontal and vertical rotation: 
        self.forbidden_regions_horizontal = []
        self.forbidden_regions_vertical = []
        #minimum region: 
        self.set_forbidden_region(0, 300000, 0, self.y_min,rotation_state='both')
        #maximum region: 
        self.set_forbidden_region(0, 300000, self.y_max, 500000,rotation_state='both')
        #region where horizontal rotation is forbidden: 
        self.set_forbidden_region(0, 300000,  self.y_safety, self.y_max,rotation_state='horizontal')

        # Initializing the robot client that is used for communication
        self.client = myClient(ip=ip, port=port, supported_json=supported_json, reload=False)

        # create config parser handler
        json_handler = JsonFileHandler(supported_json)
        # load configs and launch web server
        json_handler.reload_endpoints()

        # Flag that can be used later on for safety aborts during task execution
        self.safety_abort = False
        if modules == 'codi': 
            from dod.codi import CoDI
            self.codi = CoDI()

        # Timing section
        from pcdsdevices.evr import Trigger
        self.delay = None

        # Trigger objects
        self.trigger_Xray = Trigger('MFX:LAS:EVR:01:TRIG7', name='trigger_X-ray_simulator')
        self.trigger_nozzle_1 = Trigger('MFX:LAS:EVR:01:TRIG2', name='trigger_nozzle_1')
        self.trigger_nozzle_2 = Trigger('MFX:LAS:EVR:01:TRIG3', name='trigger_nozzle_2')
        self.trigger_LED = Trigger('MFX:LAS:EVR:01:TRIG1', name='trigger_LED_array')

        # Timing parameter
        self.timing_Xray = self.trigger_Xray.ns_delay.get()
        self.timing_nozzle_1 = self.trigger_nozzle_1.ns_delay.get()
        self.timing_nozzle_2 = self.trigger_nozzle_2.ns_delay.get()
        self.timing_LED = self.trigger_LED.ns_delay.get()
        self.timing_delay_sciPulse = 60600
        self.timing_delay_LED = 1000 #delay of LED relative to X-ray timing
        self.timing_delay_reaction = 0
        self.timing_delay_nozzle_1 = self.timing_Xray - self.timing_nozzle_1 - self.timing_delay_sciPulse - self.timing_delay_reaction
        self.timing_delay_nozzle_2 = self.timing_Xray - self.timing_nozzle_2 - self.timing_delay_sciPulse - self.timing_delay_reaction

busy_wait(timeout)

Busy wait untill timeout value is reached, timeout : sec returns true if timeout occured

Source code in dod/dod.py
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
def busy_wait(self, timeout):
    '''
        Busy wait untill timeout value is reached,
        timeout : sec
        returns true if timeout occured
    '''
    import time
    start = time.time()
    r = self.client.get_status()
    delta = 0

    while(r.STATUS['Status'] == "Busy"):
        if delta > timeout:
            return True

        time.sleep(0.1) #Wait a ms to stop spamming robot
        r = self.client.get_status()
        delta = time.time() - start    
    return False

clear_abort(verbose=True)

clear abort flag Parameters verbose : boolean Defines whether the function returns the full output, or only the results


Returns: r : status readback after error cleared '''

Source code in dod/dod.py
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
def clear_abort(self, verbose = True):
    """
    clear abort flag
    Parameters
    verbose : boolean
       Defines whether the function returns the full output, or only the results
    ----------
    Returns: 
    r : 
        status readback after error cleared
    '''
    """
    r = self.client.connect("Test")
    r = self.client.get_status()
    self.safety_abort = False
    rr = self.client.disconnect()

    if verbose == True: 
        return r

do_move(position, safety_test=False, verbose=False)

Moves robot to new position

Parameters position : string Position name to which the robot is supposed to move safety_test : Boolean question whether a safety test is to be performed or not True: Test will be performed False: Not performed verbose : boolean Defines whether the function returns the full output, or only the results ---------- Returns: r : current position

Source code in dod/dod.py
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
def do_move(self, position, safety_test = False, verbose = False):
    '''
        Moves robot to new position

    Parameters
    position : string
        Position name to which the robot is supposed to move
    safety_test : Boolean
        question whether a safety test is to be performed or not
        True: Test will be performed
        False: Not performed
    verbose : boolean
            Defines whether the function returns the full output, or only the results
        ----------
    Returns: 
    r : current position
    '''

    r = self.client.connect("Test")
    r = self.client.get_current_positions()
    current_real_position = r.RESULTS['PositionReal']

    if safety_test == False:  
        r = self.client.move(position)
    else: 
        print('safety test of move has yet to be implemented')

    # WAIT FOR MOVEMENT TO BE DONE
    self.busy_wait(15)

    r = self.client.get_current_positions()
    new_real_position = r.RESULTS['PositionReal']

    rr = self.client.disconnect()
    if verbose == True: 
        return r
    else: 
        return r.RESULTS

do_task(task_name, safety_check=False, verbose=False)

Executes a task of the robot

Parameters task_name : string task name which robot is supposed to perform safety_test : Boolean question whether a safety test is to be performed or not True: Test will be performed False: Not performed verbose : boolean Defines whether the function returns the full output, or only the results ---------- Returns: r :

Source code in dod/dod.py
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
def do_task(self, task_name, safety_check = False, verbose = False):
    '''
    Executes a task of the robot

    Parameters
    task_name : string
        task name which robot is supposed to perform
    safety_test : Boolean
        question whether a safety test is to be performed or not
        True: Test will be performed
        False: Not performed
    verbose : boolean
            Defines whether the function returns the full output, or only the results
        ----------
    Returns: 
    r : 
    '''
    import time

    rr = self.client.connect("Test")
    if safety_check == False: 
        r = self.client.execute_task(task_name)
    else: 
        print('safety check needs to be implemented')

    ## Wait for task to be done
    while(r.STATUS['Status'] == "Busy"):
        #Possible if loop is not enterd?
        time.sleep(0.5)
        r = self.client.get_status()
        if self.safety_abort == True: 
            r = self.client.stop_task()
            print('User aborted task execution')
            return r

    r = self.client.get_status()

    #Check if any error occured
    if r.ERROR_CODE == 0:
        print('no error')
    else: 
        print('error while performing task!')

    if verbose == True: 
        return r
    else: 
        return r.RESULTS

get_current_position(verbose=False)

Returns current robot position name and properties of the last selected position, together with the real current position coordinates Parameters

boolean

Defines whether the function returns the full output, or only the results

----------

Returns: r : returns the current position.

expected_keys = [

'CurrentPosition',

'Position',

'PositionReal',

]

Source code in dod/dod.py
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
def get_current_position(self, verbose = False):
    '''
    Returns current robot position
    name and properties of the last selected position, together with the real current position coordinates
    Parameters

    verbose : boolean
            Defines whether the function returns the full output, or only the results
        ----------
    Returns: 
    r : 
        returns the current position.         
    # expected_keys = [
    #         'CurrentPosition',
    #         'Position',
    #         'PositionReal',
    #         ]
    '''
    rr = self.client.connect("Test")
    r = self.client.get_current_positions()
    rr = self.client.disconnect()
    if verbose == True: 
        return r
    else: 
        return r.RESULTS

get_forbidden_region(rotation_state='both')

returns forbidden regions in the robot coordinate x y plane for end-point testing defined via a rectangle in the x-y-plane. x_start<x_stop, y_start<y_stop returns the region depending on the rotation state, as some regions are forbidden only in one configuration

Parameters rotation_state: string options are "horizontal" (nozzle sideways, base 90 degrees), "vertical" (nozzle vertical, base 0 degree), "both"


Source code in dod/dod.py
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
def get_forbidden_region(self, rotation_state = 'both'): 
    """
    returns forbidden regions in the robot coordinate x y plane for end-point testing 
    defined via a rectangle in the x-y-plane. x_start<x_stop, y_start<y_stop
    returns the region depending on the rotation state, as some regions are forbidden only in one configuration

    Parameters
    rotation_state: string
        options are "horizontal" (nozzle sideways, base 90 degrees), "vertical" (nozzle vertical, base 0 degree), "both"
    ----------

    """

    #combine the lists depending on the rotation 
    if rotation_state == "vertical": 
        test_list_safety = self.forbidden_regions_vertical
    elif rotation_state == "horizontal": 
        test_list_safety = self.forbidden_regions_horizontal
    else:
        test_list_safety = self.forbidden_regions_horizontal + self.forbidden_regions_vertical

    return test_list_safety

get_nozzle_status(verbose=False)

Returns current nozzle parameters position Parameters

boolean

Defines whether the function returns the full output, or only the results


Returns: r : returns the current nozzle parameters.

expected_keys = [

    "Activated Nozzles",
    "Selected Nozzles",
    "ID,Volt,Pulse,Freq,Volume",  # Intreseting? all one key
    "Dispensing",
    ]
Source code in dod/dod.py
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
def get_nozzle_status(self, verbose = False):
    '''
    Returns current nozzle parameters position
    Parameters

    verbose : boolean
            Defines whether the function returns the full output, or only the results
    ----------
    Returns: 
    r : 
        returns the current nozzle parameters.         
    #         expected_keys = [
            "Activated Nozzles",
            "Selected Nozzles",
            "ID,Volt,Pulse,Freq,Volume",  # Intreseting? all one key
            "Dispensing",
            ]
    '''
    rr = self.client.connect("Test")
    r = self.client.get_nozzle_status()
    rr = self.client.disconnect()
    if verbose == True: 
        return r
    else: 
        return r.RESULTS

get_status(verbose=False)

returns the robot state

Parameters verbose : boolean Defines whether the function returns the full output, or only the results


Returns: r : dict different states of the robot

Source code in dod/dod.py
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
def get_status(self, verbose = False):
    """
    returns the robot state

    Parameters
    verbose : boolean
       Defines whether the function returns the full output, or only the results
    ----------
    Returns: 
    r : dict
        different states of the robot
    """
    rr = self.client.connect("Test")
    r = self.client.get_status()
    # expected_keys = [
    #     'Position',
    #     'RunningTask',
    #     'Dialog',
    #     'LastProbe',
    #     'Humidity',
    #     'Temperature',
    #     'BathTemp',
    #     ]
    rr = self.client.disconnect()
    if verbose == True: 
        return r
    else: 
        return r.RESULTS

get_task_details(task_name, verbose=False)

This gets the details of a task from the robot to see the scripted routines Parameters task_name : string Name of the task that we want to get verbose : boolean Defines whether the function returns the full output, or only the results


Returns: r : returns the robot tasks

Source code in dod/dod.py
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
def get_task_details(self, task_name, verbose = False):
    """
        This gets the details of a task from the robot to see the scripted routines
        Parameters
        task_name : string
            Name of the task that we want to get
        verbose : boolean
            Defines whether the function returns the full output, or only the results
        ----------
        Returns: 
        r : 
            returns the robot tasks
    """
    rr = self.client.connect("Test")
    r = self.client.get_task_details(task_name)
    rr = self.client.disconnect()
    if verbose == True: 
        return r
    else: 
        return r.RESULTS

get_task_names(verbose=False)

This gets the names of available tasks from the robot Parameters task_name : string Name of the task that we want to get verbose : boolean Defines whether the function returns the full output, or only the results


Returns: r : dict returns the robot tasks

Source code in dod/dod.py
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
def get_task_names(self, verbose = False):
    """
        This gets the names of available tasks from the robot
        Parameters
        task_name : string
            Name of the task that we want to get
        verbose : boolean
            Defines whether the function returns the full output, or only the results
        ----------
        Returns: 
        r : dict
            returns the robot tasks
    """
    # Check if reponse is not an empty array or any errors occured
    rr = self.client.connect("Test")
    r = self.client.get_task_names()
    rr = self.client.disconnect()
    if verbose == True: 
        return r
    else: 
        return r.RESULTS

logging_string()

Creating the string to post to the e-log.

Parameters

Returns: post : string String with useful logging information for posting into the e-log

Source code in dod/dod.py
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
def logging_string(self):
    """
    Creating the string to post to the e-log. 

    Parameters
    ----------
    Returns: 
    post : string
        String with useful logging information for posting into the e-log 
    """
    post_str = ''

    # Info to be posted: 

    # Nozzle angles: 
    position = self.codi.get_CoDI_pos()
    position_str = 'Codi Information: \n CoDI data: name: ' + str(position[0]) + '\n rot_base: '+ str(position[1])+ '\n rot_left: '+ str(position[2]) + '\n rot_right: '+ str(position[3])+ '\n z-transl: '+ str(position[4]) 
    post_str = post_str + position_str +' \n '

    #Timings:
    post_str = post_str + 'Timing:' + ' \n '
    post_str = post_str + 'timing_Xray:' + str(self.timing_Xray) +' \n '
    post_str = post_str + 'timing_nozzle_1:' + str(self.timing_nozzle_1) +' \n '
    post_str = post_str + 'timing_nozzle_2:' + str(self.timing_nozzle_2) +' \n '
    post_str = post_str + 'timing_LED:' + str(self.timing_LED) +' \n '
    post_str = post_str + 'timing_delay_LED:' + str(self.timing_delay_LED) +' \n '
    post_str = post_str + 'timing_delay_reaction:' + str(self.timing_delay_reaction) +' \n '        
    post_str = post_str + 'timing_delay_nozzle_1:' + str(self.timing_delay_nozzle_1) +' \n '   
    post_str = post_str + 'timing_delay_nozzle_2:' + str(self.timing_delay_nozzle_2) +' \n '   

    return post_str

move_x_abs(position_x, safety_test=False, verbose=False)

Robot coordinate system!!!: 
Moves robot to new absolute x position. 
No safety test.

Parameters position : int Absolute position in um(?) to which the robot is supposed to move safety_test : Boolean question whether a safety test is to be performed or not True: Test will be performed False: Not performed verbose : boolean Defines whether the function returns the full output, or only the results ---------- Returns: r : current position

Source code in dod/dod.py
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
def move_x_abs(self, position_x, safety_test = False, verbose = False):
    '''
        Robot coordinate system!!!: 
        Moves robot to new absolute x position. 
        No safety test. 

    Parameters
    position : int
        Absolute position in um(?) to which the robot is supposed to move
    safety_test : Boolean
        question whether a safety test is to be performed or not
        True: Test will be performed
        False: Not performed
    verbose : boolean
            Defines whether the function returns the full output, or only the results
        ----------
    Returns: 
    r : current position
    '''

    r = self.client.connect("Test")
    r = self.client.get_current_positions()
    current_real_position = r.RESULTS['PositionReal']
    x_current, y_current, z_current = current_real_position

    if safety_test == False:  
        r = self.client.move_x(position_x)
    else: 
        print('safety test of move has yet to be implemented')
        if self.test_forbidden_region(position_x, y_current): 
            r = self.client.move_x(position_x)


    # WAIT FOR MOVEMENT TO BE DONE
    self.busy_wait(25)

    # r = self.client.get_current_positions()
    # new_real_position = r.RESULTS['PositionReal']

    rr = self.client.disconnect()
    if verbose == True: 
        return r
    else: 
        return r.RESULTS

move_y_abs(position_y, safety_test=False, verbose=False)

Robot coordinate system!!!: 
Moves robot to new absolute x position. 
No safety test.

Parameters position : int Absolute position in um(?) to which the robot is supposed to move safety_test : Boolean question whether a safety test is to be performed or not True: Test will be performed False: Not performed verbose : boolean Defines whether the function returns the full output, or only the results ---------- Returns: r : current position

Source code in dod/dod.py
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
def move_y_abs(self, position_y, safety_test = False, verbose = False):
    '''
        Robot coordinate system!!!: 
        Moves robot to new absolute x position. 
        No safety test. 

    Parameters
    position : int
        Absolute position in um(?) to which the robot is supposed to move
    safety_test : Boolean
        question whether a safety test is to be performed or not
        True: Test will be performed
        False: Not performed
    verbose : boolean
            Defines whether the function returns the full output, or only the results
        ----------
    Returns: 
    r : current position
    '''

    r = self.client.connect("Test")
    r = self.client.get_current_positions()
    current_real_position = r.RESULTS['PositionReal']
    x_current, y_current, z_current = current_real_position

    if safety_test == False:  
        r = self.client.move_y(position_y)
    else: 
        print('safety test of move has yet to be implemented')
        if self.test_forbidden_region(x_current, position_y): 
            r = self.client.move_y(position_y)


    # WAIT FOR MOVEMENT TO BE DONE
    self.busy_wait(25)

    # r = self.client.get_current_positions()
    # new_real_position = r.RESULTS['PositionReal']

    rr = self.client.disconnect()
    if verbose == True: 
        return r
    else: 
        return r.RESULTS

move_z_abs(position_z, safety_test=False, verbose=False)

Robot coordinate system!!!: 
Moves robot to new absolute Z position. 
No safety test.

Parameters position : int Absolute position in um(?) to which the robot is supposed to move safety_test : Boolean question whether a safety test is to be performed or not True: Test will be performed False: Not performed verbose : boolean Defines whether the function returns the full output, or only the results ---------- Returns: r : current position

Source code in dod/dod.py
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
def move_z_abs(self, position_z, safety_test = False, verbose = False):
    '''
        Robot coordinate system!!!: 
        Moves robot to new absolute Z position. 
        No safety test. 

    Parameters
    position : int
        Absolute position in um(?) to which the robot is supposed to move
    safety_test : Boolean
        question whether a safety test is to be performed or not
        True: Test will be performed
        False: Not performed
    verbose : boolean
            Defines whether the function returns the full output, or only the results
        ----------
    Returns: 
    r : current position
    '''

    r = self.client.connect("Test")
    r = self.client.get_current_positions()
    current_real_position = r.RESULTS['PositionReal']
    x_current, y_current, z_current = current_real_position

    if safety_test == False:  
        r = self.client.move_z(position_z)
    else: 
        print('safety test of move has yet to be implemented')
        if self.test_forbidden_region(x_current, y_current): 
            r = self.client.move_z(position_z)


    # WAIT FOR MOVEMENT TO BE DONE
    self.busy_wait(25)

    # r = self.client.get_current_positions()
    # new_real_position = r.RESULTS['PositionReal']

    rr = self.client.disconnect()
    if verbose == True: 
        return r
    else: 
        return r.RESULTS

set_forbidden_region(x_start, x_stop, y_start, y_stop, rotation_state='both')

set a forbidden region in the robot coordinate x y plane for end-point testing defined via a rectangle in the x-y-plane. x_start<x_stop, y_start<y_stop set the region depending on the rotation state, as some regions are forbidden only in one configuration

Parameters x_start : float x-position start x_stop : float x-position stop y_start : float y-position start y_stop : float y-position stop rotation_state: string options are "horizontal" (nozzle sideways, base 90 degrees), "vertical" (nozzle vertical, base 0 degree), "both"


Source code in dod/dod.py
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
def set_forbidden_region(self, x_start, x_stop, y_start, y_stop, rotation_state = 'both'): 
    """
    set a forbidden region in the robot coordinate x y plane for end-point testing 
    defined via a rectangle in the x-y-plane. x_start<x_stop, y_start<y_stop
    set the region depending on the rotation state, as some regions are forbidden only in one configuration

    Parameters
    x_start : float
        x-position start 
    x_stop : float
        x-position stop
    y_start : float
        y-position start 
    y_stop : float
        y-position stop
    rotation_state: string
        options are "horizontal" (nozzle sideways, base 90 degrees), "vertical" (nozzle vertical, base 0 degree), "both"
    ----------

    """
    region_tuple = (min(x_start,x_stop), max(x_start,x_stop), min(y_start,y_stop), max(y_start,y_stop))
    if rotation_state == "horizontal": 
        self.forbidden_regions_horizontal.append(region_tuple)
    elif rotation_state == "vertical": 
        self.forbidden_regions_vertical.append(region_tuple)
    elif rotation_state == "both": 
        self.forbidden_regions_vertical.append(region_tuple)
        self.forbidden_regions_horizontal.append(region_tuple)
    else:
        print('invalid input of rotation state')

set_nozzle_dispensing(mode='Off', verbose=False)

Sets the dispensing, either "Free", "Triggered", or "Off" Parameters mode : string either "free", "triggered", or "off" verbose : boolean Defines whether the function returns the full output, or only the results


Returns: r :

Source code in dod/dod.py
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
def set_nozzle_dispensing(self, mode = "Off", verbose = False):
    '''
    Sets the dispensing, either "Free", "Triggered", or "Off"
    Parameters
    mode : string 
        either "free", "triggered", or "off"
    verbose : boolean
            Defines whether the function returns the full output, or only the results
    ----------
    Returns: 
    r : 
    '''
    rr = self.client.connect("Test")
    if mode == 'Free': 
        r = self.client.dispensing('Free')
    elif mode == 'Triggered':
        r = self.client.dispensing('Triggered')
    else: 
        #turns active nozzles off. Safer if all nozzles would be turned off
        r = self.client.dispensing('Off')
        for i in [1,2,3,4]: 
            r = self.client.select_nozzle(i)
            r = self.client.dispensing('Off')

    rr = self.client.disconnect()
    if verbose == True: 
        return r
    else: 
        return r.RESULTS

set_timing_abs_Xray(timing_abs)

Setting the absolute timing of the X-rays for claculation purposes

Parameters timing_abs : float abs timing in ns from robot alignment


Returns:

Source code in dod/dod.py
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
def set_timing_abs_Xray(self, timing_abs): 
    """
    Setting the absolute timing of the X-rays for claculation purposes

    Parameters
    timing_abs : float
        abs timing in ns from robot alignment
    ----------
    Returns: 
    """
    self.timing_Xray = timing_abs #delay of LED relative to X-ray timing
    #self.trigger_Xray.ns_delay.put(self.timing_Xray)

    # update timings
    self.set_timing_update()

set_timing_rel_LED(timing_rel)

Setting the relative timing of the LED relative to the X-rays

Parameters timing_rel : float relative delay in ns from robot alignment


Returns:

Source code in dod/dod.py
702
703
704
705
706
707
708
709
710
711
712
713
714
715
def set_timing_rel_LED(self, timing_rel): 
    """
    Setting the relative timing of the LED relative to the X-rays

    Parameters
    timing_rel : float
        relative delay in ns from robot alignment
    ----------
    Returns: 
    """
    self.timing_delay_LED = timing_rel #delay of LED relative to X-ray timing

    # update timings
    self.set_timing_update()

set_timing_rel_reaction(timing_rel)

Setting the relative timing of the reaction relative to the X-rays

Parameters timing_rel : float relative delay in ns from robot alignment


Returns:

Source code in dod/dod.py
718
719
720
721
722
723
724
725
726
727
728
729
730
731
def set_timing_rel_reaction(self, timing_rel): 
    """
    Setting the relative timing of the reaction relative to the X-rays

    Parameters
    timing_rel : float
        relative delay in ns from robot alignment
    ----------
    Returns: 
    """
    self.timing_delay_reaction = timing_rel #delay of LED relative to X-ray timing

    # update timings
    self.set_timing_update()

set_timing_relative_nozzle(nozzle, timing_rel)

Changing the timing of the selected nozzle by a relative amount

Parameters nozle : int nozzle number timing_rel : float relative change in ns from robot alignment


Returns:

Source code in dod/dod.py
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
def set_timing_relative_nozzle(self, nozzle, timing_rel): 
    """
    Changing the timing of the selected nozzle by a relative amount 

    Parameters
    nozle : int
        nozzle number
    timing_rel : float
        relative change in ns from robot alignment
    ----------
    Returns: 
    """
    if nozzle == 1: 
        current_timing = self.timing_delay_nozzle_1
        self.timing_delay_nozzle_1 = self.timing_delay_nozzle_1 + timing_rel
    elif nozzle == 2: 
        current_timing = self.timing_delay_nozzle_2
        self.timing_delay_nozzle_2 = self.timing_delay_nozzle_2 + timing_rel
    else: 
        print('no valid nozzle selected.')
        return

    # update timings
    self.set_timing_update()

set_timing_update()

updating the timing triggers according to the set relative and absolute timing values

Parameters ---------- Returns:

Source code in dod/dod.py
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
def set_timing_update(self): 
    """
    updating the timing triggers according to the set relative and absolute timing values

    Parameters
           ----------
    Returns: 
    """
    # Nozzle 1
    self.timing_nozzle_1 = self.timing_Xray - self.timing_delay_nozzle_1 - self.timing_delay_sciPulse - self.timing_delay_reaction
    if self.timing_nozzle_1 < 0: 
        self.timing_nozzle_1 = self.timing_nozzle_1 + 1/120*1000000000
    self.trigger_nozzle_1.ns_delay.put(self.timing_nozzle_1)

    # Nozzle 2
    self.timing_nozzle_2 = self.timing_Xray - self.timing_delay_nozzle_2 - self.timing_delay_sciPulse - self.timing_delay_reaction
    if self.timing_nozzle_2 < 0: 
        self.timing_nozzle_2 = self.timing_nozzle_2 + 1/120*1000000000
    self.trigger_nozzle_2.ns_delay.put(self.timing_nozzle_2)

    # LED
    self.timing_LED = self.timing_Xray + self.timing_delay_LED
    self.trigger_LED.ns_delay.put(self.timing_LED)

set_timing_zero_nozzle(nozzle, timing_rel)

Setting the time zero for the nozzles from the LED alignment in robot

Parameters nozle : int nozzle number timing_rel : float relative delay in ns from robot alignment


Returns:

Source code in dod/dod.py
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
def set_timing_zero_nozzle(self, nozzle, timing_rel): 
    """
    Setting the time zero for the nozzles from the LED alignment in robot

    Parameters
    nozle : int
        nozzle number
    timing_rel : float
        relative delay in ns from robot alignment
    ----------
    Returns: 
    """
    if nozzle == 1: 
        self.timing_delay_nozzle_1 = timing_rel
    elif nozzle == 2: 
        self.timing_delay_nozzle_2 = timing_rel
    else: 
        print('no valid nozzle selected.')
        return

    # update timings
    self.set_timing_update()

stop_task(verbose=True)

Stop task while running ** ISSUES ** - When stop task is called, the Robot stays in "BUSY" Status. Parameters


verbose : boolean Defines whether the function returns the full output, or only the results Returns: r : status readback when aborted

Source code in dod/dod.py
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
def stop_task(self, verbose = True):
    """
    Stop task while running
    ** ISSUES **
    -  When stop task  is called, the Robot stays in "BUSY" Status.
    Parameters
    ----------
    verbose : boolean
        Defines whether the function returns the full output, or only the results
    Returns: 
    r : 
        status readback when aborted
    """
    r = self.client.connect("Test")
    self.safety_abort = False
    r = self.client.stop_task()
    r = self.client.disconnect()
    if verbose == True: 
        return r

test_forbidden_region(x_test, y_test)

tests if the end point of a motion is inside a forbidden region No testing of the path of a motion included!

Parameters x_test : float x-position to test y_test : float y-position to test


Returns: safe_motion : bool boolean flag if endpoint of motion is safe or not

Source code in dod/dod.py
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
def test_forbidden_region(self, x_test, y_test): 
    """
    tests if the end point of a motion is inside a forbidden region
    No testing of the path of a motion included!

    Parameters
    x_test : float
        x-position to test 
    y_test : float 
        y-position to test 

    ----------
    Returns: 
    safe_motion : bool
        boolean flag if endpoint of motion is safe or not
    """
    from dod.codi import CoDI_base
    # Get current rotation state
    pos_rot_base  = round(CoDI_base.wm(),0)

    #Initialize safe flag (True = safe)
    flag_safe_endpoint = True

    #combine the lists depending on the rotation 
    if pos_rot_base == 90: 
        test_list_safety = self.forbidden_regions_vertical
    elif pos_rot_base == 0:
        test_list_safety = self.forbidden_regions_horizontal
    else:
        test_list_safety = self.forbidden_regions_horizontal + self.forbidden_regions_vertical

    #Test for all regions if the end point is in the forbidden region
    for tuple_current in test_list_safety: 
        x_start, x_stop, y_start, y_stop = tuple_current
        if ((x_start < x_test) and (x_stop > x_test) and (y_start < y_test) and (y_stop > y_test)): 
            flag_safe_endpoint = flag_safe_endpoint and False
        else:
            flag_safe_endpoint = flag_safe_endpoint and True

    return flag_safe_endpoint