Compare commits
52 commits
Author | SHA1 | Date | |
---|---|---|---|
ff4a7705d4 | |||
701ff50ec8 | |||
04e79dca2b | |||
b27da2155e | |||
32789a4546 | |||
c6c34462bb | |||
0a0f502410 | |||
e92b0522b8 | |||
1300e040d7 | |||
a59a3c590d | |||
c403dfa7c1 | |||
5d96f6cb2d | |||
108132ef9e | |||
5d42a34f16 | |||
41e1f8eda3 | |||
d4b47c109f | |||
f7b9da5086 | |||
dc697e9dfb | |||
c5bfbc9adb | |||
a198e7b101 | |||
820712ef4c | |||
31099ebdcb | |||
7294638681 | |||
9bd3fc067d | |||
7f8d0a8397 | |||
abc0c5fb54 | |||
f34356da10 | |||
f5ca4bd2f7 | |||
76f3599d24 | |||
c7b99764e1 | |||
b95fb2713a | |||
bde028d195 | |||
fcacee8fd7 | |||
982e17933c | |||
6855ef7ed7 | |||
8566de3f70 | |||
0430a37cd2 | |||
2a51417b80 | |||
b78e6bb278 | |||
260c166ce9 | |||
7aa23f4015 | |||
c0d13009a4 | |||
bb1dcbe4a1 | |||
0cc47955af | |||
9fe4197e52 | |||
ad887a536e | |||
877b8538eb | |||
5a7183b154 | |||
e0e8562d23 | |||
e63ea62512 | |||
0c3a7ed0d8 | |||
62995f8429 |
14 changed files with 352 additions and 314 deletions
2
.cargo/config.toml
Normal file
2
.cargo/config.toml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[target.aarch64-unknown-linux-musl]
|
||||||
|
linker = "lld"
|
|
@ -1,43 +0,0 @@
|
||||||
name: Basic Cargo Checks
|
|
||||||
run-name: ${{ github.actor }} is testing
|
|
||||||
on: [push]
|
|
||||||
jobs:
|
|
||||||
docker-check:
|
|
||||||
runs-on: docker
|
|
||||||
steps:
|
|
||||||
- name: Grab misc. dependencies
|
|
||||||
run: |
|
|
||||||
apt update && apt install -y librust-libudev-sys-dev build-essential
|
|
||||||
- name: Check out repository code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- name: Grab Rust toolchain
|
|
||||||
uses: https://github.com/actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
profile: minimal
|
|
||||||
toolchain: stable
|
|
||||||
target: aarch64-unknown-linux-gnu
|
|
||||||
override: true
|
|
||||||
- name: Run check
|
|
||||||
uses: https://github.com/actions-rs/cargo@v1
|
|
||||||
with:
|
|
||||||
command: check
|
|
||||||
docker-build:
|
|
||||||
runs-on: docker
|
|
||||||
steps:
|
|
||||||
- name: Grab misc. dependencies
|
|
||||||
run: |
|
|
||||||
apt update && apt install -y librust-libudev-sys-dev build-essential
|
|
||||||
- name: Check out repository code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- name: Grab Rust toolchain
|
|
||||||
uses: https://github.com/actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
profile: minimal
|
|
||||||
toolchain: stable
|
|
||||||
target: aarch64-unknown-linux-gnu
|
|
||||||
override: true
|
|
||||||
- name: Run check
|
|
||||||
uses: https://github.com/actions-rs/cargo@v1
|
|
||||||
with:
|
|
||||||
command: build
|
|
||||||
args: --release
|
|
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -1,3 +1,5 @@
|
||||||
/target
|
/target
|
||||||
/logs
|
/logs
|
||||||
/output
|
/output
|
||||||
|
.cargo
|
||||||
|
.rustup
|
||||||
|
|
23
.woodpecker.yml
Normal file
23
.woodpecker.yml
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
pipeline:
|
||||||
|
build:
|
||||||
|
image: nixos/nix:latest
|
||||||
|
commands:
|
||||||
|
- nix-shell --run 'cargo build --release --target aarch64-unknown-linux-musl'
|
||||||
|
# publish:
|
||||||
|
# when:
|
||||||
|
# event: tag
|
||||||
|
# image: woodpeckerci/plugin-gitea-release
|
||||||
|
# settings:
|
||||||
|
# base_url: https://git.blizzard.systems
|
||||||
|
# files:
|
||||||
|
# - "target/aarch64-unknown-linux-musl/release/seymour_life"
|
||||||
|
# checksum:
|
||||||
|
# - "target/aarch64-unknown-linux-musl/release/seymour_life"
|
||||||
|
# api_key: ${FORGEJO_API}
|
||||||
|
# secrets:
|
||||||
|
# - source: forgejo_api
|
||||||
|
# target: FORGEJO_API
|
||||||
|
# target: ${CI_COMMIT_TAG}
|
||||||
|
# draft: true
|
||||||
|
# prerelease: true
|
||||||
|
# title: .woodpecker/title.txt
|
1
.woodpecker/title.txt
Normal file
1
.woodpecker/title.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
v2.3.2
|
36
Cargo.lock
generated
36
Cargo.lock
generated
|
@ -302,6 +302,12 @@ dependencies = [
|
||||||
"log",
|
"log",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glob"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
|
@ -382,26 +388,6 @@ version = "0.2.142"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317"
|
checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "libudev"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "78b324152da65df7bb95acfcaab55e3097ceaab02fb19b228a9eb74d55f135e0"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"libudev-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "libudev-sys"
|
|
||||||
version = "0.1.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"pkg-config",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "link-cplusplus"
|
name = "link-cplusplus"
|
||||||
version = "1.0.8"
|
version = "1.0.8"
|
||||||
|
@ -486,12 +472,6 @@ version = "1.17.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pkg-config"
|
|
||||||
version = "0.3.27"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro-hack"
|
name = "proc-macro-hack"
|
||||||
version = "0.5.20+deprecated"
|
version = "0.5.20+deprecated"
|
||||||
|
@ -630,7 +610,6 @@ dependencies = [
|
||||||
"IOKit-sys",
|
"IOKit-sys",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libudev",
|
|
||||||
"mach 0.3.2",
|
"mach 0.3.2",
|
||||||
"nix",
|
"nix",
|
||||||
"regex",
|
"regex",
|
||||||
|
@ -639,12 +618,13 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "seymour_life"
|
name = "seymour_life"
|
||||||
version = "2.3.1"
|
version = "2.3.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
"derivative",
|
"derivative",
|
||||||
"fern",
|
"fern",
|
||||||
|
"glob",
|
||||||
"log",
|
"log",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rppal",
|
"rppal",
|
||||||
|
|
|
@ -1,15 +1,18 @@
|
||||||
|
#This feature is currently limited to nightly versions of cargo.
|
||||||
#cargo-features = ["per-package-target"]
|
#cargo-features = ["per-package-target"]
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "seymour_life"
|
name = "seymour_life"
|
||||||
version = "2.3.1"
|
version = "2.3.3"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
#This feature is currently limited to nightly versions of cargo.
|
||||||
|
#forced-target="aarch64-unknown-linux-musl"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rppal = "0.14.1"
|
rppal = "0.14.1"
|
||||||
serialport = "4.2.0"
|
serialport = { version = "4.2.0", default-features = false }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
fern = "0.6.2"
|
fern = "0.6.2"
|
||||||
chrono = "0.4.24"
|
chrono = "0.4.24"
|
||||||
|
@ -17,6 +20,7 @@ once_cell = "1.17.1"
|
||||||
derivative = "2.2.0"
|
derivative = "2.2.0"
|
||||||
time = "0.2.23"
|
time = "0.2.23"
|
||||||
clap = { version = "4.3.2", features = ["derive"] }
|
clap = { version = "4.3.2", features = ["derive"] }
|
||||||
|
glob = "0.3.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
time = "0.2.23"
|
time = "0.2.23"
|
||||||
|
|
30
README.md
30
README.md
|
@ -1,3 +1,4 @@
|
||||||
|
[](https://ci.blizzard.systems/blizzardfinnegan/seymourLifeRust)
|
||||||
# Seymour Life
|
# Seymour Life
|
||||||
|
|
||||||
This is a personal/professional project, which makes use of serial communications (tty over USB via UART) and Raspberry Pi GPIO to simulate long term use of a Seymour device.
|
This is a personal/professional project, which makes use of serial communications (tty over USB via UART) and Raspberry Pi GPIO to simulate long term use of a Seymour device.
|
||||||
|
@ -23,9 +24,7 @@ Note that this command MUST be run as `sudo`/root, due to the way it interacts w
|
||||||
|
|
||||||
## Build From Source
|
## Build From Source
|
||||||
|
|
||||||
Note: *At this time, this project can only reliably be built on Linux. Build instructions for Windows will be written eventually.*
|
To build this project from source *ON A RASPBERRY PI*, first, download the repository. This can be done by using the Download ZIP button, or running the following command in a terminal where `git` is installed:
|
||||||
|
|
||||||
To build this project from source, first, download the repository. This can be done by using the Download ZIP button, or running the following command in a terminal where `git` is installed:
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://git.blizzard.systems/blizzardfinnegan/seymourLifeRust
|
git clone https://git.blizzard.systems/blizzardfinnegan/seymourLifeRust
|
||||||
```
|
```
|
||||||
|
@ -44,28 +43,19 @@ sudo ./target/release/seymour_life
|
||||||
|
|
||||||
You can also build without the `--release` flag, which wil take less time, but will be less optimised for the hardware. If you do this, substitue `./target/release/seymour_life` for `./target/debug/seymour_life` in the above command.
|
You can also build without the `--release` flag, which wil take less time, but will be less optimised for the hardware. If you do this, substitue `./target/release/seymour_life` for `./target/debug/seymour_life` in the above command.
|
||||||
|
|
||||||
### Build Dependencies
|
|
||||||
|
|
||||||
The following dependencies are also necessary for building this project:
|
## Cross-Compilation
|
||||||
- `pkg-config`
|
|
||||||
- `libudev`
|
|
||||||
|
|
||||||
See below for platform specific requirements.
|
Cross compilation is possible with this project, if you do not have a Raspberry Pi available specifically for compilation. Compilation directly on the Pi is rather intensive, and takes significantly longer than cross-compiling.
|
||||||
|
|
||||||
#### Debian-based
|
If you are compiling on Linux, cross-compilation has a dependency of `lld`. This can be found in your distribution's package manager, or directly distributed by LLVM. For Nix users, a predefined `shell.nix` file has been provided for your convenience.
|
||||||
This applies for all distributions of Linux using the `apt` package manager, including but not limited to Debian, Ubuntu, Raspbian/Raspberry Pi OS, and Linux Mint.
|
|
||||||
|
If you are compiling on Windows, to safely cross-compile, you must modify the `.cargo/config.toml` file. Replace `lld` with `rust-lld`, then cross-compilation should work properly.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo apt-get install librust-libudev-sys-dev librust-pkg-config-dev
|
cargo build --target aarch64-unknown-linux-musl
|
||||||
|
# OR
|
||||||
|
cargo build --release --target aarch64-unknown-linux-musl
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Fedora-based
|
|
||||||
This applies for all distributions of Linux using the `dnf` package manager, including but not limited to CentOS, Redhat Enterprise Linux (RHEL), and Fedora.
|
|
||||||
```bash
|
|
||||||
sudo dnf install rust-libudev-sys-devel rust-pkg-config-devel
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Nix
|
|
||||||
This applies to both NixOS, and any distribution where the [Nix package manager](https://nixos.org/download.html) can be installed.
|
|
||||||
|
|
||||||
If you have the Nix package manager installed, this project comes with a `shell.nix` containing the necessary build dependencies. Simply run `nix-shell` to download the necessary dependencies.
|
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
librust-udev-sys-dev
|
|
||||||
pkg-config
|
|
7
notes.md
7
notes.md
|
@ -1,7 +0,0 @@
|
||||||
Auto-serial:
|
|
||||||
|
|
||||||
`echo 'y1q' | python3 -m debugmenu` contains the serial number. Search keyword is: "DtCtrlCfgDeviceSerialNum"
|
|
||||||
split on `\n`, collect into Vec<&str>. Iterate over Vec:
|
|
||||||
if doesn't contain colon, continue
|
|
||||||
split_once on colon
|
|
||||||
if first half is keyword, trim second half, remove `"` characters, save as serial
|
|
15
shell.nix
15
shell.nix
|
@ -1,4 +1,15 @@
|
||||||
{pkgs ? import <nixpkgs> {} }:
|
{pkgs ? import <nixpkgs> {} }:
|
||||||
pkgs.mkShell {
|
pkgs.mkShell rec {
|
||||||
nativeBuildInputs = with pkgs; [ cargo rustc pkg-config libudev-zero ];
|
buildInputs = with pkgs; [ lld rustup ];
|
||||||
|
RUSTUP_HOME = toString ./.rustup;
|
||||||
|
CARGO_HOME = toString ./.cargo;
|
||||||
|
RUSTUP_TOOLCHAIN = "stable";
|
||||||
|
HOST_ARCH = "x86_64-unknown-linux-gnu";
|
||||||
|
CARGO_BUILD_TARGET = "aarch64-unknown-linux-musl";
|
||||||
|
shellHook = ''
|
||||||
|
export PATH=$PATH:${CARGO_HOME}/bin
|
||||||
|
export PATH=$PATH:${RUSTUP_HOME}/toolchains/${RUSTUP_TOOLCHAIN}-${HOST_ARCH}/bin/
|
||||||
|
|
||||||
|
rustup target add "${CARGO_BUILD_TARGET}"
|
||||||
|
'';
|
||||||
}
|
}
|
||||||
|
|
185
src/device.rs
185
src/device.rs
|
@ -3,10 +3,11 @@ use crate::tty::{TTY, Response,Command};
|
||||||
use rppal::gpio::{Gpio,OutputPin};
|
use rppal::gpio::{Gpio,OutputPin};
|
||||||
|
|
||||||
const TEMP_WAIT:Duration = Duration::from_secs(3);
|
const TEMP_WAIT:Duration = Duration::from_secs(3);
|
||||||
const REBOOTS_SECTION: &str = "Reboots: ";
|
const REBOOTS_SECTION: &str = "Reboots";
|
||||||
const BP_SECTION: &str = "Successful BP tests: ";
|
const BP_SECTION: &str = "Successful BP tests";
|
||||||
const TEMP_SECTION: &str = "Successful temp tests: ";
|
const TEMP_SECTION: &str = "Successful temp tests";
|
||||||
const OUTPUT_FOLDER: &str = "output/";
|
const OUTPUT_FOLDER: &str = "output/";
|
||||||
|
const SECTION_SEPARATOR: &str = ": ";
|
||||||
const UNINITIALISED_SERIAL: &str = "uninitialised";
|
const UNINITIALISED_SERIAL: &str = "uninitialised";
|
||||||
const SERIAL_HEADER: &str = "DtCtrlCfgDeviceSerialNum";
|
const SERIAL_HEADER: &str = "DtCtrlCfgDeviceSerialNum";
|
||||||
#[derive(PartialEq,Debug)]
|
#[derive(PartialEq,Debug)]
|
||||||
|
@ -15,7 +16,8 @@ pub enum State{
|
||||||
LoginPrompt,
|
LoginPrompt,
|
||||||
DebugMenu,
|
DebugMenu,
|
||||||
LifecycleMenu,
|
LifecycleMenu,
|
||||||
BrightnessMenu
|
BrightnessMenu,
|
||||||
|
ShellPrompt
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -30,6 +32,7 @@ pub struct Device{
|
||||||
reboots: u64,
|
reboots: u64,
|
||||||
temps: u64,
|
temps: u64,
|
||||||
init_temps: u64,
|
init_temps: u64,
|
||||||
|
temp_offset: u64,
|
||||||
bps: u64
|
bps: u64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +41,7 @@ impl Device{
|
||||||
if ! Path::new(&OUTPUT_FOLDER).is_dir(){
|
if ! Path::new(&OUTPUT_FOLDER).is_dir(){
|
||||||
_ = fs::create_dir(&OUTPUT_FOLDER);
|
_ = fs::create_dir(&OUTPUT_FOLDER);
|
||||||
};
|
};
|
||||||
log::debug!("{:?}",&self.serial);
|
//log::debug!("{:?}",&self.serial);
|
||||||
let output_path:String = OUTPUT_FOLDER.to_owned() + &self.serial + ".txt";
|
let output_path:String = OUTPUT_FOLDER.to_owned() + &self.serial + ".txt";
|
||||||
if ! Path::new(&output_path).exists(){
|
if ! Path::new(&output_path).exists(){
|
||||||
log::debug!("Creating file {}",&output_path);
|
log::debug!("Creating file {}",&output_path);
|
||||||
|
@ -61,24 +64,29 @@ impl Device{
|
||||||
log::trace!("{:?}",file_contents);
|
log::trace!("{:?}",file_contents);
|
||||||
for line in file_lines {
|
for line in file_lines {
|
||||||
if line.len() > 0{
|
if line.len() > 0{
|
||||||
log::trace!("{:?}",line);
|
//log::trace!("{:?}",line);
|
||||||
let section_and_data:Vec<&str> = line.split(": ").collect();
|
let section_and_data:Vec<&str> = line.split(SECTION_SEPARATOR).collect();
|
||||||
let section:&str = section_and_data[0];
|
let section:&str = section_and_data[0];
|
||||||
let possible_value:Result<u64, std::num::ParseIntError> = section_and_data[1].trim().parse::<u64>();
|
let possible_value:Result<u64, std::num::ParseIntError> = section_and_data[1].trim().parse::<u64>();
|
||||||
match possible_value{
|
match possible_value{
|
||||||
Ok(value) => {
|
Ok(value) => {
|
||||||
log::trace!("{:?} value: [{:?}]",section,value);
|
//log::trace!("{:?} value: [{:?}]",section,value);
|
||||||
match section {
|
match section {
|
||||||
REBOOTS_SECTION => {
|
REBOOTS_SECTION => {
|
||||||
self.reboots = value;
|
self.reboots = value;
|
||||||
|
//log::trace!("Reboots set to {:?}",self.reboots);
|
||||||
},
|
},
|
||||||
BP_SECTION => {
|
BP_SECTION => {
|
||||||
self.bps = value;
|
self.bps = value.clone();
|
||||||
|
//log::trace!("BPS set to {:?}",self.bps);
|
||||||
},
|
},
|
||||||
TEMP_SECTION => {
|
TEMP_SECTION => {
|
||||||
self.temps = value;
|
self.temp_offset = value;
|
||||||
|
//log::trace!("Temp offset set to {:?}",self.temp_offset);
|
||||||
},
|
},
|
||||||
_ => ()
|
_ => {
|
||||||
|
log::warn!("Invalid import value: [{:?}]. Please ensure that the output directory is clean.",section_and_data);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
|
@ -105,6 +113,11 @@ impl Device{
|
||||||
usb_port.write_to_device(Command::Newline);
|
usb_port.write_to_device(Command::Newline);
|
||||||
_ = usb_port.read_from_device(None);
|
_ = usb_port.read_from_device(None);
|
||||||
initial_state = State::LoginPrompt;
|
initial_state = State::LoginPrompt;
|
||||||
|
},
|
||||||
|
Response::UBoot=>{
|
||||||
|
usb_port.write_to_device(Command::Boot);
|
||||||
|
while usb_port.read_from_device(None) != Response::LoginPrompt {}
|
||||||
|
initial_state = State::LoginPrompt;
|
||||||
},
|
},
|
||||||
//Response::Empty parsing here is potentially in bad faith
|
//Response::Empty parsing here is potentially in bad faith
|
||||||
Response::Other | Response::Empty | Response::ShellPrompt | Response::FailedDebugMenu | Response::DebugInit |
|
Response::Other | Response::Empty | Response::ShellPrompt | Response::FailedDebugMenu | Response::DebugInit |
|
||||||
|
@ -113,26 +126,40 @@ impl Device{
|
||||||
Response::BPOn | Response::BPOff | Response::TempCount(_) |
|
Response::BPOn | Response::BPOff | Response::TempCount(_) |
|
||||||
Response::DebugMenu=>{
|
Response::DebugMenu=>{
|
||||||
usb_port.write_to_device(Command::Quit);
|
usb_port.write_to_device(Command::Quit);
|
||||||
_ = usb_port.read_from_device(None);
|
|
||||||
usb_port.write_to_device(Command::Newline);
|
|
||||||
match usb_port.read_from_device(None){
|
match usb_port.read_from_device(None){
|
||||||
Response::Rebooting => {
|
Response::ShuttingDown | Response::Rebooting => {
|
||||||
while usb_port.read_from_device(None) != Response::LoginPrompt {}
|
while usb_port.read_from_device(None) != Response::LoginPrompt {}
|
||||||
initial_state = State::LoginPrompt;
|
initial_state = State::LoginPrompt;
|
||||||
},
|
},
|
||||||
Response::ShellPrompt => {
|
Response::ShellPrompt => {
|
||||||
usb_port.write_to_device(Command::Shutdown);
|
initial_state = State::ShellPrompt;
|
||||||
while usb_port.read_from_device(None) != Response::LoginPrompt {}
|
},
|
||||||
initial_state = State::LoginPrompt;
|
Response::DebugMenu => {
|
||||||
|
usb_port.write_to_device(Command::Newline);
|
||||||
|
match usb_port.read_from_device(None) {
|
||||||
|
Response::DebugMenu | Response::ShellPrompt => {
|
||||||
|
initial_state = State::ShellPrompt;
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
log::error!("Unknown state for TTY {:?}!!! Consult logs immediately.",usb_port);
|
||||||
|
log::debug!("Last known state: DebugMenu.");
|
||||||
|
log::debug!("Assumed but incorrect current state: successfully exited debug menu");
|
||||||
|
return Err("Failed TTY init. Unknown state, cannot trust.".to_string());
|
||||||
|
}
|
||||||
|
};
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
log::error!("Unknown state for TTY {:?}!!! Consult logs immediately.",usb_port);
|
log::error!("Unknown state for TTY {:?}!!! Consult logs immediately.",usb_port);
|
||||||
|
log::debug!("Last known state: DebugMenu.");
|
||||||
|
log::debug!("Assumed but incorrect current state: attempted to exit debug menu");
|
||||||
return Err("Failed TTY init. Unknown state, cannot trust.".to_string());
|
return Err("Failed TTY init. Unknown state, cannot trust.".to_string());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
//Serial response shouldn't exist, emptynewline is already filtered in main
|
||||||
Response::Serial(_) | Response::EmptyNewline => {
|
Response::Serial(_) | Response::EmptyNewline => {
|
||||||
log::error!("Unknown state for TTY {:?}!!! Consult logs immediately.",usb_port);
|
log::error!("Unknown state for TTY {:?}!!! Consult logs immediately.",usb_port);
|
||||||
|
log::debug!("How did I get here???");
|
||||||
return Err("Failed TTY init. Unknown state, cannot trust.".to_string());
|
return Err("Failed TTY init. Unknown state, cannot trust.".to_string());
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -152,6 +179,7 @@ impl Device{
|
||||||
current_state: initial_state,
|
current_state: initial_state,
|
||||||
reboots: 0,
|
reboots: 0,
|
||||||
temps: 0,
|
temps: 0,
|
||||||
|
temp_offset: 0,
|
||||||
init_temps: 0,
|
init_temps: 0,
|
||||||
bps: 0
|
bps: 0
|
||||||
};
|
};
|
||||||
|
@ -190,16 +218,18 @@ impl Device{
|
||||||
Response::PreShellPrompt | Response::Empty | Response::ShuttingDown |
|
Response::PreShellPrompt | Response::Empty | Response::ShuttingDown |
|
||||||
Response::DebugInit | Response::EmptyNewline | Response::Rebooting => {},
|
Response::DebugInit | Response::EmptyNewline | Response::Rebooting => {},
|
||||||
Response::PasswordPrompt => {self.usb_tty.write_to_device(Command::Newline);},
|
Response::PasswordPrompt => {self.usb_tty.write_to_device(Command::Newline);},
|
||||||
Response::ShellPrompt => break,
|
Response::FailedDebugMenu | Response::ShellPrompt => break,
|
||||||
_ => {
|
_ => {
|
||||||
log::error!("Unexpected response from device {}!",self.serial);
|
log::error!("Unexpected response from device {}!",self.serial);
|
||||||
log::debug!("brightness menu, catch-all, first loop, {}, {:?}",self.serial,self.usb_tty);
|
log::debug!("brightness menu, catch-all, login loop, {}, {:?}",self.serial,self.usb_tty);
|
||||||
log::error!("Unsure how to continue. Expect data from device {} to be erratic until next cycle.",self.serial);
|
log::error!("Unsure how to continue. Expect data from device {} to be erratic until next cycle.",self.serial);
|
||||||
break;
|
//break;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
//_ = self.usb_tty.read_from_device(None);
|
self.current_state = State::ShellPrompt;
|
||||||
|
},
|
||||||
|
State::ShellPrompt => {
|
||||||
self.usb_tty.write_to_device(Command::DebugMenu);
|
self.usb_tty.write_to_device(Command::DebugMenu);
|
||||||
loop {
|
loop {
|
||||||
match self.usb_tty.read_from_device(None) {
|
match self.usb_tty.read_from_device(None) {
|
||||||
|
@ -213,16 +243,13 @@ impl Device{
|
||||||
Response::DebugMenu =>
|
Response::DebugMenu =>
|
||||||
break,
|
break,
|
||||||
Response::FailedDebugMenu => {
|
Response::FailedDebugMenu => {
|
||||||
while self.usb_tty.read_from_device(None) != Response::LoginPrompt {};
|
|
||||||
self.usb_tty.write_to_device(Command::Login);
|
|
||||||
while self.usb_tty.read_from_device(None) != Response::ShellPrompt {};
|
|
||||||
self.usb_tty.write_to_device(Command::DebugMenu);
|
self.usb_tty.write_to_device(Command::DebugMenu);
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
log::error!("Unexpected response from device {}!", self.serial);
|
log::error!("Unexpected response from device {}!", self.serial);
|
||||||
log::debug!("brightness menu, catch-all, second loop, {}, {:?}",self.serial,self.usb_tty);
|
log::debug!("brightness menu, catch-all, shell prompt loop, {}, {:?}",self.serial,self.usb_tty);
|
||||||
log::error!("Unsure how to continue. Expect data from device {} to be erratic until next cycle.",self.serial);
|
log::error!("Unsure how to continue. Expect data from device {} to be erratic until next cycle.",self.serial);
|
||||||
break;
|
//break;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -261,15 +288,18 @@ impl Device{
|
||||||
Response::PreShellPrompt | Response::Empty | Response::ShuttingDown |
|
Response::PreShellPrompt | Response::Empty | Response::ShuttingDown |
|
||||||
Response::DebugInit | Response::EmptyNewline | Response::Rebooting => {},
|
Response::DebugInit | Response::EmptyNewline | Response::Rebooting => {},
|
||||||
Response::PasswordPrompt => {self.usb_tty.write_to_device(Command::Newline);},
|
Response::PasswordPrompt => {self.usb_tty.write_to_device(Command::Newline);},
|
||||||
Response::ShellPrompt => break,
|
Response::FailedDebugMenu | Response::ShellPrompt => break,
|
||||||
_ => {
|
_ => {
|
||||||
log::error!("Unexpected response from device {}!",self.serial);
|
log::error!("Unexpected response from device {}!",self.serial);
|
||||||
log::debug!("lifecycle menu, catch-all, first loop, {}, {:?}",self.serial,self.usb_tty);
|
log::debug!("lifecycle menu, catch-all, first loop, {}, {:?}",self.serial,self.usb_tty);
|
||||||
log::error!("Unsure how to continue. Expect data from device {} to be erratic until next cycle.",self.serial);
|
log::error!("Unsure how to continue. Expect data from device {} to be erratic until next cycle.",self.serial);
|
||||||
break;
|
//break;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
self.current_state = State::ShellPrompt;
|
||||||
|
},
|
||||||
|
State::ShellPrompt => {
|
||||||
self.usb_tty.write_to_device(Command::DebugMenu);
|
self.usb_tty.write_to_device(Command::DebugMenu);
|
||||||
loop {
|
loop {
|
||||||
let read_in = self.usb_tty.read_from_device(None);
|
let read_in = self.usb_tty.read_from_device(None);
|
||||||
|
@ -277,23 +307,18 @@ impl Device{
|
||||||
Response::PreShellPrompt | Response::Empty | Response::ShuttingDown |
|
Response::PreShellPrompt | Response::Empty | Response::ShuttingDown |
|
||||||
Response::DebugInit | Response::EmptyNewline | Response::Rebooting => {},
|
Response::DebugInit | Response::EmptyNewline | Response::Rebooting => {},
|
||||||
Response::LoginPrompt => {
|
Response::LoginPrompt => {
|
||||||
self.usb_tty.write_to_device(Command::Login);
|
|
||||||
while self.usb_tty.read_from_device(None) != Response::ShellPrompt {};
|
|
||||||
self.usb_tty.write_to_device(Command::DebugMenu);
|
self.usb_tty.write_to_device(Command::DebugMenu);
|
||||||
},
|
},
|
||||||
Response::DebugMenu =>
|
Response::DebugMenu =>
|
||||||
break,
|
break,
|
||||||
Response::FailedDebugMenu => {
|
Response::FailedDebugMenu => {
|
||||||
while self.usb_tty.read_from_device(None) != Response::LoginPrompt {};
|
|
||||||
self.usb_tty.write_to_device(Command::Login);
|
|
||||||
while self.usb_tty.read_from_device(None) != Response::ShellPrompt {};
|
|
||||||
self.usb_tty.write_to_device(Command::DebugMenu);
|
self.usb_tty.write_to_device(Command::DebugMenu);
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
log::error!("Unexpected response from device {}! {:?}", self.serial, read_in);
|
log::error!("Unexpected response from device {}! {:?}", self.serial, read_in);
|
||||||
log::debug!("lifecycle menu, catch-all, second loop, {}, {:?}",self.serial,self.usb_tty);
|
log::debug!("lifecycle menu, catch-all, second loop, {}, {:?}",self.serial,self.usb_tty);
|
||||||
log::error!("Unsure how to continue. Expect data from device {} to be erratic until next cycle.",self.serial);
|
log::error!("Unsure how to continue. Expect data from device {} to be erratic until next cycle.",self.serial);
|
||||||
break;
|
//break;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -318,21 +343,21 @@ impl Device{
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log::trace!("Writing to file: {:?}",self.output_file);
|
|
||||||
if let Some(ref mut file_name) = self.output_file{
|
if let Some(ref mut file_name) = self.output_file{
|
||||||
log::debug!("Writing to file!");
|
|
||||||
let mut output_data = REBOOTS_SECTION.to_string();
|
let mut output_data = REBOOTS_SECTION.to_string();
|
||||||
|
output_data.push_str(SECTION_SEPARATOR);
|
||||||
output_data.push_str(&self.reboots.to_string());
|
output_data.push_str(&self.reboots.to_string());
|
||||||
output_data.push_str("\n");
|
output_data.push_str("\n");
|
||||||
output_data.push_str(BP_SECTION);
|
output_data.push_str(BP_SECTION);
|
||||||
|
output_data.push_str(SECTION_SEPARATOR);
|
||||||
output_data.push_str(&self.bps.to_string());
|
output_data.push_str(&self.bps.to_string());
|
||||||
output_data.push_str("\n");
|
output_data.push_str("\n");
|
||||||
output_data.push_str(TEMP_SECTION);
|
output_data.push_str(TEMP_SECTION);
|
||||||
log::trace!("Current temps: [{}]",self.temps);
|
output_data.push_str(SECTION_SEPARATOR);
|
||||||
log::trace!("Initial temps: [{}]",self.init_temps);
|
let saved_temps = (self.temps - self.init_temps) + self.temp_offset;
|
||||||
let saved_temps = self.temps - self.init_temps;
|
|
||||||
output_data.push_str(&saved_temps.to_string());
|
output_data.push_str(&saved_temps.to_string());
|
||||||
output_data.push_str("\n");
|
output_data.push_str("\n");
|
||||||
|
log::debug!("final data to write to '{:?}': [{:?}]",file_name,output_data);
|
||||||
let temp = file_name.write_all(output_data.as_bytes());
|
let temp = file_name.write_all(output_data.as_bytes());
|
||||||
match temp{
|
match temp{
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
|
@ -347,39 +372,56 @@ impl Device{
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
pub fn auto_set_serial(&mut self) -> bool{
|
pub fn auto_set_serial(&mut self) -> bool{
|
||||||
self.reboot();
|
loop {
|
||||||
self.usb_tty.write_to_device(Command::Login);
|
match self.current_state {
|
||||||
while self.usb_tty.read_from_device(None) != Response::ShellPrompt {}
|
State::LoginPrompt => {
|
||||||
self.usb_tty.write_to_device(Command::GetSerial);
|
self.usb_tty.write_to_device(Command::Login);
|
||||||
loop{
|
while self.usb_tty.read_from_device(None) != Response::ShellPrompt {};
|
||||||
let return_value = self.usb_tty.read_from_device(None);
|
self.current_state = State::ShellPrompt;
|
||||||
match return_value{
|
},
|
||||||
Response::Serial(Some(contains_serial)) =>{
|
State::Shutdown => {
|
||||||
for line in contains_serial.split("\n").collect::<Vec<&str>>(){
|
while self.usb_tty.read_from_device(None) != Response::LoginPrompt{};
|
||||||
if !line.contains(':') { continue; }
|
self.current_state = State::LoginPrompt;
|
||||||
let (section,value) = line.split_once(':').unwrap();
|
},
|
||||||
if section.contains(SERIAL_HEADER){
|
State::DebugMenu | State::LifecycleMenu | State::BrightnessMenu => {
|
||||||
self.serial = value.trim().replace("\"","");
|
self.usb_tty.write_to_device(Command::Quit);
|
||||||
|
_ = self.usb_tty.read_from_device(None);
|
||||||
|
self.current_state = State::ShellPrompt;
|
||||||
|
},
|
||||||
|
State::ShellPrompt => {
|
||||||
|
self.usb_tty.write_to_device(Command::GetSerial);
|
||||||
|
loop{
|
||||||
|
let return_value = self.usb_tty.read_from_device(None);
|
||||||
|
match return_value{
|
||||||
|
Response::Serial(Some(contains_serial)) =>{
|
||||||
|
for line in contains_serial.split("\n").collect::<Vec<&str>>(){
|
||||||
|
if !line.contains(':') { continue; }
|
||||||
|
let (section,value) = line.split_once(':').unwrap();
|
||||||
|
if section.contains(SERIAL_HEADER){
|
||||||
|
self.serial = value.trim().replace("\"","");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log::info!("Serial found for device {}",self.serial);
|
||||||
|
break;
|
||||||
|
},
|
||||||
|
Response::DebugInit | Response::Empty | Response::EmptyNewline => { continue; }
|
||||||
|
_ => {
|
||||||
|
log::error!("Bad value: {:?}",return_value);
|
||||||
|
return false
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log::info!("Serial found for device {}",self.serial);
|
self.usb_tty.write_to_device(Command::DebugMenu);
|
||||||
break;
|
while self.usb_tty.read_from_device(None) != Response::DebugMenu {}
|
||||||
},
|
self.current_state = State::DebugMenu;
|
||||||
Response::DebugInit | Response::Empty | Response::EmptyNewline => { continue; }
|
self.load_values();
|
||||||
_ => {
|
self.save_values();
|
||||||
log::error!("Bad value: {:?}",return_value);
|
return true
|
||||||
return false
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.usb_tty.write_to_device(Command::DebugMenu);
|
|
||||||
while self.usb_tty.read_from_device(None) != Response::DebugMenu {}
|
|
||||||
self.current_state = State::DebugMenu;
|
|
||||||
self.reboot();
|
|
||||||
self.load_values();
|
|
||||||
self.save_values();
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn manual_set_serial(&mut self, serial:&str) -> &mut Self{
|
pub fn manual_set_serial(&mut self, serial:&str) -> &mut Self{
|
||||||
self.serial = serial.to_string();
|
self.serial = serial.to_string();
|
||||||
self.load_values();
|
self.load_values();
|
||||||
|
@ -523,19 +565,22 @@ impl Device{
|
||||||
}
|
}
|
||||||
pub fn reboot(&mut self) -> () {
|
pub fn reboot(&mut self) -> () {
|
||||||
self.usb_tty.write_to_device(Command::Quit);
|
self.usb_tty.write_to_device(Command::Quit);
|
||||||
|
self.usb_tty.write_to_device(Command::Reboot);
|
||||||
let mut successful_reboot:bool = false;
|
let mut successful_reboot:bool = false;
|
||||||
let mut exited_menu:bool = false;
|
//let mut exited_menu:bool = false;
|
||||||
loop{
|
loop{
|
||||||
match self.usb_tty.read_from_device(None){
|
match self.usb_tty.read_from_device(None){
|
||||||
Response::LoginPrompt => break,
|
Response::LoginPrompt => break,
|
||||||
Response::Rebooting => {
|
Response::Rebooting => {
|
||||||
log::trace!("Successful reboot detected for device {}.",self.serial);
|
log::trace!("Successful reboot detected for device {}.",self.serial);
|
||||||
successful_reboot = true;
|
successful_reboot = true;
|
||||||
if !exited_menu { log::info!("Unusual reboot detected for device {}. Please check logs.",self.serial); }
|
//This error message is turning out to be more false positive than anything
|
||||||
|
//else. Reboots can sometimes dump both reboot flag and shutdown flag at once.
|
||||||
|
//if !exited_menu { log::info!("Unusual reboot detected for device {}. Please check logs.",self.serial); }
|
||||||
},
|
},
|
||||||
Response::ShuttingDown => {
|
Response::ShuttingDown => {
|
||||||
log::trace!("Exiting debug menu on device {}.",self.serial);
|
log::trace!("Exiting debug menu on device {}.",self.serial);
|
||||||
exited_menu = true;
|
//exited_menu = true;
|
||||||
},
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
@ -570,7 +615,7 @@ impl Device{
|
||||||
log::trace!("Has bp ended on device {}? : {:?}",self.serial,bp_end);
|
log::trace!("Has bp ended on device {}? : {:?}",self.serial,bp_end);
|
||||||
if bp_start != bp_end {
|
if bp_start != bp_end {
|
||||||
self.bps +=1;
|
self.bps +=1;
|
||||||
log::debug!("Increasing bp count for device {} to {}",self.serial,self.bps);
|
log::trace!("Increasing bp count for device {} to {}",self.serial,self.bps);
|
||||||
self.save_values();
|
self.save_values();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
246
src/main.rs
246
src/main.rs
|
@ -11,7 +11,7 @@ use clap::Parser;
|
||||||
#[derive(Parser,Debug)]
|
#[derive(Parser,Debug)]
|
||||||
#[command(author,version,about)]
|
#[command(author,version,about)]
|
||||||
struct Args{
|
struct Args{
|
||||||
/// Print all logs to screen. Sets iteration count to 50000
|
/// Print all logs to screen, improves log verbosity. Sets iteration count to 50000
|
||||||
#[arg(short,long,action)]
|
#[arg(short,long,action)]
|
||||||
debug:bool,
|
debug:bool,
|
||||||
|
|
||||||
|
@ -19,13 +19,13 @@ struct Args{
|
||||||
#[arg(short,long,action)]
|
#[arg(short,long,action)]
|
||||||
manual:bool,
|
manual:bool,
|
||||||
|
|
||||||
/// Set iteration count from command line
|
/// Set iteration count from command line. Overrides debug iteration count.
|
||||||
#[arg(short,long)]
|
#[arg(short,long)]
|
||||||
iterations:Option<u64>
|
iterations:Option<u64>
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const VERSION:&str="2.3.1";
|
const VERSION:&str="2.3.3";
|
||||||
const DEBUG_ITERATION_COUNT:u64=50000;
|
const DEBUG_ITERATION_COUNT:u64=50000;
|
||||||
|
|
||||||
fn int_input_filtering(prompt:Option<&str>) -> u64{
|
fn int_input_filtering(prompt:Option<&str>) -> u64{
|
||||||
|
@ -58,128 +58,152 @@ fn input_filtering(prompt:Option<&str>) -> String{
|
||||||
log::debug!("{}:{}",internal_prompt,user_input);
|
log::debug!("{}:{}",internal_prompt,user_input);
|
||||||
return user_input;
|
return user_input;
|
||||||
}
|
}
|
||||||
|
//Path::new(&&str).is_dir() -> bool
|
||||||
fn main(){
|
fn main(){
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
setup_logs(&args.debug);
|
setup_logs(&args.debug);
|
||||||
log::info!("Seymour Life Testing version: {}",VERSION);
|
log::info!("Seymour Life Testing version: {}",VERSION);
|
||||||
if args.debug{
|
log::trace!("Debug enabled!");
|
||||||
log::debug!("Debug enabled!");
|
|
||||||
}
|
|
||||||
loop{
|
loop{
|
||||||
let mut iteration_count:u64 = 0;
|
let mut iteration_count:u64 = 0;
|
||||||
if args.debug {
|
if let Some(value) = args.iterations{
|
||||||
iteration_count = DEBUG_ITERATION_COUNT;
|
|
||||||
}
|
|
||||||
else if let Some(value) = args.iterations{
|
|
||||||
iteration_count = value;
|
iteration_count = value;
|
||||||
}
|
}
|
||||||
|
else if args.debug {
|
||||||
|
iteration_count = DEBUG_ITERATION_COUNT;
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
while iteration_count < 1{
|
while iteration_count < 1{
|
||||||
iteration_count = int_input_filtering(Some("Enter the number of iterations to complete: "));
|
iteration_count = int_input_filtering(Some("Enter the number of iterations to complete: "));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log::info!("Testing all available USB ports for connected devices. This may take several minutes, and devices may reboot several times.");
|
||||||
let gpio = &mut GpioPins::new();
|
let gpio = &mut GpioPins::new();
|
||||||
match std::fs::read_dir("/dev/serial/by-path"){
|
let mut available_ttys:Vec<Box<Path>> = Vec::new();
|
||||||
Ok(available_ttys)=>{
|
for entry in glob::glob("/dev/serial/*").expect("Failed to read glob pattern"){
|
||||||
let mut possible_devices:Vec<Option<Device>> = Vec::new();
|
match entry{
|
||||||
let mut tty_test_threads:Vec<JoinHandle<Option<Device>>> = Vec::new();
|
Ok(real_path) =>{
|
||||||
for possible_tty in available_ttys.into_iter(){
|
match fs::read_dir::<&Path>(real_path.as_ref()){
|
||||||
tty_test_threads.push(
|
Ok(possible_ttys) =>{
|
||||||
thread::spawn(move ||{
|
possible_ttys.into_iter().for_each(|tty| {
|
||||||
let tty_ref = possible_tty.as_ref();
|
if let Ok(single_tty) = tty {
|
||||||
match tty_ref{
|
available_ttys.push(single_tty.path().into());
|
||||||
Ok(tty_real_ref)=>{
|
|
||||||
let tty_path = tty_real_ref.path();
|
|
||||||
let tty_name = tty_path.to_string_lossy();
|
|
||||||
log::info!("Testing port {}. This may take a moment...",&tty_name);
|
|
||||||
let possible_port = TTY::new(&tty_name);
|
|
||||||
match possible_port{
|
|
||||||
Some(mut port) =>{
|
|
||||||
port.write_to_device(tty::Command::Newline);
|
|
||||||
let response = port.read_from_device(Some(":"));
|
|
||||||
if response != Response::Empty{
|
|
||||||
log::debug!("{} is valid port!",tty_name);
|
|
||||||
let new_device = Device::new(port,Some(response));
|
|
||||||
match new_device{
|
|
||||||
Ok(mut device) => {
|
|
||||||
device.darken_screen();
|
|
||||||
if !args.manual {
|
|
||||||
device.auto_set_serial();
|
|
||||||
}
|
|
||||||
Some(device)
|
|
||||||
},
|
|
||||||
Err(_) => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else { None }
|
|
||||||
},
|
|
||||||
None=>{None}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(error)=>{
|
|
||||||
log::debug!("{}",error);
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}));
|
break;
|
||||||
}
|
}
|
||||||
for thread in tty_test_threads{
|
Err(error) =>{
|
||||||
let output = thread.join().unwrap_or_else(|x|{log::trace!("{:?}",x); None});
|
log::error!("Invalid permissions to /dev directory... did you run with sudo?");
|
||||||
possible_devices.push(output);
|
log::error!("{}",error);
|
||||||
}
|
return;
|
||||||
|
|
||||||
let mut serials_set:bool = true;
|
|
||||||
let mut devices:Vec<Device> = Vec::new();
|
|
||||||
for possible_device in possible_devices.into_iter(){
|
|
||||||
if let Some(device) = possible_device{
|
|
||||||
if device.get_serial().eq("uninitialised"){
|
|
||||||
serials_set = false;
|
|
||||||
}
|
}
|
||||||
devices.push(device);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Err(error) =>{
|
||||||
log::info!("--------------------------------------");
|
log::error!("{}",error);
|
||||||
log::info!("Number of devices detected: {}",devices.len());
|
}
|
||||||
log::info!("--------------------------------------\n\n");
|
}
|
||||||
|
}
|
||||||
for device in devices.iter_mut(){
|
if available_ttys.is_empty(){
|
||||||
if !serials_set || args.manual {
|
for entry in glob::glob("/dev/ttyUSB*").expect("Unable to read glob"){
|
||||||
device.brighten_screen();
|
match entry{
|
||||||
device.manual_set_serial(&input_filtering(Some("Enter the serial of the device with the bright screen: ")).to_string());
|
Ok(possible_tty) => available_ttys.push(Path::new(&possible_tty).into()),
|
||||||
device.darken_screen();
|
Err(error) => {
|
||||||
}
|
log::error!("Invalid permissions to /dev directory... did you run with sudo?");
|
||||||
log::info!("Checking probe well of device {}",device.get_serial());
|
log::error!("{}",error);
|
||||||
log::debug!("Number of unassigned addresses: {}",gpio.get_unassigned_addresses().len());
|
|
||||||
if !find_gpio(device, gpio){
|
|
||||||
device.set_pin_address(21);
|
|
||||||
log::error!("Unable to find probe-well for device {}. Please ensure that the probe well is installed properly, and the calibration key is plugged in.",device.get_serial());
|
|
||||||
device.brighten_screen();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut iteration_threads = Vec::new();
|
if available_ttys.is_empty(){
|
||||||
while let Some(mut device) = devices.pop(){
|
log::error!("No serial devices detected! Please ensure all connections.");
|
||||||
iteration_threads.push(thread::spawn(move||{
|
return;
|
||||||
device.init_temp_count();
|
}
|
||||||
for i in 1..=iteration_count{
|
let mut possible_devices:Vec<Option<Device>> = Vec::new();
|
||||||
log::info!("Starting iteration {} of {} for device {}...",
|
let mut tty_test_threads:Vec<JoinHandle<Option<Device>>> = Vec::new();
|
||||||
i,iteration_count,device.get_serial());
|
for possible_tty in available_ttys.into_iter(){
|
||||||
device.test_cycle(None);
|
tty_test_threads.push(
|
||||||
}
|
thread::spawn(move ||{
|
||||||
}));
|
let tty_name = possible_tty.to_string_lossy();
|
||||||
|
log::debug!("Testing port {}",&tty_name);
|
||||||
|
let possible_port = TTY::new(&tty_name);
|
||||||
|
match possible_port{
|
||||||
|
Some(mut port) =>{
|
||||||
|
port.write_to_device(tty::Command::Newline);
|
||||||
|
let response = port.read_from_device(Some(":"));
|
||||||
|
if response != Response::Empty{
|
||||||
|
log::debug!("{} is valid port!",tty_name);
|
||||||
|
let new_device = Device::new(port,Some(response));
|
||||||
|
match new_device{
|
||||||
|
Ok(mut device) => {
|
||||||
|
device.darken_screen();
|
||||||
|
if !args.manual {
|
||||||
|
device.auto_set_serial();
|
||||||
|
}
|
||||||
|
Some(device)
|
||||||
|
},
|
||||||
|
Err(_) => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else { None }
|
||||||
|
},
|
||||||
|
None=>{None}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
for thread in tty_test_threads{
|
||||||
|
let output = thread.join().unwrap_or_else(|x|{log::trace!("{:?}",x); None});
|
||||||
|
possible_devices.push(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut serials_set:bool = true;
|
||||||
|
let mut devices:Vec<Device> = Vec::new();
|
||||||
|
for possible_device in possible_devices.into_iter(){
|
||||||
|
if let Some(device) = possible_device{
|
||||||
|
if device.get_serial().eq("uninitialised"){
|
||||||
|
serials_set = false;
|
||||||
}
|
}
|
||||||
for thread in iteration_threads{
|
devices.push(device);
|
||||||
thread.join().unwrap();
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log::info!("--------------------------------------");
|
||||||
|
log::info!("Number of devices detected: {}",devices.len());
|
||||||
|
log::info!("--------------------------------------\n\n");
|
||||||
|
|
||||||
|
log::info!("Setting up probe wells for all devices. This may take several minutes...");
|
||||||
|
for device in devices.iter_mut(){
|
||||||
|
if !serials_set || args.manual {
|
||||||
|
device.brighten_screen();
|
||||||
|
device.manual_set_serial(&input_filtering(Some("Enter the serial of the device with the bright screen: ")).to_string());
|
||||||
|
device.darken_screen();
|
||||||
|
}
|
||||||
|
log::info!("Checking probe well of device {}",device.get_serial());
|
||||||
|
log::debug!("Number of unassigned addresses: {}",gpio.get_unassigned_addresses().len());
|
||||||
|
if !find_gpio(device, gpio){
|
||||||
|
device.set_pin_address(21);
|
||||||
|
log::error!("Unable to find probe-well for device {}. Please ensure that the probe well is installed properly, and the calibration key is plugged in.",device.get_serial());
|
||||||
|
device.brighten_screen();
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut iteration_threads = Vec::new();
|
||||||
|
while let Some(mut device) = devices.pop(){
|
||||||
|
iteration_threads.push(thread::spawn(move||{
|
||||||
|
device.init_temp_count();
|
||||||
|
for i in 1..=iteration_count{
|
||||||
|
log::info!("Starting iteration {} of {} for device {}...",
|
||||||
|
i,iteration_count,device.get_serial());
|
||||||
|
device.test_cycle(None);
|
||||||
}
|
}
|
||||||
}
|
}));
|
||||||
Err(_)=>{
|
}
|
||||||
log::error!("Invalid serial location! Please make sure that /dev/serial/by-path exists.");
|
for thread in iteration_threads{
|
||||||
break;
|
thread.join().unwrap();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if input_filtering(Some("Would you like to run the tests again? (y/N): ")).to_string().contains("y") {}
|
if input_filtering(Some("Would you like to run the tests again? (y/N): ")).to_string().contains("y") {}
|
||||||
else { break; }
|
else { break; }
|
||||||
|
@ -217,14 +241,18 @@ pub fn setup_logs(debug:&bool){
|
||||||
message
|
message
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
.chain(
|
.chain({
|
||||||
fern::Dispatch::new()
|
let mut file_logger = fern::Dispatch::new();
|
||||||
.level(log::LevelFilter::Trace)
|
let date_format = chrono_now.format("%Y-%m-%d_%H.%M").to_string();
|
||||||
.chain(fern::log_file(
|
let local_log_file = fern::log_file(format!("logs/{}.log",date_format)).unwrap();
|
||||||
format!("logs/{0}.log",
|
if *debug{
|
||||||
chrono_now.format("%Y-%m-%d_%H.%M").to_string()
|
file_logger = file_logger.level(log::LevelFilter::Trace);
|
||||||
)).unwrap()),
|
}
|
||||||
)
|
else {
|
||||||
|
file_logger = file_logger.level(log::LevelFilter::Debug);
|
||||||
|
}
|
||||||
|
file_logger.chain(local_log_file)
|
||||||
|
})
|
||||||
.chain({
|
.chain({
|
||||||
let mut stdout_logger = fern::Dispatch::new();
|
let mut stdout_logger = fern::Dispatch::new();
|
||||||
if *debug {
|
if *debug {
|
||||||
|
|
64
src/tty.rs
64
src/tty.rs
|
@ -7,7 +7,7 @@ use serialport::SerialPort;
|
||||||
use derivative::Derivative;
|
use derivative::Derivative;
|
||||||
|
|
||||||
const BAUD_RATE:u32 = 115200;
|
const BAUD_RATE:u32 = 115200;
|
||||||
const SERIAL_READ_TIMEOUT: std::time::Duration = Duration::from_millis(500);
|
const SERIAL_TIMEOUT: std::time::Duration = Duration::from_millis(500);
|
||||||
|
|
||||||
|
|
||||||
#[derive(Eq,Derivative,Debug)]
|
#[derive(Eq,Derivative,Debug)]
|
||||||
|
@ -26,8 +26,9 @@ pub enum Command{
|
||||||
Login,
|
Login,
|
||||||
DebugMenu,
|
DebugMenu,
|
||||||
Newline,
|
Newline,
|
||||||
Shutdown,
|
Reboot,
|
||||||
GetSerial,
|
GetSerial,
|
||||||
|
Boot,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone,Eq,Derivative,Debug)]
|
#[derive(Clone,Eq,Derivative,Debug)]
|
||||||
|
@ -49,6 +50,7 @@ pub enum Response{
|
||||||
EmptyNewline,
|
EmptyNewline,
|
||||||
DebugInit,
|
DebugInit,
|
||||||
Serial(Option<String>),
|
Serial(Option<String>),
|
||||||
|
UBoot,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -64,31 +66,39 @@ const COMMAND_MAP:Lazy<HashMap<Command,&str>> = Lazy::new(||HashMap::from([
|
||||||
(Command::UpMenuLevel, "\\"),
|
(Command::UpMenuLevel, "\\"),
|
||||||
(Command::Login,"root\n"),
|
(Command::Login,"root\n"),
|
||||||
(Command::RedrawMenu,"?"),
|
(Command::RedrawMenu,"?"),
|
||||||
(Command::DebugMenu," python3 -m debugmenu; shutdown -r now\n"),
|
(Command::DebugMenu,"python3 -m debugmenu\n"),
|
||||||
(Command::Newline,"\n"),
|
(Command::Newline,"\n"),
|
||||||
(Command::Shutdown,"shutdown -r now\n"),
|
(Command::Reboot,"shutdown -r now\n"),
|
||||||
|
(Command::Boot,"boot\n"),
|
||||||
(Command::GetSerial,"echo 'y1q' | python3 -m debugmenu\n"),
|
(Command::GetSerial,"echo 'y1q' | python3 -m debugmenu\n"),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
const RESPONSES:[(&str,Response);13] = [
|
const COMMAND_RESPONSES: [&str;3] = [
|
||||||
|
"python3 -m debugmenu",
|
||||||
|
"q",
|
||||||
|
"root",
|
||||||
|
];
|
||||||
|
|
||||||
|
const RESPONSES:[(&str,Response);14] = [
|
||||||
|
("uboot=>",Response::UBoot),
|
||||||
("Last login:",Response::PreShellPrompt),
|
("Last login:",Response::PreShellPrompt),
|
||||||
("reboot: Restarting",Response::Rebooting),
|
("reboot: Restarting",Response::Rebooting),
|
||||||
("command not found",Response::FailedDebugMenu),
|
("command not found",Response::FailedDebugMenu),
|
||||||
("login:",Response::LoginPrompt),
|
("login:",Response::LoginPrompt),
|
||||||
("Password:",Response::PasswordPrompt),
|
("Password:",Response::PasswordPrompt),
|
||||||
("DtCtrlCfgDeviceSerialNum",Response::Serial(None)),
|
("DtCtrlCfgDeviceSerialNum",Response::Serial(None)),
|
||||||
("root@",Response::ShellPrompt),
|
|
||||||
("EXIT Debug menu",Response::ShuttingDown),
|
|
||||||
("Check NIBP In Progress: True",Response::BPOn),
|
("Check NIBP In Progress: True",Response::BPOn),
|
||||||
("Check NIBP In Progress: False",Response::BPOff),
|
("Check NIBP In Progress: False",Response::BPOff),
|
||||||
("SureTemp Probe Pulls:",Response::TempCount(None)),
|
("SureTemp Probe Pulls:",Response::TempCount(None)),
|
||||||
(">",Response::DebugMenu),
|
(">",Response::DebugMenu),
|
||||||
("Loading App-Framework",Response::DebugInit),
|
("Loading App-Framework",Response::DebugInit),
|
||||||
|
("root@",Response::ShellPrompt),
|
||||||
|
("EXIT Debug menu",Response::ShuttingDown),
|
||||||
];
|
];
|
||||||
|
|
||||||
pub struct TTY{
|
pub struct TTY{
|
||||||
tty: Box<dyn SerialPort>,
|
tty: Box<dyn SerialPort>,
|
||||||
failed_read_count: u8
|
last: Command,
|
||||||
}
|
}
|
||||||
impl std::fmt::Debug for TTY{
|
impl std::fmt::Debug for TTY{
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result{
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result{
|
||||||
|
@ -112,23 +122,24 @@ impl std::fmt::Debug for TTY{
|
||||||
|
|
||||||
impl TTY{
|
impl TTY{
|
||||||
pub fn new(serial_location:&str) -> Option<Self>{
|
pub fn new(serial_location:&str) -> Option<Self>{
|
||||||
let possible_tty = serialport::new(serial_location,BAUD_RATE).timeout(SERIAL_READ_TIMEOUT).open();
|
let possible_tty = serialport::new(serial_location,BAUD_RATE).timeout(SERIAL_TIMEOUT).open();
|
||||||
if let Ok(tty) = possible_tty{
|
if let Ok(tty) = possible_tty{
|
||||||
Some(TTY {
|
Some(TTY{tty,last:Command::Quit})
|
||||||
tty,
|
|
||||||
failed_read_count: 0
|
|
||||||
})
|
|
||||||
} else{
|
} else{
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_to_device(&mut self,command:Command) -> bool {
|
pub fn write_to_device(&mut self,command:Command) -> bool {
|
||||||
log::trace!("writing {:?} to tty {}...", command, self.tty.name().unwrap_or("unknown".to_string()));
|
if command == self.last{
|
||||||
|
log::trace!("retry send {}",self.tty.name().unwrap_or("unknown".to_string()));
|
||||||
|
}else{
|
||||||
|
log::debug!("writing {:?} to tty {}...", command, self.tty.name().unwrap_or("unknown".to_string()));
|
||||||
|
};
|
||||||
let output = self.tty.write_all(COMMAND_MAP.get(&command).unwrap().as_bytes()).is_ok();
|
let output = self.tty.write_all(COMMAND_MAP.get(&command).unwrap().as_bytes()).is_ok();
|
||||||
|
self.last = command;
|
||||||
_ = self.tty.flush();
|
_ = self.tty.flush();
|
||||||
if command == Command::Login { std::thread::sleep(std::time::Duration::from_secs(2)); }
|
std::thread::sleep(SERIAL_TIMEOUT);
|
||||||
std::thread::sleep(std::time::Duration::from_millis(500));
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,7 +151,12 @@ impl TTY{
|
||||||
let read_line:String = String::from_utf8_lossy(read_buffer.as_slice()).to_string();
|
let read_line:String = String::from_utf8_lossy(read_buffer.as_slice()).to_string();
|
||||||
if read_line.eq("\r\n") {
|
if read_line.eq("\r\n") {
|
||||||
return Response::EmptyNewline;
|
return Response::EmptyNewline;
|
||||||
}
|
}
|
||||||
|
for command in COMMAND_RESPONSES{
|
||||||
|
if read_line.trim().eq(command.trim()){
|
||||||
|
return self.read_from_device(None);
|
||||||
|
}
|
||||||
|
};
|
||||||
for (string,enum_value) in RESPONSES{
|
for (string,enum_value) in RESPONSES{
|
||||||
if read_line.contains(string){
|
if read_line.contains(string){
|
||||||
if(enum_value == Response::BPOn) || (enum_value == Response::BPOff) {
|
if(enum_value == Response::BPOn) || (enum_value == Response::BPOff) {
|
||||||
|
@ -150,7 +166,6 @@ impl TTY{
|
||||||
else{
|
else{
|
||||||
log::trace!("Successful read of {:?} from tty {}, which matches pattern {:?}",read_line,self.tty.name().unwrap_or("unknown shell".to_string()),enum_value);
|
log::trace!("Successful read of {:?} from tty {}, which matches pattern {:?}",read_line,self.tty.name().unwrap_or("unknown shell".to_string()),enum_value);
|
||||||
};
|
};
|
||||||
self.failed_read_count = 0;
|
|
||||||
if enum_value == Response::TempCount(None){
|
if enum_value == Response::TempCount(None){
|
||||||
let mut lines = read_line.lines();
|
let mut lines = read_line.lines();
|
||||||
while let Some(single_line) = lines.next(){
|
while let Some(single_line) = lines.next(){
|
||||||
|
@ -165,7 +180,6 @@ impl TTY{
|
||||||
return Response::TempCount(None)
|
return Response::TempCount(None)
|
||||||
},
|
},
|
||||||
Ok(parsed_temp_count) => {
|
Ok(parsed_temp_count) => {
|
||||||
//log::trace!("Header: {}",header);
|
|
||||||
log::trace!("parsed temp count for device {}: {}",self.tty.name().unwrap_or("unknown shell".to_string()),temp_count);
|
log::trace!("parsed temp count for device {}: {}",self.tty.name().unwrap_or("unknown shell".to_string()),temp_count);
|
||||||
return Response::TempCount(Some(parsed_temp_count))
|
return Response::TempCount(Some(parsed_temp_count))
|
||||||
}
|
}
|
||||||
|
@ -192,17 +206,7 @@ impl TTY{
|
||||||
return Response::Other;
|
return Response::Other;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
log::debug!("Read an empty string from device {:?}. Possible read error.", self);
|
log::trace!("Read an empty string from device {:?}. Possible read error.", self);
|
||||||
//Due to a linux kernel power-saving setting that is overly complicated to fix,
|
|
||||||
//Serial connections will drop for a moment before re-opening, at seemingly-random
|
|
||||||
//intervals. The below is an attempt to catch and recover from this behaviour.
|
|
||||||
self.failed_read_count += 1;
|
|
||||||
if self.failed_read_count >= 15{
|
|
||||||
self.failed_read_count = 0;
|
|
||||||
let tty_location = self.tty.name().expect("Unable to read tty name!");
|
|
||||||
self.tty = serialport::new(tty_location,BAUD_RATE).timeout(SERIAL_READ_TIMEOUT).open().expect("Unable to open serial connection!");
|
|
||||||
return self.read_from_device(_break_char);
|
|
||||||
}
|
|
||||||
return Response::Empty;
|
return Response::Empty;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue