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.
Files changed (25) hide show
  1. {remla-0.2.4 → remla-0.2.5.dev0}/PKG-INFO +3 -2
  2. {remla-0.2.4 → remla-0.2.5.dev0}/pyproject.toml +1 -1
  3. {remla-0.2.4 → remla-0.2.5.dev0}/remla/labcontrol/Controllers.py +12 -12
  4. {remla-0.2.4 → remla-0.2.5.dev0}/remla/labcontrol/Experiment.py +40 -7
  5. {remla-0.2.4 → remla-0.2.5.dev0}/remla/main.py +12 -0
  6. {remla-0.2.4 → remla-0.2.5.dev0}/README.md +0 -0
  7. {remla-0.2.4 → remla-0.2.5.dev0}/remla/__init__.py +0 -0
  8. {remla-0.2.4 → remla-0.2.5.dev0}/remla/customvalidators.py +0 -0
  9. {remla-0.2.4 → remla-0.2.5.dev0}/remla/i2ccmd.py +0 -0
  10. {remla-0.2.4 → remla-0.2.5.dev0}/remla/labcontrol/__init__.py +0 -0
  11. {remla-0.2.4 → remla-0.2.5.dev0}/remla/settings.py +0 -0
  12. {remla-0.2.4 → remla-0.2.5.dev0}/remla/setup/finalInfoTemplate.md +0 -0
  13. {remla-0.2.4 → remla-0.2.5.dev0}/remla/setup/hello.txt +0 -0
  14. {remla-0.2.4 → remla-0.2.5.dev0}/remla/setup/i2c-output.png +0 -0
  15. {remla-0.2.4 → remla-0.2.5.dev0}/remla/setup/index.html +0 -0
  16. {remla-0.2.4 → remla-0.2.5.dev0}/remla/setup/localhost.conf +0 -0
  17. {remla-0.2.4 → remla-0.2.5.dev0}/remla/setup/mediaMTXGetFeed.js +0 -0
  18. {remla-0.2.4 → remla-0.2.5.dev0}/remla/setup/mediamtx.service +0 -0
  19. {remla-0.2.4 → remla-0.2.5.dev0}/remla/setup/mediamtx.yml +0 -0
  20. {remla-0.2.4 → remla-0.2.5.dev0}/remla/setup/reader.js +0 -0
  21. {remla-0.2.4 → remla-0.2.5.dev0}/remla/setup/remlaSocket.js +0 -0
  22. {remla-0.2.4 → remla-0.2.5.dev0}/remla/setupcmd.py +0 -0
  23. {remla-0.2.4 → remla-0.2.5.dev0}/remla/systemHelpers.py +0 -0
  24. {remla-0.2.4 → remla-0.2.5.dev0}/remla/typerHelpers.py +0 -0
  25. {remla-0.2.4 → remla-0.2.5.dev0}/remla/yaml.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: remla
3
- Version: 0.2.4
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)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "remla"
3
- version = "0.2.4"
3
+ version = "0.2.5-dev"
4
4
  description = ""
5
5
  authors = ["Zak Espley <zespley@gmail.com>"]
6
6
  readme = "README.md"
@@ -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
- await self.sendMessage(websocket, f"{deviceName} - {result}")
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