PYME-extra 1.0.4.post0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. PYMEcs/Acquire/Actions/__init__.py +0 -0
  2. PYMEcs/Acquire/Actions/custom.py +167 -0
  3. PYMEcs/Acquire/Hardware/LPthreadedSimple.py +248 -0
  4. PYMEcs/Acquire/Hardware/LPthreadedSimpleSim.py +246 -0
  5. PYMEcs/Acquire/Hardware/NikonTiFlaskServer.py +45 -0
  6. PYMEcs/Acquire/Hardware/NikonTiFlaskServerT.py +59 -0
  7. PYMEcs/Acquire/Hardware/NikonTiRESTClient.py +73 -0
  8. PYMEcs/Acquire/Hardware/NikonTiSim.py +35 -0
  9. PYMEcs/Acquire/Hardware/__init__.py +0 -0
  10. PYMEcs/Acquire/Hardware/driftTrackGUI.py +329 -0
  11. PYMEcs/Acquire/Hardware/driftTrackGUI_n.py +472 -0
  12. PYMEcs/Acquire/Hardware/driftTracking.py +424 -0
  13. PYMEcs/Acquire/Hardware/driftTracking_n.py +433 -0
  14. PYMEcs/Acquire/Hardware/fakeCamX.py +15 -0
  15. PYMEcs/Acquire/Hardware/offsetPiezoRESTCorrelLog.py +38 -0
  16. PYMEcs/Acquire/__init__.py +0 -0
  17. PYMEcs/Analysis/MBMcollection.py +552 -0
  18. PYMEcs/Analysis/MINFLUX.py +280 -0
  19. PYMEcs/Analysis/MapUtils.py +77 -0
  20. PYMEcs/Analysis/NPC.py +1176 -0
  21. PYMEcs/Analysis/Paraflux.py +218 -0
  22. PYMEcs/Analysis/Simpler.py +81 -0
  23. PYMEcs/Analysis/Sofi.py +140 -0
  24. PYMEcs/Analysis/__init__.py +0 -0
  25. PYMEcs/Analysis/decSofi.py +211 -0
  26. PYMEcs/Analysis/eventProperties.py +50 -0
  27. PYMEcs/Analysis/fitDarkTimes.py +569 -0
  28. PYMEcs/Analysis/objectVolumes.py +20 -0
  29. PYMEcs/Analysis/offlineTracker.py +130 -0
  30. PYMEcs/Analysis/stackTracker.py +180 -0
  31. PYMEcs/Analysis/timeSeries.py +63 -0
  32. PYMEcs/Analysis/trackFiducials.py +186 -0
  33. PYMEcs/Analysis/zerocross.py +91 -0
  34. PYMEcs/IO/MINFLUX.py +851 -0
  35. PYMEcs/IO/NPC.py +117 -0
  36. PYMEcs/IO/__init__.py +0 -0
  37. PYMEcs/IO/darkTimes.py +19 -0
  38. PYMEcs/IO/picasso.py +219 -0
  39. PYMEcs/IO/tabular.py +11 -0
  40. PYMEcs/__init__.py +0 -0
  41. PYMEcs/experimental/CalcZfactor.py +51 -0
  42. PYMEcs/experimental/FRC.py +338 -0
  43. PYMEcs/experimental/ImageJROItools.py +49 -0
  44. PYMEcs/experimental/MINFLUX.py +1537 -0
  45. PYMEcs/experimental/NPCcalcLM.py +560 -0
  46. PYMEcs/experimental/Simpler.py +369 -0
  47. PYMEcs/experimental/Sofi.py +78 -0
  48. PYMEcs/experimental/__init__.py +0 -0
  49. PYMEcs/experimental/binEventProperty.py +187 -0
  50. PYMEcs/experimental/chaining.py +23 -0
  51. PYMEcs/experimental/clusterTrack.py +179 -0
  52. PYMEcs/experimental/combine_maps.py +104 -0
  53. PYMEcs/experimental/eventProcessing.py +93 -0
  54. PYMEcs/experimental/fiducials.py +323 -0
  55. PYMEcs/experimental/fiducialsNew.py +402 -0
  56. PYMEcs/experimental/mapTools.py +271 -0
  57. PYMEcs/experimental/meas2DplotDh5view.py +107 -0
  58. PYMEcs/experimental/mortensen.py +131 -0
  59. PYMEcs/experimental/ncsDenoise.py +158 -0
  60. PYMEcs/experimental/onTimes.py +295 -0
  61. PYMEcs/experimental/procPoints.py +77 -0
  62. PYMEcs/experimental/pyme2caml.py +73 -0
  63. PYMEcs/experimental/qPAINT.py +965 -0
  64. PYMEcs/experimental/randMap.py +188 -0
  65. PYMEcs/experimental/regExtraCmaps.py +11 -0
  66. PYMEcs/experimental/selectROIfilterTable.py +72 -0
  67. PYMEcs/experimental/showErrs.py +51 -0
  68. PYMEcs/experimental/showErrsDh5view.py +58 -0
  69. PYMEcs/experimental/showShiftMap.py +56 -0
  70. PYMEcs/experimental/snrEvents.py +188 -0
  71. PYMEcs/experimental/specLabeling.py +51 -0
  72. PYMEcs/experimental/splitRender.py +246 -0
  73. PYMEcs/experimental/testChannelByName.py +36 -0
  74. PYMEcs/experimental/timedSpecies.py +28 -0
  75. PYMEcs/experimental/utils.py +31 -0
  76. PYMEcs/misc/ExtraCmaps.py +177 -0
  77. PYMEcs/misc/__init__.py +0 -0
  78. PYMEcs/misc/configUtils.py +169 -0
  79. PYMEcs/misc/guiMsgBoxes.py +27 -0
  80. PYMEcs/misc/mapUtils.py +230 -0
  81. PYMEcs/misc/matplotlib.py +136 -0
  82. PYMEcs/misc/rectsFromSVG.py +182 -0
  83. PYMEcs/misc/shellutils.py +1110 -0
  84. PYMEcs/misc/utils.py +205 -0
  85. PYMEcs/misc/versionCheck.py +20 -0
  86. PYMEcs/misc/zcInfo.py +90 -0
  87. PYMEcs/pyme_warnings.py +4 -0
  88. PYMEcs/recipes/__init__.py +0 -0
  89. PYMEcs/recipes/base.py +75 -0
  90. PYMEcs/recipes/localisations.py +2380 -0
  91. PYMEcs/recipes/manipulate_yaml.py +83 -0
  92. PYMEcs/recipes/output.py +177 -0
  93. PYMEcs/recipes/processing.py +247 -0
  94. PYMEcs/recipes/simpler.py +290 -0
  95. PYMEcs/version.py +2 -0
  96. pyme_extra-1.0.4.post0.dist-info/METADATA +114 -0
  97. pyme_extra-1.0.4.post0.dist-info/RECORD +101 -0
  98. pyme_extra-1.0.4.post0.dist-info/WHEEL +5 -0
  99. pyme_extra-1.0.4.post0.dist-info/entry_points.txt +3 -0
  100. pyme_extra-1.0.4.post0.dist-info/licenses/LICENSE +674 -0
  101. pyme_extra-1.0.4.post0.dist-info/top_level.txt +1 -0
