Merge pull request 'v2.3.2' (#15) from devel into stable
Some checks failed
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline failed

Reviewed-on: #15
This commit is contained in:
Blizzard Finnegan 2023-06-23 13:59:05 -04:00
commit a198e7b101
Signed by: Blizzard Finnegan
GPG key ID: 1513350E4C45A14F
14 changed files with 126 additions and 174 deletions

2
.cargo/config.toml Normal file
View file

@ -0,0 +1,2 @@
[target.aarch64-unknown-linux-musl]
linker = "lld"

View file

@ -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
View file

@ -1,3 +1,5 @@
/target
/logs
/output
/output
.cargo
.rustup

21
.woodpecker.yml Normal file
View file

@ -0,0 +1,21 @@
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:
from_secret: API_KEY
target: ${CI_COMMIT_TAG}
draft: true
prerelease: true
title: .woodpecker/title.txt

1
.woodpecker/title.txt Normal file
View file

@ -0,0 +1 @@
v2.3.2

29
Cargo.lock generated
View file

@ -382,26 +382,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 +466,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 +604,6 @@ dependencies = [
"IOKit-sys",
"bitflags",
"cfg-if",
"libudev",
"mach 0.3.2",
"nix",
"regex",
@ -639,7 +612,7 @@ dependencies = [
[[package]]
name = "seymour_life"
version = "2.3.1"
version = "2.3.2"
dependencies = [
"chrono",
"clap",

View file

@ -1,15 +1,18 @@
#This feature is currently limited to nightly versions of cargo.
#cargo-features = ["per-package-target"]
[package]
name = "seymour_life"
version = "2.3.1"
version = "2.3.2"
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"

View file

@ -1,3 +1,4 @@
[![status-badge](https://ci.blizzard.systems/api/badges/blizzardfinnegan/seymourLifeRust/status.svg?branch=stable)](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.
@ -23,9 +24,7 @@ Note that this command MUST be run as `sudo`/root, due to the way it interacts w
## 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, 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 *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
```
@ -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.
### Build Dependencies
The following dependencies are also necessary for building this project:
- `pkg-config`
- `libudev`
## Cross-Compilation
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
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 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
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.

View file

@ -1,2 +0,0 @@
librust-udev-sys-dev
pkg-config

View file

@ -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

View file

@ -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}"
'';
}

View file

@ -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)]
@ -30,6 +31,7 @@ pub struct Device{
reboots: u64,
temps: u64,
init_temps: u64,
temp_offset: u64,
bps: u64
}
@ -38,7 +40,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 +63,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(_) => {
@ -113,10 +120,8 @@ 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;
},
@ -152,6 +157,7 @@ impl Device{
current_state: initial_state,
reboots: 0,
temps: 0,
temp_offset: 0,
init_temps: 0,
bps: 0
};
@ -195,7 +201,7 @@ impl Device{
log::error!("Unexpected response from device {}!",self.serial);
log::debug!("brightness 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;
},
};
};
@ -222,7 +228,7 @@ impl Device{
log::error!("Unexpected response from device {}!", self.serial);
log::debug!("brightness 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;
},
};
};
@ -266,7 +272,7 @@ impl Device{
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;
},
};
};
@ -293,7 +299,7 @@ impl Device{
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 +324,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) => {
@ -524,18 +530,20 @@ impl Device{
pub fn reboot(&mut self) -> () {
self.usb_tty.write_to_device(Command::Quit);
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;
},
_ => {}
}
@ -570,7 +578,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();
}
}

View file

@ -11,7 +11,7 @@ use clap::Parser;
#[derive(Parser,Debug)]
#[command(author,version,about)]
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)]
debug:bool,
@ -19,13 +19,13 @@ struct Args{
#[arg(short,long,action)]
manual:bool,
/// Set iteration count from command line
/// Set iteration count from command line. Overrides debug iteration count.
#[arg(short,long)]
iterations:Option<u64>
}
const VERSION:&str="2.3.1";
const VERSION:&str="2.3.2";
const DEBUG_ITERATION_COUNT:u64=50000;
fn int_input_filtering(prompt:Option<&str>) -> u64{
@ -63,23 +63,22 @@ 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 = DEBUG_ITERATION_COUNT;
}
else if let Some(value) = args.iterations{
if let Some(value) = args.iterations{
iteration_count = value;
}
else if args.debug {
iteration_count = DEBUG_ITERATION_COUNT;
}
else {
while iteration_count < 1{
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();
match std::fs::read_dir("/dev/serial/by-path"){
Ok(available_ttys)=>{
@ -93,7 +92,7 @@ fn main(){
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);
log::debug!("Testing port {}",&tty_name);
let possible_port = TTY::new(&tty_name);
match possible_port{
Some(mut port) =>{
@ -145,6 +144,7 @@ fn main(){
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();
@ -157,7 +157,7 @@ fn main(){
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;
panic!();
}
}
@ -217,14 +217,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 {

View file

@ -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)]
@ -88,7 +88,7 @@ const RESPONSES:[(&str,Response);13] = [
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 +112,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;
}
@ -150,7 +151,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 +165,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 +191,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;
};
}