remla 0.2.4__tar.gz → 0.2.5.dev0__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.
- {remla-0.2.4 → remla-0.2.5.dev0}/PKG-INFO +3 -2
- {remla-0.2.4 → remla-0.2.5.dev0}/pyproject.toml +1 -1
- {remla-0.2.4 → remla-0.2.5.dev0}/remla/labcontrol/Controllers.py +12 -12
- {remla-0.2.4 → remla-0.2.5.dev0}/remla/labcontrol/Experiment.py +40 -7
- {remla-0.2.4 → remla-0.2.5.dev0}/remla/main.py +12 -0
- {remla-0.2.4 → remla-0.2.5.dev0}/README.md +0 -0
- {remla-0.2.4 → remla-0.2.5.dev0}/remla/__init__.py +0 -0
- {remla-0.2.4 → remla-0.2.5.dev0}/remla/customvalidators.py +0 -0
- {remla-0.2.4 → remla-0.2.5.dev0}/remla/i2ccmd.py +0 -0
- {remla-0.2.4 → remla-0.2.5.dev0}/remla/labcontrol/__init__.py +0 -0
- {remla-0.2.4 → remla-0.2.5.dev0}/remla/settings.py +0 -0
- {remla-0.2.4 → remla-0.2.5.dev0}/remla/setup/finalInfoTemplate.md +0 -0
- {remla-0.2.4 → remla-0.2.5.dev0}/remla/setup/hello.txt +0 -0
- {remla-0.2.4 → remla-0.2.5.dev0}/remla/setup/i2c-output.png +0 -0
- {remla-0.2.4 → remla-0.2.5.dev0}/remla/setup/index.html +0 -0
- {remla-0.2.4 → remla-0.2.5.dev0}/remla/setup/localhost.conf +0 -0
- {remla-0.2.4 → remla-0.2.5.dev0}/remla/setup/mediaMTXGetFeed.js +0 -0
- {remla-0.2.4 → remla-0.2.5.dev0}/remla/setup/mediamtx.service +0 -0
- {remla-0.2.4 → remla-0.2.5.dev0}/remla/setup/mediamtx.yml +0 -0
- {remla-0.2.4 → remla-0.2.5.dev0}/remla/setup/reader.js +0 -0
- {remla-0.2.4 → remla-0.2.5.dev0}/remla/setup/remlaSocket.js +0 -0
- {remla-0.2.4 → remla-0.2.5.dev0}/remla/setupcmd.py +0 -0
- {remla-0.2.4 → remla-0.2.5.dev0}/remla/systemHelpers.py +0 -0
- {remla-0.2.4 → remla-0.2.5.dev0}/remla/typerHelpers.py +0 -0
- {remla-0.2.4 → remla-0.2.5.dev0}/remla/yaml.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: remla
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.5.dev0
|
|
4
4
|
Summary:
|
|
5
5
|
Author: Zak Espley
|
|
6
6
|
Author-email: zespley@gmail.com
|
|
@@ -9,6 +9,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
9
9
|
Classifier: Programming Language :: Python :: 3.11
|
|
10
10
|
Classifier: Programming Language :: Python :: 3.12
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.13
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
12
13
|
Requires-Dist: adafruit-circuitpython-motorkit (>=1.6.14,<2.0.0)
|
|
13
14
|
Requires-Dist: dlipower (>=1.0.176,<2.0.0)
|
|
14
15
|
Requires-Dist: pathvalidate (>=3.2.0,<4.0.0)
|
|
@@ -391,9 +391,9 @@ class StepperI2C(MotorKit, BaseController):
|
|
|
391
391
|
if status == gpio.HIGH:
|
|
392
392
|
response = switch.switchAction(self, steps - i)
|
|
393
393
|
if response is None:
|
|
394
|
-
return "{0}/{1}/{2}".format(self.name, "position", "limit")
|
|
394
|
+
return ("ALERT", "{0}/{1}/{2}".format(self.name, "position", "limit"))
|
|
395
395
|
else:
|
|
396
|
-
return response
|
|
396
|
+
return ("MESSAGE", response)
|
|
397
397
|
|
|
398
398
|
if self.homing:
|
|
399
399
|
homeStatus = self.homeSwitch.getStatus(1)
|
|
@@ -410,9 +410,9 @@ class StepperI2C(MotorKit, BaseController):
|
|
|
410
410
|
self.currentPosition == self.upperBound
|
|
411
411
|
or self.currentPosition == self.lowerBound
|
|
412
412
|
):
|
|
413
|
-
return "{0}/{1}/{2}".format(self.name, "position", "limit")
|
|
413
|
+
return ("ALERT", "{0}/{1}/{2}".format(self.name, "position", "limit"))
|
|
414
414
|
else:
|
|
415
|
-
return "{0}/{1}/{2}".format(self.name, "position", self.currentPosition)
|
|
415
|
+
return ("MESSAGE", "{0}/{1}/{2}".format(self.name, "position", self.currentPosition))
|
|
416
416
|
|
|
417
417
|
def move_parser(self, params):
|
|
418
418
|
if len(params) != 1:
|
|
@@ -439,13 +439,13 @@ class StepperI2C(MotorKit, BaseController):
|
|
|
439
439
|
print(position)
|
|
440
440
|
endPoint = self.refPoints[position]
|
|
441
441
|
response = self.move(endPoint - self.currentPosition)
|
|
442
|
-
return response
|
|
442
|
+
return ("MESSAGE", response)
|
|
443
443
|
|
|
444
444
|
def admingoto(self, position):
|
|
445
445
|
print(position)
|
|
446
446
|
endPoint = self.refPoints[position]
|
|
447
447
|
response = self.adminMove(endPoint - self.currentPosition)
|
|
448
|
-
return response
|
|
448
|
+
return ("MESSAGE", response)
|
|
449
449
|
|
|
450
450
|
def goto_parser(self, params):
|
|
451
451
|
if len(params) != 1:
|
|
@@ -470,7 +470,7 @@ class StepperI2C(MotorKit, BaseController):
|
|
|
470
470
|
step = int(step * self.gearRatio)
|
|
471
471
|
step = round(step)
|
|
472
472
|
response = self.move(step)
|
|
473
|
-
return response
|
|
473
|
+
return ("MESSAGE", response)
|
|
474
474
|
|
|
475
475
|
def homeMove(self, stepLimit=5000, additionalSteps=10):
|
|
476
476
|
if self.currentPosition > 0:
|
|
@@ -1233,9 +1233,9 @@ class PololuStepperMotor(BaseController):
|
|
|
1233
1233
|
self.currentPosition == self.upperBound
|
|
1234
1234
|
or self.currentPosition == self.lowerBound
|
|
1235
1235
|
):
|
|
1236
|
-
return "{0}/{1}/{2}".format(self.name, "position", "limit")
|
|
1236
|
+
return ("ALERT", "{0}/{1}/{2}".format(self.name, "position", "limit"))
|
|
1237
1237
|
else:
|
|
1238
|
-
return "{0}/{1}/{2}".format(self.name, "position", self.currentPosition)
|
|
1238
|
+
return ("MESSAGE", "{0}/{1}/{2}".format(self.name, "position", self.currentPosition))
|
|
1239
1239
|
|
|
1240
1240
|
def move_parser(self, params):
|
|
1241
1241
|
if len(params) != 1:
|
|
@@ -1265,7 +1265,7 @@ class PololuStepperMotor(BaseController):
|
|
|
1265
1265
|
step = int(step * self.gearRatio)
|
|
1266
1266
|
step = round(step)
|
|
1267
1267
|
response = self.move(step)
|
|
1268
|
-
return response
|
|
1268
|
+
return ("MESSAGE", response)
|
|
1269
1269
|
|
|
1270
1270
|
def homeMove(self, stepLimit=5000, additionalSteps=10):
|
|
1271
1271
|
if self.currentPosition > 0:
|
|
@@ -1921,9 +1921,9 @@ class S42CStepperMotor(BaseController):
|
|
|
1921
1921
|
self.state["position"] += moveSteps
|
|
1922
1922
|
|
|
1923
1923
|
if self.state["position"] == self.UB or self.state["position"] == self.LB:
|
|
1924
|
-
return "{0}/{1}/{2}".format(self.name, "position", "limit")
|
|
1924
|
+
return ("ALERT", "{0}/{1}/{2}".format(self.name, "position", "limit"))
|
|
1925
1925
|
else:
|
|
1926
|
-
return "{0}/{1}/{2}".format(self.name, "position", self.state["position"])
|
|
1926
|
+
return ("MESSAGE", "{0}/{1}/{2}".format(self.name, "position", self.state["position"]))
|
|
1927
1927
|
|
|
1928
1928
|
def goto(self, ref: str, safe=True):
|
|
1929
1929
|
"""
|
|
@@ -63,6 +63,9 @@ class Experiment(object):
|
|
|
63
63
|
#### Starting New Log ####
|
|
64
64
|
##############################################################
|
|
65
65
|
""")
|
|
66
|
+
self.startIpcListener()
|
|
67
|
+
self.loop = asyncio.new_event_loop()
|
|
68
|
+
asyncio.set_event_loop(self.loop)
|
|
66
69
|
|
|
67
70
|
def logException(self, task):
|
|
68
71
|
if task.exception():
|
|
@@ -156,7 +159,7 @@ class Experiment(object):
|
|
|
156
159
|
if lockGroupName:
|
|
157
160
|
async with self.lockGroups[lockGroupName]:
|
|
158
161
|
loop = asyncio.get_event_loop()
|
|
159
|
-
result = await loop.run_in_executor(
|
|
162
|
+
response_type,result = await loop.run_in_executor(
|
|
160
163
|
self.executor, runMethod, device, method, params
|
|
161
164
|
)
|
|
162
165
|
else:
|
|
@@ -164,19 +167,22 @@ class Experiment(object):
|
|
|
164
167
|
raise
|
|
165
168
|
# result = await self.runMethod(device, method, params)
|
|
166
169
|
if result is not None:
|
|
167
|
-
|
|
170
|
+
if result[0] == "ALERT":
|
|
171
|
+
await self.sendAlert(websocket, f"{result[1]}")
|
|
172
|
+
else:
|
|
173
|
+
await self.sendMessage(websocket, f"{result[1]}")
|
|
168
174
|
else:
|
|
169
175
|
await self.sendMessage(websocket, f"{deviceName} ran {method}")
|
|
170
176
|
|
|
171
177
|
def startServer(self):
|
|
172
178
|
# This function sets up and runs the WebSocket server indefinitely
|
|
173
|
-
loop = asyncio.new_event_loop()
|
|
174
|
-
asyncio.set_event_loop(loop)
|
|
179
|
+
# loop = asyncio.new_event_loop()
|
|
180
|
+
asyncio.set_event_loop(self.loop)
|
|
175
181
|
start_server = websockets.serve(self.handleConnection, self.host, self.port)
|
|
176
182
|
|
|
177
183
|
print(f"Server started at ws://{self.host}:{self.port}")
|
|
178
|
-
loop.run_until_complete(start_server)
|
|
179
|
-
loop.run_forever()
|
|
184
|
+
self.loop.run_until_complete(start_server)
|
|
185
|
+
self.loop.run_forever()
|
|
180
186
|
|
|
181
187
|
async def sendDataToClient(self, websocket, dataStr: str):
|
|
182
188
|
try:
|
|
@@ -290,6 +296,33 @@ class Experiment(object):
|
|
|
290
296
|
f"Socket file not found. Did you configure uv4l-uvc.conf to use {self.socketPath}?"
|
|
291
297
|
)
|
|
292
298
|
raise
|
|
293
|
-
except socket.error as err:
|
|
294
299
|
logging.error("Socket Error!", exc_info=True)
|
|
295
300
|
print(f"Socket error: {err}")
|
|
301
|
+
|
|
302
|
+
def startIpcListener(self, ipc_path="/tmp/remla_cmd.sock"):
|
|
303
|
+
# Remove old socket if exists
|
|
304
|
+
if os.path.exists(ipc_path):
|
|
305
|
+
os.unlink(ipc_path)
|
|
306
|
+
ipc_sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
|
307
|
+
ipc_sock.bind(ipc_path)
|
|
308
|
+
ipc_sock.listen(1)
|
|
309
|
+
print(f"IPC listener started at {ipc_path}")
|
|
310
|
+
|
|
311
|
+
def ipc_loop():
|
|
312
|
+
while True:
|
|
313
|
+
conn, _ = ipc_sock.accept()
|
|
314
|
+
data = conn.recv(1024).decode().strip()
|
|
315
|
+
if data == "boot":
|
|
316
|
+
# Send boot message to active client
|
|
317
|
+
if self.activeClient:
|
|
318
|
+
future = asyncio.run_coroutine_threadsafe(
|
|
319
|
+
self.sendMessage(self.activeClient, "Experiment/message/boot"),
|
|
320
|
+
self.loop
|
|
321
|
+
)
|
|
322
|
+
print("Sent boot message to active client.")
|
|
323
|
+
else:
|
|
324
|
+
print("No active client to send boot message.")
|
|
325
|
+
conn.close()
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
threading.Thread(target=ipc_loop, daemon=True).start()
|
|
@@ -726,6 +726,18 @@ def testws():
|
|
|
726
726
|
asyncio.get_event_loop().run_until_complete(start_server)
|
|
727
727
|
asyncio.get_event_loop().run_forever()
|
|
728
728
|
|
|
729
|
+
@app.command()
|
|
730
|
+
def boot():
|
|
731
|
+
"""Send 'boot' command to the running remla server via IPC."""
|
|
732
|
+
ipc_path = "/tmp/remla_cmd.sock"
|
|
733
|
+
try:
|
|
734
|
+
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as sock:
|
|
735
|
+
sock.connect(ipc_path)
|
|
736
|
+
sock.sendall(b"boot")
|
|
737
|
+
print("Boot command sent to server.")
|
|
738
|
+
except Exception as e:
|
|
739
|
+
print(f"Failed to send boot command: {e}")
|
|
740
|
+
|
|
729
741
|
|
|
730
742
|
if __name__ == "__main__":
|
|
731
743
|
app()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|