STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
naming.py
Go to the documentation of this file.
1
"""Classify exported component names into family/field/unit/kind.
2
3
A flattened Elodin export names each file `<Entity>.<field>` where Entity is like
4
`PT_Cal.Ox_Upstream`, `TC1.CH2`, `ACT4.CH5`, `CONTROLLER.diagnostics`,
5
`BOARD.HB_52`. The last dotted segment is the field (`pressure_psi`, `raw_adc`,
6
`to_state`, ...). A handful of components are genuinely unnamed and arrive as a
7
bare numeric hash — those are grouped as "Other".
8
"""
9
10
from
__future__
import
annotations
11
12
import
re
13
from
dataclasses
import
dataclass, asdict
14
15
# Fields that are step/discrete (state enums, on/off, status) → step-draw, no
16
# min/max decimation, forward-fill when aligning to a shared time grid.
17
_DISCRETE_FIELDS = {
18
"to_state"
,
19
"from_state"
,
20
"engine_state"
,
21
"board_state"
,
22
"current_state"
,
23
"debug_mode"
,
24
"actuator_state"
,
25
"actuator_state_commanded"
,
26
"fire_active"
,
27
"cutoff_active"
,
28
"safety_filtered"
,
29
"valid"
,
30
"cal_status"
,
31
"reason"
,
32
"channel_id"
,
33
}
34
35
# Plumbing fields hidden from the picker by default (still exportable via advanced).
36
_SECONDARY_FIELDS = {
37
"cal_status"
,
38
"channel_id"
,
39
"timestamp_ns"
,
40
"packet_ts_ms"
,
41
"reason"
,
42
"raw_adc"
,
43
"raw_uv"
,
44
}
45
46
# Explicit, hand-maintained unit per field. The axis grouping keys on unit
47
# (falling back to the field name when a field is unit-less), so getting these
48
# right is what puts e.g. pressures and ADC counts on separate y-axes. Any field
49
# not listed here is treated as unit-less. Keep this in sync with the fields the
50
# DAQ actually publishes (see naming test / `elodin-db info`).
51
_UNIT_BY_FIELD = {
52
# pressures (raw PT + controller measurement/diagnostics)
53
"pressure_psi"
:
"psi"
,
54
"p_ch"
:
"psi"
,
"p_ch_mp1"
:
"psi"
,
"p_ch_mp2"
:
"psi"
,
55
"p_copv"
:
"psi"
,
"p_reg"
:
"psi"
,
56
"p_u_ox"
:
"psi"
,
"p_u_fuel"
:
"psi"
,
"p_d_ox"
:
"psi"
,
"p_d_fuel"
:
"psi"
,
57
# temperature (TC / RTD calibrated)
58
"temperature_c"
:
"°C"
,
"temp_c"
:
"°C"
,
59
# resistance (RTD raw)
60
"raw_resistance"
:
"Ω"
,
"raw_resistance_counts"
:
"counts"
,
61
# force (load cells + thrust estimate/reference)
62
"force_n"
:
"N"
,
"force_kg"
:
"kg"
,
"force_kg_tared"
:
"kg"
,
63
"f_ref"
:
"N"
,
"f_estimated"
:
"N"
,
64
# current (actuator sense)
65
"current_a"
:
"A"
,
66
# ball valve position (BV<n>_Cal), 0 closed / 100 open
67
"position_pct"
:
"%"
,
68
# raw ADC counts
69
"raw_adc"
:
"counts"
,
"raw_adc_counts"
:
"counts"
,
70
# LabJack T7: raw input volts, and the µV code calibration works in (T7<n>_Cal.*.raw_uv)
71
"volts"
:
"V"
,
"raw_uv"
:
"µV"
,
72
# encoder
73
"raw_angle"
:
"deg"
,
74
# controller duty cycles
75
"duty_f"
:
"0-1"
,
"duty_o"
:
"0-1"
,
76
# timestamps
77
"timestamp_ns"
:
"ns"
,
78
"sample_ts_ms"
:
"ms"
,
"packet_ts_ms"
:
"ms"
,
79
# left unit-less on purpose (grouped by field name): channel_id, sensor_id,
80
# board_id, board_type, status, result, cal_status, valid, *_state,
81
# fire_active, debug_mode, allowed_bitmask, reason, cutoff_active,
82
# safety_filtered, u_f_on, u_o_on, solver_iters, cost, mr_ref, mr_estimated.
83
}
84
85
_HASH_RE = re.compile(
r"^\d{6,}$"
)
86
87
88
def
_family
(entity: str) -> str:
89
"""Coarse family for grouping in the UI, derived from the entity prefix."""
90
head = entity.split(
"."
, 1)[0]
91
if
_HASH_RE.match(head):
92
return
"Other"
93
# LabJack T7: T71 / T71_Cal. The generic rule below would read "T71" as "T" + slot 71.
94
t7 = re.match(
r"^T7\d+(_Cal)?$"
, head)
95
if
t7:
96
return
"T7"
+ (t7.group(1)
or
""
)
97
# Strip a trailing board/index number: PT1 -> PT, PT_Cal -> PT_Cal, ACT4 -> ACT.
98
m = re.match(
r"^([A-Za-z_]+?)\d*$"
, head)
99
base = m.group(1).rstrip(
"_"
)
if
m
else
head
100
return
base
or
head
101
102
103
def
_unit
(field: str) -> str:
104
return
_UNIT_BY_FIELD.get(field.lower(),
""
)
105
106
107
@dataclass
108
class
Component
:
109
name: str
# full "<Entity>.<field>", == parquet file stem
110
entity: str
# "<Entity>" (may itself contain dots, e.g. PT_Cal.Ox_Upstream)
111
field: str
# last dotted segment
112
family: str
# coarse group for the UI
113
unit: str
114
discrete: bool
# step-draw / forward-fill
115
primary: bool
# surfaced in the picker by default (vs plumbing)
116
117
def
as_dict
(self) -> dict:
118
return
asdict(self)
119
120
121
def
classify
(name: str) -> Component:
122
"""Split a component name into (entity, field) and classify it."""
123
if
"."
in
name:
124
entity, field = name.rsplit(
"."
, 1)
125
else
:
126
entity, field = name,
""
127
fam =
_family
(entity)
128
return
Component
(
129
name=name,
130
entity=entity,
131
field=field,
132
family=fam,
133
unit=
_unit
(field),
134
discrete=field.lower()
in
_DISCRETE_FIELDS,
135
primary=fam !=
"Other"
and
field.lower()
not
in
_SECONDARY_FIELDS,
136
)
backend.naming.Component
Definition
naming.py:108
backend.naming.Component.as_dict
dict as_dict(self)
Definition
naming.py:117
backend.naming._family
str _family(str entity)
Definition
naming.py:88
backend.naming._unit
str _unit(str field)
Definition
naming.py:103
backend.naming.classify
Component classify(str name)
Definition
naming.py:121
daq-server
tools
postprocessing
webviewer
backend
naming.py
Generated by
1.15.0