Skip to content

Commit 2c0d823

Browse files
samvrlewisJason Mobarakjohn-michaelburke
authored
Firmware update tool [CPP-724] (#531)
Adds a "swift-updater" CLI tool that can be used to update Piksi firmware from the CLI. To do this, the update tab was refactored. Co-authored-by: Jason Mobarak <[email protected]> Co-authored-by: John-Michael Burke <[email protected]>
1 parent 2366092 commit 2c0d823

File tree

11 files changed

+625
-362
lines changed

11 files changed

+625
-362
lines changed

.github/workflows/main.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,10 @@ jobs:
314314
run: |
315315
cargo build --release --bin swift-settings --no-default-features --features env_logger
316316
317+
- name: Build ${{ runner.os }} swift-updater binary.
318+
run: |
319+
cargo build --release --bin swift-updater --no-default-features
320+
317321
- name: Pull Git LFS objects
318322
run: git lfs pull
319323
env:
@@ -351,6 +355,10 @@ jobs:
351355
with:
352356
name: swift-settings_${{ matrix.os.short_name }}
353357
path: ./target/release/swift-settings${{ matrix.os.exe_suffix }}
358+
- uses: actions/upload-artifact@v2
359+
with:
360+
name: swift-updater_${{ matrix.os.short_name }}
361+
path: ./target/release/swift-updater${{ matrix.os.exe_suffix }}
354362
- uses: actions/upload-artifact@v2
355363
with:
356364
name: ${{ runner.os }}-installer
@@ -641,6 +649,7 @@ jobs:
641649
type:
642650
- "files"
643651
- "settings"
652+
- "updater"
644653
runs-on: [self-hosted, '${{ matrix.os.name }}', code-signer]
645654
steps:
646655
- name: Store git tag vars.
@@ -743,6 +752,11 @@ jobs:
743752
with:
744753
name: swift-settings_windows-signed
745754
path: windows
755+
- name: Pull Windows swift-updater
756+
uses: actions/download-artifact@v2
757+
with:
758+
name: swift-updater_windows-signed
759+
path: windows
746760
- name: Pull Linux Installer
747761
uses: actions/download-artifact@v2
748762
with:
@@ -758,6 +772,11 @@ jobs:
758772
with:
759773
name: swift-settings_linux-signed
760774
path: linux
775+
- name: Pull Linux swift-updater
776+
uses: actions/download-artifact@v2
777+
with:
778+
name: swift-updater_linux-signed
779+
path: linux
761780
- name: Pull macOS Installer
762781
uses: actions/download-artifact@v2
763782
with:
@@ -773,6 +792,11 @@ jobs:
773792
with:
774793
name: swift-settings_macos-signed
775794
path: macos
795+
- name: Pull macOS swift-updater
796+
uses: actions/download-artifact@v2
797+
with:
798+
name: swift-updater_macos-signed
799+
path: macos
776800
- name: Prepare Release
777801
shell: bash
778802
run: |
@@ -787,11 +811,14 @@ jobs:
787811
windows/${{ env.WINDOWS_ARCHIVE }}
788812
windows/swift-files_${{ env.VERSION }}_windows.exe
789813
windows/swift-settings_${{ env.VERSION }}_windows.exe
814+
windows/swift-updater_${{ env.VERSION }}_windows.exe
790815
linux/${{ env.LINUX_ARCHIVE }}
791816
linux/swift-files_${{ env.VERSION }}_linux.zip
792817
linux/swift-settings_${{ env.VERSION }}_linux.zip
818+
linux/swift-updater_${{ env.VERSION }}_linux.zip
793819
macos/${{ env.MACOS_ARCHIVE }}
794820
macos/swift-files_${{ env.VERSION }}_macos.zip
795821
macos/swift-settings_${{ env.VERSION }}_macos.zip
822+
macos/swift-updater_${{ env.VERSION }}_macos.zip
796823
env:
797824
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

console_backend/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,11 @@ name = "headless-console"
9595
path = "src/bin/headless-console.rs"
9696
bench = false
9797

98+
[[bin]]
99+
name = "swift-updater"
100+
path = "src/bin/update_tool.rs"
101+
bench = false
102+
98103
[features]
99104
default = ["pyo3"]
100105
benches = []

console_backend/src/bin/files.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use sbp::{link::LinkSource, SbpIterExt};
1616
use console_backend::{
1717
cli_options::is_baudrate,
1818
connection::Connection,
19+
constants::EXAMPLE_SERIAL_NAME,
1920
fileio::Fileio,
2021
types::{FlowControl, MsgSender, Result},
2122
};
@@ -41,13 +42,6 @@ fn main() -> Result<()> {
4142
}
4243
}
4344

