Skip to content
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

Add sequencer attachment #6

Merged
merged 2 commits into from
Sep 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ csv = "1.3.0"
dialoguer = "0.11.0"
directories = "5.0.1"
indent = "0.1.1"
indicatif = "0.17.8"
reqwest = { version = "0.12.7", features = ["blocking"] }
serde = { version = "1.0.210", features = ["derive"] }
serde_json = "1.0.128"
Expand Down
40 changes: 40 additions & 0 deletions src/data_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use reqwest::blocking::Response;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::io::Write;
use std::path::PathBuf;
use tempfile::Builder;
use tungstenite::client::connect;
Expand All @@ -23,6 +24,9 @@
FinalOdb {
run_number: u32,
},
SequencerCsv {
run_number: u32,
},
SpillLog {
run_number: u32,
},
Expand Down Expand Up @@ -135,6 +139,42 @@
Ok(SpillLog { records })
}

#[derive(Debug, Deserialize)]
struct SequencerRecord {

Check failure

Code scanning / clippy

fields serial_number, midas_timestamp, header, and xml are never read Error

fields serial\_number, midas\_timestamp, header, and xml are never read

Check failure

Code scanning / clippy

fields serial_number, midas_timestamp, header, and xml are never read Error

fields serial\_number, midas\_timestamp, header, and xml are never read
serial_number: u32,

Check failure

Code scanning / clippy

fields serial_number, midas_timestamp, header, and xml are never read Error

fields serial\_number, midas\_timestamp, header, and xml are never read

Check failure

Code scanning / clippy

fields serial_number, midas_timestamp, header, and xml are never read Error

fields serial\_number, midas\_timestamp, header, and xml are never read
midas_timestamp: u32,

Check failure

Code scanning / clippy

fields serial_number, midas_timestamp, header, and xml are never read Error

fields serial\_number, midas\_timestamp, header, and xml are never read

Check failure

Code scanning / clippy

fields serial_number, midas_timestamp, header, and xml are never read Error

fields serial\_number, midas\_timestamp, header, and xml are never read
header: String,
xml: String,

Check failure

Code scanning / clippy

fields serial_number, midas_timestamp, header, and xml are never read Error

fields serial\_number, midas\_timestamp, header, and xml are never read

Check failure

Code scanning / clippy

fields serial_number, midas_timestamp, header, and xml are never read Error

fields serial\_number, midas\_timestamp, header, and xml are never read
}

pub fn get_sequencer_headers(run_number: u32, config: &DataHandlerConfig) -> Result<PathBuf> {
ensure!(
is_data_handler_ready(run_number, config).context("failed to query data handler state")?,
"data handler is not ready"
);

let resp = ws_request(ClientRequest::SequencerCsv { run_number }, config)
.context("failed to request sequencer CSV from data handler")?;
let records = csv::ReaderBuilder::new()
.comment(Some(b'#'))
.from_reader(resp)
.deserialize::<SequencerRecord>()
.map(|record| record.map(|record| record.header))
.collect::<Result<Vec<String>, _>>()
.context("failed to parse sequencer CSV")?
.join("\n\n\n");

let mut temp = Builder::new()
.keep(true)
.suffix(".txt")
.tempfile()
.context("failed to create temporary file")?;
temp.write_all(records.as_bytes())
.context("failed to write sequencer headers to temporary file")?;

Ok(temp.path().to_owned())
}

pub fn get_final_odb(run_number: u32, config: &DataHandlerConfig) -> Result<serde_json::Value> {
ensure!(
is_data_handler_ready(run_number, config).context("failed to query data handler state")?,
Expand Down
38 changes: 34 additions & 4 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use crate::config::{Config, Logbook};
use crate::data_handler::{get_final_odb, get_spill_log};
use crate::data_handler::{get_final_odb, get_sequencer_headers, get_spill_log};
use crate::summary::spill_log_summary;
use anyhow::{ensure, Context, Result};
use clap::Parser;
use dialoguer::{theme::ColorfulTheme, Input, Select};
use elog::{loggable_records, ElogEntry};
use indicatif::{ProgressBar, ProgressStyle};
use std::ffi::OsStr;
use std::io::Write;
use std::path::PathBuf;
Expand Down Expand Up @@ -95,6 +96,11 @@
));
}

let spinner = ProgressBar::new_spinner()
.with_style(ProgressStyle::default_spinner().tick_chars("⠁⠂⠄⡀⢀⠠⠐⠈ "));
spinner.enable_steady_tick(std::time::Duration::from_millis(100));

spinner.set_message("Getting spill log...");
let spill_log = get_spill_log(args.run_number, &config.data_handler)
.context("failed to get spill log from the data handler")?;

Expand All @@ -106,10 +112,22 @@
};

let mut elog_entry = ElogEntry::new();

spinner.set_message("Logging header...");
if let Ok(path) = get_sequencer_headers(args.run_number, &config.data_handler) {
elog_entry.attachments.push(path);
elog_entry.text.push_str("Sequencer: elog:/1\n");
}
if let Ok(path) = spill_log_summary(&spill_log, &config.spill_log_columns) {
elog_entry.text.push_str("Spill log summary: elog:/1\n\n");
elog_entry.attachments.push(path);
elog_entry.text.push_str(&format!(
"Spill log summary: elog:/{}\n",
elog_entry.attachments.len()
));
}
elog_entry.text.push_str("\n");

Check failure

Code scanning / clippy

calling push_str() using a single-character string literal Error

calling push\_str() using a single-character string literal

Check failure

Code scanning / clippy

calling push_str() using a single-character string literal Error

calling push\_str() using a single-character string literal

spinner.set_message("Logging records...");
for loggable in records {
elog_entry.add_record(args.run_number, &loggable, &final_odb, &config.data_handler);
}
Expand All @@ -120,6 +138,7 @@
.write_all(elog_entry.text.as_bytes())
.context("failed to write to temporary elog text file")?;

spinner.set_message("Pushing to server...");
let mut cmd = Command::new(&config.elog.client);
cmd.args(["-h", &config.elog.host])
.args(["-p", &config.elog.port.to_string()])
Expand All @@ -138,8 +157,19 @@
cmd.args(["-r", &parent_id]);
}

let status = cmd.status().context("failed to run the elog client")?;
ensure!(status.success(), "elog client failed with `{status}`");
let output = cmd.output().context("failed to run the elog client")?;
ensure!(
output.status.success(),
"elog client failed with {}",
output.status
);
spinner.finish_and_clear();
// The elog client doesn't report errors correctly. With some failure modes,
// it will still return a successful exit code but print an error message to
// stdout or stderr. Basically, there is no way to know if the elog was
// successfully created other than reading all output of the command.
let _ = std::io::stdout().write_all(&output.stdout);
let _ = std::io::stderr().write_all(&output.stderr);

Ok(())
}