Skip to main content

rusty_iccp/
lib.rs

1pub mod error;
2
3use async_trait::async_trait;
4
5use error::*;
6use num_bigint::BigInt;
7use rusty_mms::{ListOfVariablesItem, MmsAccessError, MmsBasicObjectClass, MmsObjectClass, MmsObjectName, MmsObjectScope, MmsVariableAccessSpecification, VariableSpecification};
8use rusty_mms_service::{
9    RustyMmsServiceClient,
10    data::{MmsServiceAccessResult, MmsServiceData, MmsServiceDeleteObjectScope},
11};
12
13#[async_trait]
14pub trait IccpClient: Send + Sync {
15    // --- Completed ---
16    // get_data_values
17    // get_data_set_names
18    // create_data_set
19    // delete_data_set
20    // get_data_value_names
21
22    // --- Required ---
23    // set_data_values
24    // get_data_value_types
25
26    // get_data_set_element_names - This is a dataset operation
27    // get_data_set_element_values - This is a dataset operation
28    // set_data_set_element_values - This is a dataset operation
29
30    // start_transfer - This is a dataset operation
31    // stop_transfer - Drop
32    // get_next_ds_transfer_set_value - Hide This
33
34    // select
35    // operate
36    // get_tag_value
37    // set_tag_value
38
39    // fn fetch_transfer_report
40}
41
42pub enum QualityFlag {
43    Bit0,
44    Bit1,
45    Bit2,
46    Bit3,
47    Bit4,
48    Bit5,
49    Bit6,
50    Bit7,
51}
52
53pub enum IccpData {
54    RealQ(f32, Vec<QualityFlag>),
55}
56
57pub enum IccpAccessResult {
58    Success(IccpData),
59    Failure(MmsAccessError),
60}
61
62pub enum IccpScope {
63    Vcc,
64    ICC(String), // Domain
65}
66
67#[async_trait]
68pub trait IccpServer: Send + Sync + Clone {
69    // fn send_transfer_report
70}
71
72pub struct RustyIccpClient {
73    mms_client: Box<dyn RustyMmsServiceClient>,
74}
75
76impl RustyIccpClient {
77    pub fn new(mms_client: Box<dyn RustyMmsServiceClient>) -> Self {
78        RustyIccpClient { mms_client }
79    }
80
81    pub async fn get_data_values(&mut self, names: Vec<String>) -> Result<Vec<IccpAccessResult>, IccpError> {
82        let spec = names.into_iter().map(|x| ListOfVariablesItem { variable_specification: VariableSpecification::Name(MmsObjectName::VmdSpecific(x)) }).collect();
83        let results = self.mms_client.read(rusty_mms::MmsVariableAccessSpecification::ListOfVariables(spec)).await?;
84
85        Ok(results
86            .into_iter()
87            .map(|x| match x {
88                MmsServiceAccessResult::Failure(x) => Ok(IccpAccessResult::Failure(x)),
89                MmsServiceAccessResult::Success(x) => Ok(IccpAccessResult::Success(convert_mms_service_data_to_iccp_data(x)?)),
90            })
91            .collect::<Result<Vec<IccpAccessResult>, IccpError>>()?)
92    }
93
94    pub async fn get_data_set_names(&mut self, scope: IccpScope) -> Result<Vec<String>, IccpError> {
95        let mms_scope = match scope {
96            IccpScope::Vcc => MmsObjectScope::Vmd,
97            IccpScope::ICC(x) => MmsObjectScope::Domain(x),
98        };
99
100        let mut full_results = vec![];
101        let mut continue_after = None;
102        loop {
103            let results = self.mms_client.get_name_list(MmsObjectClass::Basic(MmsBasicObjectClass::NamedVariableList), mms_scope.clone(), continue_after).await?;
104            let last_result = results.identifiers.last().cloned();
105            full_results.extend(results.identifiers);
106
107            if !results.more_follows {
108                return Ok(full_results);
109            }
110            continue_after = Some(last_result.ok_or_else(|| IccpError::ProtocolError("No results to choose from.".into()))?)
111        }
112    }
113
114    pub async fn get_data_value_names(&mut self, scope: IccpScope) -> Result<Vec<String>, IccpError> {
115        let mms_scope = match scope {
116            IccpScope::Vcc => MmsObjectScope::Vmd,
117            IccpScope::ICC(x) => MmsObjectScope::Domain(x),
118        };
119
120        let mut full_results = vec![];
121        let mut continue_after = None;
122        loop {
123            let results = self.mms_client.get_name_list(MmsObjectClass::Basic(MmsBasicObjectClass::NamedVariable), mms_scope.clone(), continue_after).await?;
124            let last_result = results.identifiers.last().cloned();
125            full_results.extend(results.identifiers);
126
127            if !results.more_follows {
128                return Ok(full_results);
129            }
130            continue_after = Some(last_result.ok_or_else(|| IccpError::ProtocolError("No results to choose from.".into()))?)
131        }
132    }
133
134    pub async fn create_data_set(&mut self, domain: String, name: String, identifiers: Vec<String>) -> Result<(), IccpError> {
135        self.mms_client
136            .define_named_variable_list(MmsObjectName::DomainSpecific(domain, name), identifiers.into_iter().map(|x| ListOfVariablesItem { variable_specification: VariableSpecification::Name(MmsObjectName::VmdSpecific(x)) }).collect())
137            .await?;
138        Ok(())
139    }
140
141    pub async fn delete_data_sets(&mut self, domain: String, identifiers: Vec<String>) -> Result<(), IccpError> {
142        self.mms_client.delete_named_variable_list(MmsServiceDeleteObjectScope::Specific(identifiers.into_iter().map(|x| MmsObjectName::DomainSpecific(domain.clone(), x)).collect())).await?;
143        Ok(())
144    }
145
146    pub async fn delete_domain_data_sets(&mut self, domain: String) -> Result<(), IccpError> {
147        self.mms_client.delete_named_variable_list(MmsServiceDeleteObjectScope::Domain(domain)).await?;
148        Ok(())
149    }
150
151    pub async fn start_transfer_set(&mut self, domain: String, name: String) -> Result<(), IccpError> {
152        let transfer_set_name_structure = self
153            .mms_client
154            .read(MmsVariableAccessSpecification::ListOfVariables(vec![ListOfVariablesItem { variable_specification: VariableSpecification::Name(MmsObjectName::DomainSpecific(domain.clone(), "Next_DSTransfer_Set".into())) }]))
155            .await?;
156
157        let transfer_set_name = match transfer_set_name_structure.as_slice() {
158            [MmsServiceAccessResult::Success(MmsServiceData::Structure(x))] => match x.as_slice() {
159                [MmsServiceData::Integer(_scope), MmsServiceData::VisibleString(_domain), MmsServiceData::VisibleString(transfer_set_name)] => transfer_set_name,
160                _ => return Err(IccpError::ProtocolError("Failed to get transfer set name".into())),
161            },
162            _ => return Err(IccpError::ProtocolError("Failed to get transfer set name".into())),
163        };
164
165        self.mms_client
166            .write(
167                MmsVariableAccessSpecification::ListOfVariables(vec![ListOfVariablesItem { variable_specification: VariableSpecification::Name(MmsObjectName::DomainSpecific(domain.clone(), transfer_set_name.clone())) }]),
168                vec![
169                    MmsServiceData::Structure(vec![MmsServiceData::Structure(vec![
170                        MmsServiceData::Integer(BigInt::from(1)),
171                        MmsServiceData::VisibleString(domain.clone()),
172                        MmsServiceData::VisibleString(name),
173                    ])]),
174                    MmsServiceData::Integer(BigInt::from(0)),                        // Start Time
175                    MmsServiceData::Integer(BigInt::from(0)),                        // Interval
176                    MmsServiceData::Integer(BigInt::from(0)),                        // TLE
177                    MmsServiceData::Integer(BigInt::from(10)),                       // Buffer Time
178                    MmsServiceData::Integer(BigInt::from(600)),                      // Integrity Check
179                    MmsServiceData::BitString(vec![false, true, true, true, false]), // Interval Timeout: false, Integrity Timeout: True, Object Change: True, Operator Request: true, Other External Event: false
180                    MmsServiceData::Boolean(false),                                  // Block Data
181                    MmsServiceData::Boolean(false),                                  // Critical
182                    MmsServiceData::Boolean(true),                                   // Report By Exception
183                    MmsServiceData::Boolean(false),                                  // All Changes Reported
184                    MmsServiceData::Boolean(true),                                   // Status
185                    MmsServiceData::Integer(BigInt::from(0)),                        // Event Code Requested
186                ],
187            )
188            .await?;
189        Ok(())
190    }
191}
192
193fn convert_mms_service_data_to_iccp_data(mms_data: MmsServiceData) -> Result<IccpData, IccpError> {
194    match mms_data {
195        MmsServiceData::Structure(struct_data) => match struct_data.as_slice() {
196            [MmsServiceData::FloatingPoint(value), MmsServiceData::BitString(_)] => Ok(IccpData::RealQ(value.to_f32()?, vec![])),
197            x => Err(IccpError::ProtocolError(format!("Unknown MMS Structure Data: {x:?}"))),
198        },
199        x => Err(IccpError::ProtocolError(format!("Unknown MMS Data: {x:?}"))),
200    }
201}