44-
#[cfg(target_os = "windows")]
45-
const SERIAL_NAME: &str = "COM1";
46-
#[cfg(target_os = "linux")]
47-
const SERIAL_NAME: &str = "/dev/ttyUSB0";
48-
#[cfg(target_os = "macos")]
49-
const SERIAL_NAME: &str = "/dev/cu.usbserial";
50-
5145
lazy_static! {
5246
static ref FILEIO_USAGE: String = format!(
5347
"\
@@ -84,7 +78,7 @@ lazy_static! {
8478
- Delete file from Swift device:
8579
swift-files --delete {serial}:/persistent/unwanted_file
8680
",
87-
serial = SERIAL_NAME
81+
serial = EXAMPLE_SERIAL_NAME
8882
);
8983
}
9084

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
use std::io::{stdout, Read, Write};
2+
use std::path::PathBuf;
3+
use std::str::FromStr;
4+
use std::sync::{Arc, Mutex};
5+
use std::time::Duration;
6+
7+
use anyhow::anyhow;
8+
use clap::{AppSettings::DeriveDisplayOrder, ArgGroup, Parser};
9+
use console_backend::constants::EXAMPLE_SERIAL_NAME;
10+
use lazy_static::lazy_static;
11+
12+
use console_backend::cli_options::{SerialOpts, TcpOpts};
13+
use console_backend::connection::Connection;
14+
use console_backend::firmware_update::{self, LogOverwriteBehavior};
15+
use console_backend::swift_version::SwiftVersion;
16+
use console_backend::types::MsgSender;
17+
use console_backend::types::Result;
18+
use sbp::link::{Link, LinkSource};
19+
use sbp::SbpIterExt;
20+
use sbp_settings::Client;
21+
22+
lazy_static! {
23+
static ref USAGE: String = format!(
24+
"\
25+
swift-updater [OPTIONS]
26+
27+
Examples:
28+
- Updating firmware using TCP/IP
29+
swift-updater --tcp 192.168.0.2222:55555 ./firmware.bin
30+
- Updating firmware using serial
31+
swift-updater --serial {serial} ./firmware.bin
32+
",
33+
serial = EXAMPLE_SERIAL_NAME
34+
);
35+
}
36+
37+
/// A SwiftNav firmware updater API client
38+
#[derive(Parser)]
39+
#[clap(
40+
name = "swift-updater",
41+
version = include_str!("../version.txt"),
42+
setting = DeriveDisplayOrder,
43+
group = ArgGroup::new("conn").required(true).args(&["serial", "tcp"]),
44+
override_usage = &**USAGE
45+
)]
46+
struct Opts {
47+
/// The binary (.bin) file to write to flash
48+
update_file: PathBuf,
49+
50+
#[clap(flatten)]
51+
serial: SerialOpts,
52+
53+
#[clap(flatten)]
54+
tcp: TcpOpts,
55+
}
56+
57+
const FIRMWARE_VERSION_GROUP: &str = "system_info";
58+
const FIRMWARE_VERSION_SETTING: &str = "firmware_version";
59+
const READ_TIMEOUT: Duration = Duration::from_secs(5);
60+
61+
fn get_firmware_version(link: Link<'static, ()>, msg_sender: MsgSender) -> Result<SwiftVersion> {
62+
let mut settings_client =
63+
Client::with_link(link, move |msg| msg_sender.send(msg).map_err(Into::into));
64+
65+
let setting_value = settings_client
66+
.read_setting_with_timeout(
67+
FIRMWARE_VERSION_GROUP,
68+
FIRMWARE_VERSION_SETTING,
69+
READ_TIMEOUT,
70+
)?
71+
.ok_or_else(|| anyhow!("Couldn't read firmware version"))?
72+
.value
73+
.ok_or_else(|| anyhow!("Couldn't read firmware version"))?
74+
.to_string();
75+
76+
SwiftVersion::from_str(&setting_value).map_err(Into::into)
77+
}
78+
79+
fn get_connection(opts: &Opts) -> Result<(Box<dyn Read + Send>, Box<dyn Write + Send>)> {
80+
let (reader, writer) = if let Some(ref serial) = opts.serial.serial {
81+
Connection::serial(
82+
serial.to_string_lossy().into(),
83+
opts.serial.baudrate,
84+
opts.serial.flow_control,
85+
)
86+
.try_connect(None)?
87+
} else if let Some(ref tcp) = opts.tcp.tcp {
88+
Connection::tcp(tcp.host.clone(), tcp.port)?.try_connect(None)?
89+
} else {
90+
return Err(anyhow!("No serialport or tcp string supplied"));
91+
};
92+
Ok((reader, writer))
93+
}
94+
95+
fn printer(
96+
line: &str,
97+
overwrite: LogOverwriteBehavior,
98+
last_state: &Arc<Mutex<LogOverwriteBehavior>>,
99+
) {
100+
let mut last_overwrite = last_state.lock().expect("Mutex poisoned");
101+
102+
// Logic here is needed here to avoid overwriting lines that shouldn't be overwritten
103+
match (*last_overwrite, overwrite) {
104+
(LogOverwriteBehavior::DontOverwrite, LogOverwriteBehavior::DontOverwrite) => {
105+
println!("> {line}")
106+
}
107+
(LogOverwriteBehavior::DontOverwrite, LogOverwriteBehavior::Overwrite) => {
108+
print!("> {line}")
109+
}
110+
(LogOverwriteBehavior::Overwrite, LogOverwriteBehavior::DontOverwrite) => {
111+
println!("\n> {line}")
112+
}
113+
(LogOverwriteBehavior::Overwrite, LogOverwriteBehavior::Overwrite) => print!("\r> {line}"),
114+
}
115+
116+
*last_overwrite = overwrite;
117+
118+
stdout().flush().expect("Couldn't flush stdout");
119+
}
120+
121+
fn main() -> anyhow::Result<()> {
122+
if std::env::var("RUST_LOG").is_err() {
123+
std::env::set_var("RUST_LOG", "warn");
124+
}
125+
126+
let opts = Opts::parse();
127+
128+
let (reader, writer) = get_connection(&opts)?;
129+
let source = LinkSource::new();
130+
let link = source.link();
131+
let msg_sender = MsgSender::new(writer);
132+
133+
std::thread::spawn(move || {
134+
let messages = sbp::iter_messages(reader).log_errors(log::Level::Debug);
135+
for msg in messages {
136+
source.send(msg);
137+
}
138+
});
139+
140+
let firmware_version = get_firmware_version(link.clone(), msg_sender.clone())?;
141+
142+
let log_state = Arc::new(Mutex::new(LogOverwriteBehavior::Overwrite));
143+
let progress_state = log_state.clone();
144+
145+
firmware_update::firmware_update(
146+
link,
147+
msg_sender,
148+
&opts.update_file,
149+
&firmware_version,
150+
move |msg, overwrite| printer(&msg, overwrite, &log_state),
151+
move |progress| {
152+
printer(
153+
&format!("Uploading image to device {:.2}%...", progress),
154+
LogOverwriteBehavior::Overwrite,
155+
&progress_state,
156+
)
157+
},
158+
)?;
159+
160+
Ok(())
161+
}

