Skip to main content

codetics_ocpp21/
lib.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 Kevin Vavelin, Codetics (https://codetics.fr)
3
4//! OCPP 2.1 protocol messages (Edition 2).
5//!
6//! Pure Rust definitions of every OCPP 2.1 message, including the SEND
7//! message type (see [`StationSend`]). Type names are the specification
8//! datatype names without their trailing `Type`.
9//!
10//! - [`enums`], [`datatypes`], [`messages`]: payload types with serde mappings
11//!   and [`ocpp_core::Validate`] impls for length / range / cardinality constraints.
12//! - [`actions`]: [`Action`] with message direction, and the typed
13//!   [`StationCall`] / [`CsmsCall`] / [`StationSend`] dispatch enums.
14
15pub mod actions;
16pub mod datatypes;
17pub mod enums;
18pub mod messages;
19
20pub use actions::*;
21pub use datatypes::*;
22pub use enums::*;
23pub use messages::*;
24
25/// WebSocket subprotocol of this version.
26pub const SUBPROTOCOL: &str = "ocpp2.1";
27
28#[cfg(test)]
29mod tests {
30    use super::*;
31
32    #[test]
33    fn send_message() {
34        // The Part 4 ยง4.2.4 example uses timestamps for `t` and omits `basetime`; the
35        // normative message definition makes `t` as a numeric offset from a required `basetime`.
36        let p = r#"{"id":123,"pending":0,"basetime":"2024-08-27T12:30:40Z","data":[{"t":0,"v":"230.4"},{"t":5,"v":"230.2"}]}"#;
37        assert!(
38            StationSend::decode(
39                "NotifyPeriodicEventStream",
40                r#"{"id":123,"pending":0,"data":[{"t":"2024-08-27T12:30:40Z","v":"230.4"}]}"#
41            )
42            .is_err()
43        );
44        let StationSend::NotifyPeriodicEventStream(s) =
45            StationSend::decode("NotifyPeriodicEventStream", p).unwrap();
46        assert_eq!(s.data.len(), 2);
47        assert!(Action::NotifyPeriodicEventStream.is_send());
48        assert!(!Action::BootNotification.is_send());
49    }
50
51    #[test]
52    fn new_21_actions_directions() {
53        assert_eq!(Action::BatterySwap.direction(), Direction::StationToCsms);
54        assert_eq!(Action::AFRRSignal.direction(), Direction::CsmsToStation);
55        assert_eq!(Action::DataTransfer.direction(), Direction::Both);
56        assert!(
57            StationCall::decode("VatNumberValidation", r#"{"vatNumber":"NL123","evseId":1}"#)
58                .is_ok()
59        );
60    }
61
62    #[test]
63    fn minimum_constraint() {
64        let Err(DecodeError::Invalid(v)) =
65            CsmsCall::decode("AdjustPeriodicEventStream", r#"{"id":-1,"params":{}}"#)
66        else {
67            panic!()
68        };
69        assert_eq!(v.kind, ocpp_core::ViolationKind::Property);
70    }
71}
72
73/// Compiles and runs the examples of the README.
74#[cfg(doctest)]
75#[doc = include_str!("../README.md")]
76pub struct ReadmeDoctests;