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

receive_with_id function, and python tests using nox #6

Merged
merged 3 commits into from
Jan 22, 2023
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
10 changes: 8 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,14 @@ Cargo.lock
**/*.rs.bk

# .envrc direnv, we don't want to git-track the virtual env
.direnv/**
.venv/**
**.direnv/**
**.venv/**
**.nox/**

# Ignore .vscode folder
.vscode/**

# Ignore pycache
**/__pycache__/**


2 changes: 1 addition & 1 deletion crossbuild.sh
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,4 @@ function build_target {
build_target "aarch64-unknown-linux-gnu"

# Build for x86_64
build_target "x86_64-unknown-linux-gnu"
build_target "x86_64-unknown-linux-gnu"
12 changes: 6 additions & 6 deletions examples/cpp_candump/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
cmake_minimum_required(VERSION 3.22)
project(cpp_cansend)
project(cpp_candump)

# ROS Foxy / Ubuntu 20 targets C++14
set(CMAKE_CXX_STANDARD 14)

set(JCAN_BRIDGE_CPP ${CMAKE_SOURCE_DIR}/include/jcan/jcan.cc)
set(JCAN_LIB ${CMAKE_SOURCE_DIR}/include/jcan/${CMAKE_STATIC_LIBRARY_PREFIX}jcan${CMAKE_STATIC_LIBRARY_SUFFIX})

add_executable(cpp_cansend src/main.cpp ${JCAN_BRIDGE_CPP})
add_executable(cpp_candump src/main.cpp ${JCAN_BRIDGE_CPP})

target_include_directories(
cpp_cansend
cpp_candump
PRIVATE
include/
${CMAKE_SOURCE_DIR}/include/jcan/
)

target_link_libraries(cpp_cansend ${JCAN_LIB})
target_link_libraries(cpp_candump ${JCAN_LIB})

# Windows-only configuration
if(WIN32)
target_link_libraries(cpp_cansend userenv ws2_32 bcrypt)
target_link_libraries(cpp_candump userenv ws2_32 bcrypt)
set_target_properties(
cpp_cansend
cpp_candump
PROPERTIES
MSVC_RUNTIME_LIBRARY "MultiThreadedDLL"
RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}
Expand Down
1 change: 1 addition & 0 deletions examples/cpp_candump_with_id/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
build/**
31 changes: 31 additions & 0 deletions examples/cpp_candump_with_id/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
cmake_minimum_required(VERSION 3.22)
project(cpp_candump_with_id)

# ROS Foxy / Ubuntu 20 targets C++14
set(CMAKE_CXX_STANDARD 14)

set(JCAN_BRIDGE_CPP ${CMAKE_SOURCE_DIR}/include/jcan/jcan.cc)
set(JCAN_LIB ${CMAKE_SOURCE_DIR}/include/jcan/${CMAKE_STATIC_LIBRARY_PREFIX}jcan${CMAKE_STATIC_LIBRARY_SUFFIX})

add_executable(cpp_candump_with_id src/main.cpp ${JCAN_BRIDGE_CPP})

target_include_directories(
cpp_candump_with_id
PRIVATE
include/
${CMAKE_SOURCE_DIR}/include/jcan/
)

target_link_libraries(cpp_candump_with_id ${JCAN_LIB})

# Windows-only configuration
if(WIN32)
target_link_libraries(cpp_candump_with_id userenv ws2_32 bcrypt)
set_target_properties(
cpp_candump_with_id
PROPERTIES
MSVC_RUNTIME_LIBRARY "MultiThreadedDLL"
RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}
RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}
)
endif()
5 changes: 5 additions & 0 deletions examples/cpp_candump_with_id/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
mkdir -p build
cd build
cmake ..
cmake --build .
1 change: 1 addition & 0 deletions examples/cpp_candump_with_id/include/jcan
30 changes: 30 additions & 0 deletions examples/cpp_candump_with_id/src/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#include <stdint.h>
#include <stdio.h>
#include <vector>
#include "jcan.h"

using namespace org::jcan;

/*
C++-14 example of using the jcan library.
*/

