diff --git a/src/pandablocks_ioc/ioc.py b/src/pandablocks_ioc/ioc.py index 452ceb80..fdc7e917 100644 --- a/src/pandablocks_ioc/ioc.py +++ b/src/pandablocks_ioc/ioc.py @@ -258,17 +258,34 @@ async def introspect_panda( values, all_values_dict = _create_dicts_from_changes(changes, block_dict) - panda_dict = {} - for (block_name, block_info), field_info in zip( - block_dict.items(), field_infos, strict=False - ): - panda_dict[block_name] = _BlockAndFieldInfo( - block_info=block_info, fields=field_info, values=values[block_name] + panda_dict = { + block_name: _BlockAndFieldInfo(block_info, field_info, values[block_name]) + for (block_name, block_info), field_info in zip( + block_dict.items(), field_infos, strict=False ) + } return (panda_dict, all_values_dict) +def extract_label_from_metadata(block_name_number, field_name: str): + # Parse *METADATA.LABEL_ into "" key and + # ":LABEL" value + if block_name_number.startswith("*METADATA") and field_name.startswith("LABEL_"): + _, block_name_number = field_name.split("_", maxsplit=1) + + # The block is fixed with metadata, it should end with a number + # "*METADATA.LABEL_SEQ2": "NewSeqMetadataLabel", + if not block_name_number[-1].isdigit(): + raise ValueError( + f"Recieved metadata for a block name {block_name_number} that " + "didn't contain a number" + ) + + return block_name_number + return None + + def _create_dicts_from_changes( changes: Changes, block_info_dict: dict[str, BlockInfo] ) -> tuple[dict[str, dict[EpicsName, RecordValue]], dict[EpicsName, RecordValue]]: @@ -298,22 +315,12 @@ def _store_values( block_name_number, field_name = block_and_field_name.split(".", maxsplit=1) - # Parse *METADATA.LABEL_ into "" key and - # ":LABEL" value - if block_name_number.startswith("*METADATA") and field_name.startswith( - "LABEL_" + if label_block_name_number := extract_label_from_metadata( + block_name_number, field_name ): - _, block_name_number = field_name.split("_", maxsplit=1) - - # The block is fixed with metadata, it should end with a number - # "*METADATA.LABEL_SEQ2": "NewSeqMetadataLabel", - if not block_name_number[-1].isdigit(): - raise ValueError( - f"Recieved metadata for a block name {block_name_number} that " - "didn't contain a number" - ) + block_name_number = label_block_name_number + block_name_no_number = re.sub(r"\d*$", "", label_block_name_number) - block_name_no_number = re.sub(r"\d*$", "", block_name_number) number_of_blocks = block_info_dict[block_name_no_number].number if number_of_blocks == 1: @@ -2095,6 +2102,18 @@ async def update( field = PandAName(field) field = panda_to_epics_name(field) + if block_label := extract_label_from_metadata( + *field.split(":", maxsplit=1) + ): + block_label_no_number = re.sub(r"\d*$", "", block_label) + block_label = f"{block_label}:LABEL" + block_label_no_number = f"{block_label_no_number}:LABEL" + + if block_label_no_number in all_records: + field = block_label_no_number + elif block_label in all_records: + field = block_label + if field not in all_records: logging.error( f"Unknown field {field} returned from GetChanges values" diff --git a/tests/fixtures/mocked_panda.py b/tests/fixtures/mocked_panda.py index 533ed76b..a52aaebd 100644 --- a/tests/fixtures/mocked_panda.py +++ b/tests/fixtures/mocked_panda.py @@ -859,18 +859,18 @@ def standard_responses(table_field_info, table_data_1, table_data_2): "PCAP.GATE.DELAY": "1", "PCAP.ARM": "0", "*METADATA.LABEL_PCAP1": "PcapMetadataLabel", + "*METADATA.LABEL_PULSE1": "OriginalLabel", "PULSE.DELAY": "100", "PULSE.DELAY.UNITS": "ms", }, multiline_values={"SEQ.TABLE": table_data_1}, ), - # 0.5 seconds of no changes in case the ioc setup completes - # before the test starts respond_with_no_changes(number_of_iterations=15), changes_iterator_wrapper( values={ "PCAP.TRIG_EDGE": "Either", "PULSE.DELAY.UNITS": "s", + "*METADATA.LABEL_PULSE1": "ANewLabel", }, multiline_values={"SEQ.TABLE": table_data_2}, ), diff --git a/tests/test-bobfiles/PULSE.bob b/tests/test-bobfiles/PULSE.bob index 67890618..6cfea5bd 100644 --- a/tests/test-bobfiles/PULSE.bob +++ b/tests/test-bobfiles/PULSE.bob @@ -3,7 +3,7 @@ 0 0 506 - 116 + 177 4 4 @@ -26,10 +26,37 @@ 1 - PARAMETERS + INPUTS 5 30 496 + 56 + true + + Label + Label + 0 + 0 + 250 + 20 + $(text) + + + TextEntry + TEST_PREFIX:PULSE:LABEL + 255 + 0 + 205 + 20 + 1 + 6 + + + + PARAMETERS + 5 + 91 + 496 81 true diff --git a/tests/test_ioc_system.py b/tests/test_ioc_system.py index 4bec69fd..66d2674e 100644 --- a/tests/test_ioc_system.py +++ b/tests/test_ioc_system.py @@ -83,6 +83,7 @@ async def test_introspect_panda( "PULSE:DELAY": "100", "PCAP:ARM": "0", "PULSE:DELAY:UNITS": "ms", + "PULSE:LABEL": "OriginalLabel", "SEQ:TABLE": table_data_1, } @@ -541,6 +542,27 @@ async def test_metadata_parses_into_single_pv(mocked_panda_standard_responses): ) in multiprocessing_queue_to_list(command_queue) +async def test_metadata_label_update_from_panda_updates_pv( + mocked_panda_standard_responses, +): + ( + tmp_path, + child_conn, + response_handler, + command_queue, + test_prefix, + ) = mocked_panda_standard_responses + camonitor_queue = asyncio.Queue() + m1 = camonitor( + test_prefix + ":PULSE:LABEL", camonitor_queue.put, datatype=DBR_CHAR_STR + ) + try: + assert await asyncio.wait_for(camonitor_queue.get(), TIMEOUT) == "OriginalLabel" + assert await asyncio.wait_for(camonitor_queue.get(), TIMEOUT) == "ANewLabel" + finally: + m1.close() + + async def test_metadata_parses_into_multiple_pvs_caput_single_pv( mocked_panda_multiple_seq_responses, ):