Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 36 additions & 13 deletions discos_client/namespace.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,8 @@ def __get_value__(self) -> Any:
def __bind__(
self,
callback: Callable[[DISCOSNamespace], None],
predicate: Callable[[DISCOSNamespace], bool] = None
predicate: Callable[[DISCOSNamespace], bool] = None,
unwrap: bool = False
) -> None:
"""
Bind a callback to the DISCOSNamespace object,
Expand All @@ -203,13 +204,13 @@ def __bind__(
:param callback: A function that receives the updated object
when obj changes.
:param predicate: Optional predicate that the value must satisfy
:param unwrap: If True, evaluates the predicate and calls the callback
passing the internal primitive value instead of the
namespace.
"""
with self._observers_lock:
self._observers.setdefault(callback, set()).add(
predicate
if predicate is not None
else lambda _: True
)
pred = predicate if predicate is not None else lambda _: True
self._observers.setdefault(callback, set()).add((pred, unwrap))

def __unbind__(
self,
Expand All @@ -232,33 +233,44 @@ def __unbind__(
if callback not in self._observers:
return
if predicate is not None:
self._observers[callback].discard(predicate)
to_remove = [
p_tuple
for p_tuple in self._observers[callback]
if p_tuple[0] == predicate
]
for p_tuple in to_remove:
self._observers[callback].discard(p_tuple)
if predicate is None or not self._observers[callback]:
del self._observers[callback]

def __wait__(
self,
predicate: Callable[[DISCOSNamespace], bool] = None,
timeout: float | None = None
) -> DISCOSNamespace:
timeout: float | None = None,
unwrap: bool = False
) -> Any:
"""
Block until the DISCOSNamespace triggers a change notification.

:param predicate: Optional predicate that the value must satisfy.
:param timeout: Optional timeout in seconds.
:param unwrap: If True, the predicate operates on the internal value,
and the internal value itself is returned.
:return: The updated object, or the same object if timeout has expired.
"""
event = threading.Event()

def callback(_):
event.set()

self.bind(callback, predicate)
self.bind(callback, predicate, unwrap=unwrap)
try:
event.wait(timeout)
finally:
self.unbind(callback, predicate)
with self._lock:
if unwrap and self.__has_value__(self):
return self._value
return self

def __copy__(self) -> DISCOSNamespace:
Expand Down Expand Up @@ -854,9 +866,20 @@ def __notify__(self) -> None:
observers = list(self._observers.items())

with self._lock:
for cb, predicates in observers:
if any(predicate(self) for predicate in predicates):
cb(self)
for cb, conditions in observers:
should_call = False
value_to_pass = self

for predicate, unwrap in conditions:
value_to_test = self._value if unwrap \
and self.__has_value__(self) else self

if predicate(value_to_test):
should_call = True
value_to_pass = value_to_test
break
if should_call:
cb(value_to_pass)

def __getattr__(self, name: str):
"""
Expand Down
32 changes: 32 additions & 0 deletions discos_client/schemas/common/backends.json
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,38 @@
}
},
"anyOf": [
{
"type": "object",
"properties": {
"availableBackends": {
"type": "array",
"title": "Available Backends",
"description": "List of available backends.",
"items": {
"type": "string"
}
},
"currentBackend": {
"type": "string",
"title": "Current Backend",
"description": "Currently selected backend's name."
},
"currentSetup": {
"type": "string",
"title": "Current Setup",
"description": "Currently selected backend's setup code."
},
"timestamp": {
"$ref": "../definitions/timestamp.json"
}
},
"required": [
"availableBackends",
"currentBackend",
"currentSetup",
"timestamp"
]
},
{
"type": "object",
"patternProperties": {
Expand Down
13 changes: 11 additions & 2 deletions discos_client/schemas/common/receivers.json
Original file line number Diff line number Diff line change
Expand Up @@ -134,15 +134,23 @@
{
"type": "object",
"properties": {
"availableReceivers": {
"type": "array",
"title": "Available Receivers",
"description": "List of available receivers.",
"items": {
"type": "string"
}
},
"currentReceiver": {
"type": "string",
"title": "Current Receiver",
"description": "Currently selected receiver's name."
},
"currentSetup": {
"type": "string",
"title": "Current setup",
"description": "Current DISCOS setup code"
"title": "Current Setup",
"description": "Currently selected receiver's setup code."
},
"status": {
"$ref": "../definitions/status.json"
Expand All @@ -152,6 +160,7 @@
}
},
"required": [
"availableReceivers",
"currentReceiver",
"currentSetup",
"status",
Expand Down
14 changes: 13 additions & 1 deletion discos_client/schemas/common/scheduler.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,20 @@
"title": "Current Recorder",
"description": "Name of the currently used recorder."
},
"maxScanID": {
"type": "number",
"title": "Max Scan ID",
"description": "Identification number of the final scan of the current schedule."
},
"maxSubScanID": {
"type": "number",
"title": "Max SubScan ID",
"description": "Identification number of the final subscan of the current scan."
},
"projectCode": {
"type": "string",
"title": "Project Code",
"description": "Identification number of the current project user."
"description": "Identification code of the current project user."
},
"restFrequency": {
"type": "array",
Expand Down Expand Up @@ -66,6 +76,8 @@
"currentBackend",
"currentDevice",
"currentRecorder",
"maxScanID",
"maxSubScanID",
"projectCode",
"restFrequency",
"scanID",
Expand Down
7 changes: 5 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@

html_static_path = ['_static']

latex_engine = 'lualatex'


def setup(app):
app.connect("viewcode-find-source", on_viewcode_find_source)
Expand All @@ -94,8 +96,9 @@ def on_viewcode_find_source(app, modname):
analyzer.tags["MedicinaClient.command"] = analyzer.tags.get("DISCOSClient.__command__")
analyzer.tags["NotoClient.command"] = analyzer.tags.get("DISCOSClient.__command__")
if modname == "discos_client.namespace":
if "DISCOSNamespace.__get_value__" in analyzer.tags:
analyzer.tags["DISCOSNamespace.get_value"] = analyzer.tags.get("DISCOSNamespace.__get_value__")
for method in ["bind", "copy", "get_value", "unbind", "wait"]:
if f"DISCOSNamespace.__{method}__" in analyzer.tags:
analyzer.tags[f"DISCOSNamespace.{method}"] = analyzer.tags.get(f"DISCOSNamespace.__{method}__")
return analyzer.code, analyzer.tags


Expand Down
2 changes: 1 addition & 1 deletion tests/messages/common/backends.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"TotalPower":{"backendTime":{"iso8601":"2025-12-05T15:38:58.000Z","mjd":61014.65206018509,"omg_time":139842419380000000,"unix_time":1764949138.0},"busy":false,"channels":[{"attenuation":7.0,"bandWidth":2000.0,"bins":1,"id":0,"polarization":"LHCP","sampleRate":2.5e-05,"startFrequency":50.0,"systemTemperature":0.0},{"attenuation":7.0,"bandWidth":2000.0,"bins":1,"id":1,"polarization":"RHCP","sampleRate":2.5e-05,"startFrequency":50.0,"systemTemperature":0.0},{"attenuation":7.0,"bandWidth":2000.0,"bins":1,"id":2,"polarization":"LHCP","sampleRate":2.5e-05,"startFrequency":50.0,"systemTemperature":0.0},{"attenuation":7.0,"bandWidth":2000.0,"bins":1,"id":3,"polarization":"RHCP","sampleRate":2.5e-05,"startFrequency":50.0,"systemTemperature":0.0},{"attenuation":7.0,"bandWidth":2000.0,"bins":1,"id":4,"polarization":"LHCP","sampleRate":2.5e-05,"startFrequency":50.0,"systemTemperature":0.0},{"attenuation":7.0,"bandWidth":2000.0,"bins":1,"id":5,"polarization":"RHCP","sampleRate":2.5e-05,"startFrequency":50.0,"systemTemperature":0.0},{"attenuation":7.0,"bandWidth":2000.0,"bins":1,"id":6,"polarization":"LHCP","sampleRate":2.5e-05,"startFrequency":50.0,"systemTemperature":0.0},{"attenuation":7.0,"bandWidth":2000.0,"bins":1,"id":7,"polarization":"RHCP","sampleRate":2.5e-05,"startFrequency":50.0,"systemTemperature":0.0},{"attenuation":7.0,"bandWidth":2000.0,"bins":1,"id":8,"polarization":"LHCP","sampleRate":2.5e-05,"startFrequency":50.0,"systemTemperature":0.0},{"attenuation":7.0,"bandWidth":2000.0,"bins":1,"id":9,"polarization":"RHCP","sampleRate":2.5e-05,"startFrequency":50.0,"systemTemperature":0.0},{"attenuation":7.0,"bandWidth":2000.0,"bins":1,"id":10,"polarization":"LHCP","sampleRate":2.5e-05,"startFrequency":50.0,"systemTemperature":0.0},{"attenuation":7.0,"bandWidth":2000.0,"bins":1,"id":11,"polarization":"RHCP","sampleRate":2.5e-05,"startFrequency":50.0,"systemTemperature":0.0},{"attenuation":7.0,"bandWidth":2000.0,"bins":1,"id":12,"polarization":"LHCP","sampleRate":2.5e-05,"startFrequency":50.0,"systemTemperature":0.0},{"attenuation":7.0,"bandWidth":2000.0,"bins":1,"id":13,"polarization":"RHCP","sampleRate":2.5e-05,"startFrequency":50.0,"systemTemperature":0.0}],"commandLineError":false,"dataLineError":false,"integration":40,"sampling":false,"suspended":false,"timeSync":true,"timestamp":{"iso8601":"2025-12-05T15:38:58.627Z","mjd":61014.65206744196,"omg_time":139842419386276140,"unix_time":1764949138.627614}}}
{"TotalPower":{"backendTime":{"iso8601":"2026-05-04T08:22:29.000Z","mjd":61164.34894675948,"omg_time":139971757490000000,"unix_time":1777882949.0},"busy":false,"channels":[{"attenuation":7.0,"bandWidth":2000.0,"bins":1,"id":0,"polarization":"LHCP","sampleRate":2.5e-05,"startFrequency":50.0,"systemTemperature":-18.86252081925788},{"attenuation":7.0,"bandWidth":2000.0,"bins":1,"id":1,"polarization":"RHCP","sampleRate":2.5e-05,"startFrequency":50.0,"systemTemperature":-1.0},{"attenuation":7.0,"bandWidth":2000.0,"bins":1,"id":2,"polarization":"LHCP","sampleRate":2.5e-05,"startFrequency":50.0,"systemTemperature":-1.0},{"attenuation":7.0,"bandWidth":2000.0,"bins":1,"id":3,"polarization":"RHCP","sampleRate":2.5e-05,"startFrequency":50.0,"systemTemperature":-87.87325181869207},{"attenuation":7.0,"bandWidth":2000.0,"bins":1,"id":4,"polarization":"LHCP","sampleRate":2.5e-05,"startFrequency":50.0,"systemTemperature":-1.0},{"attenuation":7.0,"bandWidth":2000.0,"bins":1,"id":5,"polarization":"RHCP","sampleRate":2.5e-05,"startFrequency":50.0,"systemTemperature":-1.0},{"attenuation":7.0,"bandWidth":2000.0,"bins":1,"id":6,"polarization":"LHCP","sampleRate":2.5e-05,"startFrequency":50.0,"systemTemperature":-1.0},{"attenuation":7.0,"bandWidth":2000.0,"bins":1,"id":7,"polarization":"RHCP","sampleRate":2.5e-05,"startFrequency":50.0,"systemTemperature":-31.30752112150976},{"attenuation":7.0,"bandWidth":2000.0,"bins":1,"id":8,"polarization":"LHCP","sampleRate":2.5e-05,"startFrequency":50.0,"systemTemperature":-1.0},{"attenuation":7.0,"bandWidth":2000.0,"bins":1,"id":9,"polarization":"RHCP","sampleRate":2.5e-05,"startFrequency":50.0,"systemTemperature":41.756043814715845},{"attenuation":7.0,"bandWidth":2000.0,"bins":1,"id":10,"polarization":"LHCP","sampleRate":2.5e-05,"startFrequency":50.0,"systemTemperature":193.59580191228858},{"attenuation":7.0,"bandWidth":2000.0,"bins":1,"id":11,"polarization":"RHCP","sampleRate":2.5e-05,"startFrequency":50.0,"systemTemperature":238.08411375989303},{"attenuation":7.0,"bandWidth":2000.0,"bins":1,"id":12,"polarization":"LHCP","sampleRate":2.5e-05,"startFrequency":50.0,"systemTemperature":46.91594690101812},{"attenuation":7.0,"bandWidth":2000.0,"bins":1,"id":13,"polarization":"RHCP","sampleRate":2.5e-05,"startFrequency":50.0,"systemTemperature":-25.911116503337915}],"commandLineError":false,"dataLineError":false,"integration":40,"sampling":false,"suspended":false,"timeSync":true,"timestamp":{"iso8601":"2026-05-04T08:22:30.421Z","mjd":61164.34896320617,"omg_time":139971757504212560,"unix_time":1777882950.421256}},"availableBackends":["Sardara","TotalPower"],"currentBackend":"TotalPower","currentSetup":"KKG","timestamp":{"iso8601":"2026-05-04T08:22:30.072Z","mjd":61164.34895916656,"omg_time":139971757500724360,"unix_time":1777882950.072436}}
2 changes: 1 addition & 1 deletion tests/messages/common/receivers.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"currentReceiver": "SRTKBandMFReceiver", "currentSetup": "KKG", "status": "OK", "timestamp": {"iso8601": "2025-07-19T19:36:06.069Z", "mjd": 60875.81673690956, "omg_time": 139722465660699220, "unix_time": 1752953766.069922}, "SRTKBandMFReceiver": {"channels": [{"bandWidth": 1936.0, "id": 0, "localOscillator": 21964.0, "polarization": "LHCP", "startFrequency": 100.0}, {"bandWidth": 1936.0, "id": 1, "localOscillator": 21964.0, "polarization": "RHCP", "startFrequency": 100.0}, {"bandWidth": 1936.0, "id": 2, "localOscillator": 21964.0, "polarization": "LHCP", "startFrequency": 100.0}, {"bandWidth": 1936.0, "id": 3, "localOscillator": 21964.0, "polarization": "RHCP", "startFrequency": 100.0}, {"bandWidth": 1936.0, "id": 4, "localOscillator": 21964.0, "polarization": "LHCP", "startFrequency": 100.0}, {"bandWidth": 1936.0, "id": 5, "localOscillator": 21964.0, "polarization": "RHCP", "startFrequency": 100.0}, {"bandWidth": 1936.0, "id": 6, "localOscillator": 21964.0, "polarization": "LHCP", "startFrequency": 100.0}, {"bandWidth": 1936.0, "id": 7, "localOscillator": 21964.0, "polarization": "RHCP", "startFrequency": 100.0}, {"bandWidth": 1936.0, "id": 8, "localOscillator": 21964.0, "polarization": "LHCP", "startFrequency": 100.0}, {"bandWidth": 1936.0, "id": 9, "localOscillator": 21964.0, "polarization": "RHCP", "startFrequency": 100.0}, {"bandWidth": 1936.0, "id": 10, "localOscillator": 21964.0, "polarization": "LHCP", "startFrequency": 100.0}, {"bandWidth": 1936.0, "id": 11, "localOscillator": 21964.0, "polarization": "RHCP", "startFrequency": 100.0}, {"bandWidth": 1936.0, "id": 12, "localOscillator": 21964.0, "polarization": "LHCP", "startFrequency": 100.0}, {"bandWidth": 1936.0, "id": 13, "localOscillator": 21964.0, "polarization": "RHCP", "startFrequency": 100.0}], "cryoTemperatureCoolHead": 519.607622398508, "cryoTemperatureCoolHeadWindow": 519.607622398508, "cryoTemperatureLNA": 519.607622398508, "cryoTemperatureLNAWindow": 519.607622398508, "environmentTemperature": 112.79, "operativeMode": "SINGLEDISH", "status": "OK", "timestamp": {"iso8601": "2025-07-19T19:40:14.063Z", "mjd": 60875.81960721081, "omg_time": 139722468140638350, "unix_time": 1752954014.063835}, "vacuum": 1e-12}}
{"SRT5GHzReceiver":{"channels":[{"bandWidth":1400.0,"id":0,"localOscillator":4100.0,"polarization":"LHCP","startFrequency":100.0},{"bandWidth":1400.0,"id":1,"localOscillator":4100.0,"polarization":"RHCP","startFrequency":100.0}],"cryoTemperatureCoolHead":519.607622398508,"cryoTemperatureCoolHeadWindow":519.607622398508,"cryoTemperatureLNA":519.607622398508,"cryoTemperatureLNAWindow":519.607622398508,"environmentTemperature":112.79,"operativeMode":"NORMAL","status":"OK","timestamp":{"iso8601":"2026-05-04T08:21:10.263Z","mjd":61164.34803545149,"omg_time":139971756702637730,"unix_time":1777882870.263773},"vacuum":1e-12},"SRT7GHzReceiver":{"channels":[{"bandWidth":2000.0,"id":0,"localOscillator":5600.0,"polarization":"LHCP","startFrequency":100.0},{"bandWidth":2000.0,"id":1,"localOscillator":5600.0,"polarization":"RHCP","startFrequency":100.0}],"cryoTemperatureCoolHead":519.607622398508,"cryoTemperatureCoolHeadWindow":519.607622398508,"cryoTemperatureLNA":519.607622398508,"cryoTemperatureLNAWindow":519.607622398508,"environmentTemperature":112.79,"operativeMode":"","status":"OK","timestamp":{"iso8601":"2026-05-04T08:21:10.476Z","mjd":61164.3480379167,"omg_time":139971756704768790,"unix_time":1777882870.476879},"vacuum":1e-12},"SRTKBandMFReceiver":{"channels":[{"bandWidth":1936.0,"id":0,"localOscillator":21964.0,"polarization":"LHCP","startFrequency":100.0},{"bandWidth":1936.0,"id":1,"localOscillator":21964.0,"polarization":"RHCP","startFrequency":100.0},{"bandWidth":1936.0,"id":2,"localOscillator":21964.0,"polarization":"LHCP","startFrequency":100.0},{"bandWidth":1936.0,"id":3,"localOscillator":21964.0,"polarization":"RHCP","startFrequency":100.0},{"bandWidth":1936.0,"id":4,"localOscillator":21964.0,"polarization":"LHCP","startFrequency":100.0},{"bandWidth":1936.0,"id":5,"localOscillator":21964.0,"polarization":"RHCP","startFrequency":100.0},{"bandWidth":1936.0,"id":6,"localOscillator":21964.0,"polarization":"LHCP","startFrequency":100.0},{"bandWidth":1936.0,"id":7,"localOscillator":21964.0,"polarization":"RHCP","startFrequency":100.0},{"bandWidth":1936.0,"id":8,"localOscillator":21964.0,"polarization":"LHCP","startFrequency":100.0},{"bandWidth":1936.0,"id":9,"localOscillator":21964.0,"polarization":"RHCP","startFrequency":100.0},{"bandWidth":1936.0,"id":10,"localOscillator":21964.0,"polarization":"LHCP","startFrequency":100.0},{"bandWidth":1936.0,"id":11,"localOscillator":21964.0,"polarization":"RHCP","startFrequency":100.0},{"bandWidth":1936.0,"id":12,"localOscillator":21964.0,"polarization":"LHCP","startFrequency":100.0},{"bandWidth":1936.0,"id":13,"localOscillator":21964.0,"polarization":"RHCP","startFrequency":100.0}],"cryoTemperatureCoolHead":519.607622398508,"cryoTemperatureCoolHeadWindow":519.607622398508,"cryoTemperatureLNA":519.607622398508,"cryoTemperatureLNAWindow":519.607622398508,"environmentTemperature":112.79,"operativeMode":"SINGLEDISH","status":"OK","timestamp":{"iso8601":"2026-05-04T08:21:10.384Z","mjd":61164.348036851734,"omg_time":139971756703847980,"unix_time":1777882870.384798},"vacuum":1e-12},"availableReceivers":["KQWBandReceiver","SRT5GHzReceiver","SRT7GHzReceiver","SRTKBandMFReceiver","SRTLPBandReceiver"],"currentReceiver":"SRTKBandMFReceiver","currentSetup":"KKG","status":"OK","timestamp":{"iso8601":"2026-05-04T08:21:10.389Z","mjd":61164.34803690994,"omg_time":139971756703890910,"unix_time":1777882870.389091}}
2 changes: 1 addition & 1 deletion tests/messages/common/scheduler.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"currentBackend":"TotalPower","currentDevice":0,"currentRecorder":"FitsZilla","projectCode":"Maintenance","restFrequency":[0.0],"scanID":0,"scheduleName":"","status":"OK","subScanID":0,"timestamp":{"iso8601":"2025-11-21T11:45:01.497Z","mjd":61000.489600659814,"omg_time":139830183014975870,"unix_time":1763725501.497587},"tracking":true}
{"currentBackend":"TotalPower","currentDevice":0,"currentRecorder":"FitsZilla","maxScanID":0,"maxSubScanID":0,"projectCode":"Maintenance","restFrequency":[0.0],"scanID":0,"scheduleName":"","status":"OK","subScanID":0,"timestamp":{"iso8601":"2025-11-21T11:45:01.497Z","mjd":61000.489600659814,"omg_time":139830183014975870,"unix_time":1763725501.497587},"tracking":true}
Loading