ReadHandler
The ReadHandler
interface is used to receive values from the outstation as it is received by the master. This callback interface is specified
for each association you create and is invoked for both unsolicited responses and responses to READ requests.
When a response is parsed and determined to carry measurement data, ReadHandler
callbacks are executed in the following order:
ReadHandler::begin_fragment
ReadHandler::handle_<TYPE>
for each object header in the messageReadHandler::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.
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
.
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.
Device Attributes
The ReadHandler
also has callbacks for receiving device attributes (Group 0) from the outstation. The Rust trait uses
a single method and passes an enum representing the various types of values. The bindings use a distinct method for each
type of value, i.e. one for strings, another for unsigned integers, etc.
All of the callbacks provide an enum allowing you to determine if the received attribute is from the default set specified by DNP.org (i.e. set "0") or a privately defined set. The intent is that you never have to match variations numbers yourself unless you are dealing with data from a private set.
The default set includes a number of signed integer attributes that are mapped to boolean values, e.g.:
Group 0 Variation 231 - Support for analog input events
Group 0 Variation 237 - Support for binary input events
Instead of providing you with the raw integer, the library converts it to a bool for convenience.
Examples
The example below shows an implementation of a ReadHandler
that prints received values to the console. The Rust and C#
examples can print all of the received device attributes. The examples for the other bindings only demonstrate receiving
string attributes.
- Rust
- C
- C++
- Java
- C#
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);
}
}
fn handle_device_attribute(&mut self, _info: HeaderInfo, attr: AnyAttribute) {
println!("Device attribute: {:?}", attr)
}
}
void begin_fragment(dnp3_read_type_t read_type, dnp3_response_header_t header, void *arg)
{
printf("Beginning fragment (broadcast: %u)\n", header.iin.iin1.broadcast);
}
void end_fragment(dnp3_read_type_t read_type, dnp3_response_header_t header, void *arg) { printf("End fragment\n"); }
void handle_binary_input(dnp3_header_info_t info, dnp3_binary_input_iterator_t *it, void *arg)
{
printf("Binaries:\n");
printf("Qualifier: %s \n", dnp3_qualifier_code_to_string(info.qualifier));
printf("Variation: %s \n", dnp3_variation_to_string(info.variation));
dnp3_binary_input_t *value = NULL;
while ((value = dnp3_binary_input_iterator_next(it))) {
printf("BI %u: Value=%u Flags=0x%02X Time=%" PRIu64 "\n", value->index, value->value, value->flags.value, value->time.value);
}
}
void handle_double_bit_binary_input(dnp3_header_info_t info, dnp3_double_bit_binary_input_iterator_t *it, void *arg)
{
printf("Double Bit Binaries:\n");
printf("Qualifier: %s \n", dnp3_qualifier_code_to_string(info.qualifier));
printf("Variation: %s \n", dnp3_variation_to_string(info.variation));
dnp3_double_bit_binary_input_t *value = NULL;
while ((value = dnp3_double_bit_binary_input_iterator_next(it))) {
printf("DBBI %u: Value=%X Flags=0x%02X Time=%" PRIu64 "\n", value->index, value->value, value->flags.value, value->time.value);
}
}
void handle_binary_output_status(dnp3_header_info_t info, dnp3_binary_output_status_iterator_t *it, void *arg)
{
printf("Binary Output Statuses:\n");
printf("Qualifier: %s \n", dnp3_qualifier_code_to_string(info.qualifier));
printf("Variation: %s \n", dnp3_variation_to_string(info.variation));
dnp3_binary_output_status_t *value = NULL;
while ((value = dnp3_binary_output_status_iterator_next(it))) {
printf("BOS %u: Value=%u Flags=0x%02X Time=%" PRIu64 "\n", value->index, value->value, value->flags.value, value->time.value);
}
}
void handle_counter(dnp3_header_info_t info, dnp3_counter_iterator_t *it, void *arg)
{
printf("Counters:\n");
printf("Qualifier: %s \n", dnp3_qualifier_code_to_string(info.qualifier));
printf("Variation: %s \n", dnp3_variation_to_string(info.variation));
dnp3_counter_t *value = NULL;
while ((value = dnp3_counter_iterator_next(it))) {
printf("Counter %u: Value=%u Flags=0x%02X Time=%" PRIu64 "\n", value->index, value->value, value->flags.value, value->time.value);
}
}
void handle_frozen_counter(dnp3_header_info_t info, dnp3_frozen_counter_iterator_t *it, void *arg)
{
printf("Frozen Counters:\n");
printf("Qualifier: %s \n", dnp3_qualifier_code_to_string(info.qualifier));
printf("Variation: %s \n", dnp3_variation_to_string(info.variation));
dnp3_frozen_counter_t *value = NULL;
while ((value = dnp3_frozen_counter_iterator_next(it))) {
printf("Frozen Counter %u: Value=%u Flags=0x%02X Time=%" PRIu64 "\n", value->index, value->value, value->flags.value, value->time.value);
}
}
void handle_analog_input(dnp3_header_info_t info, dnp3_analog_input_iterator_t *it, void *arg)
{
printf("Analogs:\n");
printf("Qualifier: %s \n", dnp3_qualifier_code_to_string(info.qualifier));
printf("Variation: %s \n", dnp3_variation_to_string(info.variation));
dnp3_analog_input_t *value = NULL;
while ((value = dnp3_analog_input_iterator_next(it))) {
printf("AI %u: Value=%f Flags=0x%02X Time=%" PRIu64 "\n", value->index, value->value, value->flags.value, value->time.value);
}
}
void handle_analog_output_status(dnp3_header_info_t info, dnp3_analog_output_status_iterator_t *it, void *arg)
{
printf("Analog Output Statuses:\n");
printf("Qualifier: %s \n", dnp3_qualifier_code_to_string(info.qualifier));
printf("Variation: %s \n", dnp3_variation_to_string(info.variation));
dnp3_analog_output_status_t *value = NULL;
while ((value = dnp3_analog_output_status_iterator_next(it))) {
printf("AOS %u: Value=%f Flags=0x%02X Time=%" PRIu64 "\n", value->index, value->value, value->flags.value, value->time.value);
}
}
void handle_octet_strings(dnp3_header_info_t info, dnp3_octet_string_iterator_t *it, void *arg)
{
printf("Octet Strings:\n");
printf("Qualifier: %s \n", dnp3_qualifier_code_to_string(info.qualifier));
printf("Variation: %s \n", dnp3_variation_to_string(info.variation));
dnp3_octet_string_t *value = NULL;
while ((value = dnp3_octet_string_iterator_next(it))) {
printf("Octet String: %u: Value=", value->index);
uint8_t *byte = dnp3_byte_iterator_next(value->value);
while (byte != NULL) {
printf("%02X", *byte);
byte = dnp3_byte_iterator_next(value->value);
}
printf("\n");
}
}
void handle_string_attr(dnp3_header_info_t info, dnp3_string_attr_t attr, uint8_t set, uint8_t variation, const char* value, void *arg)
{
printf("String attribute: %s set: %d var: %d value: %s \n", dnp3_string_attr_to_string(attr), set, variation, value);
}
class ReadHandler : public dnp3::ReadHandler {
void begin_fragment(dnp3::ReadType read_type, const dnp3::ResponseHeader& header) override
{
std::cout << "Begin fragment (broadcast: " << header.iin.iin1.broadcast << ")" << std::endl;
}
void end_fragment(dnp3::ReadType read_type, const dnp3::ResponseHeader& header) override
{
std::cout << "End fragment" << std::endl;
}
void handle_binary_input(const dnp3::HeaderInfo &info, dnp3::BinaryInputIterator &it) override
{
while (it.next()) {
const auto value = it.get();
std::cout << "BinaryInput(" << value.index << "): value: " << value.value << " flags: " << value.flags << " time: " << value.time.value << std::endl;
}
}
void handle_double_bit_binary_input(const dnp3::HeaderInfo &info, dnp3::DoubleBitBinaryInputIterator &it) override
{
while (it.next()) {
const auto value = it.get();
std::cout << "DoubleBitBinaryInput(" << value.index << "): value: " << dnp3::to_string(value.value) << " flags: " << value.flags << " time: " << value.time.value << std::endl;
}
}
void handle_binary_output_status(const dnp3::HeaderInfo& info, dnp3::BinaryOutputStatusIterator& it) override {
while (it.next()) {
const auto value = it.get();
std::cout << "BinaryOutputStatus(" << value.index << "): value: " << value.value << " flags: " << value.flags << " time: " << value.time.value << std::endl;
}
}
void handle_counter(const dnp3::HeaderInfo& info, dnp3::CounterIterator& it) override {
while (it.next()) {
const auto value = it.get();
std::cout << "Counter(" << value.index << "): value: " << value.value << " flags: " << value.flags << " time: " << value.time.value << std::endl;
}
}
void handle_frozen_counter(const dnp3::HeaderInfo& info, dnp3::FrozenCounterIterator& it) override {
while (it.next()) {
const auto value = it.get();
std::cout << "FrozenCounter(" << value.index << "): value: " << value.value << " flags: " << value.flags << " time: " << value.time.value << std::endl;
}
}
void handle_analog_input(const dnp3::HeaderInfo &info, dnp3::AnalogInputIterator &it) override
{
while (it.next()) {
const auto value = it.get();
std::cout << "AnalogInput(" << value.index << "): value: " << value.value << " flags: " << value.flags << " time: " << value.time.value << std::endl;
}
}
void handle_analog_output_status(const dnp3::HeaderInfo& info, dnp3::AnalogOutputStatusIterator& it) override {
while (it.next()) {
const auto value = it.get();
std::cout << "AnalogOutputStatus(" << value.index << "): value: " << value.value << " flags: " << value.flags << " time: " << value.time.value << std::endl;
}
}
void handle_octet_string(const dnp3::HeaderInfo& info, dnp3::OctetStringIterator& it) override {
while (it.next()) {
auto value = it.get();
std::cout << "OctetString(" << value.index << "): value: [";
bool first = false;
while (value.value.next()) {
const auto byte = value.value.get();
if (!first) {
std::cout << ",";
}
write_hex_byte(std::cout, byte);
first = false;
}
std::cout << "]" << std::endl;
}
}
void handle_string_attr(const dnp3::HeaderInfo &info, dnp3::StringAttr attr, uint8_t set, uint8_t variation, const char *value) override {
std::cout << std::dec << "String Attribute: " << dnp3::to_string(attr) << " set: " << (size_t)set << " var: " << (size_t)variation
<< " value: " << value
<< std::endl;
}
};
class TestReadHandler implements ReadHandler {
@Override
public void beginFragment(ReadType readType, ResponseHeader header) {
System.out.println("Beginning fragment (broadcast: " + header.iin.iin1.broadcast + ")");
}
@Override
public void endFragment(ReadType readType, ResponseHeader header) {
System.out.println("End fragment");
}
@Override
public void handleBinaryInput(HeaderInfo info, List<BinaryInput> it) {
System.out.println("Binary Inputs:");
System.out.println("Qualifier: " + info.qualifier);
System.out.println("Variation: " + info.variation);
it.forEach(
val -> {
System.out.println(
"BI "
+ val.index
+ ": Value="
+ val.value
+ " Flags="
+ val.flags.value
+ " Time="
+ val.time.value
+ " ("
+ val.time.quality
+ ")");
});
}
@Override
public void handleDoubleBitBinaryInput(HeaderInfo info, List<DoubleBitBinaryInput> it) {
System.out.println("Double Bit Binary Inputs:");
System.out.println("Qualifier: " + info.qualifier);
System.out.println("Variation: " + info.variation);
it.forEach(
val -> {
System.out.println(
"DBBI "
+ val.index
+ ": Value="
+ val.value
+ " Flags="
+ val.flags.value
+ " Time="
+ val.time.value
+ " ("
+ val.time.quality
+ ")");
});
}
@Override
public void handleBinaryOutputStatus(HeaderInfo info, List<BinaryOutputStatus> it) {
System.out.println("Binary Output Statuses:");
System.out.println("Qualifier: " + info.qualifier);
System.out.println("Variation: " + info.variation);
it.forEach(
val -> {
System.out.println(
"BOS "
+ val.index
+ ": Value="
+ val.value
+ " Flags="
+ val.flags.value
+ " Time="
+ val.time.value
+ " ("
+ val.time.quality
+ ")");
});
}
@Override
public void handleCounter(HeaderInfo info, List<Counter> it) {
System.out.println("Counters:");
System.out.println("Qualifier: " + info.qualifier);
System.out.println("Variation: " + info.variation);
it.forEach(
val -> {
System.out.println(
"Counter "
+ val.index
+ ": Value="
+ val.value
+ " Flags="
+ val.flags.value
+ " Time="
+ val.time.value
+ " ("
+ val.time.quality
+ ")");
});
}
@Override
public void handleFrozenCounter(HeaderInfo info, List<FrozenCounter> it) {
System.out.println("Frozen Counters:");
System.out.println("Qualifier: " + info.qualifier);
System.out.println("Variation: " + info.variation);
it.forEach(
val -> {
System.out.println(
"Frozen Counter "
+ val.index
+ ": Value="
+ val.value
+ " Flags="
+ val.flags.value
+ " Time="
+ val.time.value
+ " ("
+ val.time.quality
+ ")");
});
}
@Override
public void handleAnalogInput(HeaderInfo info, List<AnalogInput> it) {
System.out.println("Analog Inputs:");
System.out.println("Qualifier: " + info.qualifier);
System.out.println("Variation: " + info.variation);
it.forEach(
val -> {
System.out.println(
"AI "
+ val.index
+ ": Value="
+ val.value
+ " Flags="
+ val.flags.value
+ " Time="
+ val.time.value
+ " ("
+ val.time.quality
+ ")");
});
}
@Override
public void handleAnalogOutputStatus(HeaderInfo info, List<AnalogOutputStatus> it) {
System.out.println("Analog Output Statuses:");
System.out.println("Qualifier: " + info.qualifier);
System.out.println("Variation: " + info.variation);
it.forEach(
val -> {
System.out.println(
"AOS "
+ val.index
+ ": Value="
+ val.value
+ " Flags="
+ val.flags.value
+ " Time="
+ val.time.value
+ " ("
+ val.time.quality
+ ")");
});
}
@Override
public void handleOctetString(HeaderInfo info, List<OctetString> it) {
System.out.println("Octet Strings:");
System.out.println("Qualifier: " + info.qualifier);
System.out.println("Variation: " + info.variation);
it.forEach(
val -> {
System.out.print("Octet String " + val.index + ": Value=");
val.value.forEach(
b -> System.out.print(String.format("%02X", b.byteValue()) + " "));
System.out.println();
});
}
@Override
public void handleStringAttr(HeaderInfo info, StringAttr attr, UByte set, UByte variation, String value) {
System.out.printf("String attribute: %s set: %d var: %d value: %s%n", attr, set.intValue(), variation.intValue(), value);
}
}
class TestReadHandler : IReadHandler
{
public void BeginFragment(ReadType readType, ResponseHeader header)
{
Console.WriteLine($"Beginning fragment (broadcast: {header.Iin.Iin1.Broadcast})");
}
public void EndFragment(ReadType readType, ResponseHeader header)
{
Console.WriteLine("End fragment");
}
public void HandleBinaryInput(HeaderInfo info, ICollection<BinaryInput> values)
{
Console.WriteLine("Binary Inputs:");
Console.WriteLine("Qualifier: " + info.Qualifier);
Console.WriteLine("Variation: " + info.Variation);
foreach (var val in values)
{
Console.WriteLine($"BI {val.Index}: Value={val.Value} Flags={val.Flags.Value} Time={val.Time.Value} ({val.Time.Quality})");
}
}
public void HandleDoubleBitBinaryInput(HeaderInfo info, ICollection<DoubleBitBinaryInput> values)
{
Console.WriteLine("Double Bit Binary Inputs:");
Console.WriteLine("Qualifier: " + info.Qualifier);
Console.WriteLine("Variation: " + info.Variation);
foreach (var val in values)
{
Console.WriteLine($"DBBI {val.Index}: Value={val.Value} Flags={val.Flags.Value} Time={val.Time.Value} ({val.Time.Quality})");
}
}
public void HandleBinaryOutputStatus(HeaderInfo info, ICollection<BinaryOutputStatus> values)
{
Console.WriteLine("Binary Output Statuses:");
Console.WriteLine("Qualifier: " + info.Qualifier);
Console.WriteLine("Variation: " + info.Variation);
foreach (var val in values)
{
Console.WriteLine($"BOS {val.Index}: Value={val.Value} Flags={val.Flags.Value} Time={val.Time.Value} ({val.Time.Quality})");
}
}
public void HandleCounter(HeaderInfo info, ICollection<Counter> values)
{
Console.WriteLine("Counters:");
Console.WriteLine("Qualifier: " + info.Qualifier);
Console.WriteLine("Variation: " + info.Variation);
foreach (var val in values)
{
Console.WriteLine($"Counter {val.Index}: Value={val.Value} Flags={val.Flags.Value} Time={val.Time.Value} ({val.Time.Quality})");
}
}
public void HandleFrozenCounter(HeaderInfo info, ICollection<FrozenCounter> values)
{
Console.WriteLine("Frozen Counters:");
Console.WriteLine("Qualifier: " + info.Qualifier);
Console.WriteLine("Variation: " + info.Variation);
foreach (var val in values)
{
Console.WriteLine($"Frozen Counter {val.Index}: Value={val.Value} Flags={val.Flags.Value} Time={val.Time.Value} ({val.Time.Quality})");
}
}
public void HandleAnalogInput(HeaderInfo info, ICollection<AnalogInput> values)
{
Console.WriteLine("Analog Inputs:");
Console.WriteLine("Qualifier: " + info.Qualifier);
Console.WriteLine("Variation: " + info.Variation);
foreach (var val in values)
{
Console.WriteLine($"AI {val.Index}: Value={val.Value} Flags={val.Flags.Value} Time={val.Time.Value} ({val.Time.Quality})");
}
}
public void HandleFrozenAnalogInput(HeaderInfo info, ICollection<FrozenAnalogInput> values)
{
Console.WriteLine("Frozen Analog Inputs:");
Console.WriteLine("Qualifier: " + info.Qualifier);
Console.WriteLine("Variation: " + info.Variation);
foreach (var val in values)
{
Console.WriteLine($"AI {val.Index}: Value={val.Value} Flags={val.Flags.Value} Time={val.Time.Value} ({val.Time.Quality})");
}
}
public void HandleAnalogOutputStatus(HeaderInfo info, ICollection<AnalogOutputStatus> values)
{
Console.WriteLine("Analog Output Statuses:");
Console.WriteLine("Qualifier: " + info.Qualifier);
Console.WriteLine("Variation: " + info.Variation);
foreach (var val in values)
{
Console.WriteLine($"AOS {val.Index}: Value={val.Value} Flags={val.Flags.Value} Time={val.Time.Value} ({val.Time.Quality})");
}
}
public void HandleOctetString(HeaderInfo info, ICollection<OctetString> values)
{
Console.WriteLine("Octet Strings:");
Console.WriteLine("Qualifier: " + info.Qualifier);
Console.WriteLine("Variation: " + info.Variation);
foreach (var val in values)
{
Console.Write($"Octet String {val.Index}: Value=");
foreach (var b in val.Value)
{
Console.Write($"{b:X2} ");
}
Console.WriteLine();
}
}
void IReadHandler.HandleStringAttr(HeaderInfo info, StringAttr attr, byte set, byte var, string value)
{
Console.WriteLine($"Visible string attribute: {attr} set: {set} variation: {var} value: {value}");
}
void IReadHandler.HandleUintAttr(HeaderInfo info, UintAttr attr, byte set, byte var, uint value)
{
Console.WriteLine($"Unsigned integer attribute: {attr} set: {set} variation: {var} value: {value}");
}
void IReadHandler.HandleBoolAttr(HeaderInfo info, BoolAttr attr, byte set, byte var, bool value)
{
Console.WriteLine($"Boolean attribute: {attr} set: {set} variation: {var} value: {value}");
}
void IReadHandler.HandleIntAttr(HeaderInfo info, IntAttr attr, byte set, byte var, int value)
{
Console.WriteLine($"Int attribute: {attr} set: {set} variation: {var} value: {value}");
}
void IReadHandler.HandleTimeAttr(HeaderInfo info, TimeAttr attr, byte set, byte var, ulong value)
{
Console.WriteLine($"Time attribute: {attr} set: {set} variation: {var} value: {value}");
}
void IReadHandler.HandleFloatAttr(HeaderInfo info, FloatAttr attr, byte set, byte var, double value)
{
Console.WriteLine($"Float attribute: {attr} set: {set} variation: {var} value: {value}");
}
void IReadHandler.HandleVariationListAttr(HeaderInfo info, VariationListAttr attr, byte set, byte var, ICollection<AttrItem> value)
{
Console.WriteLine($"Attribute variation list: {attr} set: {set} variation: {var}");
foreach(var item in value) {
Console.WriteLine($"variation: {item.Variation} writable: {item.Properties.IsWritable}");
}
}
void IReadHandler.HandleOctetStringAttr(HeaderInfo info, OctetStringAttr attr, byte set, byte var, ICollection<byte> value)
{
Console.WriteLine($"Octet-string attribute: {attr} set: {set} variation: {var} length: {value.Count}");
}
void IReadHandler.HandleBitStringAttr(HeaderInfo info, BitStringAttr attr, byte set, byte var, ICollection<byte> value)
{
Console.WriteLine($"Bit-string attribute: {attr} set: {set} variation: {var} length: {value.Count}");
}
}