Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix receive buffer bug #26. Added rust tests and vcan testing harness utilities. #25

Merged
merged 10 commits into from
Jan 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 38 additions & 6 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ git clone https://github.com/leighleighleigh/JCAN
# in a development environment:
nix-shell

# To build cross-compiled C++ libraries for x86 and ARM64,
# (BROKEN) To build cross-compiled C++ libraries for x86 and ARM64,
# and produce Python 3.8+ compatible wheels:
nix-shell cross-build.nix

Expand Down
121 changes: 21 additions & 100 deletions cross-build.nix
Original file line number Diff line number Diff line change
@@ -1,103 +1,24 @@
# cross-build.nix
# This shell uses the 'cross-rs' tool to cross-compile JCAN.
# Whilst this is done easily on Nix, through it's own cross-compilation support,
# it doesn't produce a python wheel which is useful on non-Nix systems.
#
# Hence, this shell is primarily designed to produce a Python wheel for release to PyPi.
#
{ pkgs ? import <nixpkgs> {} }:
let
clean-script = pkgs.writeScript "clean.sh" ''
#!/usr/bin/env bash

SCRIPT_DIR=$( cd -- "$( dirname -- "''${BASH_SOURCE[0]}" )" &> /dev/null && pwd )

# Remove jcan-python/dist, build, **.egg-info folders
rm -rf "''${SCRIPT_DIR}/out"
rm -rf "''${SCRIPT_DIR}/jcan_python/dist"
rm -rf "''${SCRIPT_DIR}/jcan_python/build"
rm -rf "''${SCRIPT_DIR}/jcan_python/jcan.egg-info"

# Run cargo clean
cargo clean
'';

build-script = pkgs.writeScript "crossbuild.sh" ''
#!/usr/bin/env bash
SCRIPT_DIR=$( cd -- "$( dirname -- "''${BASH_SOURCE[0]}" )" &> /dev/null && pwd )

