Skip to content

Copper Release Log

Guillaume Binet edited this page Sep 30, 2024 · 30 revisions

Release Notes - September 30, 2024

This release introduces substantial improvements to the Copper framework's monitoring capabilities and API flexibility, with ongoing stability and performance enhancements.

Highlights

  • New multisource and optional input API: The Copper engine now supports multiple and optional inputs/outputs (see PR #44).

This is a breaking change.

Now you can link 2 tasks to one in the RON file like this:

    tasks: [
        (
            id: "balpos",
            type: "cu_ads7883::ADS7883",
        ),
        (
            id: "railpos",
            type: "cu_rp_encoder::Encoder",
        ),
        (
            id: "pidctrl",
            type: "pidtask::PIDTask",
            config: {
               [...]
            },
        ),
        (
            id: "motor",
            type: "cu_rp_sn754410::SN754410",
            [...]
        ),
     ],
    cnx: [
        //                    vvvvvvvvvv   same dest!
        (src: "balpos",   dst: "pidctrl",   msg: "cu_ads7883::ADSReadingPayload"),
        (src: "railpos",  dst: "pidctrl",   msg: "cu_rp_encoder::EncoderPayload"),
        (src: "pidctrl",  dst: "motor",   msg: "cu_rp_sn754410::MotorPayload"),
    ],
)

To help you manage the types that are generated, we are giving a set of macros to help you matching the correct input / output types:

