--- source_files: - DataPRO/IService/Classes/CAN/CANConfig.cs - DataPRO/IService/Classes/CAN/CANModuleConfig.cs generated_at: "2026-04-16T03:56:57.762497+00:00" model: "Qwen/Qwen3-Coder-Next-FP8" schema_version: 1 sha256: "49767c75e3daaeaa" --- # CAN ## Documentation: CAN Configuration Module (`DTS.DASLib.Service`) --- ### 1. Purpose This module provides XML-based serialization and deserialization for CAN (Controller Area Network) hardware configuration data in the DAS (Data Acquisition System) service layer. It enables persistent storage and retrieval of system-wide CAN module configurations (`CANConfig`) and per-module settings (`CANModuleConfig`), including channel definitions, recording parameters, and firmware metadata. The classes implement `IXmlSerializable` to support custom XML formatting, allowing configuration files to be stored in a human-readable and version-tolerant manner under the `DASConfigs` subdirectory relative to the executing assembly. --- ### 2. Public Interface #### `CANConfig` class - **`public Dictionary Modules { get; }`** Read-only dictionary mapping module serial numbers (string keys) to `CANModuleConfig` instances. Represents all configured CAN modules. - **`public string FileName { get; }`** Full path to the XML file from which this `CANConfig` instance was loaded (or to which it will be written). Set only during construction. - **`public CANConfig()`** Default constructor. Initializes an empty configuration with no modules or file association. - **`public CANConfig(string fileName, bool deleteIfPresent)`** Constructor that attempts to load configuration from `fileName` (relative to `DASConfigs/` subdirectory). If `deleteIfPresent` is `true`, the file is deleted before loading (resulting in an empty config). If `false`, the file is read via `ReadXml`. Exceptions during file I/O or XML parsing are logged via `APILogger`. - **`public void SetModule(CANModuleConfig module)`** Inserts or updates a module in the `_modules` dictionary using `module.SerialNumber` as the key. No validation beyond key existence. - **`public CANModuleConfig GetModule(CANModuleConfig module)`** Returns the module stored under `module.SerialNumber`. If not present, inserts the provided `module` instance into the dictionary and returns it. *Note: This mutates the config even when the module is not yet known.* - **`public XmlSchema GetSchema()`** Returns `null`. Required by `IXmlSerializable` but unused. - **`public void ReadXml(XmlReader reader)`** Deserializes XML starting at `` root. Reads a `` section containing multiple `` elements. Each `` is deserialized into a `CANModuleConfig` and added via `SetModule`. - **`public void WriteXml(XmlWriter writer)`** Serializes the configuration as `...`. Each `CANModuleConfig` is written via its own `WriteXml` method, with `writer.Flush()` called after writing the `` start tag and after each module. #### `CANModuleConfig` class - **`public string SerialNumber { get; set; }`** Unique identifier for the CAN module (used as dictionary key in `CANConfig.Modules`). - **`public string TestId { get; set; }`** Identifier for the test associated with this module. - **`public string TestDescription { get; set; }`** Human-readable description of the test. - **`public DFConstantsAndEnums.RecordingMode RecordingMode { get; set; }`** Recording mode enum (e.g., continuous, event-triggered). Defaults to `InvalidArmMode`. - **`public float AAFilterRateHz { get; set; }`** Anti-aliasing filter rate in Hz. Default `0`. - **`public double PreTriggerSeconds { get; set; }`** Duration (seconds) of data to capture *before* a trigger event. Default `0`. - **`public double PostTriggerSeconds { get; set; }`** Duration (seconds) of data to capture *after* a trigger event. Default `0`. - **`public string FirmwareVersion { get; set; }`** Firmware version string reported by the module. - **`public UInt64? MaxEventStorageSpaceInBytes { get; set; }`** Optional maximum storage space (in bytes) for event-triggered recordings. Nullable; defaults to `0`. - **`public int ModuleArrayIndex { get; set; }`** Index of the module in a logical array (e.g., for ordering). Default `0`. - **`public string FileName { get; }`** Full path to the XML file from which this module config was loaded. Set only during construction. - **`public CANModuleConfig()`** Default constructor. Initializes all properties to defaults. - **`public CANModuleConfig(string fileName)`** Constructor that loads configuration from `fileName` (relative to `DASConfigs/`). Logs errors on failure. - **`public void SetChannel(CANInputDASChannel channel)`** Inserts or updates a channel in the internal `_channels` dictionary using `channel.ModuleChannelNumber` as the key. - **`public CANInputDASChannel GetChannel(CANInputDASChannel channel)`** Returns the channel stored under `channel.ModuleChannelNumber`. If not present, inserts the provided `channel` and returns it. *Note: Mutates config on miss.* - **`public XmlSchema GetSchema()`** Returns `null`. Required by `IXmlSerializable` but unused. - **`public void ReadXml(XmlReader reader)`** Deserializes XML starting at ``. Reads scalar properties (`SerialNumber`, `TestId`, etc.) and the `` section. For `RecordingMode`, `AAFilterRateHz`, `PreTriggerSeconds`, `PostTriggerSeconds`, and `MaxEventStorageSpaceInBytes`, parsing errors are logged and defaults retained. `ModuleArrayIndex` is read via `ReadModuleArray`, which silently ignores errors (for backward compatibility with older config files). - **`public void WriteXml(XmlWriter writer)`** Serializes the module as `...`. Writes all scalar properties and the `` section. For each channel, calls `WriteElementStart`, `WriteXml`, and `WriteElementEnd` on the channel object. - **`public virtual void WriteElementStart(XmlWriter writer)`** Writes `` where `...` is the runtime type name (e.g., `CANInputDASChannel`). Allows polymorphic deserialization. - **`public virtual void WriteElementEnd(XmlWriter writer)`** Writes ``. --- ### 3. Invariants - **`SerialNumber` uniqueness**: Within a `CANConfig.Modules` dictionary, keys are `SerialNumber` strings. Duplicate keys are overwritten (not rejected). - **`ModuleChannelNumber` uniqueness**: Within a `CANModuleConfig._channels` dictionary, keys are `ModuleChannelNumber` integers. Duplicate keys are overwritten. - **XML structure**: `CANConfig` XML root is ``, containing `` with nested `` elements. `CANModuleConfig` XML root is ``. - **Backward compatibility**: `ReadModuleArray` silently ignores missing or malformed `ModuleArrayIndex` elements, assuming older config files may lack them. - **Default values**: All numeric/string properties have non-null defaults (e.g., `""` for strings, `0` for numerics, `InvalidArmMode` for `RecordingMode`). `MaxEventStorageSpaceInBytes` defaults to `0` (not `null`). - **File paths**: All file paths are constructed relative to the executing assembly’s directory, under `DASConfigs/`. --- ### 4. Dependencies - **Internal dependencies**: - `DTS.Common.Utilities.Logging.APILogger` for error logging (used in constructors, `ReadXml`, `WriteXml`, and `ReadModuleArray`). - `DTS.Common.Enums.DASFactory.DFConstantsAndEnums.RecordingMode` enum (used in `RecordingMode` property). - `DASChannel` (base class) and `CANInputDASChannel` (concrete channel type) for channel storage and serialization. *Note: `DASChannel` is referenced but not defined in the provided sources.* - **External dependencies**: - `System.Xml`, `System.Xml.Serialization`, `System.IO`, `System.Reflection` (standard .NET libraries). - File system access (for reading/writing XML files in `DASConfigs/`). - **Depended upon by**: Unknown from source alone. Likely consumed by higher-level DAS service components (e.g., configuration managers, CAN interface drivers) that initialize or update CAN module settings. --- ### 5. Gotchas - **`GetModule`/`GetChannel` mutate on miss**: Both methods add the provided module/channel to the internal dictionary if not found, which may be unintended (e.g., during read-only queries). Consider renaming or clarifying intent. - **No validation on `SerialNumber`/`ModuleChannelNumber`**: Duplicates are silently overwritten. No uniqueness enforcement beyond dictionary semantics. - **`ReadModuleArray` swallows errors**: The `catch` block in `ReadModuleArray` ignores all exceptions, potentially masking real issues (e.g., malformed XML). This is intentional for backward compatibility but may complicate debugging. - **`MaxEventStorageSpaceInBytes` range check is incomplete**: The condition `d >= 0 && d < ulong.MaxValue` allows `d == ulong.MaxValue`, but `Convert.ToUInt64(d)` will throw for `d == ulong.MaxValue` (since `double` may not represent it exactly). Should use `d <= ulong.MaxValue - 1` or similar. - **No `WriteXml` override for base `DASChannel`**: The `WriteXml` method iterates over `_channels.Values` as `DASChannel`, but only `CANInputDASChannel` is instantiated in `ReadXml`. If other `DASChannel` subclasses exist, `WriteElementStart`/`WriteElementEnd` must be overridden appropriately. - **Hardcoded path construction**: `Path.Combine(Path.GetDirectoryName(...), "DASConfigs", fileName)` assumes `DASConfigs` is a subdirectory of the assembly directory. May fail in non-standard deployment scenarios (e.g., single-file publish). - **No XML validation schema**: `GetSchema()` returns `null`, so no schema validation occurs during deserialization. Malformed XML may cause runtime errors or silent data loss. None identified beyond the above.