├── resources └── test │ └── AgIsoStack-rs-test-pool.iop ├── CODE_OF_CONDUCT.md ├── src ├── network_management │ ├── mod.rs │ ├── can_message.rs │ ├── common_parameter_group_numbers.rs │ ├── control_function.rs │ ├── network_manager.rs │ └── name.rs ├── lib.rs ├── object_pool │ ├── mod.rs │ ├── vt_version.rs │ ├── object_id.rs │ ├── object_type.rs │ ├── object_pool.rs │ ├── colour.rs │ ├── object.rs │ ├── object_attributes.rs │ └── writer.rs └── driver │ ├── address.rs │ ├── frame.rs │ ├── mod.rs │ ├── pgn.rs │ ├── driver.rs │ ├── socketcan.rs │ └── can_id.rs ├── .gitignore ├── README.md ├── COPYRIGHT ├── Cargo.toml ├── LICENSE ├── .github └── workflows │ ├── docs.yml │ └── ci.yml ├── CONTRIBUTING.md └── examples └── forward.rs /resources/test/AgIsoStack-rs-test-pool.iop: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Open-Agriculture/AgIsoStack-rs/HEAD/resources/test/AgIsoStack-rs-test-pool.iop -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | AgIsoStack-rs uses its [sibling project's code of 4 | conduct](https://github.com/Open-Agriculture/AgIsoStack-plus-plus/blob/main/CODE_OF_CONDUCT.md). 5 | -------------------------------------------------------------------------------- /src/network_management/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Raven Industries inc. 2 | pub mod can_message; 3 | pub mod common_parameter_group_numbers; 4 | pub mod control_function; 5 | pub mod name; 6 | pub mod network_manager; 7 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Raven Industries inc. 2 | #![allow(clippy::bool_assert_comparison)] 3 | #![allow(clippy::needless_return)] 4 | #![allow(clippy::module_inception)] 5 | 6 | pub mod driver; 7 | pub mod network_management; 8 | pub mod object_pool; 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # MSVC Windows builds of rustc generate these, which store debugging information 14 | *.pdb 15 | -------------------------------------------------------------------------------- /src/object_pool/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod colour; 2 | pub mod reader; 3 | pub mod writer; 4 | 5 | mod object; 6 | mod object_attributes; 7 | mod object_id; 8 | mod object_pool; 9 | mod object_type; 10 | mod vt_version; 11 | 12 | use crate::network_management::name::NAME; 13 | 14 | pub use colour::Colour; 15 | pub use object_pool::ObjectPool; 16 | pub use object_type::ObjectType; 17 | 18 | #[derive(Debug)] 19 | pub enum ParseError { 20 | DataEmpty, 21 | UnknownObjectType, 22 | } 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AgIsoStack-rs 2 | 3 | ## About This Library 4 | 5 | AgIsoStack-rs is an MIT licensed hardware agnostic ISOBUS (ISO11783) and SAE J1939 CAN stack written in Rust. 6 | 7 | **This project is an experimental Work in Progress, and is not suitable for consumption.** 8 | 9 | ## Compilation 10 | 11 | This library is built with Cargo 12 | 13 | ```sh 14 | cargo build 15 | ``` 16 | 17 | ## Tests 18 | 19 | Tests for this library are run with Cargo 20 | 21 | ```sh 22 | cargo test 23 | ``` 24 | -------------------------------------------------------------------------------- /src/driver/address.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Raven Industries inc. 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 4 | #[repr(transparent)] 5 | pub struct Address(pub u8); 6 | 7 | impl Address { 8 | /// Address representing broadcasts for destination specific PGNs 9 | pub const GLOBAL: Address = Address(0xFF); 10 | /// Alias for the global address 11 | pub const BROADCAST: Address = Address(0xFF); 12 | /// The null address is used by ECUs without an address such as during address claiming 13 | pub const NULL: Address = Address(0xFE); 14 | } 15 | 16 | // TODO: custom Debug impl and helpers 17 | -------------------------------------------------------------------------------- /src/driver/frame.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Raven Industries inc. 2 | use crate::driver::CanId; 3 | 4 | #[derive(Debug, Default)] 5 | #[repr(transparent)] 6 | pub struct Channel(u8); 7 | 8 | #[derive(Debug, Default)] 9 | pub struct Frame { 10 | // TODO: Is a Duration too large (64 + 32 bits) for an object that will be created so often? 11 | // Would it be better to use a u64 for microseconds? 12 | // TODO: Is this just a monotonically increasing number, or is it a unix timestamp? 13 | pub timestamp: std::time::Duration, 14 | pub id: CanId, 15 | pub channel: Channel, 16 | pub data: [u8; 8], 17 | pub data_length: u8, 18 | pub extended: bool, 19 | } 20 | -------------------------------------------------------------------------------- /src/driver/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Raven Industries inc. 2 | 3 | //! CAN Driver layer 4 | //! 5 | //! This module defines: 6 | //! 1. An abstract `Driver` trait for different CAN drivers to implement 7 | //! 2. `Frame`, `Pgn`, `Address`, et al types 8 | 9 | mod address; 10 | mod can_id; 11 | mod driver; 12 | mod frame; 13 | mod pgn; 14 | 15 | #[cfg(feature = "socketcan")] 16 | mod socketcan; 17 | 18 | pub use address::Address; 19 | pub use can_id::{CanId, Priority, Type}; 20 | pub use driver::{Driver, DriverCloseError, DriverOpenError, DriverReadError, DriverWriteError}; 21 | pub use frame::{Channel, Frame}; 22 | pub use pgn::Pgn; 23 | 24 | #[cfg(feature = "socketcan")] 25 | pub use self::socketcan::SocketcanDriver; 26 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | Copyright Ownership 2 | 3 | Contributing to this software is pursuant to the license agreement within 4 | GitHub available on GitHub’s website, 5 | https://docs.github.com/en/site-policy/github-terms/github-terms-of-service#d-user-generated-content. 6 | 7 | This additional provision is meant to clarify, and not replace or alter the terms on GitHub’s terms 8 | of service. Contribution to the code within this content started by Raven Industries, Inc. and its 9 | affiliated companies (“Raven”) vests in Raven all copyrights in content, subject to the 10 | non-exclusive license granted by GitHub in the GitHub Terms and Conditions. By contributing to the 11 | code, You agree Raven owns the copyright in the code as stated in this provision. 12 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ag-iso-stack" 3 | version = "0.1.0" 4 | edition = "2021" 5 | license = "MIT" 6 | description = "A Free ISO-11783 and J1939 CAN Stack" 7 | keywords = ["agriculture", "can", "canbus", "isobus", "j1939", "agritech", "smart-farming", "iso11783"] 8 | 9 | [dependencies] 10 | bitvec = "1.0.1" 11 | rand = "0.8.5" 12 | socketcan = { version = "2.0.0", optional = true } 13 | strum_macros = "0.25.2" 14 | 15 | [features] 16 | default = [] 17 | socketcan = ["dep:socketcan"] 18 | 19 | [dev-dependencies] 20 | clap = { version = "4.3.19", features = ["derive"] } 21 | ctrlc = "3.4.0" 22 | # TODO: Add optional tracing to the main library 23 | tracing = "0.1.37" 24 | tracing-subscriber = "0.3.17" 25 | 26 | [[example]] 27 | name = "forward" 28 | required-features = ["socketcan"] 29 | -------------------------------------------------------------------------------- /src/network_management/can_message.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Raven Industries inc. 2 | use super::name::NAME; 3 | use crate::driver::CanId; 4 | 5 | pub struct CANMessage { 6 | data: Vec, 7 | identifier: CanId, 8 | source_name: NAME, 9 | destination_name: NAME, 10 | } 11 | 12 | impl CANMessage { 13 | pub(super) fn new(data: Vec, identifier: CanId) -> CANMessage { 14 | CANMessage { 15 | data, 16 | identifier, 17 | source_name: NAME::default(), 18 | destination_name: NAME::default(), 19 | } 20 | } 21 | 22 | pub fn get_data(&self) -> &[u8] { 23 | self.data.as_slice() 24 | } 25 | 26 | pub fn get_identifier(&self) -> CanId { 27 | self.identifier 28 | } 29 | 30 | pub fn get_source_name(&self) -> NAME { 31 | self.source_name 32 | } 33 | 34 | pub fn get_destination_name(&self) -> NAME { 35 | self.destination_name 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Raven Industries inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/object_pool/vt_version.rs: -------------------------------------------------------------------------------- 1 | use crate::object_pool::ParseError; 2 | use crate::object_pool::ParseError::UnknownObjectType; 3 | 4 | #[derive(Debug, Default)] 5 | pub enum VtVersion { 6 | Version0, 7 | Version1, 8 | Version2, 9 | #[default] 10 | Version3, 11 | Version4, 12 | Version5, 13 | Version6, 14 | } 15 | 16 | impl From for u8 { 17 | fn from(vt_version: VtVersion) -> Self { 18 | match vt_version { 19 | VtVersion::Version0 => 0, 20 | VtVersion::Version1 => 1, 21 | VtVersion::Version2 => 2, 22 | VtVersion::Version3 => 3, 23 | VtVersion::Version4 => 4, 24 | VtVersion::Version5 => 5, 25 | VtVersion::Version6 => 6, 26 | } 27 | } 28 | } 29 | 30 | impl TryFrom for VtVersion { 31 | type Error = ParseError; 32 | 33 | fn try_from(value: u8) -> Result { 34 | match value { 35 | 0 => Ok(VtVersion::Version0), 36 | 1 => Ok(VtVersion::Version1), 37 | 2 => Ok(VtVersion::Version2), 38 | 3 => Ok(VtVersion::Version3), 39 | 4 => Ok(VtVersion::Version4), 40 | 5 => Ok(VtVersion::Version5), 41 | 6 => Ok(VtVersion::Version6), 42 | _ => Err(UnknownObjectType), 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: Docs 2 | on: [push] 3 | 4 | jobs: 5 | rustdoc: 6 | runs-on: ubuntu-latest 7 | env: 8 | RUSTDOCFLAGS: -Dwarnings 9 | CARGO_TERM_COLOR: always 10 | NUM_JOBS: 2 11 | steps: 12 | - name: Checkout sources 13 | uses: actions/checkout@v3 14 | 15 | - name: Install stable toolchain 16 | uses: actions-rs/toolchain@v1 17 | with: 18 | toolchain: stable 19 | profile: minimal 20 | override: true 21 | 22 | - name: Setup cache 23 | uses: Swatinem/rust-cache@v2 24 | 25 | - name: Build documentation 26 | uses: actions-rs/cargo@v1 27 | with: 28 | command: doc 29 | args: --no-deps --all-features 30 | 31 | - name: Prepare HTML 32 | run: | 33 | mkdir -p ./public/ 34 | # The ./target/doc/ directory has assets one level above the index.html, but it 35 | # requires relative links, so we redirect to it from one level higher. 36 | echo '' >./public/index.html 37 | cp -r target/doc/* ./public/ 38 | 39 | # TODO: Replace with docs.rs once crate publishing is figured out 40 | - name: Deploy documentation 41 | uses: peaceiris/actions-gh-pages@v3 42 | if: github.ref == 'refs/heads/main' 43 | with: 44 | github_token: ${{ secrets.GITHUB_TOKEN }} 45 | publish_dir: ./public/ 46 | force_orphan: true 47 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to AgIsoStack-rs 2 | 3 | We warmly welcome you to AgIsoStack-rs! 4 | 5 | Contributing to our open source repository implementing the ISOBUS (ISO11783) standard in the 6 | agricultural industry can involve anything from adding new features, improving existing code, fixing 7 | bugs, or even just helping to test and document the project. We greatly appreciate any contributions 8 | you can make to help drive progress and innovation in this field. Thank you for your interest and 9 | support! 10 | 11 | We accept all public contributions that adhere to the [code of 12 | conduct](https://github.com/Open-Agriculture/AgIsoStack-plus-plus/blob/main/CODE_OF_CONDUCT.md) 13 | defined by our sibling project 14 | [AgIsoStack++](https://github.com/Open-Agriculture/AgIsoStack-plus-plus). Additionally, for PR's we 15 | require the pass of all automated pre-merge checks, and a manual code review by a repository 16 | maintainer to ensure that our high code quality and project standards are maintained. 17 | 18 | ## What are our guidelines? 19 | 20 | * Contributions must follow the usual `rustc` and `clippy` lints, and the default `rustfmt` 21 | settings. Exceptions to lints are allowed, but should be defined in the project's `lib.rs` file 22 | 23 | You can check these settings with `cargo check`, `cargo clippy`, and `cargo fmt` 24 | * The code should compile with no warnings in the CI pipeline using `RUSTFLAGS=-Dwarnings` 25 | * `rustdoc` documentation should compile without warnings in the CI pipeline using 26 | `RUSTDOCFLAGS=-Dwarnings` 27 | * No code should be added under a more strict license than MIT, or which has not had conditions met 28 | to be distributed under our license 29 | * There must be a copyright notice in every source file 30 | * Contributions must pass the CI pipeline 31 | * Aim for ~80% code coverage on new code, but prioritize high quality tests over gaming code 32 | coverage percentages 33 | 34 | ## Minimum supported Rust version 35 | 36 | **TODO:** Define a MSRV 37 | 38 | ## Setting up a development environment 39 | 40 | ## Copyright 41 | 42 | AgIsoStack-rs is sponsored by Raven Industries inc. as an open source project under the MIT license 43 | started during an Innovation Sprint under the condition that Raven maintains copyright over the 44 | project, including future contributions. See the [COPYRIGHT](./COPYRIGHT) file for details. 45 | 46 | In addition to the `COPYRIGHT` file, every source file should include the following copyright notice 47 | 48 | ```rust 49 | // Copyright 2023 Raven Industries, inc. 50 | ``` 51 | -------------------------------------------------------------------------------- /src/network_management/common_parameter_group_numbers.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Raven Industries inc. 2 | 3 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] 4 | pub enum CommonParameterGroupNumbers { 5 | TractorImplementManagementServerToTimClient = 0x002300, 6 | TractorImplementManagementClientToTimServer = 0x002400, 7 | AuthenticationClientToAuthenticationServer = 0x006F00, 8 | AuthenticationServerToAuthenticationClient = 0x007000, 9 | NameManagement = 0x009300, 10 | GuidanceMachineStatus = 0x00AC00, 11 | GuidanceSystemCommand = 0x00AD00, 12 | ExtendedTransportProtocolData = 0x00C700, 13 | ExtendedTransportProtocolCommand = 0x00C800, 14 | RequestForRepetitionRate = 0x00CC00, 15 | BinaryDataTransfer = 0x00D700, 16 | MemoryAccessResponse = 0x00D800, 17 | MemoryAccessRequest = 0x00D900, 18 | StopStartBroadcast = 0x00DF00, 19 | VirtualTerminalToNode = 0x00E600, 20 | NodeToVirtualTerminal = 0x00E700, 21 | Acknowledgement = 0x00E800, 22 | ParameterGroupNumberRequest = 0x00EA00, 23 | TransportProtocolData = 0x00EB00, 24 | TransportProtocolCommand = 0x00EC00, 25 | AddressClaim = 0x00EE00, 26 | ProprietaryA = 0x00EF00, 27 | ElectronicEngineController2 = 0x00F003, 28 | ElectronicEngineController1 = 0x00F004, 29 | HeartbeatMessage = 0x00F0E4, 30 | ProductIdentification = 0x00FC8D, 31 | ControlFunctionFunctionalities = 0x00FC8E, 32 | DiagnosticProtocol = 0x00FD32, 33 | IsobusComplianceCertificationMessage = 0x00FD42, 34 | EcuIdentificationInformation = 0x00FDC5, 35 | WorkingSetMaster = 0x00FE0D, 36 | ResponseForRepetitionRate = 0x00FE0E, 37 | MaintainPower = 0x00FE47, 38 | WheelBasedSpeedAndDistance = 0x00FE48, 39 | GroundBasedSpeedAndDistance = 0x00FE49, 40 | ActiveDiagnosticTroubleCodes = 0x00FECA, 41 | PreviouslyActiveDiagnosticTroubleCodes = 0x00FECB, 42 | DiagnosticDataClearResetOfPreviouslyActiveDtcs = 0x00FECC, 43 | FreezeFrameParameters = 0x00FECD, 44 | DiagnosticDataClearResetForActiveDtcs = 0x00FED3, 45 | CommandedAddress = 0x00FED8, 46 | SoftwareIdentification = 0x00FEDA, 47 | TimeDate = 0x00FEE6, 48 | EngineTemperature1 = 0x00FEEE, 49 | CruiseControlVehicleSpeed1 = 0x00FEF1, 50 | IntakeExhaustConditions1 = 0x00FEF6, 51 | NmeaAttitude = 0x01F119, 52 | NmeaCogSogRapidUpdate = 0x01F802, 53 | NmeaPositionDeltaHighPrecisionRapidUpdate = 0x01F803, 54 | NmeaAltitudeDeltaHighPrecisionRapidUpdate = 0x01F804, 55 | NmeaGnssPositionData = 0x01F805, 56 | NmeaTimeDate = 0x01F809, 57 | NmeaGnssDops = 0x01FA03, 58 | NmeaGnssSatsInView = 0x01FA04, 59 | NmeaGnssPseudoRangeNoiseStatistics = 0x01FA06, 60 | NmeaGnssPseudoRangeErrorStatistics = 0x01FA0B, 61 | AllowAll = 0xFFFFFF, 62 | } 63 | -------------------------------------------------------------------------------- /examples/forward.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Raven Industries inc. 2 | 3 | use std::sync::mpsc::channel; 4 | 5 | use ag_iso_stack::driver::{Driver, DriverReadError, Frame, SocketcanDriver}; 6 | use clap::Parser; 7 | 8 | /// Forward CAN traffic from one interface to another 9 | #[derive(Debug, Parser)] 10 | #[clap(name = "forward", verbatim_doc_comment)] 11 | struct Options { 12 | /// The log level 13 | #[clap(short, long, default_value_t = tracing::Level::DEBUG)] 14 | pub log_level: tracing::Level, 15 | 16 | /// The interface to read traffic from 17 | /// 18 | /// Can be either a string interface name, or an integer interface index 19 | #[clap(short, long, default_value_t = String::from("can0"))] 20 | pub input_interface: String, 21 | 22 | /// The interface to write traffic to 23 | /// 24 | /// Can be either a string interface name, or an integer interface index 25 | #[clap(short, long, default_value_t = String::from("can1"))] 26 | pub output_interface: String, 27 | } 28 | 29 | fn create_driver(iface: &str) -> impl Driver { 30 | if let Ok(index) = iface.parse::() { 31 | SocketcanDriver::new_by_index(index) 32 | } else { 33 | SocketcanDriver::new_by_name(iface) 34 | } 35 | } 36 | 37 | fn main() { 38 | let opts = Options::parse(); 39 | 40 | let subscriber = tracing_subscriber::fmt() 41 | // ... add configuration 42 | .finish(); 43 | tracing::subscriber::set_global_default(subscriber) 44 | .map_err(|_err| eprintln!("Unable to set global default subscriber")) 45 | .unwrap(); 46 | 47 | tracing::info!( 48 | "Forwarding CAN traffic from {} to {}", 49 | opts.input_interface, 50 | opts.output_interface 51 | ); 52 | 53 | let mut input = create_driver(&opts.input_interface); 54 | let mut output = create_driver(&opts.output_interface); 55 | 56 | input.open().unwrap(); 57 | output.open().unwrap(); 58 | 59 | let (tx, rx) = channel(); 60 | ctrlc::set_handler(move || tx.send(true).unwrap()).unwrap(); 61 | 62 | loop { 63 | if rx.try_recv().is_ok() { 64 | break; 65 | } 66 | 67 | let mut frame = Frame::default(); 68 | 69 | match input.read_nonblocking(&mut frame) { 70 | Ok(_) => { 71 | tracing::info!("Read frame: {frame:?}"); 72 | tracing::info!("Attempting to write frame"); 73 | match output.write_nonblocking(&frame) { 74 | Ok(_) => tracing::info!("Wrote frame: {frame:?}"), 75 | Err(e) => tracing::info!("Failed to write frame: {e:?}"), 76 | } 77 | } 78 | Err(DriverReadError::NoFrameReady) => {} 79 | Err(e) => tracing::error!("Failed to read frame: {e:?}"), 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | types: [opened, synchronize, reopened] 7 | 8 | env: 9 | CARGO_TERM_COLOR: always 10 | NUM_JOBS: 2 11 | RUSTFLAGS: -Dwarnings 12 | 13 | jobs: 14 | # Run Format, Lint, and Test in parallel. This way we can get feedback on all topics 15 | # instead of (possibly) failing fast on the first one. It should also be faster. 16 | rustfmt: 17 | name: Format 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - name: Checkout sources 22 | uses: actions/checkout@v3 23 | 24 | - name: Install stable toolchain 25 | uses: actions-rs/toolchain@v1 26 | with: 27 | toolchain: stable 28 | profile: minimal 29 | override: true 30 | components: rustfmt 31 | 32 | - name: Setup cache 33 | uses: Swatinem/rust-cache@v2 34 | 35 | - name: Check formatting 36 | uses: actions-rs/cargo@v1 37 | with: 38 | command: fmt 39 | args: --check 40 | 41 | lint: 42 | name: Lint 43 | runs-on: ubuntu-latest 44 | 45 | steps: 46 | - name: Checkout sources 47 | uses: actions/checkout@v3 48 | 49 | - name: Install stable toolchain 50 | uses: actions-rs/toolchain@v1 51 | with: 52 | toolchain: stable 53 | profile: minimal 54 | override: true 55 | components: clippy 56 | 57 | - name: Setup cache 58 | uses: Swatinem/rust-cache@v2 59 | 60 | - name: Check linting 61 | uses: actions-rs/cargo@v1 62 | with: 63 | command: clippy 64 | args: --all-targets --all-features 65 | 66 | test_coverage: 67 | name: Test and coverage 68 | runs-on: ubuntu-latest 69 | 70 | steps: 71 | - name: Checkout sources 72 | uses: actions/checkout@v3 73 | 74 | - name: Install stable toolchain 75 | uses: actions-rs/toolchain@v1 76 | with: 77 | toolchain: stable 78 | profile: minimal 79 | override: true 80 | 81 | - name: Setup cache 82 | uses: Swatinem/rust-cache@v2 83 | 84 | - name: Setup tarpaulin 85 | uses: taiki-e/install-action@v2 86 | with: 87 | tool: cargo-tarpaulin 88 | 89 | - name: Test 90 | uses: actions-rs/cargo@v1 91 | with: 92 | command: tarpaulin 93 | args: --release --all-features --engine llvm --out xml 94 | 95 | - name: Upload to codecov.io 96 | uses: codecov/codecov-action@v2 97 | with: 98 | fail_ci_if_error: true 99 | -------------------------------------------------------------------------------- /src/object_pool/object_id.rs: -------------------------------------------------------------------------------- 1 | use crate::object_pool::ParseError; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 4 | pub struct ObjectId { 5 | id: u16, 6 | } 7 | 8 | impl ObjectId { 9 | const NULL: ObjectId = ObjectId { id: u16::MAX }; 10 | 11 | pub fn new(id: u16) -> Result { 12 | if id == Self::NULL.id { 13 | Err(ParseError::UnknownObjectType) 14 | } else { 15 | Ok(ObjectId { id }) 16 | } 17 | } 18 | } 19 | 20 | pub struct NullableObjectId(Option); 21 | 22 | impl NullableObjectId { 23 | pub const NULL: NullableObjectId = NullableObjectId(None); 24 | pub fn new(id: u16) -> Self { 25 | if id == ObjectId::NULL.id { 26 | NullableObjectId(None) 27 | } else { 28 | NullableObjectId(Some(ObjectId::new(id).unwrap())) 29 | } 30 | } 31 | } 32 | 33 | impl Default for NullableObjectId { 34 | fn default() -> Self { 35 | NullableObjectId::NULL 36 | } 37 | } 38 | 39 | impl From for NullableObjectId { 40 | fn from(id: u16) -> Self { 41 | NullableObjectId::new(id) 42 | } 43 | } 44 | 45 | impl From for u16 { 46 | fn from(id: NullableObjectId) -> Self { 47 | match id.0 { 48 | Some(id) => id.id, 49 | None => u16::from(ObjectId::NULL), 50 | } 51 | } 52 | } 53 | 54 | impl From for NullableObjectId { 55 | fn from(id: ObjectId) -> Self { 56 | if id == ObjectId::NULL { 57 | NullableObjectId(None) 58 | } else { 59 | NullableObjectId(Some(id)) 60 | } 61 | } 62 | } 63 | 64 | impl Default for ObjectId { 65 | fn default() -> Self { 66 | Self::new(0).unwrap() 67 | } 68 | } 69 | impl TryFrom for ObjectId { 70 | type Error = ParseError; 71 | 72 | fn try_from(id: u16) -> Result { 73 | ObjectId::new(id) 74 | } 75 | } 76 | 77 | impl From for u16 { 78 | fn from(val: ObjectId) -> Self { 79 | val.id 80 | } 81 | } 82 | impl TryFrom<[u8; 2]> for ObjectId { 83 | type Error = ParseError; 84 | 85 | fn try_from(val: [u8; 2]) -> Result { 86 | ObjectId::new(u16::from_le_bytes(val)) 87 | } 88 | } 89 | impl From for [u8; 2] { 90 | fn from(val: ObjectId) -> Self { 91 | val.id.to_le_bytes() 92 | } 93 | } 94 | // impl From> for ObjectId { 95 | // fn from(val: Vec) -> Self { 96 | // let val: ObjectId = val.as_slice().into(); 97 | // val 98 | // } 99 | // } 100 | // impl From for Vec { 101 | // fn from(val: ObjectId) -> Self { 102 | // let val: [u8;2] = val.into(); 103 | // val.to_vec() 104 | // } 105 | // } 106 | impl TryFrom<&[u8]> for ObjectId { 107 | type Error = ParseError; 108 | 109 | fn try_from(val: &[u8]) -> Result { 110 | match val.len() { 111 | 2.. => Ok(ObjectId::new(u16::from_le_bytes([val[0], val[1]]))?), 112 | _ => Err(ParseError::DataEmpty), 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/driver/pgn.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Raven Industries inc. 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 4 | #[repr(transparent)] 5 | pub struct Pgn(u32); 6 | 7 | impl Pgn { 8 | /// A fake PGN used to denote a PGN that does not exist 9 | pub const NULL: Pgn = Pgn(0xFFFFFFFF); 10 | 11 | pub fn from_id(can_id: u32) -> Self { 12 | const PDU2_FORMAT_MASK: u32 = 0x00F00000; 13 | let raw_pgn = if (can_id & PDU2_FORMAT_MASK) < PDU2_FORMAT_MASK { 14 | // point-to-point 15 | (can_id >> 8) & 0x03FF00 16 | } else { 17 | // broadcast 18 | (can_id >> 8) & 0x03FFFF 19 | }; 20 | Pgn(raw_pgn) 21 | } 22 | 23 | pub fn from_raw(pgn: u32) -> Self { 24 | Pgn(pgn) 25 | } 26 | 27 | #[inline] 28 | pub fn is_broadcast(&self) -> bool { 29 | !self.is_destination_specific() 30 | } 31 | 32 | #[inline] 33 | pub fn is_destination_specific(&self) -> bool { 34 | // PDU1 / destination specific PGNs have a PDU Format 0x00 - 0xEF 35 | // PDU2 / broadcast PGNs have a PDU Format 0xF0 - 0xFF 36 | self.pdu_format() <= 0xEF 37 | } 38 | 39 | #[inline] 40 | pub fn is_proprietary(&self) -> bool { 41 | self.pdu_format() == 0xEF 42 | } 43 | 44 | #[inline] 45 | pub fn raw(&self) -> u32 { 46 | self.0 47 | } 48 | 49 | #[inline] 50 | pub fn pdu_specific(&self) -> u8 { 51 | (self.raw() & 0x00FF) as u8 52 | } 53 | 54 | #[inline] 55 | pub fn pdu_format(&self) -> u8 { 56 | ((self.raw() & 0xFF00) >> 8) as u8 57 | } 58 | 59 | #[inline] 60 | pub fn data_page(&self) -> u8 { 61 | ((self.raw() & 0x10000) >> 16) as u8 62 | } 63 | 64 | #[inline] 65 | pub fn extended_data_page(&self) -> u8 { 66 | ((self.raw() & 0x20000) >> 17) as u8 67 | } 68 | } 69 | 70 | #[cfg(test)] 71 | mod tests { 72 | use super::*; 73 | 74 | #[test] 75 | fn test_from_id() { 76 | let pgn = Pgn::from_id(0x18EF1CF5); 77 | let expected = Pgn::from_raw(0x0EF00); 78 | assert_eq!(pgn, expected); 79 | 80 | let pgn = Pgn::from_id(0x18FF3F13); 81 | let expected = Pgn::from_raw(0x0FF3F); 82 | assert_eq!(pgn, expected); 83 | } 84 | 85 | #[test] 86 | fn test_bitmath() { 87 | let pgn = Pgn::from_raw(0x30000); 88 | assert_eq!(pgn.data_page(), 0x01); 89 | assert_eq!(pgn.extended_data_page(), 0x01); 90 | 91 | let pgn = Pgn::from_raw(0x0FF00); 92 | assert_eq!(pgn.pdu_format(), 0xFF); 93 | assert_eq!(pgn.pdu_specific(), 0x00); 94 | 95 | let pgn = Pgn::from_raw(0x000FF); 96 | assert_eq!(pgn.pdu_format(), 0x00); 97 | assert_eq!(pgn.pdu_specific(), 0xFF); 98 | } 99 | 100 | #[test] 101 | fn test_p2p() { 102 | let pgn = Pgn::from_raw(0x0EE00); 103 | assert_eq!(pgn.is_destination_specific(), true); 104 | let pgn = Pgn::from_raw(0x0EF00); 105 | assert_eq!(pgn.is_destination_specific(), true); 106 | let pgn = Pgn::from_raw(0x0F000); 107 | assert_eq!(pgn.is_destination_specific(), false); 108 | let pgn = Pgn::from_raw(0x0FEFF); 109 | assert_eq!(pgn.is_destination_specific(), false); 110 | let pgn = Pgn::from_raw(0x0FF00); 111 | assert_eq!(pgn.is_destination_specific(), false); 112 | let pgn = Pgn::from_raw(0x0FFFF); 113 | assert_eq!(pgn.is_destination_specific(), false); 114 | let pgn = Pgn::from_raw(0x10000); 115 | assert_eq!(pgn.is_destination_specific(), true); 116 | let pgn = Pgn::from_raw(0x1EE00); 117 | assert_eq!(pgn.is_destination_specific(), true); 118 | let pgn = Pgn::from_raw(0x1EF00); 119 | assert_eq!(pgn.is_destination_specific(), true); 120 | let pgn = Pgn::from_raw(0x1F000); 121 | assert_eq!(pgn.is_destination_specific(), false); 122 | let pgn = Pgn::from_raw(0x1FEFF); 123 | assert_eq!(pgn.is_destination_specific(), false); 124 | let pgn = Pgn::from_raw(0x1FF00); 125 | assert_eq!(pgn.is_destination_specific(), false); 126 | let pgn = Pgn::from_raw(0x1FFFF); 127 | assert_eq!(pgn.is_destination_specific(), false); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/driver/driver.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Raven Industries inc. 2 | use crate::driver::Frame; 3 | 4 | #[derive(Debug)] 5 | #[non_exhaustive] 6 | pub enum DriverOpenError { 7 | /// The driver failed to open with filesystem semantics 8 | IoError(std::io::Error), 9 | } 10 | 11 | impl std::fmt::Display for DriverOpenError { 12 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 13 | write!(f, "Failed to open driver: {:?}", self) 14 | } 15 | } 16 | impl std::error::Error for DriverOpenError { 17 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { 18 | match &self { 19 | DriverOpenError::IoError(e) => Some(e), 20 | } 21 | } 22 | } 23 | 24 | impl From for DriverOpenError { 25 | fn from(e: std::io::Error) -> DriverOpenError { 26 | DriverOpenError::IoError(e) 27 | } 28 | } 29 | 30 | #[derive(Debug)] 31 | #[non_exhaustive] 32 | pub enum DriverCloseError {} 33 | 34 | impl std::fmt::Display for DriverCloseError { 35 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 36 | write!(f, "{:?}", self) 37 | } 38 | } 39 | impl std::error::Error for DriverCloseError {} 40 | 41 | #[derive(Debug)] 42 | #[non_exhaustive] 43 | pub enum DriverReadError { 44 | /// There is no frame ready to be read 45 | NoFrameReady, 46 | /// The driver has been closed 47 | DriverClosed, 48 | /// The driver received an error frame 49 | ErrorFrame(), 50 | /// The driver failed to read with filesystem semantics 51 | IoError(std::io::Error), 52 | } 53 | 54 | impl std::fmt::Display for DriverReadError { 55 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 56 | write!(f, "{:?}", self) 57 | } 58 | } 59 | impl std::error::Error for DriverReadError {} 60 | 61 | impl From for DriverReadError { 62 | fn from(e: std::io::Error) -> DriverReadError { 63 | if matches!(e.kind(), std::io::ErrorKind::WouldBlock) { 64 | DriverReadError::NoFrameReady 65 | } else { 66 | DriverReadError::IoError(e) 67 | } 68 | } 69 | } 70 | 71 | #[derive(Debug)] 72 | #[non_exhaustive] 73 | pub enum DriverWriteError { 74 | /// The driver's internal buffer is full, or the driver is otherwise busy 75 | NotReady, 76 | /// The driver has been closed 77 | DriverClosed, 78 | /// Some fault with the CAN bus 79 | BusError(), 80 | /// Some fault with filesystem semantics 81 | IoError(std::io::Error), 82 | } 83 | 84 | impl std::fmt::Display for DriverWriteError { 85 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 86 | write!(f, "{:?}", self) 87 | } 88 | } 89 | impl std::error::Error for DriverWriteError {} 90 | 91 | impl From for DriverWriteError { 92 | fn from(e: std::io::Error) -> DriverWriteError { 93 | if matches!(e.kind(), std::io::ErrorKind::WouldBlock) { 94 | DriverWriteError::NotReady 95 | } else { 96 | DriverWriteError::IoError(e) 97 | } 98 | } 99 | } 100 | 101 | /// Generic interface for CAN drivers 102 | /// 103 | /// This layer is meant to abstract the hardware, and should not do its own queuing/buffering. 104 | /// 105 | /// This trait does _not_ define how to construct and configure a driver, as the details are likely 106 | /// to differ from driver to driver. 107 | pub trait Driver { 108 | /// Determine whether the driver is connected and healthy 109 | fn is_valid(&self) -> bool; 110 | 111 | /// Open the driver 112 | /// 113 | /// It is expected you must open the driver after creating it 114 | fn open(&mut self) -> Result<(), DriverOpenError>; 115 | 116 | /// Close the driver 117 | /// 118 | /// It is not necessary to close the driver before dropping it 119 | fn close(&mut self) -> Result<(), DriverCloseError>; 120 | 121 | /// Read a [Frame] from the driver, if possible 122 | /// 123 | /// This is a non-blocking read. If there is no frame ready to read, this function will return 124 | /// [DriverReadError::NoFrameReady]. 125 | /// 126 | /// An out-parameter is used, so that the user can choose whether to construct a new frame for 127 | /// each call, or to re-use memory. 128 | fn read_nonblocking(&mut self, frame: &mut Frame) -> Result<(), DriverReadError>; 129 | 130 | /// Write a [Frame] to the driver, if possible 131 | /// 132 | /// This is a non-blocking write. If the frame cannot be written because the driver's 133 | /// queue/buffer is full (for drivers like `socketcan` that do internal buffering), or if 134 | /// it's otherwise busy, this function will return [DriverWriteError::NotReady]. 135 | /// 136 | /// For drivers that defer to some other implementation (Peak, Socketcan), it's likely that the 137 | /// given `frame` is copied before being written. 138 | fn write_nonblocking(&mut self, frame: &Frame) -> Result<(), DriverWriteError>; 139 | } 140 | -------------------------------------------------------------------------------- /src/driver/socketcan.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Raven Industries inc. 2 | use std::time::Instant; 3 | 4 | use socketcan::frame::{CanDataFrame, CanFrame, Frame}; 5 | use socketcan::{CanSocket, EmbeddedFrame, ExtendedId, Id, Socket, StandardId}; 6 | 7 | use crate::driver::{ 8 | CanId, Channel, Driver, DriverCloseError, DriverOpenError, DriverReadError, DriverWriteError, 9 | Frame as InternalFrame, Type, 10 | }; 11 | 12 | impl From for DriverReadError { 13 | fn from(e: socketcan::Error) -> DriverReadError { 14 | match e { 15 | socketcan::Error::Can(_) => DriverReadError::ErrorFrame(), 16 | socketcan::Error::Io(e) => DriverReadError::IoError(e), 17 | } 18 | } 19 | } 20 | 21 | impl From for DriverWriteError { 22 | fn from(e: socketcan::Error) -> DriverWriteError { 23 | match e { 24 | socketcan::Error::Can(_) => DriverWriteError::BusError(), 25 | socketcan::Error::Io(e) => DriverWriteError::IoError(e), 26 | } 27 | } 28 | } 29 | 30 | impl From<&InternalFrame> for socketcan::frame::CanDataFrame { 31 | fn from(f: &InternalFrame) -> socketcan::frame::CanDataFrame { 32 | let id = match f.id.type_() { 33 | Type::Standard => Id::Standard(unsafe { StandardId::new_unchecked(f.id.raw() as u16) }), 34 | Type::Extended => Id::Extended(unsafe { ExtendedId::new_unchecked(f.id.raw()) }), 35 | }; 36 | CanDataFrame::new(id, &f.data[..f.data_length.min(8) as usize]) 37 | // guaranteed to not crash, because `f.data` is an [u8; 8] 38 | .expect("Can frame had too much data") 39 | } 40 | } 41 | 42 | enum SocketcanIface { 43 | Name(String), 44 | Index(u32), 45 | } 46 | 47 | /// A Linux socketcan [Driver] 48 | /// 49 | /// Enabled with the optional `socketcan` feature 50 | pub struct SocketcanDriver { 51 | iface: SocketcanIface, 52 | sock: Option, 53 | opened_timestamp: Instant, 54 | } 55 | 56 | impl SocketcanDriver { 57 | /// Create a socketcan driver with the given interface name. E.g., `can0`, or `vcan1` 58 | pub fn new_by_name(if_name: &str) -> Self { 59 | Self { 60 | iface: SocketcanIface::Name(if_name.to_string()), 61 | sock: None, 62 | opened_timestamp: Instant::now(), 63 | } 64 | } 65 | 66 | /// Create a socketcan driver with the given interface index 67 | pub fn new_by_index(if_index: u32) -> Self { 68 | Self { 69 | iface: SocketcanIface::Index(if_index), 70 | sock: None, 71 | opened_timestamp: Instant::now(), 72 | } 73 | } 74 | 75 | fn to_frame(&self, f: CanFrame) -> InternalFrame { 76 | match f { 77 | CanFrame::Remote(_r) => todo!("Remote frames unsupported yet"), 78 | CanFrame::Error(_e) => todo!("Error frames unsupported yet"), 79 | CanFrame::Data(f) => { 80 | let timestamp = self.opened_timestamp.elapsed(); 81 | let raw_id = f.raw_id(); 82 | let extended = f.is_extended(); 83 | let frame_type = if extended { 84 | Type::Extended 85 | } else { 86 | Type::Standard 87 | }; 88 | 89 | let id = CanId::new(raw_id, frame_type); 90 | // TODO: The Driver trait doesn't know anything about Channels yet. 91 | // 92 | // The channel exists so that we can tie Frames and CANMessages back to the network 93 | // manager they originated from. This channel value should be passed to the Driver 94 | // when it's created (or opened?) 95 | let channel = Channel::default(); 96 | let mut data = [0; 8]; 97 | let data_length = f.dlc().min(8); 98 | data[..data_length].copy_from_slice(f.data()); 99 | let data_length = data_length as u8; 100 | 101 | InternalFrame { 102 | timestamp, 103 | id, 104 | channel, 105 | data, 106 | data_length, 107 | extended, 108 | } 109 | } 110 | } 111 | } 112 | } 113 | 114 | impl Driver for SocketcanDriver { 115 | fn is_valid(&self) -> bool { 116 | self.sock.is_some() 117 | } 118 | fn open(&mut self) -> Result<(), DriverOpenError> { 119 | match &self.iface { 120 | SocketcanIface::Name(s) => self.sock = Some(CanSocket::open(s)?), 121 | SocketcanIface::Index(i) => self.sock = Some(CanSocket::open_iface(*i)?), 122 | } 123 | self.opened_timestamp = Instant::now(); 124 | 125 | // NOTE: unwrap() is safe, because we return a DriverOpenError if we fail to create it. 126 | self.sock.as_ref().unwrap().set_nonblocking(true)?; 127 | Ok(()) 128 | } 129 | fn close(&mut self) -> Result<(), DriverCloseError> { 130 | self.sock = None; 131 | Ok(()) 132 | } 133 | 134 | /// Read a frame from the driver, if possible 135 | /// 136 | /// The timestamp on the frame is the duration since [`open`](Self::open) was last called. 137 | fn read_nonblocking(&mut self, frame: &mut InternalFrame) -> Result<(), DriverReadError> { 138 | let Some(sock) = self.sock.as_mut() else { 139 | return Err(DriverReadError::DriverClosed); 140 | }; 141 | let socketcan_frame = sock.read_frame()?; 142 | *frame = self.to_frame(socketcan_frame); 143 | Ok(()) 144 | } 145 | fn write_nonblocking(&mut self, frame: &InternalFrame) -> Result<(), DriverWriteError> { 146 | let Some(sock) = self.sock.as_mut() else { 147 | return Err(DriverWriteError::DriverClosed); 148 | }; 149 | let socketcan_frame: socketcan::frame::CanDataFrame = frame.into(); 150 | sock.write_frame(&socketcan_frame)?; 151 | Ok(()) 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/object_pool/object_type.rs: -------------------------------------------------------------------------------- 1 | use crate::object_pool::ParseError; 2 | use crate::object_pool::ParseError::UnknownObjectType; 3 | 4 | #[derive(Debug, PartialEq, Eq, Copy, Clone)] 5 | pub enum ObjectType { 6 | WorkingSet = 0, 7 | DataMask = 1, 8 | AlarmMask = 2, 9 | Container = 3, 10 | SoftKeyMask = 4, 11 | Key = 5, 12 | Button = 6, 13 | InputBoolean = 7, 14 | InputString = 8, 15 | InputNumber = 9, 16 | InputList = 10, 17 | OutputString = 11, 18 | OutputNumber = 12, 19 | OutputLine = 13, 20 | OutputRectangle = 14, 21 | OutputEllipse = 15, 22 | OutputPolygon = 16, 23 | OutputMeter = 17, 24 | OutputLinearBarGraph = 18, 25 | OutputArchedBarGraph = 19, 26 | PictureGraphic = 20, 27 | NumberVariable = 21, 28 | StringVariable = 22, 29 | FontAttributes = 23, 30 | LineAttributes = 24, 31 | FillAttributes = 25, 32 | InputAttributes = 26, 33 | ObjectPointer = 27, 34 | Macro = 28, 35 | AuxiliaryFunctionType1 = 29, 36 | AuxiliaryInputType1 = 30, 37 | AuxiliaryFunctionType2 = 31, 38 | AuxiliaryInputType2 = 32, 39 | AuxiliaryControlDesignatorType2 = 33, 40 | WindowMask = 34, 41 | KeyGroup = 35, 42 | GraphicsContext = 36, 43 | OutputList = 37, 44 | ExtendedInputAttributes = 38, 45 | ColourMap = 39, 46 | ObjectLabelReferenceList = 40, 47 | ExternalObjectDefinition = 41, 48 | ExternalReferenceName = 42, 49 | ExternalObjectPointer = 43, 50 | Animation = 44, 51 | ColourPalette = 45, 52 | GraphicData = 46, 53 | WorkingSetSpecialControls = 47, 54 | ScaledGraphic = 48, 55 | } 56 | 57 | impl TryFrom for ObjectType { 58 | type Error = ParseError; 59 | 60 | fn try_from(val: u8) -> Result { 61 | match val { 62 | 0 => Ok(Self::WorkingSet), 63 | 1 => Ok(Self::DataMask), 64 | 2 => Ok(Self::AlarmMask), 65 | 3 => Ok(Self::Container), 66 | 4 => Ok(Self::SoftKeyMask), 67 | 5 => Ok(Self::Key), 68 | 6 => Ok(Self::Button), 69 | 7 => Ok(Self::InputBoolean), 70 | 8 => Ok(Self::InputString), 71 | 9 => Ok(Self::InputNumber), 72 | 10 => Ok(Self::InputList), 73 | 11 => Ok(Self::OutputString), 74 | 12 => Ok(Self::OutputNumber), 75 | 13 => Ok(Self::OutputLine), 76 | 14 => Ok(Self::OutputRectangle), 77 | 15 => Ok(Self::OutputEllipse), 78 | 16 => Ok(Self::OutputPolygon), 79 | 17 => Ok(Self::OutputMeter), 80 | 18 => Ok(Self::OutputLinearBarGraph), 81 | 19 => Ok(Self::OutputArchedBarGraph), 82 | 20 => Ok(Self::PictureGraphic), 83 | 21 => Ok(Self::NumberVariable), 84 | 22 => Ok(Self::StringVariable), 85 | 23 => Ok(Self::FontAttributes), 86 | 24 => Ok(Self::LineAttributes), 87 | 25 => Ok(Self::FillAttributes), 88 | 26 => Ok(Self::InputAttributes), 89 | 27 => Ok(Self::ObjectPointer), 90 | 28 => Ok(Self::Macro), 91 | 29 => Ok(Self::AuxiliaryFunctionType1), 92 | 30 => Ok(Self::AuxiliaryInputType1), 93 | 31 => Ok(Self::AuxiliaryFunctionType2), 94 | 32 => Ok(Self::AuxiliaryInputType2), 95 | 33 => Ok(Self::AuxiliaryControlDesignatorType2), 96 | 34 => Ok(Self::WindowMask), 97 | 35 => Ok(Self::KeyGroup), 98 | 36 => Ok(Self::GraphicsContext), 99 | 37 => Ok(Self::OutputList), 100 | 38 => Ok(Self::ExtendedInputAttributes), 101 | 39 => Ok(Self::ColourMap), 102 | 40 => Ok(Self::ObjectLabelReferenceList), 103 | 41 => Ok(Self::ExternalObjectDefinition), 104 | 42 => Ok(Self::ExternalReferenceName), 105 | 43 => Ok(Self::ExternalObjectPointer), 106 | 44 => Ok(Self::Animation), 107 | 45 => Ok(Self::ColourPalette), 108 | 46 => Ok(Self::GraphicData), 109 | 47 => Ok(Self::WorkingSetSpecialControls), 110 | 48 => Ok(Self::ScaledGraphic), 111 | _ => Err(UnknownObjectType), 112 | } 113 | } 114 | } 115 | 116 | impl From for u8 { 117 | fn from(val: ObjectType) -> Self { 118 | match val { 119 | ObjectType::WorkingSet => 0, 120 | ObjectType::DataMask => 1, 121 | ObjectType::AlarmMask => 2, 122 | ObjectType::Container => 3, 123 | ObjectType::SoftKeyMask => 4, 124 | ObjectType::Key => 5, 125 | ObjectType::Button => 6, 126 | ObjectType::InputBoolean => 7, 127 | ObjectType::InputString => 8, 128 | ObjectType::InputNumber => 9, 129 | ObjectType::InputList => 10, 130 | ObjectType::OutputString => 11, 131 | ObjectType::OutputNumber => 12, 132 | ObjectType::OutputLine => 13, 133 | ObjectType::OutputRectangle => 14, 134 | ObjectType::OutputEllipse => 15, 135 | ObjectType::OutputPolygon => 16, 136 | ObjectType::OutputMeter => 17, 137 | ObjectType::OutputLinearBarGraph => 18, 138 | ObjectType::OutputArchedBarGraph => 19, 139 | ObjectType::PictureGraphic => 20, 140 | ObjectType::NumberVariable => 21, 141 | ObjectType::StringVariable => 22, 142 | ObjectType::FontAttributes => 23, 143 | ObjectType::LineAttributes => 24, 144 | ObjectType::FillAttributes => 25, 145 | ObjectType::InputAttributes => 26, 146 | ObjectType::ObjectPointer => 27, 147 | ObjectType::Macro => 28, 148 | ObjectType::AuxiliaryFunctionType1 => 29, 149 | ObjectType::AuxiliaryInputType1 => 30, 150 | ObjectType::AuxiliaryFunctionType2 => 31, 151 | ObjectType::AuxiliaryInputType2 => 32, 152 | ObjectType::AuxiliaryControlDesignatorType2 => 33, 153 | ObjectType::WindowMask => 34, 154 | ObjectType::KeyGroup => 35, 155 | ObjectType::GraphicsContext => 36, 156 | ObjectType::OutputList => 37, 157 | ObjectType::ExtendedInputAttributes => 38, 158 | ObjectType::ColourMap => 39, 159 | ObjectType::ObjectLabelReferenceList => 40, 160 | ObjectType::ExternalObjectDefinition => 41, 161 | ObjectType::ExternalReferenceName => 42, 162 | ObjectType::ExternalObjectPointer => 43, 163 | ObjectType::Animation => 44, 164 | ObjectType::ColourPalette => 45, 165 | ObjectType::GraphicData => 46, 166 | ObjectType::WorkingSetSpecialControls => 47, 167 | ObjectType::ScaledGraphic => 48, 168 | } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/network_management/control_function.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Raven Industries inc. 2 | use crate::driver::Address; 3 | use crate::network_management::name::NAME; 4 | use rand::Rng; 5 | use std::cell::RefCell; 6 | use std::rc::Rc; 7 | use std::time::{Duration, Instant}; 8 | 9 | use super::network_manager::{MessageQueuePriority, NetworkManager}; 10 | 11 | #[derive(PartialEq, Eq, Clone, Copy)] 12 | pub enum AddressClaimingState { 13 | /// Address claiming is uninitialized 14 | None, 15 | /// State machine is waiting for the random delay time 16 | WaitForClaim, 17 | /// State machine is sending the request for address claim 18 | SendRequestForClaim, 19 | /// State machine is waiting for the address claim contention period 20 | WaitForRequestContentionPeriod, 21 | /// State machine is claiming the preferred address 22 | SendPreferredAddressClaim, 23 | /// State machine is contending the preferred address 24 | ContendForPreferredAddress, 25 | /// State machine is claiming an address 26 | SendArbitraryAddressClaim, 27 | /// An ECU requested address claim, inform the bus of our current address 28 | SendReclaimAddressOnRequest, 29 | /// State machine could not claim an address 30 | UnableToClaim, 31 | /// Address claiming is complete and we have an address 32 | AddressClaimingComplete, 33 | } 34 | 35 | pub struct AddressClaimingData { 36 | state: AddressClaimingState, 37 | name: NAME, 38 | timestamp: Option, 39 | preferred_address: Address, 40 | random_delay: u8, 41 | enabled: bool, 42 | } 43 | 44 | pub enum ControlFunction { 45 | Internal { 46 | address_claim_data: AddressClaimingData, 47 | }, 48 | External { 49 | name: NAME, 50 | }, 51 | } 52 | 53 | impl ControlFunction { 54 | pub fn new_internal_control_function( 55 | name: NAME, 56 | preferred_address: Address, 57 | enabled: bool, 58 | network: &mut NetworkManager, 59 | ) -> Rc> { 60 | let cf = Rc::new(RefCell::new(ControlFunction::Internal { 61 | address_claim_data: AddressClaimingData::new(name, preferred_address, enabled), 62 | })); 63 | network.on_new_internal_control_function(cf.clone()); 64 | cf 65 | } 66 | 67 | pub fn get_name(&self) -> NAME { 68 | match self { 69 | ControlFunction::Internal { address_claim_data } => address_claim_data.get_name(), 70 | ControlFunction::External { name } => *name, 71 | } 72 | } 73 | } 74 | 75 | impl AddressClaimingState { 76 | pub(super) fn new() -> Self { 77 | Self::None 78 | } 79 | 80 | pub(super) fn update_state_none(_claim_to_process: &AddressClaimingData) -> Self { 81 | AddressClaimingState::WaitForClaim 82 | } 83 | 84 | pub(super) fn update_state_wait_for_claim(claim_to_process: &AddressClaimingData) -> Self { 85 | if Instant::now().duration_since(claim_to_process.get_timestamp().unwrap()) 86 | > Duration::from_millis(claim_to_process.get_random_delay() as u64) 87 | { 88 | AddressClaimingState::SendRequestForClaim 89 | } else { 90 | AddressClaimingState::WaitForClaim 91 | } 92 | } 93 | 94 | pub(super) fn update_state_send_request_for_claim(network: &mut NetworkManager) -> Self { 95 | network.enqueue_can_message( 96 | NetworkManager::construct_request_for_address_claim(), 97 | MessageQueuePriority::High, 98 | ); 99 | AddressClaimingState::WaitForRequestContentionPeriod 100 | } 101 | 102 | pub(super) fn update_state_wait_for_request_contention( 103 | claim_to_process: &AddressClaimingData, 104 | network: &mut NetworkManager, 105 | ) -> Self { 106 | let contention_time_ms: u64 = 250; 107 | 108 | if Instant::now().duration_since(claim_to_process.get_timestamp().unwrap()) 109 | > Duration::from_millis(claim_to_process.get_random_delay() as u64 + contention_time_ms) 110 | { 111 | let is_device_at_our_address = 112 | network.get_control_function_by_address(claim_to_process.get_preferred_address()); 113 | let is_valid_device: bool = is_device_at_our_address.is_some(); 114 | 115 | if is_valid_device { 116 | let preferred_address_name: u64 = 117 | match *is_device_at_our_address.as_ref().unwrap().clone().borrow() { 118 | ControlFunction::External { name } => name.into(), 119 | ControlFunction::Internal { 120 | address_claim_data: _, 121 | } => claim_to_process.get_name().into(), 122 | }; 123 | 124 | if (!claim_to_process.get_name().get_self_configurable_address() 125 | && preferred_address_name > claim_to_process.get_name().into()) 126 | || >::into(NAME::default()) == preferred_address_name 127 | { 128 | // Either our preferred address is free, this is the best case, or: 129 | // Our address is not free, but we cannot be at an arbitrary address, and the address can be stolen by us 130 | AddressClaimingState::SendPreferredAddressClaim 131 | } else if !claim_to_process.get_name().get_self_configurable_address() { 132 | // We cannot claim because we cannot tolerate an arbitrary address, and the CF at that spot wins due to its lower ISONAME 133 | AddressClaimingState::UnableToClaim 134 | } else { 135 | // We will move to another address if whoever is in our spot has a lower NAME 136 | if preferred_address_name < claim_to_process.get_name().into() { 137 | // We must scan the address space and move to a free address 138 | AddressClaimingState::SendArbitraryAddressClaim 139 | } else { 140 | // Our address claim wins because it's lower than the device that's in our preferred spot 141 | AddressClaimingState::SendPreferredAddressClaim 142 | } 143 | } 144 | } else { 145 | AddressClaimingState::SendPreferredAddressClaim 146 | } 147 | } else { 148 | AddressClaimingState::WaitForRequestContentionPeriod 149 | } 150 | } 151 | 152 | pub(super) fn update_state_send_preferred_address_claim( 153 | claim_to_process: &AddressClaimingData, 154 | network: &mut NetworkManager, 155 | ) -> Self { 156 | network.enqueue_can_message( 157 | NetworkManager::construct_address_claim( 158 | claim_to_process.get_preferred_address(), 159 | claim_to_process.get_name(), 160 | ), 161 | MessageQueuePriority::High, 162 | ); 163 | AddressClaimingState::AddressClaimingComplete 164 | } 165 | 166 | pub(super) fn update_state_send_arbitrary_address_claim( 167 | claim_to_process: &AddressClaimingData, 168 | network: &mut NetworkManager, 169 | ) -> Self { 170 | let next_address = network.get_next_free_arbitrary_address(); 171 | 172 | if Address::NULL != next_address { 173 | // Found an address we can use 174 | network.enqueue_can_message( 175 | NetworkManager::construct_address_claim(next_address, claim_to_process.get_name()), 176 | MessageQueuePriority::High, 177 | ); 178 | return AddressClaimingState::AddressClaimingComplete; 179 | } 180 | AddressClaimingState::UnableToClaim 181 | } 182 | } 183 | 184 | impl Default for AddressClaimingState { 185 | fn default() -> Self { 186 | Self::new() 187 | } 188 | } 189 | 190 | impl AddressClaimingData { 191 | pub fn new(name: NAME, preferred_address: Address, enabled: bool) -> AddressClaimingData { 192 | AddressClaimingData { 193 | state: AddressClaimingState::None, 194 | name, 195 | timestamp: None, 196 | preferred_address, 197 | random_delay: AddressClaimingData::generate_random_delay(), 198 | enabled, 199 | } 200 | } 201 | 202 | pub fn get_enabled(&self) -> bool { 203 | self.enabled 204 | } 205 | 206 | pub fn set_enabled(&mut self, enable: bool) { 207 | self.enabled = enable; 208 | 209 | if !enable { 210 | self.timestamp = None; 211 | self.state = AddressClaimingState::None; 212 | } 213 | } 214 | 215 | pub fn get_preferred_address(&self) -> Address { 216 | self.preferred_address 217 | } 218 | 219 | pub(super) fn set_preferred_address(&mut self, new_address: Address) { 220 | self.preferred_address = new_address; 221 | } 222 | 223 | pub fn get_state(&self) -> AddressClaimingState { 224 | self.state 225 | } 226 | 227 | pub(super) fn set_state(&mut self, new_state: AddressClaimingState) { 228 | self.state = new_state; 229 | } 230 | 231 | pub fn get_name(&self) -> NAME { 232 | self.name 233 | } 234 | 235 | pub fn set_name(&mut self, new_name: NAME) { 236 | if self.name != new_name { 237 | self.state = AddressClaimingState::None; // Name changed, state no longer valid 238 | } 239 | self.name = new_name; 240 | } 241 | 242 | pub fn get_timestamp(&self) -> Option { 243 | self.timestamp 244 | } 245 | 246 | pub(super) fn set_timestamp(&mut self, new_timestamp: Option) { 247 | self.timestamp = new_timestamp; 248 | } 249 | 250 | pub(super) fn get_random_delay(&self) -> u8 { 251 | self.random_delay 252 | } 253 | 254 | pub(super) fn generate_random_delay() -> u8 { 255 | let mut rng: rand::rngs::ThreadRng = rand::thread_rng(); 256 | (rng.gen_range(0..255) as f32 * 0.6_f32) as u8 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /src/driver/can_id.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Raven Industries inc. 2 | use crate::driver::{Address, Pgn}; 3 | 4 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 5 | pub enum Priority { 6 | /// You may also use [`Priority::Highest`] as an alias 7 | Zero = 0x0, 8 | One = 0x1, 9 | Two = 0x2, 10 | Three = 0x3, 11 | Four = 0x4, 12 | Five = 0x5, 13 | /// You may also use [`Priority::Default`] as an alias 14 | Six = 0x6, 15 | /// You may also use [`Priority::Lowest`] as an alias 16 | Seven = 0x7, 17 | } 18 | 19 | #[allow(non_upper_case_globals)] 20 | impl Priority { 21 | pub const Highest: Priority = Priority::Zero; 22 | pub const Default: Priority = Priority::Six; 23 | pub const Lowest: Priority = Priority::Seven; 24 | } 25 | 26 | impl From for Priority { 27 | fn from(value: u8) -> Priority { 28 | match value { 29 | 0x0 => Priority::Zero, 30 | 0x1 => Priority::One, 31 | 0x2 => Priority::Two, 32 | 0x3 => Priority::Three, 33 | 0x4 => Priority::Four, 34 | 0x5 => Priority::Five, 35 | 0x6 => Priority::Six, 36 | 0x7 => Priority::Seven, 37 | _ => unreachable!( 38 | "Internal error converting a value larger than 3 bits to a CAN ID priority" 39 | ), 40 | } 41 | } 42 | } 43 | 44 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 45 | pub enum Type { 46 | /// 11-bit CAN ID 47 | Standard = 0x0, 48 | /// 29-bit CAN ID 49 | Extended = 0x1, 50 | } 51 | 52 | #[derive(Debug, Clone)] 53 | pub struct EncodingError { 54 | pub priority: Priority, 55 | pub parameter_group_number: Pgn, 56 | pub source_address: Address, 57 | pub destination_address: Address, 58 | } 59 | 60 | #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] 61 | #[repr(transparent)] 62 | pub struct CanId(u32); 63 | 64 | // Linux uses the top three unused bits to indicate whether the frame is standard/extended, remote, 65 | // or an error frame. We do the same, because it's convenient. 66 | const CAN_EFF_FLAG: u32 = 0x80000000; 67 | // const CAN_RTR_FLAG: u32 = 0x40000000; 68 | // const CAN_ERR_FLAG: u32 = 0x20000000; 69 | 70 | const CAN_EFF_MASK: u32 = 0x1FFFFFFF; 71 | const CAN_SFF_MASK: u32 = 0x000007FF; 72 | 73 | impl CanId { 74 | pub fn new(raw: u32, type_: Type) -> Self { 75 | let raw = match type_ { 76 | Type::Extended => (raw & CAN_EFF_MASK) | CAN_EFF_FLAG, 77 | Type::Standard => raw & CAN_SFF_MASK, 78 | }; 79 | Self(raw) 80 | } 81 | 82 | /// Encodes a new extended ID using the discrete parts of an identifier 83 | pub fn try_encode( 84 | parameter_group_number: Pgn, 85 | source_address: Address, 86 | destination_address: Address, 87 | priority: Priority, 88 | ) -> Result { 89 | if destination_address != Address::GLOBAL && parameter_group_number.is_broadcast() { 90 | return Err(EncodingError { 91 | priority, 92 | parameter_group_number, 93 | source_address, 94 | destination_address, 95 | }); 96 | } 97 | Ok(unsafe { 98 | CanId::encode_unchecked( 99 | parameter_group_number, 100 | source_address, 101 | destination_address, 102 | priority, 103 | ) 104 | }) 105 | } 106 | 107 | /// Encodes a new extended ID using the discrete parts of an identifier but won't validate 108 | /// your combination of PGN and destination address. 109 | /// 110 | /// # Safety 111 | /// Calling this without validating your PGN and destination address combination may result in your PGN field 112 | /// getting trashed. Specifically, the risk is when you are using a broadcast PGN but supply a non-0xFF 113 | /// destination address. 114 | pub unsafe fn encode_unchecked( 115 | parameter_group_number: Pgn, 116 | source_address: Address, 117 | destination_address: Address, 118 | priority: Priority, 119 | ) -> CanId { 120 | let mut raw_id: u32 = 0; 121 | 122 | raw_id |= (priority as u32 & 0x07) << 26; 123 | raw_id |= source_address.0 as u32; 124 | 125 | if Address::GLOBAL == destination_address { 126 | if (parameter_group_number.raw() & 0xF000) >= 0xF000 { 127 | raw_id |= (parameter_group_number.raw() & 0x3FFFF) << 8; 128 | } else { 129 | raw_id |= (destination_address.0 as u32) << 8; 130 | raw_id |= (parameter_group_number.raw() & 0x3FF00) << 8; 131 | } 132 | } else if (parameter_group_number.raw() & 0xF000) < 0xF000 { 133 | raw_id |= (destination_address.0 as u32) << 8; 134 | raw_id |= (parameter_group_number.raw() & 0x3FF00) << 8; 135 | } 136 | CanId::new(raw_id & CAN_EFF_MASK, Type::Extended) 137 | } 138 | 139 | /// Get the raw value of the CAN ID 140 | #[inline] 141 | pub fn raw(&self) -> u32 { 142 | match self.type_() { 143 | Type::Extended => self.0 & CAN_EFF_MASK, 144 | Type::Standard => self.0 & CAN_SFF_MASK, 145 | } 146 | } 147 | 148 | /// Get the type of the ID (standard or extended) 149 | #[inline] 150 | pub fn type_(&self) -> Type { 151 | if self.0 & CAN_EFF_FLAG != 0 { 152 | Type::Extended 153 | } else { 154 | Type::Standard 155 | } 156 | } 157 | 158 | /// Get the priority of the ID 159 | #[inline] 160 | pub fn priority(&self) -> Priority { 161 | match self.type_() { 162 | Type::Standard => Priority::Highest, 163 | Type::Extended => { 164 | let raw = ((self.raw() & 0x1C000000) >> 26) as u8; 165 | raw.into() 166 | } 167 | } 168 | } 169 | 170 | /// Get the source address of the ID 171 | #[inline] 172 | pub fn source_address(&self) -> Address { 173 | match self.type_() { 174 | Type::Standard => Address::GLOBAL, 175 | Type::Extended => Address((self.raw() & 0xFF) as u8), 176 | } 177 | } 178 | 179 | /// Get the ID's PGN 180 | /// 181 | /// In the case the the ID is a standard 11-bit ID, a NULL PGN will be returned. 182 | #[inline] 183 | pub fn pgn(&self) -> Pgn { 184 | match self.type_() { 185 | Type::Standard => Pgn::NULL, 186 | Type::Extended => Pgn::from_id(self.raw()), 187 | } 188 | } 189 | 190 | /// Get the destination address for this CAN ID, if it's a destination-specific PGN 191 | #[inline] 192 | pub fn destination_address(&self) -> Address { 193 | let pgn = self.pgn(); 194 | if pgn == Pgn::NULL || pgn.is_broadcast() { 195 | return Address::GLOBAL; 196 | } 197 | 198 | let raw_pdu_s = ((self.raw() & 0xFF00) >> 8) as u8; 199 | Address(raw_pdu_s) 200 | } 201 | } 202 | 203 | #[cfg(test)] 204 | mod tests { 205 | use super::*; 206 | 207 | #[test] 208 | fn test_priority() { 209 | let can_id = CanId::new(0x18EF1CF5, Type::Extended); 210 | assert_eq!(can_id.priority(), Priority::Default); 211 | } 212 | 213 | #[test] 214 | fn test_source_address() { 215 | let can_id = CanId::new(0x0705, Type::Standard); 216 | assert_eq!(can_id.type_(), Type::Standard); 217 | // TODO: Is this right? Do 11-bit IDs always have a global address? 218 | assert_eq!(can_id.source_address(), Address::GLOBAL); 219 | 220 | let can_id = CanId::new(0x18EF1CF5, Type::Extended); 221 | assert_eq!(can_id.source_address(), Address(0xF5)); 222 | } 223 | 224 | #[test] 225 | fn test_destination_address() { 226 | let can_id = CanId::new(0x0705, Type::Standard); 227 | assert_eq!(can_id.destination_address(), Address::GLOBAL); 228 | 229 | let can_id = CanId::new(0x18EEFF1C, Type::Extended); 230 | assert_eq!(can_id.destination_address(), Address::GLOBAL); 231 | 232 | let can_id = CanId::new(0x09F8031C, Type::Extended); 233 | assert_eq!(can_id.destination_address(), Address::GLOBAL); 234 | 235 | let can_id = CanId::new(0x0CAC1C13, Type::Extended); 236 | assert_eq!(can_id.destination_address(), Address(0x1C)); 237 | } 238 | 239 | #[test] 240 | fn test_pgn() { 241 | let can_id = CanId::new(0x07FF, Type::Standard); 242 | assert_eq!(can_id.pgn(), Pgn::NULL); 243 | 244 | let can_id = CanId::new(0x0CAC1C13, Type::Extended); 245 | assert_eq!(can_id.pgn(), Pgn::from_raw(0x0AC00)); 246 | 247 | let can_id = CanId::new(0x18FF3F13, Type::Extended); 248 | assert_eq!(can_id.pgn(), Pgn::from_raw(0x0FF3F)); 249 | 250 | let can_id = CanId::new(0x18EF1CF5, Type::Extended); 251 | assert_eq!(can_id.pgn(), Pgn::from_raw(0x0EF00)); 252 | 253 | let can_id = CanId::new(0x18EEFF1C, Type::Extended); 254 | assert_eq!(can_id.pgn(), Pgn::from_raw(0x0EE00)); 255 | } 256 | 257 | #[test] 258 | fn test_encode() { 259 | let encode_result = CanId::try_encode( 260 | Pgn::from_raw(0x00EF00), 261 | Address(0x81), 262 | Address(0xF9), 263 | Priority::Six, 264 | ); 265 | let can_id = encode_result.expect("EF00 Message was not encodable"); 266 | assert_eq!(can_id.pgn(), Pgn::from_raw(0xEF00)); 267 | assert_eq!(can_id.destination_address(), Address(0xF9)); 268 | assert_eq!(can_id.source_address(), Address(0x81)); 269 | assert_eq!(can_id.priority(), Priority::Six); 270 | 271 | let encode_result = CanId::try_encode( 272 | Pgn::from_raw(0x00FF40), 273 | Address(0x81), 274 | Address(0xFF), 275 | Priority::Six, 276 | ); 277 | let can_id = encode_result.expect("FF40 Message was not encodable"); 278 | assert_eq!(can_id.pgn(), Pgn::from_raw(0xFF40)); 279 | assert_eq!(can_id.destination_address(), Address(0xFF)); 280 | assert_eq!(can_id.source_address(), Address(0x81)); 281 | assert_eq!(can_id.priority(), Priority::Six); 282 | 283 | let encode_result = CanId::try_encode( 284 | Pgn::from_raw(0x00FF40), 285 | Address(0x81), 286 | Address(0x0F), 287 | Priority::Six, 288 | ); 289 | assert!(encode_result.is_err()); 290 | 291 | let error_contents: EncodingError = encode_result.unwrap_err(); 292 | assert_eq!(error_contents.priority, Priority::Six); 293 | assert_eq!(error_contents.source_address, Address(0x81)); 294 | assert_eq!(error_contents.destination_address, Address(0x0F)); 295 | assert_eq!(error_contents.parameter_group_number, Pgn::from_raw(0xFF40)); 296 | } 297 | } 298 | -------------------------------------------------------------------------------- /src/object_pool/object_pool.rs: -------------------------------------------------------------------------------- 1 | use crate::object_pool::colour::Colour; 2 | use crate::object_pool::object::{ 3 | AlarmMask, Button, Container, DataMask, GraphicsContext, InputBoolean, InputList, InputNumber, 4 | InputString, Key, KeyGroup, LineAttributes, Object, OutputLine, OutputList, OutputNumber, 5 | OutputString, PictureGraphic, SoftKeyMask, WindowMask, WorkingSet, 6 | }; 7 | use crate::object_pool::object_id::ObjectId; 8 | use crate::object_pool::vt_version::VtVersion; 9 | use crate::object_pool::ObjectType; 10 | use core::cell::Cell; 11 | 12 | #[derive(Debug)] 13 | pub struct ObjectPool { 14 | objects: Vec, 15 | colour_map: [u8; 256], 16 | colour_palette: [Colour; 256], 17 | _supported_vt_version: VtVersion, 18 | 19 | size_cache: Cell>, 20 | } 21 | 22 | impl ObjectPool { 23 | pub fn new() -> Self { 24 | // Setup the default colour map 25 | let mut colour_map = [0xFFu8; 256]; 26 | for i in 0..(colour_map.len() as u8) { 27 | colour_map[i as usize] = i; 28 | } 29 | 30 | ObjectPool { 31 | objects: Vec::new(), 32 | colour_map, 33 | colour_palette: Colour::COLOUR_PALETTE, 34 | _supported_vt_version: VtVersion::default(), 35 | 36 | size_cache: Cell::new(None), 37 | } 38 | } 39 | 40 | pub fn size(&self) -> usize { 41 | if self.size_cache.get().is_none() { 42 | self.size_cache.set(Some(self.as_iop().len())); 43 | } 44 | self.size_cache.get().unwrap_or_default() 45 | } 46 | 47 | /// 48 | /// Loads the binary encoded object pool from a buffer in accordance 49 | /// with ISO 11783-6 Annex B (object definitions) and returns the 50 | /// parsed [`ObjectPool`]. 51 | /// 52 | /// # Arguments 53 | /// 54 | /// * `data` - A buffer containing the binary encoded object pool 55 | /// 56 | /// # Examples 57 | /// ``` 58 | /// use std::fs::File; 59 | /// use std::io::Read; 60 | /// use std::path::Path; 61 | /// use ag_iso_stack::object_pool::ObjectPool; 62 | /// 63 | /// let example_path = Path::new("C:/project/resources/test/AgIsoStack-rs-test-pool.iop"); 64 | /// let mut pool_file = match File::open(example_path) { 65 | /// Err(why) => panic!("couldn't open {:?}: {}", example_path.to_str(), why), 66 | /// Ok(file) => file, 67 | /// }; 68 | /// 69 | /// let mut buffer = Vec::new(); 70 | /// match pool_file.read_to_end(&mut buffer) { 71 | /// Ok(size) => size, 72 | /// Err(why) => panic!("Could not read object pool file: {why}"), 73 | /// }; 74 | /// 75 | /// let object_pool = ObjectPool::from_iop(buffer); 76 | /// ``` 77 | /// 78 | pub fn from_iop(data: I) -> Self 79 | where 80 | I: IntoIterator, 81 | { 82 | let mut data = data.into_iter(); 83 | 84 | let mut op = Self::new(); 85 | 86 | while let Ok(o) = Object::read(&mut data) { 87 | op.objects.push(o); 88 | } 89 | 90 | op 91 | } 92 | 93 | pub fn as_iop(&self) -> Vec { 94 | let mut data = Vec::new(); 95 | 96 | for obj in &self.objects { 97 | data.extend(obj.write()); 98 | } 99 | 100 | data 101 | } 102 | 103 | pub fn add(&mut self, obj: Object) { 104 | self.objects.push(obj); 105 | } 106 | 107 | pub fn object_by_id(&self, id: ObjectId) -> Option<&Object> { 108 | self.objects.iter().find(|&o| o.id() == id) 109 | } 110 | 111 | pub fn objects_by_type(&self, object_type: ObjectType) -> Vec<&Object> { 112 | self.objects 113 | .iter() 114 | .filter(|&o| o.object_type() == object_type) 115 | .collect() 116 | } 117 | 118 | // Get objects by type 119 | 120 | pub fn working_set_object(&self) -> Option<&WorkingSet> { 121 | match &self.objects_by_type(ObjectType::WorkingSet).first() { 122 | Some(Object::WorkingSet(o)) => Some(o), 123 | _ => None, 124 | } 125 | } 126 | 127 | pub fn data_mask_objects(&self) -> Vec<&DataMask> { 128 | let r: Vec<&DataMask> = self 129 | .objects_by_type(ObjectType::DataMask) 130 | .iter() 131 | .filter_map(|&o| match o { 132 | Object::DataMask(o) => Some(o), 133 | _ => None, 134 | }) 135 | .collect(); 136 | r 137 | } 138 | 139 | pub fn picture_graphic_objects(&self) -> Vec<&PictureGraphic> { 140 | let r: Vec<&PictureGraphic> = self 141 | .objects_by_type(ObjectType::PictureGraphic) 142 | .iter() 143 | .filter_map(|&o| match o { 144 | Object::PictureGraphic(o) => Some(o), 145 | _ => None, 146 | }) 147 | .collect(); 148 | r 149 | } 150 | 151 | // Get typed objects by id 152 | 153 | pub fn data_mask_object_by_id(&self, id: ObjectId) -> Option<&DataMask> { 154 | match &self.object_by_id(id) { 155 | Some(Object::DataMask(o)) => Some(o), 156 | _ => None, 157 | } 158 | } 159 | 160 | pub fn alarm_mask_object_by_id(&self, id: ObjectId) -> Option<&AlarmMask> { 161 | match &self.object_by_id(id) { 162 | Some(Object::AlarmMask(o)) => Some(o), 163 | _ => None, 164 | } 165 | } 166 | 167 | pub fn soft_key_mask_object_by_id(&self, id: ObjectId) -> Option<&SoftKeyMask> { 168 | match &self.object_by_id(id) { 169 | Some(Object::SoftKeyMask(o)) => Some(o), 170 | _ => None, 171 | } 172 | } 173 | 174 | pub fn key_group_object_by_id(&self, id: ObjectId) -> Option<&KeyGroup> { 175 | match &self.object_by_id(id) { 176 | Some(Object::KeyGroup(o)) => Some(o), 177 | _ => None, 178 | } 179 | } 180 | 181 | pub fn window_mask_object_by_id(&self, id: ObjectId) -> Option<&WindowMask> { 182 | match &self.object_by_id(id) { 183 | Some(Object::WindowMask(o)) => Some(o), 184 | _ => None, 185 | } 186 | } 187 | 188 | pub fn container_object_by_id(&self, id: ObjectId) -> Option<&Container> { 189 | match &self.object_by_id(id) { 190 | Some(Object::Container(o)) => Some(o), 191 | _ => None, 192 | } 193 | } 194 | 195 | pub fn key_object_by_id(&self, id: ObjectId) -> Option<&Key> { 196 | match &self.object_by_id(id) { 197 | Some(Object::Key(o)) => Some(o), 198 | _ => None, 199 | } 200 | } 201 | 202 | pub fn button_object_by_id(&self, id: ObjectId) -> Option<&Button> { 203 | match &self.object_by_id(id) { 204 | Some(Object::Button(o)) => Some(o), 205 | _ => None, 206 | } 207 | } 208 | 209 | pub fn input_boolean_object_by_id(&self, id: ObjectId) -> Option<&InputBoolean> { 210 | match &self.object_by_id(id) { 211 | Some(Object::InputBoolean(o)) => Some(o), 212 | _ => None, 213 | } 214 | } 215 | 216 | pub fn input_string_object_by_id(&self, id: ObjectId) -> Option<&InputString> { 217 | match &self.object_by_id(id) { 218 | Some(Object::InputString(o)) => Some(o), 219 | _ => None, 220 | } 221 | } 222 | 223 | pub fn input_number_object_by_id(&self, id: ObjectId) -> Option<&InputNumber> { 224 | match &self.object_by_id(id) { 225 | Some(Object::InputNumber(o)) => Some(o), 226 | _ => None, 227 | } 228 | } 229 | 230 | pub fn input_list_object_by_id(&self, id: ObjectId) -> Option<&InputList> { 231 | match &self.object_by_id(id) { 232 | Some(Object::InputList(o)) => Some(o), 233 | _ => None, 234 | } 235 | } 236 | 237 | pub fn output_string_object_by_id(&self, id: ObjectId) -> Option<&OutputString> { 238 | match &self.object_by_id(id) { 239 | Some(Object::OutputString(o)) => Some(o), 240 | _ => None, 241 | } 242 | } 243 | 244 | pub fn output_number_object_by_id(&self, id: ObjectId) -> Option<&OutputNumber> { 245 | match &self.object_by_id(id) { 246 | Some(Object::OutputNumber(o)) => Some(o), 247 | _ => None, 248 | } 249 | } 250 | 251 | pub fn output_list_object_by_id(&self, id: ObjectId) -> Option<&OutputList> { 252 | match &self.object_by_id(id) { 253 | Some(Object::OutputList(o)) => Some(o), 254 | _ => None, 255 | } 256 | } 257 | 258 | pub fn output_line_object_by_id(&self, id: ObjectId) -> Option<&OutputLine> { 259 | match &self.object_by_id(id) { 260 | Some(Object::OutputLine(o)) => Some(o), 261 | _ => None, 262 | } 263 | } 264 | 265 | pub fn line_attributes_object_by_id(&self, id: ObjectId) -> Option<&LineAttributes> { 266 | match &self.object_by_id(id) { 267 | Some(Object::LineAttributes(o)) => Some(o), 268 | _ => None, 269 | } 270 | } 271 | 272 | pub fn graphics_context_object_by_id(&self, id: ObjectId) -> Option<&GraphicsContext> { 273 | match &self.object_by_id(id) { 274 | Some(Object::GraphicsContext(o)) => Some(o), 275 | _ => None, 276 | } 277 | } 278 | 279 | pub fn color_by_index(&self, index: u8) -> Colour { 280 | self.colour_palette[self.colour_map[index as usize] as usize] 281 | } 282 | } 283 | 284 | impl Default for ObjectPool { 285 | fn default() -> Self { 286 | Self::new() 287 | } 288 | } 289 | 290 | /* !todo: implement tests / fix tests 291 | #[cfg(test)] 292 | mod tests { 293 | use super::*; 294 | use std::fs::File; 295 | use std::io::Read; 296 | use std::path::Path; 297 | 298 | fn get_pool_path() -> Box { 299 | Box::from(Path::new(&format!( 300 | "{}/resources/test/AgIsoStack-rs-test-pool.iop", 301 | match std::env::var("CARGO_MANIFEST_DIR") { 302 | Err(_why) => 303 | panic!("could not find environment variable 'CARGO_MANIFEST_DIR': {_why}!"), 304 | Ok(path) => path, 305 | } 306 | ))) 307 | } 308 | 309 | fn get_pool_file() -> File { 310 | match File::open(get_pool_path()) { 311 | Err(why) => panic!("couldn't open {:?}: {}", get_pool_path().to_str(), why), 312 | Ok(file) => file, 313 | } 314 | } 315 | 316 | #[test] 317 | fn test_from_iop() { 318 | let mut buffer = Vec::new(); 319 | match get_pool_file().read_to_end(&mut buffer) { 320 | Ok(size) => size, 321 | Err(why) => panic!("Could not read object pool file: {why}"), 322 | }; 323 | 324 | let _object_pool = ObjectPool::from_iop(buffer); 325 | } 326 | } 327 | */ 328 | -------------------------------------------------------------------------------- /src/network_management/network_manager.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Raven Industries inc. 2 | use std::time::Instant; 3 | 4 | use super::control_function::{AddressClaimingState, ControlFunction}; 5 | use crate::driver::{Address, CanId, Pgn, Priority}; 6 | use crate::network_management::can_message::CANMessage; 7 | use crate::network_management::common_parameter_group_numbers::CommonParameterGroupNumbers; 8 | use crate::network_management::name::NAME; 9 | use std::cell::RefCell; 10 | use std::collections::VecDeque; 11 | use std::rc::Rc; 12 | 13 | #[derive(Debug, Clone, Copy)] 14 | pub(super) enum MessageQueuePriority { 15 | /// High priority messages are always sent to the driver before normal ones 16 | High, 17 | /// Normal messages are sent to the driver when no high priority messages are in the queue (todo) 18 | Normal, 19 | } 20 | 21 | #[derive(Debug, Clone, Copy)] 22 | pub enum CANTransmitState { 23 | /// Used to describe that a CAN message was accepted by the CAN stack to be sent 24 | Success, 25 | /// Used to describe that a CAN message was not accepted by the stack and will not be sent 26 | Fail, 27 | } 28 | 29 | pub struct NetworkManager { 30 | control_function_table: [Option>>; 253], 31 | inactive_control_functions: Vec>>, 32 | address_claim_state_machines: Vec>>, 33 | high_priority_can_message_tx_queue: VecDeque, 34 | normal_priority_can_message_tx_queue: VecDeque, 35 | receive_message_queue: VecDeque, 36 | } 37 | 38 | impl NetworkManager { 39 | pub fn new() -> Self { 40 | Self { 41 | control_function_table: std::array::from_fn(|_| None), 42 | inactive_control_functions: Vec::new(), 43 | address_claim_state_machines: Vec::new(), 44 | high_priority_can_message_tx_queue: VecDeque::new(), 45 | normal_priority_can_message_tx_queue: VecDeque::new(), 46 | receive_message_queue: VecDeque::new(), 47 | } 48 | } 49 | 50 | pub fn get_control_function_by_address( 51 | &self, 52 | address: Address, 53 | ) -> &Option>> { 54 | &self.control_function_table[address.0 as usize] 55 | } 56 | 57 | pub fn get_control_function_address_by_name(&self, name: NAME) -> Address { 58 | for (i, cf) in self.control_function_table.iter().enumerate() { 59 | if let Some(extant_cf) = cf { 60 | if extant_cf.borrow().get_name() == name { 61 | return Address(i as u8); 62 | } 63 | } 64 | } 65 | Address::NULL 66 | } 67 | 68 | pub(super) fn on_new_internal_control_function( 69 | &mut self, 70 | new_cf: Rc>, 71 | ) { 72 | self.inactive_control_functions.push(new_cf.clone()); 73 | self.address_claim_state_machines.push(new_cf); 74 | } 75 | 76 | pub(super) fn get_next_free_arbitrary_address(&self) -> Address { 77 | for address in 129..247 { 78 | let is_device_at_address = self.get_control_function_by_address(Address(address)); 79 | let is_valid_device: bool = is_device_at_address.is_some(); 80 | 81 | if !is_valid_device { 82 | return Address(address); 83 | } else { 84 | let device_at_our_address = is_device_at_address.as_ref().unwrap().borrow(); 85 | 86 | let preferred_address_name: u64 = match &*device_at_our_address { 87 | ControlFunction::External { name } => (*name).into(), 88 | ControlFunction::Internal { address_claim_data } => { 89 | address_claim_data.get_name().into() 90 | } 91 | }; 92 | 93 | if >::into(NAME::default()) == preferred_address_name { 94 | return Address(address); 95 | } 96 | } 97 | } 98 | Address::NULL 99 | } 100 | 101 | pub(super) fn construct_address_claim(source_address: Address, name: NAME) -> CANMessage { 102 | let address_claim = >::into(name).to_le_bytes().to_vec(); 103 | 104 | let request_id = CanId::try_encode( 105 | Pgn::from_raw(CommonParameterGroupNumbers::AddressClaim as u32), 106 | source_address, 107 | Address::BROADCAST, 108 | Priority::Default, 109 | ); 110 | CANMessage::new(address_claim, request_id.unwrap()) 111 | } 112 | 113 | pub(super) fn construct_request_for_address_claim() -> CANMessage { 114 | let pgn_to_request: u32 = CommonParameterGroupNumbers::AddressClaim as u32; 115 | let request = pgn_to_request.to_le_bytes().to_vec(); 116 | let request_id = CanId::try_encode( 117 | Pgn::from_raw(CommonParameterGroupNumbers::ParameterGroupNumberRequest as u32), 118 | Address::NULL, 119 | Address::BROADCAST, 120 | Priority::Three, 121 | ); 122 | CANMessage::new(request, request_id.unwrap()) 123 | } 124 | 125 | pub(super) fn enqueue_can_message( 126 | &mut self, 127 | message: CANMessage, 128 | queue_priority: MessageQueuePriority, 129 | ) { 130 | // Todo, max queue depth? 131 | match queue_priority { 132 | MessageQueuePriority::High => { 133 | self.high_priority_can_message_tx_queue.push_back(message) 134 | } 135 | MessageQueuePriority::Normal => { 136 | self.normal_priority_can_message_tx_queue.push_back(message) 137 | } 138 | } 139 | } 140 | 141 | pub fn send_can_message( 142 | &mut self, 143 | parameter_group_number: Pgn, 144 | data: &[u8], 145 | source: Rc>, 146 | destination: Rc>, 147 | priority: Priority, 148 | ) -> CANTransmitState { 149 | if !data.is_empty() { 150 | // Todo, handle lengths greater than 8 151 | 152 | if data.len() <= 8 { 153 | let source = source.borrow(); 154 | let destination = destination.borrow(); 155 | let message_id = CanId::try_encode( 156 | parameter_group_number, 157 | self.get_control_function_address_by_name(source.get_name()), 158 | self.get_control_function_address_by_name(destination.get_name()), 159 | priority, 160 | ) 161 | .unwrap_or(CanId::default()); 162 | 163 | if message_id.raw() != CanId::default().raw() { 164 | self.enqueue_can_message( 165 | CANMessage::new(data.to_vec(), message_id), 166 | MessageQueuePriority::Normal, 167 | ); 168 | return CANTransmitState::Success; 169 | } 170 | } 171 | } 172 | CANTransmitState::Fail 173 | } 174 | 175 | fn update_address_claiming(&mut self) { 176 | let mut state_machines = std::mem::take(&mut self.address_claim_state_machines); 177 | for address_claimer in &mut state_machines { 178 | let mut address_claimer = address_claimer.borrow_mut(); 179 | match *address_claimer { 180 | ControlFunction::Internal { 181 | ref mut address_claim_data, 182 | } => { 183 | if address_claim_data.get_enabled() { 184 | match address_claim_data.get_state() { 185 | AddressClaimingState::None => { 186 | address_claim_data.set_state( 187 | AddressClaimingState::update_state_none(address_claim_data), 188 | ); 189 | } 190 | AddressClaimingState::WaitForClaim => { 191 | if address_claim_data.get_timestamp().is_none() { 192 | address_claim_data.set_timestamp(Some(Instant::now())) 193 | } 194 | 195 | address_claim_data.set_state( 196 | AddressClaimingState::update_state_wait_for_claim( 197 | address_claim_data, 198 | ), 199 | ); 200 | } 201 | AddressClaimingState::SendRequestForClaim => { 202 | address_claim_data.set_state( 203 | AddressClaimingState::update_state_send_request_for_claim(self), 204 | ); 205 | } 206 | AddressClaimingState::WaitForRequestContentionPeriod => { 207 | address_claim_data.set_state( 208 | AddressClaimingState::update_state_wait_for_request_contention( 209 | address_claim_data, 210 | self, 211 | ), 212 | ); 213 | } 214 | AddressClaimingState::SendPreferredAddressClaim 215 | | AddressClaimingState::SendReclaimAddressOnRequest 216 | | AddressClaimingState::ContendForPreferredAddress => { 217 | address_claim_data.set_state( 218 | AddressClaimingState::update_state_send_preferred_address_claim( 219 | address_claim_data, 220 | self, 221 | ), 222 | ); 223 | } 224 | AddressClaimingState::SendArbitraryAddressClaim => { 225 | address_claim_data.set_state( 226 | AddressClaimingState::update_state_send_arbitrary_address_claim( 227 | address_claim_data, 228 | self, 229 | ), 230 | ); 231 | address_claim_data 232 | .set_preferred_address(self.get_next_free_arbitrary_address()); 233 | } 234 | AddressClaimingState::AddressClaimingComplete 235 | | AddressClaimingState::UnableToClaim => { 236 | // Nothing to do 237 | } 238 | } 239 | } 240 | } 241 | _ => panic!("Only Internal CFs can perform address claiming"), 242 | } 243 | } 244 | std::mem::swap(&mut state_machines, &mut self.address_claim_state_machines); 245 | } 246 | 247 | fn update_receive_messages(&mut self) { 248 | while !self.receive_message_queue.is_empty() { 249 | // Todo receive messages, need to generalize message handling 250 | let current_message = self.receive_message_queue.front().unwrap(); 251 | 252 | // Process address claims and requests to claim 253 | if NAME::default() == current_message.get_destination_name() { 254 | // Broadcast Message 255 | if current_message.get_identifier().pgn() 256 | == Pgn::from_raw(CommonParameterGroupNumbers::AddressClaim as u32) 257 | { 258 | // Todo 259 | } else if current_message.get_identifier().pgn() 260 | == Pgn::from_raw( 261 | CommonParameterGroupNumbers::ParameterGroupNumberRequest as u32, 262 | ) 263 | && current_message.get_data().len() >= 3 264 | { 265 | let message_data = current_message.get_data(); 266 | let requested_pgn: u32 = (message_data[0] as u32) 267 | | ((message_data[1] as u32) << 8) 268 | | ((message_data[2] as u32) << 16); 269 | 270 | if requested_pgn 271 | == CommonParameterGroupNumbers::ParameterGroupNumberRequest as u32 272 | { 273 | for internal_cf in &mut self.address_claim_state_machines { 274 | let mut address_claimer = internal_cf.borrow_mut(); 275 | match *address_claimer { 276 | ControlFunction::Internal { 277 | ref mut address_claim_data, 278 | } => { 279 | if address_claim_data.get_state() 280 | == AddressClaimingState::AddressClaimingComplete 281 | { 282 | address_claim_data.set_state( 283 | AddressClaimingState::SendReclaimAddressOnRequest, 284 | ); 285 | } 286 | } 287 | ControlFunction::External { name: _ } => {} 288 | } 289 | } 290 | } 291 | } else { 292 | // Destination specific 293 | } 294 | 295 | self.receive_message_queue.pop_front(); 296 | } 297 | } 298 | } 299 | 300 | fn update_transmit_messages(&mut self) { 301 | let should_continue_sending: bool = true; // Todo, check driver return values. 302 | 303 | while !self.high_priority_can_message_tx_queue.is_empty() { 304 | // todo hand off to driver 305 | self.high_priority_can_message_tx_queue.pop_front(); 306 | } 307 | 308 | while should_continue_sending && !self.normal_priority_can_message_tx_queue.is_empty() { 309 | // todo hand off to driver 310 | self.normal_priority_can_message_tx_queue.pop_front(); 311 | } 312 | } 313 | 314 | pub fn update(mut self) { 315 | self.update_receive_messages(); 316 | self.update_address_claiming(); 317 | self.update_transmit_messages(); 318 | } 319 | } 320 | 321 | impl Default for NetworkManager { 322 | fn default() -> Self { 323 | Self::new() 324 | } 325 | } 326 | 327 | #[cfg(test)] 328 | mod tests { 329 | use super::*; 330 | 331 | #[test] 332 | fn test_creating_network_manager() { 333 | let network = NetworkManager::new(); 334 | network.update(); 335 | } 336 | 337 | #[test] 338 | fn test_creating_internal_control_function() { 339 | let mut network = NetworkManager::new(); 340 | let test_name = NAME::builder() 341 | .device_class(0) 342 | .device_class_instance(0) 343 | .ecu_instance(0) 344 | .function_code(130) 345 | .function_instance(0) 346 | .identity_number(123_u32) 347 | .industry_group(2) 348 | .function_instance(0) 349 | .build(); 350 | 351 | let new_cf = ControlFunction::new_internal_control_function( 352 | test_name, 353 | Address(0x81), 354 | true, 355 | &mut network, 356 | ); 357 | 358 | assert_eq!( 359 | >::into(new_cf.borrow().get_name()), 360 | test_name.into() 361 | ); 362 | } 363 | } 364 | -------------------------------------------------------------------------------- /src/network_management/name.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Raven Industries inc. 2 | 3 | #[derive(PartialEq, Eq, Clone, Copy)] 4 | pub enum NameField { 5 | IdentityNumber(u32), 6 | ShortIdentityNumber(u16), 7 | ExtendedIdentityNumber(u8), 8 | ManufacturerCode(u16), 9 | EcuInstance(u8), 10 | FunctionInstance(u8), 11 | Function(u8), 12 | DeviceClass(u8), 13 | DeviceClassInstance(u8), 14 | IndustryGroup(u8), 15 | SelfConfigurableAddress(bool), 16 | } 17 | 18 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 19 | pub struct NAME { 20 | raw_name: u64, 21 | } 22 | 23 | impl NAME { 24 | pub fn new(raw_name: u64) -> Self { 25 | Self { raw_name } 26 | } 27 | 28 | pub fn builder() -> NameBuilder { 29 | NameBuilder::default() 30 | } 31 | 32 | pub fn has_field_value(&self, field_value: NameField) -> bool { 33 | *self != NAME::default() 34 | && match field_value { 35 | NameField::IdentityNumber(value) => self.get_identity_number() == value, 36 | NameField::ShortIdentityNumber(value) => self.get_short_identity_number() == value, 37 | NameField::ExtendedIdentityNumber(value) => { 38 | self.get_extended_identity_number() == value 39 | } 40 | NameField::ManufacturerCode(value) => self.get_manufacturer_code() == value, 41 | NameField::EcuInstance(value) => self.get_ecu_instance() == value, 42 | NameField::FunctionInstance(value) => self.get_function_instance() == value, 43 | NameField::Function(value) => self.get_function() == value, 44 | NameField::DeviceClass(value) => self.get_device_class() == value, 45 | NameField::DeviceClassInstance(value) => self.get_device_class_instance() == value, 46 | NameField::IndustryGroup(value) => self.get_industry_group() == value, 47 | NameField::SelfConfigurableAddress(value) => { 48 | self.get_self_configurable_address() == value 49 | } 50 | } 51 | } 52 | 53 | pub fn has_field_values(&self, name_fields: &[NameField]) -> bool { 54 | /// A helper function to get the index of a field 55 | /// This is used to set the bits in a mask to check if all supplied fields are satisfied 56 | fn get_index(field: &NameField) -> u8 { 57 | match field { 58 | NameField::IdentityNumber(_) => 0, 59 | NameField::ShortIdentityNumber(_) => 1, 60 | NameField::ExtendedIdentityNumber(_) => 2, 61 | NameField::ManufacturerCode(_) => 3, 62 | NameField::EcuInstance(_) => 4, 63 | NameField::FunctionInstance(_) => 5, 64 | NameField::Function(_) => 6, 65 | NameField::DeviceClass(_) => 7, 66 | NameField::DeviceClassInstance(_) => 8, 67 | NameField::IndustryGroup(_) => 9, 68 | NameField::SelfConfigurableAddress(_) => 10, 69 | } 70 | } 71 | 72 | // Make a mask of all the fields present in the supplied array 73 | let fields_present = name_fields.iter().fold(0_u16, |acc, name_field| { 74 | return acc | 1 << get_index(name_field); 75 | }); 76 | // Make a mask of all the fields satisfied in the supplied array 77 | let fields_satisfied = name_fields.iter().fold(0_u16, |acc, name_field| { 78 | if self.has_field_value(*name_field) { 79 | return acc | 1 << get_index(name_field); 80 | } else { 81 | return acc; 82 | } 83 | }); 84 | return fields_satisfied == fields_present; 85 | } 86 | 87 | pub fn get_device_class(&self) -> u8 { 88 | ((self.raw_name >> 49) & 0x7F) as u8 89 | } 90 | 91 | pub fn set_device_class(&mut self, device_class: u8) { 92 | self.raw_name &= !0x00FE000000000000_u64; 93 | self.raw_name |= ((device_class & 0x7F) as u64) << 49; 94 | } 95 | 96 | pub fn get_device_class_instance(&self) -> u8 { 97 | ((self.raw_name >> 56) & 0x0F) as u8 98 | } 99 | 100 | pub fn set_device_class_instance(&mut self, device_class_instance: u8) { 101 | self.raw_name &= !0x0F00000000000000; 102 | self.raw_name |= ((device_class_instance & 0x0F) as u64) << 56; 103 | } 104 | 105 | pub fn get_ecu_instance(&self) -> u8 { 106 | ((self.raw_name >> 32) & 0x07) as u8 107 | } 108 | 109 | pub fn set_ecu_instance(&mut self, ecu_instance: u8) { 110 | self.raw_name &= !0x0000000700000000; 111 | self.raw_name |= ((ecu_instance & 0x07) as u64) << 32; 112 | } 113 | 114 | pub fn get_extended_identity_number(&self) -> u8 { 115 | ((self.raw_name >> 16) & 0x1F) as u8 116 | } 117 | 118 | pub fn set_extended_identity_number(&mut self, extended_identity_number: u8) { 119 | self.raw_name &= !0x00000000001F0000; 120 | self.raw_name |= ((extended_identity_number & 0x1F) as u64) << 16; 121 | } 122 | 123 | pub fn get_function(&self) -> u8 { 124 | ((self.raw_name >> 40) & 0xFF) as u8 125 | } 126 | 127 | pub fn set_function(&mut self, function: u8) { 128 | self.raw_name &= !0x0000FF0000000000; 129 | self.raw_name |= (function as u64) << 40; 130 | } 131 | 132 | pub fn get_function_instance(&self) -> u8 { 133 | ((self.raw_name >> 35) & 0x1F) as u8 134 | } 135 | 136 | pub fn set_function_instance(&mut self, function: u8) { 137 | self.raw_name &= !0x000000F800000000; 138 | self.raw_name |= ((function & 0x1F) as u64) << 35; 139 | } 140 | 141 | pub fn get_identity_number(&self) -> u32 { 142 | (self.raw_name & 0x001FFFFF) as u32 143 | } 144 | 145 | pub fn set_identity_number(&mut self, identity_number: u32) { 146 | self.raw_name &= !0x00000000001FFFFF; 147 | self.raw_name |= (identity_number & 0x00000000001FFFFF) as u64; 148 | } 149 | 150 | pub fn get_industry_group(&self) -> u8 { 151 | ((self.raw_name >> 60) & 0x07) as u8 152 | } 153 | 154 | pub fn set_industry_group(&mut self, industry_group: u8) { 155 | self.raw_name &= !0x7000000000000000; 156 | self.raw_name |= ((industry_group & 0x07) as u64) << 60; 157 | } 158 | 159 | pub fn get_manufacturer_code(&self) -> u16 { 160 | ((self.raw_name >> 21) & 0x07FF) as u16 161 | } 162 | 163 | pub fn set_manufacturer_code(&mut self, manufacturer_code: u16) { 164 | self.raw_name &= !0x00000000FFE00000; 165 | self.raw_name |= ((manufacturer_code & 0x07FF) as u64) << 21; 166 | } 167 | 168 | pub fn get_self_configurable_address(&self) -> bool { 169 | (self.raw_name >> 63) != 0 170 | } 171 | 172 | pub fn set_self_configurable_address(&mut self, self_configurable_address: bool) { 173 | self.raw_name &= !0x8000000000000000; 174 | self.raw_name |= (self_configurable_address as u64) << 63; 175 | } 176 | 177 | pub fn get_short_identity_number(&self) -> u16 { 178 | (self.raw_name & 0x0000FFFF) as u16 179 | } 180 | 181 | pub fn set_short_identity_number(&mut self, short_identity_number: u16) { 182 | self.raw_name &= !0x000000000000FFFF; 183 | self.raw_name |= short_identity_number as u64; 184 | } 185 | } 186 | 187 | impl Default for NAME { 188 | fn default() -> Self { 189 | Self { 190 | raw_name: 0xFFFFFFFFFFFFFFFF, 191 | } 192 | } 193 | } 194 | 195 | impl From for u64 { 196 | fn from(name: NAME) -> Self { 197 | name.raw_name 198 | } 199 | } 200 | 201 | impl From for [u8; 8] { 202 | fn from(name: NAME) -> Self { 203 | name.raw_name.to_le_bytes() 204 | } 205 | } 206 | 207 | #[derive(Default)] 208 | pub struct NameBuilder { 209 | self_configurable_address: bool, 210 | industry_group: u8, 211 | device_class_instance: u8, 212 | device_class: u8, 213 | function_code: u8, 214 | function_instance: u8, 215 | ecu_instance: u8, 216 | manufacturer_code: u16, 217 | identity_number: u32, 218 | } 219 | 220 | impl NameBuilder { 221 | pub fn new() -> NameBuilder { 222 | NameBuilder::default() 223 | } 224 | 225 | pub fn build(&self) -> NAME { 226 | NAME { 227 | raw_name: (self.self_configurable_address as u64) << 63 228 | | (self.industry_group as u64 & 0x7) << 60 229 | | (self.device_class_instance as u64 & 0xF) << 56 230 | | (self.device_class as u64 & 0x7F) << 49 231 | | (self.function_code as u64 & 0xFF) << 40 232 | | (self.function_instance as u64 & 0x1F) << 35 233 | | (self.ecu_instance as u64 & 0x7) << 32 234 | | (self.manufacturer_code as u64 & 0x7FF) << 21 235 | | self.identity_number as u64 & 0x1FFFFF, 236 | } 237 | } 238 | 239 | pub fn self_configurable_address(&mut self, value: impl Into) -> &mut NameBuilder { 240 | self.self_configurable_address = value.into(); 241 | self 242 | } 243 | pub fn industry_group(&mut self, value: impl Into) -> &mut NameBuilder { 244 | self.industry_group = value.into(); 245 | self 246 | } 247 | pub fn device_class_instance(&mut self, value: impl Into) -> &mut NameBuilder { 248 | self.device_class_instance = value.into(); 249 | self 250 | } 251 | pub fn device_class(&mut self, value: impl Into) -> &mut NameBuilder { 252 | self.device_class = value.into(); 253 | self 254 | } 255 | pub fn function_code(&mut self, value: impl Into) -> &mut NameBuilder { 256 | self.function_code = value.into(); 257 | self 258 | } 259 | pub fn function_instance(&mut self, value: impl Into) -> &mut NameBuilder { 260 | self.function_instance = value.into(); 261 | self 262 | } 263 | pub fn ecu_instance(&mut self, value: impl Into) -> &mut NameBuilder { 264 | self.ecu_instance = value.into(); 265 | self 266 | } 267 | pub fn manufacturer_code(&mut self, value: impl Into) -> &mut NameBuilder { 268 | self.manufacturer_code = value.into(); 269 | self 270 | } 271 | pub fn identity_number(&mut self, value: impl Into) -> &mut NameBuilder { 272 | self.identity_number = value.into(); 273 | self 274 | } 275 | } 276 | 277 | impl From for NameBuilder { 278 | fn from(value: NAME) -> Self { 279 | let value: u64 = value.into(); 280 | NameBuilder { 281 | self_configurable_address: (value >> 63) != 0, 282 | industry_group: (value >> 60 & 0x7) as u8, 283 | device_class_instance: (value >> 56 & 0xF) as u8, 284 | device_class: (value >> 49 & 0x7F) as u8, 285 | function_code: (value >> 40 & 0xFF) as u8, 286 | function_instance: (value >> 35 & 0x1F) as u8, 287 | ecu_instance: (value >> 32 & 0x7) as u8, 288 | manufacturer_code: (value >> 21 & 0x7FF) as u16, 289 | identity_number: (value & 0x1FFFFF) as u32, 290 | } 291 | } 292 | } 293 | 294 | #[cfg(test)] 295 | mod tests { 296 | use super::*; 297 | 298 | #[test] 299 | fn test_name_properties() { 300 | let mut name_under_test = NAME::new(0); 301 | 302 | name_under_test.set_self_configurable_address(true); 303 | name_under_test.set_industry_group(1); 304 | name_under_test.set_device_class(2); 305 | name_under_test.set_function(3); 306 | name_under_test.set_identity_number(4); 307 | name_under_test.set_ecu_instance(5); 308 | name_under_test.set_function_instance(6); 309 | name_under_test.set_device_class_instance(7); 310 | name_under_test.set_manufacturer_code(8); 311 | 312 | assert_eq!(true, name_under_test.get_self_configurable_address()); 313 | assert_eq!(1, name_under_test.get_industry_group()); 314 | assert_eq!(2, name_under_test.get_device_class()); 315 | assert_eq!(3, name_under_test.get_function()); 316 | assert_eq!(4, name_under_test.get_identity_number()); 317 | assert_eq!(5, name_under_test.get_ecu_instance()); 318 | assert_eq!(6, name_under_test.get_function_instance()); 319 | assert_eq!(7, name_under_test.get_device_class_instance()); 320 | assert_eq!(8, name_under_test.get_manufacturer_code()); 321 | assert_eq!(0, name_under_test.get_extended_identity_number()); 322 | assert_eq!(4, name_under_test.get_short_identity_number()); 323 | assert_eq!(10881826125818888196_u64, name_under_test.raw_name); 324 | } 325 | 326 | #[test] 327 | fn test_name_builder() { 328 | let name_under_test = NAME::builder() 329 | .identity_number(4_u32) 330 | .manufacturer_code(8_u16) 331 | .ecu_instance(5) 332 | .function_instance(6) 333 | .function_code(3) 334 | .device_class(2) 335 | .device_class_instance(7) 336 | .industry_group(1) 337 | .self_configurable_address(true) 338 | .build(); 339 | 340 | assert_eq!(10881826125818888196_u64, name_under_test.into()); 341 | } 342 | 343 | #[test] 344 | fn test_out_of_range_properties() { 345 | let mut name_under_test = NAME::new(0); 346 | 347 | name_under_test.set_industry_group(8); 348 | name_under_test.set_device_class_instance(16); 349 | name_under_test.set_device_class(128); 350 | name_under_test.set_identity_number(2097152); 351 | name_under_test.set_ecu_instance(8); 352 | name_under_test.set_function_instance(32); 353 | name_under_test.set_manufacturer_code(2048); 354 | 355 | assert_ne!(name_under_test.get_industry_group(), 8); 356 | assert_ne!(name_under_test.get_device_class_instance(), 16); 357 | assert_ne!(name_under_test.get_device_class(), 128); 358 | assert_ne!(name_under_test.get_identity_number(), 2097151); 359 | assert_ne!(name_under_test.get_ecu_instance(), 8); 360 | assert_ne!(name_under_test.get_function_instance(), 32); 361 | assert_ne!(name_under_test.get_manufacturer_code(), 2048); 362 | } 363 | 364 | #[test] 365 | fn test_name_equality() { 366 | let test_value: u64 = 10376445291390828545; 367 | let name_under_test1 = NAME::new(test_value); 368 | let name_under_test2 = NAME::new(test_value); 369 | 370 | assert_eq!(test_value, name_under_test1.raw_name); 371 | assert_eq!(name_under_test1.raw_name, name_under_test2.raw_name); 372 | } 373 | 374 | #[test] 375 | fn test_filter_matching() { 376 | let mut test_name = NAME::new(0); 377 | let mut filters_to_test = Vec::new(); 378 | 379 | let identity_number_filter = NameField::IdentityNumber(1); 380 | filters_to_test.push(identity_number_filter); 381 | assert_eq!(false, test_name.has_field_value(identity_number_filter)); 382 | assert_eq!(false, test_name.has_field_values(&filters_to_test)); 383 | 384 | test_name.set_identity_number(1); 385 | assert_eq!(true, test_name.has_field_value(identity_number_filter)); 386 | assert_eq!(true, test_name.has_field_values(&filters_to_test)); 387 | 388 | let manufacturer_number_filter = NameField::ManufacturerCode(2); 389 | filters_to_test.push(manufacturer_number_filter); 390 | assert_eq!(false, test_name.has_field_value(manufacturer_number_filter)); 391 | assert_eq!(false, test_name.has_field_values(&filters_to_test)); 392 | 393 | test_name.set_manufacturer_code(2); 394 | assert_eq!(true, test_name.has_field_value(manufacturer_number_filter)); 395 | assert_eq!(true, test_name.has_field_values(&filters_to_test)); 396 | 397 | let ecu_instance_filter = NameField::EcuInstance(3); 398 | filters_to_test.push(ecu_instance_filter); 399 | assert_eq!(false, test_name.has_field_value(ecu_instance_filter)); 400 | assert_eq!(false, test_name.has_field_values(&filters_to_test)); 401 | 402 | test_name.set_ecu_instance(3); 403 | assert_eq!(true, test_name.has_field_value(ecu_instance_filter)); 404 | assert_eq!(true, test_name.has_field_values(&filters_to_test)); 405 | 406 | let function_instance_filter = NameField::FunctionInstance(4); 407 | filters_to_test.push(function_instance_filter); 408 | assert_eq!(false, test_name.has_field_value(function_instance_filter)); 409 | assert_eq!(false, test_name.has_field_values(&filters_to_test)); 410 | 411 | test_name.set_function_instance(4); 412 | assert_eq!(true, test_name.has_field_value(function_instance_filter)); 413 | assert_eq!(true, test_name.has_field_values(&filters_to_test)); 414 | 415 | let function_filter = NameField::Function(5); 416 | filters_to_test.push(function_filter); 417 | assert_eq!(false, test_name.has_field_value(function_filter)); 418 | assert_eq!(false, test_name.has_field_values(&filters_to_test)); 419 | 420 | test_name.set_function(5); 421 | assert_eq!(true, test_name.has_field_value(function_filter)); 422 | assert_eq!(true, test_name.has_field_values(&filters_to_test)); 423 | 424 | let device_class_filter = NameField::DeviceClass(6); 425 | filters_to_test.push(device_class_filter); 426 | assert_eq!(false, test_name.has_field_value(device_class_filter)); 427 | assert_eq!(false, test_name.has_field_values(&filters_to_test)); 428 | 429 | test_name.set_device_class(6); 430 | assert_eq!(true, test_name.has_field_value(device_class_filter)); 431 | assert_eq!(true, test_name.has_field_values(&filters_to_test)); 432 | 433 | let industry_group_filter = NameField::IndustryGroup(7); 434 | filters_to_test.push(industry_group_filter); 435 | assert_eq!(false, test_name.has_field_value(industry_group_filter)); 436 | assert_eq!(false, test_name.has_field_values(&filters_to_test)); 437 | 438 | test_name.set_industry_group(7); 439 | assert_eq!(true, test_name.has_field_value(industry_group_filter)); 440 | assert_eq!(true, test_name.has_field_values(&filters_to_test)); 441 | 442 | let device_class_inst_filter = NameField::DeviceClassInstance(8); 443 | filters_to_test.push(device_class_inst_filter); 444 | assert_eq!(false, test_name.has_field_value(device_class_inst_filter)); 445 | assert_eq!(false, test_name.has_field_values(&filters_to_test)); 446 | 447 | test_name.set_device_class_instance(8); 448 | assert_eq!(true, test_name.has_field_value(device_class_inst_filter)); 449 | assert_eq!(true, test_name.has_field_values(&filters_to_test)); 450 | 451 | let self_config_address_filter = NameField::SelfConfigurableAddress(true); 452 | filters_to_test.push(self_config_address_filter); 453 | assert_eq!(false, test_name.has_field_value(self_config_address_filter)); 454 | assert_eq!(false, test_name.has_field_values(&filters_to_test)); 455 | 456 | test_name.set_self_configurable_address(true); 457 | assert_eq!(true, test_name.has_field_value(self_config_address_filter)); 458 | assert_eq!(true, test_name.has_field_values(&filters_to_test)); 459 | } 460 | } 461 | -------------------------------------------------------------------------------- /src/object_pool/colour.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Clone, Copy, PartialEq)] 2 | #[non_exhaustive] 3 | pub struct Colour { 4 | pub id: u8, 5 | pub a: u8, 6 | pub r: u8, 7 | pub g: u8, 8 | pub b: u8, 9 | } 10 | 11 | impl Colour { 12 | pub fn new_by_id(id: u8) -> Self { 13 | Colour::COLOUR_PALETTE[id as usize] 14 | } 15 | 16 | pub fn new_by_rgb(r: u8, g: u8, b: u8) -> Self { 17 | for colour in Colour::COLOUR_PALETTE.iter() { 18 | if colour.r == r && colour.g == g && colour.b == b { 19 | return *colour; 20 | } 21 | } 22 | 23 | Colour::BLACK 24 | } 25 | 26 | pub fn new_by_rgba(r: u8, g: u8, b: u8, _a: u8) -> Self { 27 | Self::new_by_rgb(r, g, b) 28 | } 29 | 30 | pub fn as_rgb(&self) -> [u8; 3] { 31 | [self.r, self.g, self.b] 32 | } 33 | 34 | pub fn as_rgba(&self) -> [u8; 4] { 35 | [self.r, self.g, self.b, self.a] 36 | } 37 | 38 | pub const BLACK: Colour = Colour::COLOUR_PALETTE[0]; 39 | pub const WHITE: Colour = Colour::COLOUR_PALETTE[1]; 40 | pub const GREEN: Colour = Colour::COLOUR_PALETTE[2]; 41 | pub const TEAL: Colour = Colour::COLOUR_PALETTE[3]; 42 | pub const MAROON: Colour = Colour::COLOUR_PALETTE[4]; 43 | pub const PURPLE: Colour = Colour::COLOUR_PALETTE[5]; 44 | pub const OLIVE: Colour = Colour::COLOUR_PALETTE[6]; 45 | pub const SILVER: Colour = Colour::COLOUR_PALETTE[7]; 46 | pub const GREY: Colour = Colour::COLOUR_PALETTE[8]; 47 | pub const BLUE: Colour = Colour::COLOUR_PALETTE[9]; 48 | pub const LIME: Colour = Colour::COLOUR_PALETTE[10]; 49 | pub const CYAN: Colour = Colour::COLOUR_PALETTE[11]; 50 | pub const RED: Colour = Colour::COLOUR_PALETTE[12]; 51 | pub const MAGENTA: Colour = Colour::COLOUR_PALETTE[13]; 52 | pub const YELLOW: Colour = Colour::COLOUR_PALETTE[14]; 53 | pub const NAVY: Colour = Colour::COLOUR_PALETTE[15]; 54 | 55 | #[rustfmt::skip] // Skip formatting the lines 56 | pub const COLOUR_PALETTE: [Colour; 256] = [ 57 | Colour { r: 0x00, g: 0x00, b: 0x00, a: 0xFF, id: 0 }, 58 | Colour { r: 0xFF, g: 0xFF, b: 0xFF, a: 0xFF, id: 1 }, 59 | Colour { r: 0x00, g: 0x99, b: 0x00, a: 0xFF, id: 2 }, 60 | Colour { r: 0x00, g: 0x99, b: 0x99, a: 0xFF, id: 3 }, 61 | Colour { r: 0x99, g: 0x00, b: 0x00, a: 0xFF, id: 4 }, 62 | Colour { r: 0x99, g: 0x00, b: 0x99, a: 0xFF, id: 5 }, 63 | Colour { r: 0x99, g: 0x99, b: 0x00, a: 0xFF, id: 6 }, 64 | Colour { r: 0xCC, g: 0xCC, b: 0xCC, a: 0xFF, id: 7 }, 65 | Colour { r: 0x99, g: 0x99, b: 0x99, a: 0xFF, id: 8 }, 66 | Colour { r: 0x00, g: 0x00, b: 0xFF, a: 0xFF, id: 9 }, 67 | Colour { r: 0x00, g: 0xFF, b: 0x00, a: 0xFF, id: 10 }, 68 | Colour { r: 0x00, g: 0xFF, b: 0xFF, a: 0xFF, id: 11 }, 69 | Colour { r: 0xFF, g: 0x00, b: 0x00, a: 0xFF, id: 12 }, 70 | Colour { r: 0xFF, g: 0x00, b: 0xFF, a: 0xFF, id: 13 }, 71 | Colour { r: 0xFF, g: 0xFF, b: 0x00, a: 0xFF, id: 14 }, 72 | Colour { r: 0x00, g: 0x00, b: 0x99, a: 0xFF, id: 15 }, 73 | Colour { r: 0x00, g: 0x00, b: 0x00, a: 0xFF, id: 16 }, 74 | Colour { r: 0x00, g: 0x00, b: 0x33, a: 0xFF, id: 17 }, 75 | Colour { r: 0x00, g: 0x00, b: 0x66, a: 0xFF, id: 18 }, 76 | Colour { r: 0x00, g: 0x00, b: 0x99, a: 0xFF, id: 19 }, 77 | Colour { r: 0x00, g: 0x00, b: 0xCC, a: 0xFF, id: 20 }, 78 | Colour { r: 0x00, g: 0x00, b: 0xFF, a: 0xFF, id: 21 }, 79 | Colour { r: 0x00, g: 0x33, b: 0x00, a: 0xFF, id: 22 }, 80 | Colour { r: 0x00, g: 0x33, b: 0x33, a: 0xFF, id: 23 }, 81 | Colour { r: 0x00, g: 0x33, b: 0x66, a: 0xFF, id: 24 }, 82 | Colour { r: 0x00, g: 0x33, b: 0x99, a: 0xFF, id: 25 }, 83 | Colour { r: 0x00, g: 0x33, b: 0xCC, a: 0xFF, id: 26 }, 84 | Colour { r: 0x00, g: 0x33, b: 0xFF, a: 0xFF, id: 27 }, 85 | Colour { r: 0x00, g: 0x66, b: 0x00, a: 0xFF, id: 28 }, 86 | Colour { r: 0x00, g: 0x66, b: 0x33, a: 0xFF, id: 29 }, 87 | Colour { r: 0x00, g: 0x66, b: 0x66, a: 0xFF, id: 30 }, 88 | Colour { r: 0x00, g: 0x66, b: 0x99, a: 0xFF, id: 31 }, 89 | Colour { r: 0x00, g: 0x66, b: 0xCC, a: 0xFF, id: 32 }, 90 | Colour { r: 0x00, g: 0x66, b: 0xFF, a: 0xFF, id: 33 }, 91 | Colour { r: 0x00, g: 0x99, b: 0x00, a: 0xFF, id: 34 }, 92 | Colour { r: 0x00, g: 0x99, b: 0x33, a: 0xFF, id: 35 }, 93 | Colour { r: 0x00, g: 0x99, b: 0x66, a: 0xFF, id: 36 }, 94 | Colour { r: 0x00, g: 0x99, b: 0x99, a: 0xFF, id: 37 }, 95 | Colour { r: 0x00, g: 0x99, b: 0xCC, a: 0xFF, id: 38 }, 96 | Colour { r: 0x00, g: 0x99, b: 0xFF, a: 0xFF, id: 39 }, 97 | Colour { r: 0x00, g: 0xCC, b: 0x00, a: 0xFF, id: 40 }, 98 | Colour { r: 0x00, g: 0xCC, b: 0x33, a: 0xFF, id: 41 }, 99 | Colour { r: 0x00, g: 0xCC, b: 0x66, a: 0xFF, id: 42 }, 100 | Colour { r: 0x00, g: 0xCC, b: 0x99, a: 0xFF, id: 43 }, 101 | Colour { r: 0x00, g: 0xCC, b: 0xCC, a: 0xFF, id: 44 }, 102 | Colour { r: 0x00, g: 0xCC, b: 0xFF, a: 0xFF, id: 45 }, 103 | Colour { r: 0x00, g: 0xFF, b: 0x00, a: 0xFF, id: 46 }, 104 | Colour { r: 0x00, g: 0xFF, b: 0x33, a: 0xFF, id: 47 }, 105 | Colour { r: 0x00, g: 0xFF, b: 0x66, a: 0xFF, id: 48 }, 106 | Colour { r: 0x00, g: 0xFF, b: 0x99, a: 0xFF, id: 49 }, 107 | Colour { r: 0x00, g: 0xFF, b: 0xCC, a: 0xFF, id: 50 }, 108 | Colour { r: 0x00, g: 0xFF, b: 0xFF, a: 0xFF, id: 51 }, 109 | Colour { r: 0x33, g: 0x00, b: 0x00, a: 0xFF, id: 52 }, 110 | Colour { r: 0x33, g: 0x00, b: 0x33, a: 0xFF, id: 53 }, 111 | Colour { r: 0x33, g: 0x00, b: 0x66, a: 0xFF, id: 54 }, 112 | Colour { r: 0x33, g: 0x00, b: 0x99, a: 0xFF, id: 55 }, 113 | Colour { r: 0x33, g: 0x00, b: 0xCC, a: 0xFF, id: 56 }, 114 | Colour { r: 0x33, g: 0x00, b: 0xFF, a: 0xFF, id: 57 }, 115 | Colour { r: 0x33, g: 0x33, b: 0x00, a: 0xFF, id: 58 }, 116 | Colour { r: 0x33, g: 0x33, b: 0x33, a: 0xFF, id: 59 }, 117 | Colour { r: 0x33, g: 0x33, b: 0x66, a: 0xFF, id: 60 }, 118 | Colour { r: 0x33, g: 0x33, b: 0x99, a: 0xFF, id: 61 }, 119 | Colour { r: 0x33, g: 0x33, b: 0xCC, a: 0xFF, id: 62 }, 120 | Colour { r: 0x33, g: 0x33, b: 0xFF, a: 0xFF, id: 63 }, 121 | Colour { r: 0x33, g: 0x66, b: 0x00, a: 0xFF, id: 64 }, 122 | Colour { r: 0x33, g: 0x66, b: 0x33, a: 0xFF, id: 65 }, 123 | Colour { r: 0x33, g: 0x66, b: 0x66, a: 0xFF, id: 66 }, 124 | Colour { r: 0x33, g: 0x66, b: 0x99, a: 0xFF, id: 67 }, 125 | Colour { r: 0x33, g: 0x66, b: 0xCC, a: 0xFF, id: 68 }, 126 | Colour { r: 0x33, g: 0x66, b: 0xFF, a: 0xFF, id: 69 }, 127 | Colour { r: 0x33, g: 0x99, b: 0x00, a: 0xFF, id: 70 }, 128 | Colour { r: 0x33, g: 0x99, b: 0x33, a: 0xFF, id: 71 }, 129 | Colour { r: 0x33, g: 0x99, b: 0x66, a: 0xFF, id: 72 }, 130 | Colour { r: 0x33, g: 0x99, b: 0x99, a: 0xFF, id: 73 }, 131 | Colour { r: 0x33, g: 0x99, b: 0xCC, a: 0xFF, id: 74 }, 132 | Colour { r: 0x33, g: 0x99, b: 0xFF, a: 0xFF, id: 75 }, 133 | Colour { r: 0x33, g: 0xCC, b: 0x00, a: 0xFF, id: 76 }, 134 | Colour { r: 0x33, g: 0xCC, b: 0x33, a: 0xFF, id: 77 }, 135 | Colour { r: 0x33, g: 0xCC, b: 0x66, a: 0xFF, id: 78 }, 136 | Colour { r: 0x33, g: 0xCC, b: 0x99, a: 0xFF, id: 79 }, 137 | Colour { r: 0x33, g: 0xCC, b: 0xCC, a: 0xFF, id: 80 }, 138 | Colour { r: 0x33, g: 0xCC, b: 0xFF, a: 0xFF, id: 81 }, 139 | Colour { r: 0x33, g: 0xFF, b: 0x00, a: 0xFF, id: 82 }, 140 | Colour { r: 0x33, g: 0xFF, b: 0x33, a: 0xFF, id: 83 }, 141 | Colour { r: 0x33, g: 0xFF, b: 0x66, a: 0xFF, id: 84 }, 142 | Colour { r: 0x33, g: 0xFF, b: 0x99, a: 0xFF, id: 85 }, 143 | Colour { r: 0x33, g: 0xFF, b: 0xCC, a: 0xFF, id: 86 }, 144 | Colour { r: 0x33, g: 0xFF, b: 0xFF, a: 0xFF, id: 87 }, 145 | Colour { r: 0x66, g: 0x00, b: 0x00, a: 0xFF, id: 88 }, 146 | Colour { r: 0x66, g: 0x00, b: 0x33, a: 0xFF, id: 89 }, 147 | Colour { r: 0x66, g: 0x00, b: 0x66, a: 0xFF, id: 90 }, 148 | Colour { r: 0x66, g: 0x00, b: 0x99, a: 0xFF, id: 91 }, 149 | Colour { r: 0x66, g: 0x00, b: 0xCC, a: 0xFF, id: 92 }, 150 | Colour { r: 0x66, g: 0x00, b: 0xFF, a: 0xFF, id: 93 }, 151 | Colour { r: 0x66, g: 0x33, b: 0x00, a: 0xFF, id: 94 }, 152 | Colour { r: 0x66, g: 0x33, b: 0x33, a: 0xFF, id: 95 }, 153 | Colour { r: 0x66, g: 0x33, b: 0x66, a: 0xFF, id: 96 }, 154 | Colour { r: 0x66, g: 0x33, b: 0x99, a: 0xFF, id: 97 }, 155 | Colour { r: 0x66, g: 0x33, b: 0xCC, a: 0xFF, id: 98 }, 156 | Colour { r: 0x66, g: 0x33, b: 0xFF, a: 0xFF, id: 99 }, 157 | Colour { r: 0x66, g: 0x66, b: 0x00, a: 0xFF, id: 100 }, 158 | Colour { r: 0x66, g: 0x66, b: 0x33, a: 0xFF, id: 101 }, 159 | Colour { r: 0x66, g: 0x66, b: 0x66, a: 0xFF, id: 102 }, 160 | Colour { r: 0x66, g: 0x66, b: 0x99, a: 0xFF, id: 103 }, 161 | Colour { r: 0x66, g: 0x66, b: 0xCC, a: 0xFF, id: 104 }, 162 | Colour { r: 0x66, g: 0x66, b: 0xFF, a: 0xFF, id: 105 }, 163 | Colour { r: 0x66, g: 0x99, b: 0x00, a: 0xFF, id: 106 }, 164 | Colour { r: 0x66, g: 0x99, b: 0x33, a: 0xFF, id: 107 }, 165 | Colour { r: 0x66, g: 0x99, b: 0x66, a: 0xFF, id: 108 }, 166 | Colour { r: 0x66, g: 0x99, b: 0x99, a: 0xFF, id: 109 }, 167 | Colour { r: 0x66, g: 0x99, b: 0xCC, a: 0xFF, id: 110 }, 168 | Colour { r: 0x66, g: 0x99, b: 0xFF, a: 0xFF, id: 111 }, 169 | Colour { r: 0x66, g: 0xCC, b: 0x00, a: 0xFF, id: 112 }, 170 | Colour { r: 0x66, g: 0xCC, b: 0x33, a: 0xFF, id: 113 }, 171 | Colour { r: 0x66, g: 0xCC, b: 0x66, a: 0xFF, id: 114 }, 172 | Colour { r: 0x66, g: 0xCC, b: 0x99, a: 0xFF, id: 115 }, 173 | Colour { r: 0x66, g: 0xCC, b: 0xCC, a: 0xFF, id: 116 }, 174 | Colour { r: 0x66, g: 0xCC, b: 0xFF, a: 0xFF, id: 117 }, 175 | Colour { r: 0x66, g: 0xFF, b: 0x00, a: 0xFF, id: 118 }, 176 | Colour { r: 0x66, g: 0xFF, b: 0x33, a: 0xFF, id: 119 }, 177 | Colour { r: 0x66, g: 0xFF, b: 0x66, a: 0xFF, id: 120 }, 178 | Colour { r: 0x66, g: 0xFF, b: 0x99, a: 0xFF, id: 121 }, 179 | Colour { r: 0x66, g: 0xFF, b: 0xCC, a: 0xFF, id: 122 }, 180 | Colour { r: 0x66, g: 0xFF, b: 0xFF, a: 0xFF, id: 123 }, 181 | Colour { r: 0x99, g: 0x00, b: 0x00, a: 0xFF, id: 124 }, 182 | Colour { r: 0x99, g: 0x00, b: 0x33, a: 0xFF, id: 125 }, 183 | Colour { r: 0x99, g: 0x00, b: 0x66, a: 0xFF, id: 126 }, 184 | Colour { r: 0x99, g: 0x00, b: 0x99, a: 0xFF, id: 127 }, 185 | Colour { r: 0x99, g: 0x00, b: 0xCC, a: 0xFF, id: 128 }, 186 | Colour { r: 0x99, g: 0x00, b: 0xFF, a: 0xFF, id: 129 }, 187 | Colour { r: 0x99, g: 0x33, b: 0x00, a: 0xFF, id: 130 }, 188 | Colour { r: 0x99, g: 0x33, b: 0x33, a: 0xFF, id: 131 }, 189 | Colour { r: 0x99, g: 0x33, b: 0x66, a: 0xFF, id: 132 }, 190 | Colour { r: 0x99, g: 0x33, b: 0x99, a: 0xFF, id: 133 }, 191 | Colour { r: 0x99, g: 0x33, b: 0xCC, a: 0xFF, id: 134 }, 192 | Colour { r: 0x99, g: 0x33, b: 0xFF, a: 0xFF, id: 135 }, 193 | Colour { r: 0x99, g: 0x66, b: 0x00, a: 0xFF, id: 136 }, 194 | Colour { r: 0x99, g: 0x66, b: 0x33, a: 0xFF, id: 137 }, 195 | Colour { r: 0x99, g: 0x66, b: 0x66, a: 0xFF, id: 138 }, 196 | Colour { r: 0x99, g: 0x66, b: 0x99, a: 0xFF, id: 139 }, 197 | Colour { r: 0x99, g: 0x66, b: 0xCC, a: 0xFF, id: 140 }, 198 | Colour { r: 0x99, g: 0x66, b: 0xFF, a: 0xFF, id: 141 }, 199 | Colour { r: 0x99, g: 0x99, b: 0x00, a: 0xFF, id: 142 }, 200 | Colour { r: 0x99, g: 0x99, b: 0x33, a: 0xFF, id: 143 }, 201 | Colour { r: 0x99, g: 0x99, b: 0x66, a: 0xFF, id: 144 }, 202 | Colour { r: 0x99, g: 0x99, b: 0x99, a: 0xFF, id: 145 }, 203 | Colour { r: 0x99, g: 0x99, b: 0xCC, a: 0xFF, id: 146 }, 204 | Colour { r: 0x99, g: 0x99, b: 0xFF, a: 0xFF, id: 147 }, 205 | Colour { r: 0x99, g: 0xCC, b: 0x00, a: 0xFF, id: 148 }, 206 | Colour { r: 0x99, g: 0xCC, b: 0x33, a: 0xFF, id: 149 }, 207 | Colour { r: 0x99, g: 0xCC, b: 0x66, a: 0xFF, id: 150 }, 208 | Colour { r: 0x99, g: 0xCC, b: 0x99, a: 0xFF, id: 151 }, 209 | Colour { r: 0x99, g: 0xCC, b: 0xCC, a: 0xFF, id: 152 }, 210 | Colour { r: 0x99, g: 0xCC, b: 0xFF, a: 0xFF, id: 153 }, 211 | Colour { r: 0x99, g: 0xFF, b: 0x00, a: 0xFF, id: 154 }, 212 | Colour { r: 0x99, g: 0xFF, b: 0x33, a: 0xFF, id: 155 }, 213 | Colour { r: 0x99, g: 0xFF, b: 0x66, a: 0xFF, id: 156 }, 214 | Colour { r: 0x99, g: 0xFF, b: 0x99, a: 0xFF, id: 157 }, 215 | Colour { r: 0x99, g: 0xFF, b: 0xCC, a: 0xFF, id: 158 }, 216 | Colour { r: 0x99, g: 0xFF, b: 0xFF, a: 0xFF, id: 159 }, 217 | Colour { r: 0xCC, g: 0x00, b: 0x00, a: 0xFF, id: 160 }, 218 | Colour { r: 0xCC, g: 0x00, b: 0x33, a: 0xFF, id: 161 }, 219 | Colour { r: 0xCC, g: 0x00, b: 0x66, a: 0xFF, id: 162 }, 220 | Colour { r: 0xCC, g: 0x00, b: 0x99, a: 0xFF, id: 163 }, 221 | Colour { r: 0xCC, g: 0x00, b: 0xCC, a: 0xFF, id: 164 }, 222 | Colour { r: 0xCC, g: 0x00, b: 0xFF, a: 0xFF, id: 165 }, 223 | Colour { r: 0xCC, g: 0x33, b: 0x00, a: 0xFF, id: 166 }, 224 | Colour { r: 0xCC, g: 0x33, b: 0x33, a: 0xFF, id: 167 }, 225 | Colour { r: 0xCC, g: 0x33, b: 0x66, a: 0xFF, id: 168 }, 226 | Colour { r: 0xCC, g: 0x33, b: 0x99, a: 0xFF, id: 169 }, 227 | Colour { r: 0xCC, g: 0x33, b: 0xCC, a: 0xFF, id: 170 }, 228 | Colour { r: 0xCC, g: 0x33, b: 0xFF, a: 0xFF, id: 171 }, 229 | Colour { r: 0xCC, g: 0x66, b: 0x00, a: 0xFF, id: 172 }, 230 | Colour { r: 0xCC, g: 0x66, b: 0x33, a: 0xFF, id: 173 }, 231 | Colour { r: 0xCC, g: 0x66, b: 0x66, a: 0xFF, id: 174 }, 232 | Colour { r: 0xCC, g: 0x66, b: 0x99, a: 0xFF, id: 175 }, 233 | Colour { r: 0xCC, g: 0x66, b: 0xCC, a: 0xFF, id: 176 }, 234 | Colour { r: 0xCC, g: 0x66, b: 0xFF, a: 0xFF, id: 177 }, 235 | Colour { r: 0xCC, g: 0x99, b: 0x00, a: 0xFF, id: 178 }, 236 | Colour { r: 0xCC, g: 0x99, b: 0x33, a: 0xFF, id: 179 }, 237 | Colour { r: 0xCC, g: 0x99, b: 0x66, a: 0xFF, id: 180 }, 238 | Colour { r: 0xCC, g: 0x99, b: 0x99, a: 0xFF, id: 181 }, 239 | Colour { r: 0xCC, g: 0x99, b: 0xCC, a: 0xFF, id: 182 }, 240 | Colour { r: 0xCC, g: 0x99, b: 0xFF, a: 0xFF, id: 183 }, 241 | Colour { r: 0xCC, g: 0xCC, b: 0x00, a: 0xFF, id: 184 }, 242 | Colour { r: 0xCC, g: 0xCC, b: 0x33, a: 0xFF, id: 185 }, 243 | Colour { r: 0xCC, g: 0xCC, b: 0x66, a: 0xFF, id: 186 }, 244 | Colour { r: 0xCC, g: 0xCC, b: 0x99, a: 0xFF, id: 187 }, 245 | Colour { r: 0xCC, g: 0xCC, b: 0xCC, a: 0xFF, id: 188 }, 246 | Colour { r: 0xCC, g: 0xCC, b: 0xFF, a: 0xFF, id: 189 }, 247 | Colour { r: 0xCC, g: 0xFF, b: 0x00, a: 0xFF, id: 190 }, 248 | Colour { r: 0xCC, g: 0xFF, b: 0x33, a: 0xFF, id: 191 }, 249 | Colour { r: 0xCC, g: 0xFF, b: 0x66, a: 0xFF, id: 192 }, 250 | Colour { r: 0xCC, g: 0xFF, b: 0x99, a: 0xFF, id: 193 }, 251 | Colour { r: 0xCC, g: 0xFF, b: 0xCC, a: 0xFF, id: 194 }, 252 | Colour { r: 0xCC, g: 0xFF, b: 0xFF, a: 0xFF, id: 195 }, 253 | Colour { r: 0xFF, g: 0x00, b: 0x00, a: 0xFF, id: 196 }, 254 | Colour { r: 0xFF, g: 0x00, b: 0x33, a: 0xFF, id: 197 }, 255 | Colour { r: 0xFF, g: 0x00, b: 0x66, a: 0xFF, id: 198 }, 256 | Colour { r: 0xFF, g: 0x00, b: 0x99, a: 0xFF, id: 199 }, 257 | Colour { r: 0xFF, g: 0x00, b: 0xCC, a: 0xFF, id: 200 }, 258 | Colour { r: 0xFF, g: 0x00, b: 0xFF, a: 0xFF, id: 201 }, 259 | Colour { r: 0xFF, g: 0x33, b: 0x00, a: 0xFF, id: 202 }, 260 | Colour { r: 0xFF, g: 0x33, b: 0x33, a: 0xFF, id: 203 }, 261 | Colour { r: 0xFF, g: 0x33, b: 0x66, a: 0xFF, id: 204 }, 262 | Colour { r: 0xFF, g: 0x33, b: 0x99, a: 0xFF, id: 205 }, 263 | Colour { r: 0xFF, g: 0x33, b: 0xCC, a: 0xFF, id: 206 }, 264 | Colour { r: 0xFF, g: 0x33, b: 0xFF, a: 0xFF, id: 207 }, 265 | Colour { r: 0xFF, g: 0x66, b: 0x00, a: 0xFF, id: 208 }, 266 | Colour { r: 0xFF, g: 0x66, b: 0x33, a: 0xFF, id: 209 }, 267 | Colour { r: 0xFF, g: 0x66, b: 0x66, a: 0xFF, id: 210 }, 268 | Colour { r: 0xFF, g: 0x66, b: 0x99, a: 0xFF, id: 211 }, 269 | Colour { r: 0xFF, g: 0x66, b: 0xCC, a: 0xFF, id: 212 }, 270 | Colour { r: 0xFF, g: 0x66, b: 0xFF, a: 0xFF, id: 213 }, 271 | Colour { r: 0xFF, g: 0x99, b: 0x00, a: 0xFF, id: 214 }, 272 | Colour { r: 0xFF, g: 0x99, b: 0x33, a: 0xFF, id: 215 }, 273 | Colour { r: 0xFF, g: 0x99, b: 0x66, a: 0xFF, id: 216 }, 274 | Colour { r: 0xFF, g: 0x99, b: 0x99, a: 0xFF, id: 217 }, 275 | Colour { r: 0xFF, g: 0x99, b: 0xCC, a: 0xFF, id: 218 }, 276 | Colour { r: 0xFF, g: 0x99, b: 0xFF, a: 0xFF, id: 219 }, 277 | Colour { r: 0xFF, g: 0xCC, b: 0x00, a: 0xFF, id: 220 }, 278 | Colour { r: 0xFF, g: 0xCC, b: 0x33, a: 0xFF, id: 221 }, 279 | Colour { r: 0xFF, g: 0xCC, b: 0x66, a: 0xFF, id: 222 }, 280 | Colour { r: 0xFF, g: 0xCC, b: 0x99, a: 0xFF, id: 223 }, 281 | Colour { r: 0xFF, g: 0xCC, b: 0xCC, a: 0xFF, id: 224 }, 282 | Colour { r: 0xFF, g: 0xCC, b: 0xFF, a: 0xFF, id: 225 }, 283 | Colour { r: 0xFF, g: 0xFF, b: 0x00, a: 0xFF, id: 226 }, 284 | Colour { r: 0xFF, g: 0xFF, b: 0x33, a: 0xFF, id: 227 }, 285 | Colour { r: 0xFF, g: 0xFF, b: 0x66, a: 0xFF, id: 228 }, 286 | Colour { r: 0xFF, g: 0xFF, b: 0x99, a: 0xFF, id: 229 }, 287 | Colour { r: 0xFF, g: 0xFF, b: 0xCC, a: 0xFF, id: 230 }, 288 | Colour { r: 0xFF, g: 0xFF, b: 0xFF, a: 0xFF, id: 231 }, 289 | Colour { r: 0x00, g: 0x00, b: 0x00, a: 0xFF, id: 232 }, 290 | Colour { r: 0x00, g: 0x00, b: 0x00, a: 0xFF, id: 233 }, 291 | Colour { r: 0x00, g: 0x00, b: 0x00, a: 0xFF, id: 234 }, 292 | Colour { r: 0x00, g: 0x00, b: 0x00, a: 0xFF, id: 235 }, 293 | Colour { r: 0x00, g: 0x00, b: 0x00, a: 0xFF, id: 236 }, 294 | Colour { r: 0x00, g: 0x00, b: 0x00, a: 0xFF, id: 237 }, 295 | Colour { r: 0x00, g: 0x00, b: 0x00, a: 0xFF, id: 238 }, 296 | Colour { r: 0x00, g: 0x00, b: 0x00, a: 0xFF, id: 239 }, 297 | Colour { r: 0x00, g: 0x00, b: 0x00, a: 0xFF, id: 240 }, 298 | Colour { r: 0x00, g: 0x00, b: 0x00, a: 0xFF, id: 241 }, 299 | Colour { r: 0x00, g: 0x00, b: 0x00, a: 0xFF, id: 242 }, 300 | Colour { r: 0x00, g: 0x00, b: 0x00, a: 0xFF, id: 243 }, 301 | Colour { r: 0x00, g: 0x00, b: 0x00, a: 0xFF, id: 244 }, 302 | Colour { r: 0x00, g: 0x00, b: 0x00, a: 0xFF, id: 245 }, 303 | Colour { r: 0x00, g: 0x00, b: 0x00, a: 0xFF, id: 246 }, 304 | Colour { r: 0x00, g: 0x00, b: 0x00, a: 0xFF, id: 247 }, 305 | Colour { r: 0x00, g: 0x00, b: 0x00, a: 0xFF, id: 248 }, 306 | Colour { r: 0x00, g: 0x00, b: 0x00, a: 0xFF, id: 249 }, 307 | Colour { r: 0x00, g: 0x00, b: 0x00, a: 0xFF, id: 250 }, 308 | Colour { r: 0x00, g: 0x00, b: 0x00, a: 0xFF, id: 251 }, 309 | Colour { r: 0x00, g: 0x00, b: 0x00, a: 0xFF, id: 252 }, 310 | Colour { r: 0x00, g: 0x00, b: 0x00, a: 0xFF, id: 253 }, 311 | Colour { r: 0x00, g: 0x00, b: 0x00, a: 0xFF, id: 254 }, 312 | Colour { r: 0x00, g: 0x00, b: 0x00, a: 0xFF, id: 255 }, 313 | ]; 314 | } 315 | 316 | impl Default for Colour { 317 | fn default() -> Self { 318 | Colour::GREY 319 | } 320 | } 321 | 322 | impl From for Colour { 323 | fn from(color_id: u8) -> Self { 324 | Colour::new_by_id(color_id) 325 | } 326 | } 327 | 328 | impl From for u8 { 329 | fn from(color: Colour) -> Self { 330 | color.id 331 | } 332 | } 333 | 334 | impl From for Colour { 335 | fn from(val: u32) -> Self { 336 | let b = val.to_le_bytes(); 337 | Colour::new_by_rgba(b[0], b[1], b[2], b[3]) 338 | } 339 | } 340 | -------------------------------------------------------------------------------- /src/object_pool/object.rs: -------------------------------------------------------------------------------- 1 | use crate::network_management::name::NAME; 2 | use crate::object_pool::object_attributes::{ 3 | Alignment, AnimationOptions, ButtonOptions, ColorFormat, ColourPaletteOptions, 4 | ExternalObjectDefinitionOptions, ExternalReferenceNameOptions, FormatType, 5 | GraphicsContextOptions, InputListOptions, InputNumberOptions, InputStringOptions, 6 | KeyGroupOptions, LineDirection, MacroRef, NumberOptions, ObjectLabel, ObjectRef, 7 | OutputArchedBarGraphOptions, OutputLinearBarGraphOptions, OutputMeterOptions, 8 | OutputStringOptions, PictureGraphicOptions, Point, ScaledGraphicOptions, WindowMaskCellFormat, 9 | WindowMaskOptions, WindowType, 10 | }; 11 | use crate::object_pool::object_id::ObjectId; 12 | use crate::object_pool::{Colour, ObjectType}; 13 | 14 | #[derive(Debug)] 15 | pub enum Object { 16 | WorkingSet(WorkingSet), 17 | DataMask(DataMask), 18 | AlarmMask(AlarmMask), 19 | Container(Container), 20 | SoftKeyMask(SoftKeyMask), 21 | Key(Key), 22 | Button(Button), 23 | InputBoolean(InputBoolean), 24 | InputString(InputString), 25 | InputNumber(InputNumber), 26 | InputList(InputList), 27 | OutputString(OutputString), 28 | OutputNumber(OutputNumber), 29 | OutputLine(OutputLine), 30 | OutputRectangle(OutputRectangle), 31 | OutputEllipse(OutputEllipse), 32 | OutputPolygon(OutputPolygon), 33 | OutputMeter(OutputMeter), 34 | OutputLinearBarGraph(OutputLinearBarGraph), 35 | OutputArchedBarGraph(OutputArchedBarGraph), 36 | PictureGraphic(PictureGraphic), 37 | NumberVariable(NumberVariable), 38 | StringVariable(StringVariable), 39 | FontAttributes(FontAttributes), 40 | LineAttributes(LineAttributes), 41 | FillAttributes(FillAttributes), 42 | InputAttributes(InputAttributes), 43 | ObjectPointer(ObjectPointer), 44 | Macro(Macro), 45 | AuxiliaryFunctionType1(AuxiliaryFunctionType1), 46 | AuxiliaryInputType1(AuxiliaryInputType1), 47 | AuxiliaryFunctionType2(AuxiliaryFunctionType2), 48 | AuxiliaryInputType2(AuxiliaryInputType2), 49 | AuxiliaryControlDesignatorType2(AuxiliaryControlDesignatorType2), 50 | WindowMask(WindowMask), 51 | KeyGroup(KeyGroup), 52 | GraphicsContext(GraphicsContext), 53 | OutputList(OutputList), 54 | ExtendedInputAttributes(ExtendedInputAttributes), 55 | ColourMap(ColourMap), 56 | ObjectLabelReferenceList(ObjectLabelReferenceList), 57 | ExternalObjectDefinition(ExternalObjectDefinition), 58 | ExternalReferenceName(ExternalReferenceName), 59 | ExternalObjectPointer(ExternalObjectPointer), 60 | Animation(Animation), 61 | ColourPalette(ColourPalette), 62 | GraphicData(GraphicData), 63 | WorkingSetSpecialControls(WorkingSetSpecialControls), 64 | ScaledGraphic(ScaledGraphic), 65 | } 66 | 67 | impl Object { 68 | pub fn id(&self) -> ObjectId { 69 | match self { 70 | Object::WorkingSet(o) => o.id, 71 | Object::DataMask(o) => o.id, 72 | Object::AlarmMask(o) => o.id, 73 | Object::Container(o) => o.id, 74 | Object::SoftKeyMask(o) => o.id, 75 | Object::Key(o) => o.id, 76 | Object::Button(o) => o.id, 77 | Object::InputBoolean(o) => o.id, 78 | Object::InputString(o) => o.id, 79 | Object::InputNumber(o) => o.id, 80 | Object::InputList(o) => o.id, 81 | Object::OutputString(o) => o.id, 82 | Object::OutputNumber(o) => o.id, 83 | Object::OutputLine(o) => o.id, 84 | Object::OutputRectangle(o) => o.id, 85 | Object::OutputEllipse(o) => o.id, 86 | Object::OutputPolygon(o) => o.id, 87 | Object::OutputMeter(o) => o.id, 88 | Object::OutputLinearBarGraph(o) => o.id, 89 | Object::OutputArchedBarGraph(o) => o.id, 90 | Object::PictureGraphic(o) => o.id, 91 | Object::NumberVariable(o) => o.id, 92 | Object::StringVariable(o) => o.id, 93 | Object::FontAttributes(o) => o.id, 94 | Object::LineAttributes(o) => o.id, 95 | Object::FillAttributes(o) => o.id, 96 | Object::InputAttributes(o) => o.id, 97 | Object::ObjectPointer(o) => o.id, 98 | Object::Macro(o) => o.id, 99 | Object::AuxiliaryFunctionType1(o) => o.id, 100 | Object::AuxiliaryInputType1(o) => o.id, 101 | Object::AuxiliaryFunctionType2(o) => o.id, 102 | Object::AuxiliaryInputType2(o) => o.id, 103 | Object::AuxiliaryControlDesignatorType2(o) => o.id, 104 | Object::WindowMask(o) => o.id, 105 | Object::KeyGroup(o) => o.id, 106 | Object::GraphicsContext(o) => o.id, 107 | Object::OutputList(o) => o.id, 108 | Object::ExtendedInputAttributes(o) => o.id, 109 | Object::ColourMap(o) => o.id, 110 | Object::ObjectLabelReferenceList(o) => o.id, 111 | Object::ExternalObjectDefinition(o) => o.id, 112 | Object::ExternalReferenceName(o) => o.id, 113 | Object::ExternalObjectPointer(o) => o.id, 114 | Object::Animation(o) => o.id, 115 | Object::ColourPalette(o) => o.id, 116 | Object::GraphicData(o) => o.id, 117 | Object::WorkingSetSpecialControls(o) => o.id, 118 | Object::ScaledGraphic(o) => o.id, 119 | } 120 | } 121 | 122 | pub fn object_type(&self) -> ObjectType { 123 | match self { 124 | Object::WorkingSet(_) => ObjectType::WorkingSet, 125 | Object::DataMask(_) => ObjectType::DataMask, 126 | Object::AlarmMask(_) => ObjectType::AlarmMask, 127 | Object::Container(_) => ObjectType::Container, 128 | Object::SoftKeyMask(_) => ObjectType::SoftKeyMask, 129 | Object::Key(_) => ObjectType::Key, 130 | Object::Button(_) => ObjectType::Button, 131 | Object::InputBoolean(_) => ObjectType::InputBoolean, 132 | Object::InputString(_) => ObjectType::InputString, 133 | Object::InputNumber(_) => ObjectType::InputNumber, 134 | Object::InputList(_) => ObjectType::InputList, 135 | Object::OutputString(_) => ObjectType::OutputString, 136 | Object::OutputNumber(_) => ObjectType::OutputNumber, 137 | Object::OutputLine(_) => ObjectType::OutputLine, 138 | Object::OutputRectangle(_) => ObjectType::OutputRectangle, 139 | Object::OutputEllipse(_) => ObjectType::OutputEllipse, 140 | Object::OutputPolygon(_) => ObjectType::OutputPolygon, 141 | Object::OutputMeter(_) => ObjectType::OutputMeter, 142 | Object::OutputLinearBarGraph(_) => ObjectType::OutputLinearBarGraph, 143 | Object::OutputArchedBarGraph(_) => ObjectType::OutputArchedBarGraph, 144 | Object::PictureGraphic(_) => ObjectType::PictureGraphic, 145 | Object::NumberVariable(_) => ObjectType::NumberVariable, 146 | Object::StringVariable(_) => ObjectType::StringVariable, 147 | Object::FontAttributes(_) => ObjectType::FontAttributes, 148 | Object::LineAttributes(_) => ObjectType::LineAttributes, 149 | Object::FillAttributes(_) => ObjectType::FillAttributes, 150 | Object::InputAttributes(_) => ObjectType::InputAttributes, 151 | Object::ObjectPointer(_) => ObjectType::ObjectPointer, 152 | Object::Macro(_) => ObjectType::Macro, 153 | Object::AuxiliaryFunctionType1(_) => ObjectType::AuxiliaryFunctionType1, 154 | Object::AuxiliaryInputType1(_) => ObjectType::AuxiliaryInputType1, 155 | Object::AuxiliaryFunctionType2(_) => ObjectType::AuxiliaryFunctionType2, 156 | Object::AuxiliaryInputType2(_) => ObjectType::AuxiliaryInputType2, 157 | Object::AuxiliaryControlDesignatorType2(_) => { 158 | ObjectType::AuxiliaryControlDesignatorType2 159 | } 160 | Object::WindowMask(_) => ObjectType::WindowMask, 161 | Object::KeyGroup(_) => ObjectType::KeyGroup, 162 | Object::GraphicsContext(_) => ObjectType::GraphicsContext, 163 | Object::OutputList(_) => ObjectType::OutputList, 164 | Object::ExtendedInputAttributes(_) => ObjectType::ExtendedInputAttributes, 165 | Object::ColourMap(_) => ObjectType::ColourMap, 166 | Object::ObjectLabelReferenceList(_) => ObjectType::ObjectLabelReferenceList, 167 | Object::ExternalObjectDefinition(_) => ObjectType::ExternalObjectDefinition, 168 | Object::ExternalReferenceName(_) => ObjectType::ExternalReferenceName, 169 | Object::ExternalObjectPointer(_) => ObjectType::ExternalObjectPointer, 170 | Object::Animation(_) => ObjectType::Animation, 171 | Object::ColourPalette(_) => ObjectType::ColourPalette, 172 | Object::GraphicData(_) => ObjectType::GraphicData, 173 | Object::WorkingSetSpecialControls(_) => ObjectType::WorkingSetSpecialControls, 174 | Object::ScaledGraphic(_) => ObjectType::ScaledGraphic, 175 | } 176 | } 177 | } 178 | 179 | #[derive(Debug, PartialEq)] 180 | pub struct WorkingSet { 181 | pub id: ObjectId, 182 | pub background_colour: Colour, 183 | pub selectable: bool, 184 | pub active_mask: ObjectId, 185 | pub object_refs: Vec, 186 | pub macro_refs: Vec, 187 | pub language_codes: Vec, 188 | } 189 | 190 | #[derive(Debug, PartialEq)] 191 | pub struct DataMask { 192 | pub id: ObjectId, 193 | pub background_colour: u8, 194 | pub soft_key_mask: ObjectId, 195 | pub object_refs: Vec, 196 | pub macro_refs: Vec, 197 | } 198 | 199 | #[derive(Debug, PartialEq)] 200 | pub struct AlarmMask { 201 | pub id: ObjectId, 202 | pub background_colour: u8, 203 | pub soft_key_mask: ObjectId, 204 | pub priority: u8, 205 | pub acoustic_signal: u8, 206 | pub object_refs: Vec, 207 | pub macro_refs: Vec, 208 | } 209 | 210 | #[derive(Debug, PartialEq)] 211 | pub struct Container { 212 | pub id: ObjectId, 213 | pub width: u16, 214 | pub height: u16, 215 | pub hidden: bool, 216 | pub object_refs: Vec, 217 | pub macro_refs: Vec, 218 | } 219 | 220 | #[derive(Debug, PartialEq)] 221 | pub struct SoftKeyMask { 222 | pub id: ObjectId, 223 | pub background_colour: u8, 224 | pub objects: Vec, 225 | pub macro_refs: Vec, 226 | } 227 | 228 | #[derive(Debug, PartialEq)] 229 | pub struct Key { 230 | pub id: ObjectId, 231 | pub background_colour: u8, 232 | pub key_code: u8, 233 | pub object_refs: Vec, 234 | pub macro_refs: Vec, 235 | } 236 | 237 | #[derive(Debug, PartialEq)] 238 | pub struct Button { 239 | pub id: ObjectId, 240 | pub width: u16, 241 | pub height: u16, 242 | pub background_colour: u8, 243 | pub border_colour: u8, 244 | pub key_code: u8, 245 | pub options: ButtonOptions, 246 | pub object_refs: Vec, 247 | pub macro_refs: Vec, 248 | } 249 | 250 | #[derive(Debug, PartialEq)] 251 | pub struct InputBoolean { 252 | pub id: ObjectId, 253 | pub background_colour: u8, 254 | pub width: u16, 255 | pub foreground_colour: ObjectId, 256 | pub variable_reference: ObjectId, 257 | pub value: bool, 258 | pub enabled: bool, 259 | pub macro_refs: Vec, 260 | } 261 | 262 | #[derive(Debug, PartialEq)] 263 | pub struct InputString { 264 | pub id: ObjectId, 265 | pub width: u16, 266 | pub height: u16, 267 | pub background_colour: u8, 268 | pub font_attributes: ObjectId, 269 | pub input_attributes: ObjectId, 270 | pub options: InputStringOptions, 271 | pub variable_reference: ObjectId, 272 | pub justification: Alignment, 273 | pub value: String, 274 | pub enabled: bool, 275 | pub macro_refs: Vec, 276 | } 277 | 278 | #[derive(Debug, Clone, PartialEq)] 279 | pub struct InputNumber { 280 | pub id: ObjectId, 281 | pub width: u16, 282 | pub height: u16, 283 | pub background_colour: u8, 284 | pub font_attributes: ObjectId, 285 | pub options: NumberOptions, 286 | pub variable_reference: ObjectId, 287 | pub value: u32, 288 | pub min_value: u32, 289 | pub max_value: u32, 290 | pub offset: i32, 291 | pub scale: f32, 292 | pub nr_of_decimals: u8, 293 | pub format: FormatType, 294 | pub justification: Alignment, 295 | pub options2: InputNumberOptions, 296 | pub macro_refs: Vec, 297 | } 298 | 299 | #[derive(Debug, PartialEq, Clone)] 300 | pub struct InputList { 301 | pub id: ObjectId, 302 | pub width: u16, 303 | pub height: u16, 304 | pub variable_reference: ObjectId, 305 | pub value: u8, 306 | pub options: InputListOptions, 307 | pub list_items: Vec, 308 | pub macro_refs: Vec, 309 | } 310 | 311 | #[derive(Debug, PartialEq, Clone)] 312 | pub struct OutputString { 313 | pub id: ObjectId, 314 | pub width: u16, 315 | pub height: u16, 316 | pub background_colour: u8, 317 | pub font_attributes: ObjectId, 318 | pub options: OutputStringOptions, 319 | pub variable_reference: ObjectId, 320 | pub justification: Alignment, 321 | pub value: String, 322 | pub macro_refs: Vec, 323 | } 324 | 325 | #[derive(Debug, PartialEq)] 326 | pub struct OutputNumber { 327 | pub id: ObjectId, 328 | pub width: u16, 329 | pub height: u16, 330 | pub background_colour: u8, 331 | pub font_attributes: ObjectId, 332 | pub options: NumberOptions, 333 | pub variable_reference: ObjectId, 334 | pub value: u32, 335 | pub offset: i32, 336 | pub scale: f32, 337 | pub nr_of_decimals: u8, 338 | pub format: FormatType, 339 | pub justification: Alignment, 340 | pub macro_refs: Vec, 341 | } 342 | 343 | #[derive(Debug, PartialEq, Clone)] 344 | pub struct OutputList { 345 | pub id: ObjectId, 346 | pub width: u16, 347 | pub height: u16, 348 | pub variable_reference: ObjectId, 349 | pub value: u8, 350 | pub list_items: Vec, 351 | pub macro_refs: Vec, 352 | } 353 | 354 | #[derive(Debug, PartialEq, Clone)] 355 | pub struct OutputLine { 356 | pub id: ObjectId, 357 | pub line_attributes: ObjectId, 358 | pub width: u16, 359 | pub height: u16, 360 | pub line_direction: LineDirection, 361 | pub macro_refs: Vec, 362 | } 363 | 364 | #[derive(Debug)] 365 | pub struct OutputRectangle { 366 | pub id: ObjectId, 367 | pub line_attributes: ObjectId, 368 | pub width: u16, 369 | pub height: u16, 370 | pub line_suppression: u8, 371 | pub fill_attributes: ObjectId, 372 | pub macro_refs: Vec, 373 | } 374 | 375 | #[derive(Debug)] 376 | pub struct OutputEllipse { 377 | pub id: ObjectId, 378 | pub line_attributes: ObjectId, 379 | pub width: u16, 380 | pub height: u16, 381 | pub ellipse_type: u8, 382 | pub start_angle: u8, 383 | pub end_angle: u8, 384 | pub fill_attributes: ObjectId, 385 | pub macro_refs: Vec, 386 | } 387 | 388 | #[derive(Debug)] 389 | pub struct OutputPolygon { 390 | pub id: ObjectId, 391 | pub width: u16, 392 | pub height: u16, 393 | pub line_attributes: ObjectId, 394 | pub fill_attributes: ObjectId, 395 | pub polygon_type: u8, 396 | pub points: Vec>, 397 | pub macro_refs: Vec, 398 | } 399 | 400 | #[derive(Debug)] 401 | pub struct OutputMeter { 402 | pub id: ObjectId, 403 | pub width: u16, 404 | pub needle_colour: u8, 405 | pub border_colour: u8, 406 | pub arc_and_tick_colour: u8, 407 | pub options: OutputMeterOptions, 408 | pub nr_of_ticks: u8, 409 | pub start_angle: u8, 410 | pub end_angle: u8, 411 | pub min_value: u16, 412 | pub max_value: u16, 413 | pub variable_reference: ObjectId, 414 | pub value: u16, 415 | pub macro_refs: Vec, 416 | } 417 | 418 | #[derive(Debug, Clone, PartialEq, Eq)] 419 | pub struct OutputLinearBarGraph { 420 | pub id: ObjectId, 421 | pub width: u16, 422 | pub height: u16, 423 | pub colour: u8, 424 | pub target_line_colour: u8, 425 | pub options: OutputLinearBarGraphOptions, 426 | pub nr_of_ticks: u8, 427 | pub min_value: u16, 428 | pub max_value: u16, 429 | pub variable_reference: ObjectId, 430 | pub value: u16, 431 | pub target_value_variable_reference: ObjectId, 432 | pub target_value: u16, 433 | pub macro_refs: Vec, 434 | } 435 | 436 | #[derive(Debug)] 437 | pub struct OutputArchedBarGraph { 438 | pub id: ObjectId, 439 | pub width: u16, 440 | pub height: u16, 441 | pub colour: u8, 442 | pub target_line_colour: u8, 443 | pub options: OutputArchedBarGraphOptions, 444 | pub start_angle: u8, 445 | pub end_angle: u8, 446 | pub bar_graph_width: u16, 447 | pub min_value: u16, 448 | pub max_value: u16, 449 | pub variable_reference: ObjectId, 450 | pub value: u16, 451 | pub target_value_variable_reference: ObjectId, 452 | pub target_value: u16, 453 | pub macro_refs: Vec, 454 | } 455 | 456 | #[derive(Debug)] 457 | pub struct PictureGraphic { 458 | pub id: ObjectId, 459 | pub width: u16, 460 | pub actual_width: u16, 461 | pub actual_height: u16, 462 | pub format: u8, 463 | pub options: PictureGraphicOptions, 464 | pub transparency_colour: u8, 465 | pub data: Vec, 466 | pub macro_refs: Vec, 467 | } 468 | 469 | #[derive(Debug)] 470 | pub struct NumberVariable { 471 | pub id: ObjectId, 472 | pub value: u32, 473 | } 474 | 475 | #[derive(Debug)] 476 | pub struct StringVariable { 477 | pub id: ObjectId, 478 | pub value: String, 479 | } 480 | 481 | #[derive(Debug)] 482 | pub struct FontAttributes { 483 | pub id: ObjectId, 484 | pub font_colour: u8, 485 | pub font_size: u8, 486 | pub font_type: u8, 487 | pub font_style: u8, 488 | pub macro_refs: Vec, 489 | } 490 | 491 | #[derive(Debug)] 492 | pub struct LineAttributes { 493 | pub id: ObjectId, 494 | pub line_colour: u8, 495 | pub line_width: u8, 496 | pub line_art: u16, 497 | pub macro_refs: Vec, 498 | } 499 | 500 | #[derive(Debug)] 501 | pub struct FillAttributes { 502 | pub id: ObjectId, 503 | pub fill_type: u8, 504 | pub fill_colour: u8, 505 | pub fill_pattern: ObjectId, 506 | pub macro_refs: Vec, 507 | } 508 | 509 | #[derive(Debug)] 510 | pub struct InputAttributes { 511 | pub id: ObjectId, 512 | pub validation_type: u8, 513 | pub validation_string: String, 514 | pub macro_refs: Vec, 515 | } 516 | 517 | #[derive(Debug, Copy, Clone)] 518 | pub enum ValidationType { 519 | ValidCharacters, 520 | InvalidCharacters, 521 | } 522 | 523 | impl From for u8 { 524 | fn from(value: ValidationType) -> Self { 525 | match value { 526 | ValidationType::ValidCharacters => 0, 527 | ValidationType::InvalidCharacters => 1, 528 | } 529 | } 530 | } 531 | 532 | impl From for ValidationType { 533 | fn from(value: u8) -> Self { 534 | match value { 535 | 0 => ValidationType::ValidCharacters, 536 | 1 => ValidationType::InvalidCharacters, 537 | _ => panic!("Invalid validation type"), 538 | } 539 | } 540 | } 541 | 542 | #[derive(Debug)] 543 | pub struct CharacterRange { 544 | pub first_character: u16, 545 | pub last_character: u16, 546 | } 547 | 548 | #[derive(Debug)] 549 | pub struct CodePlane { 550 | pub number: u8, 551 | pub character_ranges: Vec, 552 | } 553 | 554 | #[derive(Debug)] 555 | pub struct ExtendedInputAttributes { 556 | pub id: ObjectId, 557 | pub validation_type: ValidationType, 558 | pub code_planes: Vec, 559 | } 560 | 561 | #[derive(Debug)] 562 | pub struct ObjectPointer { 563 | pub id: ObjectId, 564 | pub value: ObjectId, 565 | } 566 | 567 | #[derive(Debug)] 568 | pub struct Macro { 569 | pub id: ObjectId, 570 | pub commands: Vec, 571 | } 572 | 573 | #[derive(Debug)] 574 | pub struct AuxiliaryFunctionType1 { 575 | pub id: ObjectId, 576 | pub background_colour: u8, 577 | pub function_type: u8, 578 | pub object_refs: Vec, 579 | } 580 | 581 | #[derive(Debug)] 582 | pub struct AuxiliaryInputType1 { 583 | pub id: ObjectId, 584 | pub background_colour: u8, 585 | pub function_type: u8, 586 | pub input_id: u8, 587 | pub object_refs: Vec, 588 | } 589 | 590 | #[derive(Debug)] 591 | pub struct AuxiliaryFunctionType2 { 592 | pub id: ObjectId, 593 | pub background_colour: u8, 594 | pub function_attributes: u8, 595 | pub object_refs: Vec, 596 | } 597 | 598 | #[derive(Debug)] 599 | pub struct AuxiliaryInputType2 { 600 | pub id: ObjectId, 601 | pub background_colour: u8, 602 | pub function_attributes: u8, 603 | pub object_refs: Vec, 604 | } 605 | 606 | #[derive(Debug)] 607 | pub struct AuxiliaryControlDesignatorType2 { 608 | pub id: ObjectId, 609 | pub pointer_type: u8, 610 | pub auxiliary_object_id: ObjectId, 611 | } 612 | 613 | #[derive(Debug)] 614 | pub struct ColourMap { 615 | pub id: ObjectId, 616 | pub colour_map: Vec, 617 | } 618 | 619 | #[derive(Debug, PartialEq, Copy, Clone)] 620 | pub struct GraphicsContext { 621 | pub id: ObjectId, 622 | pub viewport_width: u16, 623 | pub viewport_height: u16, 624 | pub viewport_x: i16, 625 | pub viewport_y: i16, 626 | pub canvas_width: u16, 627 | pub canvas_height: u16, 628 | pub viewport_zoom: f32, 629 | pub graphics_cursor_x: i16, 630 | pub graphics_cursor_y: i16, 631 | pub foreground_colour: u8, 632 | pub background_colour: u8, 633 | pub font_attributes_object: ObjectId, 634 | pub line_attributes_object: ObjectId, 635 | pub fill_attributes_object: ObjectId, 636 | pub format: ColorFormat, 637 | pub options: GraphicsContextOptions, 638 | pub transparency_colour: u8, 639 | } 640 | 641 | #[derive(Debug, Clone, PartialEq)] 642 | pub struct WindowMask { 643 | pub id: ObjectId, 644 | pub cell_format: WindowMaskCellFormat, 645 | pub window_type: WindowType, 646 | pub background_colour: u8, 647 | pub options: WindowMaskOptions, 648 | pub name: ObjectId, 649 | pub window_title: ObjectId, 650 | pub window_icon: ObjectId, 651 | pub objects: Vec, 652 | pub object_refs: Vec, 653 | pub macro_refs: Vec, 654 | } 655 | 656 | #[derive(Debug, Clone, PartialEq, Eq)] 657 | pub struct KeyGroup { 658 | pub id: ObjectId, 659 | pub options: KeyGroupOptions, 660 | pub name: ObjectId, 661 | pub key_group_icon: ObjectId, 662 | pub objects: Vec, 663 | pub macro_refs: Vec, 664 | } 665 | 666 | #[derive(Debug)] 667 | pub struct ObjectLabelReferenceList { 668 | pub id: ObjectId, 669 | pub object_labels: Vec, 670 | } 671 | 672 | #[derive(Debug, Clone, PartialEq, Eq)] 673 | pub struct ExternalObjectDefinition { 674 | pub id: ObjectId, 675 | pub options: ExternalObjectDefinitionOptions, 676 | pub name: NAME, 677 | pub objects: Vec, 678 | } 679 | 680 | #[derive(Debug)] 681 | pub struct ExternalReferenceName { 682 | pub id: ObjectId, 683 | pub options: ExternalReferenceNameOptions, 684 | pub name: NAME, 685 | } 686 | 687 | #[derive(Debug)] 688 | pub struct ExternalObjectPointer { 689 | pub id: ObjectId, 690 | pub default_object_id: ObjectId, 691 | pub external_reference_name_id: ObjectId, 692 | pub external_object_id: ObjectId, 693 | } 694 | 695 | #[derive(Debug)] 696 | pub struct Animation { 697 | pub id: ObjectId, 698 | pub width: u16, 699 | pub height: u16, 700 | pub refresh_interval: u16, 701 | pub value: u8, 702 | pub enabled: bool, 703 | pub first_child_index: u8, 704 | pub last_child_index: u8, 705 | pub default_child_index: u8, 706 | pub options: AnimationOptions, 707 | pub object_refs: Vec, 708 | pub macro_refs: Vec, 709 | } 710 | 711 | #[derive(Debug)] 712 | pub struct ColourPalette { 713 | pub id: ObjectId, 714 | pub options: ColourPaletteOptions, 715 | pub colours: Vec, 716 | } 717 | 718 | #[derive(Debug)] 719 | pub struct GraphicData { 720 | pub id: ObjectId, 721 | pub format: u8, 722 | pub data: Vec, 723 | } 724 | 725 | #[derive(Debug)] 726 | pub struct ScaledGraphic { 727 | pub id: ObjectId, 728 | pub width: u16, 729 | pub height: u16, 730 | pub scale_type: u8, 731 | pub options: ScaledGraphicOptions, 732 | pub value: u16, 733 | pub macro_refs: Vec, 734 | } 735 | 736 | #[derive(Debug)] 737 | pub struct WorkingSetSpecialControls { 738 | pub id: ObjectId, 739 | pub id_of_colour_map: ObjectId, 740 | pub id_of_colour_palette: ObjectId, 741 | pub language_pairs: Vec<(String, String)>, 742 | } 743 | -------------------------------------------------------------------------------- /src/object_pool/object_attributes.rs: -------------------------------------------------------------------------------- 1 | use crate::object_pool::object_id::ObjectId; 2 | use bitvec::field::BitField; 3 | use bitvec::order::{Lsb0, Msb0}; 4 | use bitvec::vec::BitVec; 5 | use bitvec::view::BitView; 6 | use strum_macros::FromRepr; 7 | 8 | #[derive(FromRepr, Debug, PartialEq, Clone, Copy)] 9 | #[repr(u8)] 10 | pub enum WindowType { 11 | FreeForm = 0, 12 | NumericOutputValueWithUnits1x1 = 1, 13 | NumericOutputValueNoUnits1x1 = 2, 14 | StringOutputValue1x1 = 3, 15 | NumericInputValueWithUnits1x1 = 4, 16 | NumericInputValueNoUnits1x1 = 5, 17 | StringInputValue1x1 = 6, 18 | HorizontalLinearBarGraph1x1 = 7, 19 | SingleButton1x1 = 8, 20 | DoubleButton1x1 = 9, 21 | NumericOutputValueWithUnits2x1 = 10, 22 | NumericOutputValueNoUnits2x1 = 11, 23 | StringOutputValue2x1 = 12, 24 | NumericInputValueWithUnits2x1 = 13, 25 | NumericInputValueNoUnits2x1 = 14, 26 | StringInputValue2x1 = 15, 27 | HorizontalLinearBarGraph2x1 = 16, 28 | SingleButton2x1 = 17, 29 | DoubleButton2x1 = 18, 30 | } 31 | 32 | impl From for WindowType { 33 | fn from(value: u8) -> Self { 34 | WindowType::from_repr(value).unwrap() 35 | } 36 | } 37 | 38 | impl From for u8 { 39 | fn from(value: WindowType) -> Self { 40 | value.into() 41 | } 42 | } 43 | 44 | #[derive(Debug, Clone, Copy, PartialEq)] 45 | pub enum WindowMaskCellFormat { 46 | CF1x1, 47 | CF1x2, 48 | CF1x3, 49 | CF1x4, 50 | CF1x5, 51 | CF1x6, 52 | CF2x1, 53 | CF2x2, 54 | CF2x3, 55 | CF2x4, 56 | CF2x5, 57 | CF2x6, 58 | } 59 | 60 | impl WindowMaskCellFormat { 61 | const fn from_size(x: u8, y: u8) -> WindowMaskCellFormat { 62 | let size = Point { x, y }; 63 | match size { 64 | Point { x: 1, y: 1 } => WindowMaskCellFormat::CF1x1, 65 | Point { x: 1, y: 2 } => WindowMaskCellFormat::CF1x2, 66 | Point { x: 1, y: 3 } => WindowMaskCellFormat::CF1x3, 67 | Point { x: 1, y: 4 } => WindowMaskCellFormat::CF1x4, 68 | Point { x: 1, y: 5 } => WindowMaskCellFormat::CF1x5, 69 | Point { x: 1, y: 6 } => WindowMaskCellFormat::CF1x6, 70 | Point { x: 2, y: 1 } => WindowMaskCellFormat::CF2x1, 71 | Point { x: 2, y: 2 } => WindowMaskCellFormat::CF2x2, 72 | Point { x: 2, y: 3 } => WindowMaskCellFormat::CF2x3, 73 | Point { x: 2, y: 4 } => WindowMaskCellFormat::CF2x4, 74 | Point { x: 2, y: 5 } => WindowMaskCellFormat::CF2x5, 75 | Point { x: 2, y: 6 } => WindowMaskCellFormat::CF2x6, 76 | _ => WindowMaskCellFormat::CF1x1, 77 | } 78 | } 79 | 80 | pub const fn size(self) -> Point { 81 | match self { 82 | WindowMaskCellFormat::CF1x1 => Point { x: 1, y: 1 }, 83 | WindowMaskCellFormat::CF1x2 => Point { x: 1, y: 2 }, 84 | WindowMaskCellFormat::CF1x3 => Point { x: 1, y: 3 }, 85 | WindowMaskCellFormat::CF1x4 => Point { x: 1, y: 4 }, 86 | WindowMaskCellFormat::CF1x5 => Point { x: 1, y: 5 }, 87 | WindowMaskCellFormat::CF1x6 => Point { x: 1, y: 6 }, 88 | WindowMaskCellFormat::CF2x1 => Point { x: 2, y: 1 }, 89 | WindowMaskCellFormat::CF2x2 => Point { x: 2, y: 2 }, 90 | WindowMaskCellFormat::CF2x3 => Point { x: 2, y: 3 }, 91 | WindowMaskCellFormat::CF2x4 => Point { x: 2, y: 4 }, 92 | WindowMaskCellFormat::CF2x5 => Point { x: 2, y: 5 }, 93 | WindowMaskCellFormat::CF2x6 => Point { x: 2, y: 6 }, 94 | } 95 | } 96 | } 97 | 98 | impl From for WindowMaskCellFormat { 99 | fn from(value: u16) -> Self { 100 | WindowMaskCellFormat::from_size((value << 8) as u8, value as u8) 101 | } 102 | } 103 | 104 | impl From for u16 { 105 | fn from(value: WindowMaskCellFormat) -> Self { 106 | let size = value.size(); 107 | ((size.x as u16) << 8) | size.y as u16 108 | } 109 | } 110 | 111 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 112 | pub struct WindowMaskOptions { 113 | pub available: bool, 114 | pub transparent: bool, 115 | } 116 | 117 | impl From for WindowMaskOptions { 118 | fn from(value: u8) -> Self { 119 | let mut bit_data = value.view_bits::().to_bitvec(); 120 | WindowMaskOptions { 121 | available: bit_data.pop().unwrap(), 122 | transparent: bit_data.pop().unwrap(), 123 | } 124 | } 125 | } 126 | 127 | impl From for u8 { 128 | fn from(value: WindowMaskOptions) -> u8 { 129 | let mut bit_data: BitVec = BitVec::new(); 130 | bit_data.push(value.available); 131 | bit_data.push(value.transparent); 132 | bit_data.extend([0; 6]); 133 | bit_data.load::() 134 | } 135 | } 136 | 137 | #[derive(Debug, PartialEq, Clone)] 138 | pub struct ObjectRef { 139 | pub id: ObjectId, 140 | pub offset: Point, 141 | // pub x: i16, 142 | // pub y: i16, 143 | } 144 | 145 | #[derive(Debug, PartialEq, Eq, Clone)] 146 | pub struct MacroRef { 147 | pub macro_id: u8, 148 | pub event_id: u8, 149 | } 150 | 151 | #[derive(Debug, Default, Clone, Copy, PartialEq)] 152 | pub struct Point { 153 | pub x: T, 154 | pub y: T, 155 | } 156 | 157 | impl core::ops::Add> for Point { 158 | type Output = Point; 159 | 160 | fn add(self, rhs: Point) -> Self::Output { 161 | Point { 162 | x: (self.x as i16 + rhs.x) as u16, 163 | y: (self.y as i16 + rhs.y) as u16, 164 | } 165 | } 166 | } 167 | 168 | #[derive(Debug)] 169 | pub struct ObjectLabel { 170 | pub id: ObjectId, 171 | pub string_variable_reference: ObjectId, 172 | pub font_type: u8, 173 | pub graphic_representation: ObjectId, 174 | } 175 | 176 | #[derive(Debug, PartialEq, Clone, Copy)] 177 | pub struct ButtonOptions { 178 | pub latchable: bool, 179 | pub state: ButtonState, 180 | pub suppress_border: bool, 181 | pub transparent_background: bool, 182 | pub disabled: bool, 183 | pub no_border: bool, 184 | } 185 | 186 | impl From for ButtonOptions { 187 | fn from(value: u8) -> Self { 188 | let mut bit_data = value.view_bits::().to_bitvec(); 189 | ButtonOptions { 190 | latchable: bit_data.pop().unwrap(), 191 | state: bit_data.pop().unwrap().into(), 192 | suppress_border: bit_data.pop().unwrap(), 193 | transparent_background: bit_data.pop().unwrap(), 194 | disabled: bit_data.pop().unwrap(), 195 | no_border: bit_data.pop().unwrap(), 196 | } 197 | } 198 | } 199 | 200 | impl From for u8 { 201 | fn from(value: ButtonOptions) -> u8 { 202 | let mut bit_data: BitVec = BitVec::new(); 203 | bit_data.push(value.latchable); 204 | bit_data.push(value.state.into()); 205 | bit_data.push(value.suppress_border); 206 | bit_data.push(value.transparent_background); 207 | bit_data.push(value.disabled); 208 | bit_data.push(value.no_border); 209 | bit_data.extend([0; 3]); 210 | bit_data.load::() 211 | } 212 | } 213 | 214 | #[derive(Debug, PartialEq, Clone, Copy)] 215 | pub enum ButtonState { 216 | Released, 217 | Latched, 218 | } 219 | 220 | impl From for bool { 221 | fn from(value: ButtonState) -> Self { 222 | match value { 223 | ButtonState::Released => false, 224 | ButtonState::Latched => true, 225 | } 226 | } 227 | } 228 | 229 | impl From for ButtonState { 230 | fn from(value: bool) -> Self { 231 | match value { 232 | false => ButtonState::Released, 233 | true => ButtonState::Latched, 234 | } 235 | } 236 | } 237 | 238 | #[derive(Debug, PartialEq, Copy, Clone)] 239 | pub struct InputStringOptions { 240 | pub transparent: bool, 241 | pub auto_wrap: bool, 242 | pub wrap_on_hyphen: bool, 243 | } 244 | 245 | impl From for InputStringOptions { 246 | fn from(value: u8) -> Self { 247 | let mut bit_data = value.view_bits::().to_bitvec(); 248 | InputStringOptions { 249 | transparent: bit_data.pop().unwrap(), 250 | auto_wrap: bit_data.pop().unwrap(), 251 | wrap_on_hyphen: bit_data.pop().unwrap(), 252 | } 253 | } 254 | } 255 | 256 | impl From for u8 { 257 | fn from(value: InputStringOptions) -> u8 { 258 | let mut bit_data: BitVec = BitVec::new(); 259 | bit_data.push(value.transparent); 260 | bit_data.push(value.auto_wrap); 261 | bit_data.push(value.wrap_on_hyphen); 262 | bit_data.extend([0; 5]); 263 | bit_data.load::() 264 | } 265 | } 266 | 267 | #[derive(Debug, Clone, Copy, PartialEq)] 268 | pub struct Alignment { 269 | pub horizontal: HorizontalAlignment, 270 | pub vertical: VerticalAlignment, 271 | } 272 | 273 | impl From for Alignment { 274 | fn from(value: u8) -> Self { 275 | let mut bit_data = value.view_bits::().to_bitvec(); 276 | Alignment { 277 | horizontal: HorizontalAlignment::from([ 278 | bit_data.pop().unwrap(), 279 | bit_data.pop().unwrap(), 280 | ]), 281 | vertical: VerticalAlignment::from([bit_data.pop().unwrap(), bit_data.pop().unwrap()]), 282 | } 283 | } 284 | } 285 | 286 | impl From for u8 { 287 | fn from(value: Alignment) -> Self { 288 | let mut bit_data: BitVec = BitVec::new(); 289 | let horizontal_align: [bool; 2] = value.horizontal.into(); 290 | let vertical_align: [bool; 2] = value.vertical.into(); 291 | 292 | bit_data.push(horizontal_align[0]); 293 | bit_data.push(horizontal_align[1]); 294 | 295 | bit_data.push(vertical_align[0]); 296 | bit_data.push(vertical_align[1]); 297 | 298 | bit_data.load::() 299 | } 300 | } 301 | 302 | #[derive(Debug, Clone, Copy, PartialEq)] 303 | pub enum HorizontalAlignment { 304 | Left = 0, 305 | Middle = 1, 306 | Right = 2, 307 | Reserved = 3, 308 | } 309 | 310 | impl From<[bool; 2]> for HorizontalAlignment { 311 | fn from(value: [bool; 2]) -> Self { 312 | match value[0] { 313 | false => match value[1] { 314 | false => HorizontalAlignment::Left, 315 | true => HorizontalAlignment::Middle, 316 | }, 317 | true => match value[1] { 318 | false => HorizontalAlignment::Middle, 319 | true => HorizontalAlignment::Reserved, 320 | }, 321 | } 322 | } 323 | } 324 | 325 | impl From for [bool; 2] { 326 | fn from(value: HorizontalAlignment) -> Self { 327 | match value { 328 | HorizontalAlignment::Left => [false, false], 329 | HorizontalAlignment::Middle => [false, true], 330 | HorizontalAlignment::Right => [true, false], 331 | HorizontalAlignment::Reserved => [true, true], 332 | } 333 | } 334 | } 335 | 336 | #[derive(Debug, Clone, Copy, PartialEq)] 337 | pub enum VerticalAlignment { 338 | Top = 0, 339 | Middle = 1, 340 | Bottom = 2, 341 | Reserved = 3, 342 | } 343 | 344 | impl From<[bool; 2]> for VerticalAlignment { 345 | fn from(value: [bool; 2]) -> Self { 346 | match value[0] { 347 | false => match value[1] { 348 | false => VerticalAlignment::Top, 349 | true => VerticalAlignment::Middle, 350 | }, 351 | true => match value[1] { 352 | false => VerticalAlignment::Bottom, 353 | true => VerticalAlignment::Reserved, 354 | }, 355 | } 356 | } 357 | } 358 | 359 | impl From for [bool; 2] { 360 | fn from(value: VerticalAlignment) -> Self { 361 | match value { 362 | VerticalAlignment::Top => [false, false], 363 | VerticalAlignment::Middle => [false, true], 364 | VerticalAlignment::Bottom => [true, false], 365 | VerticalAlignment::Reserved => [true, true], 366 | } 367 | } 368 | } 369 | 370 | #[derive(Debug, Clone, Copy, PartialEq)] 371 | pub struct InputNumberOptions { 372 | pub enabled: bool, 373 | pub real_time_editing: bool, 374 | } 375 | 376 | impl From for InputNumberOptions { 377 | fn from(value: u8) -> Self { 378 | let mut bit_data = value.view_bits::().to_bitvec(); 379 | InputNumberOptions { 380 | enabled: bit_data.pop().unwrap(), 381 | real_time_editing: bit_data.pop().unwrap(), 382 | } 383 | } 384 | } 385 | 386 | impl From for u8 { 387 | fn from(value: InputNumberOptions) -> u8 { 388 | let mut bit_data: BitVec = BitVec::new(); 389 | bit_data.push(value.enabled); 390 | bit_data.push(value.real_time_editing); 391 | bit_data.extend([0; 6]); 392 | bit_data.load::() 393 | } 394 | } 395 | 396 | #[derive(Debug, Clone, Copy, PartialEq)] 397 | pub enum FormatType { 398 | Decimal, 399 | Exponential, 400 | } 401 | 402 | impl From for FormatType { 403 | fn from(value: bool) -> Self { 404 | match value { 405 | false => FormatType::Decimal, 406 | true => FormatType::Exponential, 407 | } 408 | } 409 | } 410 | 411 | impl From for bool { 412 | fn from(value: FormatType) -> Self { 413 | match value { 414 | FormatType::Decimal => false, 415 | FormatType::Exponential => true, 416 | } 417 | } 418 | } 419 | 420 | #[derive(Debug, PartialEq, Copy, Clone)] 421 | pub struct InputListOptions { 422 | pub enabled: bool, 423 | pub real_time_editing: bool, 424 | } 425 | 426 | impl From for InputListOptions { 427 | fn from(value: u8) -> Self { 428 | let mut bit_data = value.view_bits::().to_bitvec(); 429 | InputListOptions { 430 | enabled: bit_data.pop().unwrap(), 431 | real_time_editing: bit_data.pop().unwrap(), 432 | } 433 | } 434 | } 435 | 436 | impl From for u8 { 437 | fn from(value: InputListOptions) -> u8 { 438 | let mut bit_data: BitVec = BitVec::new(); 439 | bit_data.push(value.enabled); 440 | bit_data.push(value.real_time_editing); 441 | bit_data.extend([0; 6]); 442 | bit_data.load::() 443 | } 444 | } 445 | 446 | #[derive(Debug, PartialEq, Copy, Clone)] 447 | pub struct OutputStringOptions { 448 | pub transparent: bool, 449 | pub auto_wrap: bool, 450 | pub wrap_on_hyphen: bool, 451 | } 452 | 453 | impl From for OutputStringOptions { 454 | fn from(value: u8) -> Self { 455 | let mut bit_data = value.view_bits::().to_bitvec(); 456 | OutputStringOptions { 457 | transparent: bit_data.pop().unwrap(), 458 | auto_wrap: bit_data.pop().unwrap(), 459 | wrap_on_hyphen: bit_data.pop().unwrap(), 460 | } 461 | } 462 | } 463 | 464 | #[derive(Debug, PartialEq, Clone, Copy)] 465 | pub struct NumberOptions { 466 | pub transparent: bool, 467 | pub display_leading_zeros: bool, 468 | pub display_zero_as_blank: bool, 469 | pub truncate: bool, 470 | } 471 | 472 | impl From for NumberOptions { 473 | fn from(value: u8) -> Self { 474 | let mut bit_data = value.view_bits::().to_bitvec(); 475 | NumberOptions { 476 | transparent: bit_data.pop().unwrap(), 477 | display_leading_zeros: bit_data.pop().unwrap(), 478 | display_zero_as_blank: bit_data.pop().unwrap(), 479 | truncate: bit_data.pop().unwrap(), 480 | } 481 | } 482 | } 483 | 484 | #[derive(Debug, PartialEq, Copy, Clone)] 485 | pub enum LineDirection { 486 | TopLeftToBottomRight, 487 | BottomLeftToTopRight, 488 | } 489 | 490 | impl From for LineDirection { 491 | fn from(value: u8) -> Self { 492 | match value { 493 | 0 => LineDirection::TopLeftToBottomRight, 494 | 1 => LineDirection::BottomLeftToTopRight, 495 | _ => panic!("Invalid line direction"), 496 | } 497 | } 498 | } 499 | 500 | impl From for u8 { 501 | fn from(value: LineDirection) -> Self { 502 | match value { 503 | LineDirection::TopLeftToBottomRight => 0, 504 | LineDirection::BottomLeftToTopRight => 1, 505 | } 506 | } 507 | } 508 | 509 | impl From for u8 { 510 | fn from(value: NumberOptions) -> Self { 511 | let mut bit_data: BitVec = BitVec::new(); 512 | bit_data.push(value.transparent); 513 | bit_data.push(value.display_leading_zeros); 514 | bit_data.push(value.display_zero_as_blank); 515 | bit_data.push(value.truncate); 516 | bit_data.extend([0; 4]); 517 | bit_data.load::() 518 | } 519 | } 520 | 521 | impl From for u8 { 522 | fn from(value: OutputStringOptions) -> u8 { 523 | let mut bit_data: BitVec = BitVec::new(); 524 | bit_data.push(value.transparent); 525 | bit_data.push(value.auto_wrap); 526 | bit_data.push(value.wrap_on_hyphen); 527 | bit_data.extend([0; 5]); 528 | bit_data.load::() 529 | } 530 | } 531 | 532 | #[derive(Debug, PartialEq, Copy, Clone)] 533 | pub enum ColorFormat { 534 | ColorMonochrome, 535 | Color4Bit, 536 | Color8Bit, 537 | } 538 | 539 | impl From for u8 { 540 | fn from(value: ColorFormat) -> Self { 541 | match value { 542 | ColorFormat::ColorMonochrome => 0, 543 | ColorFormat::Color4Bit => 1, 544 | ColorFormat::Color8Bit => 2, 545 | } 546 | } 547 | } 548 | 549 | impl From for ColorFormat { 550 | fn from(value: u8) -> Self { 551 | match value { 552 | 0 => ColorFormat::ColorMonochrome, 553 | 1 => ColorFormat::Color4Bit, 554 | 2 => ColorFormat::Color8Bit, 555 | _ => panic!("Invalid color format: {}", value), 556 | } 557 | } 558 | } 559 | 560 | #[derive(Debug, PartialEq, Copy, Clone)] 561 | pub enum ColorOption { 562 | ForegroundBackground, 563 | LineFontFill, 564 | } 565 | 566 | impl From for ColorOption { 567 | fn from(value: bool) -> Self { 568 | match value { 569 | false => ColorOption::ForegroundBackground, 570 | true => ColorOption::LineFontFill, 571 | } 572 | } 573 | } 574 | 575 | impl From for bool { 576 | fn from(value: ColorOption) -> Self { 577 | match value { 578 | ColorOption::ForegroundBackground => false, 579 | ColorOption::LineFontFill => true, 580 | } 581 | } 582 | } 583 | 584 | #[derive(Debug, PartialEq, Copy, Clone)] 585 | pub struct GraphicsContextOptions { 586 | pub transparent: bool, 587 | pub color: ColorOption, 588 | } 589 | 590 | impl From for GraphicsContextOptions { 591 | fn from(value: u8) -> Self { 592 | let mut bit_data = value.view_bits::().to_bitvec(); 593 | GraphicsContextOptions { 594 | transparent: bit_data.pop().unwrap(), 595 | color: bit_data.pop().unwrap().into(), 596 | } 597 | } 598 | } 599 | 600 | impl From for u8 { 601 | fn from(value: GraphicsContextOptions) -> u8 { 602 | let mut bit_data: BitVec = BitVec::new(); 603 | bit_data.push(value.transparent); 604 | bit_data.push(value.color.into()); 605 | bit_data.extend([0; 6]); 606 | bit_data.load::() 607 | } 608 | } 609 | 610 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 611 | pub struct KeyGroupOptions { 612 | pub available: bool, 613 | pub transparent: bool, 614 | } 615 | 616 | impl From for KeyGroupOptions { 617 | fn from(value: u8) -> Self { 618 | let mut bit_data = value.view_bits::().to_bitvec(); 619 | KeyGroupOptions { 620 | available: bit_data.pop().unwrap(), 621 | transparent: bit_data.pop().unwrap(), 622 | } 623 | } 624 | } 625 | 626 | impl From for u8 { 627 | fn from(value: KeyGroupOptions) -> u8 { 628 | let mut bit_data: BitVec = BitVec::new(); 629 | bit_data.push(value.available); 630 | bit_data.push(value.transparent); 631 | bit_data.extend([0; 6]); 632 | bit_data.load::() 633 | } 634 | } 635 | 636 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 637 | pub enum DeflectionDirection { 638 | AntiClockwise, 639 | Clockwise, 640 | } 641 | 642 | impl From for DeflectionDirection { 643 | fn from(value: bool) -> Self { 644 | match value { 645 | false => DeflectionDirection::AntiClockwise, 646 | true => DeflectionDirection::Clockwise, 647 | } 648 | } 649 | } 650 | 651 | impl From for bool { 652 | fn from(value: DeflectionDirection) -> Self { 653 | match value { 654 | DeflectionDirection::AntiClockwise => false, 655 | DeflectionDirection::Clockwise => true, 656 | } 657 | } 658 | } 659 | 660 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 661 | pub struct OutputMeterOptions { 662 | pub draw_arc: bool, 663 | pub draw_border: bool, 664 | pub draw_ticks: bool, 665 | pub deflection_direction: DeflectionDirection, 666 | } 667 | 668 | impl From for OutputMeterOptions { 669 | fn from(value: u8) -> Self { 670 | let mut bit_data = value.view_bits::().to_bitvec(); 671 | OutputMeterOptions { 672 | draw_arc: bit_data.pop().unwrap(), 673 | draw_border: bit_data.pop().unwrap(), 674 | draw_ticks: bit_data.pop().unwrap(), 675 | deflection_direction: bit_data.pop().unwrap().into(), 676 | } 677 | } 678 | } 679 | 680 | impl From for u8 { 681 | fn from(value: OutputMeterOptions) -> u8 { 682 | let mut bit_data: BitVec = BitVec::new(); 683 | bit_data.push(value.draw_arc); 684 | bit_data.push(value.draw_border); 685 | bit_data.push(value.draw_ticks); 686 | bit_data.push(value.deflection_direction.into()); 687 | bit_data.extend([0; 4]); 688 | bit_data.load::() 689 | } 690 | } 691 | 692 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 693 | pub enum BarGraphType { 694 | Filled, 695 | NotFilled, 696 | } 697 | 698 | impl From for BarGraphType { 699 | fn from(value: bool) -> Self { 700 | match value { 701 | false => BarGraphType::Filled, 702 | true => BarGraphType::NotFilled, 703 | } 704 | } 705 | } 706 | 707 | impl From for bool { 708 | fn from(value: BarGraphType) -> Self { 709 | match value { 710 | BarGraphType::Filled => false, 711 | BarGraphType::NotFilled => true, 712 | } 713 | } 714 | } 715 | 716 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 717 | pub enum AxisOrientation { 718 | Vertical, 719 | Horizontal, 720 | } 721 | 722 | impl From for AxisOrientation { 723 | fn from(value: bool) -> Self { 724 | match value { 725 | false => AxisOrientation::Vertical, 726 | true => AxisOrientation::Horizontal, 727 | } 728 | } 729 | } 730 | 731 | impl From for bool { 732 | fn from(value: AxisOrientation) -> Self { 733 | match value { 734 | AxisOrientation::Vertical => false, 735 | AxisOrientation::Horizontal => true, 736 | } 737 | } 738 | } 739 | 740 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 741 | pub enum GrowDirection { 742 | GrowLeftDown, 743 | GrowRightUp, 744 | } 745 | 746 | impl From for GrowDirection { 747 | fn from(value: bool) -> Self { 748 | match value { 749 | false => GrowDirection::GrowLeftDown, 750 | true => GrowDirection::GrowRightUp, 751 | } 752 | } 753 | } 754 | 755 | impl From for bool { 756 | fn from(value: GrowDirection) -> Self { 757 | match value { 758 | GrowDirection::GrowLeftDown => false, 759 | GrowDirection::GrowRightUp => true, 760 | } 761 | } 762 | } 763 | 764 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 765 | pub struct OutputLinearBarGraphOptions { 766 | pub draw_border: bool, 767 | pub draw_target_line: bool, 768 | pub draw_ticks: bool, 769 | pub bar_graph_type: BarGraphType, 770 | pub axis_orientation: AxisOrientation, 771 | pub grow_direction: GrowDirection, 772 | } 773 | 774 | impl From for OutputLinearBarGraphOptions { 775 | fn from(value: u8) -> Self { 776 | let mut bit_data = value.view_bits::().to_bitvec(); 777 | OutputLinearBarGraphOptions { 778 | draw_border: bit_data.pop().unwrap(), 779 | draw_target_line: bit_data.pop().unwrap(), 780 | draw_ticks: bit_data.pop().unwrap(), 781 | bar_graph_type: bit_data.pop().unwrap().into(), 782 | axis_orientation: bit_data.pop().unwrap().into(), 783 | grow_direction: bit_data.pop().unwrap().into(), 784 | } 785 | } 786 | } 787 | 788 | impl From for u8 { 789 | fn from(value: OutputLinearBarGraphOptions) -> u8 { 790 | let mut bit_data: BitVec = BitVec::new(); 791 | bit_data.push(value.draw_border); 792 | bit_data.push(value.draw_target_line); 793 | bit_data.push(value.draw_ticks); 794 | bit_data.push(value.bar_graph_type.into()); 795 | bit_data.push(value.axis_orientation.into()); 796 | bit_data.push(value.grow_direction.into()); 797 | bit_data.extend([0; 2]); 798 | bit_data.load::() 799 | } 800 | } 801 | 802 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 803 | pub struct OutputArchedBarGraphOptions { 804 | pub draw_border: bool, 805 | pub draw_target_line: bool, 806 | pub bar_graph_type: BarGraphType, 807 | pub axis_orientation: AxisOrientation, 808 | pub grow_direction: GrowDirection, 809 | pub deflection_direction: DeflectionDirection, 810 | } 811 | 812 | impl From for OutputArchedBarGraphOptions { 813 | fn from(value: u8) -> Self { 814 | let mut bit_data = value.view_bits::().to_bitvec(); 815 | let draw_border = bit_data.pop().unwrap(); 816 | let draw_target_line = bit_data.pop().unwrap(); 817 | bit_data.pop(); //undefined bit 818 | 819 | OutputArchedBarGraphOptions { 820 | draw_border, 821 | draw_target_line, 822 | bar_graph_type: bit_data.pop().unwrap().into(), 823 | axis_orientation: bit_data.pop().unwrap().into(), 824 | grow_direction: bit_data.pop().unwrap().into(), 825 | deflection_direction: bit_data.pop().unwrap().into(), 826 | } 827 | } 828 | } 829 | 830 | impl From for u8 { 831 | fn from(value: OutputArchedBarGraphOptions) -> u8 { 832 | let mut bit_data: BitVec = BitVec::new(); 833 | bit_data.push(value.draw_border); 834 | bit_data.push(value.draw_target_line); 835 | bit_data.push(false); //undefined bit 836 | bit_data.push(value.bar_graph_type.into()); 837 | bit_data.push(value.axis_orientation.into()); 838 | bit_data.push(value.grow_direction.into()); 839 | bit_data.push(value.deflection_direction.into()); 840 | bit_data.extend([0; 1]); 841 | bit_data.load::() 842 | } 843 | } 844 | 845 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 846 | pub enum DataCodeType { 847 | Raw, 848 | RunLength, 849 | } 850 | 851 | impl From for DataCodeType { 852 | fn from(value: bool) -> Self { 853 | match value { 854 | false => DataCodeType::Raw, 855 | true => DataCodeType::RunLength, 856 | } 857 | } 858 | } 859 | 860 | impl From for bool { 861 | fn from(value: DataCodeType) -> Self { 862 | match value { 863 | DataCodeType::Raw => false, 864 | DataCodeType::RunLength => true, 865 | } 866 | } 867 | } 868 | 869 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 870 | pub struct PictureGraphicOptions { 871 | pub transparent: bool, 872 | pub flashing: bool, 873 | pub data_code_type: DataCodeType, 874 | } 875 | 876 | impl From for PictureGraphicOptions { 877 | fn from(value: u8) -> Self { 878 | let mut bit_data = value.view_bits::().to_bitvec(); 879 | PictureGraphicOptions { 880 | transparent: bit_data.pop().unwrap(), 881 | flashing: bit_data.pop().unwrap(), 882 | data_code_type: bit_data.pop().unwrap().into(), 883 | } 884 | } 885 | } 886 | 887 | impl From for u8 { 888 | fn from(value: PictureGraphicOptions) -> u8 { 889 | let mut bit_data: BitVec = BitVec::new(); 890 | bit_data.push(value.transparent); 891 | bit_data.push(value.flashing); 892 | bit_data.push(value.data_code_type.into()); 893 | bit_data.extend([0; 5]); 894 | bit_data.load::() 895 | } 896 | } 897 | 898 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 899 | pub struct ExternalObjectDefinitionOptions { 900 | pub enabled: bool, 901 | } 902 | 903 | impl From for ExternalObjectDefinitionOptions { 904 | fn from(value: u8) -> Self { 905 | let mut bit_data = value.view_bits::().to_bitvec(); 906 | ExternalObjectDefinitionOptions { 907 | enabled: bit_data.pop().unwrap(), 908 | } 909 | } 910 | } 911 | 912 | impl From for u8 { 913 | fn from(value: ExternalObjectDefinitionOptions) -> u8 { 914 | let mut bit_data: BitVec = BitVec::new(); 915 | bit_data.push(value.enabled); 916 | bit_data.extend([0; 7]); 917 | bit_data.load::() 918 | } 919 | } 920 | 921 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 922 | pub struct ExternalReferenceNameOptions { 923 | pub enabled: bool, 924 | } 925 | 926 | impl From for ExternalReferenceNameOptions { 927 | fn from(value: u8) -> Self { 928 | let mut bit_data = value.view_bits::().to_bitvec(); 929 | ExternalReferenceNameOptions { 930 | enabled: bit_data.pop().unwrap(), 931 | } 932 | } 933 | } 934 | 935 | impl From for u8 { 936 | fn from(value: ExternalReferenceNameOptions) -> u8 { 937 | let mut bit_data: BitVec = BitVec::new(); 938 | bit_data.push(value.enabled); 939 | bit_data.extend([0; 7]); 940 | bit_data.load::() 941 | } 942 | } 943 | 944 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 945 | pub enum AnimationSequence { 946 | SingleShot, 947 | Loop, 948 | } 949 | 950 | impl From for AnimationSequence { 951 | fn from(value: bool) -> Self { 952 | match value { 953 | false => AnimationSequence::SingleShot, 954 | true => AnimationSequence::Loop, 955 | } 956 | } 957 | } 958 | 959 | impl From for bool { 960 | fn from(value: AnimationSequence) -> Self { 961 | match value { 962 | AnimationSequence::SingleShot => false, 963 | AnimationSequence::Loop => true, 964 | } 965 | } 966 | } 967 | 968 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 969 | pub enum DisabledBehaviour { 970 | Pause, 971 | ResetToFirst, 972 | DefaultObject, 973 | Blank, 974 | } 975 | 976 | impl From<[bool; 2]> for DisabledBehaviour { 977 | fn from(value: [bool; 2]) -> Self { 978 | match value { 979 | [false, false] => DisabledBehaviour::Pause, 980 | [false, true] => DisabledBehaviour::ResetToFirst, 981 | [true, false] => DisabledBehaviour::DefaultObject, 982 | [true, true] => DisabledBehaviour::Blank, 983 | } 984 | } 985 | } 986 | 987 | impl From for [bool; 2] { 988 | fn from(value: DisabledBehaviour) -> Self { 989 | match value { 990 | DisabledBehaviour::Pause => [false, false], 991 | DisabledBehaviour::ResetToFirst => [false, true], 992 | DisabledBehaviour::DefaultObject => [true, false], 993 | DisabledBehaviour::Blank => [true, true], 994 | } 995 | } 996 | } 997 | 998 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 999 | pub struct AnimationOptions { 1000 | pub animation_sequence: AnimationSequence, 1001 | pub disabled_behaviour: DisabledBehaviour, 1002 | } 1003 | 1004 | impl From for AnimationOptions { 1005 | fn from(value: u8) -> Self { 1006 | let mut bit_data = value.view_bits::().to_bitvec(); 1007 | AnimationOptions { 1008 | animation_sequence: bit_data.pop().unwrap().into(), 1009 | disabled_behaviour: DisabledBehaviour::from([ 1010 | bit_data.pop().unwrap(), 1011 | bit_data.pop().unwrap(), 1012 | ]), 1013 | } 1014 | } 1015 | } 1016 | 1017 | impl From for u8 { 1018 | fn from(value: AnimationOptions) -> u8 { 1019 | let mut bit_data: BitVec = BitVec::new(); 1020 | bit_data.push(value.animation_sequence.into()); 1021 | let disabled_behaviour: [bool; 2] = value.disabled_behaviour.into(); 1022 | bit_data.push(disabled_behaviour[0]); 1023 | bit_data.push(disabled_behaviour[1]); 1024 | bit_data.extend([0; 5]); 1025 | bit_data.load::() 1026 | } 1027 | } 1028 | 1029 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 1030 | pub struct ColourPaletteOptions {} 1031 | 1032 | impl From for ColourPaletteOptions { 1033 | fn from(value: u8) -> Self { 1034 | let mut _bit_data = value.view_bits::().to_bitvec(); 1035 | ColourPaletteOptions {} 1036 | } 1037 | } 1038 | 1039 | impl From for u8 { 1040 | fn from(_value: ColourPaletteOptions) -> u8 { 1041 | let mut bit_data: BitVec = BitVec::new(); 1042 | bit_data.extend([0; 8]); 1043 | bit_data.load::() 1044 | } 1045 | } 1046 | 1047 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 1048 | pub struct ScaledGraphicOptions { 1049 | pub flashing: bool, 1050 | } 1051 | 1052 | impl From for ScaledGraphicOptions { 1053 | fn from(value: u8) -> Self { 1054 | let mut bit_data = value.view_bits::().to_bitvec(); 1055 | ScaledGraphicOptions { 1056 | flashing: bit_data.pop().unwrap(), 1057 | } 1058 | } 1059 | } 1060 | 1061 | impl From for u8 { 1062 | fn from(value: ScaledGraphicOptions) -> u8 { 1063 | let mut bit_data: BitVec = BitVec::new(); 1064 | bit_data.push(value.flashing); 1065 | bit_data.extend([0; 7]); 1066 | bit_data.load::() 1067 | } 1068 | } 1069 | -------------------------------------------------------------------------------- /src/object_pool/writer.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::object_pool::colour::Colour; 3 | use crate::object_pool::object::{ 4 | AlarmMask, Animation, AuxiliaryControlDesignatorType2, AuxiliaryFunctionType1, 5 | AuxiliaryFunctionType2, AuxiliaryInputType1, AuxiliaryInputType2, Button, CharacterRange, 6 | CodePlane, ColourMap, ColourPalette, Container, DataMask, ExtendedInputAttributes, 7 | ExternalObjectDefinition, ExternalObjectPointer, ExternalReferenceName, FillAttributes, 8 | FontAttributes, GraphicData, GraphicsContext, InputAttributes, InputBoolean, InputList, 9 | InputNumber, InputString, Key, KeyGroup, LineAttributes, Macro, NumberVariable, Object, 10 | ObjectLabelReferenceList, ObjectPointer, OutputArchedBarGraph, OutputEllipse, OutputLine, 11 | OutputLinearBarGraph, OutputList, OutputMeter, OutputNumber, OutputPolygon, OutputRectangle, 12 | OutputString, PictureGraphic, ScaledGraphic, SoftKeyMask, StringVariable, WindowMask, 13 | WorkingSet, WorkingSetSpecialControls, 14 | }; 15 | use crate::object_pool::object_attributes::{MacroRef, ObjectLabel, ObjectRef, Point}; 16 | use crate::object_pool::object_id::ObjectId; 17 | 18 | impl Object { 19 | pub fn write(&self) -> Vec { 20 | let mut data = Vec::new(); 21 | 22 | match self { 23 | Object::WorkingSet(o) => Self::write_working_set(&mut data, o), 24 | Object::DataMask(o) => Self::write_data_mask(&mut data, o), 25 | Object::AlarmMask(o) => Self::write_alarm_mask(&mut data, o), 26 | Object::Container(o) => Self::write_container(&mut data, o), 27 | Object::SoftKeyMask(o) => Self::write_soft_key_mask(&mut data, o), 28 | Object::Key(o) => Self::write_key(&mut data, o), 29 | Object::Button(o) => Self::write_button(&mut data, o), 30 | Object::InputBoolean(o) => Self::write_input_boolean(&mut data, o), 31 | Object::InputString(o) => Self::write_input_string(&mut data, o), 32 | Object::InputNumber(o) => Self::write_input_number(&mut data, o), 33 | Object::InputList(o) => Self::write_input_list(&mut data, o), 34 | Object::OutputString(o) => Self::write_output_string(&mut data, o), 35 | Object::OutputNumber(o) => Self::write_output_number(&mut data, o), 36 | Object::OutputLine(o) => Self::write_output_line(&mut data, o), 37 | Object::OutputRectangle(o) => Self::write_output_rectangle(&mut data, o), 38 | Object::OutputEllipse(o) => Self::write_output_ellipse(&mut data, o), 39 | Object::OutputPolygon(o) => Self::write_output_polygon(&mut data, o), 40 | Object::OutputMeter(o) => Self::write_output_meter(&mut data, o), 41 | Object::OutputLinearBarGraph(o) => Self::write_output_linear_bar_graph(&mut data, o), 42 | Object::OutputArchedBarGraph(o) => Self::write_output_arched_bar_graph(&mut data, o), 43 | Object::PictureGraphic(o) => Self::write_picture_graphic(&mut data, o), 44 | Object::NumberVariable(o) => Self::write_number_variable(&mut data, o), 45 | Object::StringVariable(o) => Self::write_string_variable(&mut data, o), 46 | Object::FontAttributes(o) => Self::write_font_attributes(&mut data, o), 47 | Object::LineAttributes(o) => Self::write_line_attributes(&mut data, o), 48 | Object::FillAttributes(o) => Self::write_fill_attributes(&mut data, o), 49 | Object::InputAttributes(o) => Self::write_input_attributes(&mut data, o), 50 | Object::ObjectPointer(o) => Self::write_object_pointer(&mut data, o), 51 | Object::Macro(o) => Self::write_macro(&mut data, o), 52 | Object::AuxiliaryFunctionType1(o) => Self::write_auxiliary_function_type1(&mut data, o), 53 | Object::AuxiliaryInputType1(o) => Self::write_auxiliary_input_type1(&mut data, o), 54 | Object::AuxiliaryFunctionType2(o) => Self::write_auxiliary_function_type2(&mut data, o), 55 | Object::AuxiliaryInputType2(o) => Self::write_auxiliary_input_type2(&mut data, o), 56 | Object::AuxiliaryControlDesignatorType2(o) => { 57 | Self::write_auxiliary_control_designator_type2(&mut data, o) 58 | } 59 | Object::WindowMask(o) => Self::write_window_mask(&mut data, o), 60 | Object::KeyGroup(o) => Self::write_key_group(&mut data, o), 61 | Object::GraphicsContext(o) => Self::write_graphics_context(&mut data, o), 62 | Object::OutputList(o) => Self::write_output_list(&mut data, o), 63 | Object::ExtendedInputAttributes(o) => { 64 | Self::write_extended_input_attributes(&mut data, o) 65 | } 66 | Object::ColourMap(o) => Self::write_colour_map(&mut data, o), 67 | Object::ObjectLabelReferenceList(o) => { 68 | Self::write_object_label_reference_list(&mut data, o) 69 | } 70 | Object::ExternalObjectDefinition(o) => { 71 | Self::write_external_object_definition(&mut data, o) 72 | } 73 | Object::ExternalReferenceName(o) => Self::write_external_reference_name(&mut data, o), 74 | Object::ExternalObjectPointer(o) => Self::write_external_object_pointer(&mut data, o), 75 | Object::Animation(o) => Self::write_animation(&mut data, o), 76 | Object::ColourPalette(o) => Self::write_colour_palette(&mut data, o), 77 | Object::GraphicData(o) => Self::write_graphic_data(&mut data, o), 78 | Object::WorkingSetSpecialControls(o) => { 79 | Self::write_working_set_special_controls(&mut data, o) 80 | } 81 | Object::ScaledGraphic(o) => Self::write_scaled_graphic(&mut data, o), 82 | } 83 | 84 | data 85 | } 86 | 87 | fn write_objects(data: &mut Vec, objects: &Vec) { 88 | for d in objects { 89 | Self::write_u16(data, *d); 90 | } 91 | } 92 | fn write_object_refs(data: &mut Vec, object_refs: &Vec) { 93 | for d in object_refs { 94 | Self::write_u16(data, d.id); 95 | Self::write_i16(data, d.offset.x); 96 | Self::write_i16(data, d.offset.y); 97 | } 98 | } 99 | fn write_macro_refs(data: &mut Vec, macro_refs: &Vec) { 100 | for d in macro_refs { 101 | Self::write_u8(data, d.event_id); 102 | Self::write_u8(data, d.macro_id); 103 | } 104 | } 105 | fn write_bytes(data: &mut Vec, bytes: &Vec) { 106 | for d in bytes { 107 | Self::write_u8(data, *d); 108 | } 109 | } 110 | fn write_language_codes(data: &mut Vec, language_codes: &Vec) { 111 | for d in language_codes { 112 | Self::write_string(data, d); 113 | } 114 | } 115 | fn write_points(data: &mut Vec, points: &Vec>) { 116 | for d in points { 117 | Self::write_u16(data, d.x); 118 | Self::write_u16(data, d.y); 119 | } 120 | } 121 | fn write_colours(data: &mut Vec, colours: &Vec) { 122 | for d in colours { 123 | Self::write_u8(data, d.b); 124 | Self::write_u8(data, d.g); 125 | Self::write_u8(data, d.r); 126 | Self::write_u8(data, d.a); 127 | } 128 | } 129 | fn write_object_labels(data: &mut Vec, object_labels: &Vec) { 130 | for d in object_labels { 131 | Self::write_u16(data, d.id); 132 | Self::write_u16(data, d.string_variable_reference); 133 | Self::write_u8(data, d.font_type); 134 | Self::write_u16(data, d.graphic_representation); 135 | } 136 | } 137 | fn write_language_pairs(data: &mut Vec, language_pairs: &Vec<(String, String)>) { 138 | for d in language_pairs { 139 | Self::write_string(data, &d.0); 140 | Self::write_string(data, &d.1); 141 | } 142 | } 143 | 144 | fn write_character_ranges(data: &mut Vec, character_ranges: &Vec) { 145 | for character_range in character_ranges { 146 | Self::write_u16(data, character_range.first_character); 147 | Self::write_u16(data, character_range.last_character); 148 | } 149 | } 150 | 151 | fn write_code_planes(data: &mut Vec, code_planes: &Vec) { 152 | Self::write_u8(data, code_planes.len() as u8); 153 | 154 | for code_plane in code_planes { 155 | Self::write_u8(data, code_plane.number); 156 | Self::write_u8(data, code_plane.character_ranges.len() as u8); 157 | Self::write_character_ranges(data, &code_plane.character_ranges); 158 | } 159 | } 160 | 161 | fn write_u8(data: &mut Vec, val: impl Into) { 162 | let val: u8 = val.into(); 163 | data.push(val); 164 | } 165 | fn write_u16(data: &mut Vec, val: impl Into) { 166 | let val: u16 = val.into(); 167 | data.extend(val.to_le_bytes()); 168 | } 169 | fn write_i16(data: &mut Vec, val: impl Into) { 170 | let val: i16 = val.into(); 171 | data.extend(val.to_le_bytes()); 172 | } 173 | fn write_u32(data: &mut Vec, val: impl Into) { 174 | let val: u32 = val.into(); 175 | data.extend(val.to_le_bytes()); 176 | } 177 | fn write_i32(data: &mut Vec, val: impl Into) { 178 | let val: i32 = val.into(); 179 | data.extend(val.to_le_bytes()); 180 | } 181 | fn write_f32(data: &mut Vec, val: impl Into) { 182 | let val: f32 = val.into(); 183 | data.extend(val.to_le_bytes()); 184 | } 185 | fn write_string(data: &mut Vec, val: impl Into) { 186 | let val: String = val.into(); 187 | data.extend(val.as_bytes()); 188 | } 189 | fn write_name(data: &mut Vec, val: impl Into) { 190 | let val: NAME = val.into(); 191 | data.extend::<[u8; 8]>(val.into()); 192 | } 193 | fn write_working_set(data: &mut Vec, o: &WorkingSet) { 194 | Self::write_u16(data, o.id); 195 | Self::write_u8(data, ObjectType::WorkingSet); 196 | Self::write_u8(data, o.background_colour); 197 | Self::write_u8(data, o.selectable); 198 | Self::write_u16(data, o.active_mask); 199 | Self::write_u8(data, o.object_refs.len() as u8); 200 | Self::write_u8(data, o.macro_refs.len() as u8); 201 | Self::write_u8(data, o.language_codes.len() as u8); 202 | 203 | Self::write_object_refs(data, &o.object_refs); 204 | Self::write_macro_refs(data, &o.macro_refs); 205 | Self::write_language_codes(data, &o.language_codes); 206 | } 207 | fn write_data_mask(data: &mut Vec, o: &DataMask) { 208 | Self::write_u16(data, o.id); 209 | Self::write_u8(data, ObjectType::DataMask); 210 | Self::write_u8(data, o.background_colour); 211 | Self::write_u16(data, o.soft_key_mask); 212 | Self::write_u8(data, o.object_refs.len() as u8); 213 | Self::write_u8(data, o.macro_refs.len() as u8); 214 | 215 | Self::write_object_refs(data, &o.object_refs); 216 | Self::write_macro_refs(data, &o.macro_refs); 217 | } 218 | fn write_alarm_mask(data: &mut Vec, o: &AlarmMask) { 219 | Self::write_u16(data, o.id); 220 | Self::write_u8(data, ObjectType::AlarmMask); 221 | Self::write_u8(data, o.background_colour); 222 | Self::write_u16(data, o.soft_key_mask); 223 | Self::write_u8(data, o.priority); 224 | Self::write_u8(data, o.acoustic_signal); 225 | Self::write_u8(data, o.object_refs.len() as u8); 226 | Self::write_u8(data, o.macro_refs.len() as u8); 227 | 228 | Self::write_object_refs(data, &o.object_refs); 229 | Self::write_macro_refs(data, &o.macro_refs); 230 | } 231 | fn write_container(data: &mut Vec, o: &Container) { 232 | Self::write_u16(data, o.id); 233 | Self::write_u8(data, ObjectType::Container); 234 | Self::write_u16(data, o.width); 235 | Self::write_u16(data, o.height); 236 | Self::write_u8(data, o.hidden); 237 | Self::write_u8(data, o.object_refs.len() as u8); 238 | Self::write_u8(data, o.macro_refs.len() as u8); 239 | 240 | Self::write_object_refs(data, &o.object_refs); 241 | Self::write_macro_refs(data, &o.macro_refs); 242 | } 243 | fn write_soft_key_mask(data: &mut Vec, o: &SoftKeyMask) { 244 | Self::write_u16(data, o.id); 245 | Self::write_u8(data, ObjectType::SoftKeyMask); 246 | Self::write_u8(data, o.background_colour); 247 | Self::write_u8(data, o.objects.len() as u8); 248 | Self::write_u8(data, o.macro_refs.len() as u8); 249 | 250 | Self::write_objects(data, &o.objects); 251 | Self::write_macro_refs(data, &o.macro_refs); 252 | } 253 | fn write_key(data: &mut Vec, o: &Key) { 254 | Self::write_u16(data, o.id); 255 | Self::write_u8(data, ObjectType::Key); 256 | Self::write_u8(data, o.background_colour); 257 | Self::write_u8(data, o.key_code); 258 | Self::write_u8(data, o.object_refs.len() as u8); 259 | Self::write_u8(data, o.macro_refs.len() as u8); 260 | 261 | Self::write_object_refs(data, &o.object_refs); 262 | Self::write_macro_refs(data, &o.macro_refs); 263 | } 264 | fn write_button(data: &mut Vec, o: &Button) { 265 | Self::write_u16(data, o.id); 266 | Self::write_u8(data, ObjectType::Button); 267 | Self::write_u16(data, o.width); 268 | Self::write_u16(data, o.height); 269 | Self::write_u8(data, o.background_colour); 270 | Self::write_u8(data, o.border_colour); 271 | Self::write_u8(data, o.key_code); 272 | Self::write_u8(data, o.options); 273 | Self::write_u8(data, o.object_refs.len() as u8); 274 | Self::write_u8(data, o.macro_refs.len() as u8); 275 | 276 | Self::write_object_refs(data, &o.object_refs); 277 | Self::write_macro_refs(data, &o.macro_refs); 278 | } 279 | fn write_input_boolean(data: &mut Vec, o: &InputBoolean) { 280 | Self::write_u16(data, o.id); 281 | Self::write_u8(data, ObjectType::InputBoolean); 282 | Self::write_u8(data, o.background_colour); 283 | Self::write_u16(data, o.width); 284 | Self::write_u16(data, o.foreground_colour); 285 | Self::write_u16(data, o.variable_reference); 286 | Self::write_u8(data, o.value); 287 | Self::write_u8(data, o.enabled); 288 | Self::write_u8(data, o.macro_refs.len() as u8); 289 | 290 | Self::write_macro_refs(data, &o.macro_refs); 291 | } 292 | fn write_input_string(data: &mut Vec, o: &InputString) { 293 | Self::write_u16(data, o.id); 294 | Self::write_u8(data, ObjectType::InputString); 295 | Self::write_u16(data, o.width); 296 | Self::write_u16(data, o.height); 297 | Self::write_u8(data, o.background_colour); 298 | Self::write_u16(data, o.font_attributes); 299 | Self::write_u16(data, o.input_attributes); 300 | Self::write_u8(data, o.options); 301 | Self::write_u16(data, o.variable_reference); 302 | Self::write_u8(data, o.justification); 303 | Self::write_string(data, &o.value); 304 | Self::write_u8(data, o.enabled); 305 | Self::write_u8(data, o.macro_refs.len() as u8); 306 | 307 | Self::write_macro_refs(data, &o.macro_refs); 308 | } 309 | fn write_input_number(data: &mut Vec, o: &InputNumber) { 310 | Self::write_u16(data, o.id); 311 | Self::write_u8(data, ObjectType::InputNumber); 312 | Self::write_u16(data, o.width); 313 | Self::write_u16(data, o.height); 314 | Self::write_u8(data, o.background_colour); 315 | Self::write_u16(data, o.font_attributes); 316 | Self::write_u8(data, o.options); 317 | Self::write_u16(data, o.variable_reference); 318 | Self::write_u32(data, o.value); 319 | Self::write_u32(data, o.min_value); 320 | Self::write_u32(data, o.max_value); 321 | Self::write_i32(data, o.offset); 322 | Self::write_f32(data, o.scale); 323 | Self::write_u8(data, o.nr_of_decimals); 324 | Self::write_u8(data, o.format as u8); 325 | Self::write_u8(data, o.justification); 326 | Self::write_u8(data, o.options2); 327 | Self::write_u8(data, o.macro_refs.len() as u8); 328 | 329 | Self::write_macro_refs(data, &o.macro_refs); 330 | } 331 | fn write_input_list(data: &mut Vec, o: &InputList) { 332 | Self::write_u16(data, o.id); 333 | Self::write_u8(data, ObjectType::InputList); 334 | Self::write_u16(data, o.width); 335 | Self::write_u16(data, o.height); 336 | Self::write_u16(data, o.variable_reference); 337 | Self::write_u8(data, o.value); 338 | Self::write_u8(data, o.list_items.len() as u8); 339 | Self::write_u8(data, o.options); 340 | Self::write_u8(data, o.macro_refs.len() as u8); 341 | 342 | Self::write_objects(data, &o.list_items); 343 | Self::write_macro_refs(data, &o.macro_refs); 344 | } 345 | fn write_output_string(data: &mut Vec, o: &OutputString) { 346 | Self::write_u16(data, o.id); 347 | Self::write_u8(data, ObjectType::OutputString); 348 | Self::write_u16(data, o.width); 349 | Self::write_u16(data, o.height); 350 | Self::write_u8(data, o.background_colour); 351 | Self::write_u16(data, o.font_attributes); 352 | Self::write_u8(data, o.options); 353 | Self::write_u16(data, o.variable_reference); 354 | Self::write_u8(data, o.justification); 355 | Self::write_u16(data, o.value.len() as u16); 356 | Self::write_string(data, &o.value); 357 | Self::write_u8(data, o.macro_refs.len() as u8); 358 | 359 | Self::write_macro_refs(data, &o.macro_refs); 360 | } 361 | fn write_output_number(data: &mut Vec, o: &OutputNumber) { 362 | Self::write_u16(data, o.id); 363 | Self::write_u8(data, ObjectType::OutputNumber); 364 | Self::write_u16(data, o.width); 365 | Self::write_u16(data, o.height); 366 | Self::write_u8(data, o.background_colour); 367 | Self::write_u16(data, o.font_attributes); 368 | Self::write_u8(data, o.options); 369 | Self::write_u16(data, o.variable_reference); 370 | Self::write_u32(data, o.value); 371 | Self::write_i32(data, o.offset); 372 | Self::write_f32(data, o.scale); 373 | Self::write_u8(data, o.nr_of_decimals); 374 | Self::write_u8(data, o.format as u8); 375 | Self::write_u8(data, o.justification); 376 | Self::write_u8(data, o.macro_refs.len() as u8); 377 | 378 | Self::write_macro_refs(data, &o.macro_refs); 379 | } 380 | fn write_output_line(data: &mut Vec, o: &OutputLine) { 381 | Self::write_u16(data, o.id); 382 | Self::write_u8(data, ObjectType::OutputLine); 383 | Self::write_u16(data, o.line_attributes); 384 | Self::write_u16(data, o.width); 385 | Self::write_u16(data, o.height); 386 | Self::write_u8(data, o.line_direction); 387 | Self::write_u8(data, o.macro_refs.len() as u8); 388 | 389 | Self::write_macro_refs(data, &o.macro_refs); 390 | } 391 | fn write_output_rectangle(data: &mut Vec, o: &OutputRectangle) { 392 | Self::write_u16(data, o.id); 393 | Self::write_u8(data, ObjectType::OutputRectangle); 394 | Self::write_u16(data, o.line_attributes); 395 | Self::write_u16(data, o.width); 396 | Self::write_u16(data, o.height); 397 | Self::write_u8(data, o.line_suppression); 398 | Self::write_u16(data, o.fill_attributes); 399 | Self::write_u8(data, o.macro_refs.len() as u8); 400 | 401 | Self::write_macro_refs(data, &o.macro_refs); 402 | } 403 | fn write_output_ellipse(data: &mut Vec, o: &OutputEllipse) { 404 | Self::write_u16(data, o.id); 405 | Self::write_u8(data, ObjectType::OutputEllipse); 406 | Self::write_u16(data, o.line_attributes); 407 | Self::write_u16(data, o.width); 408 | Self::write_u16(data, o.height); 409 | Self::write_u8(data, o.ellipse_type); 410 | Self::write_u8(data, o.start_angle); 411 | Self::write_u8(data, o.end_angle); 412 | Self::write_u16(data, o.fill_attributes); 413 | Self::write_u8(data, o.macro_refs.len() as u8); 414 | 415 | Self::write_macro_refs(data, &o.macro_refs); 416 | } 417 | fn write_output_polygon(data: &mut Vec, o: &OutputPolygon) { 418 | Self::write_u16(data, o.id); 419 | Self::write_u8(data, ObjectType::OutputPolygon); 420 | Self::write_u16(data, o.width); 421 | Self::write_u16(data, o.height); 422 | Self::write_u16(data, o.line_attributes); 423 | Self::write_u16(data, o.fill_attributes); 424 | Self::write_u8(data, o.polygon_type); 425 | Self::write_u8(data, o.points.len() as u8); 426 | Self::write_u8(data, o.macro_refs.len() as u8); 427 | 428 | Self::write_points(data, &o.points); 429 | Self::write_macro_refs(data, &o.macro_refs); 430 | } 431 | fn write_output_meter(data: &mut Vec, o: &OutputMeter) { 432 | Self::write_u16(data, o.id); 433 | Self::write_u8(data, ObjectType::OutputMeter); 434 | Self::write_u16(data, o.width); 435 | Self::write_u8(data, o.needle_colour); 436 | Self::write_u8(data, o.border_colour); 437 | Self::write_u8(data, o.arc_and_tick_colour); 438 | Self::write_u8(data, o.options); 439 | Self::write_u8(data, o.nr_of_ticks); 440 | Self::write_u8(data, o.start_angle); 441 | Self::write_u8(data, o.end_angle); 442 | Self::write_u16(data, o.min_value); 443 | Self::write_u16(data, o.max_value); 444 | Self::write_u16(data, o.variable_reference); 445 | Self::write_u16(data, o.value); 446 | Self::write_u8(data, o.macro_refs.len() as u8); 447 | 448 | Self::write_macro_refs(data, &o.macro_refs); 449 | } 450 | fn write_output_linear_bar_graph(data: &mut Vec, o: &OutputLinearBarGraph) { 451 | Self::write_u16(data, o.id); 452 | Self::write_u8(data, ObjectType::OutputLinearBarGraph); 453 | Self::write_u16(data, o.width); 454 | Self::write_u16(data, o.height); 455 | Self::write_u8(data, o.colour); 456 | Self::write_u8(data, o.target_line_colour); 457 | Self::write_u8(data, o.options); 458 | Self::write_u8(data, o.nr_of_ticks); 459 | Self::write_u16(data, o.min_value); 460 | Self::write_u16(data, o.max_value); 461 | Self::write_u16(data, o.variable_reference); 462 | Self::write_u16(data, o.value); 463 | Self::write_u16(data, o.target_value_variable_reference); 464 | Self::write_u16(data, o.target_value); 465 | Self::write_u8(data, o.macro_refs.len() as u8); 466 | 467 | Self::write_macro_refs(data, &o.macro_refs); 468 | } 469 | fn write_output_arched_bar_graph(data: &mut Vec, o: &OutputArchedBarGraph) { 470 | Self::write_u16(data, o.id); 471 | Self::write_u8(data, ObjectType::OutputArchedBarGraph); 472 | Self::write_u16(data, o.width); 473 | Self::write_u16(data, o.height); 474 | Self::write_u8(data, o.colour); 475 | Self::write_u8(data, o.target_line_colour); 476 | Self::write_u8(data, o.options); 477 | Self::write_u8(data, o.start_angle); 478 | Self::write_u8(data, o.end_angle); 479 | Self::write_u16(data, o.bar_graph_width); 480 | Self::write_u16(data, o.min_value); 481 | Self::write_u16(data, o.max_value); 482 | Self::write_u16(data, o.variable_reference); 483 | Self::write_u16(data, o.value); 484 | Self::write_u16(data, o.target_value_variable_reference); 485 | Self::write_u16(data, o.target_value); 486 | Self::write_u8(data, o.macro_refs.len() as u8); 487 | 488 | Self::write_macro_refs(data, &o.macro_refs); 489 | } 490 | fn write_picture_graphic(data: &mut Vec, o: &PictureGraphic) { 491 | Self::write_u16(data, o.id); 492 | Self::write_u8(data, ObjectType::PictureGraphic); 493 | Self::write_u16(data, o.width); 494 | Self::write_u16(data, o.actual_width); 495 | Self::write_u16(data, o.actual_height); 496 | Self::write_u8(data, o.format); 497 | Self::write_u8(data, o.options); 498 | Self::write_u8(data, o.transparency_colour); 499 | Self::write_u32(data, o.data.len() as u32); 500 | Self::write_u8(data, o.macro_refs.len() as u8); 501 | 502 | Self::write_bytes(data, &o.data); 503 | Self::write_macro_refs(data, &o.macro_refs); 504 | } 505 | fn write_number_variable(data: &mut Vec, o: &NumberVariable) { 506 | Self::write_u16(data, o.id); 507 | Self::write_u8(data, ObjectType::NumberVariable); 508 | Self::write_u32(data, o.value); 509 | } 510 | fn write_string_variable(data: &mut Vec, o: &StringVariable) { 511 | Self::write_u16(data, o.id); 512 | Self::write_u8(data, ObjectType::StringVariable); 513 | Self::write_string(data, &o.value); 514 | } 515 | fn write_font_attributes(data: &mut Vec, o: &FontAttributes) { 516 | Self::write_u16(data, o.id); 517 | Self::write_u8(data, ObjectType::FontAttributes); 518 | Self::write_u8(data, o.font_colour); 519 | Self::write_u8(data, o.font_size); 520 | Self::write_u8(data, o.font_type); 521 | Self::write_u8(data, o.font_style); 522 | Self::write_u8(data, o.macro_refs.len() as u8); 523 | 524 | Self::write_macro_refs(data, &o.macro_refs); 525 | } 526 | fn write_line_attributes(data: &mut Vec, o: &LineAttributes) { 527 | Self::write_u16(data, o.id); 528 | Self::write_u8(data, ObjectType::LineAttributes); 529 | Self::write_u8(data, o.line_colour); 530 | Self::write_u8(data, o.line_width); 531 | Self::write_u16(data, o.line_art); 532 | Self::write_u8(data, o.macro_refs.len() as u8); 533 | 534 | Self::write_macro_refs(data, &o.macro_refs); 535 | } 536 | fn write_fill_attributes(data: &mut Vec, o: &FillAttributes) { 537 | Self::write_u16(data, o.id); 538 | Self::write_u8(data, ObjectType::FillAttributes); 539 | Self::write_u8(data, o.fill_type); 540 | Self::write_u8(data, o.fill_colour); 541 | Self::write_u16(data, o.fill_pattern); 542 | Self::write_u8(data, o.macro_refs.len() as u8); 543 | 544 | Self::write_macro_refs(data, &o.macro_refs); 545 | } 546 | fn write_input_attributes(data: &mut Vec, o: &InputAttributes) { 547 | Self::write_u16(data, o.id); 548 | Self::write_u8(data, ObjectType::InputAttributes); 549 | Self::write_u8(data, o.validation_type); 550 | Self::write_string(data, &o.validation_string); 551 | Self::write_u8(data, o.macro_refs.len() as u8); 552 | 553 | Self::write_macro_refs(data, &o.macro_refs); 554 | } 555 | fn write_object_pointer(data: &mut Vec, o: &ObjectPointer) { 556 | Self::write_u16(data, o.id); 557 | Self::write_u8(data, ObjectType::ObjectPointer); 558 | Self::write_u16(data, o.value); 559 | Self::write_u16(data, o.id); 560 | Self::write_u8(data, ObjectType::ObjectPointer); 561 | Self::write_u16(data, o.value); 562 | } 563 | fn write_macro(data: &mut Vec, o: &Macro) { 564 | Self::write_u16(data, o.id); 565 | Self::write_u8(data, ObjectType::Macro); 566 | Self::write_u16(data, o.commands.len() as u16); 567 | 568 | Self::write_bytes(data, &o.commands); 569 | } 570 | fn write_auxiliary_function_type1(data: &mut Vec, o: &AuxiliaryFunctionType1) { 571 | Self::write_u16(data, o.id); 572 | Self::write_u8(data, ObjectType::AuxiliaryFunctionType1); 573 | Self::write_u8(data, o.background_colour); 574 | Self::write_u8(data, o.function_type); 575 | Self::write_u8(data, o.object_refs.len() as u8); 576 | 577 | Self::write_object_refs(data, &o.object_refs); 578 | } 579 | fn write_auxiliary_input_type1(data: &mut Vec, o: &AuxiliaryInputType1) { 580 | Self::write_u16(data, o.id); 581 | Self::write_u8(data, ObjectType::AuxiliaryInputType1); 582 | Self::write_u8(data, o.background_colour); 583 | Self::write_u8(data, o.function_type); 584 | Self::write_u8(data, o.input_id); 585 | Self::write_u8(data, o.object_refs.len() as u8); 586 | 587 | Self::write_object_refs(data, &o.object_refs); 588 | } 589 | fn write_auxiliary_function_type2(data: &mut Vec, o: &AuxiliaryFunctionType2) { 590 | Self::write_u16(data, o.id); 591 | Self::write_u8(data, ObjectType::AuxiliaryFunctionType2); 592 | Self::write_u8(data, o.background_colour); 593 | Self::write_u8(data, o.function_attributes); 594 | Self::write_u8(data, o.object_refs.len() as u8); 595 | 596 | Self::write_object_refs(data, &o.object_refs); 597 | } 598 | fn write_auxiliary_input_type2(data: &mut Vec, o: &AuxiliaryInputType2) { 599 | Self::write_u16(data, o.id); 600 | Self::write_u8(data, ObjectType::AuxiliaryInputType2); 601 | Self::write_u8(data, o.background_colour); 602 | Self::write_u8(data, o.function_attributes); 603 | Self::write_u8(data, o.object_refs.len() as u8); 604 | 605 | Self::write_object_refs(data, &o.object_refs); 606 | } 607 | fn write_auxiliary_control_designator_type2( 608 | data: &mut Vec, 609 | o: &AuxiliaryControlDesignatorType2, 610 | ) { 611 | Self::write_u16(data, o.id); 612 | Self::write_u8(data, ObjectType::AuxiliaryControlDesignatorType2); 613 | Self::write_u8(data, o.pointer_type); 614 | Self::write_u16(data, o.auxiliary_object_id); 615 | } 616 | fn write_window_mask(data: &mut Vec, o: &WindowMask) { 617 | Self::write_u16(data, o.id); 618 | Self::write_u8(data, ObjectType::WindowMask); 619 | Self::write_u8(data, o.cell_format.size().x); 620 | Self::write_u8(data, o.cell_format.size().y); 621 | Self::write_u8(data, o.window_type); 622 | Self::write_u8(data, o.background_colour); 623 | Self::write_u8(data, o.options); 624 | Self::write_u16(data, o.name); 625 | Self::write_u16(data, o.window_title); 626 | Self::write_u16(data, o.window_icon); 627 | Self::write_u8(data, o.objects.len() as u8); 628 | Self::write_u8(data, o.object_refs.len() as u8); 629 | Self::write_u8(data, o.macro_refs.len() as u8); 630 | 631 | Self::write_objects(data, &o.objects); 632 | Self::write_object_refs(data, &o.object_refs); 633 | Self::write_macro_refs(data, &o.macro_refs); 634 | } 635 | fn write_key_group(data: &mut Vec, o: &KeyGroup) { 636 | Self::write_u16(data, o.id); 637 | Self::write_u8(data, ObjectType::KeyGroup); 638 | Self::write_u8(data, o.options); 639 | Self::write_u16(data, o.name); 640 | Self::write_u16(data, o.key_group_icon); 641 | Self::write_u8(data, o.objects.len() as u8); 642 | Self::write_u8(data, o.macro_refs.len() as u8); 643 | 644 | Self::write_objects(data, &o.objects); 645 | Self::write_macro_refs(data, &o.macro_refs); 646 | } 647 | fn write_graphics_context(data: &mut Vec, o: &GraphicsContext) { 648 | Self::write_u16(data, o.id); 649 | Self::write_u8(data, ObjectType::GraphicsContext); 650 | Self::write_u16(data, o.viewport_width); 651 | Self::write_u16(data, o.viewport_height); 652 | Self::write_i16(data, o.viewport_x); 653 | Self::write_i16(data, o.viewport_y); 654 | Self::write_u16(data, o.canvas_width); 655 | Self::write_u16(data, o.canvas_height); 656 | Self::write_f32(data, o.viewport_zoom); 657 | Self::write_i16(data, o.graphics_cursor_x); 658 | Self::write_i16(data, o.graphics_cursor_y); 659 | Self::write_u8(data, o.foreground_colour); 660 | Self::write_u8(data, o.background_colour); 661 | Self::write_u16(data, o.font_attributes_object); 662 | Self::write_u16(data, o.line_attributes_object); 663 | Self::write_u16(data, o.fill_attributes_object); 664 | Self::write_u8(data, o.format); 665 | Self::write_u8(data, o.options); 666 | Self::write_u8(data, o.transparency_colour); 667 | } 668 | fn write_output_list(data: &mut Vec, o: &OutputList) { 669 | Self::write_u16(data, o.id); 670 | Self::write_u8(data, ObjectType::OutputList); 671 | Self::write_u16(data, o.width); 672 | Self::write_u16(data, o.height); 673 | Self::write_u16(data, o.variable_reference); 674 | Self::write_u8(data, o.value); 675 | Self::write_u8(data, o.list_items.len() as u8); 676 | Self::write_u8(data, o.macro_refs.len() as u8); 677 | 678 | Self::write_objects(data, &o.list_items); 679 | Self::write_macro_refs(data, &o.macro_refs); 680 | } 681 | fn write_extended_input_attributes(data: &mut Vec, o: &ExtendedInputAttributes) { 682 | Self::write_u16(data, o.id); 683 | Self::write_u8(data, ObjectType::ExtendedInputAttributes); 684 | Self::write_u8(data, o.validation_type as u8); 685 | Self::write_code_planes(data, &o.code_planes); 686 | } 687 | fn write_colour_map(data: &mut Vec, o: &ColourMap) { 688 | Self::write_u16(data, o.id); 689 | Self::write_u8(data, ObjectType::ColourMap); 690 | Self::write_u16(data, o.colour_map.len() as u16); 691 | 692 | Self::write_bytes(data, &o.colour_map); 693 | } 694 | fn write_object_label_reference_list(data: &mut Vec, o: &ObjectLabelReferenceList) { 695 | Self::write_u16(data, o.id); 696 | Self::write_u8(data, ObjectType::ObjectLabelReferenceList); 697 | Self::write_u16(data, o.object_labels.len() as u16); 698 | 699 | Self::write_object_labels(data, &o.object_labels); 700 | } 701 | fn write_external_object_definition(data: &mut Vec, o: &ExternalObjectDefinition) { 702 | Self::write_u16(data, o.id); 703 | Self::write_u8(data, ObjectType::ExternalObjectDefinition); 704 | Self::write_u8(data, o.options); 705 | Self::write_name(data, o.name); 706 | Self::write_u8(data, o.objects.len() as u8); 707 | 708 | Self::write_objects(data, &o.objects); 709 | } 710 | fn write_external_reference_name(data: &mut Vec, o: &ExternalReferenceName) { 711 | Self::write_u16(data, o.id); 712 | Self::write_u8(data, ObjectType::ExternalReferenceName); 713 | Self::write_u8(data, o.options); 714 | Self::write_name(data, o.name); 715 | } 716 | fn write_external_object_pointer(data: &mut Vec, o: &ExternalObjectPointer) { 717 | Self::write_u16(data, o.id); 718 | Self::write_u8(data, ObjectType::ExternalObjectPointer); 719 | Self::write_u16(data, o.default_object_id); 720 | Self::write_u16(data, o.external_reference_name_id); 721 | Self::write_u16(data, o.external_object_id); 722 | } 723 | fn write_animation(data: &mut Vec, o: &Animation) { 724 | Self::write_u16(data, o.id); 725 | Self::write_u8(data, ObjectType::Animation); 726 | Self::write_u16(data, o.width); 727 | Self::write_u16(data, o.height); 728 | Self::write_u16(data, o.refresh_interval); 729 | Self::write_u8(data, o.value); 730 | Self::write_u8(data, o.enabled); 731 | Self::write_u8(data, o.first_child_index); 732 | Self::write_u8(data, o.last_child_index); 733 | Self::write_u8(data, o.default_child_index); 734 | Self::write_u8(data, o.options); 735 | Self::write_u8(data, o.object_refs.len() as u8); 736 | Self::write_u8(data, o.macro_refs.len() as u8); 737 | 738 | Self::write_object_refs(data, &o.object_refs); 739 | Self::write_macro_refs(data, &o.macro_refs); 740 | } 741 | fn write_colour_palette(data: &mut Vec, o: &ColourPalette) { 742 | Self::write_u16(data, o.id); 743 | Self::write_u8(data, ObjectType::ColourPalette); 744 | Self::write_u8(data, o.options); 745 | Self::write_u16(data, o.colours.len() as u16); 746 | 747 | Self::write_colours(data, &o.colours); 748 | } 749 | fn write_graphic_data(data: &mut Vec, o: &GraphicData) { 750 | Self::write_u16(data, o.id); 751 | Self::write_u8(data, ObjectType::GraphicData); 752 | Self::write_u8(data, o.format); 753 | Self::write_u32(data, o.data.len() as u32); 754 | 755 | Self::write_bytes(data, &o.data); 756 | } 757 | fn write_working_set_special_controls(data: &mut Vec, o: &WorkingSetSpecialControls) { 758 | Self::write_u16(data, o.id); 759 | Self::write_u8(data, ObjectType::WorkingSetSpecialControls); 760 | Self::write_u16(data, o.id_of_colour_map); 761 | Self::write_u16(data, o.id_of_colour_palette); 762 | Self::write_u8(data, o.language_pairs.len() as u8); 763 | 764 | Self::write_language_pairs(data, &o.language_pairs); 765 | } 766 | fn write_scaled_graphic(data: &mut Vec, o: &ScaledGraphic) { 767 | Self::write_u16(data, o.id); 768 | Self::write_u8(data, ObjectType::ScaledGraphic); 769 | Self::write_u16(data, o.width); 770 | Self::write_u16(data, o.height); 771 | Self::write_u8(data, o.scale_type); 772 | Self::write_u8(data, o.options); 773 | Self::write_u16(data, o.value); 774 | Self::write_u8(data, o.macro_refs.len() as u8); 775 | 776 | Self::write_macro_refs(data, &o.macro_refs); 777 | } 778 | } 779 | --------------------------------------------------------------------------------