///|
/// A station profile exchanged by deployment configuration and test tools.
pub struct StationProfile {
name : String
common_address : CommonAddress
parameters : ConnectionParameters
supported_types : Array[ApplicationType]
max_objects : Int
tls_required : Bool
} derive(Debug)
///|
pub fn StationProfile::new(
name : String,
common_address : CommonAddress,
parameters : ConnectionParameters,
supported_types : Array[ApplicationType],
max_objects? : Int = 127,
tls_required? : Bool = false,
) -> Result[StationProfile, String] {
if name == "" {
Err("station profile name must not be empty")
} else if max_objects < 1 || max_objects > 127 {
Err("station profile object limit is outside 1..127")
} else {
Ok({
name,
common_address,
parameters,
supported_types,
max_objects,
tls_required,
})
}
}
///|
pub fn StationProfile::name(self : StationProfile) -> String {
self.name
}
///|
pub fn StationProfile::common_address(self : StationProfile) -> CommonAddress {
self.common_address
}
///|
pub fn StationProfile::parameters(
self : StationProfile,
) -> ConnectionParameters {
self.parameters
}
///|
pub fn StationProfile::supports(
self : StationProfile,
type_id : ApplicationType,
) -> Bool {
for supported in self.supported_types {
if supported == type_id {
return true
}
}
false
}
///|
pub fn StationProfile::supported_types(
self : StationProfile,
) -> Array[ApplicationType] {
self.supported_types.copy()
}
///|
pub fn StationProfile::max_objects(self : StationProfile) -> Int {
self.max_objects
}
///|
pub fn StationProfile::tls_required(self : StationProfile) -> Bool {
self.tls_required
}
///|
pub enum NegotiationDecision {
CompatibleProfile
IncompatibleProfile(Array[String])
} derive(Debug)
///|
pub fn negotiate_profiles(
local_profile : StationProfile,
remote : StationProfile,
) -> NegotiationDecision {
let issues : Array[String] = []
if local_profile.parameters().k() > remote.parameters().k() {
issues.push("local k exceeds remote capacity")
}
if local_profile.parameters().w() > remote.parameters().w() {
issues.push("local w exceeds remote capacity")
}
if local_profile.max_objects() > remote.max_objects() {
issues.push("local object batch exceeds remote capacity")
}
for type_id in local_profile.supported_types() {
if !remote.supports(type_id) {
issues.push("remote does not support \{type_name(type_id)}")
}
}
if issues.is_empty() {
CompatibleProfile
} else {
IncompatibleProfile(issues)
}
}
///|
pub struct ProfileRegistry {
profiles : Map[String, StationProfile]
} derive(Debug)
///|
pub fn ProfileRegistry::new() -> ProfileRegistry {
{ profiles: {} }
}
///|
pub fn ProfileRegistry::put(
self : ProfileRegistry,
profile : StationProfile,
) -> Result[Unit, String] {
if self.profiles.get(profile.name()) is Some(_) {
Err("station profile already exists")
} else {
self.profiles[profile.name()] = profile
Ok(())
}
}
///|
pub fn ProfileRegistry::replace(
self : ProfileRegistry,
profile : StationProfile,
) -> Unit {
self.profiles[profile.name()] = profile
}
///|
pub fn ProfileRegistry::get(
self : ProfileRegistry,
name : String,
) -> StationProfile? {
self.profiles.get(name)
}
///|
pub fn ProfileRegistry::remove(self : ProfileRegistry, name : String) -> Bool {
if self.profiles.get(name) is Some(_) {
self.profiles.remove(name)
true
} else {
false
}
}
///|
pub fn ProfileRegistry::len(self : ProfileRegistry) -> Int {
self.profiles.length()
}
///|
pub fn ProfileRegistry::names(self : ProfileRegistry) -> Array[String] {
let result : Array[String] = []
for name, _ in self.profiles {
result.push(name)
}
result.sort()
result
}
///|
pub enum TransportMode {
Tcp
SerialGateway
ReplayFile
InMemory
} derive(Eq, Debug)
///|
pub fn transport_mode_examples() -> Array[TransportMode] {
[Tcp, SerialGateway, ReplayFile, InMemory]
}
///|
pub fn profile_examples() -> Array[StationProfile] {
[
StationProfile::new(
"demo",
CommonAddress::new(1).unwrap(),
ConnectionParameters::default(),
[MSpNa, MDpNa, MMeNa, CScNa],
).unwrap(),
]
}