///|
/// Operational profile around a calibrated device record.
pub(all) struct DeviceProfile {
device : Device
calibration_date : String
calibration_expiry : String
owner : String
}
///|
/// Create a device profile with ISO-8601 date strings.
pub fn device_profile(
device~ : Device,
calibration_date~ : String,
calibration_expiry~ : String,
owner~ : String,
) -> DeviceProfile {
{ device, calibration_date, calibration_expiry, owner }
}
///|
fn date_not_after(left : String, right : String) -> Bool {
left == right || left < right
}
///|
/// Return whether calibration covers a requested date.
pub fn calibration_is_current(profile : DeviceProfile, date : String) -> Bool {
date_not_after(profile.calibration_date, date) &&
date_not_after(date, profile.calibration_expiry)
}
///|
/// Return a stable calibration status label.
pub fn device_profile_status(profile : DeviceProfile, date : String) -> String {
if profile.calibration_date.trim().length() == 0 ||
profile.calibration_expiry.trim().length() == 0 {
"unknown"
} else if calibration_is_current(profile, date) {
"calibrated"
} else {
"expired"
}
}
///|
/// Return whether the profiled device can measure a feature.
pub fn device_can_measure_with_profile(
profile : DeviceProfile,
feature : Feature,
) -> Bool {
profile.device.can_measure(feature)
}
///|
/// Count catalog features available to a profile.
pub fn profile_capacity(profile : DeviceProfile, catalog : Catalog) -> Int {
supported_feature_count(profile.device, catalog.features)
}
///|
/// Return whether a profile covers every catalog feature.
pub fn device_can_cover_catalog(
profile : DeviceProfile,
catalog : Catalog,
) -> Bool {
profile_capacity(profile, catalog) == catalog.features.length()
}
///|
/// Return the number of supported feature kinds.
pub fn profile_supported_kind_count(profile : DeviceProfile) -> Int {
profile.device.supported_kinds.length()
}
///|
/// Return the reach margin for a feature.
pub fn profile_reach_margin(
profile : DeviceProfile,
feature : Feature,
) -> Double {
profile.device.reach - feature.nominal.abs()
}
///|
/// Return an accuracy rank where lower is better.
pub fn profile_accuracy_rank(profile : DeviceProfile) -> Double {
profile.device.accuracy
}
///|
/// Return whether the profile has positive physical capabilities.
pub fn profile_is_usable(profile : DeviceProfile) -> Bool {
profile.device.accuracy > 0.0 &&
profile.device.reach > 0.0 &&
profile.device.supported_kinds.length() > 0
}
///|
/// Return whether the profile needs recalibration before a date.
pub fn profile_needs_recalibration(
profile : DeviceProfile,
date : String,
) -> Bool {
!calibration_is_current(profile, date)
}
///|
/// Return the list of unsupported catalog feature IDs.
pub fn profile_unsupported_ids(
profile : DeviceProfile,
catalog : Catalog,
) -> Array[String] {
let result = []
for feature in catalog.features {
if !profile.device.can_measure(feature) {
result.push(feature.id)
}
}
result
}
///|
/// Render a stable device profile summary.
pub fn profile_summary(profile : DeviceProfile, date : String) -> String {
"device=" +
profile.device.id +
" owner=" +
profile.owner +
" status=" +
device_profile_status(profile, date) +
" accuracy=" +
profile.device.accuracy.to_string() +
" reach=" +
profile.device.reach.to_string() +
" kinds=" +
profile.device.supported_kinds.length().to_string()
}
///|
/// Return whether the profile is both usable and currently calibrated.
pub fn profile_is_ready(profile : DeviceProfile, date : String) -> Bool {
profile_is_usable(profile) && calibration_is_current(profile, date)
}
///|
/// Return a warning string for a profile/date pair.
pub fn profile_warning(profile : DeviceProfile, date : String) -> String {
if !profile_is_usable(profile) {
"device capabilities are invalid"
} else if !calibration_is_current(profile, date) {
"device calibration is expired"
} else {
""
}
}