#usb #usb-device #bill-validator #bill-acceptor

jcm

纯Rust实现的JCM USB通信协议

7个版本

0.2.3 2024年5月20日
0.2.2 2024年5月9日
0.1.1 2024年4月29日
0.1.0-预发布版2024年3月25日

22财务

Download history 94/week @ 2024-04-20 201/week @ 2024-04-27 423/week @ 2024-05-04 67/week @ 2024-05-11 341/week @ 2024-05-18 75/week @ 2024-05-25 84/week @ 2024-06-01 26/week @ 2024-06-08 12/week @ 2024-06-15 12/week @ 2024-06-29 69/week @ 2024-07-06 14/week @ 2024-07-13 1/week @ 2024-07-20 28/week @ 2024-07-27

每月 112次下载

MIT许可证

475KB
12K SLoC

JCM USB通信协议

该库是JCM USB设备通信协议的纯Rust实现。

兼容性

该库与以下协议规范兼容

  • ID-008

示例使用

以下是一个示例启动程序

use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
use std::{thread, time};

use jcm::{Error, Result};

/// Dummy event responder that sends `ACK` responses to all device-sent events.
fn ack_event_responder(
    stop: Arc<AtomicBool>,
    event_recv: crossbeam::channel::Receiver<jcm::Message>,
    event_res_send: crossbeam::channel::Sender<jcm::Message>,
) -> Result<()> {
    thread::spawn(move || -> Result<()> {
        while !stop.load(Ordering::Relaxed) {
            if let Ok(event) = event_recv.try_recv() {
                event_res_send
                    .try_send(
                        jcm::Message::new().with_data(
                            event
                                .data()
                                .clone()
                                .with_additional(&[jcm::ResponseCode::Ack.into()]),
                        ),
                    )
                    .map_err(|err| Error::Usb(format!("error sending event response: {err}")))?;
            }

            thread::sleep(time::Duration::from_millis(100));
        }
        Ok(())
    });

    Ok(())
}

fn main() -> Result<()> {
    env_logger::Builder::from_default_env()
        .format_timestamp_millis()
        .try_init()
        .ok();

    let usb = Arc::new(Mutex::new(jcm::usb::UsbDeviceHandle::find_usb()?));
    let stop = Arc::new(AtomicBool::new(false));

    let (event_send, event_recv) = crossbeam::channel::unbounded();
    let (response_send, response_recv) = crossbeam::channel::unbounded();
    let (event_res_send, event_res_recv) = crossbeam::channel::unbounded();

    jcm::usb::poll_device_message(
        Arc::clone(&usb),
        Arc::clone(&stop),
        event_send,
        event_res_recv,
        response_send,
    )?;

    jcm::usb::wait_for_power_up(&event_recv, &event_res_send).ok();

    ack_event_responder(Arc::clone(&stop), event_recv, event_res_send)?;

    let req: jcm::Message = jcm::MessageData::from(jcm::UidRequest::new_set(0x1)).into();
    let res = jcm::usb::poll_request(Arc::clone(&usb), &req, &response_recv, 3)?;

    log::info!("UID response: {res}");

    let req: jcm::Message = jcm::MessageData::from(jcm::StatusRequest::new())
        .with_uid(1)
        .into();
    let res = jcm::usb::poll_request(Arc::clone(&usb), &req, &response_recv, 3)?;

    log::debug!("Raw status response: {res}");

    let res = jcm::StatusResponse::try_from(&res)?;

    log::info!("Status response: {res}");

    stop.store(true, Ordering::SeqCst);

    Ok(())
}

辅助函数

以下函数是常见例程的辅助函数

  • jcm::usb::poll_device_message:轮询设备发送的消息
  • jcm::usb::wait_for_power_up:在跨线程通道上等待PowerUp事件
  • jcm::usb::poll_request:轮询发送请求消息到设备,给定重试次数

每个函数都很简短,所以重实现它们相对直接。

例如,您可能希望使用不同的跨线程通道原语、互斥锁类型等。

依赖关系

~0.1–12MB
~84K SLoC