File without changes
@@ -0,0 +1,167 @@
1
+ from traits.api import HasTraits, Str, Int, CStr, List, Enum, Float, Bool
2
+ from traitsui.api import View, Item, Group
3
+ from traitsui.menu import OKButton, CancelButton, OKCancelButtons
4
+
5
+ from PYMEcs.misc.guiMsgBoxes import YesNo
6
+ from PYME.IO.FileUtils.nameUtils import numToAlpha
7
+
8
+
9
+ # could be called in init file as:
10
+
11
+ # @init_gui('ROI Calibration')
12
+ # def roi_calibration(MainFrame, scope):
13
+ #
14
+ # def roi_action_callback(event=None):
15
+ # from PYMEcs.Acquire.Actions.custom import queue_calibration_series
16
+ # queue_calibration_series(scope)
17
+
18
+ # MainFrame.AddMenuItem('Calibration', 'Camera Maps>Sub ROIs', roi_action_callback)
19
+
20
+ class ChipROI(HasTraits):
21
+ roiSize = Int(256)
22
+ overlap = Int(20)
23
+ numberOfFrames = Int(500)
24
+ checkBeforeQueuingActions = Bool(True)
25
+
26
+
27
+ # commenting out the below which was just a quick trial how to do this
28
+ # keeping in for reference right now
29
+
30
+ # def queue_roi_series(scope):
31
+ # cam = scope.cam
32
+
33
+ # args = {'state': {'Camera.ROI' : [50, 50, 200, 200]}}
34
+ # scope.actions.QueueAction('state.update', args)
35
+ # args = {'maxFrames': 500, 'stack': False}
36
+ # scope.actions.QueueAction('spoolController.StartSpooling', args)
37
+ # args = {'state': {'Camera.ROI' : [100, 100, 250, 250]}}
38
+ # scope.actions.QueueAction('state.update', args)
39
+ # args = {'maxFrames': 500, 'stack': False}
40
+ # scope.actions.QueueAction('spoolController.StartSpooling', args)
41
+ # args = {'state': {'Camera.ROI' : [0, 0, 256, 256]}}
42
+ # scope.actions.QueueAction('state.update', args)
43
+
44
+ # # in future we might code this as:
45
+ # #
46
+ # # calib = [actions.SpoolSeries(maxFrames=500, stack=False,
47
+ # # state={'Camera.ROI' : [50, 50, 200, 200]}),
48
+ # # actions.SpoolSeries(maxFrames=500, stack=False,
49
+ # # state={'Camera.ROI' : [100, 100, 250, 250]}),
50
+ # # ]
51
+
52
+ # # scope.actions.queue_actions(calib)
53
+
54
+
55
+ def check_roi(x0,y0,x1,y1,width=None, height=None):
56
+ if x0<0:
57
+ x0=0
58
+ if y0<0:
59
+ y0=0
60
+ if x1>(width):
61
+ x1 = width
62
+ if y1>(height):
63
+ y1 = height
64
+ return [x0,y0,x1,y1]
65
+
66
+
67
+ def spoolSeries(scope, maxFrames=500, stack=False, state=None,
68
+ method=None, subdirectory=None):
69
+ if state is not None:
70
+ args = {'state': state}
71
+ scope.actions.QueueAction('state.update', args)
72
+
73
+ args = {'settings': {'max_frames': maxFrames,
74
+ 'stack': stack,
75
+ 'subdirectory': subdirectory,
76
+ 'method': method}}
77
+ scope.actions.QueueAction('spoolController.start_spooling', args)
78
+
79
+
80
+ def setState(scope,state):
81
+ args = {'state': state}
82
+ scope.actions.QueueAction('state.update', args)
83
+
84
+
85
+
86
+ def get_mapdir(scope):
87
+ import os
88
+ seriesCounter = 0
89
+ subdirectory = 'map_' + numToAlpha(seriesCounter)
90
+ while os.path.exists(scope.spoolController.get_dirname(subdirectory)):
91
+ seriesCounter += 1
92
+ subdirectory = 'map_' + numToAlpha(seriesCounter)
93
+ return subdirectory
94
+
95
+
96
+ # ToDo - refine implementation as action interface improves
97
+ # - add online help, using traits help infrastructure (i.e. in class ChipROI)
98
+ def camera_chip_calibration_series(scope):
99
+ import os
100
+
101
+ chipROI = ChipROI()
102
+ if not chipROI.configure_traits(kind='modal'):
103
+ return
104
+
105
+ cam = scope.cam
106
+ chipWidth = cam.GetCCDWidth()
107
+ chipHeight = cam.GetCCDHeight()
108
+ curROI = cam.GetROI()
109
+
110
+ stepsize = chipROI.roiSize - chipROI.overlap
111
+ rsz = chipROI.roiSize
112
+
113
+ xsteps = int(chipWidth / stepsize)
114
+ ysteps = int(chipHeight / stepsize)
115
+
116
+ rois = []
117
+
118
+ # note PYME cam ROI conventions: 1) chip origin starts at 0,0
119
+ # 2) ROI with coordinates [x0,y0,x1,y1] starts and includes point x0,y0
120
+ # but extends to and *excludes* point x1, y1
121
+ # This implies that the ROI width is x1-x0 and height is y1-y0 (used further below)
122
+ x0 = 0
123
+ y0 = 0
124
+ x1 = rsz # note *not* rsz-1, since it excludes x1, y1, see above
125
+ y1 = rsz
126
+
127
+ for iy in range(0,ysteps):
128
+ for ix in range(0,xsteps):
129
+ rois.append(check_roi(x0+ix*stepsize,y0+iy*stepsize,
130
+ x1+ix*stepsize,y1+iy*stepsize,
131
+ width = chipWidth, height=chipHeight))
132
+
133
+ # show tiling on chip
134
+
135
+ import matplotlib.pyplot as plt
136
+ import matplotlib.patches as patches
137
+ import numpy as np
138
+
139
+ cols = ['r','g']
140
+
141
+ fig,ax = plt.subplots(1)
142
+ ax.imshow(np.ones((chipWidth,chipHeight)))
143
+ for i, roi in enumerate(rois):
144
+ rect = patches.Rectangle((roi[0],roi[1]),
145
+ roi[2]-roi[0], # remember we get ROI width by difference of corner points
146
+ roi[3]-roi[1], # same for height
147
+ linewidth=1,edgecolor=cols[i %2],facecolor='none')
148
+ ax.add_patch(rect)
149
+ cx = 0.5*(roi[0]+roi[2])
150
+ cy = 0.5*(roi[1]+roi[3])
151
+ plt.text(cx,cy,'%d' % i,c='w') # plot in white to ensure we see on top of darkish chip plot
152
+
153
+
154
+ if chipROI.checkBeforeQueuingActions:
155
+ if not YesNo(None, "Will use %d ROIS.\nProceed with running ROI actions?" % len(rois), caption='Proceed'):
156
+ return
157
+
158
+ mapdir = get_mapdir(scope)
159
+ # actually queue series
160
+ for roi in rois:
161
+ spoolSeries(scope, maxFrames=chipROI.numberOfFrames, stack=False,
162
+ state={'Camera.ROI' : roi}, subdirectory=mapdir,
163
+ method='FILE')
164
+
165
+ # set back to original ROI
166
+ setState(scope,state={'Camera.ROI' : curROI})
167
+
@@ -0,0 +1,248 @@
1
+ from threading import Thread, Lock
2
+ from queue import Queue
3
+ import logging
4
+ from PYME.Acquire.Hardware.NikonTi import LightPath
5
+ from http import HTTPStatus
6
+
7
+ # derived class of NikonTi.LightPath that provides "safe" methods that catch certain exceptions
8
+ # and return a status if the method invocation was successful
9
+ #
10
+ # in actual hardware version we would check for a com_error only, all other exceptions will not be
11
+ # caught, i.e.
12
+ #
13
+ from pywintypes import com_error
14
+ # ...
15
+ # except com_error:
16
+ # ...
17
+
18
+ # note that we use HTTP status codes as we use a REST server anyway and
19
+ # thus having status values we can directly pass back via HTTP responses are useful
20
+ class LPSafe(LightPath):
21
+ def __init__(self):
22
+ super().__init__()
23
+
24
+ def GetNamesS(self):
25
+ try:
26
+ names = self.names
27
+ status = HTTPStatus.OK
28
+ except com_error:
29
+ status = HTTPStatus.SERVICE_UNAVAILABLE
30
+ names = None
31
+ return status, names
32
+
33
+ def GetPortS(self):
34
+ try:
35
+ port = self.GetPort()
36
+ status = HTTPStatus.OK
37
+ except com_error:
38
+ status = HTTPStatus.SERVICE_UNAVAILABLE
39
+ port = None
40
+ return status, port
41
+
42
+ # we check the input argument range and return a suitable status
43
+ # if an invalid argument was provided
44
+ def SetPortS(self,port):
45
+ if port not in self.names:
46
+ return HTTPStatus.BAD_REQUEST
47
+ try:
48
+ self.SetPort(port)
49
+ except com_error:
50
+ status = HTTPStatus.SERVICE_UNAVAILABLE
51
+ else:
52
+ status = HTTPStatus.OK
53
+ return status
54
+
55
+ def GetPositionS(self):
56
+ try:
57
+ pos = self.GetPosition()
58
+ status = HTTPStatus.OK
59
+ except com_error:
60
+ status = HTTPStatus.SERVICE_UNAVAILABLE
61
+ pos = None
62
+ return status, pos
63
+
64
+ def SetPositionS(self,pos):
65
+ if pos not in range(len(self.names)):
66
+ return HTTPStatus.BAD_REQUEST
67
+ try:
68
+ self.SetPosition(pos)
69
+ except com_error:
70
+ status = HTTPStatus.SERVICE_UNAVAILABLE
71
+ else:
72
+ status = HTTPStatus.OK
73
+ return status
74
+
75
+ # objects that are used to pass info to and from the
76
+ # threaded version of the LightPath object
77
+ class commandObject(object):
78
+ def __init__(self,cmd,*args):
79
+ self.cmd = cmd
80
+ self.args = args
81
+
82
+ def __repr__(self):
83
+ return 'CMDMSG : %s!' % (self.cmd) + \
84
+ '[' + ','.join(str(x) for x in self.args) + ']'
85
+
86
+ class returnObject(object):
87
+ def __init__(self,cmd,status,*args):
88
+ self.cmd = cmd
89
+ self.status = status
90
+ self.return_args = args
91
+
92
+ def __repr__(self):
93
+ return 'RETMSG : %s!%s!' % (self.cmd,self.status) + \
94
+ '[' + ','.join(str(x) for x in self.return_args) + ']'
95
+
96
+
97
+ class LPThread(Thread):
98
+ def __init__(self, *args, **kwargs):
99
+ super().__init__(*args, **kwargs)
100
+ self.command_queue = Queue()
101
+ self.results_queue = Queue()
102
+ self.lp = LPSafe()
103
+
104
+ self.daemon = True
105
+
106
+ def run(self):
107
+ while True:
108
+ cmd = self.command_queue.get()
109
+ logging.debug('received command %s' % cmd)
110
+ result = self._parse_command_and_execute(cmd)
111
+ self.results_queue.put(result)
112
+
113
+ def _parse_command_and_execute(self,cmd):
114
+ if (not cmd.cmd) or (cmd.cmd is None):
115
+ return returnObject(None, HTTPStatus.NO_CONTENT, 'no command received')
116
+ logging.warn('no command received')
117
+ elif cmd.cmd == 'GetPort':
118
+ status, port = self.lp.GetPortS()
119
+ return returnObject(cmd.cmd, status, port)
120
+ elif cmd.cmd == 'SetPort':
121
+ status = self.lp.SetPortS(cmd.args[0])
122
+ return returnObject(cmd.cmd, status)
123
+ elif cmd.cmd == 'GetPosition':
124
+ status, pos = self.lp.GetPositionS()
125
+ return returnObject(cmd.cmd, status,pos)
126
+ elif cmd.cmd == 'SetPosition':
127
+ status = self.lp.SetPositionS(cmd.args[0])
128
+ return returnObject(cmd.cmd, status)
129
+ elif cmd.cmd == 'GetNames':
130
+ status, names = self.lp.GetNamesS()
131
+ return returnObject(cmd.cmd, status, names)
132
+ else:
133
+ return returnObject(cmd.cmd, HTTPStatus.NOT_IMPLEMENTED)
134
+ logging.warn('received unknown command %s' % cmd)
135
+
136
+ # primary external facing method to execute a command in the NikonTi LP thread
137
+ # and return in a form suitable for the excecute command (e.g. getter, setter, array getter)
138
+ def run_command(self,cmd,*args):
139
+ self.command_queue.put(commandObject(cmd,*args))
140
+ logging.debug('running command %s' % cmd)
141
+ ret = self.results_queue.get()
142
+ # setter command
143
+ if len(ret.return_args) == 0:
144
+ return ret.status
145
+ # getter command
146
+ elif len(ret.return_args) == 1:
147
+ return ret.status, ret.return_args[0]
148
+ # getter command that expects an array back
149
+ else:
150
+ return ret.status, ret.return_args
151
+
152
+ # below we replicate the normal LightPath interface and allow using the threaded
153
+ # version as a stand-in for the "plain" LightPath
154
+ def GetPort(self):
155
+ status, port = self.run_command('GetPort')
156
+ if status == HTTPStatus.OK:
157
+ return port
158
+ else:
159
+ raise RuntimeError('GetPort: received HTTP status %s' % status)
160
+
161
+ def SetPort(self,port):
162
+ status = self.run_command('SetPort',port)
163
+ if status != HTTPStatus.OK:
164
+ raise RuntimeError('SetPort: received HTTP status %s' % status)
165
+ self.lastPosition = self.GetPosition()
166
+ self.OnChange()
167
+
168
+ def GetNames(self):
169
+ status, names = self.run_command('GetNames')
170
+ if status == HTTPStatus.OK:
171
+ return names
172
+ else:
173
+ raise RuntimeError('GetPort: received HTTP status %s' % status)
174
+
175
+ def GetPosition(self):
176
+ status, pos = self.run_command('GetPosition')
177
+ if status == HTTPStatus.OK:
178
+ return pos
179
+ else:
180
+ raise RuntimeError('GetPosition: received HTTP status %s' % status)
181
+
182
+ def SetPosition(self,pos):
183
+ status = self.run_command('SetPosition',pos)
184
+ if status != HTTPStatus.OK:
185
+ raise RuntimeError('SetPosition: received HTTP status %s' % status)
186
+ self.lastPosition = pos
187
+ self.OnChange()
188
+
189
+ def ProvideMetadata(self,mdh):
190
+ mdh.setEntry('NikonTi.LightPath', self.GetPort())
191
+
192
+ def OnChange(self):
193
+ for a in self.wantChangeNotification:
194
+ a()
195
+
196
+ def Poll_initialize(self):
197
+ if self.is_alive(): # make sure we are already running
198
+ self.lastPosition = self.GetPosition()
199
+ self.names = self.GetNames()
200
+ self.wantChangeNotification = []
201
+ else:
202
+ logging.warning('Thread not running - Poll initialisation failed')
203
+
204
+ def Poll(self):
205
+ pos = self.GetPosition()
206
+ if not self.lastPosition == pos:
207
+ self.lastPosition = pos
208
+ self.OnChange()
209
+
210
+ # test some aspects of the code here
211
+ def main():
212
+ import time
213
+
214
+ lpt = LPThread(name='LPThread')
215
+ lpt.Poll_initialize() # this one should give a warning, thread not yet started
216
+ lpt.start()
217
+ lpt.Poll_initialize() # this one is done at the proper time
218
+
219
+ status,port = lpt.run_command('GetPort')
220
+ if status == HTTPStatus.OK:
221
+ print('Port is %s' % port)
222
+
223
+ status = lpt.run_command('SetPort','L100')
224
+ time.sleep(2.0) # wait two seconds for the command to complete
225
+
226
+ status, port = lpt.run_command('GetPort')
227
+ if status == HTTPStatus.OK:
228
+ print('Port is %s' % port)
229
+
230
+ status, pos = lpt.run_command('GetPosition')
231
+ if status == HTTPStatus.OK:
232
+ print('Position is %d' % pos)
233
+
234
+ status, names = lpt.run_command('GetNames')
235
+ if status == HTTPStatus.OK:
236
+ print('Names:',names)
237
+
238
+ print('Testing high level interface: Port is %s' % lpt.GetPort())
239
+
240
+ status = lpt.run_command(None)
241
+
242
+ print(commandObject('GetIt',1,'L100'))
243
+ print(returnObject('Received',HTTPStatus.OK,1,'L100'))
244
+
245
+ if __name__ == '__main__':
246
+ logging.basicConfig(level=logging.DEBUG,
247
+ format='(%(threadName)-9s) %(message)s',)
248
+ main()
@@ -0,0 +1,246 @@
1
+ from threading import Thread, Lock
2
+ from queue import Queue
3
+ import logging
4
+ from PYMEcs.Acquire.Hardware.NikonTiSim import LightPath
5
+ from http import HTTPStatus
6
+
7
+ # derived class of NikonTi.LightPath that provides "safe" methods that catch certain exceptions
8
+ # and return a status if the method invocation was successful
9
+ #
10
+ # in actual hardware version we would check for a com_error only, all other exceptions will not be
11
+ # caught, i.e.
12
+ #
13
+ # from pywintypes import com_error
14
+ # ...
15
+ # except com_error:
16
+ # ...
17
+
18
+ # note that we use HTTP status codes as we use a REST server anyway and
19
+ # thus having status values we can directly pass back via HTTP responses are useful
20
+ class LPSafe(LightPath):
21
+ def __init__(self):
22
+ super().__init__()
23
+
24
+ def GetNamesS(self):
25
+ try:
26
+ names = self.names
27
+ status = HTTPStatus.OK
28
+ except:
29
+ status = HTTPStatus.SERVICE_UNAVAILABLE
30
+ names = None
31
+ return status, names
32
+
33
+ def GetPortS(self):
34
+ try:
35
+ port = self.GetPort()
36
+ status = HTTPStatus.OK
37
+ except:
38
+ status = HTTPStatus.SERVICE_UNAVAILABLE
39
+ port = None
40
+ return status, port
41
+
42
+ # we check the input argument range and return a suitable status
43
+ # if an invalid argument was provided
44
+ def SetPortS(self,port):
45
+ if port not in self.names:
46
+ return HTTPStatus.BAD_REQUEST
47
+ try:
48
+ self.SetPort(port)
49
+ except:
50
+ status = HTTPStatus.SERVICE_UNAVAILABLE
51
+ else:
52
+ status = HTTPStatus.OK
53
+ return status
54
+
55
+ def GetPositionS(self):
56
+ try:
57
+ pos = self.GetPosition()
58
+ status = HTTPStatus.OK
59
+ except:
60
+ status = HTTPStatus.SERVICE_UNAVAILABLE
61
+ pos = None
62
+ return status, pos
63
+
64
+ def SetPositionS(self,pos):
65
+ if pos not in range(len(self.names)):
66
+ return HTTPStatus.BAD_REQUEST
67
+ try:
68
+ self.SetPosition(pos)
69
+ except:
70
+ status = HTTPStatus.SERVICE_UNAVAILABLE
71
+ else:
72
+ status = HTTPStatus.OK
73
+
74
+ return status
75
+
76
+ # objects that are used to pass info to and from the
77
+ # threaded version of the LightPath object
78
+ class commandObject(object):
79
+ def __init__(self,cmd,*args):
80
+ self.cmd = cmd
81
+ self.args = args
82
+
83
+ def __repr__(self):
84
+ return 'CMDMSG : %s!' % (self.cmd) + \
85
+ '[' + ','.join(str(x) for x in self.args) + ']'
86
+
87
+ class returnObject(object):
88
+ def __init__(self,cmd,status,*args):
89
+ self.cmd = cmd
90
+ self.status = status
91
+ self.return_args = args
92
+
93
+ def __repr__(self):
94
+ return 'RETMSG : %s!%s!' % (self.cmd,self.status) + \
95
+ '[' + ','.join(str(x) for x in self.return_args) + ']'
96
+
97
+
98
+ class LPThread(Thread):
99
+ def __init__(self, *args, **kwargs):
100
+ super().__init__(*args, **kwargs)
101
+ self.command_queue = Queue()
102
+ self.results_queue = Queue()
103
+ self.lp = LPSafe()
104
+
105
+ self.daemon = True
106
+
107
+ def run(self):
108
+ while True:
109
+ cmd = self.command_queue.get()
110
+ logging.debug('received command %s' % cmd)
111
+ result = self._parse_command_and_execute(cmd)
112
+ self.results_queue.put(result)
113
+
114
+ def _parse_command_and_execute(self,cmd):
115
+ if (not cmd.cmd) or (cmd.cmd is None):
116
+ return returnObject(None, HTTPStatus.NO_CONTENT, 'no command received')
117
+ logging.warn('no command received')
118
+ elif cmd.cmd == 'GetPort':
119
+ status, port = self.lp.GetPortS()
120
+ return returnObject(cmd.cmd, status, port)
121
+ elif cmd.cmd == 'SetPort':
122
+ status = self.lp.SetPortS(cmd.args[0])
123
+ return returnObject(cmd.cmd, status)
124
+ elif cmd.cmd == 'GetPosition':
125
+ status, pos = self.lp.GetPositionS()
126
+ return returnObject(cmd.cmd, status,pos)
127
+ elif cmd.cmd == 'SetPosition':
128
+ status = self.lp.SetPositionS(cmd.args[0])
129
+ return returnObject(cmd.cmd, status)
130
+ elif cmd.cmd == 'GetNames':
131
+ status, names = self.lp.GetNamesS()
132
+ return returnObject(cmd.cmd, status, names)
133
+ else:
134
+ return returnObject(cmd.cmd, HTTPStatus.NOT_IMPLEMENTED)
135
+ logging.warn('received unknown command %s' % cmd)
136
+
137
+ # primary external facing method to execute a command in the NikonTi LP thread
138
+ # and return in a form suitable for the excecute command (e.g. getter, setter, array getter)
139
+ def run_command(self,cmd,*args):
140
+ self.command_queue.put(commandObject(cmd,*args))
141
+ logging.debug('running command %s' % cmd)
142
+ ret = self.results_queue.get()
143
+ # setter command
144
+ if len(ret.return_args) == 0:
145
+ return ret.status
146
+ # getter command
147
+ elif len(ret.return_args) == 1:
148
+ return ret.status, ret.return_args[0]
149
+ # getter command that expects an array back
150
+ else:
151
+ return ret.status, ret.return_args
152
+
153
+ # below we replicate the normal LightPath interface and allow using the threaded
154
+ # version as a stand-in for the "plain" LightPath
155
+ def GetPort(self):
156
+ status, port = self.run_command('GetPort')
157
+ if status == HTTPStatus.OK:
158
+ return port
159
+ else:
160
+ raise RuntimeError('GetPort: received HTTP status %s' % status)
161
+
162
+ def SetPort(self,port):
163
+ status = self.run_command('SetPort',port)
164
+ if status != HTTPStatus.OK:
165
+ raise RuntimeError('SetPort: received HTTP status %s' % status)
166
+ self.lastPosition = self.GetPosition()
167
+ self.OnChange()
168
+
169
+ def GetNames(self):
170
+ status, names = self.run_command('GetNames')
171
+ if status == HTTPStatus.OK:
172
+ return names
173
+ else:
174
+ raise RuntimeError('GetPort: received HTTP status %s' % status)
175
+
176
+ def GetPosition(self):
177
+ status, pos = self.run_command('GetPosition')
178
+ if status == HTTPStatus.OK:
179
+ return pos
180
+ else:
181
+ raise RuntimeError('GetPosition: received HTTP status %s' % status)
182
+
183
+ def SetPosition(self,pos):
184
+ status = self.run_command('SetPosition',pos)
185
+ if status != HTTPStatus.OK:
186
+ raise RuntimeError('SetPosition: received HTTP status %s' % status)
187
+ self.lastPosition = pos
188
+ self.OnChange()
189
+
190
+ def ProvideMetadata(self,mdh):
191
+ mdh.setEntry('NikonTi.LightPath', self.GetPort())
192
+
193
+ def OnChange(self):
194
+ for a in self.wantChangeNotification:
195
+ a()
196
+
197
+ def Poll_initialize(self):
198
+ if self.is_alive(): # make sure we are already running
199
+ self.lastPosition = self.GetPosition()
200
+ self.names = self.GetNames()
201
+ self.wantChangeNotification = []
202
+ else:
203
+ logging.warning('Thread not running - Poll initialisation failed')
204
+
205
+ def Poll(self):
206
+ pos = self.GetPosition()
207
+ if not self.lastPosition == pos:
208
+ self.lastPosition = pos
209
+ self.OnChange()
210
+
211
+ # test some aspects of the code here
212
+ def main():
213
+ lpt = LPThread(name='LPThread')
214
+ lpt.Poll_initialize() # this one should give a warning, thread not yet started
215
+ lpt.start()
216
+ lpt.Poll_initialize() # this one is done at the proper time
217
+
218
+ status,port = lpt.run_command('GetPort')
219
+ if status == HTTPStatus.OK:
220
+ print('Port is %s' % port)
221
+
222
+ status = lpt.run_command('SetPort','L100')
223
+
224
+ status, port = lpt.run_command('GetPort')
225
+ if status == HTTPStatus.OK:
226
+ print('Port is %s' % port)
227
+
228
+ status, pos = lpt.run_command('GetPosition')
229
+ if status == HTTPStatus.OK:
230
+ print('Position is %d' % pos)
231
+
232
+ status, names = lpt.run_command('GetNames')
233
+ if status == HTTPStatus.OK:
234
+ print('Names:',names)
235
+
236
+ print('Testing high level interface: Port is %s' % lpt.GetPort())
237
+
238
+ status = lpt.run_command(None)
239
+
240
+ print(commandObject('GetIt',1,'L100'))
241
+ print(returnObject('Received',HTTPStatus.OK,1,'L100'))
242
+
243
+ if __name__ == '__main__':
244
+ logging.basicConfig(level=logging.DEBUG,
245
+ format='(%(threadName)-9s) %(message)s',)
246
+ main()
@@ -0,0 +1,45 @@
1
+ from flask import Flask, request, jsonify, make_response
2
+ from http import HTTPStatus
3
+
4
+ def create_app(mode='simulated'):
5
+ # create and configure the app
6
+ app = Flask(__name__)
7
+
8
+ if mode == 'simulated':
9
+ from PYMEcs.Acquire.Hardware import NikonTiSim
10
+ lp = NikonTiSim.LightPath()
11
+ else:
12
+ from PYME.Acquire.Hardware import NikonTi
13
+ lp = NikonTi.LightPath()
14
+ # lp.SetPosition(2)
15
+
16
+ @app.get("/names")
17
+ def get_names():
18
+ return jsonify(lp.names)
19
+
20
+ @app.get("/port")
21
+ def get_port():
22
+ response = make_response(lp.GetPort(), HTTPStatus.OK)
23
+ response.mimetype = "text/plain"
24
+ return response
25
+
26
+ @app.put("/port")
27
+ def set_port():
28
+ lp.SetPort(request.get_data().decode("utf-8"))
29
+ return lp.GetPort(), HTTPStatus.OK
30
+
31
+ @app.get("/position")
32
+ def get_position():
33
+ response = make_response(str(lp.GetPosition()), HTTPStatus.OK)
34
+ response.mimetype = "text/plain"
35
+ return response
36
+
37
+ @app.put("/position")
38
+ def set_position():
39
+ lp.SetPosition(int(request.get_data().decode("utf-8")))
40
+ return str(lp.GetPosition()), HTTPStatus.OK
41
+
42
+ return app
43
+
44
+ if __name__ == '__main__':
45
+ app.run(threaded=False, processes=1)