feather-xrol 0.1.0__tar.gz

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 (40) hide show
  1. feather_xrol-0.1.0/LICENSE +28 -0
  2. feather_xrol-0.1.0/PKG-INFO +65 -0
  3. feather_xrol-0.1.0/README.md +51 -0
  4. feather_xrol-0.1.0/pyproject.toml +20 -0
  5. feather_xrol-0.1.0/setup.cfg +4 -0
  6. feather_xrol-0.1.0/src/feather_xrol/__init__.py +2 -0
  7. feather_xrol-0.1.0/src/feather_xrol/attocube.py +50 -0
  8. feather_xrol-0.1.0/src/feather_xrol/hexapod.py +27 -0
  9. feather_xrol-0.1.0/src/feather_xrol/ids_api/ACS.py +245 -0
  10. feather_xrol-0.1.0/src/feather_xrol/ids_api/Examples/IDS_Example.py +57 -0
  11. feather_xrol-0.1.0/src/feather_xrol/ids_api/SEN.py +51 -0
  12. feather_xrol-0.1.0/src/feather_xrol/ids_api/__init__.py +1 -0
  13. feather_xrol-0.1.0/src/feather_xrol/ids_api/about.py +37 -0
  14. feather_xrol-0.1.0/src/feather_xrol/ids_api/access.py +62 -0
  15. feather_xrol-0.1.0/src/feather_xrol/ids_api/adjustment.py +39 -0
  16. feather_xrol-0.1.0/src/feather_xrol/ids_api/axis.py +133 -0
  17. feather_xrol-0.1.0/src/feather_xrol/ids_api/displacement.py +168 -0
  18. feather_xrol-0.1.0/src/feather_xrol/ids_api/ecu.py +170 -0
  19. feather_xrol-0.1.0/src/feather_xrol/ids_api/manual.py +153 -0
  20. feather_xrol-0.1.0/src/feather_xrol/ids_api/network.py +364 -0
  21. feather_xrol-0.1.0/src/feather_xrol/ids_api/nlc.py +241 -0
  22. feather_xrol-0.1.0/src/feather_xrol/ids_api/pilotlaser.py +39 -0
  23. feather_xrol-0.1.0/src/feather_xrol/ids_api/realtime.py +522 -0
  24. feather_xrol-0.1.0/src/feather_xrol/ids_api/streaming/Examples/__init__.py +0 -0
  25. feather_xrol-0.1.0/src/feather_xrol/ids_api/streaming/Examples/streaming_example_animate_loop.py +160 -0
  26. feather_xrol-0.1.0/src/feather_xrol/ids_api/streaming/Examples/streaming_example_backgroundStreaming.py +76 -0
  27. feather_xrol-0.1.0/src/feather_xrol/ids_api/streaming/Examples/streaming_example_while_loop.py +63 -0
  28. feather_xrol-0.1.0/src/feather_xrol/ids_api/streaming/__init__.py +0 -0
  29. feather_xrol-0.1.0/src/feather_xrol/ids_api/streaming/dll_wrapper.py +47 -0
  30. feather_xrol-0.1.0/src/feather_xrol/ids_api/streaming/file_parser.py +130 -0
  31. feather_xrol-0.1.0/src/feather_xrol/ids_api/streaming/stream.py +262 -0
  32. feather_xrol-0.1.0/src/feather_xrol/ids_api/streaming/streaming.py +143 -0
  33. feather_xrol-0.1.0/src/feather_xrol/ids_api/system.py +294 -0
  34. feather_xrol-0.1.0/src/feather_xrol/ids_api/system_service.py +214 -0
  35. feather_xrol-0.1.0/src/feather_xrol/ids_api/update.py +83 -0
  36. feather_xrol-0.1.0/src/feather_xrol.egg-info/PKG-INFO +65 -0
  37. feather_xrol-0.1.0/src/feather_xrol.egg-info/SOURCES.txt +38 -0
  38. feather_xrol-0.1.0/src/feather_xrol.egg-info/dependency_links.txt +1 -0
  39. feather_xrol-0.1.0/src/feather_xrol.egg-info/requires.txt +5 -0
  40. feather_xrol-0.1.0/src/feather_xrol.egg-info/top_level.txt +1 -0
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2026, Dream Beam
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,65 @@
1
+ Metadata-Version: 2.4
2
+ Name: feather-xrol
3
+ Version: 0.1.0
4
+ Summary: Bluesky and Ophyd integration for Attocube interferometer and Hexapod
5
+ Requires-Python: >=3.9
6
+ Description-Content-Type: text/markdown
7
+ License-File: LICENSE
8
+ Requires-Dist: bluesky
9
+ Requires-Dist: ophyd
10
+ Requires-Dist: pyepics
11
+ Requires-Dist: tiled
12
+ Requires-Dist: python-dotenv
13
+ Dynamic: license-file
14
+
15
+ # Project Feather
16
+ contact: awojdyla@lbl.gov
17
+ ![Starlings in flight](/assets/starlings_flach.jpg)
18
+
19
+ Project Feather aims at expending the use of [bluesky data collection framework](https://blueskyproject.io/bluesky/main/index.html) beyond synchrotron beamlines
20
+
21
+ We want to use Tiled to collect data from optical experiments, or to store metrology data so that it can easily be accessed.
22
+
23
+ Eventually, you might even be able to start a container that has everything you need.
24
+
25
+ ## feather-xrol Package
26
+
27
+ Integration for Attocube interferometer and Hexapod.
28
+
29
+ ### Installation
30
+
31
+ #### From Source (Development)
32
+ To install the package locally in editable mode for development:
33
+ ```bash
34
+ pip install -e .
35
+ ```
36
+
37
+ ### Usage
38
+
39
+ The package provides Ophyd device definitions for the ALSHexapod and Attocube interferometer.
40
+
41
+ #### Hexapod Example
42
+ See `examples/hexapod_scan.py` for a complete example of running a scan.
43
+ ```python
44
+ from feather_xrol.hexapod import ALSHexapod
45
+ from bluesky import RunEngine
46
+ from bluesky.plans import scan
47
+
48
+ hexapod = ALSHexapod('ALS:XROL-HEX0', name='hexapod')
49
+ RE = RunEngine({})
50
+ RE(scan([hexapod.x], hexapod.x, 0.0, 5.0, 10))
51
+ ```
52
+
53
+ #### Attocube Example
54
+ See `examples/test_attocube.py` for Attocube axis integration.
55
+ ```python
56
+ from feather_xrol.attocube import AttocubeAxis
57
+ from feather_xrol.ids_api.SEN import Device as AttocubeIDS3010
58
+
59
+ ids3010 = AttocubeIDS3010("192.168.10.81")
60
+ ids3010.connect()
61
+ atto = AttocubeAxis("atto_3", hardware_interface=ids3010, axis=2)
62
+ atto.trigger()
63
+ print(atto.read())
64
+ ids3010.close()
65
+ ```
@@ -0,0 +1,51 @@
1
+ # Project Feather
2
+ contact: awojdyla@lbl.gov
3
+ ![Starlings in flight](/assets/starlings_flach.jpg)
4
+
5
+ Project Feather aims at expending the use of [bluesky data collection framework](https://blueskyproject.io/bluesky/main/index.html) beyond synchrotron beamlines
6
+
7
+ We want to use Tiled to collect data from optical experiments, or to store metrology data so that it can easily be accessed.
8
+
9
+ Eventually, you might even be able to start a container that has everything you need.
10
+
11
+ ## feather-xrol Package
12
+
13
+ Integration for Attocube interferometer and Hexapod.
14
+
15
+ ### Installation
16
+
17
+ #### From Source (Development)
18
+ To install the package locally in editable mode for development:
19
+ ```bash
20
+ pip install -e .
21
+ ```
22
+
23
+ ### Usage
24
+
25
+ The package provides Ophyd device definitions for the ALSHexapod and Attocube interferometer.
26
+
27
+ #### Hexapod Example
28
+ See `examples/hexapod_scan.py` for a complete example of running a scan.
29
+ ```python
30
+ from feather_xrol.hexapod import ALSHexapod
31
+ from bluesky import RunEngine
32
+ from bluesky.plans import scan
33
+
34
+ hexapod = ALSHexapod('ALS:XROL-HEX0', name='hexapod')
35
+ RE = RunEngine({})
36
+ RE(scan([hexapod.x], hexapod.x, 0.0, 5.0, 10))
37
+ ```
38
+
39
+ #### Attocube Example
40
+ See `examples/test_attocube.py` for Attocube axis integration.
41
+ ```python
42
+ from feather_xrol.attocube import AttocubeAxis
43
+ from feather_xrol.ids_api.SEN import Device as AttocubeIDS3010
44
+
45
+ ids3010 = AttocubeIDS3010("192.168.10.81")
46
+ ids3010.connect()
47
+ atto = AttocubeAxis("atto_3", hardware_interface=ids3010, axis=2)
48
+ atto.trigger()
49
+ print(atto.read())
50
+ ids3010.close()
51
+ ```
@@ -0,0 +1,20 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "feather-xrol"
7
+ version = "0.1.0"
8
+ description = "Bluesky and Ophyd integration for Attocube interferometer and Hexapod"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ dependencies = [
12
+ "bluesky",
13
+ "ophyd",
14
+ "pyepics",
15
+ "tiled",
16
+ "python-dotenv"
17
+ ]
18
+
19
+ [tool.setuptools.packages.find]
20
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,2 @@
1
+ from .hexapod import ALSHexapod
2
+ from .attocube import AttocubeAxis
@@ -0,0 +1,50 @@
1
+ import time
2
+ from ophyd import Device
3
+ from ophyd.status import DeviceStatus
4
+
5
+ PM_TO_MM = 1e-9
6
+
7
+ class AttocubeAxis(Device):
8
+
9
+ def __init__(self, *args, hardware_interface, axis: int, **kwargs):
10
+ super().__init__(*args, **kwargs)
11
+ self._hw = hardware_interface
12
+ self._axis = axis
13
+ self._position_mm = None
14
+ self._timestamp = None
15
+
16
+ def trigger(self):
17
+ status = DeviceStatus(self)
18
+ try:
19
+ error, value = self._hw.displacement.getAbsolutePosition(self._axis)
20
+ if not error:
21
+ self._position_mm = value * PM_TO_MM
22
+ self._timestamp = time.time()
23
+ status.set_finished()
24
+ else:
25
+ status.set_exception(Exception(f"Attocube error code: {error}"))
26
+ except Exception as exc:
27
+ status.set_exception(exc)
28
+ return status
29
+
30
+ def read(self):
31
+ return {
32
+ self.name: {
33
+ "value": self._position_mm,
34
+ "timestamp": self._timestamp,
35
+ }
36
+ }
37
+
38
+ def describe(self):
39
+ return {
40
+ self.name: {
41
+ "source": f"AttocubeIDS3010:axis{self._axis}",
42
+ "dtype": "number",
43
+ "shape": [],
44
+ "units": "mm",
45
+ }
46
+ }
47
+
48
+ @property
49
+ def hints(self):
50
+ return {"fields": [self.name]}
@@ -0,0 +1,27 @@
1
+ from ophyd import Device, PVPositioner, EpicsSignal, EpicsSignalRO
2
+ from ophyd import Component as Cpt, FormattedComponent as FCpt
3
+
4
+ class HexapodAxis(PVPositioner):
5
+ setpoint = Cpt(EpicsSignal, ':TARGET')
6
+ readback = Cpt(EpicsSignalRO, ':POSITION')
7
+ done = FCpt(EpicsSignalRO, '{self.parent.prefix}-alldone')
8
+ done_value = 'done'
9
+ high_limit = Cpt(EpicsSignalRO, ':HLM')
10
+ low_limit = Cpt(EpicsSignalRO, ':LLM')
11
+ servo = Cpt(EpicsSignal, ':SERVO')
12
+ rb_servo = Cpt(EpicsSignalRO, ':RBSERVO')
13
+
14
+ class ALSHexapod(Device):
15
+ """ALS XROL Hexapod — 6DOF (X, Y, Z, U, V, W)"""
16
+ x = Cpt(HexapodAxis, '-X')
17
+ y = Cpt(HexapodAxis, '-Y')
18
+ z = Cpt(HexapodAxis, '-Z')
19
+ u = Cpt(HexapodAxis, '-U')
20
+ v = Cpt(HexapodAxis, '-V')
21
+ w = Cpt(HexapodAxis, '-W')
22
+ # Shared status / control
23
+ alldone = Cpt(EpicsSignalRO, '-alldone')
24
+ allstop = Cpt(EpicsSignal, '-allstop')
25
+ moving = Cpt(EpicsSignalRO, '-moving')
26
+ clear_err = Cpt(EpicsSignal, '-CLEAR_ERR')
27
+ last_err = Cpt(EpicsSignalRO, '-LAST_ERR')
@@ -0,0 +1,245 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ import sys
4
+ import socket
5
+ import json
6
+
7
+ from time import time, sleep
8
+
9
+ from random import randint
10
+
11
+ from threading import Thread, Lock
12
+ try:
13
+ import netifaces
14
+ except:
15
+ pass
16
+
17
+ import urllib.request
18
+ import xml.dom.minidom as minidom
19
+
20
+ class AttoException(Exception):
21
+ def __init__(self, errorText = None, errorNumber = 0):
22
+ self.errorText = errorText
23
+ self.errorNumber = errorNumber
24
+
25
+ class AttoResult():
26
+ def __init__(self, resultDict):
27
+ self.resultDict = resultDict
28
+
29
+ def __getitem__(self, index):
30
+ if "error" in self.resultDict:
31
+ raise AttoException("JSON error in %s" % self.resultDict['error'])
32
+
33
+ resultList = self.resultDict.get("result", [])
34
+ if len(resultList) <= index:
35
+ raise AttoException(errorText="Unknown error occured", errorNumber=-1)
36
+ return resultList[index]
37
+
38
+ def __repr__(self):
39
+ return json.dumps(self.resultDict)
40
+
41
+ def __str__(self):
42
+ return self.__repr__()
43
+
44
+
45
+ class Device(object):
46
+ TCP_PORT = 9090
47
+ is_open = False
48
+ request_id = randint(0, 1000000)
49
+ request_id_lock = Lock()
50
+ response_buffer = {}
51
+
52
+ def __init__(self, address):
53
+ self.address = address
54
+ self.language = 0
55
+ self.apiversion = 2
56
+ self.response_lock = Lock()
57
+
58
+ def __del__(self):
59
+ self.close()
60
+
61
+ def __enter__(self):
62
+ self.connect()
63
+ return self
64
+
65
+ def __exit__(self, exc_type, exc_value, traceback):
66
+ self.close()
67
+
68
+ def connect(self):
69
+ """
70
+ Initializes and connects the selected AMC device.
71
+ """
72
+ if not self.is_open:
73
+ tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
74
+ tcp.settimeout(10)
75
+ tcp.connect((self.address, self.TCP_PORT))
76
+ self.tcp = tcp
77
+ if sys.version_info[0] > 2:
78
+ self.bufferedSocket = tcp.makefile("rw", newline='\r\n')
79
+ else:
80
+ self.bufferedSocket = tcp.makefile("rw")
81
+ self.is_open = True
82
+
83
+ def close(self):
84
+ """
85
+ Closes the connection to the device.
86
+ Returns
87
+ -------
88
+ """
89
+ if self.is_open:
90
+ self.bufferedSocket.close()
91
+ self.tcp.close()
92
+ self.is_open = False
93
+
94
+ def sendRequest(self, method, params=False):
95
+ req = {
96
+ "jsonrpc": "2.0",
97
+ "method": method,
98
+ "api": self.apiversion,
99
+ "pid": self.pid
100
+ }
101
+ if params:
102
+ req["params"] = params
103
+ with Device.request_id_lock:
104
+ req["id"] = Device.request_id
105
+ self.bufferedSocket.write(json.dumps(req))
106
+ self.bufferedSocket.flush()
107
+ Device.request_id = Device.request_id + 1
108
+ return req["id"]
109
+
110
+ def getResponse(self, request_id):
111
+ start_time = time()
112
+ while True:
113
+ if request_id in self.response_buffer:
114
+ response = self.response_buffer[request_id]
115
+ del self.response_buffer[request_id]
116
+ return response
117
+ if time() - start_time > 10:
118
+ raise TimeoutError("No result")
119
+
120
+ # Only one thread is allowed to read buffer
121
+ # Otherwise, deadlock is possible
122
+ if self.response_lock.acquire(blocking=False):
123
+ try:
124
+ response = self.bufferedSocket.readline()
125
+ parsed = json.loads(response)
126
+ if parsed["id"] == request_id:
127
+ return AttoResult(parsed)
128
+ else:
129
+ self.response_buffer[parsed["id"]] = AttoResult(parsed)
130
+ finally:
131
+ self.response_lock.release()
132
+ else:
133
+ # Sleep to unblock scheduler
134
+ sleep(0.01)
135
+
136
+
137
+ def request(self,method,params=False):
138
+ """ Synchronous request.
139
+ """
140
+ if not self.is_open:
141
+ raise AttoException("not connected, use connect()");
142
+ request_id = self.sendRequest(method, params)
143
+ return self.getResponse(request_id)
144
+
145
+ def printError(self, errorNumber):
146
+ """ Converts the errorNumber into an error string an prints it to the
147
+ console.
148
+ Parameters
149
+ ----------
150
+ errorNumber : int
151
+ """
152
+ print("Error! " + str(self.system_service.errorNumberToString(self.language, errorNumber)[1]))
153
+
154
+ def handleError(self, response, ignoreFunctionError=False):
155
+ errNo = response[0]
156
+ if (errNo != 0 and errNo != 'null' and not ignoreFunctionError):
157
+ raise AttoException(("Error! " + str(self.system_service.errorNumberToString(self.language ,errNo))), errNo)
158
+ return errNo
159
+
160
+ @staticmethod
161
+ def discover(cls):
162
+ try:
163
+ network_ifaces = netifaces.interfaces()
164
+ except NameError:
165
+ print("Install netifaces for discovery")
166
+ print("Python:")
167
+ print("pip install netifaces")
168
+ print("\nPython3:")
169
+ print("pip3 install netifaces")
170
+ return {}
171
+
172
+ msg = \
173
+ 'M-SEARCH * HTTP/1.1\r\n' \
174
+ 'HOST:239.255.255.250:1900\r\n' \
175
+ 'ST:urn:schemas-attocube-com:device:' + str(cls) + ':1\r\n' \
176
+ 'MX:2\r\n' \
177
+ 'MAN:"ssdp:discover"\r\n' \
178
+ '\r\n'
179
+
180
+ def send_and_recv(iface, devices, devices_lock):
181
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
182
+ s.bind((iface, 0))
183
+ s.settimeout(2)
184
+ s.sendto(str.encode(msg), ('239.255.255.250', 1900))
185
+ try:
186
+ while True:
187
+ _, addr = s.recvfrom(65507)
188
+ with devices_lock:
189
+ devices.append(addr[0])
190
+ except socket.timeout:
191
+ pass
192
+
193
+ thread_pool = []
194
+ devices = []
195
+ devices_lock = Lock()
196
+
197
+ for iface in network_ifaces:
198
+ addr = netifaces.ifaddresses(iface)
199
+ if netifaces.AF_INET not in addr:
200
+ continue
201
+ for ip in addr[netifaces.AF_INET]:
202
+ if "addr" not in ip:
203
+ continue
204
+ thread_pool.append(Thread(target=send_and_recv, args=(ip["addr"], devices, devices_lock)))
205
+ thread_pool[-1].start()
206
+
207
+ for thread in thread_pool:
208
+ thread.join()
209
+
210
+ def getElementData(xmlNode, tag):
211
+ tagNodes = xmlNode.getElementsByTagName(tag)
212
+ if len(tagNodes) == 0:
213
+ return None
214
+ childNodes = tagNodes[0].childNodes
215
+ if len(childNodes) == 0:
216
+ return None
217
+ return childNodes[0].data
218
+
219
+ deviceInfos = {}
220
+ for ip in devices:
221
+ try:
222
+ location = "http://" + ip + ":49000/upnp.xml"
223
+ response = urllib.request.urlopen(location)
224
+ response = response.read()
225
+ xmlNode = minidom.parseString(response)
226
+
227
+ serialNumber = getElementData(xmlNode, 'serialNumber')
228
+ ipAddress = getElementData(xmlNode, 'ipAddress')
229
+ macAddress = getElementData(xmlNode, 'macAddress')
230
+ friendlyName = getElementData(xmlNode, 'friendlyName')
231
+ modelName = getElementData(xmlNode, 'modelName')
232
+ lockedStatus = getElementData(xmlNode, 'lockedStatus')
233
+
234
+ deviceInfos[ip] = (
235
+ serialNumber,
236
+ ipAddress,
237
+ macAddress,
238
+ friendlyName,
239
+ modelName,
240
+ lockedStatus
241
+ )
242
+ except:
243
+ pass
244
+
245
+ return deviceInfos
@@ -0,0 +1,57 @@
1
+ from ids_api import SEN # IDS API is the folder, in which the API is located.
2
+
3
+ import time
4
+
5
+ IP = "192.168.1.1" # IP of the IDS device
6
+ checkAlignment = True
7
+ data_samples = 10
8
+
9
+ # Setup connection to IDS
10
+ ids = SEN.Device(IP)
11
+ ids.connect()
12
+
13
+ axis = 0 # Internally, axes are numbered 0 to 2
14
+
15
+ ids.displacement.setAverageN(14) # this is important: it sets the average of the IDS displacement
16
+
17
+ # check alignment
18
+ if checkAlignment:
19
+ if ids.system.getCurrentMode() == 'measurement running':
20
+ ids.system.stopMeasurement()
21
+ time.sleep(3)
22
+
23
+ if ids.system.getCurrentMode() == "system idle":
24
+ ids.system.startOpticsAlignment()
25
+ while ids.system.getCurrentMode() == 'optics alignment starting':
26
+ time.sleep(1)
27
+ for i in range(5):
28
+ print("Alignment: ", ids.adjustment.getContrastInPermille(axis))
29
+ ids.system.stopOpticsAlignment()
30
+ time.sleep(5)
31
+
32
+
33
+ # check if the IDS measurement is running and start the measurement:
34
+ if ids.system.getCurrentMode() != 'measurement running':
35
+ if ids.system.getCurrentMode() == "optics alignment running":
36
+ ids.system.stopOpticsAlignment()
37
+ time.sleep(3)
38
+ if ids.system.getCurrentMode() == "system idle":
39
+ ids.system.startMeasurement()
40
+ time.sleep(1)
41
+ print('measurement will be started')
42
+ while ids.system.getCurrentMode() == "measurement starting":
43
+ time.sleep(2)
44
+
45
+ if ids.system.getCurrentMode() != 'measurement running':
46
+ raise Exception("IDS not in measurement mode. Please check mode!")
47
+
48
+ # readout data
49
+ for i in range(data_samples):
50
+ error, value = ids.displacement.getAxisDisplacement(axis)
51
+ print("Displacement: ", value)
52
+ time.sleep(0.5)
53
+
54
+ # turn off measurement mode
55
+ ids.system.stopMeasurement()
56
+
57
+ ids.close()
@@ -0,0 +1,51 @@
1
+ from . import ACS
2
+ from .pilotlaser import Pilotlaser
3
+ from .update import Update
4
+ from .system import System
5
+ from .nlc import Nlc
6
+ from .realtime import Realtime
7
+ from .access import Access
8
+ from .adjustment import Adjustment
9
+ from .ecu import Ecu
10
+ from .manual import Manual
11
+ from .system_service import System_service
12
+ from .about import About
13
+ from .axis import Axis
14
+ from .network import Network
15
+ from .displacement import Displacement
16
+ try:
17
+ from .streaming.streaming import Streaming
18
+ except:
19
+ pass
20
+
21
+
22
+ class Device(ACS.Device):
23
+ def __init__(self, address):
24
+ super().__init__(address)
25
+ self.pid = "sen"
26
+ self.pilotlaser = Pilotlaser(self)
27
+ self.update = Update(self)
28
+ self.system = System(self)
29
+ self.nlc = Nlc(self)
30
+ self.realtime = Realtime(self)
31
+ self.access = Access(self)
32
+ self.adjustment = Adjustment(self)
33
+ self.ecu = Ecu(self)
34
+ self.manual = Manual(self)
35
+ self.system_service = System_service(self)
36
+ self.about = About(self)
37
+ self.axis = Axis(self)
38
+ self.network = Network(self)
39
+ self.displacement = Displacement(self)
40
+
41
+ try:
42
+ self.streaming = Streaming(self)
43
+ except NameError as e:
44
+ if "Streaming" in str(e):
45
+ print("Warning: Streaming is not supported on your platform")
46
+ else:
47
+ raise e
48
+
49
+
50
+ def discover():
51
+ return Device.discover("sen")
@@ -0,0 +1 @@
1
+ # This marks the directory as a Python package
@@ -0,0 +1,37 @@
1
+ class About:
2
+ def __init__(self, device):
3
+ self.device = device
4
+ self.interface_name = "com.attocube.system.about"
5
+
6
+ def getInstalledPackages(self):
7
+ # type: () -> (str)
8
+ """
9
+ Gets list of packages installed on the device.
10
+ Returns:
11
+ value_errNo: errNo error code, if there was an error, otherwise 0 for ok
12
+ value_string: string comma separated list of packages
13
+
14
+ """
15
+
16
+ response = self.device.request(self.interface_name + ".getInstalledPackages")
17
+ self.device.handleError(response)
18
+ return response[1]
19
+
20
+ def getPackageLicense(self, pckg):
21
+ # type: (str) -> (str)
22
+ """
23
+ Gets the license for a specific package.
24
+
25
+ Parameters:
26
+ pckg: package name string
27
+
28
+ Returns:
29
+ value_errNo: errNo error code, if there was an error, otherwise 0 for ok
30
+ value_string: string license for this package
31
+
32
+ """
33
+
34
+ response = self.device.request(self.interface_name + ".getPackageLicense", [pckg, ])
35
+ self.device.handleError(response)
36
+ return response[1]
37
+