Skip to content

Commit

Permalink
add ping example
Browse files Browse the repository at this point in the history
  • Loading branch information
wucke13 committed Jul 14, 2023
1 parent 5363444 commit 41d6ac0
Show file tree
Hide file tree
Showing 9 changed files with 266 additions and 0 deletions.
1 change: 1 addition & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ jobs:
example:
- hello_part
- fuel_tank
- ping
env:
DURATION: 10s
RUST_LOG: trace
Expand Down
18 changes: 18 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@ members = [
"hypervisor",
"partition",
"core",

"examples/hello_part",

"examples/fuel_tank_simulation",
"examples/fuel_tank_controller",

"examples/ping_client",
"examples/ping_server",
]

[workspace.dependencies]
Expand Down
27 changes: 27 additions & 0 deletions examples/ping.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
major_frame: 1s
partitions:
- id: 0
name: ping_client
duration: 10ms
offset: 0ms
period: 1s
image: target/x86_64-unknown-linux-musl/release/ping_client
- id: 1
name: ping_server
duration: 20ms
offset: 450ms
period: 1s
image: target/x86_64-unknown-linux-musl/release/ping_server
channel:
- !Sampling
name: ping_request
msg_size: 16B
source: ping_client
destination:
- ping_server
- !Sampling
name: ping_response
msg_size: 32B
source: ping_server
destination:
- ping_client
11 changes: 11 additions & 0 deletions examples/ping_client/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "ping_client"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
a653rs = { workspace = true, features = ["macros"] }
a653rs-linux = { path = "../../partition" }
log.workspace = true
111 changes: 111 additions & 0 deletions examples/ping_client/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
use a653rs::partition;
use a653rs::prelude::PartitionExt;
use a653rs_linux::partition::ApexLogger;
use log::LevelFilter;

fn main() {
ApexLogger::install_panic_hook();
ApexLogger::install_logger(LevelFilter::Trace).unwrap();

ping_client::Partition.run()
}

#[partition(a653rs_linux::partition::ApexLinuxPartition)]
mod ping_client {
use core::time::Duration;
use log::{info, warn};

#[sampling_out(name = "ping_request", msg_size = "16B")]
struct PingRequest;

#[sampling_in(name = "ping_response", msg_size = "32B", refresh_period = "10s")]
struct PingResponse;

#[start(cold)]
fn cold_start(mut ctx: start::Context) {
// initialize both sampling ports
ctx.create_ping_request().unwrap();
ctx.create_ping_response().unwrap();

// create and start a periodic process
ctx.create_periodic_ping_client().unwrap().start().unwrap();
}

// do the same as a cold_start
#[start(warm)]
fn warm_start(ctx: start::Context) {
cold_start(ctx);
}

// this process requests a ping from the server at the beginning of each
// partition window / MiF
#[periodic(
period = "0ms",
time_capacity = "Infinite",
stack_size = "8KB",
base_priority = 1,
deadline = "Soft"
)]
fn periodic_ping_client(ctx: periodic_ping_client::Context) {
info!("started periodic_ping_client process");

// a periodic process does not actually return at the end of a partition window,
// it just pauses itself once it is done with the work from the current MiF
// see below at the `ctx.periodic_wait().unwrap()` call.
loop {
// first, send a request:

// `ctx.get_time()` returns a [SystemTime], which might be `Infinite`, or just a
// normal time. Thus we have to check that indeed a normal time was returned.
let SystemTime::Normal(time) = ctx.get_time() else {
panic!("could not read time");
};
info!("sending a request");

// convert the current time to an u128 integer representing nanoseconds, and
// serialize the integer to a byte array
let time_in_nanoseconds = time.as_nanos();
let buf = time_in_nanoseconds.to_le_bytes();

// finally send the byte array to the ping_request port
ctx.ping_request.unwrap().send(&buf).unwrap();

// then receive a response, if any:

// allocate a buffer on the stack for receival of the response
let mut buf = [0u8; 32];

// sample the ping_response sampling port into `buf`
// - validity indicates whether data received was sitting in the samplin port
// for no more than the refresh_period
// - `bytes` is a subslice of `buf`, containing only the bytes actually read
// from the sampling port
let (validity, bytes) = ctx.ping_response.unwrap().receive(&mut buf).unwrap();

// only if the message is valid and has the expected length try to process it
if validity == Validity::Valid && bytes.len() == 32 {
// deserialize the bytes into an u128
let request_timestamp = u128::from_le_bytes(bytes[0..16].try_into().unwrap());
let response_timestamp = u128::from_le_bytes(bytes[16..32].try_into().unwrap());
// the difference is the time passed since sending the request for this response
let round_trip = time_in_nanoseconds - request_timestamp;
let req_to_server = response_timestamp - request_timestamp;
let resp_to_client = time_in_nanoseconds - response_timestamp;

// convert the integers of nanoseconds back to a [Duration]s for nicer logging
let req_sent_to_resp_recv = Duration::from_nanos(round_trip as u64);
let req_sent_to_resp_sent = Duration::from_nanos(req_to_server as u64);
let resp_sent_to_resp_recv = Duration::from_nanos(resp_to_client as u64);

// and log the results!
info!("received valid response:\n\tround-trip {req_sent_to_resp_recv:?}\n\treq-to-server {req_sent_to_resp_sent:?}\n\tresp-to-client{resp_sent_to_resp_recv:?}");
} else {
warn!("response seems to be incomplete: {validity:?}, {bytes:?}");
}

// wait until the beginning of this partitions next MiF. In scheduling terms
// this function would probably be called `yield()`.
ctx.periodic_wait().unwrap();
}
}
}
11 changes: 11 additions & 0 deletions examples/ping_server/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "ping_server"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
a653rs = { workspace = true, features = ["macros"] }
a653rs-linux = { path = "../../partition" }
log.workspace = true
78 changes: 78 additions & 0 deletions examples/ping_server/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
use a653rs::partition;
use a653rs::prelude::PartitionExt;
use a653rs_linux::partition::ApexLogger;
use log::LevelFilter;