// main function which opens a JBus, and prints incoming frames
int main(int argc, char **argv) {
// Open the CAN bus, and un-boxes it!
Bus *bus = org::jcan::open_bus("vcan0").into_raw();

// Run forever
while (1) {
// Say we are only interested in frames with id 0x42
printf("Waiting for frame with id 0x42\n");

// Receve a frame
Frame frame = bus->receive_with_id(0x42);

// Print frame using it's to_string method
printf("%s\n", frame.to_string().c_str());
}

return 0;
}
14 changes: 14 additions & 0 deletions examples/python_candump_with_id/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/usr/bin/env python
import jcan

bustype = 'socketcan'
channel = 'vcan0'

bus = jcan.Bus(channel)

while True:
print("Waiting for frame with ID 0x42")
f = bus.receive_with_id(0x42)
print(str(f))


2 changes: 1 addition & 1 deletion jcan-python/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "jcan_python"
version = "0.1.2"
version = "0.1.3"
edition = "2021"
publish = false

Expand Down
11 changes: 11 additions & 0 deletions jcan-python/noxfile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from os.path import dirname

import nox

SETUPTOOLS_RUST = dirname(__file__)

@nox.session()
def test(session: nox.Session):
session.install(SETUPTOOLS_RUST, "pytest")
session.install("--no-build-isolation", ".")
session.run("pytest", *session.posargs)
Empty file added jcan-python/pytest.ini
Empty file.
9 changes: 8 additions & 1 deletion jcan-python/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

setup(
name="jcan",
version="0.1.2",
version="0.1.3",
packages=["jcan"],
zip_safe=False,
rust_extensions=[
Expand All @@ -16,4 +16,11 @@
)
],
include_package_data=True,
# Requirements
install_requires=[
"setuptools-rust",
],
setup_requires=[
"setuptools-rust",
],
)
21 changes: 18 additions & 3 deletions jcan-python/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,14 @@ struct PyJFrame {
impl PyJBus {
#[new]
fn new(interface: String) -> PyResult<Self> {
// Unbox the result of new_jbus, and return it
let bus = new_jbus(interface).map_err(|e| {
PyOSError::new_err(format!("Error opening bus: {}", e))
})?;

// bus is a Box<JBus>, so we need to dereference it
Ok(PyJBus {
bus: *new_jbus(interface),
bus: *bus,
})
}

Expand All @@ -46,6 +52,16 @@ impl PyJBus {
})
}

// Implement the receive_with_id method for the PyJBus
fn receive_with_id(&mut self, id: u32) -> PyResult<PyJFrame> {
let frame = self.bus.receive_with_id(id).map_err(|e| {
PyOSError::new_err(format!("Error receiving frame: {}", e))
})?;
Ok(PyJFrame {
frame,
})
}

// Implement the send method for the PyJBus
fn send(&mut self, frame: PyJFrame) -> PyResult<()> {
self.bus.send(frame.frame).map_err(|e| {
Expand Down Expand Up @@ -73,11 +89,10 @@ impl PyJFrame {
}
}


#[pymodule]
fn jcan_python(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
m.add_class::<PyJBus>()?;
m.add_class::<PyJFrame>()?;

Ok(())
}
}
33 changes: 33 additions & 0 deletions jcan-python/tests/test_jcan.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import jcan
import pytest

def test_jcan_bus_open_failure():
with pytest.raises(IOError):
channel = "vcan99"
bus = jcan.Bus(channel)

def test_jcan_standard_frame():
try:
print("Creating standard frame with ID 0x123")
f = jcan.Frame(0x123, bytes([0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88]))
print(f)
assert True
except:
assert False

def test_jcan_ext_frame():
try:
print("Creating extended frame with ID 0xF00123")
f = jcan.Frame(0xF00123, bytes([0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88]))
print(f)
assert True
except:
assert False

def test_jcan_standard_frame_large():
with pytest.raises(IOError):
f = jcan.Frame(0x123, bytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA]))