# This function takes a TARGET as an argument, and builds the library for that target
# It then moves the build artifacts to out/<profile>/<target>/jcan/
function build_target {
CROSSTARGET="''${1}"
export CROSS_CONTAINER_ENGINE=docker

# Very important to clean, incase old crates for x86 are present
cargo clean
cross build --package jcan --target $CROSSTARGET --release
cross build --package scripts_postbuild --target $CROSSTARGET --release

# python build uses a special pyo3 image
export CARGO=cross
export CARGO_BUILD_TARGET=''${CROSSTARGET}
export CROSS_CONFIG=''${SCRIPT_DIR}/jcan_python/Cross.toml

cross build --package jcan_python --target $CROSSTARGET --release

# Run setuptools-rust on jcan_python
cd jcan_python
rm -rf ./dist
rm -rf ./build

# Change plat-name depending on CROSSTARGET
if [[ "''${CROSSTARGET}" == "aarch64-unknown-linux-gnu" ]];
then
PLATNAME="manylinux2014_aarch64"
elif [[ "''${CROSSTARGET}" == "x86_64-unknown-linux-gnu" ]];
then
PLATNAME="manylinux2014_x86_64"
else
echo "Unknown CROSSTARGET: ''${CROSSTARGET}"
exit 1
fi

python setup.py bdist_wheel --plat-name $PLATNAME --py-limited-api=cp38 || exit 1

cd ..

# Copy the resulting wheels to out folder
mkdir -p out/python/
cp -r jcan_python/dist/*.whl out/python/
}

# Build for aarch64
build_target "aarch64-unknown-linux-gnu"

# Build for x86_64
build_target "x86_64-unknown-linux-gnu"
'';
in
(pkgs.buildFHSEnv {
name = "jcan-cross-env";

targetPkgs = pkgs: [
pkgs.rustup
pkgs.cargo
pkgs.python3
pkgs.python310Packages.pip
pkgs.python310Packages.wheel
pkgs.python310Packages.setuptools-rust
pkgs.python3Packages.toml
pkgs.docker
#pkgs.podman
pkgs.hostname
pkgs.direnv
rustOverlay = builtins.fetchTarball "https://github.com/oxalica/rust-overlay/archive/master.tar.gz";

pkgs = import <nixpkgs> {
overlays = [ (import rustOverlay) ];
crossSystem = {
config = "aarch64-unknown-linux-gnu";
};
};

rust = pkgs.rust-bin.nightly.latest.default.override {
extensions = [
"rust-src" # for rust-analyzer
];
};

runScript = pkgs.writeScript "init.sh" ''
export PYTHONPATH="/lib/python3.10/site-packages/"
bash ${clean-script}
bash ${build-script}
rm -rf ./target
'';
}).env
jcan-python = pkgs.python3Packages.callPackage ./jcan_python.nix {pkgs=pkgs;};
in
pkgs.mkShell {
buildInputs = [rust jcan-python];
shellHook = ''
echo "hi"
'';
}
5 changes: 4 additions & 1 deletion jcan/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "jcan"
version = "0.2.0"
version = "0.2.4"
edition = "2021"
publish = false
links = "jcan"
Expand All @@ -17,6 +17,9 @@ socketcan = "2.0.0"
cxx = "1.0"
log = "0.4.17"
env_logger = "0.10.0"
serde_json = "1.0.108"
serde = { version = "1.0.192", features = ["derive"] }
lazy_static = "1.4.0"

[build-dependencies]
cxx-build = "1.0"
Expand Down
2 changes: 2 additions & 0 deletions jcan/justfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
test:
cargo test -- --show-output --test-threads 1
45 changes: 32 additions & 13 deletions jcan/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
extern crate socketcan;

use log::{debug, error, warn};
use log::{debug, info, error, warn};
use embedded_can::{ExtendedId, Frame as EmbeddedFrame, Id, StandardId};
use socketcan::{CanFilter, CanFrame, CanSocket, Socket};
use std::sync::mpsc;
use std::sync::mpsc::TrySendError;
use std::sync::Arc;
use std::sync::atomic::Ordering;
use std::thread;
Expand All @@ -13,7 +14,7 @@ use std::time::Duration;
pub mod ffi {

#[cxx_name = "Frame"]
#[derive(Clone)]
#[derive(Clone,PartialEq,Eq)]
pub struct JFrame {
id: u32,
data: Vec<u8>,
Expand Down Expand Up @@ -184,15 +185,21 @@ impl JBus {
let frame: ffi::JFrame = frame.into();

// Send the frame to the channel
match tx.send(frame) {
match tx.try_send(frame) {
Ok(_) => {
// All good!
debug!("jcan_recv_thread queued a frame for next spin()");
}
Err(e) => {
// This error can only occur if there are no receivers
// If this happens, the main thread has closed, and we should also close
warn!("jcan_recv_thread failed to queue frame: {}",e);
// This error can only occur if there are no receivers, or the buffer is full.
match e {
TrySendError::Full(_) => {
debug!("jcan_recv_thread dropped a received frame, buffer is full");
},
TrySendError::Disconnected(_) => {
break;
},
}
}
}
}
Expand All @@ -212,6 +219,13 @@ impl JBus {
// This means the socket is closed, break the loop.
// Check the os error code
_ => {
// Check if .close() was called
if !socket_opened_clone.load(Ordering::Relaxed) {
info!("jcan_recv_thread closed");
break;
}

// Print different logs
match e.raw_os_error() {
Some(19) => {
// Network is down
Expand Down Expand Up @@ -530,12 +544,13 @@ impl JBus {
// Builder for JBus, used to create C++ instances of the opaque JBus type
// Takes in a String interface
pub fn new_jbus() -> Result<Box<JBus>, std::io::Error> {
// Initialise the logger, if it hasn't already been initialised
// This is done by the first call to new_jbus()
match env_logger::builder().filter_level(log::LevelFilter::Warn).try_init() {
// make a logger which shows Warnings by default,
// but can show other levels by setting
// JCAN_LOG=info/debug/fatal/error, etc
match env_logger::builder().filter_level(log::LevelFilter::Error).parse_env("JCAN_LOG").try_init() {
Ok(_) => {}
Err(_) => {
warn!("env_logger already initialised");
info!("env_logger already initialised");
}
}

Expand Down Expand Up @@ -610,15 +625,19 @@ impl ffi::JFrame {
// Implement Display for JFrame, used for Rust only
impl std::fmt::Display for ffi::JFrame {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
// Prints in the same format as CANDUMP
// OLD: Prints in the same format as CANDUMP
// e.g: vcan0 123 [2] 10 20

// NEW: Prints in the same format as cansend input
// e.g: cansend vcan0 123#AABBCC

let mut s = String::new();

s.push_str(&format!("0x{:03X} [{}] ", self.id, self.data.len()));
//s.push_str(&format!("0x{:03X} [{}] ", self.id, self.data.len()));
s.push_str(&format!("{:03X}#", self.id));

for byte in self.data.iter() {
s.push_str(&format!("{:02X} ", byte));
s.push_str(&format!("{:02X}", byte));
}

write!(f, "{}", s).unwrap();
Expand Down
Loading