-
Notifications
You must be signed in to change notification settings - Fork 10
Update the configure logic #48
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,7 @@ use argh::FromArgs; | |
| use env_logger::{Builder, Env}; | ||
| use std::io::Write; | ||
| use std::net::Ipv4Addr; | ||
| use std::str::FromStr; | ||
| use tokio::net::TcpStream; | ||
| use tokio_stream::StreamExt; | ||
| use zvt::sequences::Sequence; | ||
|
|
@@ -27,10 +28,38 @@ enum SubCommands { | |
| ChangeHostConfiguration(ChangeHostConfigurationArgs), | ||
| } | ||
|
|
||
| #[derive(Debug, PartialEq)] | ||
| enum StatusType { | ||
| Feig, | ||
| Zvt, | ||
| } | ||
|
|
||
| impl FromStr for StatusType { | ||
| type Err = anyhow::Error; | ||
|
|
||
| fn from_str(s: &str) -> Result<Self, Self::Err> { | ||
| match s.to_lowercase().as_str() { | ||
| "feig" => Ok(StatusType::Feig), | ||
| "zvt" => Ok(StatusType::Zvt), | ||
| _ => Err(anyhow::anyhow!( | ||
| "'{s}' is not a valid StatusType (feig | zvt)" | ||
| )), | ||
| } | ||
| } | ||
| } | ||
|
|
||
| #[derive(FromArgs, PartialEq, Debug)] | ||
| /// Query the cVEND status from the terminal and print to stdout. | ||
| /// Query the status. | ||
| #[argh(subcommand, name = "status")] | ||
| struct StatusArgs {} | ||
| struct StatusArgs { | ||
| /// which type of status to use (feig | zvt) | ||
| #[argh(option, default = "StatusType::Zvt")] | ||
| r#type: StatusType, | ||
|
|
||
| /// in case of zvt - which service byte to use. See section 2.55.1 for more details. | ||
| #[argh(option)] | ||
| service_byte: Option<u8>, | ||
| } | ||
|
|
||
| #[derive(FromArgs, PartialEq, Debug)] | ||
| /// Factory resets the terminal. | ||
|
|
@@ -240,18 +269,49 @@ fn init_logger() { | |
| .init(); | ||
| } | ||
|
|
||
| async fn status(socket: &mut PacketTransport) -> Result<()> { | ||
| // Check the current version of the software | ||
| let request = feig::packets::CVendFunctions { | ||
| password: None, | ||
| instr: feig::constants::CVendFunctions::SystemsInfo as u16, | ||
| }; | ||
| let mut stream = feig::sequences::GetSystemInfo::into_stream(&request, socket); | ||
| while let Some(response) = stream.next().await { | ||
| use feig::sequences::GetSystemInfoResponse::*; | ||
| match response? { | ||
| CVendFunctionsEnhancedSystemInformationCompletion(data) => log::info!("{data:#?}"), | ||
| Abort(_) => bail!("Failed to get system info. Received Abort."), | ||
| async fn status( | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm having trouble understanding how these changes here map to what's in the PR description. This seems to be modifying the CLI tool to log some data in response to a command. How does that relate to the rest of the changes?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i think we discussed it in person - but to be complete here: we're using the status inquiry in the zvt_feig_terminal and this adds the feature to debug the new status through the cli |
||
| socket: &mut PacketTransport, | ||
| password: usize, | ||
| status_type: StatusType, | ||
| service_byte: Option<u8>, | ||
| ) -> Result<()> { | ||
| match status_type { | ||
| StatusType::Feig => { | ||
| // Check the current version of the software | ||
| let request = feig::packets::CVendFunctions { | ||
| password: None, | ||
| instr: feig::constants::CVendFunctions::SystemsInfo as u16, | ||
| }; | ||
| let mut stream = feig::sequences::GetSystemInfo::into_stream(&request, socket); | ||
| while let Some(response) = stream.next().await { | ||
| use feig::sequences::GetSystemInfoResponse::*; | ||
| match response? { | ||
| CVendFunctionsEnhancedSystemInformationCompletion(data) => { | ||
| log::info!("{data:#?}") | ||
| } | ||
| Abort(_) => bail!("Failed to get system info. Received Abort."), | ||
| } | ||
| } | ||
| } | ||
| StatusType::Zvt => { | ||
| let request = packets::StatusEnquiry { | ||
| password: Some(password), | ||
| service_byte: service_byte, | ||
dorezyuk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| tlv: None, | ||
| }; | ||
|
|
||
| // See table 12 in the definition. We cannot parse this reqeust | ||
| // correctly. | ||
| if let Some(sb) = service_byte { | ||
| if (sb & 0x02) == 0 { | ||
| log::warn!("The 'Do send SW-Version' is not supported. The output will be not correctly parsed."); | ||
| } | ||
| } | ||
|
|
||
| let mut stream = sequences::StatusEnquiry::into_stream(&request, socket); | ||
| while let Some(response) = stream.next().await { | ||
dorezyuk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| log::info!("{response:#?}"); | ||
| } | ||
| } | ||
| } | ||
| Ok(()) | ||
|
|
@@ -542,7 +602,9 @@ async fn main() -> Result<()> { | |
| }; | ||
|
|
||
| match args.command { | ||
| SubCommands::Status(_) => status(&mut socket).await?, | ||
| SubCommands::Status(a) => { | ||
| status(&mut socket, args.password, a.r#type, a.service_byte).await? | ||
| } | ||
| SubCommands::FactoryReset(_) => factory_reset(&mut socket, args.password).await?, | ||
| SubCommands::Registration(a) => registration(&mut socket, args.password, &a).await?, | ||
| SubCommands::SetTerminalId(a) => set_terminal_id(&mut socket, args.password, &a).await?, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -139,9 +139,6 @@ pub struct Feig { | |
|
|
||
| /// The last end of day job. | ||
| end_of_day_last_instant: std::time::Instant, | ||
|
|
||
| /// Was the terminal successfully configured | ||
| successfully_configured: bool, | ||
| } | ||
|
|
||
| impl Feig { | ||
|
|
@@ -155,16 +152,11 @@ impl Feig { | |
| transactions_max_num, | ||
| end_of_day_max_interval, | ||
| end_of_day_last_instant: std::time::Instant::now(), | ||
| successfully_configured: false, | ||
| }; | ||
|
|
||
| // Ignore the errors from configure beyond setting the flag | ||
| // (call fails if e.x. the terminal id is invalid) | ||
| let mut successfully_configured = false; | ||
| if let Ok(_) = this.configure().await { | ||
| successfully_configured = true; | ||
| } | ||
| this.successfully_configured = successfully_configured; | ||
| // Ignore the errors from configure. (call fails if e.x. the terminal id | ||
| // is invalid) | ||
| let _unused = this.configure().await; | ||
| Ok(this) | ||
| } | ||
|
|
||
|
|
@@ -176,9 +168,6 @@ impl Feig { | |
| config | ||
| }; | ||
| self.socket = TcpStream::new(config)?; | ||
| // Reset this to trigger a network call inside the `configure` call | ||
| // below. | ||
| self.successfully_configured = false; | ||
| // This checks if the new connection is sound. | ||
| self.configure().await | ||
| } | ||
|
|
@@ -224,7 +213,7 @@ impl Feig { | |
|
|
||
| // Set the terminal id if required. | ||
| if config.terminal_id == system_info.terminal_id { | ||
| info!("Terminal id already up-to-date"); | ||
| log::debug!("Terminal id already up-to-date"); | ||
| return Ok(false); | ||
| } | ||
|
|
||
|
|
@@ -431,24 +420,89 @@ impl Feig { | |
| Err(error) | ||
| } | ||
|
|
||
| async fn status_enquiry(&mut self) -> Result<constants::TerminalStatusCode> { | ||
| // Get the status inquiry so we can reason on the terminal_status_code. | ||
dorezyuk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| let password = self.socket.config().feig_config.password; | ||
| let request = packets::StatusEnquiry { | ||
| password: Some(password), | ||
| service_byte: None, | ||
| tlv: None, | ||
| }; | ||
|
|
||
| let mut error = zvt::ZVTError::IncompleteData.into(); | ||
| let mut terminal_status_code = None; | ||
| let mut stream = sequences::StatusEnquiry::into_stream(request, &mut self.socket); | ||
|
|
||
| while let Some(response) = stream.next().await { | ||
| let response = match response { | ||
| Ok(response) => response, | ||
| Err(err) => { | ||
| error = err; | ||
| continue; | ||
| } | ||
| }; | ||
| match response { | ||
| sequences::StatusEnquiryResponse::CompletionData(completion_data) => { | ||
| terminal_status_code = Some(completion_data.terminal_status_code); | ||
| } | ||
| other => { | ||
| log::debug!("{other:#?}"); | ||
dorezyuk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| } | ||
| } | ||
| drop(stream); | ||
|
|
||
| let Some(terminal_status_code) = terminal_status_code else { | ||
| return Err(error); | ||
| }; | ||
|
|
||
| constants::TerminalStatusCode::from_u8(terminal_status_code).ok_or(anyhow!( | ||
| "Unknown terminal status code: 0x{:02x}", | ||
| terminal_status_code | ||
| )) | ||
| } | ||
|
|
||
| /// Initializes the connection. | ||
| /// | ||
| /// We're doing the following | ||
| /// * Set the terminal id if required. | ||
| /// * Initialize the terminal. | ||
| /// * Run end-of-day job. | ||
| /// We're doing the following based on the terminal status code: | ||
| /// * If PtReady - return | ||
| /// * If ReconciliationRequired - run end-of-day | ||
| /// * If InitialisationRequired, DiagnosisRequired or TerminalActivationRequired | ||
| /// - set terminal id, run emv diagnostics and initialize the terminal. | ||
| pub async fn configure(&mut self) -> Result<()> { | ||
| if self.successfully_configured { | ||
| return Ok(()); | ||
| let status = self.status_enquiry().await?; | ||
| let mut force_init = false; | ||
| match status { | ||
| constants::TerminalStatusCode::PtReady => { | ||
| log::debug!("Terminal is ready"); | ||
dorezyuk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| constants::TerminalStatusCode::ReconciliationRequired => { | ||
dorezyuk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| info!("Reconciliation required, running end of day"); | ||
| self.end_of_day().await?; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We wouldn't know a failure occurred here, since
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Better yet - since if this function fails we won't have a working PT on a pole, better log it in the calling function and add an alert for this failure to track it |
||
| } | ||
| constants::TerminalStatusCode::InitialisationRequired | ||
| | constants::TerminalStatusCode::DiagnosisRequired | ||
| | constants::TerminalStatusCode::TerminalActivationRequired => { | ||
| info!("Initialization or diagnosis required"); | ||
| force_init = true; | ||
| } | ||
| _ => { | ||
| warn!("Unexpected terminal status: {status}") | ||
| } | ||
| } | ||
|
|
||
| // If we've an outdated tid, we would actually still receive PtReady or | ||
| // ReconciliationRequired. After running potential end of day jobs on | ||
| // what ever tid is currently stored in the payment terminal we now | ||
| // force the payment terminal to have our desired tid. If the tid didn't | ||
| // change this call returns right away. | ||
| let tid_changed = self.set_terminal_id().await?; | ||
| if tid_changed { | ||
| if tid_changed || force_init { | ||
| info!("tid_changed: {tid_changed} and force_init {force_init}"); | ||
| self.run_diagnosis(packets::DiagnosisType::EmvConfiguration) | ||
| .await?; | ||
| self.initialize().await?; | ||
| } | ||
| self.initialize().await?; | ||
| self.successfully_configured = true; | ||
|
|
||
| Ok(()) | ||
| } | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.