def test_update_handler_publishes_enum_update(): producer = FakeProducer() context = FakeContext() pv_index = 0 pv_value_str = "choice0" pv_timestamp_s = 1.1 # seconds from unix epoch pv_source_name = "source_name" pva_update_handler = PVAUpdateHandler(producer, context, pv_source_name, "output_topic", "f142") # type: ignore context.call_monitor_callback_with_fake_pv_update( NTEnum(valueAlarm=True).wrap( { "index": pv_index, "choices": [pv_value_str, "choice1", "choice2"] }, timestamp=pv_timestamp_s, )) assert producer.published_payload is not None pv_update_output = deserialise_f142(producer.published_payload) assert pv_update_output.value == pv_value_str assert pv_update_output.source_name == pv_source_name pva_update_handler.stop()
def test_update_handler_publishes_periodic_update(): producer = FakeProducer() context = FakeContext() pv_timestamp_s = 1.1 # seconds from unix epoch pv_source_name = "source_name" pv_value = -3 pv_type = "i" update_period_ms = 10 pva_update_handler = PVAUpdateHandler(producer, context, pv_source_name, "output_topic", "f142", update_period_ms) # type: ignore context.call_monitor_callback_with_fake_pv_update( NTScalar(pv_type, valueAlarm=True).wrap(pv_value, timestamp=pv_timestamp_s)) assert producer.published_payload is not None pv_update_output = deserialise_f142(producer.published_payload) assert pv_update_output.value == pv_value assert pv_update_output.source_name == pv_source_name sleep(0.05) assert ( producer.messages_published > 1 ), "Expected more than the 1 message from triggered update due to periodic updates being active" pva_update_handler.stop()
def test_update_handler_does_not_include_alarm_details_if_unchanged_in_subsequent_updates( ): producer = FakeProducer() context = FakeContext() pv_timestamp_s = 1.1 # seconds from unix epoch pv_source_name = "source_name" pv_value = -3 pv_type = "i" alarm_status = 4 # Indicates RECORD alarm, we map the alarm message to a specific alarm status to forward alarm_severity = 1 # AlarmSeverity.MINOR alarm_message = "HIGH_ALARM" pva_update_handler = PVAUpdateHandler(producer, context, pv_source_name, "output_topic", "f142") # type: ignore context.call_monitor_callback_with_fake_pv_update( NTScalar(pv_type, valueAlarm=True).wrap( { "value": pv_value, "alarm": { "status": alarm_status, "severity": alarm_severity, "message": alarm_message, }, }, timestamp=pv_timestamp_s, )) # Second update, with unchanged alarm context.call_monitor_callback_with_fake_pv_update( NTScalar(pv_type, valueAlarm=True).wrap( { "value": pv_value, "alarm": { "status": alarm_status, "severity": alarm_severity, "message": alarm_message, }, }, timestamp=pv_timestamp_s, )) assert producer.messages_published == 2 pv_update_output = deserialise_f142(producer.published_payload) assert pv_update_output.alarm_status == AlarmStatus.NO_CHANGE assert pv_update_output.alarm_severity == AlarmSeverity.NO_CHANGE pva_update_handler.stop()
def test_update_handler_throws_if_schema_not_recognised(): producer = FakeProducer() context = FakeContext() non_existing_schema = "DOESNTEXIST" with pytest.raises(ValueError): PVAUpdateHandler(producer, context, "source_name", "output_topic", non_existing_schema) # type: ignore
def test_update_handler_publishes_int_update(pv_value, pv_type): producer = FakeProducer() context = FakeContext() pv_timestamp_s = 1.1 # seconds from unix epoch pv_source_name = "source_name" pva_update_handler = PVAUpdateHandler(producer, context, pv_source_name, "output_topic", "f142") # type: ignore context.call_monitor_callback_with_fake_pv_update( NTScalar(pv_type, valueAlarm=True).wrap(pv_value, timestamp=pv_timestamp_s)) assert producer.published_payload is not None pv_update_output = deserialise_f142(producer.published_payload) assert pv_update_output.value == pv_value assert pv_update_output.source_name == pv_source_name pva_update_handler.stop()
def test_update_handler_publishes_alarm_update(): producer = FakeProducer() context = FakeContext() pv_value = 42 pv_type = "i" pv_timestamp_s = 1.1 # seconds from unix epoch pv_source_name = "source_name" alarm_status = 4 # Indicates RECORD alarm, we map the alarm message to a specific alarm status to forward alarm_severity = 1 # AlarmSeverity.MINOR alarm_message = "HIGH_ALARM" pva_update_handler = PVAUpdateHandler(producer, context, pv_source_name, "output_topic", "f142") # type: ignore context.call_monitor_callback_with_fake_pv_update( NTScalar(pv_type, valueAlarm=True).wrap( { "value": pv_value, "alarm": { "status": alarm_status, "severity": alarm_severity, "message": alarm_message, }, }, timestamp=pv_timestamp_s, )) assert producer.published_payload is not None pv_update_output = deserialise_f142(producer.published_payload) assert pv_update_output.value == pv_value assert pv_update_output.source_name == pv_source_name assert pv_update_output.alarm_status == AlarmStatus.HIGH assert pv_update_output.alarm_severity == AlarmSeverity.MINOR pva_update_handler.stop()
def create_update_handler( producer: KafkaProducer, ca_context: CAContext, pva_context: PVAContext, channel: ConfigChannel, fake_pv_period_ms: int, periodic_update_ms: Optional[int] = None, ) -> UpdateHandler: if not channel.name: raise RuntimeError( "PV name not specified when adding handler for channel") if not channel.output_topic: raise RuntimeError( f"Output topic not specified when adding handler for channel {channel.name}" ) if not channel.schema: raise RuntimeError( f"Schema not specified when adding handler for channel {channel.name}" ) if channel.protocol == EpicsProtocol.PVA: return PVAUpdateHandler( producer, pva_context, channel.name, channel.output_topic, channel.schema, periodic_update_ms, ) elif channel.protocol == EpicsProtocol.CA: return CAUpdateHandler( producer, ca_context, channel.name, channel.output_topic, channel.schema, periodic_update_ms, ) elif channel.protocol == EpicsProtocol.FAKE: return FakeUpdateHandler( producer, channel.name, channel.output_topic, channel.schema, fake_pv_period_ms, ) raise RuntimeError("Unexpected EpicsProtocol in create_update_handler")