|
| 1 | +# Open-Loop Transit Service Handler |
| 2 | + |
| 3 | +[](https://opensource.org/licenses/MIT) |
| 4 | +[](https://isocpp.org/std/the-standard) |
| 5 | +[](https://cmake.org/) |
| 6 | + |
| 7 | +A professional, type-safe, and robust C++17 library for parsing, manipulating, and serializing data structures for open-loop transit cards. This library provides a complete, object-oriented model for the 96-byte **Common Service Area (CSA)** and a flexible 96-byte **Operator Service Area (OSA)** as specified by NCMC and related standards. |
| 8 | + |
| 9 | +The entire library is designed with a focus on data integrity, type safety, and ease of use. It abstracts away the complex bit-level details, allowing developers to work with clean, validated C++ objects. |
| 10 | + |
| 11 | +## ✨ Features |
| 12 | + |
| 13 | +- **Type-Safe C++17 Design**: Utilizes modern C++ features like `enum class`, `std::optional`, `constexpr`, and `[[nodiscard]]` to prevent common errors. |
| 14 | +- **Robust Data Validation**: All setter methods perform strict range and format checking, throwing standard exceptions on invalid input to ensure data integrity. |
| 15 | +- **Symmetrical Serialization**: Guaranteed symmetrical `to_bytes()` and `parse()` operations ensure no data is lost or corrupted during serialization cycles. |
| 16 | +- **Object-Oriented Abstraction**: Models each distinct data block (General, Terminal, Validation, Log, History, Trip Pass) as a fully encapsulated class. |
| 17 | +- **Comprehensive Documentation**: The entire API is thoroughly documented using Doxygen-style comments directly in the header file. |
| 18 | +- **Modern CMake Build System**: Provides a clean, cross-platform build configuration for the static library and its comprehensive test suite. |
| 19 | +- **Extensively Tested**: Includes a full suite of unit tests covering functionality, error handling, stateful logic, and data integrity. |
| 20 | + |
| 21 | +## ⚙️ Requirements |
| 22 | + |
| 23 | +- **CMake**: Version 3.15 or higher |
| 24 | +- **C++ Compiler**: A compiler with full C++17 support (e.g., GCC 7+, Clang 6+, MSVC 2017+). |
| 25 | + |
| 26 | +## 🚀 Building the Project |
| 27 | + |
| 28 | +The project is configured to build the core logic as a static library (`open_loop`) and a test executable (`run_open_loop_tests`). |
| 29 | + |
| 30 | +### Command-Line Instructions |
| 31 | + |
| 32 | +1. **Clone the repository:** |
| 33 | + ```bash |
| 34 | + git clone https://github.com/your_username/open_loop_service_handler.git |
| 35 | + cd open_loop_service_handler |
| 36 | + ``` |
| 37 | + |
| 38 | +2. **Configure the project with CMake:** |
| 39 | + ```bash |
| 40 | + mkdir build |
| 41 | + cd build |
| 42 | + cmake .. |
| 43 | + ``` |
| 44 | + |
| 45 | +3. **Build the library and tests:** |
| 46 | + ```bash |
| 47 | + cmake --build . |
| 48 | + ``` |
| 49 | + |
| 50 | +The compiled static library (`libopen_loop_service.a` or `open_loop_service.lib`) and the test executable will be located in the `build/` directory. |
| 51 | + |
| 52 | +### Building with CLion |
| 53 | + |
| 54 | +Simply open the project's root directory in CLion. The IDE will automatically detect the `CMakeLists.txt` files and configure the project. You can then select the `run_open_loop_tests` target and click "Build" or "Run". |
| 55 | +
|
| 56 | +## ✅ Running the Tests |
| 57 | +
|
| 58 | +After a successful build, you can run the comprehensive test suite from the build directory: |
| 59 | +
|
| 60 | +```bash |
| 61 | +cd build/test |
| 62 | +./run_open_loop_tests |
| 63 | +``` |
| 64 | +
|
| 65 | +A successful run will show a report with all tests passing. |
| 66 | +
|
| 67 | +## 💡 Usage Example |
| 68 | +
|
| 69 | +The library is designed to be intuitive. Here is a basic example of creating, populating, serializing, and parsing a Common Service Area (`CSA`) object. |
| 70 | +
|
| 71 | +```cpp |
| 72 | +#include <iostream> |
| 73 | +#include <cassert> |
| 74 | +#include "open_loop/open_loop.h" |
| 75 | +
|
| 76 | +using namespace open_loop; |
| 77 | +
|
| 78 | +int main() { |
| 79 | + // 1. Define the card's effective date in minutes since the Unix epoch. |
| 80 | + // This is a mandatory step for time-sensitive data. |
| 81 | + constexpr std::time_t card_effective_date = 28399680; |
| 82 | + |
| 83 | + // 2. Create the main CSA container. |
| 84 | + csa::container original_csa(card_effective_date); |
| 85 | + |
| 86 | + // 3. Populate the data using the high-level API. |
| 87 | + original_csa.get_general().set_version(1, 1, 0); |
| 88 | + original_csa.get_general().set_language(language_code::English); |
| 89 | + |
| 90 | + csa::terminal term; |
| 91 | + term.set_acquirer_id(20); |
| 92 | + term.set_operator_id(1024); |
| 93 | + term.set_terminal_id("AABBCC"); |
| 94 | + original_csa.get_validation().set_terminal_info(term); |
| 95 | + original_csa.get_validation().set_fare_amount(1500); |
| 96 | + |
| 97 | + // 4. Serialize the entire 96-byte structure into a byte vector. |
| 98 | + std::vector<uint8_t> bytes_to_write = original_csa.to_bytes(); |
| 99 | + assert(bytes_to_write.size() == 96); |
| 100 | + |
| 101 | + // 5. Parse the raw bytes back into a new object. |
| 102 | + // The same effective date must be used to correctly interpret time offsets. |
| 103 | + csa::container parsed_csa = csa::container::parse(bytes_to_write, card_effective_date); |
| 104 | + |
| 105 | + // 6. Verify that the parsed data is identical to the original. |
| 106 | + assert(original_csa == parsed_csa); |
| 107 | + |
| 108 | + std::cout << "Successfully created, serialized, and parsed a CSA object!" << std::endl; |
| 109 | + std::cout << "\n--- Parsed CSA Object ---" << std::endl; |
| 110 | + std::cout << parsed_csa << std::endl; |
| 111 | +
|
| 112 | + return 0; |
| 113 | +} |
| 114 | +``` |
| 115 | +
|
| 116 | +## 📚 API Reference |
| 117 | +
|
| 118 | +The entire public API is documented within the `include/open_loop/open_loop.h` header file using Doxygen-style comments. For detailed information on specific classes, methods, and parameters, please refer directly to the source code. |
| 119 | +
|
| 120 | +You can also generate a full set of HTML documentation by running Doxygen on the project. |
| 121 | +
|
| 122 | +### Data Structure Overview |
| 123 | +
|
| 124 | +#### Common Service Area (CSA) - 96 Bytes |
| 125 | +
|
| 126 | +| Offset (Bytes) | Size (Bytes) | Description | Managed By | |
| 127 | +| :------------- | :----------- | :--------------------------- | :-------------------- | |
| 128 | +| 0-1 | 2 | Version & Language Info | `csa::general` | |
| 129 | +| 2-20 | 19 | Last Validation Record | `csa::validation` | |
| 130 | +| 21-88 | 68 | Transaction History (4 logs) | `csa::history` | |
| 131 | +| 89-95 | 7 | Reserved for Future Use | `std::array<uint8_t, 7>` | |
| 132 | +
|
| 133 | +#### Operator Service Area (OSA) - 96 Bytes |
| 134 | +
|
| 135 | +| Offset (Bytes) | Size (Bytes) | Description | Managed By | |
| 136 | +| :------------- | :----------- | :-------------------------------- | :------------------------- | |
| 137 | +| 0-6 | 7 | Version, Phone (BCD), etc. | `osa::general` | |
| 138 | +| 7-19 | 13 | Last Validation Record | `osa::transaction_record` | |
| 139 | +| 20-45 | 26 | Transaction History (2 records) | `osa::history` | |
| 140 | +| 46-65 | 20 | Trip Pass Product Slot 1 | `osa::trip_pass` | |
| 141 | +| 66-85 | 20 | Trip Pass Product Slot 2 | `osa::trip_pass` | |
| 142 | +| 86-95 | 10 | Padding (zero-filled) | N/A | |
| 143 | +
|
| 144 | +
|
| 145 | +## 🤝 Contributing |
| 146 | +
|
| 147 | +Contributions are welcome! Please feel free to open an issue to report bugs or suggest features, or submit a pull request with your improvements. |
| 148 | +
|
| 149 | +## 📄 License |
| 150 | +
|
| 151 | +This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. |
0 commit comments