console_backend/src/cli_options.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ pub struct SerialOpts {
241241
)]
242242
pub serial: Option<PathBuf>,
243243

244-
/// The baudrate for processing packets.
244+
/// The baudrate for processing packets when connecting via serial.
245245
#[clap(
246246
long,
247247
default_value = "115200",
@@ -250,7 +250,7 @@ pub struct SerialOpts {
250250
)]
251251
pub baudrate: u32,
252252

253-
/// The flow control spec to use.
253+
/// The flow control spec to use when connecting via serial.
254254
#[clap(
255255
long,
256256
default_value = "None",

console_backend/src/constants.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
use lazy_static::lazy_static;
12
use std::time::Duration;
23

4+
use crate::swift_version::SwiftVersion;
5+
36
// 'Universal' constants
47

58
pub(crate) const NANOSECONDS_PER_SECOND: f64 = 1.0e+9;
@@ -242,3 +245,19 @@ pub(crate) const UNKNOWN_ERROR: &str = "Unk Error";
242245
pub(crate) const UNKNOWN_ERROR_SHORT: &str = "Unk";
243246
pub(crate) const ODO_POSTFIX: &str = "+Odo";
244247
pub(crate) const INS_POSTFIX: &str = "+INS";
248+
249+
// Update firmware constants.
250+
pub(crate) const HARDWARE_REVISION: &str = "piksi_multi";
251+
pub(crate) const FIRMWARE_V2_VERSION: &str = "v2.0.0";
252+
lazy_static! {
253+
pub(crate) static ref FIRMWARE_V2: SwiftVersion =
254+
SwiftVersion::parse(FIRMWARE_V2_VERSION).unwrap();
255+
}
256+
257+
// CLI constants
258+
#[cfg(target_os = "windows")]
259+
pub const EXAMPLE_SERIAL_NAME: &str = "COM1";
260+
#[cfg(target_os = "linux")]
261+
pub const EXAMPLE_SERIAL_NAME: &str = "/dev/ttyUSB0";
262+
#[cfg(target_os = "macos")]
263+
pub const EXAMPLE_SERIAL_NAME: &str = "/dev/cu.usbserial";

0 commit comments

Comments
 (0)