Skip to main content

scs_sdk/
channel.rs

1use core::ffi::{CStr, c_void};
2use core::marker::PhantomData;
3use core::ops::{BitOr, BitOrAssign};
4
5use crate::{
6    GameSchemaAvailability, SdkCall, SdkError, SdkIndex, SdkResult, SdkValue, ValueRef, ValueType,
7    sys,
8};
9
10/// Value types which can be requested from an SCS telemetry channel.
11///
12/// The game validates whether a particular channel supports the requested
13/// representation. For example, placement channels commonly accept placement,
14/// vector, or Euler representations, while `u32` channels commonly accept
15/// `u64`. Unsupported requests return [`SdkError::UnsupportedType`].
16pub trait ChannelValue: SdkValue {}
17
18impl<T: SdkValue> ChannelValue for T {}
19
20#[derive(Debug, PartialEq, Eq)]
21pub struct Channel<T> {
22    name: &'static CStr,
23    indexed: bool,
24    availability: GameSchemaAvailability,
25    marker: PhantomData<fn() -> T>,
26}
27
28/// A telemetry channel descriptor after erasing its Rust marker type.
29///
30/// Framework code needs a uniform representation because a plugin can
31/// subscribe to channels carrying many different SDK value types. Erasure does
32/// not discard the ABI type discriminator: [`AnyChannel::value_type`] remains
33/// available and callback values must still carry that exact tag before typed
34/// decoding succeeds.
35///
36/// Application plugins normally construct this value through
37/// [`Channel::erase`] rather than directly.
38#[derive(Clone, Copy, Debug, PartialEq, Eq)]
39pub struct AnyChannel {
40    name: &'static CStr,
41    value_type: ValueType,
42    indexed: bool,
43    availability: GameSchemaAvailability,
44}
45
46impl AnyChannel {
47    /// Returns the canonical, NUL-terminated channel name from the SDK header.
48    #[must_use]
49    pub const fn name(self) -> &'static CStr {
50        self.name
51    }
52
53    /// Returns the concrete tagged-union member requested during registration.
54    #[must_use]
55    pub const fn value_type(self) -> ValueType {
56        self.value_type
57    }
58
59    /// Whether the channel requires a separate zero-based SDK index.
60    ///
61    /// This index is used for arrays such as wheels. It is distinct from the
62    /// trailer number embedded in a multi-trailer channel name.
63    #[must_use]
64    pub const fn is_indexed(self) -> bool {
65        self.indexed
66    }
67
68    /// Returns the official per-game schema history for this channel.
69    ///
70    /// Type erasure retains availability so framework code can reject a
71    /// required channel, or skip an optional channel, before asking an older
72    /// game schema to register a name it cannot expose.
73    #[must_use]
74    pub const fn availability(self) -> GameSchemaAvailability {
75        self.availability
76    }
77}
78
79impl<T> Clone for Channel<T> {
80    fn clone(&self) -> Self {
81        *self
82    }
83}
84
85impl<T> Copy for Channel<T> {}
86
87impl<T: ChannelValue> Channel<T> {
88    #[must_use]
89    pub const fn new(name: &'static CStr, availability: GameSchemaAvailability) -> Self {
90        Self {
91            name,
92            indexed: false,
93            availability,
94            marker: PhantomData,
95        }
96    }
97
98    /// Creates a descriptor whose values are selected through the SDK `index`
99    /// parameter, such as individual wheel or H-shifter selector channels.
100    #[must_use]
101    pub const fn indexed(name: &'static CStr, availability: GameSchemaAvailability) -> Self {
102        Self {
103            name,
104            indexed: true,
105            availability,
106            marker: PhantomData,
107        }
108    }
109
110    #[must_use]
111    pub const fn name(self) -> &'static CStr {
112        self.name
113    }
114
115    #[must_use]
116    pub const fn value_type(self) -> sys::ScsValueType {
117        T::TYPE
118    }
119
120    /// Whether registration requires an explicit zero-based SDK index.
121    #[must_use]
122    pub const fn is_indexed(self) -> bool {
123        self.indexed
124    }
125
126    /// Returns the first official ETS2 and ATS schemas for this channel.
127    #[must_use]
128    pub const fn availability(self) -> GameSchemaAvailability {
129        self.availability
130    }
131
132    /// Requests the same channel using another SDK value representation.
133    ///
134    /// SCS performs the authoritative compatibility check during registration
135    /// and reports `SCS_RESULT_unsupported_type` when conversion is unavailable.
136    #[must_use]
137    pub const fn requesting<U: ChannelValue>(self) -> Channel<U> {
138        Channel {
139            name: self.name,
140            indexed: self.indexed,
141            availability: self.availability,
142            marker: PhantomData,
143        }
144    }
145
146    /// Erases the Rust marker type while preserving the SDK value discriminator.
147    ///
148    /// This is the representation used by the plugin framework's heterogeneous
149    /// subscription table. A value can later be decoded with the original
150    /// typed descriptor only when the names, index mode, and value types match.
151    #[must_use]
152    pub const fn erase(self) -> AnyChannel {
153        AnyChannel {
154            name: self.name,
155            value_type: T::VALUE_TYPE,
156            indexed: self.indexed,
157            availability: self.availability,
158        }
159    }
160
161    /// Decodes a callback value according to this descriptor's requested type.
162    #[must_use]
163    pub fn decode(self, value: ValueRef<'_>) -> Option<T::Decoded<'_>> {
164        T::decode(value)
165    }
166}
167
168#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
169#[repr(transparent)]
170pub struct ChannelFlags(sys::ScsU32);
171
172impl ChannelFlags {
173    pub const NONE: Self = Self(sys::SCS_TELEMETRY_CHANNEL_FLAG_NONE);
174    pub const EACH_FRAME: Self = Self(sys::SCS_TELEMETRY_CHANNEL_FLAG_EACH_FRAME);
175    pub const NO_VALUE: Self = Self(sys::SCS_TELEMETRY_CHANNEL_FLAG_NO_VALUE);
176
177    #[must_use]
178    pub const fn bits(self) -> sys::ScsU32 {
179        self.0
180    }
181}
182
183impl BitOr for ChannelFlags {
184    type Output = Self;
185
186    fn bitor(self, rhs: Self) -> Self::Output {
187        Self(self.0 | rhs.0)
188    }
189}
190
191impl BitOrAssign for ChannelFlags {
192    fn bitor_assign(&mut self, rhs: Self) {
193        self.0 |= rhs.0;
194    }
195}
196
197impl SdkCall<'_> {
198    /// Registers a typed telemetry channel callback.
199    ///
200    /// # Safety
201    ///
202    /// `callback` must implement the exact SDK ABI, must decode the value
203    /// according to `T`, and must not unwind across the ABI boundary. `context`
204    /// must remain valid for every invocation of `callback`. This method must
205    /// be called from initialization or an SCS event callback, never from a
206    /// worker thread or another channel callback.
207    ///
208    /// # Errors
209    ///
210    /// Returns the result reported by the game's registration function.
211    pub unsafe fn register_channel<T: ChannelValue>(
212        &self,
213        channel: Channel<T>,
214        index: Option<SdkIndex>,
215        flags: ChannelFlags,
216        callback: sys::ScsTelemetryChannelCallback,
217        context: *mut c_void,
218    ) -> SdkResult {
219        // SAFETY: The caller upholds the SDK callback, context, and call-site
220        // restrictions documented above.
221        let result = unsafe {
222            (self.table.register_for_channel)(
223                channel.name().as_ptr(),
224                index.map_or(sys::SCS_U32_NIL, SdkIndex::raw),
225                channel.value_type(),
226                flags.bits(),
227                callback,
228                context,
229            )
230        };
231        SdkError::from_code(result)
232    }
233
234    /// Unregisters a telemetry channel callback.
235    ///
236    /// # Safety
237    ///
238    /// Must be called from initialization, shutdown, or an SCS event callback,
239    /// never from a channel callback or a worker thread. Context storage
240    /// associated with the old registration must remain alive until this
241    /// function succeeds and no invocation of that callback is active.
242    ///
243    /// # Errors
244    ///
245    /// Returns the result reported by the game's unregistration function.
246    pub unsafe fn unregister_channel<T: ChannelValue>(
247        &self,
248        channel: Channel<T>,
249        index: Option<SdkIndex>,
250    ) -> SdkResult {
251        // SAFETY: The caller upholds the SDK call-site restriction.
252        let result = unsafe {
253            (self.table.unregister_from_channel)(
254                channel.name().as_ptr(),
255                index.map_or(sys::SCS_U32_NIL, SdkIndex::raw),
256                channel.value_type(),
257            )
258        };
259        SdkError::from_code(result)
260    }
261
262    /// Registers a channel whose Rust marker type has been erased by a
263    /// framework subscription table.
264    ///
265    /// Unlike [`SdkCall::register_channel`], the channel name may be owned by
266    /// the caller. This is required for multi-trailer names such as
267    /// `trailer.3.connected`, which do not exist as static macros in the C
268    /// header.
269    ///
270    /// # Safety
271    ///
272    /// `callback` must implement the exact SDK ABI, verify every received value
273    /// against `value_type`, and prevent unwinding across the ABI boundary.
274    /// `name` and `context` must remain valid for the complete registration
275    /// lifetime. This method may only be called at an SDK-approved registration
276    /// point on the game main thread.
277    ///
278    /// # Errors
279    ///
280    /// Returns the exact result reported by the game's registration function.
281    pub unsafe fn register_erased_channel(
282        &self,
283        name: &CStr,
284        index: Option<SdkIndex>,
285        value_type: ValueType,
286        flags: ChannelFlags,
287        callback: sys::ScsTelemetryChannelCallback,
288        context: *mut c_void,
289    ) -> SdkResult {
290        // SAFETY: The caller owns the name/context lifetime and callback ABI
291        // invariants documented above.
292        let result = unsafe {
293            (self.table.register_for_channel)(
294                name.as_ptr(),
295                index.map_or(sys::SCS_U32_NIL, SdkIndex::raw),
296                value_type.raw(),
297                flags.bits(),
298                callback,
299                context,
300            )
301        };
302        SdkError::from_code(result)
303    }
304
305    /// Unregisters one previously registered type-erased channel.
306    ///
307    /// # Safety
308    ///
309    /// This must run at an SDK-approved unregistration point on the game main
310    /// thread. The callback context and channel name must remain alive until
311    /// unregistration succeeds and any active callback has returned.
312    ///
313    /// # Errors
314    ///
315    /// Returns the exact result reported by the game's unregistration function.
316    pub unsafe fn unregister_erased_channel(
317        &self,
318        name: &CStr,
319        index: Option<SdkIndex>,
320        value_type: ValueType,
321    ) -> SdkResult {
322        // SAFETY: The caller upholds the SDK call-site and storage-lifetime
323        // restrictions documented above.
324        let result = unsafe {
325            (self.table.unregister_from_channel)(
326                name.as_ptr(),
327                index.map_or(sys::SCS_U32_NIL, SdkIndex::raw),
328                value_type.raw(),
329            )
330        };
331        SdkError::from_code(result)
332    }
333}
334
335/// Complete typed channel catalog for SCS Telemetry SDK 1.14.
336pub mod channels {
337    use super::AnyChannel;
338    use crate::{GameSchemaAvailability, game};
339
340    // These constants encode the game-schema history documented by the ETS2
341    // and ATS headers. They are deliberately named by both version domains:
342    // an ETS2 minor must never be reused as though it were the equivalent ATS
343    // minor merely because both games expose the same common C macro.
344    const INITIAL: GameSchemaAvailability =
345        GameSchemaAvailability::new(Some(game::ets2::V1_00), Some(game::ats::V1_00));
346    const ETS2_1_01_ATS_1_00: GameSchemaAvailability =
347        GameSchemaAvailability::new(Some(game::ets2::V1_01), Some(game::ats::V1_00));
348    const ETS2_1_02_ATS_1_00: GameSchemaAvailability =
349        GameSchemaAvailability::new(Some(game::ets2::V1_02), Some(game::ats::V1_00));
350    const ETS2_1_04_ATS_1_00: GameSchemaAvailability =
351        GameSchemaAvailability::new(Some(game::ets2::V1_04), Some(game::ats::V1_00));
352    const ETS2_1_09_ATS_1_00: GameSchemaAvailability =
353        GameSchemaAvailability::new(Some(game::ets2::V1_09), Some(game::ats::V1_00));
354    const ETS2_1_10_ATS_1_00: GameSchemaAvailability =
355        GameSchemaAvailability::new(Some(game::ets2::V1_10), Some(game::ats::V1_00));
356    const ETS2_1_11_ATS_1_00: GameSchemaAvailability =
357        GameSchemaAvailability::new(Some(game::ets2::V1_11), Some(game::ats::V1_00));
358    const ETS2_1_12_ATS_1_00: GameSchemaAvailability =
359        GameSchemaAvailability::new(Some(game::ets2::V1_12), Some(game::ats::V1_00));
360    const ETS2_ONLY_1_12: GameSchemaAvailability =
361        GameSchemaAvailability::new(Some(game::ets2::V1_12), None);
362    const ETS2_1_14_ATS_1_01: GameSchemaAvailability = game::capabilities::MULTI_TRAILER;
363    const ETS2_1_17_ATS_1_04: GameSchemaAvailability =
364        GameSchemaAvailability::new(Some(game::ets2::V1_17), Some(game::ats::V1_04));
365    const ETS2_1_18_ATS_1_05: GameSchemaAvailability =
366        GameSchemaAvailability::new(Some(game::ets2::V1_18), Some(game::ats::V1_05));
367
368    /// First schema which supports the numbered `trailer.[index].*` namespace.
369    ///
370    /// The static `trailer.*` descriptors predate this feature. Framework code
371    /// must combine their individual availability with this capability when it
372    /// constructs a numbered multi-trailer channel name.
373    pub const MULTI_TRAILER_AVAILABILITY: GameSchemaAvailability =
374        game::capabilities::MULTI_TRAILER;
375
376    /// Channels declared by `scssdk_telemetry_common_channels.h`.
377    pub mod common {
378        use super::super::{AnyChannel, Channel};
379        use super::{ETS2_1_09_ATS_1_00, ETS2_1_18_ATS_1_05, INITIAL};
380
381        /// Number of typed descriptors in this catalog group.
382        pub const COUNT: usize = 4;
383
384        /// Scale applied to distance and time to compensate
385        /// for the scale of the map (e.g. 1s of real time corresponds to `local_scale`
386        /// seconds of simulated game time).
387        ///
388        /// Games which use real 1:1 maps will not provide this
389        /// channel.
390        ///
391        /// Type: float
392        pub const LOCAL_SCALE: Channel<f32> = Channel::new(c"local.scale", INITIAL);
393
394        /// Absolute in-game time.
395        ///
396        /// Represented in number of in-game minutes since beginning (i.e. 00:00)
397        /// of the first in-game day.
398        ///
399        /// Type: u32
400        pub const GAME_TIME: Channel<u32> = Channel::new(c"game.time", ETS2_1_09_ATS_1_00);
401
402        /// Offset from the `game_time` simulated in the local economy to the
403        /// game time of the Convoy multiplayer server.
404        ///
405        /// The value of this channel can change frequently during the Convoy
406        /// session. For example when the user enters the desktop, the local
407        /// economy time stops however the multiplayer time continues to run
408        /// so the value will start to change.
409        ///
410        /// Represented in in-game minutes. Set to 0 when multiplayer is not active.
411        ///
412        /// Type: s32
413        pub const MULTIPLAYER_TIME_OFFSET: Channel<i32> =
414            Channel::new(c"multiplayer.time.offset", ETS2_1_18_ATS_1_05);
415
416        /// Time until next rest stop.
417        ///
418        /// When the fatique simulation is disabled, the behavior of this channel
419        /// is implementation dependent. The game might provide the value which would
420        /// apply if it was enabled or provide no value at all.
421        ///
422        /// Represented in in-game minutes.
423        ///
424        /// Type: s32
425        pub const NEXT_REST_STOP: Channel<i32> = Channel::new(c"rest.stop", ETS2_1_09_ATS_1_00);
426
427        /// Every common channel in the order used by the SDK 1.14 header.
428        ///
429        /// The marker types are erased only so callers can enumerate mixed
430        /// channel types. Each entry still retains its exact value type and
431        /// indexed/scalar registration mode.
432        pub const ALL: [AnyChannel; COUNT] = [
433            LOCAL_SCALE.erase(),
434            GAME_TIME.erase(),
435            MULTIPLAYER_TIME_OFFSET.erase(),
436            NEXT_REST_STOP.erase(),
437        ];
438    }
439
440    /// Channels declared by `scssdk_telemetry_truck_common_channels.h`.
441    pub mod truck {
442        use super::super::{AnyChannel, Channel};
443        use super::{
444            ETS2_1_01_ATS_1_00, ETS2_1_02_ATS_1_00, ETS2_1_04_ATS_1_00, ETS2_1_10_ATS_1_00,
445            ETS2_1_11_ATS_1_00, ETS2_1_12_ATS_1_00, ETS2_1_17_ATS_1_04, ETS2_ONLY_1_12, INITIAL,
446        };
447
448        /// Number of typed descriptors in this catalog group.
449        pub const COUNT: usize = 84;
450
451        /// Represents world space position and orientation of the truck.
452        ///
453        /// Type: dplacement
454        pub const WORLD_PLACEMENT: Channel<crate::DPlacement> =
455            Channel::new(c"truck.world.placement", INITIAL);
456
457        /// Represents vehicle space linear velocity of the truck measured
458        /// in meters per second.
459        ///
460        /// Type: fvector
461        pub const LOCAL_LINEAR_VELOCITY: Channel<crate::FVector> =
462            Channel::new(c"truck.local.velocity.linear", INITIAL);
463
464        /// Represents vehicle space angular velocity of the truck measured
465        /// in rotations per second.
466        ///
467        /// Type: fvector
468        pub const LOCAL_ANGULAR_VELOCITY: Channel<crate::FVector> =
469            Channel::new(c"truck.local.velocity.angular", INITIAL);
470
471        /// Represents vehicle space linear acceleration of the truck measured
472        /// in meters per second^2
473        ///
474        /// Type: fvector
475        pub const LOCAL_LINEAR_ACCELERATION: Channel<crate::FVector> =
476            Channel::new(c"truck.local.acceleration.linear", INITIAL);
477
478        /// Represents vehicle space angular acceleration of the truck meassured
479        /// in rotations per second^2
480        ///
481        /// Type: fvector
482        pub const LOCAL_ANGULAR_ACCELERATION: Channel<crate::FVector> =
483            Channel::new(c"truck.local.acceleration.angular", INITIAL);
484
485        /// Represents a vehicle space position and orientation delta
486        /// of the cabin from its default position.
487        ///
488        /// Type: fplacement
489        pub const CABIN_OFFSET: Channel<crate::FPlacement> =
490            Channel::new(c"truck.cabin.offset", ETS2_1_02_ATS_1_00);
491
492        /// Represents cabin space angular velocity of the cabin measured
493        /// in rotations per second.
494        ///
495        /// Type: fvector
496        pub const CABIN_ANGULAR_VELOCITY: Channel<crate::FVector> =
497            Channel::new(c"truck.cabin.velocity.angular", INITIAL);
498
499        /// Represents cabin space angular acceleration of the cabin
500        /// measured in rotations per second^2
501        ///
502        /// Type: fvector
503        pub const CABIN_ANGULAR_ACCELERATION: Channel<crate::FVector> =
504            Channel::new(c"truck.cabin.acceleration.angular", INITIAL);
505
506        /// Represents a cabin space position and orientation delta
507        /// of the driver head from its default position.
508        ///
509        /// Note that this value might change rapidly as result of
510        /// the user switching between cameras or camera presets.
511        ///
512        /// Type: fplacement
513        pub const HEAD_OFFSET: Channel<crate::FPlacement> =
514            Channel::new(c"truck.head.offset", INITIAL);
515
516        /// Speedometer speed in meters per second.
517        ///
518        /// Uses negative value to represent reverse movement.
519        ///
520        /// Type: float
521        pub const SPEED: Channel<f32> = Channel::new(c"truck.speed", INITIAL);
522
523        /// RPM of the engine.
524        ///
525        /// Type: float
526        pub const ENGINE_RPM: Channel<f32> = Channel::new(c"truck.engine.rpm", INITIAL);
527
528        /// Gear currently selected in the engine.
529        ///
530        /// - >0 - Forwad gears
531        /// - 0 - Neutral
532        /// - <0 - Reverse gears
533        ///
534        /// Type: s32
535        pub const ENGINE_GEAR: Channel<i32> = Channel::new(c"truck.engine.gear", INITIAL);
536
537        /// Gear currently displayed on dashboard.
538        ///
539        /// - >0 - Forwad gears
540        /// - 0 - Neutral
541        /// - <0 - Reverse gears
542        ///
543        /// Type: s32
544        pub const DISPLAYED_GEAR: Channel<i32> =
545            Channel::new(c"truck.displayed.gear", ETS2_1_11_ATS_1_00);
546
547        /// Steering received from input <-1;1>.
548        ///
549        /// Note that it is interpreted counterclockwise.
550        ///
551        /// If the user presses the steer right button on digital input
552        /// (e.g. keyboard) this value goes immediatelly to -1.0
553        ///
554        /// Type: float
555        pub const INPUT_STEERING: Channel<f32> = Channel::new(c"truck.input.steering", INITIAL);
556
557        /// Throttle received from input <0;1>
558        ///
559        /// If the user presses the forward button on digital input
560        /// (e.g. keyboard) this value goes immediatelly to 1.0
561        ///
562        /// Type: float
563        pub const INPUT_THROTTLE: Channel<f32> = Channel::new(c"truck.input.throttle", INITIAL);
564
565        /// Brake received from input <0;1>
566        ///
567        /// If the user presses the brake button on digital input
568        /// (e.g. keyboard) this value goes immediatelly to 1.0
569        ///
570        /// Type: float
571        pub const INPUT_BRAKE: Channel<f32> = Channel::new(c"truck.input.brake", INITIAL);
572
573        /// Clutch received from input <0;1>
574        ///
575        /// If the user presses the clutch button on digital input
576        /// (e.g. keyboard) this value goes immediatelly to 1.0
577        ///
578        /// Type: float
579        pub const INPUT_CLUTCH: Channel<f32> = Channel::new(c"truck.input.clutch", INITIAL);
580
581        /// Steering as used by the simulation <-1;1>
582        ///
583        /// Note that it is interpreted counterclockwise.
584        ///
585        /// Accounts for interpolation speeds and simulated
586        /// counterfoces for digital inputs.
587        ///
588        /// Type: float
589        pub const EFFECTIVE_STEERING: Channel<f32> =
590            Channel::new(c"truck.effective.steering", INITIAL);
591
592        /// Throttle pedal input as used by the simulation <0;1>
593        ///
594        /// Accounts for the press attack curve for digital inputs
595        /// or cruise-control input.
596        ///
597        /// Type: float
598        pub const EFFECTIVE_THROTTLE: Channel<f32> =
599            Channel::new(c"truck.effective.throttle", INITIAL);
600
601        /// Brake pedal input as used by the simulation <0;1>
602        ///
603        /// Accounts for the press attack curve for digital inputs. Does
604        /// not contain retarder, parking or engine brake.
605        ///
606        /// Type: float
607        pub const EFFECTIVE_BRAKE: Channel<f32> = Channel::new(c"truck.effective.brake", INITIAL);
608
609        /// Clutch pedal input as used by the simulation <0;1>
610        ///
611        /// Accounts for the automatic shifting or interpolation of
612        /// player input.
613        ///
614        /// Type: float
615        pub const EFFECTIVE_CLUTCH: Channel<f32> = Channel::new(c"truck.effective.clutch", INITIAL);
616
617        /// Speed selected for the cruise control in m/s
618        ///
619        /// Is zero if cruise control is disabled.
620        ///
621        /// Type: float
622        pub const CRUISE_CONTROL: Channel<f32> = Channel::new(c"truck.cruise_control", INITIAL);
623
624        /// Gearbox slot the h-shifter handle is currently in.
625        ///
626        /// 0 means that no slot is selected.
627        ///
628        /// Type: u32
629        pub const HSHIFTER_SLOT: Channel<u32> = Channel::new(c"truck.hshifter.slot", INITIAL);
630
631        /// Enabled state of range/splitter selector toggles.
632        ///
633        /// Mapping between the range/splitter functionality and
634        /// selector index is described by HSHIFTER configuration.
635        ///
636        /// Type: indexed bool
637        pub const HSHIFTER_SELECTOR: Channel<bool> =
638            Channel::indexed(c"truck.hshifter.select", INITIAL);
639
640        /// Is the parking brake enabled?
641        ///
642        /// Type: bool
643        pub const PARKING_BRAKE: Channel<bool> = Channel::new(c"truck.brake.parking", INITIAL);
644
645        /// Is the engine brake enabled?
646        ///
647        /// Type: bool
648        pub const MOTOR_BRAKE: Channel<bool> = Channel::new(c"truck.brake.motor", INITIAL);
649
650        /// Current level of the retarder.
651        ///
652        /// <0;max> where 0 is disabled retarder and max is maximum
653        /// value found in TRUCK configuration.
654        ///
655        /// Type: u32
656        pub const RETARDER_LEVEL: Channel<u32> = Channel::new(c"truck.brake.retarder", INITIAL);
657
658        /// Pressure in the brake air tank in psi
659        ///
660        /// Type: float
661        pub const BRAKE_AIR_PRESSURE: Channel<f32> =
662            Channel::new(c"truck.brake.air.pressure", INITIAL);
663
664        /// Is the air pressure warning active?
665        ///
666        /// Type: bool
667        pub const BRAKE_AIR_PRESSURE_WARNING: Channel<bool> =
668            Channel::new(c"truck.brake.air.pressure.warning", INITIAL);
669
670        /// Are the emergency brakes active as result of low air pressure?
671        ///
672        /// Type: bool
673        pub const BRAKE_AIR_PRESSURE_EMERGENCY: Channel<bool> =
674            Channel::new(c"truck.brake.air.pressure.emergency", ETS2_1_01_ATS_1_00);
675
676        /// Temperature of the brakes in degrees celsius.
677        ///
678        /// Aproximated for entire truck, not at the wheel level.
679        ///
680        /// Type: float
681        pub const BRAKE_TEMPERATURE: Channel<f32> =
682            Channel::new(c"truck.brake.temperature", INITIAL);
683
684        /// Amount of fuel in liters
685        ///
686        /// Type: float
687        pub const FUEL: Channel<f32> = Channel::new(c"truck.fuel.amount", INITIAL);
688
689        /// Is the low fuel warning active?
690        ///
691        /// Type: bool
692        pub const FUEL_WARNING: Channel<bool> = Channel::new(c"truck.fuel.warning", INITIAL);
693
694        /// Average consumption of the fuel in liters/km
695        ///
696        /// Type: float
697        pub const FUEL_AVERAGE_CONSUMPTION: Channel<f32> =
698            Channel::new(c"truck.fuel.consumption.average", INITIAL);
699
700        /// Estimated range of truck with current amount of fuel in km
701        ///
702        /// Type: float
703        pub const FUEL_RANGE: Channel<f32> = Channel::new(c"truck.fuel.range", ETS2_1_12_ATS_1_00);
704
705        /// Amount of `AdBlue` in liters
706        ///
707        /// Type: float
708        pub const ADBLUE: Channel<f32> = Channel::new(c"truck.adblue", ETS2_ONLY_1_12);
709
710        /// Is the low adblue warning active?
711        ///
712        /// Type: bool
713        pub const ADBLUE_WARNING: Channel<bool> =
714            Channel::new(c"truck.adblue.warning", ETS2_ONLY_1_12);
715
716        /// Average consumption of the adblue in liters/km
717        ///
718        /// Type: float
719        pub const ADBLUE_AVERAGE_CONSUMPTION: Channel<f32> =
720            Channel::new(c"truck.adblue.consumption.average", ETS2_ONLY_1_12);
721
722        /// Pressure of the oil in psi
723        ///
724        /// Type: float
725        pub const OIL_PRESSURE: Channel<f32> = Channel::new(c"truck.oil.pressure", INITIAL);
726
727        /// Is the oil pressure warning active?
728        ///
729        /// Type: bool
730        pub const OIL_PRESSURE_WARNING: Channel<bool> =
731            Channel::new(c"truck.oil.pressure.warning", INITIAL);
732
733        /// Temperature of the oil in degrees celsius.
734        ///
735        /// Type: float
736        pub const OIL_TEMPERATURE: Channel<f32> = Channel::new(c"truck.oil.temperature", INITIAL);
737
738        /// Temperature of the water in degrees celsius.
739        ///
740        /// Type: float
741        pub const WATER_TEMPERATURE: Channel<f32> =
742            Channel::new(c"truck.water.temperature", INITIAL);
743
744        /// Is the water temperature warning active?
745        ///
746        /// Type: bool
747        pub const WATER_TEMPERATURE_WARNING: Channel<bool> =
748            Channel::new(c"truck.water.temperature.warning", INITIAL);
749
750        /// Voltage of the battery in volts.
751        ///
752        /// Type: float
753        pub const BATTERY_VOLTAGE: Channel<f32> = Channel::new(c"truck.battery.voltage", INITIAL);
754
755        /// Is the battery voltage/not charging warning active?
756        ///
757        /// Type: bool
758        pub const BATTERY_VOLTAGE_WARNING: Channel<bool> =
759            Channel::new(c"truck.battery.voltage.warning", INITIAL);
760
761        /// Is the electric enabled?
762        ///
763        /// Type: bool
764        pub const ELECTRIC_ENABLED: Channel<bool> =
765            Channel::new(c"truck.electric.enabled", INITIAL);
766
767        /// Is the engine enabled?
768        ///
769        /// Type: bool
770        pub const ENGINE_ENABLED: Channel<bool> = Channel::new(c"truck.engine.enabled", INITIAL);
771
772        /// Is the left blinker enabled?
773        ///
774        /// This represents the logical enable state of the blinker. It
775        /// it is true as long the blinker is enabled regardless of the
776        /// physical enabled state of the light (i.e. it does not blink
777        /// and ignores enable state of electric).
778        ///
779        /// Type: bool
780        pub const LBLINKER: Channel<bool> = Channel::new(c"truck.lblinker", INITIAL);
781
782        /// Is the right blinker enabled?
783        ///
784        /// This represents the logical enable state of the blinker. It
785        /// it is true as long the blinker is enabled regardless of the
786        /// physical enabled state of the light (i.e. it does not blink
787        /// and ignores enable state of electric).
788        ///
789        /// Type: bool
790        pub const RBLINKER: Channel<bool> = Channel::new(c"truck.rblinker", INITIAL);
791
792        /// Are the hazard warning light enabled?
793        ///
794        /// This represents the logical enable state of the hazard warning.
795        /// It it is true as long it is enabled regardless of the physical
796        /// enabled state of the light (i.e. it does not blink).
797        ///
798        /// Type: bool
799        pub const HAZARD_WARNING: Channel<bool> =
800            Channel::new(c"truck.hazard.warning", ETS2_1_17_ATS_1_04);
801
802        /// Is the light in the left blinker currently on?
803        ///
804        /// Type: bool
805        pub const LIGHT_LBLINKER: Channel<bool> =
806            Channel::new(c"truck.light.lblinker", ETS2_1_04_ATS_1_00);
807
808        /// Is the light in the right blinker currently on?
809        ///
810        /// Type: bool
811        pub const LIGHT_RBLINKER: Channel<bool> =
812            Channel::new(c"truck.light.rblinker", ETS2_1_04_ATS_1_00);
813
814        /// Are the parking lights enabled?
815        ///
816        /// Type: bool
817        pub const LIGHT_PARKING: Channel<bool> = Channel::new(c"truck.light.parking", INITIAL);
818
819        /// Are the low beam lights enabled?
820        ///
821        /// Type: bool
822        pub const LIGHT_LOW_BEAM: Channel<bool> = Channel::new(c"truck.light.beam.low", INITIAL);
823
824        /// Are the high beam lights enabled?
825        ///
826        /// Type: bool
827        pub const LIGHT_HIGH_BEAM: Channel<bool> = Channel::new(c"truck.light.beam.high", INITIAL);
828
829        /// Are the auxiliary front lights active?
830        ///
831        /// Those lights have several intensity levels:
832        /// - 1 - dimmed state
833        /// - 2 - full state
834        ///
835        /// Type: u32
836        pub const LIGHT_AUX_FRONT: Channel<u32> = Channel::new(c"truck.light.aux.front", INITIAL);
837
838        /// Are the auxiliary roof lights active?
839        ///
840        /// Those lights have several intensity levels:
841        /// - 1 - dimmed state
842        /// - 2 - full state
843        ///
844        /// Type: u32
845        pub const LIGHT_AUX_ROOF: Channel<u32> = Channel::new(c"truck.light.aux.roof", INITIAL);
846
847        /// Are the beacon lights enabled?
848        ///
849        /// Type: bool
850        pub const LIGHT_BEACON: Channel<bool> = Channel::new(c"truck.light.beacon", INITIAL);
851
852        /// Is the brake light active?
853        ///
854        /// Type: bool
855        pub const LIGHT_BRAKE: Channel<bool> = Channel::new(c"truck.light.brake", INITIAL);
856
857        /// Is the reverse light active?
858        ///
859        /// Type: bool
860        pub const LIGHT_REVERSE: Channel<bool> = Channel::new(c"truck.light.reverse", INITIAL);
861
862        /// Are the wipers enabled?
863        ///
864        /// Type: bool
865        pub const WIPERS: Channel<bool> = Channel::new(c"truck.wipers", INITIAL);
866
867        /// Intensity of the dashboard backlight as factor <0;1>
868        ///
869        /// Type: float
870        pub const DASHBOARD_BACKLIGHT: Channel<f32> =
871            Channel::new(c"truck.dashboard.backlight", INITIAL);
872
873        /// Is the differential lock enabled?
874        ///
875        /// Type: bool
876        pub const DIFFERENTIAL_LOCK: Channel<bool> =
877            Channel::new(c"truck.differential_lock", ETS2_1_17_ATS_1_04);
878
879        /// Is the lift axle control set to lifted state?
880        ///
881        /// Type: bool
882        pub const LIFT_AXLE: Channel<bool> = Channel::new(c"truck.lift_axle", ETS2_1_17_ATS_1_04);
883
884        /// Is the lift axle indicator lit?
885        ///
886        /// Type: bool
887        pub const LIFT_AXLE_INDICATOR: Channel<bool> =
888            Channel::new(c"truck.lift_axle.indicator", ETS2_1_17_ATS_1_04);
889
890        /// Is the trailer lift axle control set to lifted state?
891        ///
892        /// Type: bool
893        pub const TRAILER_LIFT_AXLE: Channel<bool> =
894            Channel::new(c"truck.trailer.lift_axle", ETS2_1_17_ATS_1_04);
895
896        /// Is the trailer lift axle indicator lit?
897        ///
898        /// Type: bool
899        pub const TRAILER_LIFT_AXLE_INDICATOR: Channel<bool> =
900            Channel::new(c"truck.trailer.lift_axle.indicator", ETS2_1_17_ATS_1_04);
901
902        /// Wear of the engine accessory as <0;1>
903        ///
904        /// Type: float
905        pub const WEAR_ENGINE: Channel<f32> = Channel::new(c"truck.wear.engine", INITIAL);
906
907        /// Wear of the transmission accessory as <0;1>
908        ///
909        /// Type: float
910        pub const WEAR_TRANSMISSION: Channel<f32> =
911            Channel::new(c"truck.wear.transmission", INITIAL);
912
913        /// Wear of the cabin accessory as <0;1>
914        ///
915        /// Type: float
916        pub const WEAR_CABIN: Channel<f32> = Channel::new(c"truck.wear.cabin", INITIAL);
917
918        /// Wear of the chassis accessory as <0;1>
919        ///
920        /// Type: float
921        pub const WEAR_CHASSIS: Channel<f32> = Channel::new(c"truck.wear.chassis", INITIAL);
922
923        /// Average wear across the wheel accessories as <0;1>
924        ///
925        /// Type: float
926        pub const WEAR_WHEELS: Channel<f32> = Channel::new(c"truck.wear.wheels", INITIAL);
927
928        /// The value of the odometer in km.
929        ///
930        /// Type: float
931        pub const ODOMETER: Channel<f32> = Channel::new(c"truck.odometer", INITIAL);
932
933        /// The value of truck's navigation distance (in meters).
934        ///
935        /// This is the value used by the advisor.
936        ///
937        /// Type: float
938        pub const NAVIGATION_DISTANCE: Channel<f32> =
939            Channel::new(c"truck.navigation.distance", ETS2_1_12_ATS_1_00);
940
941        /// The value of truck's navigation eta (in second).
942        ///
943        /// This is the value used by the advisor.
944        ///
945        /// Type: float
946        pub const NAVIGATION_TIME: Channel<f32> =
947            Channel::new(c"truck.navigation.time", ETS2_1_12_ATS_1_00);
948
949        /// The value of truck's navigation speed limit (in m/s).
950        ///
951        /// This is the value used by the advisor and respects the
952        /// current state of the "Route Advisor speed limit" option.
953        ///
954        /// Type: float
955        pub const NAVIGATION_SPEED_LIMIT: Channel<f32> =
956            Channel::new(c"truck.navigation.speed.limit", ETS2_1_12_ATS_1_00);
957
958        /// Vertical displacement of the wheel from its
959        /// axis in meters.
960        ///
961        /// Type: indexed float
962        pub const WHEEL_SUSP_DEFLECTION: Channel<f32> =
963            Channel::indexed(c"truck.wheel.suspension.deflection", INITIAL);
964
965        /// Is the wheel in contact with ground?
966        ///
967        /// Type: indexed bool
968        pub const WHEEL_ON_GROUND: Channel<bool> =
969            Channel::indexed(c"truck.wheel.on_ground", INITIAL);
970
971        /// Substance below the whell.
972        ///
973        /// Index of substance as delivered trough SUBSTANCE config.
974        ///
975        /// Type: indexed u32
976        pub const WHEEL_SUBSTANCE: Channel<u32> =
977            Channel::indexed(c"truck.wheel.substance", INITIAL);
978
979        /// Angular velocity of the wheel in rotations per
980        /// second.
981        ///
982        /// Positive velocity corresponds to forward movement.
983        ///
984        /// Type: indexed float
985        pub const WHEEL_VELOCITY: Channel<f32> =
986            Channel::indexed(c"truck.wheel.angular_velocity", INITIAL);
987
988        /// Steering rotation of the wheel in rotations.
989        ///
990        /// Value is from <-0.25,0.25> range in counterclockwise direction
991        /// when looking from top (e.g. 0.25 corresponds to left and
992        /// -0.25 corresponds to right).
993        ///
994        /// Set to zero for non-steered wheels.
995        ///
996        /// Type: indexed float
997        pub const WHEEL_STEERING: Channel<f32> = Channel::indexed(c"truck.wheel.steering", INITIAL);
998
999        /// Rolling rotation of the wheel in rotations.
1000        ///
1001        /// Value is from <0.0,1.0) range in which value
1002        /// increase corresponds to forward movement.
1003        ///
1004        /// Type: indexed float
1005        pub const WHEEL_ROTATION: Channel<f32> = Channel::indexed(c"truck.wheel.rotation", INITIAL);
1006
1007        /// Lift state of the wheel <0;1>
1008        ///
1009        /// For use with simple lifted/non-lifted test or logical
1010        /// visualization of the lifting progress.
1011        ///
1012        /// Value of 0 corresponds to non-lifted axle.
1013        /// Value of 1 corresponds to fully lifted axle.
1014        ///
1015        /// Set to zero or not provided for non-liftable axles.
1016        ///
1017        /// Type: indexed float
1018        pub const WHEEL_LIFT: Channel<f32> =
1019            Channel::indexed(c"truck.wheel.lift", ETS2_1_10_ATS_1_00);
1020
1021        /// Vertical displacement of the wheel axle
1022        /// from its normal position in meters as result of
1023        /// lifting.
1024        ///
1025        /// Might have non-linear relation to lift ratio.
1026        ///
1027        /// Set to zero or not provided for non-liftable axles.
1028        ///
1029        /// Type: indexed float
1030        pub const WHEEL_LIFT_OFFSET: Channel<f32> =
1031            Channel::indexed(c"truck.wheel.lift.offset", ETS2_1_10_ATS_1_00);
1032
1033        /// Every truck channel in the order used by the SDK 1.14 header.
1034        ///
1035        /// Indexed wheel and H-shifter entries keep `is_indexed() == true`;
1036        /// the array itself never supplies or guesses a runtime index.
1037        pub const ALL: [AnyChannel; COUNT] = [
1038            WORLD_PLACEMENT.erase(),
1039            LOCAL_LINEAR_VELOCITY.erase(),
1040            LOCAL_ANGULAR_VELOCITY.erase(),
1041            LOCAL_LINEAR_ACCELERATION.erase(),
1042            LOCAL_ANGULAR_ACCELERATION.erase(),
1043            CABIN_OFFSET.erase(),
1044            CABIN_ANGULAR_VELOCITY.erase(),
1045            CABIN_ANGULAR_ACCELERATION.erase(),
1046            HEAD_OFFSET.erase(),
1047            SPEED.erase(),
1048            ENGINE_RPM.erase(),
1049            ENGINE_GEAR.erase(),
1050            DISPLAYED_GEAR.erase(),
1051            INPUT_STEERING.erase(),
1052            INPUT_THROTTLE.erase(),
1053            INPUT_BRAKE.erase(),
1054            INPUT_CLUTCH.erase(),
1055            EFFECTIVE_STEERING.erase(),
1056            EFFECTIVE_THROTTLE.erase(),
1057            EFFECTIVE_BRAKE.erase(),
1058            EFFECTIVE_CLUTCH.erase(),
1059            CRUISE_CONTROL.erase(),
1060            HSHIFTER_SLOT.erase(),
1061            HSHIFTER_SELECTOR.erase(),
1062            PARKING_BRAKE.erase(),
1063            MOTOR_BRAKE.erase(),
1064            RETARDER_LEVEL.erase(),
1065            BRAKE_AIR_PRESSURE.erase(),
1066            BRAKE_AIR_PRESSURE_WARNING.erase(),
1067            BRAKE_AIR_PRESSURE_EMERGENCY.erase(),
1068            BRAKE_TEMPERATURE.erase(),
1069            FUEL.erase(),
1070            FUEL_WARNING.erase(),
1071            FUEL_AVERAGE_CONSUMPTION.erase(),
1072            FUEL_RANGE.erase(),
1073            ADBLUE.erase(),
1074            ADBLUE_WARNING.erase(),
1075            ADBLUE_AVERAGE_CONSUMPTION.erase(),
1076            OIL_PRESSURE.erase(),
1077            OIL_PRESSURE_WARNING.erase(),
1078            OIL_TEMPERATURE.erase(),
1079            WATER_TEMPERATURE.erase(),
1080            WATER_TEMPERATURE_WARNING.erase(),
1081            BATTERY_VOLTAGE.erase(),
1082            BATTERY_VOLTAGE_WARNING.erase(),
1083            ELECTRIC_ENABLED.erase(),
1084            ENGINE_ENABLED.erase(),
1085            LBLINKER.erase(),
1086            RBLINKER.erase(),
1087            HAZARD_WARNING.erase(),
1088            LIGHT_LBLINKER.erase(),
1089            LIGHT_RBLINKER.erase(),
1090            LIGHT_PARKING.erase(),
1091            LIGHT_LOW_BEAM.erase(),
1092            LIGHT_HIGH_BEAM.erase(),
1093            LIGHT_AUX_FRONT.erase(),
1094            LIGHT_AUX_ROOF.erase(),
1095            LIGHT_BEACON.erase(),
1096            LIGHT_BRAKE.erase(),
1097            LIGHT_REVERSE.erase(),
1098            WIPERS.erase(),
1099            DASHBOARD_BACKLIGHT.erase(),
1100            DIFFERENTIAL_LOCK.erase(),
1101            LIFT_AXLE.erase(),
1102            LIFT_AXLE_INDICATOR.erase(),
1103            TRAILER_LIFT_AXLE.erase(),
1104            TRAILER_LIFT_AXLE_INDICATOR.erase(),
1105            WEAR_ENGINE.erase(),
1106            WEAR_TRANSMISSION.erase(),
1107            WEAR_CABIN.erase(),
1108            WEAR_CHASSIS.erase(),
1109            WEAR_WHEELS.erase(),
1110            ODOMETER.erase(),
1111            NAVIGATION_DISTANCE.erase(),
1112            NAVIGATION_TIME.erase(),
1113            NAVIGATION_SPEED_LIMIT.erase(),
1114            WHEEL_SUSP_DEFLECTION.erase(),
1115            WHEEL_ON_GROUND.erase(),
1116            WHEEL_SUBSTANCE.erase(),
1117            WHEEL_VELOCITY.erase(),
1118            WHEEL_STEERING.erase(),
1119            WHEEL_ROTATION.erase(),
1120            WHEEL_LIFT.erase(),
1121            WHEEL_LIFT_OFFSET.erase(),
1122        ];
1123    }
1124
1125    /// Channels declared by `scssdk_telemetry_trailer_common_channels.h`.
1126    pub mod trailer {
1127        use super::super::{AnyChannel, Channel};
1128        use super::{ETS2_1_14_ATS_1_01, ETS2_1_18_ATS_1_05, INITIAL};
1129
1130        /// Number of typed descriptors in this catalog group.
1131        pub const COUNT: usize = 18;
1132
1133        /// Is the trailer connected to the truck?
1134        ///
1135        /// Type: bool
1136        ///
1137        /// For trailer indices above zero, the plugin framework derives the
1138        /// `trailer.[index].*` channel name described by the official SDK.
1139        pub const CONNECTED: Channel<bool> = Channel::new(c"trailer.connected", INITIAL);
1140
1141        /// How much is the cargo damaged that is loaded to this trailer in <0.0, 1.0> range.
1142        ///
1143        /// Type: float
1144        ///
1145        /// For trailer indices above zero, the plugin framework derives the
1146        /// `trailer.[index].*` channel name described by the official SDK.
1147        pub const CARGO_DAMAGE: Channel<f32> =
1148            Channel::new(c"trailer.cargo.damage", ETS2_1_14_ATS_1_01);
1149
1150        /// world placement trailer telemetry.
1151        ///
1152        /// Type: dplacement
1153        ///
1154        /// For trailer indices above zero, the plugin framework derives the
1155        /// `trailer.[index].*` channel name described by the official SDK.
1156        pub const WORLD_PLACEMENT: Channel<crate::DPlacement> =
1157            Channel::new(c"trailer.world.placement", INITIAL);
1158
1159        /// local linear velocity trailer telemetry.
1160        ///
1161        /// Type: fvector
1162        ///
1163        /// For trailer indices above zero, the plugin framework derives the
1164        /// `trailer.[index].*` channel name described by the official SDK.
1165        pub const LOCAL_LINEAR_VELOCITY: Channel<crate::FVector> =
1166            Channel::new(c"trailer.velocity.linear", INITIAL);
1167
1168        /// local angular velocity trailer telemetry.
1169        ///
1170        /// Type: fvector
1171        ///
1172        /// For trailer indices above zero, the plugin framework derives the
1173        /// `trailer.[index].*` channel name described by the official SDK.
1174        pub const LOCAL_ANGULAR_VELOCITY: Channel<crate::FVector> =
1175            Channel::new(c"trailer.velocity.angular", INITIAL);
1176
1177        /// local linear acceleration trailer telemetry.
1178        ///
1179        /// Type: fvector
1180        ///
1181        /// For trailer indices above zero, the plugin framework derives the
1182        /// `trailer.[index].*` channel name described by the official SDK.
1183        pub const LOCAL_LINEAR_ACCELERATION: Channel<crate::FVector> =
1184            Channel::new(c"trailer.acceleration.linear", INITIAL);
1185
1186        /// local angular acceleration trailer telemetry.
1187        ///
1188        /// Type: fvector
1189        ///
1190        /// For trailer indices above zero, the plugin framework derives the
1191        /// `trailer.[index].*` channel name described by the official SDK.
1192        pub const LOCAL_ANGULAR_ACCELERATION: Channel<crate::FVector> =
1193            Channel::new(c"trailer.acceleration.angular", INITIAL);
1194
1195        /// wear body trailer telemetry.
1196        ///
1197        /// Type: float
1198        ///
1199        /// For trailer indices above zero, the plugin framework derives the
1200        /// `trailer.[index].*` channel name described by the official SDK.
1201        pub const WEAR_BODY: Channel<f32> = Channel::new(c"trailer.wear.body", ETS2_1_18_ATS_1_05);
1202
1203        /// wear chassis trailer telemetry.
1204        ///
1205        /// Type: float
1206        ///
1207        /// For trailer indices above zero, the plugin framework derives the
1208        /// `trailer.[index].*` channel name described by the official SDK.
1209        pub const WEAR_CHASSIS: Channel<f32> = Channel::new(c"trailer.wear.chassis", INITIAL);
1210
1211        /// wear wheels trailer telemetry.
1212        ///
1213        /// Type: float
1214        ///
1215        /// For trailer indices above zero, the plugin framework derives the
1216        /// `trailer.[index].*` channel name described by the official SDK.
1217        pub const WEAR_WHEELS: Channel<f32> =
1218            Channel::new(c"trailer.wear.wheels", ETS2_1_14_ATS_1_01);
1219
1220        /// wheel susp deflection trailer telemetry.
1221        ///
1222        /// Type: indexed float
1223        ///
1224        /// For trailer indices above zero, the plugin framework derives the
1225        /// `trailer.[index].*` channel name described by the official SDK.
1226        pub const WHEEL_SUSP_DEFLECTION: Channel<f32> =
1227            Channel::indexed(c"trailer.wheel.suspension.deflection", INITIAL);
1228
1229        /// wheel on ground trailer telemetry.
1230        ///
1231        /// Type: indexed bool
1232        ///
1233        /// For trailer indices above zero, the plugin framework derives the
1234        /// `trailer.[index].*` channel name described by the official SDK.
1235        pub const WHEEL_ON_GROUND: Channel<bool> =
1236            Channel::indexed(c"trailer.wheel.on_ground", INITIAL);
1237
1238        /// wheel substance trailer telemetry.
1239        ///
1240        /// Type: indexed u32
1241        ///
1242        /// For trailer indices above zero, the plugin framework derives the
1243        /// `trailer.[index].*` channel name described by the official SDK.
1244        pub const WHEEL_SUBSTANCE: Channel<u32> =
1245            Channel::indexed(c"trailer.wheel.substance", INITIAL);
1246
1247        /// wheel velocity trailer telemetry.
1248        ///
1249        /// Type: indexed float
1250        ///
1251        /// For trailer indices above zero, the plugin framework derives the
1252        /// `trailer.[index].*` channel name described by the official SDK.
1253        pub const WHEEL_VELOCITY: Channel<f32> =
1254            Channel::indexed(c"trailer.wheel.angular_velocity", INITIAL);
1255
1256        /// wheel steering trailer telemetry.
1257        ///
1258        /// Type: indexed float
1259        ///
1260        /// For trailer indices above zero, the plugin framework derives the
1261        /// `trailer.[index].*` channel name described by the official SDK.
1262        pub const WHEEL_STEERING: Channel<f32> =
1263            Channel::indexed(c"trailer.wheel.steering", INITIAL);
1264
1265        /// wheel rotation trailer telemetry.
1266        ///
1267        /// Type: indexed float
1268        ///
1269        /// For trailer indices above zero, the plugin framework derives the
1270        /// `trailer.[index].*` channel name described by the official SDK.
1271        pub const WHEEL_ROTATION: Channel<f32> =
1272            Channel::indexed(c"trailer.wheel.rotation", INITIAL);
1273
1274        /// wheel lift trailer telemetry.
1275        ///
1276        /// Type: indexed float
1277        ///
1278        /// For trailer indices above zero, the plugin framework derives the
1279        /// `trailer.[index].*` channel name described by the official SDK.
1280        pub const WHEEL_LIFT: Channel<f32> =
1281            Channel::indexed(c"trailer.wheel.lift", ETS2_1_14_ATS_1_01);
1282
1283        /// wheel lift offset trailer telemetry.
1284        ///
1285        /// Type: indexed float
1286        ///
1287        /// For trailer indices above zero, the plugin framework derives the
1288        /// `trailer.[index].*` channel name described by the official SDK.
1289        pub const WHEEL_LIFT_OFFSET: Channel<f32> =
1290            Channel::indexed(c"trailer.wheel.lift.offset", ETS2_1_14_ATS_1_01);
1291
1292        /// Every first-trailer channel in SDK 1.14 header order.
1293        ///
1294        /// These canonical names use the backward-compatible `trailer.*`
1295        /// form. Multi-trailer subscription remains an explicit framework
1296        /// operation which derives `trailer.[index].*` for indices 0 through
1297        /// 9; catalog enumeration itself performs no such expansion.
1298        pub const ALL: [AnyChannel; COUNT] = [
1299            CONNECTED.erase(),
1300            CARGO_DAMAGE.erase(),
1301            WORLD_PLACEMENT.erase(),
1302            LOCAL_LINEAR_VELOCITY.erase(),
1303            LOCAL_ANGULAR_VELOCITY.erase(),
1304            LOCAL_LINEAR_ACCELERATION.erase(),
1305            LOCAL_ANGULAR_ACCELERATION.erase(),
1306            WEAR_BODY.erase(),
1307            WEAR_CHASSIS.erase(),
1308            WEAR_WHEELS.erase(),
1309            WHEEL_SUSP_DEFLECTION.erase(),
1310            WHEEL_ON_GROUND.erase(),
1311            WHEEL_SUBSTANCE.erase(),
1312            WHEEL_VELOCITY.erase(),
1313            WHEEL_STEERING.erase(),
1314            WHEEL_ROTATION.erase(),
1315            WHEEL_LIFT.erase(),
1316            WHEEL_LIFT_OFFSET.erase(),
1317        ];
1318    }
1319
1320    /// Channels declared by `scssdk_telemetry_job_common_channels.h`.
1321    pub mod job {
1322        use super::super::{AnyChannel, Channel};
1323        use super::ETS2_1_14_ATS_1_01;
1324
1325        /// Number of typed descriptors in this catalog group.
1326        pub const COUNT: usize = 1;
1327
1328        /// The total damage of the cargo in range 0.0 to 1.0.
1329        ///
1330        /// Type: float
1331        pub const CARGO_DAMAGE: Channel<f32> =
1332            Channel::new(c"job.cargo.damage", ETS2_1_14_ATS_1_01);
1333
1334        /// Every job channel in SDK 1.14 header order.
1335        pub const ALL: [AnyChannel; COUNT] = [CARGO_DAMAGE.erase()];
1336    }
1337
1338    /// Total number of typed descriptors in the catalog.
1339    pub const COUNT: usize = common::COUNT + truck::COUNT + trailer::COUNT + job::COUNT;
1340
1341    /// Every public telemetry channel from the SDK 1.14 header bundle.
1342    ///
1343    /// Entries follow header grouping and declaration order: common, truck,
1344    /// trailer, then job. The catalog is descriptive only. Merely iterating it
1345    /// never registers anything; plugin code must still call the appropriate
1346    /// explicit `PluginContext::subscribe*` method for every desired channel,
1347    /// index, trailer number, delivery flag, and requested representation.
1348    pub const ALL: [AnyChannel; COUNT] = [
1349        common::LOCAL_SCALE.erase(),
1350        common::GAME_TIME.erase(),
1351        common::MULTIPLAYER_TIME_OFFSET.erase(),
1352        common::NEXT_REST_STOP.erase(),
1353        truck::WORLD_PLACEMENT.erase(),
1354        truck::LOCAL_LINEAR_VELOCITY.erase(),
1355        truck::LOCAL_ANGULAR_VELOCITY.erase(),
1356        truck::LOCAL_LINEAR_ACCELERATION.erase(),
1357        truck::LOCAL_ANGULAR_ACCELERATION.erase(),
1358        truck::CABIN_OFFSET.erase(),
1359        truck::CABIN_ANGULAR_VELOCITY.erase(),
1360        truck::CABIN_ANGULAR_ACCELERATION.erase(),
1361        truck::HEAD_OFFSET.erase(),
1362        truck::SPEED.erase(),
1363        truck::ENGINE_RPM.erase(),
1364        truck::ENGINE_GEAR.erase(),
1365        truck::DISPLAYED_GEAR.erase(),
1366        truck::INPUT_STEERING.erase(),
1367        truck::INPUT_THROTTLE.erase(),
1368        truck::INPUT_BRAKE.erase(),
1369        truck::INPUT_CLUTCH.erase(),
1370        truck::EFFECTIVE_STEERING.erase(),
1371        truck::EFFECTIVE_THROTTLE.erase(),
1372        truck::EFFECTIVE_BRAKE.erase(),
1373        truck::EFFECTIVE_CLUTCH.erase(),
1374        truck::CRUISE_CONTROL.erase(),
1375        truck::HSHIFTER_SLOT.erase(),
1376        truck::HSHIFTER_SELECTOR.erase(),
1377        truck::PARKING_BRAKE.erase(),
1378        truck::MOTOR_BRAKE.erase(),
1379        truck::RETARDER_LEVEL.erase(),
1380        truck::BRAKE_AIR_PRESSURE.erase(),
1381        truck::BRAKE_AIR_PRESSURE_WARNING.erase(),
1382        truck::BRAKE_AIR_PRESSURE_EMERGENCY.erase(),
1383        truck::BRAKE_TEMPERATURE.erase(),
1384        truck::FUEL.erase(),
1385        truck::FUEL_WARNING.erase(),
1386        truck::FUEL_AVERAGE_CONSUMPTION.erase(),
1387        truck::FUEL_RANGE.erase(),
1388        truck::ADBLUE.erase(),
1389        truck::ADBLUE_WARNING.erase(),
1390        truck::ADBLUE_AVERAGE_CONSUMPTION.erase(),
1391        truck::OIL_PRESSURE.erase(),
1392        truck::OIL_PRESSURE_WARNING.erase(),
1393        truck::OIL_TEMPERATURE.erase(),
1394        truck::WATER_TEMPERATURE.erase(),
1395        truck::WATER_TEMPERATURE_WARNING.erase(),
1396        truck::BATTERY_VOLTAGE.erase(),
1397        truck::BATTERY_VOLTAGE_WARNING.erase(),
1398        truck::ELECTRIC_ENABLED.erase(),
1399        truck::ENGINE_ENABLED.erase(),
1400        truck::LBLINKER.erase(),
1401        truck::RBLINKER.erase(),
1402        truck::HAZARD_WARNING.erase(),
1403        truck::LIGHT_LBLINKER.erase(),
1404        truck::LIGHT_RBLINKER.erase(),
1405        truck::LIGHT_PARKING.erase(),
1406        truck::LIGHT_LOW_BEAM.erase(),
1407        truck::LIGHT_HIGH_BEAM.erase(),
1408        truck::LIGHT_AUX_FRONT.erase(),
1409        truck::LIGHT_AUX_ROOF.erase(),
1410        truck::LIGHT_BEACON.erase(),
1411        truck::LIGHT_BRAKE.erase(),
1412        truck::LIGHT_REVERSE.erase(),
1413        truck::WIPERS.erase(),
1414        truck::DASHBOARD_BACKLIGHT.erase(),
1415        truck::DIFFERENTIAL_LOCK.erase(),
1416        truck::LIFT_AXLE.erase(),
1417        truck::LIFT_AXLE_INDICATOR.erase(),
1418        truck::TRAILER_LIFT_AXLE.erase(),
1419        truck::TRAILER_LIFT_AXLE_INDICATOR.erase(),
1420        truck::WEAR_ENGINE.erase(),
1421        truck::WEAR_TRANSMISSION.erase(),
1422        truck::WEAR_CABIN.erase(),
1423        truck::WEAR_CHASSIS.erase(),
1424        truck::WEAR_WHEELS.erase(),
1425        truck::ODOMETER.erase(),
1426        truck::NAVIGATION_DISTANCE.erase(),
1427        truck::NAVIGATION_TIME.erase(),
1428        truck::NAVIGATION_SPEED_LIMIT.erase(),
1429        truck::WHEEL_SUSP_DEFLECTION.erase(),
1430        truck::WHEEL_ON_GROUND.erase(),
1431        truck::WHEEL_SUBSTANCE.erase(),
1432        truck::WHEEL_VELOCITY.erase(),
1433        truck::WHEEL_STEERING.erase(),
1434        truck::WHEEL_ROTATION.erase(),
1435        truck::WHEEL_LIFT.erase(),
1436        truck::WHEEL_LIFT_OFFSET.erase(),
1437        trailer::CONNECTED.erase(),
1438        trailer::CARGO_DAMAGE.erase(),
1439        trailer::WORLD_PLACEMENT.erase(),
1440        trailer::LOCAL_LINEAR_VELOCITY.erase(),
1441        trailer::LOCAL_ANGULAR_VELOCITY.erase(),
1442        trailer::LOCAL_LINEAR_ACCELERATION.erase(),
1443        trailer::LOCAL_ANGULAR_ACCELERATION.erase(),
1444        trailer::WEAR_BODY.erase(),
1445        trailer::WEAR_CHASSIS.erase(),
1446        trailer::WEAR_WHEELS.erase(),
1447        trailer::WHEEL_SUSP_DEFLECTION.erase(),
1448        trailer::WHEEL_ON_GROUND.erase(),
1449        trailer::WHEEL_SUBSTANCE.erase(),
1450        trailer::WHEEL_VELOCITY.erase(),
1451        trailer::WHEEL_STEERING.erase(),
1452        trailer::WHEEL_ROTATION.erase(),
1453        trailer::WHEEL_LIFT.erase(),
1454        trailer::WHEEL_LIFT_OFFSET.erase(),
1455        job::CARGO_DAMAGE.erase(),
1456    ];
1457
1458    pub use common::{GAME_TIME, LOCAL_SCALE, MULTIPLAYER_TIME_OFFSET, NEXT_REST_STOP};
1459    pub use job::CARGO_DAMAGE as JOB_CARGO_DAMAGE;
1460    pub use truck::{
1461        ENGINE_GEAR as TRUCK_ENGINE_GEAR, ENGINE_RPM as TRUCK_ENGINE_RPM,
1462        NAVIGATION_DISTANCE as TRUCK_NAVIGATION_DISTANCE,
1463        NAVIGATION_SPEED_LIMIT as TRUCK_NAVIGATION_SPEED_LIMIT,
1464        NAVIGATION_TIME as TRUCK_NAVIGATION_TIME, SPEED as TRUCK_SPEED,
1465        WORLD_PLACEMENT as TRUCK_WORLD_PLACEMENT,
1466    };
1467
1468    const _: [(); 107] = [(); COUNT];
1469}
1470
1471#[cfg(test)]
1472mod tests {
1473    use super::*;
1474    use crate::GameSchemaVersion;
1475
1476    fn assert_since<T: ChannelValue>(
1477        channel: Channel<T>,
1478        ets2: Option<GameSchemaVersion>,
1479        ats: Option<GameSchemaVersion>,
1480    ) {
1481        assert_eq!(
1482            channel.availability().available_since_ets2(),
1483            ets2,
1484            "{:?}",
1485            channel.name()
1486        );
1487        assert_eq!(
1488            channel.availability().available_since_ats(),
1489            ats,
1490            "{:?}",
1491            channel.name()
1492        );
1493    }
1494
1495    fn assert_catalog_matches_raw(typed: &[AnyChannel], raw: &[&[u8]]) {
1496        assert_eq!(typed.len(), raw.len());
1497        for (descriptor, raw_name) in typed.iter().zip(raw) {
1498            assert_eq!(descriptor.name().to_bytes_with_nul(), *raw_name);
1499        }
1500    }
1501
1502    #[test]
1503    fn typed_channels_select_the_sdk_value_type() {
1504        assert_eq!(
1505            channels::TRUCK_SPEED.value_type(),
1506            sys::SCS_VALUE_TYPE_FLOAT
1507        );
1508        assert_eq!(
1509            channels::TRUCK_ENGINE_GEAR.value_type(),
1510            sys::SCS_VALUE_TYPE_S32
1511        );
1512        assert_eq!(
1513            channels::TRUCK_WORLD_PLACEMENT.value_type(),
1514            sys::SCS_VALUE_TYPE_DPLACEMENT
1515        );
1516    }
1517
1518    #[test]
1519    fn channel_flags_match_the_header_bits() {
1520        assert_eq!(ChannelFlags::EACH_FRAME.bits(), 1);
1521        assert_eq!(ChannelFlags::NO_VALUE.bits(), 2);
1522        assert_eq!(
1523            (ChannelFlags::EACH_FRAME | ChannelFlags::NO_VALUE).bits(),
1524            3
1525        );
1526    }
1527
1528    #[test]
1529    fn typed_catalog_counts_match_the_raw_sdk_catalog() {
1530        assert_eq!(channels::common::COUNT, sys::channels::common::COUNT);
1531        assert_eq!(channels::truck::COUNT, sys::channels::truck::COUNT);
1532        assert_eq!(channels::trailer::COUNT, sys::channels::trailer::COUNT);
1533        assert_eq!(channels::job::COUNT, sys::channels::job::COUNT);
1534        assert_eq!(channels::COUNT, sys::channels::COUNT);
1535        assert_eq!(channels::COUNT, 107);
1536    }
1537
1538    #[test]
1539    fn every_typed_channel_matches_the_raw_header_catalog() {
1540        assert_catalog_matches_raw(&channels::common::ALL, &sys::channels::common::ALL);
1541        assert_catalog_matches_raw(&channels::truck::ALL, &sys::channels::truck::ALL);
1542        assert_catalog_matches_raw(&channels::trailer::ALL, &sys::channels::trailer::ALL);
1543        assert_catalog_matches_raw(&channels::job::ALL, &sys::channels::job::ALL);
1544
1545        let groups = [
1546            channels::common::ALL.as_slice(),
1547            channels::truck::ALL.as_slice(),
1548            channels::trailer::ALL.as_slice(),
1549            channels::job::ALL.as_slice(),
1550        ];
1551        let mut offset = 0;
1552        for group in groups {
1553            let end = offset + group.len();
1554            assert_eq!(&channels::ALL[offset..end], group);
1555            offset = end;
1556        }
1557        assert_eq!(offset, channels::COUNT);
1558
1559        for (position, descriptor) in channels::ALL.iter().enumerate() {
1560            assert!(
1561                channels::ALL[..position]
1562                    .iter()
1563                    .all(|earlier| earlier.name() != descriptor.name()),
1564                "duplicate channel descriptor: {:?}",
1565                descriptor.name()
1566            );
1567        }
1568        assert_eq!(
1569            channels::ALL
1570                .iter()
1571                .filter(|channel| channel.is_indexed())
1572                .count(),
1573            17
1574        );
1575    }
1576
1577    #[test]
1578    fn descriptors_preserve_name_type_and_index_mode() {
1579        assert_eq!(channels::common::GAME_TIME.name(), c"game.time");
1580        assert_eq!(
1581            channels::truck::WORLD_PLACEMENT.erase(),
1582            AnyChannel {
1583                name: c"truck.world.placement",
1584                value_type: ValueType::DPlacement,
1585                indexed: false,
1586                availability: GameSchemaAvailability::new(
1587                    Some(crate::game::ets2::V1_00),
1588                    Some(crate::game::ats::V1_00),
1589                ),
1590            }
1591        );
1592        assert_eq!(
1593            channels::truck::WHEEL_ROTATION.erase(),
1594            AnyChannel {
1595                name: c"truck.wheel.rotation",
1596                value_type: ValueType::F32,
1597                indexed: true,
1598                availability: GameSchemaAvailability::new(
1599                    Some(crate::game::ets2::V1_00),
1600                    Some(crate::game::ats::V1_00),
1601                ),
1602            }
1603        );
1604        assert_eq!(
1605            channels::trailer::WHEEL_ON_GROUND.erase(),
1606            AnyChannel {
1607                name: c"trailer.wheel.on_ground",
1608                value_type: ValueType::Bool,
1609                indexed: true,
1610                availability: GameSchemaAvailability::new(
1611                    Some(crate::game::ets2::V1_00),
1612                    Some(crate::game::ats::V1_00),
1613                ),
1614            }
1615        );
1616        assert_eq!(channels::job::CARGO_DAMAGE.name(), c"job.cargo.damage");
1617    }
1618
1619    #[test]
1620    fn channel_availability_follows_early_game_schema_history() {
1621        use channels::{common, truck};
1622
1623        // Early ETS2 additions are preserved even though the oldest archived
1624        // downloadable bundle (SDK 1.0) already reports schema 1.05.
1625        assert_since(
1626            truck::BRAKE_AIR_PRESSURE_EMERGENCY,
1627            Some(crate::game::ets2::V1_01),
1628            Some(crate::game::ats::V1_00),
1629        );
1630        assert_since(
1631            truck::CABIN_OFFSET,
1632            Some(crate::game::ets2::V1_02),
1633            Some(crate::game::ats::V1_00),
1634        );
1635        for channel in [truck::LIGHT_LBLINKER, truck::LIGHT_RBLINKER] {
1636            assert_since(
1637                channel,
1638                Some(crate::game::ets2::V1_04),
1639                Some(crate::game::ats::V1_00),
1640            );
1641        }
1642
1643        for channel in [
1644            common::GAME_TIME.requesting::<i32>(),
1645            common::NEXT_REST_STOP,
1646        ] {
1647            assert_since(
1648                channel,
1649                Some(crate::game::ets2::V1_09),
1650                Some(crate::game::ats::V1_00),
1651            );
1652        }
1653        for channel in [truck::WHEEL_LIFT, truck::WHEEL_LIFT_OFFSET] {
1654            assert_since(
1655                channel,
1656                Some(crate::game::ets2::V1_10),
1657                Some(crate::game::ats::V1_00),
1658            );
1659        }
1660        assert_since(
1661            truck::DISPLAYED_GEAR,
1662            Some(crate::game::ets2::V1_11),
1663            Some(crate::game::ats::V1_00),
1664        );
1665        for channel in [
1666            truck::FUEL_RANGE,
1667            truck::NAVIGATION_DISTANCE,
1668            truck::NAVIGATION_TIME,
1669            truck::NAVIGATION_SPEED_LIMIT,
1670        ] {
1671            assert_since(
1672                channel,
1673                Some(crate::game::ets2::V1_12),
1674                Some(crate::game::ats::V1_00),
1675            );
1676        }
1677        for channel in [
1678            truck::ADBLUE,
1679            truck::ADBLUE_WARNING.requesting::<f32>(),
1680            truck::ADBLUE_AVERAGE_CONSUMPTION,
1681        ] {
1682            assert_since(channel, Some(crate::game::ets2::V1_12), None);
1683        }
1684    }
1685
1686    #[test]
1687    fn channel_availability_follows_later_game_schema_history() {
1688        use channels::{common, job, trailer, truck};
1689
1690        for channel in [
1691            trailer::CARGO_DAMAGE,
1692            trailer::WEAR_WHEELS,
1693            trailer::WHEEL_LIFT,
1694            trailer::WHEEL_LIFT_OFFSET,
1695            job::CARGO_DAMAGE,
1696        ] {
1697            assert_since(
1698                channel,
1699                Some(crate::game::ets2::V1_14),
1700                Some(crate::game::ats::V1_01),
1701            );
1702        }
1703        for channel in [
1704            truck::HAZARD_WARNING,
1705            truck::DIFFERENTIAL_LOCK,
1706            truck::LIFT_AXLE,
1707            truck::LIFT_AXLE_INDICATOR,
1708            truck::TRAILER_LIFT_AXLE,
1709            truck::TRAILER_LIFT_AXLE_INDICATOR,
1710        ] {
1711            assert_since(
1712                channel,
1713                Some(crate::game::ets2::V1_17),
1714                Some(crate::game::ats::V1_04),
1715            );
1716        }
1717        assert_since(
1718            common::MULTIPLAYER_TIME_OFFSET,
1719            Some(crate::game::ets2::V1_18),
1720            Some(crate::game::ats::V1_05),
1721        );
1722        assert_since(
1723            trailer::WEAR_BODY,
1724            Some(crate::game::ets2::V1_18),
1725            Some(crate::game::ats::V1_05),
1726        );
1727
1728        assert_eq!(
1729            channels::MULTI_TRAILER_AVAILABILITY,
1730            GameSchemaAvailability::new(
1731                Some(crate::game::ets2::V1_14),
1732                Some(crate::game::ats::V1_01),
1733            )
1734        );
1735        assert!(
1736            channels::ALL
1737                .iter()
1738                .all(|channel| channel.availability().available_since_ets2().is_some())
1739        );
1740        assert_eq!(
1741            channels::ALL
1742                .iter()
1743                .filter(|channel| channel.availability().available_since_ats().is_none())
1744                .count(),
1745            3,
1746            "only the three AdBlue channels are explicitly unsupported by ATS",
1747        );
1748    }
1749}