Skip to content

Commit

Permalink
Using string representations of enums again
Browse files Browse the repository at this point in the history
  • Loading branch information
evalott100 committed Oct 16, 2023
1 parent 8df422a commit 3a0a2b6
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 33 deletions.
40 changes: 35 additions & 5 deletions src/pandablocks_ioc/_tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,12 +312,16 @@ def __init__(
full_name = EpicsName(full_name)
description = trim_description(field_details.description, full_name)

waveform_val = self._construct_waveform_val(
field_data, field_name, field_details
)

field_record: RecordWrapper = builder.WaveformOut(
full_name,
DESC=description,
validate=self.validate_waveform,
on_update_name=self.update_waveform,
initial_value=field_data[field_name],
initial_value=waveform_val,
length=field_info.max_length,
)

Expand Down Expand Up @@ -577,6 +581,7 @@ async def update_mode(self, new_val: int):
return

assert isinstance(old_val, list)

field_data = words_to_table(old_val, self.field_info)
for field_name, field_record in self.table_fields_records.items():
assert field_record.record_info
Expand Down Expand Up @@ -609,6 +614,23 @@ async def update_mode(self, new_val: int):
# avoid recursion
self.mode_record_info.record.set(TableModeEnum.VIEW.value, process=False)

def _construct_waveform_val(
self,
field_data: Dict[str, UnpackedArray],
field_name: str,
field_details: TableFieldDetails,
):
"""Convert the values into the right form. For enums this means converting
the numeric values PandA sends us into the string representation. For all other
types the numeric representation is used."""
if field_details.labels:
max_length = max([len(x) for x in field_details.labels])
return np.array(
[field_details.labels[x] for x in field_data[field_name]],
dtype=f"<U{max_length + 1}",
)
return field_data[field_name]

def update_table(self, new_values: List[str]) -> None:
"""Update the waveform records with the given values from the PandA, depending
on the value of the table's MODE record.
Expand All @@ -626,10 +648,12 @@ def update_table(self, new_values: List[str]) -> None:
for field_name, field_record in self.table_fields_records.items():
assert field_record.record_info

# Must skip processing as the validate method would reject the update
field_record.record_info.record.set(
field_data[field_name], process=False
waveform_val = self._construct_waveform_val(
field_data, field_name, field_record.field
)

# Must skip processing as the validate method would reject the update
field_record.record_info.record.set(waveform_val, process=False)
self._update_scalar(field_record.record_info.record.name)

# All items in field_data have the same length, so just use 0th.
Expand Down Expand Up @@ -675,6 +699,9 @@ def _update_scalar(self, waveform_record_name: str) -> None:

try:
scalar_val = waveform_data[index]
if labels:
# mbbi/o records must use the numeric index
scalar_val = labels.index(scalar_val)
sev = alarm.NO_ALARM
except IndexError as e:
logging.warning(
Expand All @@ -694,7 +721,10 @@ def _update_scalar(self, waveform_record_name: str) -> None:

# alarm value is ignored if severity = NO_ALARM. Softioc also defaults
# alarm value to UDF_ALARM, but I'm specifying it for clarity.
scalar_record.set(scalar_val, severity=sev, alarm=alarm.UDF_ALARM)
if labels:
scalar_record.set(scalar_val, severity=sev, alarm=alarm.UDF_ALARM)
else:
scalar_record.set(scalar_val, severity=sev, alarm=alarm.UDF_ALARM)

def _update_index_drvh(self, data: UnpackedArray):
"""Set the DRVH value of the index record based on the newly set data length"""
Expand Down
30 changes: 30 additions & 0 deletions tests/fixtures/mocked_panda.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ def new_random_test_prefix():
return append_random_uppercase(TEST_PREFIX)


def multiprocessing_queue_to_list(queue: Queue):
queue.put(None)
return list(iter(queue.get, None))


@pytest_asyncio.fixture
def mocked_time_record_updater(
new_random_test_prefix,
Expand Down Expand Up @@ -465,6 +470,31 @@ def multiple_seq_responses(table_field_info, table_data_1, table_data_2):
],
)
): repeat(None),
command_to_key(
Put(
field="SEQ4.TABLE",
value=["0", "0", "0", "0", "1", "0", "0", "0", "0", "0", "0", "0"],
)
): repeat(None),
command_to_key(
Put(
field="SEQ3.TABLE",
value=[
"2457862144",
"4294967291",
"100",
"0",
"269877249",
"678",
"0",
"55",
"4293918720",
"0",
"9",
"9999",
],
)
): repeat(None),
# DRVL changing from 8e-06 ms to minutes
command_to_key(GetFieldInfo(block="SEQ", extended_metadata=True)): repeat(
{"TABLE": table_field_info}
Expand Down
4 changes: 2 additions & 2 deletions tests/fixtures/panda_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,8 +212,8 @@ def table_unpacked_data(
"""The unpacked equivalent of table_data"""
array_values: List[ndarray] = [
array([5, 0, 50000], dtype=uint16),
# Below labels correspond to label values ["Immediate", "BITC=1", "Immediate"]
array([0, 6, 0], dtype=uint8),
# Below labels correspond to label values
array(["Immediate", "BITC=1", "Immediate"]),
array([-5, 678, 0], dtype=int32),
array([100, 0, 9], dtype=uint32),
array([0, 1, 1], dtype=uint8),
Expand Down
32 changes: 18 additions & 14 deletions tests/test_ioc_system.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import asyncio
import os
from multiprocessing import Queue
from pathlib import Path
from typing import List, OrderedDict

Expand All @@ -13,6 +12,7 @@
MockedAsyncioClient,
ResponseHandler,
command_to_key,
multiprocessing_queue_to_list,
)
from numpy import ndarray
from pandablocks.commands import Arm, Disarm, Put
Expand Down Expand Up @@ -387,11 +387,6 @@ async def test_create_bobfiles_deletes_existing_files_with_clear_bobfiles(
assert generated_bobfile.read_text() != ""


def multiprocessing_queue_to_list(queue: Queue):
queue.put(None)
return list(iter(queue.get, None))


async def test_create_softioc_record_update_send_to_panda(
mocked_panda_standard_responses,
):
Expand Down Expand Up @@ -630,16 +625,26 @@ async def test_non_defined_seq_table_can_be_added_to_panda_side(
finally:
monitor.close()

await caput(test_prefix + ":SEQ3:TABLE:MODE", 1, wait=True) # TableModeEnum.EDIT
table_mode = await caget(test_prefix + ":SEQ3:TABLE:MODE", timeout=TIMEOUT)
assert table_mode == 1

await caput(
test_prefix + ":SEQ3:TABLE:REPEATS",
numpy.array([0, 1, 0]),
wait=True,
)
curr_val = await caget(test_prefix + ":SEQ3:TABLE:REPEATS", timeout=TIMEOUT)

assert list(curr_val) == [0, 1, 0]

# TODO Test that the ioc can update the panda values for the enums.
await caput(test_prefix + ":SEQ3:TABLE:MODE", 2, wait=True) # TableModeEnum.SUBMIT


async def test_non_defined_seq_table_can_be_added_to_ioc_side(
mocked_panda_multiple_seq_responses,
):
"""
TDDO
This test currently doesn't work. The ioc can accept the values, but the waveform
enum values aren't accepted by the panda. This will be handled by an upcoming PR.
"""
(
tmp_path,
child_conn,
Expand Down Expand Up @@ -669,8 +674,7 @@ async def test_non_defined_seq_table_can_be_added_to_ioc_side(

assert list(curr_val) == [0, 1, 0]

# TODO Test that the ioc can update the panda values for the enums.
# await caput(test_prefix + ":SEQ4:TABLE:MODE", 2, wait=True) # TableModeEnum.SUBMIT
await caput(test_prefix + ":SEQ4:TABLE:MODE", 2, wait=True) # TableModeEnum.SUBMIT


async def test_not_including_number_in_metadata_throws_error(
Expand Down
44 changes: 32 additions & 12 deletions tests/test_tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
from pandablocks.responses import TableFieldDetails, TableFieldInfo
from softioc import alarm, fields

from fixtures.mocked_panda import multiprocessing_queue_to_list

from pandablocks_ioc._tables import (
TableFieldRecordContainer,
TableModeEnum,
Expand Down Expand Up @@ -138,8 +140,19 @@ async def test_create_softioc_update_table(

# And check some other columns too
curr_val = await caget(test_prefix + ":SEQ:TABLE:TRIGGER")
# ["Immediate", "Immediate", "Immediate", "POSB>=POSITION", "POSC<=POSITION"],
assert numpy.array_equal(curr_val, [0, 0, 0, 9, 12])
#
assert numpy.array_equal(
curr_val,
numpy.array(
[
"Immediate",
"Immediate",
"Immediate",
"POSB>=POSITION",
"POSC<=POSITION",
],
),
)

curr_val = await caget(test_prefix + ":SEQ:TABLE:POSITION")
assert numpy.array_equal(curr_val, [-5, 0, 0, 444444, -99])
Expand Down Expand Up @@ -222,8 +235,11 @@ async def test_create_softioc_table_update_send_to_panda(

await caput(test_prefix + ":SEQ:TABLE:MODE", "SUBMIT", wait=True, timeout=TIMEOUT)

command_queue.put(None)
commands_recieved_by_panda = list(iter(command_queue.get, None))
# Give the queue time to be put to
await asyncio.sleep(0.1)

commands_recieved_by_panda = multiprocessing_queue_to_list(command_queue)

assert (
command_to_key(
Put(
Expand Down Expand Up @@ -286,10 +302,7 @@ async def test_create_softioc_update_table_index(
curr_val = await asyncio.wait_for(repeats_queue.get(), TIMEOUT)
assert curr_val == table_unpacked_data["REPEATS"][index_val]
curr_val = await asyncio.wait_for(trigger_queue.get(), TIMEOUT)
assert (
curr_val
== table_fields["TRIGGER"].labels[table_unpacked_data["TRIGGER"][index_val]]
)
assert curr_val == table_unpacked_data["TRIGGER"][index_val]

# Now set a new INDEX
index_val = 1
Expand All @@ -299,10 +312,7 @@ async def test_create_softioc_update_table_index(
curr_val = await asyncio.wait_for(repeats_queue.get(), TIMEOUT)
assert curr_val == table_unpacked_data["REPEATS"][index_val]
curr_val = await asyncio.wait_for(trigger_queue.get(), TIMEOUT)
assert (
curr_val
== table_fields["TRIGGER"].labels[table_unpacked_data["TRIGGER"][index_val]]
)
assert curr_val == table_unpacked_data["TRIGGER"][index_val]

finally:
repeats_monitor.close()
Expand Down Expand Up @@ -439,6 +449,7 @@ async def test_table_updater_update_mode_submit_exception(
table_updater: TableUpdater,
table_data_1: List[str],
table_unpacked_data: typing.OrderedDict[EpicsName, ndarray],
table_fields: Dict[str, TableFieldDetails],
):
"""Test that update_mode with new value of SUBMIT handles an exception from Put
correctly"""
Expand All @@ -463,6 +474,10 @@ async def test_table_updater_update_mode_submit_exception(

expected = called_args[0][0]

if table_fields[field_name].labels:
labels = table_fields[field_name].labels
expected = numpy.array([labels[x] for x in expected])

numpy.testing.assert_array_equal(data, expected)

table_updater.mode_record_info.record.set.assert_called_once_with(
Expand Down Expand Up @@ -498,6 +513,7 @@ async def test_table_updater_update_mode_discard(
table_updater: TableUpdater,
table_data_1: List[str],
table_unpacked_data: typing.OrderedDict[EpicsName, ndarray],
table_fields: Dict[str, TableFieldDetails],
):
"""Test that update_mode with new value of DISCARD resets record data"""
assert isinstance(table_updater.client.send, AsyncMock)
Expand All @@ -520,6 +536,10 @@ async def test_table_updater_update_mode_discard(

expected = called_args[0][0]

if table_fields[field_name].labels:
labels = table_fields[field_name].labels
expected = numpy.array([labels[x] for x in expected])

numpy.testing.assert_array_equal(data, expected)

table_updater.mode_record_info.record.set.assert_called_once_with(
Expand Down

0 comments on commit 3a0a2b6

Please sign in to comment.