Skip to content

Commit bbd7a5c

Browse files
SimonHeybrockclaude
andcommitted
Extend nexus_helpers with units extraction and code generation
Add tooling to help generate f144_log_streams configuration from NeXus files: - Add `units` field to StreamInfo dataclass, extracted from 'value' datasets - Add `suggest_internal_name()` to derive internal names from group paths - Add `filter_f144_streams()` to filter by writer module, topic, and patterns - Add `generate_f144_log_streams_code()` to generate Python dict code - Add CLI options: --f144, --topic, --exclude, --generate, --var-name Refactor bifrost configuration to use combined f144_log_streams dict: - Define `f144_log_streams` mapping internal name -> {source, units} - Derive `f144_attribute_registry` from `f144_log_streams` - Derive StreamLUT in `_make_bifrost_logs()` from `f144_log_streams` This keeps source names, internal names, and units co-located in one place. Example usage: python -m ess.livedata.nexus_helpers file.hdf --generate --topic bifrost_motion Original prompt: Can you extend the nexus_helpers.py script to also extract and list units? Or, going beyond that, it would be good if we could turn this into a better tool to generate code for attr registry and log_source_mapping. Is having the two in two parts convenient? 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent ef6cbe7 commit bbd7a5c

File tree

4 files changed

+511
-31
lines changed

4 files changed

+511
-31
lines changed

src/ess/livedata/config/instruments/bifrost/specs.py

Lines changed: 130 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -231,11 +231,137 @@ class QMapOutputs(WorkflowOutputsBase):
231231
'113_psd1',
232232
]
233233

234-
# Some example motions used for testing, probably not reflecting reality
234+
# Combined f144 log stream configuration.
235+
# Maps internal name -> {source: Kafka source name, units: unit string, topic: topic}
236+
# Generated using: python -m ess.livedata.nexus_helpers <file> --generate --topic <t>
237+
f144_log_streams: dict[str, dict[str, str]] = {
238+
# Motion streams (topic: bifrost_motion)
239+
'attenuator_1': {
240+
'source': 'BIFRO-AttChg:MC-Pne-01:ShtAuxBits07',
241+
'units': 'dimensionless',
242+
'topic': 'bifrost_motion',
243+
},
244+
'attenuator_2': {
245+
'source': 'BIFRO-AttChg:MC-Pne-02:ShtAuxBits07',
246+
'units': 'dimensionless',
247+
'topic': 'bifrost_motion',
248+
},
249+
'attenuator_3': {
250+
'source': 'BIFRO-AttChg:MC-Pne-03:ShtAuxBits07',
251+
'units': 'dimensionless',
252+
'topic': 'bifrost_motion',
253+
},
254+
'detector_rotation': {
255+
'source': 'BIFRO-DtCar:MC-RotZ-01:Mtr.RBV',
256+
'units': 'deg',
257+
'topic': 'bifrost_motion',
258+
},
259+
'get_lost_tube': {
260+
'source': 'BIFRO-InBm:MC-Pne-01:ShtAuxBits07',
261+
'units': 'dimensionless',
262+
'topic': 'bifrost_motion',
263+
},
264+
'goniometer_x': {
265+
'source': 'BIFRO-SpGon:MC-RotX-01:Mtr.RBV',
266+
'units': 'deg',
267+
'topic': 'bifrost_motion',
268+
},
269+
'goniometer_y': {
270+
'source': 'BIFRO-SpGon:MC-RotY-01:Mtr.RBV',
271+
'units': 'deg',
272+
'topic': 'bifrost_motion',
273+
},
274+
'sample_rotation': {
275+
'source': 'BIFRO-SpRot:MC-RotZ-01:Mtr.RBV',
276+
'units': 'deg',
277+
'topic': 'bifrost_motion',
278+
},
279+
'slit_bottom': {
280+
'source': 'BIFRO-SpSl1:MC-SlZm-01:PzMtr.RBV',
281+
'units': 'mm',
282+
'topic': 'bifrost_motion',
283+
},
284+
'slit_left': {
285+
'source': 'BIFRO-SpSl1:MC-SlYp-01:PzMtr.RBV',
286+
'units': 'mm',
287+
'topic': 'bifrost_motion',
288+
},
289+
'slit_right': {
290+
'source': 'BIFRO-SpSl1:MC-SlYm-01:PzMtr.RBV',
291+
'units': 'mm',
292+
'topic': 'bifrost_motion',
293+
},
294+
'slit_top': {
295+
'source': 'BIFRO-SpSl1:MC-SlZp-01:PzMtr.RBV',
296+
'units': 'mm',
297+
'topic': 'bifrost_motion',
298+
},
299+
'slit_position': {
300+
'source': 'BIFRO-SpSl1:MC-LinX-01:PzMtr-PosReadback',
301+
'units': 'mm',
302+
'topic': 'bifrost_motion',
303+
},
304+
# Sample environment streams (topic: bifrost_sample_env)
305+
'heater_1': {
306+
'source': 'YMIR-SEE:SE-LS336-004:HTR1',
307+
'units': 'W',
308+
'topic': 'bifrost_sample_env',
309+
},
310+
'heater_2': {
311+
'source': 'YMIR-SEE:SE-LS336-004:HTR2',
312+
'units': 'W',
313+
'topic': 'bifrost_sample_env',
314+
},
315+
'temperature_0': {
316+
'source': 'YMIR-SEE:SE-LS336-004:KRDG0',
317+
'units': 'K',
318+
'topic': 'bifrost_sample_env',
319+
},
320+
'temperature_1': {
321+
'source': 'YMIR-SEE:SE-LS336-004:KRDG1',
322+
'units': 'K',
323+
'topic': 'bifrost_sample_env',
324+
},
325+
'temperature_2': {
326+
'source': 'YMIR-SEE:SE-LS336-004:KRDG2',
327+
'units': 'K',
328+
'topic': 'bifrost_sample_env',
329+
},
330+
'temperature_3': {
331+
'source': 'YMIR-SEE:SE-LS336-004:KRDG3',
332+
'units': 'K',
333+
'topic': 'bifrost_sample_env',
334+
},
335+
'temperature_setpoint': {
336+
'source': 'YMIR-SEE:SE-LS336-004:SETP_S1',
337+
'units': 'K',
338+
'topic': 'bifrost_sample_env',
339+
},
340+
'sensor_0': {
341+
'source': 'YMIR-SEE:SE-LS336-004:SRDG0',
342+
'units': 'V',
343+
'topic': 'bifrost_sample_env',
344+
},
345+
'sensor_1': {
346+
'source': 'YMIR-SEE:SE-LS336-004:SRDG1',
347+
'units': 'V',
348+
'topic': 'bifrost_sample_env',
349+
},
350+
'sensor_2': {
351+
'source': 'YMIR-SEE:SE-LS336-004:SRDG2',
352+
'units': 'V',
353+
'topic': 'bifrost_sample_env',
354+
},
355+
'sensor_3': {
356+
'source': 'YMIR-SEE:SE-LS336-004:SRDG3',
357+
'units': 'V',
358+
'topic': 'bifrost_sample_env',
359+
},
360+
}
361+
362+
# Derived from f144_log_streams for use by the Instrument
235363
f144_attribute_registry = {
236-
'detector_rotation': {'units': 'deg'},
237-
'sample_rotation': {'units': 'deg'},
238-
'sample_temperature': {'units': 'K'},
364+
name: {'units': info['units']} for name, info in f144_log_streams.items()
239365
}
240366

