Compare commits
56 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 | |||
ed897d6ff7 | |||
e73dac51f5 | |||
c501f6c5e6 | |||
10606b14ed |
14 changed files with 425 additions and 288 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
|
||||
/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
|
38
Cargo.lock
generated
38
Cargo.lock
generated
|
@ -302,6 +302,12 @@ dependencies = [
|
|||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
|
@ -382,26 +388,6 @@ version = "0.2.142"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "link-cplusplus"
|
||||
version = "1.0.8"
|
||||
|
@ -486,12 +472,6 @@ version = "1.17.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.20+deprecated"
|
||||
|
@ -630,7 +610,6 @@ dependencies = [
|
|||
"IOKit-sys",
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"libudev",
|
||||
"mach 0.3.2",
|
||||
"nix",
|
||||
"regex",
|
||||
|
@ -638,13 +617,14 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "seymour_life_rust"
|
||||
version = "2.3.0"
|
||||
name = "seymour_life"
|
||||
version = "2.3.3"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap",
|
||||
"derivative",
|
||||
"fern",
|
||||
"glob",
|
||||
"log",
|
||||
"once_cell",
|
||||
"rppal",
|
||||
|
|
10
Cargo.toml
10
Cargo.toml
|
@ -1,15 +1,18 @@
|
|||
#This feature is currently limited to nightly versions of cargo.
|
||||
#cargo-features = ["per-package-target"]
|
||||
|
||||
[package]
|
||||
name = "seymour_life_rust"
|
||||
version = "2.3.0"
|
||||
name = "seymour_life"
|
||||
version = "2.3.3"
|
||||
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
|
||||
|
||||
[dependencies]
|
||||
rppal = "0.14.1"
|
||||
serialport = "4.2.0"
|
||||
serialport = { version = "4.2.0", default-features = false }
|
||||
log = "0.4"
|
||||
fern = "0.6.2"
|
||||
chrono = "0.4.24"
|
||||
|
@ -17,6 +20,7 @@ once_cell = "1.17.1"
|
|||
derivative = "2.2.0"
|
||||
time = "0.2.23"
|
||||
clap = { version = "4.3.2", features = ["derive"] }
|
||||
glob = "0.3.1"
|
||||
|
||||
[dev-dependencies]
|
||||
time = "0.2.23"
|
||||
|
|
61
README.md
Normal file
61
README.md
Normal file
|
@ -0,0 +1,61 @@
|
|||
[](https://ci.blizzard.systems/blizzardfinnegan/seymourLifeRust)
|
||||
# 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.
|
||||
|
||||
Note that this project will ONLY run properly on a Raspberry Pi. It is developed on, and tested for, the Raspberry Pi 400. This should also be compatible with the Raspberry Pi 4, and Raspberry Pi 3, although they have not been explicitly tested. Older models of Raspberry Pi may work properly, however as this project is originally intended for the `aarch64`/`ARM64` architecture, and older compatibility will not be tested.
|
||||
|
||||
## Install
|
||||
|
||||
### Precompiled Binary
|
||||
|
||||
Pre-compiled binaries can be found in the [releases tab](https://git.blizzard.systems/blizzardfinnegan/seymourLifeRust/releases/latest). Download the preferred version to your preferred directory, then run the following command to make the code executable:
|
||||
|
||||
```bash
|
||||
sudo chmod u+x ./seymour_life
|
||||
```
|
||||
|
||||
To run the binary, simply run:
|
||||
```bash
|
||||
sudo ./seymour_life
|
||||
```
|
||||
|
||||
Note that this command MUST be run as `sudo`/root, due to the way it interacts with GPIO. For more information, please see [the GPIO documentation](https://github.com/golemparts/rppal).
|
||||
|
||||
## Build From Source
|
||||
|
||||
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:
|
||||
```bash
|
||||
git clone https://git.blizzard.systems/blizzardfinnegan/seymourLifeRust
|
||||
```
|
||||
|
||||
Once the repository has been downloaded, the project can be built with `cargo`. This can be done using any of the listed install methods on [the Rust install website](https://rustup.rs/).
|
||||
|
||||
Once cargo has been installed, run the following to build the project:
|
||||
```bash
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
The runnable command can then be run by the following:
|
||||
```bash
|
||||
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.
|
||||
|
||||
|
||||
## Cross-Compilation
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
cargo build --target aarch64-unknown-linux-musl
|
||||
# OR
|
||||
cargo build --release --target aarch64-unknown-linux-musl
|
||||
```
|
||||
|
||||
|
|
@ -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.mkShell {
|
||||
nativeBuildInputs = with pkgs; [ cargo rustc pkg-config libudev-zero ];
|
||||
pkgs.mkShell rec {
|
||||
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}"
|
||||
'';
|
||||
}
|
||||
|
|
189
src/device.rs
189
src/device.rs
|
@ -3,10 +3,11 @@ use crate::tty::{TTY, Response,Command};
|
|||
use rppal::gpio::{Gpio,OutputPin};
|
||||
|
||||
const TEMP_WAIT:Duration = Duration::from_secs(3);
|
||||
const REBOOTS_SECTION: &str = "Reboots: ";
|
||||
const BP_SECTION: &str = "Successful BP tests: ";
|
||||
const TEMP_SECTION: &str = "Successful temp tests: ";
|
||||
const REBOOTS_SECTION: &str = "Reboots";
|
||||
const BP_SECTION: &str = "Successful BP tests";
|
||||
const TEMP_SECTION: &str = "Successful temp tests";
|
||||
const OUTPUT_FOLDER: &str = "output/";
|
||||
const SECTION_SEPARATOR: &str = ": ";
|
||||
const UNINITIALISED_SERIAL: &str = "uninitialised";
|
||||
const SERIAL_HEADER: &str = "DtCtrlCfgDeviceSerialNum";
|
||||
#[derive(PartialEq,Debug)]
|
||||
|
@ -15,7 +16,8 @@ pub enum State{
|
|||
LoginPrompt,
|
||||
DebugMenu,
|
||||
LifecycleMenu,
|
||||
BrightnessMenu
|
||||
BrightnessMenu,
|
||||
ShellPrompt
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -30,6 +32,7 @@ pub struct Device{
|
|||
reboots: u64,
|
||||
temps: u64,
|
||||
init_temps: u64,
|
||||
temp_offset: u64,
|
||||
bps: u64
|
||||
}
|
||||
|
||||
|
@ -38,7 +41,7 @@ impl Device{
|
|||
if ! Path::new(&OUTPUT_FOLDER).is_dir(){
|
||||
_ = fs::create_dir(&OUTPUT_FOLDER);
|
||||
};
|
||||
log::debug!("{:?}",&self.serial);
|
||||
//log::debug!("{:?}",&self.serial);
|
||||
let output_path:String = OUTPUT_FOLDER.to_owned() + &self.serial + ".txt";
|
||||
if ! Path::new(&output_path).exists(){
|
||||
log::debug!("Creating file {}",&output_path);
|
||||
|
@ -61,24 +64,29 @@ impl Device{
|
|||
log::trace!("{:?}",file_contents);
|
||||
for line in file_lines {
|
||||
if line.len() > 0{
|
||||
log::trace!("{:?}",line);
|
||||
let section_and_data:Vec<&str> = line.split(": ").collect();
|
||||
//log::trace!("{:?}",line);
|
||||
let section_and_data:Vec<&str> = line.split(SECTION_SEPARATOR).collect();
|
||||
let section:&str = section_and_data[0];
|
||||
let possible_value:Result<u64, std::num::ParseIntError> = section_and_data[1].trim().parse::<u64>();
|
||||
match possible_value{
|
||||
Ok(value) => {
|
||||
log::trace!("{:?} value: [{:?}]",section,value);
|
||||
//log::trace!("{:?} value: [{:?}]",section,value);
|
||||
match section {
|
||||
REBOOTS_SECTION => {
|
||||
self.reboots = value;
|
||||
//log::trace!("Reboots set to {:?}",self.reboots);
|
||||
},
|
||||
BP_SECTION => {
|
||||
self.bps = value;
|
||||
self.bps = value.clone();
|
||||
//log::trace!("BPS set to {:?}",self.bps);
|
||||
},
|
||||
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(_) => {
|
||||
|
@ -105,6 +113,11 @@ impl Device{
|
|||
usb_port.write_to_device(Command::Newline);
|
||||
_ = usb_port.read_from_device(None);
|
||||
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::Other | Response::Empty | Response::ShellPrompt | Response::FailedDebugMenu | Response::DebugInit |
|
||||
|
@ -113,26 +126,40 @@ impl Device{
|
|||
Response::BPOn | Response::BPOff | Response::TempCount(_) |
|
||||
Response::DebugMenu=>{
|
||||
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){
|
||||
Response::Rebooting => {
|
||||
Response::ShuttingDown | Response::Rebooting => {
|
||||
while usb_port.read_from_device(None) != Response::LoginPrompt {}
|
||||
initial_state = State::LoginPrompt;
|
||||
},
|
||||
Response::ShellPrompt => {
|
||||
usb_port.write_to_device(Command::Shutdown);
|
||||
while usb_port.read_from_device(None) != Response::LoginPrompt {}
|
||||
initial_state = State::LoginPrompt;
|
||||
initial_state = State::ShellPrompt;
|
||||
},
|
||||
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::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());
|
||||
}
|
||||
};
|
||||
},
|
||||
//Serial response shouldn't exist, emptynewline is already filtered in main
|
||||
Response::Serial(_) | Response::EmptyNewline => {
|
||||
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());
|
||||
},
|
||||
};
|
||||
|
@ -152,6 +179,7 @@ impl Device{
|
|||
current_state: initial_state,
|
||||
reboots: 0,
|
||||
temps: 0,
|
||||
temp_offset: 0,
|
||||
init_temps: 0,
|
||||
bps: 0
|
||||
};
|
||||
|
@ -190,16 +218,18 @@ impl Device{
|
|||
Response::PreShellPrompt | Response::Empty | Response::ShuttingDown |
|
||||
Response::DebugInit | Response::EmptyNewline | Response::Rebooting => {},
|
||||
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::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);
|
||||
break;
|
||||
//break;
|
||||
},
|
||||
};
|
||||
};
|
||||
//_ = self.usb_tty.read_from_device(None);
|
||||
self.current_state = State::ShellPrompt;
|
||||
},
|
||||
State::ShellPrompt => {
|
||||
self.usb_tty.write_to_device(Command::DebugMenu);
|
||||
loop {
|
||||
match self.usb_tty.read_from_device(None) {
|
||||
|
@ -213,16 +243,13 @@ impl Device{
|
|||
Response::DebugMenu =>
|
||||
break,
|
||||
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);
|
||||
},
|
||||
_ => {
|
||||
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);
|
||||
break;
|
||||
//break;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
@ -261,15 +288,18 @@ impl Device{
|
|||
Response::PreShellPrompt | Response::Empty | Response::ShuttingDown |
|
||||
Response::DebugInit | Response::EmptyNewline | Response::Rebooting => {},
|
||||
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::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);
|
||||
break;
|
||||
//break;
|
||||
},
|
||||
};
|
||||
};
|
||||
self.current_state = State::ShellPrompt;
|
||||
},
|
||||
State::ShellPrompt => {
|
||||
self.usb_tty.write_to_device(Command::DebugMenu);
|
||||
loop {
|
||||
let read_in = self.usb_tty.read_from_device(None);
|
||||
|
@ -277,23 +307,18 @@ impl Device{
|
|||
Response::PreShellPrompt | Response::Empty | Response::ShuttingDown |
|
||||
Response::DebugInit | Response::EmptyNewline | Response::Rebooting => {},
|
||||
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);
|
||||
},
|
||||
Response::DebugMenu =>
|
||||
break,
|
||||
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);
|
||||
},
|
||||
_ => {
|
||||
log::error!("Unexpected response from device {}! {:?}", self.serial, read_in);
|
||||
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);
|
||||
break;
|
||||
//break;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
@ -318,21 +343,21 @@ impl Device{
|
|||
return false
|
||||
}
|
||||
}
|
||||
log::trace!("Writing to file: {:?}",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();
|
||||
output_data.push_str(SECTION_SEPARATOR);
|
||||
output_data.push_str(&self.reboots.to_string());
|
||||
output_data.push_str("\n");
|
||||
output_data.push_str(BP_SECTION);
|
||||
output_data.push_str(SECTION_SEPARATOR);
|
||||
output_data.push_str(&self.bps.to_string());
|
||||
output_data.push_str("\n");
|
||||
output_data.push_str(TEMP_SECTION);
|
||||
log::trace!("Current temps: [{}]",self.temps);
|
||||
log::trace!("Initial temps: [{}]",self.init_temps);
|
||||
let saved_temps = self.temps - self.init_temps;
|
||||
output_data.push_str(SECTION_SEPARATOR);
|
||||
let saved_temps = (self.temps - self.init_temps) + self.temp_offset;
|
||||
output_data.push_str(&saved_temps.to_string());
|
||||
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());
|
||||
match temp{
|
||||
Err(error) => {
|
||||
|
@ -346,41 +371,64 @@ impl Device{
|
|||
}
|
||||
return true
|
||||
}
|
||||
pub fn set_serial(&mut self) -> &mut Self{
|
||||
self.reboot();
|
||||
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::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("\"","");
|
||||
pub fn auto_set_serial(&mut self) -> bool{
|
||||
loop {
|
||||
match self.current_state {
|
||||
State::LoginPrompt => {
|
||||
self.usb_tty.write_to_device(Command::Login);
|
||||
while self.usb_tty.read_from_device(None) != Response::ShellPrompt {};
|
||||
self.current_state = State::ShellPrompt;
|
||||
},
|
||||
State::Shutdown => {
|
||||
while self.usb_tty.read_from_device(None) != Response::LoginPrompt{};
|
||||
self.current_state = State::LoginPrompt;
|
||||
},
|
||||
State::DebugMenu | State::LifecycleMenu | State::BrightnessMenu => {
|
||||
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);
|
||||
break;
|
||||
},
|
||||
Response::DebugInit | Response::Empty | Response::EmptyNewline => { continue; }
|
||||
_ => {
|
||||
log::error!("Bad value: {:?}",return_value);
|
||||
todo!();
|
||||
self.usb_tty.write_to_device(Command::DebugMenu);
|
||||
while self.usb_tty.read_from_device(None) != Response::DebugMenu {}
|
||||
self.current_state = State::DebugMenu;
|
||||
self.load_values();
|
||||
self.save_values();
|
||||
return true
|
||||
},
|
||||
}
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
pub fn manual_set_serial(&mut self, serial:&str) -> &mut Self{
|
||||
self.serial = serial.to_string();
|
||||
self.load_values();
|
||||
self.save_values();
|
||||
return self;
|
||||
}
|
||||
pub fn get_serial(&mut self) -> &str{
|
||||
pub fn get_serial(&self) -> &str{
|
||||
&self.serial
|
||||
}
|
||||
pub fn get_location(&mut self) -> String{
|
||||
|
@ -517,19 +565,22 @@ impl Device{
|
|||
}
|
||||
pub fn reboot(&mut self) -> () {
|
||||
self.usb_tty.write_to_device(Command::Quit);
|
||||
self.usb_tty.write_to_device(Command::Reboot);
|
||||
let mut successful_reboot:bool = false;
|
||||
let mut exited_menu:bool = false;
|
||||
//let mut exited_menu:bool = false;
|
||||
loop{
|
||||
match self.usb_tty.read_from_device(None){
|
||||
Response::LoginPrompt => break,
|
||||
Response::Rebooting => {
|
||||
log::trace!("Successful reboot detected for device {}.",self.serial);
|
||||
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 => {
|
||||
log::trace!("Exiting debug menu on device {}.",self.serial);
|
||||
exited_menu = true;
|
||||
//exited_menu = true;
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
|
@ -564,7 +615,7 @@ impl Device{
|
|||
log::trace!("Has bp ended on device {}? : {:?}",self.serial,bp_end);
|
||||
if bp_start != bp_end {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
254
src/main.rs
254
src/main.rs
|
@ -1,6 +1,6 @@
|
|||
use seymour_life_rust::{device::Device,
|
||||
tty::{self,TTY,Response},
|
||||
gpio_facade::GpioPins};
|
||||
use seymour_life::{device::Device,
|
||||
tty::{self,TTY,Response},
|
||||
gpio_facade::GpioPins};
|
||||
use std::{io::{stdin,stdout,Write},
|
||||
thread::{self, JoinHandle},
|
||||
path::Path,
|
||||
|
@ -11,11 +11,22 @@ use clap::Parser;
|
|||
#[derive(Parser,Debug)]
|
||||
#[command(author,version,about)]
|
||||
struct Args{
|
||||
/// Print all logs to screen, improves log verbosity. Sets iteration count to 50000
|
||||
#[arg(short,long,action)]
|
||||
debug:bool
|
||||
debug:bool,
|
||||
|
||||
/// Force manually setting serial numbers
|
||||
#[arg(short,long,action)]
|
||||
manual:bool,
|
||||
|
||||
/// Set iteration count from command line. Overrides debug iteration count.
|
||||
#[arg(short,long)]
|
||||
iterations:Option<u64>
|
||||
|
||||
}
|
||||
|
||||
const VERSION:&str="2.3.0";
|
||||
const VERSION:&str="2.3.3";
|
||||
const DEBUG_ITERATION_COUNT:u64=50000;
|
||||
|
||||
fn int_input_filtering(prompt:Option<&str>) -> u64{
|
||||
let internal_prompt = prompt.unwrap_or(">>>");
|
||||
|
@ -47,18 +58,19 @@ fn input_filtering(prompt:Option<&str>) -> String{
|
|||
log::debug!("{}:{}",internal_prompt,user_input);
|
||||
return user_input;
|
||||
}
|
||||
|
||||
//Path::new(&&str).is_dir() -> bool
|
||||
fn main(){
|
||||
let args = Args::parse();
|
||||
setup_logs(&args.debug);
|
||||
log::info!("Seymour Life Testing version: {}",VERSION);
|
||||
if args.debug{
|
||||
log::debug!("Debug enabled!");
|
||||
}
|
||||
log::trace!("Debug enabled!");
|
||||
loop{
|
||||
let mut iteration_count:u64 = 0;
|
||||
if args.debug {
|
||||
iteration_count = 10000;
|
||||
if let Some(value) = args.iterations{
|
||||
iteration_count = value;
|
||||
}
|
||||
else if args.debug {
|
||||
iteration_count = DEBUG_ITERATION_COUNT;
|
||||
}
|
||||
else {
|
||||
while iteration_count < 1{
|
||||
|
@ -66,98 +78,132 @@ fn main(){
|
|||
}
|
||||
}
|
||||
|
||||
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();
|
||||
match std::fs::read_dir("/dev/serial/by-path"){
|
||||
Ok(available_ttys)=>{
|
||||
let mut possible_devices:Vec<Option<Device>> = Vec::new();
|
||||
let mut tty_test_threads:Vec<JoinHandle<Option<Device>>> = Vec::new();
|
||||
for possible_tty in available_ttys.into_iter(){
|
||||
tty_test_threads.push(
|
||||
thread::spawn(move ||{
|
||||
let tty_ref = possible_tty.as_ref();
|
||||
match tty_ref{
|
||||
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();
|
||||
device.set_serial();
|
||||
Some(device)
|
||||
},
|
||||
Err(_) => None
|
||||
}
|
||||
}
|
||||
else { None }
|
||||
},
|
||||
None=>{None}
|
||||
}
|
||||
},
|
||||
Err(error)=>{
|
||||
log::debug!("{}",error);
|
||||
None
|
||||
let mut available_ttys:Vec<Box<Path>> = Vec::new();
|
||||
for entry in glob::glob("/dev/serial/*").expect("Failed to read glob pattern"){
|
||||
match entry{
|
||||
Ok(real_path) =>{
|
||||
match fs::read_dir::<&Path>(real_path.as_ref()){
|
||||
Ok(possible_ttys) =>{
|
||||
possible_ttys.into_iter().for_each(|tty| {
|
||||
if let Ok(single_tty) = tty {
|
||||
available_ttys.push(single_tty.path().into());
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
for thread in tty_test_threads{
|
||||
let output = thread.join().unwrap_or_else(|x|{log::trace!("{:?}",x); None});
|
||||
possible_devices.push(output);
|
||||
}
|
||||
|
||||
let mut devices:Vec<Device> = Vec::new();
|
||||
for possible_device in possible_devices.into_iter(){
|
||||
if let Some(device) = possible_device{
|
||||
devices.push(device);
|
||||
});
|
||||
break;
|
||||
}
|
||||
Err(error) =>{
|
||||
log::error!("Invalid permissions to /dev directory... did you run with sudo?");
|
||||
log::error!("{}",error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log::info!("--------------------------------------");
|
||||
log::info!("Number of devices detected: {}",devices.len());
|
||||
log::info!("--------------------------------------\n\n");
|
||||
|
||||
for device in devices.iter_mut(){
|
||||
//device.brighten_screen();
|
||||
//device.set_serial();
|
||||
//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();
|
||||
Err(error) =>{
|
||||
log::error!("{}",error);
|
||||
}
|
||||
}
|
||||
}
|
||||
if available_ttys.is_empty(){
|
||||
for entry in glob::glob("/dev/ttyUSB*").expect("Unable to read glob"){
|
||||
match entry{
|
||||
Ok(possible_tty) => available_ttys.push(Path::new(&possible_tty).into()),
|
||||
Err(error) => {
|
||||
log::error!("Invalid permissions to /dev directory... did you run with sudo?");
|
||||
log::error!("{}",error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}));
|
||||
if available_ttys.is_empty(){
|
||||
log::error!("No serial devices detected! Please ensure all connections.");
|
||||
return;
|
||||
}
|
||||
let mut possible_devices:Vec<Option<Device>> = Vec::new();
|
||||
let mut tty_test_threads:Vec<JoinHandle<Option<Device>>> = Vec::new();
|
||||
for possible_tty in available_ttys.into_iter(){
|
||||
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{
|
||||
thread.join().unwrap();
|
||||
devices.push(device);
|
||||
}
|
||||
}
|
||||
|
||||
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.");
|
||||
break;
|
||||
}
|
||||
}));
|
||||
}
|
||||
for thread in iteration_threads{
|
||||
thread.join().unwrap();
|
||||
}
|
||||
if input_filtering(Some("Would you like to run the tests again? (y/N): ")).to_string().contains("y") {}
|
||||
else { break; }
|
||||
|
@ -195,14 +241,18 @@ pub fn setup_logs(debug:&bool){
|
|||
message
|
||||
))
|
||||
})
|
||||
.chain(
|
||||
fern::Dispatch::new()
|
||||
.level(log::LevelFilter::Trace)
|
||||
.chain(fern::log_file(
|
||||
format!("logs/{0}.log",
|
||||
chrono_now.format("%Y-%m-%d_%H.%M").to_string()
|
||||
)).unwrap()),
|
||||
)
|
||||
.chain({
|
||||
let mut file_logger = fern::Dispatch::new();
|
||||
let date_format = chrono_now.format("%Y-%m-%d_%H.%M").to_string();
|
||||
let local_log_file = fern::log_file(format!("logs/{}.log",date_format)).unwrap();
|
||||
if *debug{
|
||||
file_logger = file_logger.level(log::LevelFilter::Trace);
|
||||
}
|
||||
else {
|
||||
file_logger = file_logger.level(log::LevelFilter::Debug);
|
||||
}
|
||||
file_logger.chain(local_log_file)
|
||||
})
|
||||
.chain({
|
||||
let mut stdout_logger = fern::Dispatch::new();
|
||||
if *debug {
|
||||
|
|
64
src/tty.rs
64
src/tty.rs
|
@ -7,7 +7,7 @@ use serialport::SerialPort;
|
|||
use derivative::Derivative;
|
||||
|
||||
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)]
|
||||
|
@ -26,8 +26,9 @@ pub enum Command{
|
|||
Login,
|
||||
DebugMenu,
|
||||
Newline,
|
||||
Shutdown,
|
||||
Reboot,
|
||||
GetSerial,
|
||||
Boot,
|
||||
}
|
||||
|
||||
#[derive(Clone,Eq,Derivative,Debug)]
|
||||
|
@ -49,6 +50,7 @@ pub enum Response{
|
|||
EmptyNewline,
|
||||
DebugInit,
|
||||
Serial(Option<String>),
|
||||
UBoot,
|
||||
}
|
||||
|
||||
|
||||
|
@ -64,31 +66,39 @@ const COMMAND_MAP:Lazy<HashMap<Command,&str>> = Lazy::new(||HashMap::from([
|
|||
(Command::UpMenuLevel, "\\"),
|
||||
(Command::Login,"root\n"),
|
||||
(Command::RedrawMenu,"?"),
|
||||
(Command::DebugMenu," python3 -m debugmenu; shutdown -r now\n"),
|
||||
(Command::DebugMenu,"python3 -m debugmenu\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"),
|
||||
]));
|
||||
|
||||
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),
|
||||
("reboot: Restarting",Response::Rebooting),
|
||||
("command not found",Response::FailedDebugMenu),
|
||||
("login:",Response::LoginPrompt),
|
||||
("Password:",Response::PasswordPrompt),
|
||||
("DtCtrlCfgDeviceSerialNum",Response::Serial(None)),
|
||||
("root@",Response::ShellPrompt),
|
||||
("EXIT Debug menu",Response::ShuttingDown),
|
||||
("Check NIBP In Progress: True",Response::BPOn),
|
||||
("Check NIBP In Progress: False",Response::BPOff),
|
||||
("SureTemp Probe Pulls:",Response::TempCount(None)),
|
||||
(">",Response::DebugMenu),
|
||||
("Loading App-Framework",Response::DebugInit),
|
||||
("root@",Response::ShellPrompt),
|
||||
("EXIT Debug menu",Response::ShuttingDown),
|
||||
];
|
||||
|
||||
pub struct TTY{
|
||||
tty: Box<dyn SerialPort>,
|
||||
failed_read_count: u8
|
||||
last: Command,
|
||||
}
|
||||
impl std::fmt::Debug for TTY{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result{
|
||||
|
@ -112,23 +122,24 @@ impl std::fmt::Debug for TTY{
|
|||
|
||||
impl TTY{
|
||||
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{
|
||||
Some(TTY {
|
||||
tty,
|
||||
failed_read_count: 0
|
||||
})
|
||||
Some(TTY{tty,last:Command::Quit})
|
||||
} else{
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
self.last = command;
|
||||
_ = self.tty.flush();
|
||||
if command == Command::Login { std::thread::sleep(std::time::Duration::from_secs(2)); }
|
||||
std::thread::sleep(std::time::Duration::from_millis(500));
|
||||
std::thread::sleep(SERIAL_TIMEOUT);
|
||||
return output;
|
||||
}
|
||||
|
||||
|
@ -140,7 +151,12 @@ impl TTY{
|
|||
let read_line:String = String::from_utf8_lossy(read_buffer.as_slice()).to_string();
|
||||
if read_line.eq("\r\n") {
|
||||
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{
|
||||
if read_line.contains(string){
|
||||
if(enum_value == Response::BPOn) || (enum_value == Response::BPOff) {
|
||||
|
@ -150,7 +166,6 @@ impl TTY{
|
|||
else{
|
||||
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){
|
||||
let mut lines = read_line.lines();
|
||||
while let Some(single_line) = lines.next(){
|
||||
|
@ -165,7 +180,6 @@ impl TTY{
|
|||
return Response::TempCount(None)
|
||||
},
|
||||
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);
|
||||
return Response::TempCount(Some(parsed_temp_count))
|
||||
}
|
||||
|
@ -192,17 +206,7 @@ impl TTY{
|
|||
return Response::Other;
|
||||
}
|
||||
else {
|
||||
log::debug!("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);
|
||||
}
|
||||
log::trace!("Read an empty string from device {:?}. Possible read error.", self);
|
||||
return Response::Empty;
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue