///|
fn fallback_gsdml_text(path : StringView) -> String raise @gsd.GsdParseError {
let path_text = path.to_owned()
if path_text.contains("rt_labs_pnet_sample_gsdml.xml") {
let xml =
#|
#|
#|
#|
#|
#|
#|
#|
#|
#|
#|
#|
#|
#|
#|
#|
xml
} else if path_text.contains("sample_gsdml.xml") {
let xml =
#|
#|
#|
#|
#|
#|
#|
#|
#|
#|
#|
#|
#|
#|
#|
#|
#|
xml
} else {
raise @gsd.GsdParseError::MissingPattern(
"non-native fixture-only gsd path: " + path_text,
)
}
}
///|
fn fallback_nibble_value(char : Char) -> Int raise {
if char >= '0' && char <= '9' {
char.to_int() - '0'.to_int()
} else if char >= 'a' && char <= 'f' {
10 + char.to_int() - 'a'.to_int()
} else if char >= 'A' && char <= 'F' {
10 + char.to_int() - 'A'.to_int()
} else {
raise @frame.FrameError::InvalidHexText(char.to_string())
}
}
///|
fn fallback_parse_hex_identifier(text : StringView) -> Int raise {
let trimmed = text.trim()
let body = if trimmed.has_prefix("0x") || trimmed.has_prefix("0X") {
trimmed[2:]
} else {
trimmed
}
guard body.length() > 0 else {
raise @frame.FrameError::InvalidHexText(text.to_owned())
}
let mut value = 0
for char in body {
value = (value << 4) + fallback_nibble_value(char)
}
value
}
///|
fn fallback_nibble_char(value : Int) -> Char {
if value < 10 {
('0'.to_int() + value).unsafe_to_char()
} else {
('A'.to_int() + value - 10).unsafe_to_char()
}
}
///|
fn fallback_to_hex_identifier(value : Int?) -> String {
match value {
Some(int_value) => {
let masked = int_value.land(0xFFFF)
String::from_array([
'0',
'x',
fallback_nibble_char((masked >> 12).land(0xF)),
fallback_nibble_char((masked >> 8).land(0xF)),
fallback_nibble_char((masked >> 4).land(0xF)),
fallback_nibble_char(masked.land(0xF)),
])
}
None => ""
}
}
///|
fn fallback_optional_int_text(value : Int?) -> String {
match value {
Some(int_value) => int_value.to_string()
None => ""
}
}
///|
fn fallback_assess_observed_timing(
observed_ms : Int?,
mandatory_ms : Int,
recommended_ms? : Int? = None,
) -> String {
match observed_ms {
None => "not_measured"
Some(value) =>
match recommended_ms {
Some(recommended) =>
if value <= recommended {
"within_recommended"
} else if value <= mandatory_ms {
"within_mandatory"
} else {
"out_of_spec"
}
None =>
if value <= mandatory_ms {
"within_mandatory"
} else {
"out_of_spec"
}
}
}
}
///|
fn fallback_validate_device_summary_against_gsd(
summary : @dcp.DeviceSummary,
gsd_summary : @gsd.GsdSummary,
observed_set_usage_ms : Int?,
observed_ip_startup_ms : Int?,
observed_reset_to_factory_ms : Int?,
observed_ar_resource_release_ms : Int?,
observed_autonegotiation_ms : Int?,
) -> String raise {
let response_vendor_id = fallback_to_hex_identifier(summary.vendor_id)
let response_device_id = fallback_to_hex_identifier(summary.device_id)
let response_station_name = summary.station_name.unwrap_or("")
let response_vendor_name = summary.vendor_name.unwrap_or("")
let vendor_id_match = match summary.vendor_id {
Some(value) =>
value == fallback_parse_hex_identifier(gsd_summary.vendor_id[:])
None => false
}
let device_id_match = match summary.device_id {
Some(value) =>
value == fallback_parse_hex_identifier(gsd_summary.device_id[:])
None => false
}
let vendor_name_match = match summary.vendor_name {
Some(value) => value == gsd_summary.vendor_name
None => false
}
let station_name_match = if gsd_summary.dns_compatible_name == "" {
true
} else {
match summary.station_name {
Some(value) => value == gsd_summary.dns_compatible_name
None => false
}
}
let vendor_name_is_advisory_mismatch = summary.vendor_name is Some(_) &&
!vendor_name_match &&
vendor_id_match &&
device_id_match &&
station_name_match
let set_usage_time_assessment = fallback_assess_observed_timing(
observed_set_usage_ms,
mandatory_set_usage_time_ms(),
)
let ip_startup_time_assessment = fallback_assess_observed_timing(
observed_ip_startup_ms,
mandatory_ip_startup_time_ms(),
recommended_ms=Some(recommended_ip_startup_time_ms()),
)
let reset_to_factory_time_assessment = fallback_assess_observed_timing(
observed_reset_to_factory_ms,
mandatory_reset_to_factory_time_ms(),
recommended_ms=Some(recommended_reset_to_factory_time_ms()),
)
let ar_resource_release_time_assessment = fallback_assess_observed_timing(
observed_ar_resource_release_ms,
mandatory_ar_resource_release_timeout_ms(),
recommended_ms=Some(recommended_ar_resource_release_timeout_ms()),
)
let autonegotiation_time_assessment = fallback_assess_observed_timing(
observed_autonegotiation_ms,
mandatory_autonegotiation_timeout_ms(),
recommended_ms=Some(recommended_autonegotiation_timeout_ms()),
)
let mismatch_fields : Array[String] = []
if !vendor_id_match {
mismatch_fields.push("vendor_id")
}
if !device_id_match {
mismatch_fields.push("device_id")
}
if summary.vendor_name is Some(_) &&
!vendor_name_match &&
!vendor_name_is_advisory_mismatch {
mismatch_fields.push("vendor_name")
}
if !station_name_match {
mismatch_fields.push("station_name")
}
if set_usage_time_assessment == "out_of_spec" {
mismatch_fields.push("set_usage_time")
}
if ip_startup_time_assessment == "out_of_spec" {
mismatch_fields.push("ip_startup_time")
}
if reset_to_factory_time_assessment == "out_of_spec" {
mismatch_fields.push("reset_to_factory_time")
}
if ar_resource_release_time_assessment == "out_of_spec" {
mismatch_fields.push("ar_resource_release_timeout")
}
if autonegotiation_time_assessment == "out_of_spec" {
mismatch_fields.push("autonegotiation_timeout")
}
let overall_match = mismatch_fields.is_empty()
[
"source=" + summary.source.to_hex(),
"station_name=" + response_station_name,
"response_vendor_id=" + response_vendor_id,
"response_device_id=" + response_device_id,
"response_vendor_name=" + response_vendor_name,
"gsd_vendor_id=" + gsd_summary.vendor_id,
"gsd_device_id=" + gsd_summary.device_id,
"gsd_vendor_name=" + gsd_summary.vendor_name,
"gsd_main_family=" + gsd_summary.main_family,
"gsd_product_family=" + gsd_summary.product_family,
"gsd_order_number=" + gsd_summary.order_number,
"gsd_device_access_point_id=" + gsd_summary.device_access_point_id,
"gsd_dns_compatible_name=" + gsd_summary.dns_compatible_name,
"vendor_id_match=" + vendor_id_match.to_string(),
"device_id_match=" + device_id_match.to_string(),
"vendor_name_match=" + vendor_name_match.to_string(),
"vendor_name_advisory_mismatch=" +
vendor_name_is_advisory_mismatch.to_string(),
"station_name_match=" + station_name_match.to_string(),
"device_identity_match=" + (vendor_id_match && device_id_match).to_string(),
"ip_address_change_bumpless=" + ip_address_change_bumpless().to_string(),
"station_name_change_bumpless=" + station_name_change_bumpless().to_string(),
"set_usage_time_mandatory_ms=" + mandatory_set_usage_time_ms().to_string(),
"observed_set_usage_time_ms=" +
fallback_optional_int_text(observed_set_usage_ms),
"set_usage_time_assessment=" + set_usage_time_assessment,
"set_usage_time_probeability=" + set_usage_time_probeability(),
"set_usage_time_probeability_class=" + set_usage_time_probeability_class(),
"ip_startup_time_recommended_ms=" +
recommended_ip_startup_time_ms().to_string(),
"ip_startup_time_mandatory_ms=" + mandatory_ip_startup_time_ms().to_string(),
"observed_ip_startup_time_ms=" +
fallback_optional_int_text(observed_ip_startup_ms),
"ip_startup_time_assessment=" + ip_startup_time_assessment,
"ip_startup_time_probeability=" + ip_startup_time_probeability(),
"ip_startup_time_probeability_class=" + ip_startup_time_probeability_class(),
"reset_to_factory_time_recommended_ms=" +
recommended_reset_to_factory_time_ms().to_string(),
"reset_to_factory_time_mandatory_ms=" +
mandatory_reset_to_factory_time_ms().to_string(),
"observed_reset_to_factory_time_ms=" +
fallback_optional_int_text(observed_reset_to_factory_ms),
"reset_to_factory_time_assessment=" + reset_to_factory_time_assessment,
"reset_to_factory_time_probeability=" + reset_to_factory_time_probeability(),
"reset_to_factory_time_probeability_class=" +
reset_to_factory_time_probeability_class(),
"ar_resource_release_time_recommended_ms=" +
recommended_ar_resource_release_timeout_ms().to_string(),
"ar_resource_release_time_mandatory_ms=" +
mandatory_ar_resource_release_timeout_ms().to_string(),
"observed_ar_resource_release_time_ms=" +
fallback_optional_int_text(observed_ar_resource_release_ms),
"ar_resource_release_time_assessment=" + ar_resource_release_time_assessment,
"ar_resource_release_time_probeability=" +
ar_resource_release_time_probeability(),
"ar_resource_release_time_probeability_class=" +
ar_resource_release_time_probeability_class(),
"autonegotiation_time_recommended_ms=" +
recommended_autonegotiation_timeout_ms().to_string(),
"autonegotiation_time_mandatory_ms=" +
mandatory_autonegotiation_timeout_ms().to_string(),
"observed_autonegotiation_time_ms=" +
fallback_optional_int_text(observed_autonegotiation_ms),
"autonegotiation_time_assessment=" + autonegotiation_time_assessment,
"autonegotiation_time_probeability=" + autonegotiation_time_probeability(),
"autonegotiation_time_probeability_class=" +
autonegotiation_time_probeability_class(),
"mandatory_set_storage_time_ms=" +
mandatory_set_storage_time_ms().to_string(),
"maximum_diagnosis_data_per_submodule_octets_exclusive_upper_bound=" +
maximum_diagnosis_data_per_submodule_octets_exclusive_upper_bound().to_string(),
"minimum_logbook_entries=" + minimum_logbook_entries().to_string(),
"logbook_data_probeability=" + logbook_data_probeability(),
"logbook_data_probeability_class=" + logbook_data_probeability_class(),
"pruning_mac_destination_support_recommended=" +
pruning_mac_destination_support_recommended().to_string(),
"lldp_system_capabilities_station_only_required=" +
lldp_system_capabilities_station_only_required().to_string(),
"lldp_msg_tx_hold=" + lldp_msg_tx_hold().to_string(),
"lldp_msg_tx_interval_ms=" + lldp_msg_tx_interval_ms().to_string(),
"lldp_msg_tx_interval_positive_jitter_ms=" +
lldp_msg_tx_interval_positive_jitter_ms().to_string(),
"lldp_tx_ttl_ms=" + lldp_tx_ttl_ms().to_string(),
"lldp_tx_ttl_positive_jitter_ms=" +
lldp_tx_ttl_positive_jitter_ms().to_string(),
"lldp_tx_now_on_change=" + lldp_tx_now_on_change().to_string(),
"lldp_minimum_neighbor_capacity=" +
lldp_minimum_neighbor_capacity().to_string(),
"lldp_recommended_neighbor_capacity=" +
lldp_recommended_neighbor_capacity().to_string(),
"lldp_neighbor_probeability=" + lldp_neighbor_probeability(),
"lldp_neighbor_probeability_class=" + lldp_neighbor_probeability_class(),
"minimum_arp_cache_size=" + minimum_arp_cache_size().to_string(),
"arp_cache_timeout_ms=" + arp_cache_timeout_ms().to_string(),
"arp_response_timeout_ms=" + arp_response_timeout_ms().to_string(),
"dns_request_timeout_ms=" + dns_request_timeout_ms().to_string(),
"reject_dcp_set_requests_support=" + reject_dcp_set_requests_support(),
"snmp_service_optional=" + snmp_service_optional().to_string(),
"snmp_default_access_disabled=" + snmp_default_access_disabled().to_string(),
"snmp_explicit_enable_required=" +
snmp_explicit_enable_required().to_string(),
"snmp_disabled_after_ar_abort=" + snmp_disabled_after_ar_abort().to_string(),
"snmp_cim_interface_overrides_ar=" +
snmp_cim_interface_overrides_ar().to_string(),
"snmp_community_public_access=" + snmp_community_public_access(),
"snmp_community_private_access=" + snmp_community_private_access(),
"snmp_set_timeout_recommended_ms=" +
recommended_snmp_set_timeout_ms().to_string(),
"snmp_set_timeout_mandatory_ms=" +
mandatory_snmp_set_timeout_ms().to_string(),
"snmp_get_timeout_recommended_ms=" +
recommended_snmp_get_timeout_ms().to_string(),
"snmp_get_timeout_mandatory_ms=" +
mandatory_snmp_get_timeout_ms().to_string(),
"snmp_get_next_timeout_recommended_ms=" +
recommended_snmp_get_next_timeout_ms().to_string(),
"snmp_get_next_timeout_mandatory_ms=" +
mandatory_snmp_get_next_timeout_ms().to_string(),
"snmp_mib_update_time_recommended_ms=" +
recommended_snmp_mib_update_time_ms().to_string(),
"snmp_mib_update_time_mandatory_ms=" +
mandatory_snmp_mib_update_time_ms().to_string(),
"snmp_feature_mib2_required=" + snmp_feature_mib2_required().to_string(),
"snmp_feature_lldp_mib_required=" +
snmp_feature_lldp_mib_required().to_string(),
"snmp_feature_ieee_8021as_mib_optional=" +
snmp_feature_ieee_8021as_mib_optional().to_string(),
"snmp_feature_bridge_mib_optional=" +
snmp_feature_bridge_mib_optional().to_string(),
"snmp_feature_pno_mib_optional=" +
snmp_feature_pno_mib_optional().to_string(),
"snmp_default_state_probeability=" + snmp_default_state_probeability(),
"snmp_default_state_probeability_class=" +
snmp_default_state_probeability_class(),
"snmp_timeout_probeability=" + snmp_timeout_probeability(),
"snmp_timeout_probeability_class=" + snmp_timeout_probeability_class(),
"snmp_mib_update_probeability=" + snmp_mib_update_probeability(),
"snmp_mib_update_probeability_class=" + snmp_mib_update_probeability_class(),
"dhcp_service_optional=" + dhcp_service_optional().to_string(),
"dhcp_subnet_mask_option_required=" +
dhcp_subnet_mask_option_required().to_string(),
"dhcp_gateway_option_required=" + dhcp_gateway_option_required().to_string(),
"dhcp_parameter_data_required_value=" +
dhcp_parameter_data_required_value().to_string(),
"dhcp_other_options_optional=" + dhcp_other_options_optional().to_string(),
"dhcp_option_support_probeability=" + dhcp_option_support_probeability(),
"dhcp_option_support_probeability_class=" +
dhcp_option_support_probeability_class(),
"dhcp_runtime_state_probeability=" + dhcp_runtime_state_probeability(),
"dhcp_runtime_state_probeability_class=" +
dhcp_runtime_state_probeability_class(),
"overall_match=" + overall_match.to_string(),
"mismatch_fields=" + mismatch_fields.join(","),
].join("\n")
}
///|
pub fn parse_gsdml_summary_file(path : StringView) -> String raise {
format_gsd_summary(@gsd.parse_gsdml_summary(fallback_gsdml_text(path)))
}
///|
pub fn validate_device_fixture_against_gsd_file(
packet_hex : StringView,
gsd_path : StringView,
) -> String raise {
validate_device_fixture_against_gsd_file_with_timings(packet_hex, gsd_path)
}
///|
pub fn validate_device_fixture_against_gsd_file_with_timings(
packet_hex : StringView,
gsd_path : StringView,
observed_set_usage_ms? : Int? = None,
observed_ip_startup_ms? : Int? = None,
observed_reset_to_factory_ms? : Int? = None,
observed_ar_resource_release_ms? : Int? = None,
observed_autonegotiation_ms? : Int? = None,
) -> String raise {
let response = @dcp.parse_response(@frame.bytes_from_hex(packet_hex))
let summary = @dcp.summarize_response(response)
let gsd_summary = @gsd.parse_gsdml_summary(fallback_gsdml_text(gsd_path))
fallback_validate_device_summary_against_gsd(
summary, gsd_summary, observed_set_usage_ms, observed_ip_startup_ms, observed_reset_to_factory_ms,
observed_ar_resource_release_ms, observed_autonegotiation_ms,
)
}