241367
# Create instrument

src/ess/livedata/config/instruments/bifrost/streams.py

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from ess.livedata.kafka import InputStreamKey, StreamLUT, StreamMapping
1313

1414
from .._ess import make_common_stream_mapping_inputs, make_dev_stream_mapping
15-
from .specs import f144_attribute_registry, monitors
15+
from .specs import f144_log_streams, monitors
1616

1717

1818
def _bifrost_generator() -> Generator[tuple[str, tuple[int, int]]]:
@@ -61,25 +61,16 @@ def _make_bifrost_detectors() -> StreamLUT:
6161
}
6262

6363

64-
# Mapping from Kafka source names to ESSlivedata-internal names for f144 log data.
65-
# Source names extracted from NeXus file stream info (writer_module='f144').
66-
_bifrost_log_source_mapping: dict[str, str] = {
67-
'BIFRO-DtCar:MC-RotZ-01:Mtr.RBV': 'detector_rotation',
68-
'BIFRO-SpRot:MC-RotZ-01:Mtr.RBV': 'sample_rotation',
69-
}
70-
71-
7264
def _make_bifrost_logs() -> StreamLUT:
7365
"""
7466
Bifrost log data mapping.
7567
76-
Maps Kafka source names (EPICS PV names) to ESSlivedata-internal stream names.
77-
Only sources registered in f144_attribute_registry are included.
68+
Derives StreamLUT from f144_log_streams, mapping Kafka source names
69+
(EPICS PV names) to ESSlivedata-internal stream names.
7870
"""
7971
return {
80-
InputStreamKey(topic='bifrost_motion', source_name=source_name): internal_name
81-
for source_name, internal_name in _bifrost_log_source_mapping.items()
82-
if internal_name in f144_attribute_registry
72+
InputStreamKey(topic=info['topic'], source_name=info['source']): internal_name
73+
for internal_name, info in f144_log_streams.items()
8374
}
8475

8576

@@ -88,7 +79,7 @@ def _make_bifrost_logs() -> StreamLUT:
8879
'bifrost',
8980
detector_names=list(detector_fakes),
9081
monitor_names=monitors,
91-
log_names=list(f144_attribute_registry.keys()),
82+
log_names=list(f144_log_streams.keys()),
9283
),
9384
StreamingEnv.PROD: StreamMapping(
9485
**make_common_stream_mapping_inputs(

0 commit comments

Comments
 (0)