def test_jcan_standard_frame_empty():
with pytest.raises(IOError):
f = jcan.Frame(0x200, bytes([]))
2 changes: 1 addition & 1 deletion jcan/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "jcan"
version = "0.1.2"
version = "0.1.3"
edition = "2021"
publish = false

Expand Down
47 changes: 29 additions & 18 deletions jcan/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,13 @@ pub mod ffi {
data: Vec<u8>,
}

#[derive(Debug)]
pub struct JError {
message: String,
}

extern "Rust" {
#[cxx_name = "Bus"]
type JBus;
#[cxx_name = "open_bus"]
fn new_jbus(interface: String) -> Box<JBus>;
fn new_jbus(interface: String) -> Result<Box<JBus>>;
fn receive(self: &mut JBus) -> Result<JFrame>;
fn receive_with_id(self: &mut JBus, id: u32) -> Result<JFrame>;
fn send(self: &mut JBus, frame: JFrame) -> Result<()>;
fn new_jframe(id: u32, data: Vec<u8>) -> Result<JFrame>;
fn to_string(self: &JFrame) -> String;
Expand Down Expand Up @@ -55,6 +51,16 @@ impl JBus {
};
Ok(frame)
}

// Keeps receiving frames, returning the first frame with the given id
pub fn receive_with_id(&mut self, id: u32) -> Result<ffi::JFrame, std::io::Error> {
loop {
let frame = self.receive()?;
if frame.id == id {
return Ok(frame);
}
}
}

// Blocks until a frame is sent
pub fn send(&mut self, frame: ffi::JFrame) -> Result<(), std::io::Error> {
Expand All @@ -77,25 +83,30 @@ impl JBus {
}
}

// Implement Display for JError
impl std::fmt::Display for ffi::JError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.message)
}
}

// Public 'builder' method used to create C++ instances of the opaque JBus type
pub fn new_jbus(interface: String) -> Box<JBus> {
// If an error is caught here, it will be propagated to the C++ side
// as a std::runtime_error, with the message "CanSocketOpenError"
// TODO: Make this fail gracefully on C++
let socket = CanSocket::open(&interface).expect("CanSocketOpenError");
Box::new(JBus { socket })
pub fn new_jbus(interface: String) -> Result<Box<JBus>, std::io::Error> {
// Open and map to Error if it fails
let socket = CanSocket::open(&interface).map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
Ok(Box::new(JBus {
socket,
}))
}

// Builder for jframe, used to create C++ instances of the opaque JFrame type
// Takes in a u32 id, and a Vec<u8> data
pub fn new_jframe(id: u32, data: Vec<u8>) -> Result<ffi::JFrame, std::io::Error> {
// Check if data is empty (zero length)
if data.len() == 0 {
// Print error message that shows N = 0 bytes
return Err(std::io::Error::new(std::io::ErrorKind::Other, "Data length cannot be 0 bytes"));
}

// Check if data is too long
if data.len() > 8 {
// Print error message that shows N > 8 bytes
return Err(std::io::Error::new(std::io::ErrorKind::Other, format!("Data length {} > 8 bytes", data.len())));
}
Ok(ffi::JFrame {
id,
data,
Expand Down
6 changes: 5 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
attrs==22.2.0
exceptiongroup==1.1.0
iniconfig==2.0.0
msgpack==1.0.4
packaging==23.0
patchelf==0.17.2.0
pluggy==1.0.0
pytest==7.2.1
python-can==4.1.0
semantic-version==2.10.0
setuptools-rust==1.5.2
tomli==2.0.1
typing_extensions==4.4.0
wrapt==1.14.1
wheel
2 changes: 1 addition & 1 deletion scripts-postbuild/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "scripts_postbuild"
version = "0.1.2"
version = "0.1.3"
edition = "2021"
publish = false

Expand Down
Loading