Add is_admin flag and admin_mode for dangerous write-only parameters
Parameters like CORE_SCRAM_BUTTON, CORE_EMERGENCY_STOP, bay hatch/fuel loading, VALVE_OPEN/CLOSE/OFF, STEAM_TURBINE_TRIP, and all FUN_* event triggers are now marked is_admin=True. Writing to them is blocked unless the Nucon instance has admin_mode=True or force=True is used. Normal control setpoints (MSCV_*, STEAM_TURBINE_*_BYPASS_ORDERED, CHEM_BORON_*) remain write-only but are not admin-gated. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
90616dcf69
commit
2ec68ff2e5
@ -68,12 +68,13 @@ SPECIAL_VARIABLES = frozenset({
|
|||||||
})
|
})
|
||||||
|
|
||||||
class NuconParameter:
|
class NuconParameter:
|
||||||
def __init__(self, nucon: 'Nucon', id: str, param_type: Type, is_writable: bool, min_val: Optional[Union[int, float]] = None, max_val: Optional[Union[int, float]] = None, unit: Optional[str] = None, is_readable: bool = True):
|
def __init__(self, nucon: 'Nucon', id: str, param_type: Type, is_writable: bool, min_val: Optional[Union[int, float]] = None, max_val: Optional[Union[int, float]] = None, unit: Optional[str] = None, is_readable: bool = True, is_admin: bool = False):
|
||||||
self.nucon = nucon
|
self.nucon = nucon
|
||||||
self.id = id
|
self.id = id
|
||||||
self.param_type = param_type
|
self.param_type = param_type
|
||||||
self.is_writable = is_writable
|
self.is_writable = is_writable
|
||||||
self.is_readable = is_readable
|
self.is_readable = is_readable
|
||||||
|
self.is_admin = is_admin
|
||||||
self.min_val = min_val
|
self.min_val = min_val
|
||||||
self.max_val = max_val
|
self.max_val = max_val
|
||||||
self.unit = unit
|
self.unit = unit
|
||||||
@ -120,15 +121,17 @@ class NuconParameter:
|
|||||||
unit_str = f", unit='{self.unit}'" if self.unit else ""
|
unit_str = f", unit='{self.unit}'" if self.unit else ""
|
||||||
value_str = f", value={self.value}" if self.is_readable else ""
|
value_str = f", value={self.value}" if self.is_readable else ""
|
||||||
rw_str = "write-only" if not self.is_readable else f"is_writable={self.is_writable}"
|
rw_str = "write-only" if not self.is_readable else f"is_writable={self.is_writable}"
|
||||||
return f"NuconParameter(id='{self.id}'{value_str}, param_type={self.param_type.__name__}, {rw_str}{unit_str})"
|
admin_str = ", is_admin=True" if self.is_admin else ""
|
||||||
|
return f"NuconParameter(id='{self.id}'{value_str}, param_type={self.param_type.__name__}, {rw_str}{admin_str}{unit_str})"
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.id
|
return self.id
|
||||||
|
|
||||||
class Nucon:
|
class Nucon:
|
||||||
def __init__(self, host: str = 'localhost', port: int = 8785):
|
def __init__(self, host: str = 'localhost', port: int = 8785, admin_mode: bool = False):
|
||||||
self.base_url = f'http://{host}:{port}/'
|
self.base_url = f'http://{host}:{port}/'
|
||||||
self.dummy_mode = False
|
self.dummy_mode = False
|
||||||
|
self.admin_mode = admin_mode
|
||||||
self._parameters = self._create_parameters()
|
self._parameters = self._create_parameters()
|
||||||
|
|
||||||
def _create_parameters(self) -> Dict[str, NuconParameter]:
|
def _create_parameters(self) -> Dict[str, NuconParameter]:
|
||||||
@ -354,47 +357,53 @@ class Nucon:
|
|||||||
'CHEMICAL_CLEANING_PUMP_OVERLOAD_STATUS': (PumpOverloadStatus, False),
|
'CHEMICAL_CLEANING_PUMP_OVERLOAD_STATUS': (PumpOverloadStatus, False),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Write-only params: normal control setpoints (no admin restriction)
|
||||||
write_only_values = {
|
write_only_values = {
|
||||||
# --- Core actions ---
|
# --- MSCVs (Main Steam Control Valves) ---
|
||||||
|
**{f'MSCV_{i}_OPENING_ORDERED': (float, True, 0, 100, '%') for i in range(3)},
|
||||||
|
|
||||||
|
# --- Steam turbine bypass setpoints ---
|
||||||
|
**{f'STEAM_TURBINE_{i}_BYPASS_ORDERED': (float, True, 0, 100, '%') for i in range(3)},
|
||||||
|
|
||||||
|
# --- Chemistry setpoints ---
|
||||||
|
'CHEM_BORON_DOSAGE_ORDERED_RATE': (float, True, 0, 100, '%'),
|
||||||
|
'CHEM_BORON_FILTER_ORDERED_SPEED': (float, True, 0, 100, '%'),
|
||||||
|
}
|
||||||
|
|
||||||
|
# Write-only admin params: destructive/irreversible operations, blocked unless admin_mode=True
|
||||||
|
write_only_admin_values = {
|
||||||
|
# --- Core safety actions ---
|
||||||
'CORE_SCRAM_BUTTON': (bool, True),
|
'CORE_SCRAM_BUTTON': (bool, True),
|
||||||
'CORE_EMERGENCY_STOP': (bool, True),
|
'CORE_EMERGENCY_STOP': (bool, True),
|
||||||
'CORE_END_EMERGENCY_STOP': (bool, True),
|
'CORE_END_EMERGENCY_STOP': (bool, True),
|
||||||
'RESET_AO': (bool, True),
|
'RESET_AO': (bool, True),
|
||||||
|
|
||||||
# --- Core bay commands (9 bays) ---
|
# --- Core bay physical operations ---
|
||||||
**{f'CORE_BAY_{i}_HATCH': (bool, True) for i in range(1, 10)},
|
**{f'CORE_BAY_{i}_HATCH': (bool, True) for i in range(1, 10)},
|
||||||
**{f'CORE_BAY_{i}_FUEL_LOADING': (int, True) for i in range(1, 10)},
|
**{f'CORE_BAY_{i}_FUEL_LOADING': (int, True) for i in range(1, 10)},
|
||||||
|
|
||||||
# --- Rods group command ---
|
# --- Bulk rod override ---
|
||||||
'RODS_ALL_POS_ORDERED': (float, True, 0, 100, '%'),
|
'RODS_ALL_POS_ORDERED': (float, True, 0, 100, '%'),
|
||||||
|
|
||||||
# --- MSCVs (Main Steam Control Valves) ---
|
# --- Steam turbine trip ---
|
||||||
**{f'MSCV_{i}_OPENING_ORDERED': (float, True, 0, 100, '%') for i in range(3)},
|
|
||||||
|
|
||||||
# --- Steam turbine ---
|
|
||||||
'STEAM_TURBINE_TRIP': (bool, True),
|
'STEAM_TURBINE_TRIP': (bool, True),
|
||||||
**{f'STEAM_TURBINE_{i}_BYPASS_ORDERED': (float, True, 0, 100, '%') for i in range(3)},
|
|
||||||
|
|
||||||
# --- Steam ejector valves ---
|
# --- Steam ejector valves ---
|
||||||
'STEAM_EJECTOR_CONDENSER_RETURN_VALVE': (bool, True),
|
'STEAM_EJECTOR_CONDENSER_RETURN_VALVE': (bool, True),
|
||||||
'STEAM_EJECTOR_OPERATIONAL_MOTIVE_VALVE': (bool, True),
|
'STEAM_EJECTOR_OPERATIONAL_MOTIVE_VALVE': (bool, True),
|
||||||
'STEAM_EJECTOR_STARTUP_MOTIVE_VALVE': (bool, True),
|
'STEAM_EJECTOR_STARTUP_MOTIVE_VALVE': (bool, True),
|
||||||
|
|
||||||
# --- Valve commands (take valve name as value) ---
|
# --- Generic valve commands (take valve name as value) ---
|
||||||
'VALVE_OPEN': (str, True),
|
'VALVE_OPEN': (str, True),
|
||||||
'VALVE_CLOSE': (str, True),
|
'VALVE_CLOSE': (str, True),
|
||||||
'VALVE_OFF': (str, True),
|
'VALVE_OFF': (str, True),
|
||||||
|
|
||||||
# --- Condenser / emergency generators ---
|
# --- Infrastructure start/stop ---
|
||||||
'CONDENSER_VACUUM_PUMP_START_STOP': (bool, True),
|
'CONDENSER_VACUUM_PUMP_START_STOP': (bool, True),
|
||||||
'EMERGENCY_GENERATOR_1_START_STOP': (bool, True),
|
'EMERGENCY_GENERATOR_1_START_STOP': (bool, True),
|
||||||
'EMERGENCY_GENERATOR_2_START_STOP': (bool, True),
|
'EMERGENCY_GENERATOR_2_START_STOP': (bool, True),
|
||||||
|
|
||||||
# --- Chemistry ---
|
# --- Fun / event triggers (game cheats) ---
|
||||||
'CHEM_BORON_DOSAGE_ORDERED_RATE': (float, True, 0, 100, '%'),
|
|
||||||
'CHEM_BORON_FILTER_ORDERED_SPEED': (float, True, 0, 100, '%'),
|
|
||||||
|
|
||||||
# --- Fun / event triggers ---
|
|
||||||
'FUN_IS_ENABLED': (bool, True),
|
'FUN_IS_ENABLED': (bool, True),
|
||||||
'FUN_REQUEST_ENABLE': (bool, True),
|
'FUN_REQUEST_ENABLE': (bool, True),
|
||||||
'FUN_AO_SABOTAGE_ONCE': (bool, True),
|
'FUN_AO_SABOTAGE_ONCE': (bool, True),
|
||||||
@ -419,6 +428,8 @@ class Nucon:
|
|||||||
}
|
}
|
||||||
for name, values in write_only_values.items():
|
for name, values in write_only_values.items():
|
||||||
params[name] = NuconParameter(self, name, *values, is_readable=False)
|
params[name] = NuconParameter(self, name, *values, is_readable=False)
|
||||||
|
for name, values in write_only_admin_values.items():
|
||||||
|
params[name] = NuconParameter(self, name, *values, is_readable=False, is_admin=True)
|
||||||
return params
|
return params
|
||||||
|
|
||||||
def _parse_value(self, parameter: NuconParameter, value: str) -> Union[float, int, bool, str, Enum, None]:
|
def _parse_value(self, parameter: NuconParameter, value: str) -> Union[float, int, bool, str, Enum, None]:
|
||||||
@ -458,6 +469,8 @@ class Nucon:
|
|||||||
|
|
||||||
if not force and not parameter.is_writable:
|
if not force and not parameter.is_writable:
|
||||||
raise ValueError(f"Parameter {parameter} is not writable")
|
raise ValueError(f"Parameter {parameter} is not writable")
|
||||||
|
if not force and parameter.is_admin and not self.admin_mode:
|
||||||
|
raise ValueError(f"Parameter {parameter} is an admin parameter. Enable admin_mode on the Nucon instance or use force=True")
|
||||||
|
|
||||||
if not force:
|
if not force:
|
||||||
parameter.check_in_range(value, raise_on_oob=True)
|
parameter.check_in_range(value, raise_on_oob=True)
|
||||||
@ -594,6 +607,9 @@ class Nucon:
|
|||||||
def set_dummy_mode(self, dummy_mode: bool) -> None:
|
def set_dummy_mode(self, dummy_mode: bool) -> None:
|
||||||
self.dummy_mode = dummy_mode
|
self.dummy_mode = dummy_mode
|
||||||
|
|
||||||
|
def set_admin_mode(self, admin_mode: bool) -> None:
|
||||||
|
self.admin_mode = admin_mode
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
if isinstance(name, int):
|
if isinstance(name, int):
|
||||||
return self.__getattr__(list(self._parameters.keys())[name])
|
return self.__getattr__(list(self._parameters.keys())[name])
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user