#sport #serial #decoder #tokio #output #daktronics #allsport-5000

daktronics-allsport-5000

A Rust implementation of decoders for the Daktronics All Sport 5000's serial output

7 unstable releases (3 breaking)

0.4.0 Dec 1, 2024
0.3.1 Jul 11, 2024
0.2.2 Jul 9, 2024
0.1.0 Jul 6, 2024

#147 in Asynchronous

Download history 14/week @ 2024-09-23 1/week @ 2024-11-04 79/week @ 2024-11-25 94/week @ 2024-12-02

173 downloads per month

MIT license

1.5MB
17K SLoC

Daktronics All Sport 5000 for Rust

Easily decode the serial output of a console and optionally serialize it.

Documentation Crates.io License Downloads

A Rust implementation of decoders for the Daktronics All Sport 5000's serial output. Please see the documentation for more help.

[!NOTE]

If you use this crate: let me know about your use case by creating a GitHub discussion or starring! It also lets me know other people find this crate interesting and useful.

Installation

Add this to your Cargo.toml:

daktronics-allsport-5000 = "0.4.0"

Or if you want to use the serde feature:

daktronics-allsport-5000 = { version = "0.4.0", features = ["serde"] }

Or do it with the CLI:

cargo add daktronics-allsport-5000

Cargo features

  • Default features: tokio-serial
  • tokio-serial: Enables support for serial communication using the tokio-serial crate.
  • tokio: Enables asynchronous support using the tokio crate.
  • async: Enables support for asynchronous programming, which as of right now only works with Tokio.
  • serde: Enables serialization support of sports using the serde crate.

Where is this used?

I made this crate to use in a project I'm working on, daktronics-singular-ui, which links together a Daktronics All Sport 5000 with a Singular.Live overlay. This powers overlays for Christian Academy in Japan sports livestreams.

Usage

High-level access

Create a new RTDState instance with RTDState::from_serial_stream (available with default features enabled). Get the SerialStream with tokio_serial::new then open_native_async. Check out the examples for more help.

If you need any help, there should be more extensive documentation for each item at docs.rs. Don't hesitate to create a GitHub issue if something is unclear, either.

Sports

This crate supports all sports the control console supports. For a list of them, see the sports module documentation.

Examples

Getting a sport-specific field
use daktronics_allsport_5000::{
    RTDState,
    // there are lots of other sports available in their respective modules
    sports::basketball::BasketballSport
};
use tokio_serial::SerialPortBuilderExt; // for open_native_async
use crate::daktronics_allsport_5000::sports::Sport; // for rtd_state

#[tokio::main]
async fn main() {
    let serial_stream = tokio_serial::new("/dev/ttyUSB0", 19200)
        .parity(tokio_serial::Parity::None)
        .open_native_async()
        .unwrap();
    let rtd_state = RTDState::from_serial_stream(serial_stream, true).unwrap();
    let mut basketball = BasketballSport::new(rtd_state);

    loop {
        // get the underlying rtd_state to update it
        let update_result = basketball.rtd_state().update_async().await.unwrap();

        basketball.main_clock_time(); // -> Result<&str, ...>
    }
}
With Serde

Enable the serde feature to enable serialization for sports.

use tokio;
use daktronics_allsport_5000::{
    RTDState,
    sports::basketball::BasketballSport
};
use tokio_serial::SerialPortBuilderExt; // for open_native_async
use crate::daktronics_allsport_5000::sports::Sport; // for rtd_state

#[tokio::main]
async fn main() {
    let serial_stream = tokio_serial::new("/dev/ttyUSB0", 19200)
        .parity(tokio_serial::Parity::None)
        .open_native_async()
        .unwrap();
    let rtd_state = RTDState::from_serial_stream(serial_stream, true).unwrap();
    let basketball = BasketballSport::new(rtd_state);

    loop {
        // get the underlying rtd_state to update it
        let update_result = basketball
            .rtd_state()
            .update_async()
            .await
            .unwrap();

        serde_json::to_string(&basketball); // -> Result<String, ...>
    }
}
Getting the raw data buffer
use tokio;
use daktronics_allsport_5000::{RTDState, RTDFieldJustification};
use tokio_serial::SerialPortBuilderExt; // for open_native_async

#[tokio::main]
async fn main() {
    let serial_stream = tokio_serial::new("/dev/ttyUSB0", 19200)
        .parity(tokio_serial::Parity::None)
        .open_native_async()
        .unwrap();
    let mut rtd_state = RTDState::from_serial_stream(serial_stream, true).unwrap();

    loop {
        let update_result = rtd_state.update_async().await.unwrap();

        // do something with `rtd_state`
        rtd_state.field_str(1, 5, RTDFieldJustification::Left); // -> Result<&str, ...>
    }
}

Low-level access

When used with the tokio feature, this package provides a tokio-util codec implementing Decoder to decode packets from a serial stream from the control console. That is used internally in SerialStreamDataSource to route data into RTDState.

If you're not using tokio or you're not using a serial stream to deliver the data (e.g. using UDP), you must get the packets somehow yourself, but after that, you can use daktronics_allsport_5000::packet::Packet's TryFrom<bytes::Bytes> implementation to parse the packet into a readable format. Then, you can provide that to the main RTDState struct by implementing daktronics_allsport_5000::rtd_state::data_source::RTDStateDataSource then giving that to RTDState::new. After that, everything works as normal.

Inspiration

The same concept as this crate is also implemented in Python by @FlantasticDan, C# by @JimThatcher, and Python again by @fimion. In fact, the data in this crate is extracted from a PDF provided by @fimion, so thank you!

The offsets of the various fields have been processed from a PDF and typed by hand. If you're interested in porting this crate to another language, check out the Excel spreadsheet I compiled with the data that underpins this crate in ./sports_data.

Given that this protocol is technically proprietary, please note that this crate does not come with any warranty. For more details, see the license.

Dependencies

~5–14MB
~161K SLoC