impl<'cl> CuTask<'cl> for PIDTask {
    // This tasks takes 2 inputs!
    // They are given in the order of task declaration
    // the input_msg! macro build a (&CuMsg<ADSReadingPayload>, &CuMsg<EncoderPayload>) tuple under the hood.
    // it also works with 1 input and then you will get a straight &CuMsg<> immutable ref.
    // For technical Rust reasons, you need to explicitely tie the lifetime ('cl means copperlist if you are curious: the internal structure of copper for messages)
    type Input = input_msg!('cl, ADSReadingPayload, EncoderPayload);

    // same thing but as an output this is a &mut CuMsg<MotorPayload>
    type Output = output_msg!('cl, MotorPayload);

    fn process(
        &mut self,
        clock: &RobotClock,
        input: Self::Input,  // here this is now straight the input type, it is a little simpler.
        output: Self::Output,
    ) -> CuResult<()> {
        let (bal_pos, rail_pos) = input;  // you can unpack the tuple directly those are resp. &CuMsg<ADSReadingPayload> and &CuMsg<EncoderPayload>
        let bal_tov = bal_pos.metadata.tov.expect("we should have had a message here!");  // the messages are now optional depending on the context they could be expected or really optional.
        // we have a new method called set_payload for the output
        output.set_payload(MotorPayload { power: 0.0 }); // If you don't do that it will send away a message with a None payload

- **Monitoring System**: The monitoring framework is now fully integrated, allowing real-time stats collection and cumulative statistics (see PRs #49, #50, and #51). We can imagine complex decision trees happening at that stage for complex robots and various degraded modes.

The monitoring component is really similar to a task, but with specialized callbacks:
```ron
// This is in the RON file, just add a monitor entry like this:

    tasks: [
        (
            id: "task0",
            type: "tasks::ExampleSrc",
        ),
        (
            id: "task1",
            type: "tasks::ExampleTask",
        ),
        (
            id: "task2",
            type: "tasks::ExampleSink",
        ),
     ],
    cnx: [
        (src: "task0", dst: "task1", msg: "i32"),
        (src: "task1", dst: "task2", msg: "i32"),
    ],
    monitor: (type: "ExampleMonitor")  // here, add a config entry if necessary
)
struct ExampleMonitor {
    tasks: &'static [&'static str], // We give you the task ordinal to task id mapping (so it is stable as long as you don't change your task ids.
}

impl CuMonitor for ExampleMonitor {
    // We pass you the config you gave in the RON file exactly like for the tasks.
    fn new(_config: Option<&ComponentConfig>, taskids: &'static [&str]) -> CuResult<Self> {
        Ok(ExampleMonitor { tasks: taskids })
    }

    fn start(&mut self, clock: &_RobotClock) -> CuResult<()> {
        // callbacked when all the tasks, start called.
    }

    fn process_copperlist(&self, msgs: &[&CuMsgMetadata]) -> CuResult<()> {
        // This is callbacked at the end of the processing of a copper list (basically near when the CL is getting serialized to disk after a success. 
        // The metadata gives you all the timings you need to check if your robot is still behaving nominally.
        for t in msgs.iter().enumerate() {
            let (taskid, metadata) = t;
            debug!("Task: {} -> {}", taskid, metadata);
        }
        Ok(())
    }

    fn process_error(&self, taskid: usize, step: CuTaskState, error: &CuError) -> Decision {
        // This is called back if any task reports an error at any step (start, process, ...)
        // You can then match that taskid and compute a decision for your robot: Abort, Ignore, Shutdown (see the cu28/monitoring.rs file for semantic details. 
        Decision::Ignore
    }

    fn stop(&mut self, clock: &_RobotClock) -> CuResult<()> {
        // call when the stack is stopping
        Ok(())
    }
}

Other Notable Changes

New Features

  • Real-time cumulative stats for CuDurations. See cu29/monitoring.rs we built an histogramming feature for timings so you can get percentiles, means, jitter, max jitters, etc etc... super useful for monitoring components.

Fixes

  • Deserialization Bug (#42):
    • Fixed bincode deserialization issue with cu29 values.

Enhancements

  • Virtual Output for Sinks (#53):
    • before that there was no mean to monitor sinks (or hacks you might have seen on the incoming message). Now the stack behind the scene generates a () empty message for each sink you you get the perf number cleanly for them even if they don't output anything.
  • Balance Bot Demo (#46):
    • a more complete example of a real robot demo we will bring at conferences.

Miscellaneous

  • Dependency Updates (#52):
    • General dependency version bumps.
  • Documentation:
    • README updates to include monitoring (#acfc9c0e06fdb989875fbf355814aa0531d902e5) and restructured timelines (#baa2d2c3ffeb4ec0828ea41bea89d77e19cf1314).
    • Added missing descriptions for publications.

Additional Notes

  • Minor fixes and typo corrections have been made across several commits to improve code quality and maintainability (#5ffb3778a667d983f6c6933a9d70f0aa4f6cc312, #16921983b72d09de81c1037b2ef5a5b8c60a2a77).

Copper v0.2.3 Release Notes

We are pleased to announce the release of Copper v0.2.3, which includes several new features, enhancements, and bug fixes. Below is a summary of the key changes in this release:

New Features

  • SN754410 Driver Support #40: Added a new driver for the SN754410 motor driver. This driver allows easy integration with motor control applications, providing robust support for H-bridge motor control on a wide range of systems. This driver is fully compatible with the BalanceHAT.
  • ADS7883 Driver #39: Introduced a driver for the ADS7883, a 12-bit SPI ADC. This addition includes comprehensive documentation in the README to facilitate setup and integration. The ADS7883 driver is also compatible with the BalanceHAT.

Enhancements

  • macOS Development Support #25: Copper can now be developed on macOS! The CI/CD pipeline has been updated to support macOS, ensuring cross-platform compatibility for all users.
  • cu29_clock Enhancements #32: Added a division feature to the cu29_clock, allowing more granular time management and synchronization within the Copper runtime.
  • Structured Log Index File in Debug Mode #30: Removed the requirement for an index file in debug mode within struct_log, simplifying the debugging process ie. only the config and the executable needs to be deployed for the debug build and you get a standard debug text logging.
  • Logging Slabs Addition #24: The current unified logger works with big memory mapped files. Initially we thought we could rely on the mmap resize feature of the kernel api but it just doesn't work. We are reverting into building "slabs", ie dividing the data logger into large files. Those files can be concatenated and read or directly read from the log exporter.

Bug Fixes

  • Compilation Feedback Adjustments #33: Moved compilation feedback from standard output to standard error to better align with conventional logging practices.
  • Flush and Core Dump Fixes #29: Resolved issues where changes after a section flush could cause a core dump, improving the stability of the logging system.

Infrastructure and CI/CD

  • CI/CD Pipeline Enhancements #25: Added macOS support to the CI/CD pipeline, ensuring better cross-platform compatibility.
  • Removed 'Continue on Error' in CI/CD #36: Addressed a CI/CD issue where the pipeline was green while failing Oo.

Refactoring and Internal Changes

  • Unified Logger Refactor #27: Refactored the Unified Logger to introduce a dual-slab logging system, improving performance and eliminating the need for remap/resize operations.

  • Runtime Loop Enhancements #21: Added a new notion of loops in the runtime plan, setting the stage for more flexible and powerful runtime behavior.


If you're looking to build your own interfacing with the SN754410 or ADS7883 drivers, we share detailed connectivity schematics with a Raspberry Pi to help you get started.

We recommend updating to this latest version to take advantage of these improvements. As always, please refer to the updated documentation and release notes for detailed information on how to integrate these changes into your projects.

If you have any questions or need further assistance, feel free to reach out to our support team.

Clone this wiki locally