Skip to main content

ReadHandler

The ReadHandler interface is how the measurement data received from the outstation is passed to your application code. This callback interface is specified for each association you create and is invoked for both unsolicited responses and responses to polls.

When a response is parsed and determined to carry measurement data, ReadHandler callbacks are executed in the following order:

  1. ReadHandler::begin_fragment
  2. ReadHandler::handle_<TYPE> for each object header in the message
  3. ReadHandler::end_fragment

The begin/end methods provide useful information, including:

  • The full header of the response
  • Context regarding what triggered the response, such as unsolicited, startup integrity scan, periodic poll, etc.
tip

To determine if a measurement is a static value or an event, use the HeaderInfo::is_event field.

Each value has a set of associated flags. You can check the presence of a flag by using the Flags::value field and comparing it with the constants defined in Flag.

tip

It's common to maintain a list of measurement values in your ReadHandler implementation. You can then use the endFragment method as a trigger to publish them. Since responses may consist of multiple fragments, you can inspect the ResponseHeader.Control.Fin field to determine if the current fragment is the final fragment in a response series.

It is possible to send a read request with a specific ReadHandler by using the MasterChannel::read_with_handler() method. All the response data associated with the request will be forwarded to the ReadHandler specified as an argument instead of going to the handler specified at the creation of the association.

impl ReadHandler for ExampleReadHandler {
fn begin_fragment(&mut self, _read_type: ReadType, header: ResponseHeader) -> MaybeAsync<()> {
println!(
"Beginning fragment (broadcast: {})",
header.iin.iin1.get_broadcast()
);
MaybeAsync::ready(())
}

fn end_fragment(&mut self, _read_type: ReadType, _header: ResponseHeader) -> MaybeAsync<()> {
println!("End fragment");
MaybeAsync::ready(())
}

fn handle_binary_input(
&mut self,
info: HeaderInfo,
iter: &mut dyn Iterator<Item = (BinaryInput, u16)>,
) {
println!("Binary Inputs:");
println!("Qualifier: {}", info.qualifier);
println!("Variation: {}", info.variation);

for (x, idx) in iter {
println!(
"BI {}: Value={} Flags={:#04X} Time={:?}",
idx, x.value, x.flags.value, x.time
);
}
}

fn handle_double_bit_binary_input(
&mut self,
info: HeaderInfo,
iter: &mut dyn Iterator<Item = (DoubleBitBinaryInput, u16)>,
) {
println!("Double Bit Binary Inputs:");
println!("Qualifier: {}", info.qualifier);
println!("Variation: {}", info.variation);

for (x, idx) in iter {
println!(
"DBBI {}: Value={} Flags={:#04X} Time={:?}",
idx, x.value, x.flags.value, x.time
);
}
}

fn handle_binary_output_status(
&mut self,
info: HeaderInfo,
iter: &mut dyn Iterator<Item = (BinaryOutputStatus, u16)>,
) {
println!("Binary Output Statuses:");
println!("Qualifier: {}", info.qualifier);
println!("Variation: {}", info.variation);

for (x, idx) in iter {
println!(
"BOS {}: Value={} Flags={:#04X} Time={:?}",
idx, x.value, x.flags.value, x.time
);
}
}

fn handle_counter(&mut self, info: HeaderInfo, iter: &mut dyn Iterator<Item = (Counter, u16)>) {
println!("Counters:");
println!("Qualifier: {}", info.qualifier);
println!("Variation: {}", info.variation);

for (x, idx) in iter {
println!(
"Counter {}: Value={} Flags={:#04X} Time={:?}",
idx, x.value, x.flags.value, x.time
);
}
}

fn handle_frozen_counter(
&mut self,
info: HeaderInfo,
iter: &mut dyn Iterator<Item = (FrozenCounter, u16)>,
) {
println!("Frozen Counters:");
println!("Qualifier: {}", info.qualifier);
println!("Variation: {}", info.variation);

for (x, idx) in iter {
println!(
"Frozen Counter {}: Value={} Flags={:#04X} Time={:?}",
idx, x.value, x.flags.value, x.time
);
}
}

fn handle_analog_input(
&mut self,
info: HeaderInfo,
iter: &mut dyn Iterator<Item = (AnalogInput, u16)>,
) {
println!("Analog Inputs:");
println!("Qualifier: {}", info.qualifier);
println!("Variation: {}", info.variation);

for (x, idx) in iter {
println!(
"AI {}: Value={} Flags={:#04X} Time={:?}",
idx, x.value, x.flags.value, x.time
);
}
}

fn handle_analog_output_status(
&mut self,
info: HeaderInfo,
iter: &mut dyn Iterator<Item = (AnalogOutputStatus, u16)>,
) {
println!("Analog Output Statuses:");
println!("Qualifier: {}", info.qualifier);
println!("Variation: {}", info.variation);

for (x, idx) in iter {
println!(
"AOS {}: Value={} Flags={:#04X} Time={:?}",
idx, x.value, x.flags.value, x.time
);
}
}

fn handle_octet_string<'a>(
&mut self,
info: HeaderInfo,
iter: &mut dyn Iterator<Item = (&'a [u8], u16)>,
) {
println!("Octet Strings:");
println!("Qualifier: {}", info.qualifier);
println!("Variation: {}", info.variation);

for (x, idx) in iter {
println!("Octet String {}: Value={:X?}", idx, x);
}
}
}