Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DM-47930 Fixes needed for the ATBuilding RPi #7

Open
wants to merge 7 commits into
base: develop
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion python/lsst/ts/vent/controller/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class Config:
device_id = 1
"""The default modbus device ID for the variable frequency drive."""

max_freq = 50.0
max_freq = 25.0
"""Default maximum frequency for the dome fans."""

megaind_bus = 1
Expand Down
51 changes: 40 additions & 11 deletions python/lsst/ts/vent/controller/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,9 @@ async def fan_manual_control(self, manual: bool) -> None:
self.log.debug("set vfd_manual_control")
assert self.connected
assert self.vfd_client is not None

await self.set_fan_frequency(0.0)

settings = vf_drive.MANUAL if manual else vf_drive.AUTO
for address, value in zip(vf_drive.CFG_REGISTERS, settings):
await self.vfd_client.write_register(
Expand Down Expand Up @@ -188,20 +191,22 @@ async def get_fan_frequency(self) -> float:
self.log.debug("get fan_frequency")
assert self.connected
assert self.vfd_client is not None
cmd = (
await self.vfd_client.read_holding_registers(
slave=self.config.device_id, address=vf_drive.Registers.CMD_REGISTER
)
).registers[0]
if cmd == 0:
return 0.0

lfr = (
output_frequency = (
await self.vfd_client.read_holding_registers(
slave=self.config.device_id, address=vf_drive.Registers.LFR_REGISTER
slave=self.config.device_id, address=vf_drive.Registers.RFR_REGISTER
)
).registers[0]
return 0.1 * lfr
output_frequency *= 0.1 # RFR register holds frequency in units of 0.1 Hz
return output_frequency

def get_max_frequency(self) -> float:
"""Returns the maximum allowed frequency.

Calls to `set_fan_frequency` may not have an argument exceeding
this value.
"""
return self.config.max_freq

async def set_fan_frequency(self, frequency: float) -> None:
"""Sets the target frequency for the dome exhaust fan. The frequency
Expand Down Expand Up @@ -300,6 +305,30 @@ async def get_drive_state(self) -> FanDriveState:
return FanDriveState.OPERATING
return FanDriveState.FAULT

async def get_drive_voltage(self) -> float:
"""Returns the target frequency configured in the drive.

Raises
------
AssertionError
If the controller is not connected.

ModbusException
If a communications error occurs.
"""

self.log.debug("get drive_voltage")
assert self.connected
assert self.vfd_client is not None

drive_voltage = (
await self.vfd_client.read_holding_registers(
slave=self.config.device_id, address=vf_drive.Registers.ULN_REGISTER
)
).registers[0]
drive_voltage *= 0.1 # ULN register holds voltage in units of 0.1 V
return drive_voltage

async def last8faults(self) -> list[tuple[int, str]]:
"""Returns the last eight fault conditions recorded by the drive.

Expand Down Expand Up @@ -327,7 +356,7 @@ async def last8faults(self) -> list[tuple[int, str]]:
address=vf_drive.Registers.FAULT_REGISTER,
count=8,
)
return [(r, vf_drive.FAULTS[r]) for r in rvals.registers]
return [(r, vf_drive.FAULTS[r]) for r in reversed(rvals.registers)]

def vent_open(self, vent_number: int) -> None:
"""Opens the specified vent.
Expand Down
17 changes: 15 additions & 2 deletions python/lsst/ts/vent/controller/dispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ def __init__(
self.dispatch_dict: Final[dict[str, list[type]]] = {
"close_vent_gate": [int, int, int, int],
"open_vent_gate": [int, int, int, int],
"get_fan_drive_max_frequency": [],
"reset_extraction_fan_drive": [],
"set_extraction_fan_drive_freq": [float],
"set_extraction_fan_manual_control_mode": [bool],
Expand Down Expand Up @@ -164,12 +165,13 @@ async def read_and_dispatch(self) -> None:
# Convert the arguments to their expected type.
args = [cast_string_to_type(t, arg) for t, arg in zip(types, args)]
# Call the method with the specified arguments.
await getattr(self, command)(*args)
return_value = await getattr(self, command)(*args)
# Send back a success response.
await self.respond(
json.dumps(
dict(
command=command,
return_value=return_value,
error=0,
exception_name="",
message="",
Expand Down Expand Up @@ -211,6 +213,9 @@ async def open_vent_gate(
if gate != -1:
raise ValueError(f"Invalid vent ({gate}) must be between 0 and 3.")

async def get_fan_drive_max_frequency(self) -> float:
return self.controller.get_max_frequency()

async def reset_extraction_fan_drive(self) -> None:
await self.controller.vfd_fault_reset()

Expand Down Expand Up @@ -243,6 +248,7 @@ async def monitor_status(self) -> None:
last_fault = None
fan_drive_state = None
fan_frequency = None
drive_voltage = None

while self.connected:
try:
Expand Down Expand Up @@ -320,11 +326,18 @@ async def monitor_status(self) -> None:
# Send telemetry every TELEMETRY_INTERVAL times through the loop
self.telemetry_count -= 1
new_fan_frequency = await self.controller.get_fan_frequency()
if self.telemetry_count < 0 or new_fan_frequency != fan_frequency:
new_drive_voltage = await self.controller.get_drive_voltage()
if (
self.telemetry_count < 0
or new_fan_frequency != fan_frequency
or new_drive_voltage != drive_voltage
):
fan_frequency = new_fan_frequency
drive_voltage = new_drive_voltage
self.telemetry_count = self.TELEMETRY_INTERVAL
telemetry = {
"tel_extraction_fan": new_fan_frequency,
"tel_drive_voltage": new_drive_voltage,
}
await self.respond(
json.dumps(
Expand Down
39 changes: 39 additions & 0 deletions python/lsst/ts/vent/controller/dome_vents_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@
import os
import random

from pymodbus.datastore.simulator import Cell
from pymodbus.server import ModbusSimulatorServer

from .config import Config
from .vf_drive import Registers


class DomeVentsSimulator:
Expand All @@ -39,6 +41,7 @@ def __init__(self, config: Config):
http_host="localhost",
http_port=self.http_port,
json_file=os.path.dirname(__file__) + "/simulator_setup.json",
custom_actions_module=__name__,
)

async def start(self) -> None:
Expand Down Expand Up @@ -138,3 +141,39 @@ def set_bits(self, input_bits: tuple[int]) -> None:
"""
assert len(input_bits) == 16
self.input_bits = list(input_bits)


def mirror_lfr_action(
registers: list[Cell],
inx: int,
cell: Cell,
minval: int | None = None,
maxval: int | None = None,
) -> None:
"""Custom action for the modbus simulator.

The RFR register should read the same value as what was written to the
LFR register. This custom action is tied to the RFR register
to ensure that it behaves as expected.

Parameters
----------
registers: list[Cell]
An array of all cells in the simulated modbus server.

inx: int
The index of the cell being read.

cell: Cell
An object representing the cell being read.

minval: int
The minimum allowed value for the cell.

maxval: int
The maximum allowed value for the cell.
"""
cell.value = registers[Registers.LFR_REGISTER].value


custom_actions_dict = {"mirror_lfr": mirror_lfr_action}
6 changes: 5 additions & 1 deletion python/lsst/ts/vent/controller/run_dispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,11 @@ async def async_main() -> None:
)

# Keep the event loop running indefinitely.
await asyncio.Event().wait()
try:
while True:
await asyncio.sleep(60)
except asyncio.CancelledError:
log.info("Event loop is stopping.")


def main() -> None:
Expand Down
4 changes: 3 additions & 1 deletion python/lsst/ts/vent/controller/simulator_setup.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@
}
},
"invalid": [],
"write": [ 7010, 7124, 8401, 8413, 8423, 8501, 8502, 8602, 64279],
"write": [ 3202, 3207, 7010, 7124, 8401, 8413, 8423, 8501, 8502, 8602, 64279],
"bits": [],
"uint16": [
{ "addr": 3202, "value": 0, "action": "mirror_lfr" },
{ "addr": 3207, "value": 3829 },
{ "addr": 7010, "value": 1 },
{ "addr": 7124, "value": 0 },
{ "addr": [ 7201, 7211 ], "value": 22 },
Expand Down
2 changes: 2 additions & 0 deletions python/lsst/ts/vent/controller/vf_drive.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@


class Registers(IntEnum):
RFR_REGISTER = 3202
ULN_REGISTER = 3207
SLL_REGISTER = 7010
RSF_REGISTER = 7124
FAULT_REGISTER = 7201
Expand Down
Loading