Just pushing everything in for backup.

This commit is contained in:
Myrddin Dundragon 2025-05-09 11:14:03 -04:00
parent 64720ab226
commit 6a9bc09be3
8 changed files with 521 additions and 41 deletions

261
Cargo.lock generated Normal file
View File

@ -0,0 +1,261 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "addr2line"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
dependencies = [
"gimli",
]
[[package]]
name = "adler2"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
[[package]]
name = "backtrace"
version = "0.3.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
dependencies = [
"addr2line",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
"windows-targets",
]
[[package]]
name = "bytes"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "gimli"
version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "libc"
version = "0.2.172"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
[[package]]
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "miniz_oxide"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
dependencies = [
"adler2",
]
[[package]]
name = "mio"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
dependencies = [
"libc",
"wasi",
"windows-sys",
]
[[package]]
name = "object"
version = "0.36.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
dependencies = [
"memchr",
]
[[package]]
name = "pin-project-lite"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]]
name = "proc-macro2"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rustc-demangle"
version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[package]]
name = "satellite"
version = "0.0.0"
dependencies = [
"tokio",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410"
dependencies = [
"libc",
]
[[package]]
name = "syn"
version = "2.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tokio"
version = "1.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165"
dependencies = [
"backtrace",
"bytes",
"libc",
"mio",
"pin-project-lite",
"signal-hook-registry",
"tokio-macros",
"windows-sys",
]
[[package]]
name = "tokio-macros"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-ident"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"

View File

@ -11,3 +11,4 @@ license-file = "LICENSE.md"
[dependencies]
tokio = { version = "1.44.1", features = ["macros", "rt-multi-thread", "io-std", "io-util", "time", "signal", "sync"] }

64
docs/problem_statement.md Normal file
View File

@ -0,0 +1,64 @@
# Problem Statement
You are a member of the flight software team at Umbra and are responsible
for writing code that manages the satellites propulsion system.
Firing the propulsion system involves waiting for a certain period of time before ignition.
The following is an example usage of this system:
* At absolute time t = 0, send a command to the computer to fire the propulsion in 15 seconds
* At absolute time t = 2, send a command to the computer to fire the propulsion in 30 seconds
* At absolute time t = 32, the computer begins firing the propulsion
The flight computer should accept a command with a relative time of when to fire
the propulsion - once that time has elapsed, the program should print out “firing now!”.
If another command is received before the propulsion is fired then the most recently
received relative time should overwrite any existing commands.
More formally, if a command _A_ is waiting to fire and another command _B_ is received
before A has fired, then B should replace _A_ as the pending command and _A_ should never fire.
If a time of -1 is given, any outstanding commands to fire the propulsion are cancelled.
Note that the flight computer should be able to fire the thruster multiple times in a
single execution of the program.
You may use exactly one of the following interfaces for getting data into and out of your program:
* Standard input/standard output
* TCP
You can use whichever is more convenient - we note that some languages make asynchronous
IO with standard input/standard output cumbersome and have thus included TCP.
If you do choose to use TCP, please have your server listen on port `8124`.
A sample TCP client, `propulsion_tcp_client.py`, is provided that plumbs standard input
to TCP writes and likewise plumbs TCP reads to standard output.
Commands should be delineated by newlines.
A sample _complete_ execution of your program using standard input and output is shown below
```
./your_program
15
30
firing now!
```
# Submitting your code
You may complete this assignment in any programming language that you wish.
We're most familiar with Rust and Python at Umbra, but we always enjoy learning
new languages and seeing solutions with different tools!
Your final submission for this exercise should be a zip file including:
* The source code of your program.
* A brief README with a description of your approach and instructions on how to
build and run the program.
* Any libraries or frameworks needed to run your program, or instructions for installing them.
Please ensure that your code is clear and legible before submitting.
While we don't expect you to write production-quality code for this exercise,
readability goes a long way in helping us review your submission.

BIN
src/.main.rs.swp Normal file

Binary file not shown.

18
src/commands.rs Normal file
View File

@ -0,0 +1,18 @@
use tokio::time::Duration;
/// The commands available to send to the Satellite.
#[derive(Clone, Copy)]
pub enum Command
{
/// Fire the engine in T minus x seconds!
Propulsion
{
/// The amount of seconds to wait before we fire the engines.
delay: Duration
},
/// Cancel any waiting commands.
Cancel
}

View File

@ -1,20 +1,186 @@
//! Rust Challenge
mod commands;
mod project;
use std::io::Write;
use tokio::sync::{mpsc, watch};
use tokio::time::{Duration, Interval};
use tokio::io::AsyncWriteExt;
use crate::commands::Command;
/// Print the version of the project.
fn print_version()
type IoResult<T> = Result<T, std::io::Error>;
/// Reads input from the user until the termination signal is
/// received. It will send command messages every time it reads valid input.
fn read_user_input(command_sender: mpsc::Sender<Command>,
term_sender: watch::Sender<bool>)
-> IoResult<()>
{
println!("{} v{}", project::get_name(), project::get_version());
let mut running: bool = true;
let mut buffer = String::new();
// Loop and read the user input.
while running
{
buffer.clear();
std::io::stdin().read_line(&mut buffer)?;
std::io::stdout().flush().unwrap();
let input = buffer.trim();
match input
{
// A termination method wasn't specified in the document, so
// this will terminate when either stop, exit, or quit is entered.
"exit" | "stop" | "quit" =>
{
println!("Exiting application.");
running = false;
term_sender.send_replace(running);
}
_ =>
{
// Check to see if we were given a number.
match input.parse::<i32>()
{
Ok(seconds) =>
{
// Here we are just handling the different possible numbers
// passed in. -1 cancels commands, less than -1 is ignored
// with a message, and greated than -1 is turned into a
// propulsion command.
if seconds == -1
{
match command_sender.blocking_send(Command::Cancel)
{
Ok(_) =>
{
println!("Cancelling any outstanding commands.")
}
Err(e) => println!("Failed to send command: {}", e)
}
}
else if seconds < -1
{
println!("All propulsion delay times are given in \
seconds within the range of [0, {}]",
u32::MAX);
println!("A value of -1 will cancel any current \
propulsion commands.");
}
else
{
let larger_seconds: u64 = seconds as u64;
let delay_duration: Duration = Duration::from_secs(larger_seconds);
match command_sender.try_send(Command::Propulsion { delay: delay_duration })
{
Ok(_) =>
{
println!("Firing the engines in {} seconds.",
seconds)
}
Err(e) => println!("Failed to send command: {}", e)
}
}
}
Err(e) =>
{
println!("Unable to parse the given input into seconds.");
println!("Please specify only seconds until the time to \
fire the engines.");
}
}
}
}
}
Ok(())
}
/// The usual starting point of your project.
fn main()
async fn process_commands(mut command_receiver: mpsc::Receiver<Command>,
mut term_receiver: watch::Receiver<bool>)
->IoResult<()>
{
print_version();
let mut stdout = tokio::io::stdout();
let mut running: bool = true;
while running
{
tokio::select! {
Some(command) = command_receiver.recv() =>
{
match command
{
Command::Cancel =>
{
stdout.write_all(b"Received: Cancel\n").await?;
stdout.flush().await?;
}
Command::Propulsion { delay } =>
{
let mut buffer: Vec<u8> = Vec::new();
writeln!(&mut buffer, "Received: {:?} delay", delay);
stdout.write_all(&buffer).await?;
stdout.flush().await?;
}
_ => {}
}
}
_ = term_receiver.changed() => {
stdout.write_all(b"Communication task received shutdown message.").await?;
stdout.flush().await?;
running = *term_receiver.borrow_and_update();
}
}
}
Ok(())
}
/// Program entry point.
#[tokio::main]
async fn main() -> IoResult<()>
{
// The channel that will be used to send satellite commands between tasks.
let (command_sender, mut command_receiver) = mpsc::channel::<Command>(100);
// The channel that will be used to signal termination of the program.
// True means the program is running. False means it has been terminated.
//
// This could be done as a satellite command since the program is driven by
// user interaction and it all happens from the input_task, but this is a
// program signal so I prefer to keep it seperate from the command messages.
// It also allows for moving termination control to another task, say from
// Ctrl+C, if the program was changed to use TCP or something else.
let (term_sender, mut term_receiver) = watch::channel(true);
// Spawn a task to handle reading input from the user. We use a thread here
// because tokio recommends it for the blocking read calls for interactive
// user input.
let input_task = tokio::task::spawn_blocking(move || {
read_user_input(command_sender, term_sender)
});
// Spawn a task to handle simulating the satellite.
let sim_task = tokio::spawn(async move {
process_commands(command_receiver, term_receiver).await
});
let (sim_result, input_result) = tokio::join!(sim_task, input_task);
sim_result?;
input_result??;
println!("Shutdown complete.");
Ok(())
}

View File

@ -1,33 +0,0 @@
//! This is where the Projects build information can be retreived from.
/// The environment variable defined by Cargo for the name.
const NAME: Option<&str> = option_env!("CARGO_PKG_NAME");
/// The environment variable defined by Cargo for the version.
const VERSION: Option<&str> = option_env!("CARGO_PKG_VERSION");
/// The string to display if a value is not defined during compile time.
const NOT_DEFINED: &'static str = "UNDEFINED";
/// Returns the name of the program as defined by the CARGO_PKG_NAME. This is
/// set at compile time and comes from the Cargo.toml file.
///
/// If a value is not found, then it will return the not defined value.
pub fn get_name() -> &'static str
{
NAME.unwrap_or(NOT_DEFINED)
}
/// Returns the name of the program as defined by the CARGO_PKG_VERSION. This is
/// set at compile time and comes from the Cargo.toml file.
///
/// If a value is not found, then it will return the not defined value.
pub fn get_version() -> &'static str
{
VERSION.unwrap_or(NOT_DEFINED)
}

3
src/satellite.rs Normal file
View File

@ -0,0 +1,3 @@
pub struct Satellite
{
}