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 subcommands #104

Merged
merged 1 commit into from
Aug 16, 2023
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
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ The program is designed to be run during a flight and display information in a t

Run your executable from the terminal with the path to your configuration file:
```shell
./packetraven_Windows.exe examples/example_1.yaml
./packetraven_Windows.exe run examples/example_1.yaml
```

[Instructions for creating a configuration file can be found in the documentation](https://packetraven.readthedocs.io/en/latest/configuration.html).
Expand All @@ -61,3 +61,13 @@ and the up and down arrow keys change the current plot (or scroll through log me

To quit, press `q` or `Esc`.

### Prediction

You can run the `predict` subcommand to retrieve a prediction:

```shell
./packetraven_Windows.exe predict "2023-08-16T10:00:00" -- -79 39 5 30000 9
```

> **Note**\
> Negative values must be prepended with `-- `, e.g. `-- -79`.
6 changes: 3 additions & 3 deletions src/configuration/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ fn default_name() -> String {
String::from("unnamed_flight")
}

#[derive(serde::Deserialize, Clone)]
#[derive(serde::Deserialize, Clone, Default, serde::Serialize)]
pub struct RunConfiguration {
#[serde(default = "default_name")]
pub name: String,
Expand All @@ -23,7 +23,7 @@ fn default_interval() -> chrono::Duration {
}

#[serde_with::serde_as]
#[derive(PartialEq, Debug, serde::Deserialize, Clone)]
#[derive(PartialEq, Debug, serde::Deserialize, Clone, serde::Serialize)]
pub struct TimeConfiguration {
#[serde(default)]
#[serde(with = "crate::utilities::optional_local_datetime_string")]
Expand All @@ -46,7 +46,7 @@ impl Default for TimeConfiguration {
}
}

#[derive(Default, serde::Deserialize, PartialEq, Debug, Clone)]
#[derive(Default, serde::Deserialize, PartialEq, Debug, Clone, serde::Serialize)]
pub struct ConnectionConfiguration {
pub text: Option<Vec<crate::connection::text::TextStream>>,
#[cfg(feature = "sondehub")]
Expand Down
8 changes: 4 additions & 4 deletions src/configuration/prediction.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use serde_with::serde_as;

#[derive(serde::Deserialize, Clone)]
#[derive(serde::Deserialize, Clone, serde::Serialize)]
#[serde(untagged)]
pub enum PredictionConfiguration {
Single(Prediction),
Expand All @@ -14,7 +14,7 @@ fn default_name() -> String {
String::from("prediction")
}

#[derive(serde::Deserialize, PartialEq, Debug, Clone)]
#[derive(serde::Deserialize, PartialEq, Debug, Clone, serde::Serialize)]
pub struct Prediction {
pub start: crate::location::Location,
pub profile: StandardProfile,
Expand Down Expand Up @@ -63,7 +63,7 @@ fn default_descent_only() -> bool {
false
}

#[derive(serde::Deserialize, PartialEq, Debug, Clone)]
#[derive(serde::Deserialize, PartialEq, Debug, Clone, serde::Serialize)]
pub struct StandardProfile {
pub ascent_rate: f64,
pub burst_altitude: f64,
Expand All @@ -74,7 +74,7 @@ pub struct StandardProfile {
}

#[serde_as]
#[derive(serde::Deserialize, PartialEq, Debug, Clone)]
#[derive(serde::Deserialize, PartialEq, Debug, Clone, serde::Serialize)]
pub struct FloatProfile {
pub altitude: f64,
pub uncertainty: Option<f64>,
Expand Down
2 changes: 1 addition & 1 deletion src/connection/aprs_fi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ lazy_static::lazy_static! {
static ref MINIMUM_ACCESS_INTERVAL: chrono::Duration = chrono::Duration::seconds(10);
}

#[derive(serde::Deserialize, Debug, PartialEq, Clone)]
#[derive(serde::Deserialize, Debug, PartialEq, Clone, serde::Serialize)]
pub struct AprsFiQuery {
pub api_key: String,
pub callsigns: Option<Vec<String>>,
Expand Down
2 changes: 1 addition & 1 deletion src/connection/postgres.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use chrono::TimeZone;

#[derive(serde::Deserialize, Debug, PartialEq, Clone)]
#[derive(serde::Deserialize, Debug, PartialEq, Clone, serde::Serialize)]
pub struct DatabaseCredentials {
pub hostname: String,
pub port: u32,
Expand Down
2 changes: 1 addition & 1 deletion src/connection/sondehub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ lazy_static::lazy_static! {
static ref MINIMUM_ACCESS_INTERVAL: chrono::Duration = chrono::Duration::seconds(10);
}

#[derive(serde::Deserialize, Debug, PartialEq, Clone, Default)]
#[derive(serde::Deserialize, Debug, PartialEq, Clone, Default, serde::Serialize)]
pub struct SondeHubQuery {
pub start: Option<chrono::DateTime<chrono::Local>>,
pub end: Option<chrono::DateTime<chrono::Local>>,
Expand Down
4 changes: 2 additions & 2 deletions src/connection/text/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::io::prelude::BufRead;

use chrono::{TimeZone, Timelike};

#[derive(serde::Deserialize, Debug, PartialEq, Clone)]
#[derive(serde::Deserialize, Debug, PartialEq, Clone, serde::Serialize)]
pub struct AprsTextFile {
pub path: String,
pub callsigns: Option<Vec<String>>,
Expand Down Expand Up @@ -134,7 +134,7 @@ impl AprsTextFile {
}
}

#[derive(serde::Deserialize, Debug, PartialEq, Clone)]
#[derive(serde::Deserialize, Debug, PartialEq, Clone, serde::Serialize)]
pub struct GeoJsonFile {
pub path: String,
}
Expand Down
2 changes: 1 addition & 1 deletion src/connection/text/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ pub mod file;
#[cfg(feature = "serial")]
pub mod serial;

#[derive(serde::Deserialize, Debug, PartialEq, Clone)]
#[derive(serde::Deserialize, Debug, PartialEq, Clone, serde::Serialize)]
#[serde(untagged)]
pub enum TextStream {
AprsTextFile(file::AprsTextFile),
Expand Down
2 changes: 1 addition & 1 deletion src/connection/text/serial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ lazy_static::lazy_static! {
static ref DEFAULT_BAUD_RATE: u32 = 9600;
}

#[derive(serde::Deserialize, Debug, PartialEq, Clone)]
#[derive(serde::Deserialize, Debug, PartialEq, Clone, serde::Serialize)]
pub struct AprsSerial {
#[serde(default = "first_available_port")]
pub port: String,
Expand Down
112 changes: 105 additions & 7 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,115 @@ lazy_static::lazy_static! {
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
struct PacketravenCommand {
// configuration file to read
config_file: std::path::PathBuf,
#[command(subcommand)]
command: Command,
}

#[derive(clap::Subcommand)]
enum Command {
/// run program from configuration
Run {
/// file path to configuration
config_file: std::path::PathBuf,
},
/// retrieve a balloon prediction from the given API
Predict {
/// start time
time: chrono::NaiveDateTime,
/// start longitude
longitude: f64,
/// start latitude
latitude: f64,
/// start altitude
#[arg(short, long)]
altitude: Option<f64>,
/// expected average ascent rate
ascent_rate: f64,
/// expected burst altitude
burst_altitude: f64,
/// descent rate at sea level
sea_level_descent_rate: f64,
/// desired float altitude
#[arg(long)]
float_altitude: Option<f64>,
/// desired float duration in seconds
#[arg(long)]
float_duration: Option<f64>,
},
/// write an empty configuration file
Write {
/// file path to configuration
filename: std::path::PathBuf,
},
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
let arguments = PacketravenCommand::parse();

let configuration_file = std::fs::File::open(arguments.config_file).unwrap();
let configuration: crate::configuration::RunConfiguration =
serde_yaml::from_reader(configuration_file).expect("error reading configuration");
match arguments.command {
Command::Run { config_file } => {
let file = std::fs::File::open(config_file).unwrap();
let configuration: crate::configuration::RunConfiguration =
serde_yaml::from_reader(file).expect("error reading configuration");

tui::run(configuration, *LOG_LEVEL)?;
Ok(())
}
Command::Predict {
time,
longitude,
latitude,
altitude,
ascent_rate,
burst_altitude,
sea_level_descent_rate,
float_altitude,
float_duration,
} => {
let start = location::Location {
time: time.and_local_timezone(chrono::Local).unwrap(),
coord: geo::coord! {x:longitude,y:latitude},
altitude,
};
let profile = prediction::FlightProfile::new(
ascent_rate,
float_altitude,
match float_duration {
Some(seconds) => Some(chrono::Duration::seconds(seconds as i64)),
None => None,
},
None,
burst_altitude,
sea_level_descent_rate,
);

let query = prediction::tawhiri::TawhiriQuery::new(
&start, &profile, None, None, None, false, None,
);

match query.retrieve_prediction() {
Ok(prediction) => {
for location in prediction {
println!(
"{:}, {:.1}, {:.1}, {:.1}",
location.location.time.format("%Y-%m-%d %H:%M:%S"),
location.location.coord.x,
location.location.coord.y,
location.location.altitude.unwrap_or(0.0)
);
}
}
Err(error) => return Err(Box::new(error)),
}

Ok(())
}
Command::Write { filename } => {
let configuration = configuration::RunConfiguration::default();
let file = std::fs::File::create(filename).unwrap();

tui::run(configuration, *LOG_LEVEL)?;
Ok(())
serde_yaml::to_writer(file, &configuration).unwrap();
Ok(())
}
}
}