From ef4ebf00395f8be16f85eb461a99885bf8dcaa42 Mon Sep 17 00:00:00 2001 From: Reed Riddle Date: Tue, 19 May 2026 16:33:28 -0700 Subject: [PATCH 01/16] Initial keyword implementation of fw --- config/hsfei/hsfei_filterwheel.yaml | 31 ++++ daemons/generic/filterwheel | 260 ++++++++++++++++++++++++++++ 2 files changed, 291 insertions(+) create mode 100644 config/hsfei/hsfei_filterwheel.yaml create mode 100644 daemons/generic/filterwheel diff --git a/config/hsfei/hsfei_filterwheel.yaml b/config/hsfei/hsfei_filterwheel.yaml new file mode 100644 index 0000000..f072de0 --- /dev/null +++ b/config/hsfei/hsfei_filterwheel.yaml @@ -0,0 +1,31 @@ +# Single Daemon Configuration Example +# For standalone daemon deployment or development/testing +# +# Usage: +# daemon = Atcfwheel.from_config_file("config/hsfei_atcfwheel.yaml") +# ATC Pickoff Dichroic Selector — C-663 stepper instance +# +# Usage: +# daemons/hsfei/pi-daemon -c config/hsfei/hsfei_atcpickoff.yaml + +peer_id: hsfei_atcfwheel +group_id: hsfei + +hardware: + ip_address: 192.168.29.100 + tcp_port: 10010 + units: filter_pos + timeout_s: 30.0 + retry_count: 3 + +named_positions: + clear: 1 + nd1: 2 + nd2: 3 + nd3: 4 + nd4: 5 + nd5: 6 + +logging: + level: INFO + # file: /var/log/hispec/atcfwheel.log diff --git a/daemons/generic/filterwheel b/daemons/generic/filterwheel new file mode 100644 index 0000000..7f80c27 --- /dev/null +++ b/daemons/generic/filterwheel @@ -0,0 +1,260 @@ +#!/usr/bin/python3.12 +'''Module for the Filter Wheel Daemon''' +import argparse +import sys +import threading +from typing import Dict, Any, Optional #pylint: disable = W0611 + +from hispec.daemon import HispecDaemon #pylint: disable = E0401,E0611 +from hispec.driver.thorlabs.fw102c import FilterWheelController #pylint: disable = E0401,E0611 +from libby import KeywordRegistry #pylint: disable = E0611 + +class Filterwheel(HispecDaemon): #pylint: disable = W0223 + '''Daemon for controlling the Filter Wheel via Thorlabs FW102C controller''' + + def __init__(self): + """Initialize the Filter Wheel daemon. + + Args: come from the hsfei configuration file + """ + super().__init__() + + self.host = None + self.port = None + self.dev = FilterWheelController(log=True) + self.daemon_desc = self.get_config("peer_id") + self._soft_min = None # No limit by default + self._soft_max = None # No limit by default + + # Daemon state + self.state = { + 'connected': False, + 'error': '' + } + + def on_start(self, libby): + '''Starts up daemon and initializies the hardware device''' + self.host = self.get_config("hardware.ip_address") + self.port = self.get_config("hardware.tcp_port") + + # Initialize hardware connection + if not(self.host and self.port): + self.logger.error("No IP address or port specified for Filter Wheel controller") + self.state['error'] = 'No IP address or port specified' + return + try: + connection = self.connect() + if not connection.get("ok"): + raise ConnectionError(connection.get("Error")) + self.state['connected'] = True + self.logger.info("Daemon started successfully and connected to hardware") + self.initialize() + self.logger.info("Initialized %s", self.daemon_desc) + except ConnectionRefusedError as e: + self.logger.error("Failed to connect to hardware: %s", e) + self.logger.warning("Daemon will start but hardware is not available") + self.state['error'] = str(e) + self.state['connected'] = False + + # Publish initial status + libby.publish(self.peer_id, self.state) + + def initialize(self): + """handles initialization""" + if not self.state['connected']: + return {"ok": False, "error": "Not connected to hardware"} + + try: + self.dev.initialize() + self.keyword_registry.bool("isconnected", + getter=self.keyword_wrapper(self.dev.isconnected), + description="Check if daemon can talk to the FW controller.") + self.keyword_registry.int("position", + getter=self.keyword_wrapper(self.get_pos, key="position"), + setter=lambda v: self.keyword_wrapper(self.set_pos, int(v)), + description="Set and get current position of FilterWheel.") + self.keyword_registry.str("namedposition", + getter=self.keyword_wrapper(self.cur_named_position, key="named_pos"), + setter=lambda v: self.keyword_wrapper(self.goto_named_pos, str(v)), + description="Set and get named position of FilterWheel.") + self.keyword_registry.int("softmin", + getter=lambda: self._soft_min, + setter=lambda v: setattr(self, "_soft_min", int(v)), + description="Software lower limit for filter wheel position.") + self.keyword_registry.int("softmax", + getter=lambda: self._soft_max, + setter=lambda v: setattr(self, "_soft_max", int(v)), + description="Software upper limit for filter wheel position.") + + except Exception as e: # pylint: disable=W0718 + self.logger.error("Error: %s",e) + return {"ok":False , "error": str(e)} + return {"ok": True} + + def get_named_positions(self): + """Get named positions from config (e.g., home, deployed, science).""" + return self._config.get("named_positions", {}) + + def get_named_position(self, name: str): + """Get a specific named position value, or None if not found.""" + return self.get_named_positions().get(name) + + def cur_named_position(self): + """Get the name of the current position, if it matches a named position.""" + current_pos = self.dev.get_pos() + for name, pos in self.get_named_positions().items(): + if str(pos) == str(current_pos): + return {"ok": True, "named_pos": name, "position": current_pos} + return {"ok": False, "error": "Current position does not match any named position", "position": current_pos} #pylint: disable = C0301 + + def on_stop(self, libby) -> None: #pylint: disable=W0222 + '''Stops the daemon and disconnects from hardware device''' + try: + self.disconnect() + self.logger.info("Disconnected %s", self.daemon_desc) + libby.publish(self.peer_id, {"Daemon Shutdown": "Success"}) + except Exception as e: # pylint: disable=W0718 + libby.publish(self.peer_id, {"Daemon Startup": "Failed", "Error":f"{e}"}) + self.logger.error("Disconnect %s:: Failed ", self.daemon_desc) + + + def connect(self): + """handles connection""" + try: + self.dev.connect(host = self.host, port = self.port) + if not self.dev.is_connected(): + raise ConnectionError("Failed to connect to device") + self.logger.info("Connected %s", self.daemon_desc) + except Exception as e: # pylint: disable=W0718 + self.logger.error("Failed to Connect to Hardware: %s",e) + return {"ok": False, "error": str(e)} + return {"ok": True, "message": "Connected to hardware"} + + def disconnect(self): + """handles disconnection""" + try: + self.dev.disconnect() + if self.dev.is_connected(): + raise ConnectionAbortedError("Failed to disconnect to device") + self.logger.info("Disconnected from %s", self.daemon_desc) + except Exception as e: # pylint: disable=W0718 + self.logger.error("Error: %s",e) + return {"ok": False, "error": str(e)} + return {"ok": True, "message": "Disconnected from hardware"} + + def status(self): + """handles status""" + try: + limits = self.dev.get_limits() + position = self.cur_named_position() + status = { + "connected": self.dev.is_connected(), + "position": position.get("position"), + "named_pos": position.get("named_pos"), + "min_limit": limits.get("1")[0], + "max_limit": limits.get("1")[1], + } + self.logger.debug("status: %s",status) + except Exception as e: # pylint: disable=W0718 + self.logger.error("Error: %s",e) + return {"ok": False, "error": str(e)} + return {"ok": True, "status": status} + + def get_pos(self): + '''gets current position''' + if not self.state['connected']: + return {"ok": False, "error": "Not connected to hardware"} + + try: + position = self.dev.get_pos() + self.logger.debug("get_pos: %s",position) + except Exception as e: # pylint: disable=W0718 + self.logger.error("Error: %s",e) + return {"ok": False, "error": str(e)} + return {"ok":True, "position": int(position)} + + def set_pos(self, pos): + '''sets current position''' + if not self.state['connected']: + return {"ok": False, "error": "Not connected to hardware"} + + try: + pos = int(pos) + self._check_soft_limits(pos) + self.dev.set_pos(pos) + self.logger.debug("set_pos: %d",pos) + except Exception as e: # pylint: disable=W0718 + self.logger.error("Error: %s",e) + return {"ok": False, "error": str(e)} + return {"ok": True, "position": int(pos)} + + def goto_named_pos(self, name): + '''moves to named position''' + if not self.state['connected']: + return {"ok": False, "error": "Not connected to hardware"} + + try: + goal = self.get_named_position(name.lower()) + if goal is not None: + self.dev.set_pos(int(goal)) + self.logger.debug("goto_named_pos: %s -> %s",name,goal) + except Exception as e: # pylint: disable=W0718 + self.logger.error("Error: %s",e) + return {"ok": False, "error": str(e)} + return {"ok": True, "named_pos": name, "position": goal} + + def _check_soft_limits(self, pos: int) -> bool: + """Returns True if position is within soft limits.""" + if self._soft_min is not None and pos < self._soft_min: + raise ValueError(f"Position {pos} below soft min {self._soft_min}") + if self._soft_max is not None and pos > self._soft_max: + raise ValueError(f"Position {pos} above soft max {self._soft_max}") + return True + + @staticmethod + def keyword_wrapper(func, key=None): + """Wrap a daemon method for use as a keyword getter/setter. + + Returns the value at `key` on success, or raises RuntimeError on failure. + If no `key` is given, returns the full result dict (minus 'ok'). + """ + def wrapper(self, *args, **kwargs): + result = func(*args, **kwargs) + if not result.get("ok"): + error = result.get("error", f"Unknown error in {func.__name__}") + self.logger.error("keyword_wrapper [%s]: %s", func.__name__, error) + raise RuntimeError(error) + if key: + return result[key] + return {k: v for k, v in result.items() if k != "ok"} + return wrapper + +def main(): + """Main entry point for the daemon.""" + parser = argparse.ArgumentParser( + description='FilterWheel Daemon' + ) + parser.add_argument('-c', '--config', type=str, + help='Path to config file (YAML or JSON)') + parser.add_argument('-d', '--daemon-id', type=str, + help='Daemon ID (required for subsystem configs with multiple daemons)') + + args = parser.parse_args() + + if not args.config: + print("--config is required", file=sys.stderr) + sys.exit(2) + + try: + daemon = Filterwheel.from_config_file(args.config, daemon_id=args.daemon_id) + daemon.serve() + except KeyboardInterrupt: + print("\nDaemon interrupted by user") + sys.exit(0) + except Exception as e: + print(f"Error running daemon: {e}", file=sys.stderr) + sys.exit(1) + + +if __name__ == '__main__': + main() From ca4f602e598879e3bff0de6b3dc9ec5797713e0f Mon Sep 17 00:00:00 2001 From: Reed Riddle Date: Tue, 19 May 2026 16:37:38 -0700 Subject: [PATCH 02/16] pylint compliant --- daemons/generic/filterwheel | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/daemons/generic/filterwheel b/daemons/generic/filterwheel index 7f80c27..7b541b4 100644 --- a/daemons/generic/filterwheel +++ b/daemons/generic/filterwheel @@ -67,24 +67,24 @@ class Filterwheel(HispecDaemon): #pylint: disable = W0223 try: self.dev.initialize() self.keyword_registry.bool("isconnected", - getter=self.keyword_wrapper(self.dev.isconnected), - description="Check if daemon can talk to the FW controller.") + getter=self.keyword_wrapper(self.dev.isconnected), + description="Check if daemon can talk to the FW controller.") self.keyword_registry.int("position", - getter=self.keyword_wrapper(self.get_pos, key="position"), - setter=lambda v: self.keyword_wrapper(self.set_pos, int(v)), - description="Set and get current position of FilterWheel.") + getter=self.keyword_wrapper(self.get_pos, key="position"), + setter=lambda v: self.keyword_wrapper(self.set_pos, int(v)), + description="Set and get current position of FilterWheel.") self.keyword_registry.str("namedposition", - getter=self.keyword_wrapper(self.cur_named_position, key="named_pos"), - setter=lambda v: self.keyword_wrapper(self.goto_named_pos, str(v)), - description="Set and get named position of FilterWheel.") + getter=self.keyword_wrapper(self.cur_named_position, key="named_pos"), + setter=lambda v: self.keyword_wrapper(self.goto_named_pos, str(v)), + description="Set and get named position of FilterWheel.") self.keyword_registry.int("softmin", - getter=lambda: self._soft_min, - setter=lambda v: setattr(self, "_soft_min", int(v)), - description="Software lower limit for filter wheel position.") + getter=lambda: self._soft_min, + setter=lambda v: setattr(self, "_soft_min", int(v)), + description="Software lower limit for filter wheel position.") self.keyword_registry.int("softmax", - getter=lambda: self._soft_max, - setter=lambda v: setattr(self, "_soft_max", int(v)), - description="Software upper limit for filter wheel position.") + getter=lambda: self._soft_max, + setter=lambda v: setattr(self, "_soft_max", int(v)), + description="Software upper limit for filter wheel position.") except Exception as e: # pylint: disable=W0718 self.logger.error("Error: %s",e) From 74c130eb70d17ca77164c87ab253ead4d28c9d6e Mon Sep 17 00:00:00 2001 From: Reed Riddle Date: Tue, 19 May 2026 16:37:41 -0700 Subject: [PATCH 03/16] pylint compliant --- daemons/generic/filterwheel | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/daemons/generic/filterwheel b/daemons/generic/filterwheel index 7b541b4..48ade48 100644 --- a/daemons/generic/filterwheel +++ b/daemons/generic/filterwheel @@ -2,12 +2,10 @@ '''Module for the Filter Wheel Daemon''' import argparse import sys -import threading from typing import Dict, Any, Optional #pylint: disable = W0611 from hispec.daemon import HispecDaemon #pylint: disable = E0401,E0611 from hispec.driver.thorlabs.fw102c import FilterWheelController #pylint: disable = E0401,E0611 -from libby import KeywordRegistry #pylint: disable = E0611 class Filterwheel(HispecDaemon): #pylint: disable = W0223 '''Daemon for controlling the Filter Wheel via Thorlabs FW102C controller''' @@ -251,7 +249,7 @@ def main(): except KeyboardInterrupt: print("\nDaemon interrupted by user") sys.exit(0) - except Exception as e: + except Exception as e: #pylint: disable=W0718 print(f"Error running daemon: {e}", file=sys.stderr) sys.exit(1) From bda9b68f1ce2e10d0f49fe2df32e81cc5ba3b361 Mon Sep 17 00:00:00 2001 From: elijahab Date: Wed, 20 May 2026 18:08:55 -0700 Subject: [PATCH 04/16] Fixed Keyword wrapper, units implementation and validator. for limits --- daemons/generic/filterwheel | 33 +++++++++++++++++++-------------- src/hispec/driver/thorlabs | 2 +- 2 files changed, 20 insertions(+), 15 deletions(-) mode change 100644 => 100755 daemons/generic/filterwheel diff --git a/daemons/generic/filterwheel b/daemons/generic/filterwheel old mode 100644 new mode 100755 index 48ade48..dad452b --- a/daemons/generic/filterwheel +++ b/daemons/generic/filterwheel @@ -21,6 +21,7 @@ class Filterwheel(HispecDaemon): #pylint: disable = W0223 self.port = None self.dev = FilterWheelController(log=True) self.daemon_desc = self.get_config("peer_id") + self.units = self.get_config("units") self._soft_min = None # No limit by default self._soft_max = None # No limit by default @@ -65,23 +66,28 @@ class Filterwheel(HispecDaemon): #pylint: disable = W0223 try: self.dev.initialize() self.keyword_registry.bool("isconnected", - getter=self.keyword_wrapper(self.dev.isconnected), + getter=self.keyword_wrapper(self.dev.is_connected), description="Check if daemon can talk to the FW controller.") self.keyword_registry.int("position", getter=self.keyword_wrapper(self.get_pos, key="position"), - setter=lambda v: self.keyword_wrapper(self.set_pos, int(v)), + setter=self.keyword_wrapper(self.set_pos, key="position"), + validator=self._check_soft_limits, + units=self.units, description="Set and get current position of FilterWheel.") - self.keyword_registry.str("namedposition", + self.keyword_registry.string("namedposition", getter=self.keyword_wrapper(self.cur_named_position, key="named_pos"), - setter=lambda v: self.keyword_wrapper(self.goto_named_pos, str(v)), + setter=self.keyword_wrapper(self.goto_named_pos, key="named_pos"), + validator=self._check_soft_limits, description="Set and get named position of FilterWheel.") self.keyword_registry.int("softmin", getter=lambda: self._soft_min, setter=lambda v: setattr(self, "_soft_min", int(v)), + units=self.units, description="Software lower limit for filter wheel position.") self.keyword_registry.int("softmax", getter=lambda: self._soft_max, setter=lambda v: setattr(self, "_soft_max", int(v)), + units=self.units, description="Software upper limit for filter wheel position.") except Exception as e: # pylint: disable=W0718 @@ -211,17 +217,16 @@ class Filterwheel(HispecDaemon): #pylint: disable = W0223 @staticmethod def keyword_wrapper(func, key=None): - """Wrap a daemon method for use as a keyword getter/setter. - - Returns the value at `key` on success, or raises RuntimeError on failure. - If no `key` is given, returns the full result dict (minus 'ok'). - """ - def wrapper(self, *args, **kwargs): - result = func(*args, **kwargs) + """Wrap a daemon method for use as a keyword getter/setter.""" + def wrapper(*args, **kwargs): + try: + result = func(*args, **kwargs) + print(f"DEBUG keyword_wrapper [{func.__name__}]: result={result}") + except Exception as e: + print(f"DEBUG keyword_wrapper [{func.__name__}]: exception={e}") + raise if not result.get("ok"): - error = result.get("error", f"Unknown error in {func.__name__}") - self.logger.error("keyword_wrapper [%s]: %s", func.__name__, error) - raise RuntimeError(error) + raise RuntimeError(result.get("error", f"Unknown error in {func.__name__}")) if key: return result[key] return {k: v for k, v in result.items() if k != "ok"} diff --git a/src/hispec/driver/thorlabs b/src/hispec/driver/thorlabs index 0c39034..89cee42 160000 --- a/src/hispec/driver/thorlabs +++ b/src/hispec/driver/thorlabs @@ -1 +1 @@ -Subproject commit 0c390342f07fe06f0f92469a6a082af28e4a82e1 +Subproject commit 89cee42fb943e88fc17c192e3197396735a904e5 From b79e3f4c98af5ed3bdd30491c7bc62d26048b099 Mon Sep 17 00:00:00 2001 From: elijahab Date: Fri, 22 May 2026 15:46:32 -0700 Subject: [PATCH 05/16] debugging prints and investigation of keyword registry --- config/hsfei/hsfei_filterwheel.yaml | 2 +- daemons/generic/.filterwheel.swp | Bin 0 -> 16384 bytes daemons/generic/filterwheel | 19 +++++++++++++++++-- 3 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 daemons/generic/.filterwheel.swp diff --git a/config/hsfei/hsfei_filterwheel.yaml b/config/hsfei/hsfei_filterwheel.yaml index f072de0..6ffbbe9 100644 --- a/config/hsfei/hsfei_filterwheel.yaml +++ b/config/hsfei/hsfei_filterwheel.yaml @@ -8,7 +8,7 @@ # Usage: # daemons/hsfei/pi-daemon -c config/hsfei/hsfei_atcpickoff.yaml -peer_id: hsfei_atcfwheel +peer_id: atcfwheel group_id: hsfei hardware: diff --git a/daemons/generic/.filterwheel.swp b/daemons/generic/.filterwheel.swp new file mode 100644 index 0000000000000000000000000000000000000000..dd72cbfbd3e742cf5221468cefec488128a77de9 GIT binary patch literal 16384 zcmeHN&5t8T6|ayl60)$HL;`_8wQV7LB;&DXqYzm#A=#OQ5$q;pH_Rbfwc74#yRz-> zrmA{o&9K0M1D6m1e*g!hNJt0?Bn~+MLR|KMI0T^xamoo1IKhPrAHS-u{;=2USqM20 zRZBmAbk%$H>i1rKz3y@Ui~ASp6TJb!b()aB|N8Vx-#l~ku0Q<#VKS2;JJk12xMo~t zY;r;+6o1ty+-5OtW56+Rrwp7T z=XM5tUAyI-rH?%P{GAHAdX53dfMdWh;23ZWI0hU8jseHOyMO_e-9x^He!R!_<+A<0 zYvuc2_Ik$Nzp_&PPg{P~-v9ne`Q7&aDf>-q17$nqZTIaMa11yG90QI4$ADwNG2j?* z3^)cH1C9a5!2h0sASC2bsQrpQh~xQxdH#Rv1BCn%_%ZM*zme;3xMG@^xSfcpZt2?*a@s1^gC?lnginybew^ zugAAZa?lM|$mB%uOz|`sd6$`@K@!p_zLQ|iLYk$L>oS@~^n!U`;y}MI(et**2Bq{c zhykVH?FFUg=VmM3X$Qi?4bN57yj{n^vv;6&y|Z|tI$mYTW_p&KbY2HH4QI%3PWAwrSour8pX&f_g(#Sb6Hu0)dk4qmK6E;yST%l}z-^OVgcRVgD zsg)qi5U1$d5Q@Kd!v6o)+hV()ZOV*_x6CxG(?_Elj z?nXhVcH1lv;ZY!%ccW4R|0bwrdLmVz*bD7Sql2nKdcJTccz=#Xxn$vZ7*z~zUyuAXEL5&ujq7HI3iXyC!wp)H`2<5rMtK}B%;H-`RkNS# zXflDY6oqip6_GQd`_;Tez$I%?Vz}tohCopdvrfk|cfGCU-r2BUt&4Glpl!BM*U-VD zSABb@G0@GxYzh_Qa!HR7wOun2V4|;mDW&_*(jW{4$fEFMmLjzD`=C7HENtJZV>yaW zkf#w;784CMJDkRp-Qd1pqZpO5R2I7>W3e`;!APr~!dM;rHAnU6G}2~8E_u_77;0h? z?Rci3VSuUK2*q_WcDh)VGK5*zG@B^rx}KV3SUiPV6*L{)B*-)0I;J7oZnrP%;V)^P zl`Es(TUH~5V}{hrW7)o4RUl`4si(;_@y#$Xgm)-imAJIX0kMv-#hpTDFdRg1GY=HHmDX2?wy`|pj`M!3G?>EM3xm;0P zPl>4lLZa+R9ftW%n8B{up)|k#un}D$_Cw! z(#wbj>j^zT(VE#t>0N=@SvBkqnn{l^D`W%b1ZYSlF@JV{HTMlCYUkCor$6V;_pC5tYl&zNIg5UhD z@A~|YpXPc6=lmhi0`3LgK>4qLZv$6=PXkW^p8{Hdmj58&F2{glz%k$$a11yG90QI4 z$ADwNG2j^Z|6)L=7v=#^ka<9I%{PzTe4ma+wC(%4!1vq8j+-oju8X5-k6q`gGpG|E z#j}H`NMKTtCpr^tb528g%x)k Date: Tue, 26 May 2026 14:56:32 -0700 Subject: [PATCH 06/16] tested and working filterwheel daemon --- config/hsfei/hsfei_filterwheel.yaml | 4 +-- daemons/generic/filterwheel | 43 +++++++++++++++++------------ 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/config/hsfei/hsfei_filterwheel.yaml b/config/hsfei/hsfei_filterwheel.yaml index 6ffbbe9..eb34657 100644 --- a/config/hsfei/hsfei_filterwheel.yaml +++ b/config/hsfei/hsfei_filterwheel.yaml @@ -8,7 +8,7 @@ # Usage: # daemons/hsfei/pi-daemon -c config/hsfei/hsfei_atcpickoff.yaml -peer_id: atcfwheel +peer_id: hsfei_atcfwheel group_id: hsfei hardware: @@ -28,4 +28,4 @@ named_positions: logging: level: INFO - # file: /var/log/hispec/atcfwheel.log + file: /tmp/atcfwheel.log diff --git a/daemons/generic/filterwheel b/daemons/generic/filterwheel index 0c82613..31afa5b 100755 --- a/daemons/generic/filterwheel +++ b/daemons/generic/filterwheel @@ -22,6 +22,8 @@ class Filterwheel(HispecDaemon): #pylint: disable = W0223 self.dev = FilterWheelController(log=True) self._soft_min = None # No limit by default self._soft_max = None # No limit by default + self._hard_min = 1 # Default hard limits for FW102C + self._hard_max = None # Default hard limits for FW102C self.daemon_desc = "Filter Wheel Daemon" self.units = "position units" # Set appropriate units for your filter wheel @@ -66,22 +68,26 @@ class Filterwheel(HispecDaemon): #pylint: disable = W0223 return {"ok": False, "error": "Not connected to hardware"} try: - print("DEBUG: Initializing FilterWheel with config:", self._config) self.dev.initialize() - print("DEBUGDEBUGDEBUGDEBUGDEBUG: initializing devices with config:", self._config) + limits = self.dev.get_limits().get("1") + self._hard_min = limits[0] + self._hard_max = limits[1] self.keyword_registry.bool("isconnected", - getter=self.keyword_wrapper(self.dev.is_connected), + getter=self.dev.is_connected, description="Check if daemon can talk to the FW controller.") + self.keyword_registry.string("error", + getter=lambda: self.state['error'], + description="Get the current error message.") self.keyword_registry.int("position", getter=self.keyword_wrapper(self.get_pos, key="position"), setter=self.keyword_wrapper(self.set_pos, key="position"), - validator=self._check_soft_limits, + #validator=self._check_soft_limits, units=self.units, description="Set and get current position of FilterWheel.") self.keyword_registry.string("namedposition", getter=self.keyword_wrapper(self.cur_named_position, key="named_pos"), setter=self.keyword_wrapper(self.goto_named_pos, key="named_pos"), - validator=self._check_soft_limits, + #validator=self._check_soft_limits, description="Set and get named position of FilterWheel.") self.keyword_registry.int("softmin", getter=lambda: self._soft_min, @@ -93,14 +99,18 @@ class Filterwheel(HispecDaemon): #pylint: disable = W0223 setter=lambda v: setattr(self, "_soft_max", int(v)), units=self.units, description="Software upper limit for filter wheel position.") - self.keyword_registry.int("printcheck", - getter=self.print_check, - description="Debug keyword to check registration and execution.") - - print("DEBUG: Registered keywords") + self.keyword_registry.int("hardmin", + getter=lambda: self._hard_min, + units=self.units, + description="Hardware lower limit for filter wheel position.") + self.keyword_registry.int("hardmax", + getter=lambda: self._hard_max, + units=self.units, + description="Hardware upper limit for filter wheel position.") except Exception as e: # pylint: disable=W0718 self.logger.error("Error: %s",e) + self.state['error'] = str(e) return {"ok":False , "error": str(e)} return {"ok": True} @@ -152,6 +162,7 @@ class Filterwheel(HispecDaemon): #pylint: disable = W0223 self.logger.info("Disconnected from %s", self.daemon_desc) except Exception as e: # pylint: disable=W0718 self.logger.error("Error: %s",e) + self.state['error'] = str(e) return {"ok": False, "error": str(e)} return {"ok": True, "message": "Disconnected from hardware"} @@ -170,6 +181,7 @@ class Filterwheel(HispecDaemon): #pylint: disable = W0223 self.logger.debug("status: %s",status) except Exception as e: # pylint: disable=W0718 self.logger.error("Error: %s",e) + self.state['error'] = str(e) return {"ok": False, "error": str(e)} return {"ok": True, "status": status} @@ -183,13 +195,9 @@ class Filterwheel(HispecDaemon): #pylint: disable = W0223 self.logger.debug("get_pos: %s",position) except Exception as e: # pylint: disable=W0718 self.logger.error("Error: %s",e) + self.state['error'] = str(e) return {"ok": False, "error": str(e)} return {"ok":True, "position": int(position)} - - def print_check(self): - """"Debug keyword registration""" - print("DEBUG: Registered and executed") - return 1 def set_pos(self, pos): '''sets current position''' @@ -203,6 +211,7 @@ class Filterwheel(HispecDaemon): #pylint: disable = W0223 self.logger.debug("set_pos: %d",pos) except Exception as e: # pylint: disable=W0718 self.logger.error("Error: %s",e) + self.state['error'] = str(e) return {"ok": False, "error": str(e)} return {"ok": True, "position": int(pos)} @@ -218,6 +227,7 @@ class Filterwheel(HispecDaemon): #pylint: disable = W0223 self.logger.debug("goto_named_pos: %s -> %s",name,goal) except Exception as e: # pylint: disable=W0718 self.logger.error("Error: %s",e) + self.state['error'] = str(e) return {"ok": False, "error": str(e)} return {"ok": True, "named_pos": name, "position": goal} @@ -235,7 +245,6 @@ class Filterwheel(HispecDaemon): #pylint: disable = W0223 def wrapper(*args, **kwargs): try: result = func(*args, **kwargs) - print(f"DEBUG keyword_wrapper [{func.__name__}]: result={result}") except Exception as e: print(f"DEBUG keyword_wrapper [{func.__name__}]: exception={e}") raise @@ -263,7 +272,7 @@ def main(): sys.exit(2) try: - print(f"DDDDDDDDDDDDDDDDD Starting FilterWheel Daemon with config: {args.config} and daemon ID: {args}") + print(f"Starting FilterWheel Daemon with config: {args.config} and daemon ID: {args}") daemon = Filterwheel.from_config_file(args.config, daemon_id=args.daemon_id) daemon.serve() except KeyboardInterrupt: From e9feb6070a1db22f1e12b67e1fd76f9a58691ca5 Mon Sep 17 00:00:00 2001 From: elijahab Date: Tue, 26 May 2026 16:14:36 -0700 Subject: [PATCH 07/16] fixed names of keywords --- daemons/generic/filterwheel | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/daemons/generic/filterwheel b/daemons/generic/filterwheel index 31afa5b..8862b05 100755 --- a/daemons/generic/filterwheel +++ b/daemons/generic/filterwheel @@ -78,13 +78,13 @@ class Filterwheel(HispecDaemon): #pylint: disable = W0223 self.keyword_registry.string("error", getter=lambda: self.state['error'], description="Get the current error message.") - self.keyword_registry.int("position", + self.keyword_registry.int("positionvalue", getter=self.keyword_wrapper(self.get_pos, key="position"), setter=self.keyword_wrapper(self.set_pos, key="position"), #validator=self._check_soft_limits, units=self.units, description="Set and get current position of FilterWheel.") - self.keyword_registry.string("namedposition", + self.keyword_registry.string("positionnamed", getter=self.keyword_wrapper(self.cur_named_position, key="named_pos"), setter=self.keyword_wrapper(self.goto_named_pos, key="named_pos"), #validator=self._check_soft_limits, From 90ae441b70bb302f4bb277f2828ca10dbacd8ae3 Mon Sep 17 00:00:00 2001 From: elijahab Date: Tue, 26 May 2026 16:39:13 -0700 Subject: [PATCH 08/16] connect and disconnect implemented --- daemons/generic/filterwheel | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/daemons/generic/filterwheel b/daemons/generic/filterwheel index 8862b05..9724d70 100755 --- a/daemons/generic/filterwheel +++ b/daemons/generic/filterwheel @@ -72,6 +72,8 @@ class Filterwheel(HispecDaemon): #pylint: disable = W0223 limits = self.dev.get_limits().get("1") self._hard_min = limits[0] self._hard_max = limits[1] + self._soft_max = self._hard_max # Default soft max to hard max + self._soft_min = self._hard_min # Default soft min to hard min self.keyword_registry.bool("isconnected", getter=self.dev.is_connected, description="Check if daemon can talk to the FW controller.") @@ -107,6 +109,12 @@ class Filterwheel(HispecDaemon): #pylint: disable = W0223 getter=lambda: self._hard_max, units=self.units, description="Hardware upper limit for filter wheel position.") + self.keyword_registry.trigger("connect", + action=self.keyword_wrapper(self.connect), + description="Connect to the filter wheel hardware.") + self.keyword_registry.trigger("disconnect", + action=self.keyword_wrapper(self.disconnect), + description="Disconnect from the filter wheel hardware.") except Exception as e: # pylint: disable=W0718 self.logger.error("Error: %s",e) From 46d0fc03c85d71d880872eac3375c085fca1a0b6 Mon Sep 17 00:00:00 2001 From: elijahab Date: Tue, 26 May 2026 17:45:31 -0700 Subject: [PATCH 09/16] Fixed check limits --- config/hsfei/hsfei_filterwheel.yaml | 6 ++++++ daemons/generic/filterwheel | 23 +++++++++++------------ 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/config/hsfei/hsfei_filterwheel.yaml b/config/hsfei/hsfei_filterwheel.yaml index eb34657..893282b 100644 --- a/config/hsfei/hsfei_filterwheel.yaml +++ b/config/hsfei/hsfei_filterwheel.yaml @@ -18,6 +18,12 @@ hardware: timeout_s: 30.0 retry_count: 3 +limits: + soft_min: 1 + soft_max: 6 + hard_min: 1 + hard_max: 6 + named_positions: clear: 1 nd1: 2 diff --git a/daemons/generic/filterwheel b/daemons/generic/filterwheel index 9724d70..d69c0f4 100755 --- a/daemons/generic/filterwheel +++ b/daemons/generic/filterwheel @@ -20,10 +20,10 @@ class Filterwheel(HispecDaemon): #pylint: disable = W0223 self.host = None self.port = None self.dev = FilterWheelController(log=True) - self._soft_min = None # No limit by default - self._soft_max = None # No limit by default - self._hard_min = 1 # Default hard limits for FW102C - self._hard_max = None # Default hard limits for FW102C + self._soft_min = None + self._soft_max = None + self._hard_min = None + self._hard_max = None self.daemon_desc = "Filter Wheel Daemon" self.units = "position units" # Set appropriate units for your filter wheel @@ -39,6 +39,10 @@ class Filterwheel(HispecDaemon): #pylint: disable = W0223 self.port = self.get_config("hardware.tcp_port") self.daemon_desc = self.get_config("peer_id") self.units = self.get_config("units") + self._soft_min = self.get_config("limits.soft_min") + self._soft_max = self.get_config("limits.soft_max") + self._hard_min = self.get_config("limits.hard_min") + self._hard_max = self.get_config("limits.hard_max") # Initialize hardware connection if not(self.host and self.port): @@ -69,11 +73,6 @@ class Filterwheel(HispecDaemon): #pylint: disable = W0223 try: self.dev.initialize() - limits = self.dev.get_limits().get("1") - self._hard_min = limits[0] - self._hard_max = limits[1] - self._soft_max = self._hard_max # Default soft max to hard max - self._soft_min = self._hard_min # Default soft min to hard min self.keyword_registry.bool("isconnected", getter=self.dev.is_connected, description="Check if daemon can talk to the FW controller.") @@ -83,13 +82,13 @@ class Filterwheel(HispecDaemon): #pylint: disable = W0223 self.keyword_registry.int("positionvalue", getter=self.keyword_wrapper(self.get_pos, key="position"), setter=self.keyword_wrapper(self.set_pos, key="position"), - #validator=self._check_soft_limits, + validator=self._check_soft_limits, units=self.units, description="Set and get current position of FilterWheel.") self.keyword_registry.string("positionnamed", getter=self.keyword_wrapper(self.cur_named_position, key="named_pos"), setter=self.keyword_wrapper(self.goto_named_pos, key="named_pos"), - #validator=self._check_soft_limits, + validator=self._check_soft_limits, description="Set and get named position of FilterWheel.") self.keyword_registry.int("softmin", getter=lambda: self._soft_min, @@ -245,7 +244,7 @@ class Filterwheel(HispecDaemon): #pylint: disable = W0223 raise ValueError(f"Position {pos} below soft min {self._soft_min}") if self._soft_max is not None and pos > self._soft_max: raise ValueError(f"Position {pos} above soft max {self._soft_max}") - return True + return None @staticmethod def keyword_wrapper(func, key=None): From 80b1d00839f92c44d5f99e15d6543da70dd8a8ee Mon Sep 17 00:00:00 2001 From: elijahab Date: Tue, 26 May 2026 17:45:48 -0700 Subject: [PATCH 10/16] fixed check limits for named positions --- daemons/generic/filterwheel | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/daemons/generic/filterwheel b/daemons/generic/filterwheel index d69c0f4..87186f3 100755 --- a/daemons/generic/filterwheel +++ b/daemons/generic/filterwheel @@ -24,6 +24,7 @@ class Filterwheel(HispecDaemon): #pylint: disable = W0223 self._soft_max = None self._hard_min = None self._hard_max = None + self.named_positions = None self.daemon_desc = "Filter Wheel Daemon" self.units = "position units" # Set appropriate units for your filter wheel @@ -43,6 +44,7 @@ class Filterwheel(HispecDaemon): #pylint: disable = W0223 self._soft_max = self.get_config("limits.soft_max") self._hard_min = self.get_config("limits.hard_min") self._hard_max = self.get_config("limits.hard_max") + self.named_positions = self.get_config("named_positions") # Initialize hardware connection if not(self.host and self.port): @@ -88,7 +90,7 @@ class Filterwheel(HispecDaemon): #pylint: disable = W0223 self.keyword_registry.string("positionnamed", getter=self.keyword_wrapper(self.cur_named_position, key="named_pos"), setter=self.keyword_wrapper(self.goto_named_pos, key="named_pos"), - validator=self._check_soft_limits, + validator=self._check_named, description="Set and get named position of FilterWheel.") self.keyword_registry.int("softmin", getter=lambda: self._soft_min, @@ -246,6 +248,14 @@ class Filterwheel(HispecDaemon): #pylint: disable = W0223 raise ValueError(f"Position {pos} above soft max {self._soft_max}") return None + def _check_named(self, name: str) -> Optional[str]: + if not name: + return "value must be a non-empty string" + if name not in self.named_positions: + return (f"unknown named position '{name}'; " + f"available: {list(self.named_positions)}") + return None + @staticmethod def keyword_wrapper(func, key=None): """Wrap a daemon method for use as a keyword getter/setter.""" From 111720cce62b1b8e1abd5eb51b2f63496737055e Mon Sep 17 00:00:00 2001 From: elijahab Date: Tue, 26 May 2026 19:03:38 -0700 Subject: [PATCH 11/16] formatted to fit current requirements for connection --- daemons/generic/filterwheel | 42 +++++++++++++------------------------ 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/daemons/generic/filterwheel b/daemons/generic/filterwheel index 87186f3..7a4a428 100755 --- a/daemons/generic/filterwheel +++ b/daemons/generic/filterwheel @@ -52,7 +52,7 @@ class Filterwheel(HispecDaemon): #pylint: disable = W0223 self.state['error'] = 'No IP address or port specified' return try: - connection = self.connect() + connection = self.connect(True) if not connection.get("ok"): raise ConnectionError(connection.get("Error")) self.state['connected'] = True @@ -77,6 +77,7 @@ class Filterwheel(HispecDaemon): #pylint: disable = W0223 self.dev.initialize() self.keyword_registry.bool("isconnected", getter=self.dev.is_connected, + setter=self.keyword_wrapper(self.connect, key="isconnected"), description="Check if daemon can talk to the FW controller.") self.keyword_registry.string("error", getter=lambda: self.state['error'], @@ -110,12 +111,6 @@ class Filterwheel(HispecDaemon): #pylint: disable = W0223 getter=lambda: self._hard_max, units=self.units, description="Hardware upper limit for filter wheel position.") - self.keyword_registry.trigger("connect", - action=self.keyword_wrapper(self.connect), - description="Connect to the filter wheel hardware.") - self.keyword_registry.trigger("disconnect", - action=self.keyword_wrapper(self.disconnect), - description="Disconnect from the filter wheel hardware.") except Exception as e: # pylint: disable=W0718 self.logger.error("Error: %s",e) @@ -142,7 +137,7 @@ class Filterwheel(HispecDaemon): #pylint: disable = W0223 def on_stop(self, libby) -> None: #pylint: disable=W0222 '''Stops the daemon and disconnects from hardware device''' try: - self.disconnect() + self.connect(False) self.logger.info("Disconnected %s", self.daemon_desc) libby.publish(self.peer_id, {"Daemon Shutdown": "Success"}) except Exception as e: # pylint: disable=W0718 @@ -150,30 +145,21 @@ class Filterwheel(HispecDaemon): #pylint: disable = W0223 self.logger.error("Disconnect %s:: Failed ", self.daemon_desc) - def connect(self): + def connect(self, connect): """handles connection""" try: - self.dev.connect(host = self.host, port = self.port) - if not self.dev.is_connected(): - raise ConnectionError("Failed to connect to device") - self.logger.info("Connected %s", self.daemon_desc) + if connect: + self.dev.connect(host = self.host, port = self.port) + else: + self.dev.disconnect() + result = self.dev.is_connected() + if result != connect: + raise ConnectionError("Failed to Handle Connection Request") + self.logger.info("isconnected: %s", result) except Exception as e: # pylint: disable=W0718 - self.logger.error("Failed to Connect to Hardware: %s",e) + self.logger.error("Failed to execute: %s",e) return {"ok": False, "error": str(e)} - return {"ok": True, "message": "Connected to hardware"} - - def disconnect(self): - """handles disconnection""" - try: - self.dev.disconnect() - if self.dev.is_connected(): - raise ConnectionAbortedError("Failed to disconnect to device") - self.logger.info("Disconnected from %s", self.daemon_desc) - except Exception as e: # pylint: disable=W0718 - self.logger.error("Error: %s",e) - self.state['error'] = str(e) - return {"ok": False, "error": str(e)} - return {"ok": True, "message": "Disconnected from hardware"} + return {"ok": True, "isconnected": result} def status(self): """handles status""" From 95c0d3634081fd6dbcdf2c80355cf0d2eaf14c98 Mon Sep 17 00:00:00 2001 From: elijahab Date: Wed, 27 May 2026 10:30:39 -0700 Subject: [PATCH 12/16] rid of .swp file and fixed some info sections of yaml file --- config/hsfei/hsfei_filterwheel.yaml | 4 ++-- daemons/generic/.filterwheel.swp | Bin 16384 -> 0 bytes 2 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 daemons/generic/.filterwheel.swp diff --git a/config/hsfei/hsfei_filterwheel.yaml b/config/hsfei/hsfei_filterwheel.yaml index 893282b..02b1dd0 100644 --- a/config/hsfei/hsfei_filterwheel.yaml +++ b/config/hsfei/hsfei_filterwheel.yaml @@ -3,10 +3,10 @@ # # Usage: # daemon = Atcfwheel.from_config_file("config/hsfei_atcfwheel.yaml") -# ATC Pickoff Dichroic Selector — C-663 stepper instance +# ATC FilterWheel — Thor labs fw 102c contoller instance # # Usage: -# daemons/hsfei/pi-daemon -c config/hsfei/hsfei_atcpickoff.yaml +# daemons/hsfei/pi-daemon -c config/hsfei/hsfei_atcfwheel.yaml peer_id: hsfei_atcfwheel group_id: hsfei diff --git a/daemons/generic/.filterwheel.swp b/daemons/generic/.filterwheel.swp deleted file mode 100644 index dd72cbfbd3e742cf5221468cefec488128a77de9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 zcmeHN&5t8T6|ayl60)$HL;`_8wQV7LB;&DXqYzm#A=#OQ5$q;pH_Rbfwc74#yRz-> zrmA{o&9K0M1D6m1e*g!hNJt0?Bn~+MLR|KMI0T^xamoo1IKhPrAHS-u{;=2USqM20 zRZBmAbk%$H>i1rKz3y@Ui~ASp6TJb!b()aB|N8Vx-#l~ku0Q<#VKS2;JJk12xMo~t zY;r;+6o1ty+-5OtW56+Rrwp7T z=XM5tUAyI-rH?%P{GAHAdX53dfMdWh;23ZWI0hU8jseHOyMO_e-9x^He!R!_<+A<0 zYvuc2_Ik$Nzp_&PPg{P~-v9ne`Q7&aDf>-q17$nqZTIaMa11yG90QI4$ADwNG2j?* z3^)cH1C9a5!2h0sASC2bsQrpQh~xQxdH#Rv1BCn%_%ZM*zme;3xMG@^xSfcpZt2?*a@s1^gC?lnginybew^ zugAAZa?lM|$mB%uOz|`sd6$`@K@!p_zLQ|iLYk$L>oS@~^n!U`;y}MI(et**2Bq{c zhykVH?FFUg=VmM3X$Qi?4bN57yj{n^vv;6&y|Z|tI$mYTW_p&KbY2HH4QI%3PWAwrSour8pX&f_g(#Sb6Hu0)dk4qmK6E;yST%l}z-^OVgcRVgD zsg)qi5U1$d5Q@Kd!v6o)+hV()ZOV*_x6CxG(?_Elj z?nXhVcH1lv;ZY!%ccW4R|0bwrdLmVz*bD7Sql2nKdcJTccz=#Xxn$vZ7*z~zUyuAXEL5&ujq7HI3iXyC!wp)H`2<5rMtK}B%;H-`RkNS# zXflDY6oqip6_GQd`_;Tez$I%?Vz}tohCopdvrfk|cfGCU-r2BUt&4Glpl!BM*U-VD zSABb@G0@GxYzh_Qa!HR7wOun2V4|;mDW&_*(jW{4$fEFMmLjzD`=C7HENtJZV>yaW zkf#w;784CMJDkRp-Qd1pqZpO5R2I7>W3e`;!APr~!dM;rHAnU6G}2~8E_u_77;0h? z?Rci3VSuUK2*q_WcDh)VGK5*zG@B^rx}KV3SUiPV6*L{)B*-)0I;J7oZnrP%;V)^P zl`Es(TUH~5V}{hrW7)o4RUl`4si(;_@y#$Xgm)-imAJIX0kMv-#hpTDFdRg1GY=HHmDX2?wy`|pj`M!3G?>EM3xm;0P zPl>4lLZa+R9ftW%n8B{up)|k#un}D$_Cw! z(#wbj>j^zT(VE#t>0N=@SvBkqnn{l^D`W%b1ZYSlF@JV{HTMlCYUkCor$6V;_pC5tYl&zNIg5UhD z@A~|YpXPc6=lmhi0`3LgK>4qLZv$6=PXkW^p8{Hdmj58&F2{glz%k$$a11yG90QI4 z$ADwNG2j^Z|6)L=7v=#^ka<9I%{PzTe4ma+wC(%4!1vq8j+-oju8X5-k6q`gGpG|E z#j}H`NMKTtCpr^tb528g%x)k Date: Wed, 27 May 2026 10:43:54 -0700 Subject: [PATCH 13/16] fixed #! --- daemons/generic/filterwheel | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daemons/generic/filterwheel b/daemons/generic/filterwheel index 7a4a428..ac39b0b 100755 --- a/daemons/generic/filterwheel +++ b/daemons/generic/filterwheel @@ -1,4 +1,4 @@ -#!/usr/bin/python3.12 +#!/usr/bin/env python3 '''Module for the Filter Wheel Daemon''' import argparse import sys From 3ed8c8122cd60693b59bba09cd6c84318f27c0d7 Mon Sep 17 00:00:00 2001 From: elijahab Date: Wed, 27 May 2026 15:52:43 -0700 Subject: [PATCH 14/16] rid of the libby.publish, unused --- daemons/generic/filterwheel | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/daemons/generic/filterwheel b/daemons/generic/filterwheel index ac39b0b..cbbb203 100755 --- a/daemons/generic/filterwheel +++ b/daemons/generic/filterwheel @@ -4,8 +4,8 @@ import argparse import sys from typing import Dict, Any, Optional #pylint: disable = W0611 -from hispec.daemon import HispecDaemon #pylint: disable = E0401,E0611 -from hispec.driver.thorlabs.fw102c import FilterWheelController #pylint: disable = E0401,E0611 +from hispec.daemon import HispecDaemon #pylint: disable = E0611 +from hispec.driver.thorlabs.fw102c import FilterWheelController #pylint: disable = E0611 class Filterwheel(HispecDaemon): #pylint: disable = W0223 '''Daemon for controlling the Filter Wheel via Thorlabs FW102C controller''' @@ -65,9 +65,6 @@ class Filterwheel(HispecDaemon): #pylint: disable = W0223 self.state['error'] = str(e) self.state['connected'] = False - # Publish initial status - libby.publish(self.peer_id, self.state) - def initialize(self): """handles initialization""" if not self.state['connected']: @@ -139,11 +136,9 @@ class Filterwheel(HispecDaemon): #pylint: disable = W0223 try: self.connect(False) self.logger.info("Disconnected %s", self.daemon_desc) - libby.publish(self.peer_id, {"Daemon Shutdown": "Success"}) except Exception as e: # pylint: disable=W0718 - libby.publish(self.peer_id, {"Daemon Startup": "Failed", "Error":f"{e}"}) self.logger.error("Disconnect %s:: Failed ", self.daemon_desc) - + self.logger.error("Error: %s",e) def connect(self, connect): """handles connection""" From 5b114463c54a2e070261dc6e0a7f21f18f841178 Mon Sep 17 00:00:00 2001 From: elijahab Date: Wed, 27 May 2026 16:00:02 -0700 Subject: [PATCH 15/16] yaml file fix for the named pos --- config/hsfei/hsfei_filterwheel.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/config/hsfei/hsfei_filterwheel.yaml b/config/hsfei/hsfei_filterwheel.yaml index 02b1dd0..804b711 100644 --- a/config/hsfei/hsfei_filterwheel.yaml +++ b/config/hsfei/hsfei_filterwheel.yaml @@ -25,12 +25,12 @@ limits: hard_max: 6 named_positions: - clear: 1 - nd1: 2 - nd2: 3 - nd3: 4 - nd4: 5 - nd5: 6 + empty: 1 + OD1: 2 + OD2: 3 + OD4: 4 + OD5: 5 + empty2: 6 logging: level: INFO From da398814a9d3cd2e830471d8ed317691a84f10e5 Mon Sep 17 00:00:00 2001 From: elijahab Date: Wed, 27 May 2026 16:08:18 -0700 Subject: [PATCH 16/16] yaml comment fix --- config/hsfei/hsfei_filterwheel.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/hsfei/hsfei_filterwheel.yaml b/config/hsfei/hsfei_filterwheel.yaml index 804b711..931ef7b 100644 --- a/config/hsfei/hsfei_filterwheel.yaml +++ b/config/hsfei/hsfei_filterwheel.yaml @@ -6,7 +6,7 @@ # ATC FilterWheel — Thor labs fw 102c contoller instance # # Usage: -# daemons/hsfei/pi-daemon -c config/hsfei/hsfei_atcfwheel.yaml +# daemons/generic/filterwheel -c config/hsfei/hsfei_filterwheel.yaml peer_id: hsfei_atcfwheel group_id: hsfei