fn main() {
ApexLogger::install_panic_hook();
ApexLogger::install_logger(LevelFilter::Trace).unwrap();

ping_server::Partition.run()
}

#[partition(a653rs_linux::partition::ApexLinuxPartition)]
mod ping_server {
use log::info;

#[sampling_in(name = "ping_request", msg_size = "16B", refresh_period = "10s")]
struct PingRequest;

#[sampling_out(name = "ping_response", msg_size = "32B")]
struct PingResponse;

#[start(cold)]
fn cold_start(mut ctx: start::Context) {
// intialize the request destination port
ctx.create_ping_request().unwrap();

// intialize the response source port
ctx.create_ping_response().unwrap();

// launch the periodic process
ctx.create_periodic_ping_server().unwrap().start().unwrap();
}

#[start(warm)]
fn warm_start(ctx: start::Context) {
cold_start(ctx);
}

// the server process is super simple; all it does is receive a request and
// respond to it
#[periodic(
period = "0ms",
time_capacity = "Infinite",
stack_size = "8KB",
base_priority = 1,
deadline = "Soft"
)]
fn periodic_ping_server(ctx: periodic_ping_server::Context) {
info!("started ping_server process");
loop {
info!("forwarding request as response ");

// allocate a buffer to receive into
let mut buf = [0u8; 32];

// receive a request, storing it to `buf`
ctx.ping_request.unwrap().receive(&mut buf).unwrap();

// `ctx.get_time()` returns a [SystemTime], which might be `Infinite`, or just a
// normal time. Thus we have to check that indeed a normal time was returned.
let SystemTime::Normal(time) = ctx.get_time() else {
panic!("could not read time");
};

// convert the current time to an u128 integer representing nanoseconds, and
// serialize the integer to a byte array
let time_in_nanoseconds = time.as_nanos();
buf[16..32].copy_from_slice(&time_in_nanoseconds.to_le_bytes());

// send the contents of `buf` back as response
ctx.ping_response.unwrap().send(&buf).unwrap();

// wait until the next partition window / MiF
ctx.periodic_wait().unwrap();
}
}
}
4 changes: 4 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@
name = "fuel_tank";
partitions = [ "fuel_tank_simulation" "fuel_tank_controller" ];
}
{
name = "ping";
partitions = [ "ping_server" "ping_client" ];
}
];

cargoPackageList = ps: builtins.map (p: "--package=${p}") ps;
Expand Down

0 comments on commit 41d6ac0

Please sign in to comment.