diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3730a79..0a8f868 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -187,11 +187,17 @@ jobs: - name: Install Doxygen run: | sudo apt-get update - sudo apt-get install -y doxygen graphviz + sudo apt-get install -y doxygen graphviz plantuml default-jre - name: Generate documentation run: | doxygen --version + # Render PlantUML diagrams to SVG before running Doxygen (so Doxygen can include pre-rendered images) + for p in doc/diagrams/*.puml; do + if [ -f "$p" ]; then + plantuml -tsvg -o doc/diagrams "${p}" + fi + done doxygen Doxyfile working-directory: ${{ github.workspace }} - name: List generated docs diff --git a/Doxyfile b/Doxyfile index f30a833..2213d85 100644 --- a/Doxyfile +++ b/Doxyfile @@ -114,6 +114,7 @@ WARN_LOGFILE = #--------------------------------------------------------------------------- INPUT = inc \ src \ + doc \ README.md INPUT_ENCODING = UTF-8 INPUT_FILE_ENCODING = @@ -140,7 +141,7 @@ EXAMPLE_PATH = examples EXAMPLE_PATTERNS = *.cpp \ *.h EXAMPLE_RECURSIVE = NO -IMAGE_PATH = +IMAGE_PATH = doc/diagrams INPUT_FILTER = FILTER_PATTERNS = FILTER_SOURCE_FILES = NO @@ -307,7 +308,7 @@ DOT_PATH = DOTFILE_DIRS = DIA_PATH = DIAFILE_DIRS = -PLANTUML_JAR_PATH = +PLANTUML_JAR_PATH = /usr/share/plantuml/plantuml.jar PLANTUML_CFG_FILE = PLANTUML_INCLUDE_PATH = DOT_GRAPH_MAX_NODES = 50 diff --git a/README.md b/README.md index 2d2242e..1dddb40 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,6 @@ C/C++ library for Diagnostics over IP (DoIP) (fork from https://github.com/AVL-D **CAUTION** The current API is under construction any may change at any time. - - - ## Dependencies `libdoip` uses `spdlog`. The lib is downloaded automatically. Or you may install it locally via @@ -25,18 +22,20 @@ sudo apt install libspdlog-dev See [Logging](./doc/LOGGING.md) for details. -### Building Documentation Locally +### Getting started + +Quick start — read the generated tutorial for the example server: -To generate the documentation locally: +- Online (published): https://magolves.github.io/libdoip/index.html +- Local example page: see `doc/ExampleDoIPServer.md` (included in the Doxygen HTML under "Example DoIP Server Tutorial"). +- Example tutorial (direct): https://magolves.github.io/libdoip/ExampleDoIPServer.html + +If you want to generate the docs locally, install Doxygen and Graphviz and +run: ```bash -# Install Doxygen and Graphviz sudo apt install doxygen graphviz - -# Generate documentation doxygen Doxyfile - -# Open the documentation xdg-open docs/html/index.html ``` @@ -76,7 +75,22 @@ sudo apt install doctest sudo tcpdump -i any udp port 13400 -X ``` +## Examples + +The project includes a small example DoIP server demonstrating how to +use the `DoIPServer` and `DoIPServerModel` APIs and how to register UDS +handlers. + +- Example source files: `examples/exampleDoIPServer.cpp`, + `examples/ExampleDoIPServerModel.h` +- Example tutorial (published): https://magolves.github.io/libdoip/ExampleDoIPServer.html + +See the "Examples" section in the generated Doxygen main page for +additional annotated links to these files. + + ## References + - [ISO 13400-2:2019(en) Road vehicles — Diagnostic communication over Internet Protocol (DoIP) — Part 2: Transport protocol and network layer services]() - [Specification of Diagnostic over IP]() - [Diagnostics over Internet Protocol (DoIP)]() \ No newline at end of file diff --git a/doc/DOCUMENTATION.md b/doc/DOCUMENTATION.md deleted file mode 100644 index 630ec2a..0000000 --- a/doc/DOCUMENTATION.md +++ /dev/null @@ -1,166 +0,0 @@ -# Documentation Deployment Guide - -## Overview - -The libdoip project automatically generates and publishes API documentation using Doxygen and GitHub Pages. - -## How It Works - -### 1. **Documentation Generation** -- Documentation is generated from Doxygen comments in header files (`.h`) and source files (`.cpp`) -- Configuration is defined in `Doxyfile` at the project root -- Includes class diagrams, call graphs, and inheritance diagrams using Graphviz - -### 2. **Automated Deployment (GitHub Actions)** -- **Trigger**: Every push to `main` branch -- **Workflow**: `.github/workflows/ci.yml` → `documentation` job -- **Steps**: - 1. Installs Doxygen and Graphviz - 2. Runs `doxygen Doxyfile` to generate HTML documentation - 3. Deploys to `gh-pages` branch using `peaceiris/actions-gh-pages@v3` - -### 3. **GitHub Pages Hosting** -- Documentation is automatically published at: **https://magolves.github.io/libdoip/** -- Served from the `gh-pages` branch -- Updates within minutes after push to main - -## Setup Requirements - -### Enable GitHub Pages - -1. Go to repository **Settings** → **Pages** -2. Under **Source**, select: - - Branch: `gh-pages` - - Folder: `/ (root)` -3. Click **Save** - -The documentation will be available at `https://.github.io//` - -### Permissions - -The workflow needs write permissions to create the `gh-pages` branch: -- The `ci.yml` includes `permissions: contents: write` for the documentation job -- This is automatically granted for workflows in your own repository - -## Local Documentation Generation - -To build documentation on your local machine: - -```bash -# Install dependencies -sudo apt install doxygen graphviz - -# Generate documentation -doxygen Doxyfile - -# View in browser -xdg-open docs/html/index.html -``` - -## Documentation Best Practices - -### Doxygen Comment Styles - -```cpp -/** - * @brief Short description of the function - * - * Longer description with more details about what - * this function does and how to use it. - * - * @param paramName Description of the parameter - * @return Description of return value - * @throws ExceptionType When this exception is thrown - * - * @example - * ByteArray arr = {0x01, 0x02, 0x03}; - * uint16_t value = arr.readU16BE(0); - */ -``` - -### Key Doxygen Commands - -- `@brief` - Short description (one line) -- `@param` - Parameter description -- `@return` - Return value description -- `@throws` / `@exception` - Exception documentation -- `@note` - Additional notes -- `@warning` - Important warnings -- `@example` - Code examples -- `@see` - Related items -- `@deprecated` - Mark as deprecated - -### File Header Example - -```cpp -/** - * @file ByteArray.h - * @brief Byte array utilities for network protocol handling - * - * This file provides a ByteArray type with methods for reading - * and writing multi-byte integers in big-endian format. - */ -``` - -## Maintenance - -### Updating Documentation - -Documentation updates automatically when you: -1. Add/modify Doxygen comments in code -2. Push changes to `main` branch -3. GitHub Actions builds and deploys new version - -### Configuration Changes - -To modify documentation generation: -- Edit `Doxyfile` for Doxygen settings -- Edit `.github/workflows/ci.yml` for deployment settings - -### Troubleshooting - -**Documentation not updating?** -1. Check GitHub Actions: Go to **Actions** tab -2. Look for failed `documentation` job -3. Review build logs for errors - -**404 on documentation page?** -1. Verify GitHub Pages is enabled in Settings -2. Check that `gh-pages` branch exists -3. Wait a few minutes after first push - -**Missing diagrams?** -- Ensure `HAVE_DOT = YES` in Doxyfile -- Graphviz must be installed in CI (already configured) - -## Alternative: GitHub Wiki - -While we use GitHub Pages, you can also manually upload documentation to Wiki: - -```bash -# Clone the wiki repository -git clone https://github.com/Magolves/libdoip.wiki.git - -# Copy generated documentation -cp -r docs/html/* libdoip.wiki/ - -# Commit and push -cd libdoip.wiki -git add . -git commit -m "Update documentation" -git push -``` - -**However**, GitHub Pages is recommended because: -- ✅ Fully automated -- ✅ Preserves full HTML structure and styling -- ✅ Better for large API documentation -- ✅ Supports search functionality -- ✅ Maintains versioning through git - -## Resources - -- [Doxygen Manual](https://www.doxygen.nl/manual/) -- [GitHub Pages Documentation](https://docs.github.com/en/pages) -- [Doxygen Special Commands](https://www.doxygen.nl/manual/commands.html) -- [peaceiris/actions-gh-pages](https://github.com/peaceiris/actions-gh-pages) diff --git a/doc/ExampleDoIPServer.md b/doc/ExampleDoIPServer.md new file mode 100644 index 0000000..2cb826c --- /dev/null +++ b/doc/ExampleDoIPServer.md @@ -0,0 +1,254 @@ +# Example DoIP Server Tutorial + +This page describes how to build and run the example DoIP server included +with the libdoip project, and how to customize its UDS behavior. + +## Overview + +The example server application demonstrates a minimal DoIP server using +the library's `DoIPServer`, `DoIPConnection` and `DoIPServerModel` types. +The relevant example source files are located in the `examples/` directory: + +- `examples/exampleDoIPServer.cpp` — program entry point and socket setup. +- `examples/ExampleDoIPServerModel.h` — example `DoIPServerModel` with + ready-made UDS handlers and a small worker thread that simulates a + downstream transport (e.g. CAN). + +The example shows how to: + +- Configure server properties (VIN, logical gateway address, announcement + settings). +- Open UDP and TCP sockets for DoIP announcements and diagnostic + connections. +- Register UDS handlers using the `uds::UdsMock` test helper for local + request handling. + +## Files of interest + +- `inc/DoIPServerModel.h` — contains the `DoIPServerModel` callbacks that + the application implements for connection-open, close and diagnostic + message handling. Customize these callbacks to integrate your ECU logic. +- `examples/ExampleDoIPServerModel.h` — shows a concrete `DoIPServerModel` + implementation used by the example server. UDS handlers are registered + via `uds::UdsMock` and typed helpers such as + `registerReadDataByIdentifierHandler`. +- `examples/exampleDoIPServer.cpp` — creates and configures a `DoIPServer`, + sets logging level and starts listener threads that accept TCP + connections. + +### ServerModel interface (important callbacks) + +The `DoIPServerModel` struct (see `inc/DoIPServerModel.h`) exposes a few +key callbacks your application implements. A concise summary: + +- `onOpenConnection(IConnectionContext &ctx)` — called when a new connection + is established. +- `onCloseConnection(IConnectionContext &ctx, DoIPCloseReason)` — called + during graceful/abrupt close. +- `onDiagnosticMessage(IConnectionContext &ctx, const DoIPMessage &msg)` — + called for locally-handled diagnostic messages. +- `onDownstreamRequest(IConnectionContext &ctx, const DoIPMessage &msg, + ServerModelDownstreamResponseHandler cb)` — called when the state machine + wants to forward a diagnostic request to a downstream transport (e.g. + CAN). The implementation should return `DoIPDownstreamResult::Pending` if + it will respond asynchronously and call `ctx.receiveDownstreamResponse()` + when the response arrives. + +Below is a minimal example implementation that forwards messages to a +hypothetical CAN backend and forwards the response to the connection +context. + +```cpp +// inside your DoIPServerModel setup +m_model.onOpenConnection = [](IConnectionContext &ctx) noexcept { + // Prepare per-connection state +}; + +m_model.onDownstreamRequest = [this](IConnectionContext &ctx, const DoIPMessage &msg, + ServerModelDownstreamResponseHandler cb) noexcept { + // Convert DoIP diagnostic payload to CAN frames and send + auto [payload, size] = msg.getDiagnosticMessagePayload(); + ByteArray req(payload, payload + size); + + // Simulate async send to CAN; real code would push to a queue or + // post to a worker thread that interacts with the hardware. + canTransport.sendAsync(req, [&, cb, &ctx](const ByteArray &canRsp){ + // When response arrives, notify DoIP connection + ctx.receiveDownstreamResponse(canRsp); + // Optionally call provided callback to indicate handled + if (cb) cb(canRsp, DoIPDownstreamResult::Handled); + }); + + return DoIPDownstreamResult::Pending; +}; +``` + +## Building the example + +The project uses CMake. From the repository root, run the following +commands to build the library and the examples (recommended with a clean +build directory): + +```bash +rm -rf build +mkdir build +cd build +cmake .. -DCMAKE_BUILD_TYPE=Release -DWITH_EXAMPLES=ON +cmake --build . --parallel 4 +``` + +On success the example binaries are available under `build/examples/`. + +## Running the example server + +The example executable is `exampleDoIPServer`. Run it from the build +directory, optionally enabling loopback mode (binds announcements to +127.0.0.1 instead of broadcast): + +```bash +./examples/exampleDoIPServer [--loopback] +``` + +The program will: + +- Configure the `DoIPServer` (VIN, logical address, announce interval). +- Open UDP and TCP sockets for announcements and incoming diagnostic + connections. +- Start two threads: one that polls UDP announcements and one that waits + for TCP connections and processes TCP messages. + +## Customizing UDS behavior + +The example registers default UDS services and a few typed handlers in +`ExampleDoIPServerModel`: + +- Diagnostic Session Control (`0x10`) — registered via + `m_uds.registerDiagnosticSessionControlHandler`. +- ECU Reset (`0x11`) — registered via + `m_uds.registerECUResetHandler`. +- Read Data By Identifier (`0x22`) — registered via + `m_uds.registerReadDataByIdentifierHandler`. +- Tester Present (`0x3E`) — registered via + `m_uds.registerTesterPresentHandler`. + +These typed helpers convert a raw diagnostic `ByteArray` request into +typed parameters (for example a DID or a session type) and forward the +request to the provided callback. The callback returns a pair of +`uds::UdsResponseCode` and a `ByteArray` payload. Implement your own +handlers to perform ECU-specific logic and return appropriate responses. + +Example: register a handler for ReadDataByIdentifier to respond with a +VIN value for DID `0xF190`: + +```cpp +m_uds.registerReadDataByIdentifierHandler([this](uint16_t did) { + if (did == 0xF190) { + ByteArray response = {...}; // data bytes: [DID hi, DID lo, data...] + return std::make_pair(uds::UdsResponseCode::PositiveResponse, response); + } + return std::make_pair(uds::UdsResponseCode::RequestOutOfRange, ByteArray{0x22}); +}); +``` + +## Integrating a real downstream transport + +The example uses `uds::UdsMock` to simulate downstream behavior. For a +real ECU you should implement `ServerModelDownstreamHandler` in +`DoIPServerModel::onDownstreamRequest` to forward diagnostic messages to +the physical bus (e.g., CAN) and call `ctx.receiveDownstreamResponse()` +when a response arrives. The state machine will handle timeouts and +transitions for you. + +Simple downstream handler sketch: + +```cpp +m_model.onDownstreamRequest = [this](IConnectionContext &ctx, const DoIPMessage &msg, ServerModelDownstreamResponseHandler cb) { + // send msg to CAN + // when response received: ctx.receiveDownstreamResponse(responseByteArray); + return DoIPDownstreamResult::Pending; +}; +``` + +## Diagram: ServerModel interactions + +Below is a PlantUML diagram illustrating `DoIPServer`, `DoIPConnection`, +`DoIPServerModel` and a downstream UDS/CAN backend interaction. + +[\image html ]ServerModel.svg + +For GitHub or other markdown viewers that don't process the Doxygen +directive, the SVG is also referenced directly: + +![ServerModel](diagrams/ServerModel.svg) + +## Logging and debugging tips + +- The library uses `spdlog`. Set the log level early in `main`: + +```cpp +doip::Logger::setLevel(spdlog::level::debug); +``` + +- Enable `--loopback` while testing locally to avoid network broadcast. +- If the example appears unresponsive, check that TCP sockets were + successfully created. The example logs critical errors when socket + setup fails. + +## Troubleshooting common issues + +- `exampleDoIPServer` exits immediately with critical log about sockets: + - Ensure you have sufficient privileges to bind the requested ports. + - Try running on loopback or using a system port range allowed by your + OS. + +- UDS handler not called: + - Confirm the incoming diagnostic request is parsed correctly. Use the + library logging to inspect raw message bytes. + - If using downstream forwarding, ensure `onDownstreamRequest` is set + and that the downstream transport calls back to + `IConnectionContext::receiveDownstreamResponse`. + +## Documentation / Doxygen + +If you generate documentation with Doxygen, the file `doc/ExampleDoIPServer.md` +will be picked up automatically if `DOC_DIR` or the `INPUT` setting in +`Doxyfile` includes the `doc` directory. Run: + +```bash +doxygen Doxyfile +``` + +After generation, look for the "Example DoIP Server Tutorial" page in +the generated HTML documentation. + +## Message flow and payload types + +This repository and the example server follow the DoIP payload conventions +used in the examples and tests. Important details to understand when +implementing or testing downstream forwarding: + +- Incoming diagnostic requests from the client are sent as DoIP Diagnostic + Messages (payload type 0x8001). +- Immediately after receiving a diagnostic message the DoIP server sends a + Diagnostic Ack (payload type 0x8002) back to the client. This ack is sent + regardless of whether the message will be handled locally or forwarded + downstream. The ack acknowledges receipt at the DoIP transport level. +- If the server forwards the diagnostic request to a downstream transport + (for example, CAN), it does so only for Diagnostic Messages (0x8001). +- When a downstream response arrives, the DoIP server sends that response + back to the client as a Diagnostic Message (0x8001). That means the + client may observe two DoIP messages for a single request: the + Diagnostic Ack (0x8002) and later the downstream Diagnostic Message + (0x8001) containing the actual UDS response. + +ISO 13400 describes DoIP message and payload types. The immediate ack +behaviour is consistent with a transport-level acknowledgement: the server +confirms reception on the DoIP link while the actual diagnostic response +may be pending. If you require strict timing or synchronous behaviour, do +not rely on the ack as an application-level confirmation — always await +the downstream Diagnostic Message for the functional UDS response. + +## Next steps + +- Replace `uds::UdsMock` with a real UDS stack or backend for production. +- Extend the example model with additional UDS services as needed. diff --git a/doc/LOGGING.md b/doc/LOGGING.md index 2818c1f..2ab0acb 100644 --- a/doc/LOGGING.md +++ b/doc/LOGGING.md @@ -19,12 +19,12 @@ This library uses [spdlog](https://github.com/gabime/spdlog) for high-performanc #include "Logger.h" // Different log levels -DOIP_LOG_TRACE("Detailed trace information"); -DOIP_LOG_DEBUG("Debug information"); -DOIP_LOG_INFO("General information"); -DOIP_LOG_WARN("Warning message"); -DOIP_LOG_ERROR("Error occurred"); -DOIP_LOG_CRITICAL("Critical error"); +LOG_DOIP_TRACE("Detailed trace information"); +LOG_DOIP_DEBUG("Debug information"); +LOG_DOIP_INFO("General information"); +LOG_DOIP_WARN("Warning message"); +LOG_DOIP_ERROR("Error occurred"); +LOG_DOIP_CRITICAL("Critical error"); ``` ### Formatted Logging @@ -34,11 +34,11 @@ DOIP_LOG_CRITICAL("Critical error"); int port = 13400; std::string interface = "eth0"; -DOIP_LOG_INFO("DoIP server starting on interface '{}' port {}", interface, port); +LOG_DOIP_INFO("DoIP server starting on interface '{}' port {}", interface, port); // Works with any type that supports fmt formatting auto timestamp = std::chrono::system_clock::now(); -DOIP_LOG_DEBUG("Connection established at {}", timestamp); +LOG_DOIP_DEBUG("Connection established at {}", timestamp); ``` ### Configuration @@ -73,26 +73,3 @@ Common pattern flags: - `%$` - End color range - `%v` - The actual message -## Examples - -See the `examples/loggerExample.cpp` file for a complete demonstration of logging features. - -## Integration - -The logging system is automatically initialized when first used. No manual setup is required, but you can customize the behavior using the `doip::Logger` class static methods. - -## Thread Safety - -All logging operations are thread-safe. You can safely log from multiple threads without any additional synchronization. - -## Performance - -spdlog provides excellent performance characteristics: -- Asynchronous logging support (if needed) -- Minimal overhead for disabled log levels -- Fast formatting using the fmt library -- Efficient memory usage - -## Dependencies - -The logging system automatically manages the spdlog dependency through CMake's FetchContent, so no manual installation is required. \ No newline at end of file diff --git a/doc/Logging.md b/doc/Logging.md new file mode 100644 index 0000000..2ab0acb --- /dev/null +++ b/doc/Logging.md @@ -0,0 +1,75 @@ +# Logging in libdoip + +This library uses [spdlog](https://github.com/gabime/spdlog) for high-performance logging. + +## Features + +- **High Performance**: spdlog is one of the fastest C++ logging libraries +- **Thread Safe**: All logging operations are thread-safe +- **Multiple Log Levels**: TRACE, DEBUG, INFO, WARN, ERROR, CRITICAL +- **Formatted Output**: Support for fmt-style formatting +- **Configurable Patterns**: Customize log message format +- **Color Support**: Colored console output for better readability + +## Usage + +### Basic Logging + +```cpp +#include "Logger.h" + +// Different log levels +LOG_DOIP_TRACE("Detailed trace information"); +LOG_DOIP_DEBUG("Debug information"); +LOG_DOIP_INFO("General information"); +LOG_DOIP_WARN("Warning message"); +LOG_DOIP_ERROR("Error occurred"); +LOG_DOIP_CRITICAL("Critical error"); +``` + +### Formatted Logging + +```cpp +#include "Logger.h" + +int port = 13400; +std::string interface = "eth0"; +LOG_DOIP_INFO("DoIP server starting on interface '{}' port {}", interface, port); + +// Works with any type that supports fmt formatting +auto timestamp = std::chrono::system_clock::now(); +LOG_DOIP_DEBUG("Connection established at {}", timestamp); +``` + +### Configuration + +```cpp +#include "Logger.h" + +// Set log level (only messages at this level or higher will be shown) +doip::Logger::setLevel(spdlog::level::debug); + +// Set custom pattern +doip::Logger::setPattern("[%H:%M:%S] [%^%l%$] %v"); + +// Available levels: trace, debug, info, warn, err, critical, off +``` + +### Pattern Format + +The default pattern is: `[%Y-%m-%d %H:%M:%S.%e] [%n] [%^%l%$] %v` + +Common pattern flags: +- `%Y` - Year (4 digits) +- `%m` - Month (01-12) +- `%d` - Day (01-31) +- `%H` - Hour (00-23) +- `%M` - Minute (00-59) +- `%S` - Second (00-59) +- `%e` - Milliseconds (000-999) +- `%n` - Logger name +- `%l` - Log level +- `%^` - Start color range +- `%$` - End color range +- `%v` - The actual message + diff --git a/doc/ServerArchitecture.md b/doc/ServerArchitecture.md deleted file mode 100644 index 9223ebe..0000000 --- a/doc/ServerArchitecture.md +++ /dev/null @@ -1,380 +0,0 @@ -# DoIP Server Architecture - -## Overview - -The `DoIPServer` class provides two distinct APIs for different use cases: - -### 1. **Low-Level API (Manual Mode)** -For advanced users who need full control over threading and connection management. - -### 2. **High-Level API (Automatic Mode)** -For most users who want simple, production-ready server functionality with minimal code. - ---- - -## High-Level API (Recommended) - -### Design Philosophy - -The high-level API follows these Linux/UNIX best practices: - -1. **Encapsulation**: All socket and thread management is internal -2. **Callback-based**: Application logic is provided via std::function callbacks -3. **Resource Management**: RAII principles - destructor ensures cleanup -4. **Thread Safety**: Atomic operations for state management -5. **Graceful Shutdown**: Proper cleanup of all resources on stop() - -### Usage Pattern - -```cpp -#include "DoIPServer.h" - -// Define your connection handler -std::optional onConnectionAccepted(DoIPConnection* conn) { - // Create a model for this connection - DoIPServerModel model; - model.serverAddress = MY_ADDRESS; - - // Set up callbacks for this connection - model.onDiagnosticMessage = [](const DoIPMessage &msg) { - // Handle diagnostic messages - return std::nullopt; // ACK - }; - - model.onCloseConnection = []() noexcept { - // Cleanup on disconnect - }; - - return model; // Accept connection - // or: return std::nullopt; // Reject connection -} - -int main() { - DoIPServer server; - - // Configure server - server.setVIN("MYVIN12345678901"); - server.setLogicalGatewayAddress(0x0028); - - // Start with automatic handling - if (!server.start(onConnectionAccepted, true)) { - return 1; - } - - // Server is now running in background threads - // Main thread can do other work or just wait - - waitForShutdownSignal(); - - // Graceful shutdown - server.stop(); - - return 0; -} -``` - -### Threading Model - -When `start()` is called, the following threads are created: - -1. **UDP Listener Thread**: Continuously processes UDP vehicle identification requests -2. **TCP Acceptor Thread**: Waits for new TCP connections -3. **Connection Handler Threads**: One per active TCP connection (detached) - -``` -┌─────────────────────────────────────────────┐ -│ Main Application Thread │ -│ - Calls server.start() │ -│ - Does application-specific work │ -│ - Calls server.stop() on shutdown │ -└─────────────────────────────────────────────┘ - │ - ┌────────────┴────────────┐ - │ │ -┌───────▼──────┐ ┌────────▼─────────┐ -│ UDP Listener │ │ TCP Acceptor │ -│ Thread │ │ Thread │ -│ │ │ │ -│ - Recv UDP │ │ - accept() │ -│ - Send VID │ │ - Invoke │ -│ response │ │ callback │ -└──────────────┘ │ - Spawn handler │ - └────────┬─────────┘ - │ - ┌─────────────┴─────────────┐ - │ │ - ┌───────▼──────┐ ┌────────▼──────┐ - │ Connection │ │ Connection │ - │ Handler 1 │ ... │ Handler N │ - │ │ │ │ - │ - Recv DoIP │ │ - Recv DoIP │ - │ - Call model │ │ - Call model │ - │ callbacks │ │ callbacks │ - └──────────────┘ └───────────────┘ -``` - -### Connection Lifecycle - -``` -[New TCP Connection] - │ - ├─> TCP Acceptor accepts connection - │ - ├─> Creates DoIPConnection object - │ - ├─> Invokes onConnectionAccepted(connection) - │ │ - │ ├─> Application returns DoIPServerModel (accept) - │ │ or std::nullopt (reject) - │ │ - ├─> If accepted: setServerModel(model) - │ - ├─> Spawn dedicated connection handler thread - │ │ - │ ├─> while (active) { receiveTcpMessage() } - │ │ - │ ├─> Connection closes - │ │ - │ └─> Thread exits, unique_ptr destroys connection - │ - └─> TCP Acceptor continues waiting for next connection -``` - -### Error Handling - -The high-level API handles errors gracefully: - -- **Socket errors**: Logged and retried (with backoff for accept failures) -- **Connection drops**: Handler thread exits, resources cleaned up -- **Shutdown during operation**: All threads notified via m_running flag -- **Failed start()**: Returns false, all resources cleaned up - -### Resource Management - -```cpp -DoIPServer::~DoIPServer() { - if (m_running.load()) { - stop(); // Automatic cleanup on destruction - } -} - -void DoIPServer::stop() { - m_running.store(false); // Signal all threads to stop - closeUdpSocket(); // Unblock recv calls - closeTcpSocket(); // Unblock accept calls - - for (auto &thread : m_workerThreads) { - if (thread.joinable()) { - thread.join(); // Wait for clean exit - } - } - // All threads stopped, sockets closed -} -``` - ---- - -## Low-Level API (Advanced Users) - -### When to Use - -Use the low-level API when you need: -- Custom threading models -- Integration with existing event loops -- Fine-grained control over connection acceptance -- Custom timeout handling - -### Usage Pattern - -```cpp -DoIPServer server; -server.setupTcpSocket(); -server.setupUdpSocket(); - -// You manage the threading -std::thread udpThread([&]() { - while (running) { - server.receiveUdpMessage(); - } -}); - -std::thread tcpThread([&]() { - server.setupTcpSocket(); - while (running) { - auto connection = server.waitForTcpConnection(); - if (connection) { - // You manage the connection lifecycle - DoIPServerModel model = createModel(); - connection->setServerModel(model); - - while (connection->isSocketActive()) { - connection->receiveTcpMessage(); - } - } - } -}); -``` - ---- - -## Callback Interface: DoIPServerModel - -Each connection is configured with a `DoIPServerModel` that defines its behavior: - -```cpp -struct DoIPServerModel { - // Required: Server's logical address - DoIPAddress serverAddress; - - // Optional callbacks: - CloseConnectionHandler onCloseConnection; - DiagnosticMessageHandler onDiagnosticMessage; - DiagnosticNotificationHandler onDiagnosticNotification; -}; -``` - -### Callback Signatures - -```cpp -// Called when connection closes -using CloseConnectionHandler = std::function; - -// Called when diagnostic message arrives -// Return std::nullopt for ACK, or DoIPNegativeDiagnosticAck for NACK -using DiagnosticMessageHandler = - std::function; - -// Called after sending diagnostic ACK/NACK -using DiagnosticNotificationHandler = - std::function; -``` - ---- - -## Performance Considerations - -### Thread Pooling vs Thread-per-Connection - -The current implementation uses **thread-per-connection** (detached threads): - -**Pros:** -- Simple, clean code -- Excellent for low-to-moderate connection counts (<100) -- No complex synchronization between connections -- Each connection has dedicated CPU time - -**Cons:** -- Does not scale to thousands of connections -- Small overhead per thread - -**Future Enhancement:** -For high-scale deployments, a thread pool could be added: - -```cpp -// Potential future API -server.start(onConnectionAccepted, - /*sendAnnouncements=*/true, - /*workerThreads=*/4); // Thread pool size -``` - -### Memory Usage - -Per active connection: -- DoIPConnection object: ~256 bytes -- Thread stack: 2-8 MB (OS dependent) -- TCP buffers: ~4 KB (receive buffer) - -For 10 concurrent connections: ~25 MB - ---- - -## Signal Handling - -The high-level API is designed to work with POSIX signals: - -```cpp -static std::atomic g_shutdown{false}; - -void signalHandler(int sig) { - g_shutdown.store(true); -} - -int main() { - signal(SIGINT, signalHandler); - signal(SIGTERM, signalHandler); - - server.start(onConnectionAccepted); - - while (!g_shutdown.load()) { - std::this_thread::sleep_for(100ms); - } - - server.stop(); // Clean shutdown -} -``` - ---- - -## Comparison with Other Patterns - -### vs. libevent/libev (Event Loop) - -**Event Loop:** -```cpp -// Pseudo-code -while (running) { - events = epoll_wait(epollfd, ...); - for (event : events) { - handle(event); - } -} -``` - -**DoIPServer High-Level API:** -```cpp -server.start(onConnectionAccepted); -// That's it! -``` - -**Trade-off:** Event loops are more scalable but require significant boilerplate. DoIPServer prioritizes simplicity for typical automotive use cases (<100 connections). - -### vs. Boost.Asio (Async I/O) - -**Boost.Asio:** -```cpp -async_accept(socket, [](error_code ec, socket s) { - async_read(s, buffer, [](error_code ec, size_t n) { - // nested callbacks... - }); -}); -io_context.run(); -``` - -**DoIPServer:** -```cpp -model.onDiagnosticMessage = [](const DoIPMessage &msg) { - // synchronous, clean callback -}; -server.start(onConnectionAccepted); -``` - -**Trade-off:** Asio is more powerful but complex. DoIPServer uses synchronous I/O with threads for simplicity. - ---- - -## Best Practices - -1. **Always call stop() before destruction** (or rely on RAII) -2. **Keep callbacks fast** - they run on connection threads -3. **Use atomic/mutex for shared state** accessed from callbacks -4. **Return std::nullopt from onConnectionAccepted** to rate-limit connections -5. **Set reasonable timeouts** in socket operations -6. **Handle exceptions in callbacks** - they're on background threads - ---- - -## Examples - -See: -- `examples/exampleDoIPServerSimple.cpp` - High-level API usage -- `examples/exampleDoIPServer.cpp` - Low-level API usage (manual mode) diff --git a/doc/Timers.md b/doc/Timers.md deleted file mode 100644 index 7203516..0000000 --- a/doc/Timers.md +++ /dev/null @@ -1,225 +0,0 @@ -Great question! The ISO 13400 timing mechanisms can indeed be confusing. Let me clarify the different timers and when they're used: - -## DoIP Server Timers Explained - -### 1. **T_TCP_Initial_Inactivity (default: 2 seconds)** - -**When started:** Immediately when a TCP socket connection is established (before any DoIP messages are received) - -**When stopped:** When the first valid DoIP message is received (typically a Routing Activation Request) - -**Purpose:** Protects against clients that open a TCP connection but never send anything. This is a "connection handshake timeout." - -**Action on timeout:** Close the TCP socket immediately - -**Key point:** This timer is ONLY active in the very beginning, before any DoIP communication has started. - ---- - -### 2. **T_TCP_General_Inactivity (default: 5 minutes)** - -**When started:** After a Routing Activation Response with successful activation code (0x10) is sent - -**When stopped/reset:** -- When ANY valid DoIP message is received from the client (Diagnostic Message, Alive Check Response, etc.) -- When the timer expires (not restarted, triggers alive check mechanism instead) - -**Purpose:** Detects if the client has stopped communicating after routing was successfully activated - -**Action on timeout:** Does NOT immediately close the connection! Instead, it triggers the Alive Check mechanism (see below) - -**Key point:** This is the main "keep-alive" timer during normal operation - ---- - -### 3. **T_TCP_Alive_Check (default: 500 milliseconds)** - -**When started:** -- After T_TCP_General_Inactivity expires AND the server sends an Alive Check Request -- Restarted after each retry if no response is received - -**When stopped:** When an Alive Check Response is received from the client - -**Purpose:** Gives the client a chance to prove it's still alive before closing the connection - -**Action on timeout:** -- If retry count < max retries (typically 3): Send another Alive Check Request and restart timer -- If max retries reached: Close the TCP connection - -**Key point:** This is a "last chance" mechanism before disconnecting - ---- - -## Timeline Example - -Here's a practical timeline showing when each timer is active: - -``` -Time Event Active Timer(s) ----- ----- --------------- -0ms TCP socket established T_TCP_Initial_Inactivity - [2s countdown starts] - -1500ms Routing Activation Req received T_TCP_Initial_Inactivity (stopped) - Server sends Routing Act. Res - [Routing activated] T_TCP_General_Inactivity starts - [5min countdown starts] - -10s Diagnostic Message received T_TCP_General_Inactivity (reset) - [5min countdown restarts] - -30s Another Diagnostic Msg received T_TCP_General_Inactivity (reset) - [5min countdown restarts] - -... [Client stops sending anything] ... - -5min (silence from client) T_TCP_General_Inactivity expires! -30s Server sends Alive Check Request - T_TCP_Alive_Check starts - [500ms countdown starts] - -5min (no response) T_TCP_Alive_Check timeout -30.5s Retry 1: Send Alive Check Request - T_TCP_Alive_Check restarts - -5min (no response) T_TCP_Alive_Check timeout -31s Retry 2: Send Alive Check Request - T_TCP_Alive_Check restarts - -5min Alive Check Response received! T_TCP_Alive_Check (stopped) -31.3s T_TCP_General_Inactivity (restarted) - [5min countdown restarts] - -... [normal operation continues] ... -``` - ---- - -## Alternative Scenario: Client Dies - -``` -Time Event Active Timer(s) ----- ----- --------------- -0ms TCP socket established T_TCP_Initial_Inactivity - -1500ms Routing Activation Req received T_TCP_Initial_Inactivity (stopped) - Routing activated T_TCP_General_Inactivity starts - -... [Client process crashes] ... - -5min (silence) T_TCP_General_Inactivity expires -1.5s Server sends Alive Check Request - T_TCP_Alive_Check starts - -5min (no response) T_TCP_Alive_Check timeout -2s Retry 1: Send Alive Check Request - -5min (no response) T_TCP_Alive_Check timeout -2.5s Retry 2: Send Alive Check Request - -5min (no response) T_TCP_Alive_Check timeout -3s Retry 3: Send Alive Check Request - -5min (no response) T_TCP_Alive_Check timeout -3.5s Max retries reached - **Connection closed** -``` - ---- - -## Key Differences Summary - -| Timer | Phase | Timeout Action | Can Reset? | -|-------|-------|----------------|------------| -| T_TCP_Initial_Inactivity | Before routing activation | Close immediately | No - one-shot | -| T_TCP_General_Inactivity | After routing activation | Start alive check | Yes - on any message | -| T_TCP_Alive_Check | During alive check procedure | Retry or close | Yes - per retry | - ---- - -## Corrected State Machine Code - -Here's the corrected timer logic: - -```cpp -void DoIPServerStateMachine::handleSocketInitialized( - DoIPEvent event, const DoIPMessage* msg) { - - switch (event) { - case DoIPEvent::ROUTING_ACTIVATION_RECEIVED: - // Stop initial inactivity timer - first message received! - stopTimer(TimerID::INITIAL_INACTIVITY); - // Process the routing activation... - transitionTo(DoIPState::WAIT_ROUTING_ACTIVATION); - // Note: Don't start general inactivity yet - - // wait until routing is successfully activated - break; - - case DoIPEvent::INITIAL_INACTIVITY_TIMEOUT: - // No message received within 2 seconds - close connection - transitionTo(DoIPState::FINALIZE); - break; - - default: - break; - } -} - -void DoIPServerStateMachine::handleWaitRoutingActivation( - DoIPEvent event, const DoIPMessage* msg) { - - switch (event) { - case DoIPEvent::ROUTING_ACTIVATION_RECEIVED: { - if (msg) { - uint16_t source_address = msg->getSourceAddress(); - uint8_t response_code = 0x10; // Success - - sendRoutingActivationResponse(source_address, response_code); - active_source_address_ = source_address; - - // NOW start general inactivity timer (after successful activation) - startTimer(TimerID::GENERAL_INACTIVITY, - config_.general_inactivity_timeout); - - transitionTo(DoIPState::ROUTING_ACTIVATED); - } - break; - } - - default: - break; - } -} - -void DoIPServerStateMachine::handleRoutingActivated( - DoIPEvent event, const DoIPMessage* msg) { - - switch (event) { - case DoIPEvent::DIAGNOSTIC_MESSAGE_RECEIVED: - // Any message resets the general inactivity timer - stopTimer(TimerID::GENERAL_INACTIVITY); - startTimer(TimerID::GENERAL_INACTIVITY, - config_.general_inactivity_timeout); - - // Process the diagnostic message... - if (msg) { - sendDiagnosticMessageAck(msg->getSourceAddress()); - } - break; - - case DoIPEvent::GENERAL_INACTIVITY_TIMEOUT: - // Client hasn't sent anything for 5 minutes - // Don't close yet - give it a chance with alive check - alive_check_retry_count_ = 0; - sendAliveCheckRequest(); - startTimer(TimerID::ALIVE_CHECK, config_.alive_check_timeout); - transitionTo(DoIPState::WAIT_ALIVE_CHECK_RESPONSE); - break; - - default: - break; - } -} -``` - -Does this clarify the timing mechanisms? The key insight is that **T_TCP_General_Inactivity doesn't directly close the connection** - it triggers the alive check mechanism, which gives the client multiple chances (with T_TCP_Alive_Check) before finally closing. \ No newline at end of file diff --git a/doc/developer documentation.pdf b/doc/developer documentation.pdf deleted file mode 100644 index c19c461..0000000 Binary files a/doc/developer documentation.pdf and /dev/null differ diff --git a/doc/diagrams/ServerModel.puml b/doc/diagrams/ServerModel.puml new file mode 100644 index 0000000..e19a363 --- /dev/null +++ b/doc/diagrams/ServerModel.puml @@ -0,0 +1,44 @@ +@startuml +title DoIP ServerModel Overview + +actor Client +participant "DoIPServer" as Server +participant "DoIPConnection" as Connection +participant "DoIPServerModel" as Model +participant "UDS Backend / CAN" as Backend + +' Notes: +' - Client sends Diagnostic Request (payload type 0x8001) +' - Server sends immediate Diagnostic Ack (payload type 0x8002) after receiving the message +' - If downstream forwarding occurs, the downstream response is sent as a Diagnostic Message (0x8001) back to the client + +Client -> Server: TCP connect +Server -> Connection: createConnection(ctx) +Connection -> Model: onOpenConnection(ctx) + +Client -> Server : Routing Activation Request (0x0004) +Server -> Connection: Routing Activation Request (0x0004) +Connection -> Model: onRoutingActivationRequest(ctx, msg, callback) +Model -> Connection: invoke callback with Routing Activation Response +Connection -> Client: Routing Activation Response (0x0005) + +' -- Incoming diagnostic message (payload type 0x8001) +Client -> Connection: Diagnostic Message (0x8001) + +' -- Server sends immediate Diagnostic Ack (0x8002) +Connection -> Client: Diagnostic Ack (0x8002) + +' -- If server forwards to downstream (only for 0x8001): +alt downstream forwarding + Connection -> Model: onDownstreamRequest(ctx, msg, callback) + Model -> Backend: forward diagnostic payload (CAN) + Backend -> Model: downstream response (CAN) + Model -> Connection: invoke callback with downstream response + ' The downstream response is sent back to the client as a Diagnostic Message (0x8001) + Connection -> Client: Diagnostic Message (0x8001) [downstream response] +end + +' -- Final notification callback to application +Connection -> Model: onDiagnosticNotification(ctx, ack) + +@enduml diff --git a/doc/diagrams/ServerModel.svg b/doc/diagrams/ServerModel.svg new file mode 100644 index 0000000..d231474 --- /dev/null +++ b/doc/diagrams/ServerModel.svg @@ -0,0 +1,47 @@ +DoIP ServerModel OverviewClientClientDoIPServerDoIPServerDoIPConnectionDoIPConnectionDoIPServerModelDoIPServerModelUDS Backend / CANUDS Backend / CANTCP connectcreateConnection(ctx)onOpenConnection(ctx)Routing Activation Request (0x0004)Routing Activation Request (0x0004)onRoutingActivationRequest(ctx, msg, callback)invoke callback with Routing Activation ResponseRouting Activation Response (0x0005)Diagnostic Message (0x8001)Diagnostic Ack (0x8002)alt[downstream forwarding]onDownstreamRequest(ctx, msg, callback)forward diagnostic payload (CAN)downstream response (CAN)invoke callback with downstream responseDiagnostic Message (0x8001) [downstream response]onDiagnosticNotification(ctx, ack) \ No newline at end of file diff --git a/doc/examples/python_ack_response_example.md b/doc/examples/python_ack_response_example.md new file mode 100644 index 0000000..79f8314 --- /dev/null +++ b/doc/examples/python_ack_response_example.md @@ -0,0 +1,58 @@ +# Python example: handling DoIP ack vs downstream response + +This short example shows how a DoIP/UDS client should treat the transport-level +Diagnostic Ack (payload type 0x8002) and the functional UDS response which +arrives later as a Diagnostic Message (payload type 0x8001). + +The `change-vin-and-reset.py` script in `test/scripts/` already demonstrates +an end-to-end flow: it sends UDS requests and waits for functional responses. + +Key points for client implementers: + +- The DoIP server sends an immediate Diagnostic Ack (0x8002) after receiving + a Diagnostic Message (0x8001). This confirms transport reception only. +- The functional UDS response may arrive later as another Diagnostic Message + (0x8001). The client must wait for that message to obtain the service + result. +- When using `udsoncan`, the library abstracts away DoIP transport details. + If you implement a custom connector, ensure you forward both the ack and + subsequent response frames appropriately. + +Minimal pseudo-code (conceptual): + +```python +# Using doipclient + udsoncan +from doipclient import DoIPClient +from doipclient.connectors import DoIPClientUDSConnector +from udsoncan.client import Client + +client = DoIPClient('127.0.0.1', 0x00E0) +conn = DoIPClientUDSConnector(client) + +with Client(conn, request_timeout=2) as u: + # change session -> server will ack on transport, then send response + resp = u.change_session(0x01) + # udsoncan waits for the real UDS response; transport ACK is handled internally + print('Session change response:', resp) +``` + +If you handle sockets yourself, inspect packet payload types: treat 0x8002 +as transport-level ack and do not consider it the functional response. + +```python +# Pseudocode for low-level handling +sock = setup_doip_socket() +while True: + pkt = sock.recv() + payload_type = parse_payload_type(pkt) + if payload_type == 0x8002: + # transport ack - ignore for UDS result + continue + if payload_type == 0x8001: + # functional UDS response - process + handle_uds_response(pkt) +``` + +For completeness, see `test/scripts/change-vin-and-reset.py` which performs a +full read/write VIN and ECU reset sequence using `udsoncan` with the DoIP +connector. diff --git a/doc/sockets/SOCKET_CASTING_GUIDE.md b/doc/sockets/SOCKET_CASTING_GUIDE.md deleted file mode 100644 index 064bb5b..0000000 --- a/doc/sockets/SOCKET_CASTING_GUIDE.md +++ /dev/null @@ -1,359 +0,0 @@ -# Socket Casting Best Practices in C++ - -## Das Problem - -Bei Socket-APIs musst du oft `reinterpret_cast` verwenden: - -```cpp -// ❌ Typisches Problem -sockaddr_in addr; -bind(sockfd, reinterpret_cast(&addr), sizeof(addr)); - -std::vector data; -send(sockfd, reinterpret_cast(data.data()), data.size(), 0); -``` - -**Probleme mit reinterpret_cast:** -- Unsicheres Type-Punning (kann Strict Aliasing Rules verletzen) -- Keine Compile-Zeit-Prüfung -- Fehleranfällig -- Schwer zu lesen - -## Lösungen - -### 1. SocketAddress Wrapper (✅ Empfohlen für C++11+) - -**Problem:** `sockaddr*` Cast bei bind/connect/accept - -**Lösung:** Union-basierter Wrapper - -```cpp -class SocketAddress { -public: - static SocketAddress createIPv4(const char* ip, uint16_t port) { - SocketAddress addr; - auto& ipv4 = addr.storage_.ipv4; - ipv4.sin_family = AF_INET; - ipv4.sin_port = htons(port); - inet_pton(AF_INET, ip, &ipv4.sin_addr); - addr.length_ = sizeof(sockaddr_in); - return addr; - } - - // ✅ Sichere Konvertierung - sockaddr* get() { return &storage_.base; } - const sockaddr* get() const { return &storage_.base; } - socklen_t length() const { return length_; } - -private: - union Storage { - sockaddr base; - sockaddr_in ipv4; - sockaddr_in6 ipv6; - sockaddr_storage storage; - } storage_; - - socklen_t length_ = 0; -}; - -// Verwendung -auto addr = SocketAddress::createIPv4("127.0.0.1", 13400); -bind(sockfd, addr.get(), addr.length()); // ✅ Kein reinterpret_cast! -``` - -**Warum funktioniert das?** -- Union für Type-Punning ist vom C++-Standard erlaubt (seit C++11) -- Zugriff auf inaktive Union-Member ist definiertes Verhalten -- Common initial sequence Regel garantiert Kompatibilität - -**Vorteile:** -- ✅ Vollständig typsicher -- ✅ Keine reinterpret_casts -- ✅ C++11 kompatibel -- ✅ Unterstützt IPv4 und IPv6 -- ✅ Keine Performance-Overhead - -### 2. std::span für send/recv (✅ Empfohlen für C++20) - -**Problem:** Cast von `uint8_t*` zu `char*` bei send/recv - -**Lösung:** std::span + std::as_bytes() - -```cpp -template -ssize_t send(std::span data, int flags = 0) { - static_assert(std::is_trivially_copyable_v, - "Type must be trivially copyable"); - - // ✅ std::as_bytes eliminiert Casts - auto bytes = std::as_bytes(data); - return ::send(fd_, bytes.data(), bytes.size(), flags); -} - -// Verwendung -std::vector data = {0x01, 0x02, 0x03}; -socket.send(std::span{data}); // ✅ Kein Cast! - -std::array buffer; -socket.recv(std::span{buffer}); // ✅ Typsicher! -``` - -**Vorteile:** -- ✅ Generisch für alle Typen -- ✅ Compile-Zeit Type-Checks -- ✅ std::as_bytes() ist vom Standard definiert -- ✅ Keine Performance-Overhead (Zero-Cost Abstraction) - -### 3. Socket Wrapper-Klasse (✅ Beste Gesamtlösung) - -Kombiniere beide Ansätze in einer Wrapper-Klasse: - -```cpp -class Socket { -public: - // RAII: Automatisches close() - ~Socket() { close(); } - - // Move-only - Socket(Socket&& other) noexcept; - Socket& operator=(Socket&& other) noexcept; - - // Factory - static Socket createTCP(); - static Socket createUDP(); - - // ✅ Typsichere Methoden - bool bind(const SocketAddress& addr); - bool connect(const SocketAddress& addr); - bool listen(int backlog = 5); - Socket accept(SocketAddress* addr = nullptr); - - // ✅ Generisches send/recv - template - ssize_t send(std::span data, int flags = 0); - - template - ssize_t recv(std::span buffer, int flags = 0); - - // Convenience für vector - ssize_t send(const std::vector& data, int flags = 0); - ssize_t recv(std::vector& buffer, int flags = 0); - -private: - int fd_; -}; - -// Verwendung -auto server = Socket::createTCP(); -auto addr = SocketAddress::createIPv4("0.0.0.0", 13400); - -server.bind(addr); -server.listen(); - -auto client = server.accept(); - -std::vector data = {0x01, 0x02}; -client.send(data); // ✅ Kein reinterpret_cast! -``` - -**Vorteile:** -- ✅ Komplett typsicher -- ✅ RAII (kein Vergessen von close()) -- ✅ Move-only Semantik (keine versehentlichen Kopien) -- ✅ Saubere, moderne API -- ✅ Zero-Cost Abstraction - -### 4. std::byte für Rohdaten (C++17) - -**Problem:** uint8_t ist eigentlich ein Integer-Typ - -**Lösung:** std::byte für semantisch korrekte Bytes - -```cpp -ssize_t send(const std::byte* data, size_t size, int flags = 0) { - return ::send(fd_, data, size, flags); -} - -// Verwendung -std::vector data = { - std::byte{0x01}, - std::byte{0x02} -}; -socket.send(data); -``` - -**Vorteile:** -- ✅ Semantisch korrekter (Byte ≠ Integer) -- ✅ Verhindert arithmetische Operationen -- ✅ Explizite Konvertierung nötig - -**Nachteile:** -- ⚠️ Verbosity (jedes Byte braucht std::byte{}) -- ⚠️ Konvertierung von/zu uint8_t nötig - -### 5. Der erlaubte Cast: uint8_t* → char* - -**Wichtig:** Dieser spezifische Cast ist vom Standard erlaubt! - -```cpp -std::vector data; - -// ✅ Dieser Cast ist OK (vom Standard garantiert) -send(sockfd, reinterpret_cast(data.data()), data.size(), 0); -``` - -**Warum ist das erlaubt?** -- `char`, `signed char`, `unsigned char` haben spezielle Aliasing-Regeln -- Sie dürfen für Zugriff auf jede Byte-Sequenz verwendet werden -- uint8_t ist typedef für unsigned char - -**Aber:** Auch wenn erlaubt, ist ein Wrapper trotzdem besser! - -## Vergleichstabelle - -| Ansatz | C++ Version | Typsicherheit | Performance | Empfehlung | -|--------|-------------|---------------|-------------|------------| -| `reinterpret_cast` | Alle | ❌ Niedrig | ✅ Gut | ❌ Vermeiden | -| Union Wrapper | C++11+ | ✅ Hoch | ✅ Gut | ✅ Für sockaddr | -| std::span | C++20 | ✅ Sehr hoch | ✅ Gut | ✅ Für send/recv | -| std::byte | C++17 | ✅ Hoch | ✅ Gut | ⚠️ Verbose | -| uint8_t→char cast | Alle | ⚠️ Mittel | ✅ Gut | ⚠️ Als Fallback OK | -| Socket Wrapper | C++11+ | ✅ Sehr hoch | ✅ Gut | ✅✅ Beste Lösung | - -## Empfohlene Implementierung für dein Projekt - -Für libdoip würde ich empfehlen: - -### Phase 1: SocketAddress Wrapper (sofort) - -```cpp -// In socket_utils.h -namespace doip { - -class SocketAddress { - // ... wie oben -}; - -} // namespace doip -``` - -### Phase 2: Socket Wrapper (mittelfristig) - -```cpp -namespace doip { - -class Socket { - // ... RAII + typsichere Methoden -}; - -} // namespace doip -``` - -### Phase 3: Integration mit DoIPMessage (langfristig) - -```cpp -// DoIPMessage hat bereits getData() -auto msg = doip::message::makeDiagnosticMessage(...); - -// ✅ Perfekte Integration -socket.send(msg.getData()); - -// Oder mit ByteArrayRef -auto [data, size] = msg.getData(); -socket.send(data, size); -``` - -## Quick Reference - -### Alte Methode vs. Neue Methode - -**Bind:** -```cpp -// ❌ Alt -sockaddr_in addr; -bind(sockfd, reinterpret_cast(&addr), sizeof(addr)); - -// ✅ Neu -auto addr = SocketAddress::createIPv4("127.0.0.1", 13400); -socket.bind(addr); -``` - -**Send:** -```cpp -// ❌ Alt -std::vector data; -send(sockfd, reinterpret_cast(data.data()), data.size(), 0); - -// ✅ Neu (C++20) -socket.send(std::span{data}); - -// ✅ Neu (C++11) -socket.send(data); // Wrapper übernimmt den Cast -``` - -**Accept:** -```cpp -// ❌ Alt -sockaddr_in client_addr; -socklen_t len = sizeof(client_addr); -int client_fd = accept(sockfd, reinterpret_cast(&client_addr), &len); - -// ✅ Neu -SocketAddress client_addr; -auto client_socket = server.accept(&client_addr); -``` - -## Performance - -**Keine Overhead!** - -Alle vorgeschlagenen Lösungen sind Zero-Cost Abstractions: -- Compiler optimiert Wrapper weg -- std::span ist nur Pointer + Size -- Union hat keine Runtime-Kosten -- Inline-Funktionen werden eingefügt - -**Benchmark-Ergebnis (typisch):** -``` -reinterpret_cast: 100 ns -Union wrapper: 100 ns (identisch!) -std::span: 100 ns (identisch!) -Socket wrapper: 100 ns (identisch!) -``` - -## Compiler-Unterstützung - -| Feature | GCC | Clang | MSVC | -|---------|-----|-------|------| -| Union Type-Punning | ✅ 4.8+ | ✅ 3.4+ | ✅ 2015+ | -| std::byte | ✅ 7+ | ✅ 5+ | ✅ 2017+ | -| std::span | ✅ 10+ | ✅ 7+ | ✅ 2019+ | - -## Fazit - -**Für libdoip:** - -1. **Sofort:** SocketAddress Wrapper implementieren -2. **Mittelfristig:** Socket Wrapper-Klasse -3. **Optional:** std::span für C++20 (wenn gewünscht) - -**Code wird:** -- ✅ Typsicherer -- ✅ Lesbarer -- ✅ Wartbarer -- ✅ Moderner -- ✅ Genauso performant - -**Minimale Migration:** -```cpp -// Alte Calls können sogar kompatibel bleiben: -class Socket { - // Neue API - ssize_t send(const std::vector& data); - - // Legacy-Kompatibilität - ssize_t send(const void* data, size_t size) { - return ::send(fd_, data, size, 0); - } -}; -``` diff --git a/doc/sockets/socket_casting_alternatives.cpp b/doc/sockets/socket_casting_alternatives.cpp deleted file mode 100644 index c281b39..0000000 --- a/doc/sockets/socket_casting_alternatives.cpp +++ /dev/null @@ -1,526 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// ============================================================================ -// Problem: Typische Socket-API Verwendung mit reinterpret_cast -// ============================================================================ - -namespace unsafe_example { - -void typical_socket_code() { - int sockfd = socket(AF_INET, SOCK_STREAM, 0); - - sockaddr_in server_addr{}; - server_addr.sin_family = AF_INET; - server_addr.sin_port = htons(13400); - inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr); - - // ❌ Problematisch: reinterpret_cast - bind(sockfd, reinterpret_cast(&server_addr), sizeof(server_addr)); - - // ❌ Bei send/recv auch problematisch - std::vector data = {0x01, 0x02, 0x03}; - send(sockfd, reinterpret_cast(data.data()), data.size(), 0); -} - -} // namespace unsafe_example - -// ============================================================================ -// Lösung 1: std::bit_cast (C++20) für Type-Punning -// ============================================================================ - -namespace solution1_bitcast { - -#if __cplusplus >= 202002L -#include - -// std::bit_cast ist der sicherste Weg für Type-Punning -// Aber funktioniert nur für gleich große Typen - -// Für kleine Strukturen OK: -void example_bitcast() { - struct A { int x; }; - struct B { int y; }; - - A a{42}; - B b = std::bit_cast(a); // ✅ Sicher -} -#endif - -} // namespace solution1_bitcast - -// ============================================================================ -// Lösung 2: Wrapper-Klassen mit Union (für sockaddr) -// ============================================================================ - -namespace solution2_union { - -/** - * @brief Type-safe socket address wrapper - * - * Verwendet union für legales Type-Punning (seit C++11) - */ -class SocketAddress { -public: - SocketAddress() { - std::memset(&storage_, 0, sizeof(storage_)); - } - - // IPv4 - static SocketAddress createIPv4(const char* ip, uint16_t port) { - SocketAddress addr; - auto& ipv4 = addr.storage_.ipv4; - ipv4.sin_family = AF_INET; - ipv4.sin_port = htons(port); - inet_pton(AF_INET, ip, &ipv4.sin_addr); - addr.length_ = sizeof(sockaddr_in); - return addr; - } - - // IPv6 - static SocketAddress createIPv6(const char* ip, uint16_t port) { - SocketAddress addr; - auto& ipv6 = addr.storage_.ipv6; - ipv6.sin6_family = AF_INET6; - ipv6.sin6_port = htons(port); - inet_pton(AF_INET6, ip, &ipv6.sin6_addr); - addr.length_ = sizeof(sockaddr_in6); - return addr; - } - - // ✅ Sichere Konvertierung zu sockaddr* - sockaddr* get() { return &storage_.base; } - const sockaddr* get() const { return &storage_.base; } - - socklen_t length() const { return length_; } - socklen_t* length_ptr() { return &length_; } - - // Typsicherer Zugriff - bool isIPv4() const { return storage_.base.sa_family == AF_INET; } - bool isIPv6() const { return storage_.base.sa_family == AF_INET6; } - - sockaddr_in& asIPv4() { return storage_.ipv4; } - const sockaddr_in& asIPv4() const { return storage_.ipv4; } - - sockaddr_in6& asIPv6() { return storage_.ipv6; } - const sockaddr_in6& asIPv6() const { return storage_.ipv6; } - -private: - union Storage { - sockaddr base; - sockaddr_in ipv4; - sockaddr_in6 ipv6; - sockaddr_storage storage; - } storage_; - - socklen_t length_ = 0; -}; - -void example_usage() { - int sockfd = socket(AF_INET, SOCK_STREAM, 0); - - // ✅ Typsicher ohne reinterpret_cast - auto addr = SocketAddress::createIPv4("127.0.0.1", 13400); - bind(sockfd, addr.get(), addr.length()); - - // ✅ Auch bei accept - SocketAddress client_addr; - int client_fd = accept(sockfd, client_addr.get(), client_addr.length_ptr()); - - if (client_addr.isIPv4()) { - char ip[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, &client_addr.asIPv4().sin_addr, ip, sizeof(ip)); - std::cout << "IPv4 client: " << ip << std::endl; - } -} - -} // namespace solution2_union - -// ============================================================================ -// Lösung 3: std::span für send/recv (C++20) -// ============================================================================ - -namespace solution3_span { - -/** - * @brief Type-safe socket send/recv wrapper - */ -class Socket { -public: - explicit Socket(int fd) : fd_(fd) {} - - // ✅ Send mit std::span - kein cast nötig - template - ssize_t send(std::span data, int flags = 0) { - static_assert(std::is_trivially_copyable_v, - "Type must be trivially copyable for socket transmission"); - - // std::span gibt uns bereits void* kompatible Daten - auto bytes = std::as_bytes(data); - return ::send(fd_, bytes.data(), bytes.size(), flags); - } - - // ✅ Recv mit std::span - template - ssize_t recv(std::span buffer, int flags = 0) { - static_assert(std::is_trivially_copyable_v, - "Type must be trivially copyable for socket transmission"); - - auto bytes = std::as_writable_bytes(buffer); - return ::recv(fd_, bytes.data(), bytes.size(), flags); - } - - // Überladungen für ByteArray/vector - ssize_t send(const std::vector& data, int flags = 0) { - return send(std::span{data}, flags); - } - - ssize_t recv(std::vector& buffer, int flags = 0) { - return recv(std::span{buffer}, flags); - } - -private: - int fd_; -}; - -void example_usage() { - Socket sock(/* fd */); - - // ✅ Kein reinterpret_cast! - std::vector data = {0x01, 0x02, 0x03, 0x04}; - sock.send(data); - - // ✅ Auch mit Arrays - std::array buffer; - sock.recv(std::span{buffer}); - - // ✅ Oder mit rohen Pointern - uint8_t raw_buffer[512]; - sock.recv(std::span{raw_buffer}); -} - -} // namespace solution3_span - -// ============================================================================ -// Lösung 4: std::byte für Byte-Buffer (C++17) -// ============================================================================ - -namespace solution4_byte { - -/** - * @brief Socket wrapper mit std::byte - */ -class Socket { -public: - explicit Socket(int fd) : fd_(fd) {} - - // ✅ std::byte ist semantisch korrekter als uint8_t für Bytes - ssize_t send(const std::byte* data, size_t size, int flags = 0) { - return ::send(fd_, data, size, flags); - } - - ssize_t recv(std::byte* data, size_t size, int flags = 0) { - return ::recv(fd_, data, size, flags); - } - - // Convenience für vector - ssize_t send(const std::vector& data, int flags = 0) { - return send(data.data(), data.size(), flags); - } - - ssize_t recv(std::vector& data, int flags = 0) { - return recv(data.data(), data.size(), flags); - } - -private: - int fd_; -}; - -void example_usage() { - Socket sock(/* fd */); - - // ✅ std::byte ist typsicherer für Rohdaten - std::vector data = { - std::byte{0x01}, std::byte{0x02}, std::byte{0x03} - }; - sock.send(data); - - // Konvertierung von/zu uint8_t wenn nötig - std::vector uint8_data = {0x01, 0x02, 0x03}; - auto byte_data = std::as_bytes(std::span{uint8_data}); - sock.send(reinterpret_cast(byte_data.data()), - byte_data.size()); -} - -} // namespace solution4_byte - -// ============================================================================ -// Lösung 5: Memcpy statt reinterpret_cast (für alte Compiler) -// ============================================================================ - -namespace solution5_memcpy { - -/** - * @brief Sichere Alternative für C++11/14/17 - * - * memcpy ist vom Standard garantiert korrekt für Type-Punning - */ -class Socket { -public: - explicit Socket(int fd) : fd_(fd) {} - - // ✅ memcpy ist sicherer als reinterpret_cast - template - ssize_t send(const T* data, size_t count, int flags = 0) { - static_assert(std::is_trivially_copyable::value, - "Type must be trivially copyable"); - - // memcpy ist vom Standard erlaubt für Type-Punning - std::vector buffer(count * sizeof(T)); - std::memcpy(buffer.data(), data, buffer.size()); - - return ::send(fd_, buffer.data(), buffer.size(), flags); - } - - // Aber für vector können wir direkten Zugriff nutzen - ssize_t sendBytes(const std::vector& data, int flags = 0) { - // ✅ Der Cast von uint8_t* zu char* ist erlaubt - return ::send(fd_, - reinterpret_cast(data.data()), - data.size(), - flags); - } -}; - -} // namespace solution5_memcpy - -// ============================================================================ -// Lösung 6: Kombinierter moderner Socket-Wrapper -// ============================================================================ - -namespace solution6_complete { - -/** - * @brief Moderne, typsichere Socket-Klasse - * - * Kombiniert alle Best Practices - */ -class Socket { -public: - Socket() : fd_(-1) {} - explicit Socket(int fd) : fd_(fd) {} - - // RAII: Automatisches Schließen - ~Socket() { close(); } - - // Move-only (kein Kopieren von File Descriptors) - Socket(const Socket&) = delete; - Socket& operator=(const Socket&) = delete; - - Socket(Socket&& other) noexcept : fd_(other.fd_) { - other.fd_ = -1; - } - - Socket& operator=(Socket&& other) noexcept { - if (this != &other) { - close(); - fd_ = other.fd_; - other.fd_ = -1; - } - return *this; - } - - // Factory-Methoden - static Socket createTCP() { - return Socket(::socket(AF_INET, SOCK_STREAM, 0)); - } - - static Socket createUDP() { - return Socket(::socket(AF_INET, SOCK_DGRAM, 0)); - } - - // Bind mit SocketAddress - bool bind(const solution2_union::SocketAddress& addr) { - return ::bind(fd_, addr.get(), addr.length()) == 0; - } - - // Connect mit SocketAddress - bool connect(const solution2_union::SocketAddress& addr) { - return ::connect(fd_, addr.get(), addr.length()) == 0; - } - - // Listen - bool listen(int backlog = 5) { - return ::listen(fd_, backlog) == 0; - } - - // Accept - Socket accept(solution2_union::SocketAddress* addr = nullptr) { - if (addr) { - return Socket(::accept(fd_, addr->get(), addr->length_ptr())); - } else { - return Socket(::accept(fd_, nullptr, nullptr)); - } - } - - // ✅ Type-safe send mit std::span (C++20) -#if __cplusplus >= 202002L - template - ssize_t send(std::span data, int flags = 0) { - static_assert(std::is_trivially_copyable_v); - auto bytes = std::as_bytes(data); - return ::send(fd_, bytes.data(), bytes.size(), flags); - } - - template - ssize_t recv(std::span buffer, int flags = 0) { - static_assert(std::is_trivially_copyable_v); - auto bytes = std::as_writable_bytes(buffer); - return ::recv(fd_, bytes.data(), bytes.size(), flags); - } -#endif - - // ✅ Send/recv für vector (C++11 kompatibel) - ssize_t send(const std::vector& data, int flags = 0) { - return ::send(fd_, - reinterpret_cast(data.data()), - data.size(), - flags); - } - - ssize_t recv(std::vector& buffer, int flags = 0) { - return ::recv(fd_, - reinterpret_cast(buffer.data()), - buffer.size(), - flags); - } - - // ✅ Raw pointer interface - ssize_t send(const void* data, size_t size, int flags = 0) { - return ::send(fd_, data, size, flags); - } - - ssize_t recv(void* buffer, size_t size, int flags = 0) { - return ::recv(fd_, buffer, size, flags); - } - - // Getter - int fd() const { return fd_; } - bool isValid() const { return fd_ >= 0; } - - void close() { - if (fd_ >= 0) { - ::close(fd_); - fd_ = -1; - } - } - -private: - int fd_; -}; - -// ============================================================================ -// Verwendungsbeispiel -// ============================================================================ - -void example_server() { - using namespace solution2_union; - - // ✅ Kein reinterpret_cast in Sicht! - auto server_socket = Socket::createTCP(); - auto addr = SocketAddress::createIPv4("0.0.0.0", 13400); - - if (!server_socket.bind(addr)) { - std::cerr << "Bind failed\n"; - return; - } - - if (!server_socket.listen()) { - std::cerr << "Listen failed\n"; - return; - } - - std::cout << "Listening on port 13400...\n"; - - SocketAddress client_addr; - auto client_socket = server_socket.accept(&client_addr); - - if (client_socket.isValid()) { - std::cout << "Client connected\n"; - - // ✅ Type-safe senden - std::vector response = {0x01, 0x02, 0x03, 0x04}; - client_socket.send(response); - - // ✅ Type-safe empfangen - std::vector buffer(1024); - ssize_t n = client_socket.recv(buffer); - if (n > 0) { - buffer.resize(n); - std::cout << "Received " << n << " bytes\n"; - } - } -} - -void example_client() { - using namespace solution2_union; - - auto socket = Socket::createTCP(); - auto addr = SocketAddress::createIPv4("127.0.0.1", 13400); - - if (socket.connect(addr)) { - std::cout << "Connected!\n"; - - // ✅ Kein reinterpret_cast - std::vector request = {0x10, 0x01}; - socket.send(request); - - std::vector response(1024); - ssize_t n = socket.recv(response); - response.resize(n); - } -} - -} // namespace solution6_complete - -// ============================================================================ -// Zusammenfassung und Best Practices -// ============================================================================ - -int main() { - std::cout << "Socket Casting Best Practices:\n\n"; - - std::cout << "1. SocketAddress Wrapper (union-based)\n"; - std::cout << " ✅ Typsicher für bind/connect/accept\n"; - std::cout << " ✅ Keine reinterpret_casts\n"; - std::cout << " ✅ C++11 kompatibel\n\n"; - - std::cout << "2. std::span für send/recv (C++20)\n"; - std::cout << " ✅ Generisch und typsicher\n"; - std::cout << " ✅ Compile-time checks\n"; - std::cout << " ✅ std::as_bytes() eliminiert Casts\n\n"; - - std::cout << "3. std::byte für Bytebuffer (C++17)\n"; - std::cout << " ✅ Semantisch korrekter als uint8_t\n"; - std::cout << " ✅ Verhindert arithmetische Operationen\n\n"; - - std::cout << "4. RAII Socket Wrapper\n"; - std::cout << " ✅ Automatisches close()\n"; - std::cout << " ✅ Move-only Semantik\n"; - std::cout << " ✅ Saubere API\n\n"; - - std::cout << "5. uint8_t* zu char* Cast\n"; - std::cout << " ✅ Dieser Cast ist vom Standard erlaubt\n"; - std::cout << " ✅ Für Legacy-Kompatibilität OK\n\n"; - - return 0; -} - -} // namespace solution6_complete diff --git a/examples/ExampleDoIPServerModel.h b/examples/ExampleDoIPServerModel.h index 9e4a0c2..abb065a 100644 --- a/examples/ExampleDoIPServerModel.h +++ b/examples/ExampleDoIPServerModel.h @@ -23,7 +23,7 @@ class ExampleDoIPServerModel : public DoIPServerModel { onCloseConnection = [this](IConnectionContext &ctx, DoIPCloseReason reason) noexcept { (void)ctx; stopWorker(); - DOIP_LOG_WARN("Connection closed ({})", fmt::streamed(reason)); + LOG_DOIP_WARN("Connection closed ({})", fmt::streamed(reason)); }; onDiagnosticMessage = [this](IConnectionContext &ctx, const DoIPMessage &msg) noexcept -> DoIPDiagnosticAck { diff --git a/examples/exampleDoIPClient.cpp b/examples/exampleDoIPClient.cpp index 6955dc3..75e1deb 100644 --- a/examples/exampleDoIPClient.cpp +++ b/examples/exampleDoIPClient.cpp @@ -27,10 +27,10 @@ int main(int argc, char *argv[]) { string arg = argv[i]; if (arg == "--loopback") { serverAddress = "127.0.0.1"; - DOIP_LOG_INFO("Loopback mode enabled - using 127.0.0.1"); + LOG_DOIP_INFO("Loopback mode enabled - using 127.0.0.1"); } else if (arg == "--server" && i + 1 < argc) { serverAddress = argv[++i]; - DOIP_LOG_INFO("Using custom server address: {}", serverAddress); + LOG_DOIP_INFO("Using custom server address: {}", serverAddress); } else if (arg == "--help") { printUsage(argv[0]); return 0; @@ -41,24 +41,24 @@ int main(int argc, char *argv[]) { } } - DOIP_LOG_INFO("Starting DoIP Client"); + LOG_DOIP_INFO("Starting DoIP Client"); // Start UDP connections (don't start TCP yet) client.startUdpConnection(); client.startAnnouncementListener(); // Listen for Vehicle Announcements on port 13401 // Listen for Vehicle Announcements first - DOIP_LOG_INFO("Listening for Vehicle Announcements..."); + LOG_DOIP_INFO("Listening for Vehicle Announcements..."); client.receiveVehicleAnnouncement(); // Send Vehicle Identification Request to configured address if (client.sendVehicleIdentificationRequest(serverAddress.c_str()) > 0) { - DOIP_LOG_INFO("Vehicle Identification Request sent successfully"); + LOG_DOIP_INFO("Vehicle Identification Request sent successfully"); client.receiveUdpMessage(); } // Now start TCP connection for diagnostic communication - DOIP_LOG_INFO("Starting TCP connection for diagnostic messages"); + LOG_DOIP_INFO("Starting TCP connection for diagnostic messages"); client.startTcpConnection(); if (client.sendRoutingActivationRequest() < 0) { diff --git a/examples/exampleDoIPServer.cpp b/examples/exampleDoIPServer.cpp index 582f964..24c8a8a 100644 --- a/examples/exampleDoIPServer.cpp +++ b/examples/exampleDoIPServer.cpp @@ -28,7 +28,7 @@ void listenTcp(); * Check permantly if udp message was received */ void listenUdp() { - UDP_LOG_INFO("UDP listener thread started"); + LOG_UDP_INFO("UDP listener thread started"); while (serverActive) { ssize_t result = server.receiveUdpMessage(); // If timeout (result == 0), sleep briefly to prevent CPU spinning @@ -42,7 +42,7 @@ void listenUdp() { * Check permantly if tcp message was received */ void listenTcp() { - UDP_LOG_INFO("TCP listener thread started"); + LOG_UDP_INFO("TCP listener thread started"); while (true) { tcpConnection = server.waitForTcpConnection(); @@ -85,7 +85,7 @@ int main(int argc, char *argv[]) { string arg = argv[i]; if (arg == "--loopback") { useLoopback = true; - DOIP_LOG_INFO("Loopback mode enabled"); + LOG_DOIP_INFO("Loopback mode enabled"); } else if (arg == "--help") { printUsage(argv[0]); return 0; @@ -98,7 +98,7 @@ int main(int argc, char *argv[]) { // Configure logging doip::Logger::setLevel(spdlog::level::debug); - DOIP_LOG_INFO("Starting DoIP Server Example"); + LOG_DOIP_INFO("Starting DoIP Server Example"); ConfigureDoipServer(); @@ -108,25 +108,25 @@ int main(int argc, char *argv[]) { } if (!server.setupUdpSocket()) { - DOIP_LOG_CRITICAL("Failed to set up UDP socket"); + LOG_DOIP_CRITICAL("Failed to set up UDP socket"); return 1; } if (!server.setupTcpSocket()) { - DOIP_LOG_CRITICAL("Failed to set up TCP socket"); + LOG_DOIP_CRITICAL("Failed to set up TCP socket"); return 1; } serverActive = true; - DOIP_LOG_INFO("Starting UDP and TCP listener threads"); + LOG_DOIP_INFO("Starting UDP and TCP listener threads"); doipReceiver.push_back(thread(&listenUdp)); doipReceiver.push_back(thread(&listenTcp)); server.sendVehicleAnnouncement(); - DOIP_LOG_INFO("Vehicle announcement sent"); + LOG_DOIP_INFO("Vehicle announcement sent"); doipReceiver.at(0).join(); doipReceiver.at(1).join(); - DOIP_LOG_INFO("DoIP Server Example terminated"); + LOG_DOIP_INFO("DoIP Server Example terminated"); return 0; } diff --git a/examples/exampleDoIPServerSimple.cpp b/examples/exampleDoIPServerSimple.cpp index 2ea9e0a..4e04e04 100644 --- a/examples/exampleDoIPServerSimple.cpp +++ b/examples/exampleDoIPServerSimple.cpp @@ -111,7 +111,7 @@ int main(int argc, char *argv[]) { // Configure logging doip::Logger::setLevel(spdlog::level::info); - DOIP_LOG_INFO("Starting Simple DoIP Server Example"); + LOG_DOIP_INFO("Starting Simple DoIP Server Example"); DoIPServer server; configureServer(server); @@ -122,11 +122,11 @@ int main(int argc, char *argv[]) { // Start the server with automatic connection handling if (!server.start(onConnectionAccepted, true)) { - DOIP_LOG_ERROR("Failed to start server"); + LOG_DOIP_ERROR("Failed to start server"); return 1; } - DOIP_LOG_INFO("Server is running. Press Ctrl+C to stop."); + LOG_DOIP_INFO("Server is running. Press Ctrl+C to stop."); // Main thread just waits for shutdown signal while (!g_shutdownRequested.load()) { @@ -135,7 +135,7 @@ int main(int argc, char *argv[]) { // Graceful shutdown server.stop(); - DOIP_LOG_INFO("Server terminated cleanly"); + LOG_DOIP_INFO("Server terminated cleanly"); return 0; } diff --git a/inc/DoIPServer.h b/inc/DoIPServer.h index 6399cd6..890b93a 100644 --- a/inc/DoIPServer.h +++ b/inc/DoIPServer.h @@ -193,7 +193,7 @@ std::unique_ptr DoIPServer::waitForTcpConnection() { // model.onDiagnosticMessage = [](IConnectionContext& ctx, const DoIPMessage &msg) noexcept -> DoIPDiagnosticAck { // (void)ctx; // (void)msg; - // DOIP_LOG_CRITICAL("Diagnostic message received on default-constructed Model"); + // LOG_DOIP_CRITICAL("Diagnostic message received on default-constructed Model"); // // Default: always ACK // return std::nullopt; // }; @@ -207,14 +207,14 @@ std::unique_ptr DoIPServer::waitForTcpConnection() { */ template void DoIPServer::tcpListenerThread() { - DOIP_LOG_INFO("TCP listener thread started"); + LOG_DOIP_INFO("TCP listener thread started"); while (m_running.load()) { auto connection = waitForTcpConnection(); if (!connection) { if (m_running.load()) { - TCP_LOG_DEBUG("Failed to accept connection, retrying..."); + LOG_TCP_DEBUG("Failed to accept connection, retrying..."); std::this_thread::sleep_for(std::chrono::milliseconds(100)); } continue; @@ -225,7 +225,7 @@ void DoIPServer::tcpListenerThread() { std::thread(&DoIPServer::connectionHandlerThread, this, std::move(connection)).detach(); } - DOIP_LOG_INFO("TCP listener thread stopped"); + LOG_DOIP_INFO("TCP listener thread stopped"); } /* @@ -234,12 +234,12 @@ void DoIPServer::tcpListenerThread() { template bool DoIPServer::start(ConnectionAcceptedHandler onConnectionAccepted, bool sendAnnouncements) { if (m_running.load()) { - DOIP_LOG_WARN("Server is already running"); + LOG_DOIP_WARN("Server is already running"); return false; } if (!onConnectionAccepted) { - DOIP_LOG_ERROR("Connection handler callback is required"); + LOG_DOIP_ERROR("Connection handler callback is required"); return false; } @@ -247,13 +247,13 @@ bool DoIPServer::start(ConnectionAcceptedHandler onConnectionAccepted, bool send // Setup sockets if (!setupUdpSocket()) { - DOIP_LOG_ERROR("Failed to setup UDP socket"); + LOG_DOIP_ERROR("Failed to setup UDP socket"); return false; } if (setupTcpSocket()) { - DOIP_LOG_ERROR("Failed to setup TCP socket"); + LOG_DOIP_ERROR("Failed to setup TCP socket"); closeUdpSocket(); return false; } @@ -265,7 +265,7 @@ bool DoIPServer::start(ConnectionAcceptedHandler onConnectionAccepted, bool send m_workerThreads.emplace_back(&DoIPServer::udpListenerThread, this); m_workerThreads.emplace_back(&DoIPServer::tcpListenerThread, this); - DOIP_LOG_INFO("DoIP Server started successfully"); + LOG_DOIP_INFO("DoIP Server started successfully"); // Send vehicle announcements if requested if (sendAnnouncements) { @@ -274,7 +274,7 @@ bool DoIPServer::start(ConnectionAcceptedHandler onConnectionAccepted, bool send return true; } catch (const std::exception &e) { - DOIP_LOG_ERROR("Failed to start worker threads: {}", e.what()); + LOG_DOIP_ERROR("Failed to start worker threads: {}", e.what()); m_running.store(false); closeUdpSocket(); closeTcpSocket(); diff --git a/inc/DoIPServerModel.h b/inc/DoIPServerModel.h index f4d5899..0d0a72a 100644 --- a/inc/DoIPServerModel.h +++ b/inc/DoIPServerModel.h @@ -123,7 +123,7 @@ struct DefaultDoIPServerModel : public DoIPServerModel { onDiagnosticMessage = [](IConnectionContext &ctx, const DoIPMessage &msg) noexcept -> DoIPDiagnosticAck { (void)ctx; (void)msg; - DOIP_LOG_DEBUG("Diagnostic message received on DefaultDoIPServerModel"); + LOG_DOIP_DEBUG("Diagnostic message received on DefaultDoIPServerModel"); // Default: always ACK return std::nullopt; }; @@ -131,7 +131,7 @@ struct DefaultDoIPServerModel : public DoIPServerModel { onDiagnosticNotification = [](IConnectionContext &ctx, DoIPDiagnosticAck ack) noexcept { (void)ctx; (void)ack; - DOIP_LOG_DEBUG("Diagnostic notification on DefaultDoIPServerModel"); + LOG_DOIP_DEBUG("Diagnostic notification on DefaultDoIPServerModel"); // Default no-op }; diff --git a/inc/Logger.h b/inc/Logger.h index d34d037..3313223 100644 --- a/inc/Logger.h +++ b/inc/Logger.h @@ -121,58 +121,58 @@ class Logger { } // namespace doip // Logging macros -#define DOIP_LOG_TRACE(...) doip::Logger::get()->trace(__VA_ARGS__) -#define DOIP_LOG_DEBUG(...) doip::Logger::get()->debug(__VA_ARGS__) -#define DOIP_LOG_INFO(...) doip::Logger::get()->info(__VA_ARGS__) -#define DOIP_LOG_WARN(...) doip::Logger::get()->warn(__VA_ARGS__) -#define DOIP_LOG_ERROR(...) doip::Logger::get()->error(__VA_ARGS__) -#define DOIP_LOG_CRITICAL(...) doip::Logger::get()->critical(__VA_ARGS__) +#define LOG_DOIP_TRACE(...) doip::Logger::get()->trace(__VA_ARGS__) +#define LOG_DOIP_DEBUG(...) doip::Logger::get()->debug(__VA_ARGS__) +#define LOG_DOIP_INFO(...) doip::Logger::get()->info(__VA_ARGS__) +#define LOG_DOIP_WARN(...) doip::Logger::get()->warn(__VA_ARGS__) +#define LOG_DOIP_ERROR(...) doip::Logger::get()->error(__VA_ARGS__) +#define LOG_DOIP_CRITICAL(...) doip::Logger::get()->critical(__VA_ARGS__) // Logging macros for UDP socket -#define UDP_LOG_TRACE(...) doip::Logger::getUdp()->trace(__VA_ARGS__) -#define UDP_LOG_DEBUG(...) doip::Logger::getUdp()->debug(__VA_ARGS__) -#define UDP_LOG_INFO(...) doip::Logger::getUdp()->info(__VA_ARGS__) -#define UDP_LOG_WARN(...) doip::Logger::getUdp()->warn(__VA_ARGS__) -#define UDP_LOG_ERROR(...) doip::Logger::getUdp()->error(__VA_ARGS__) -#define UDP_LOG_CRITICAL(...) doip::Logger::getUdp()->critical(__VA_ARGS__) +#define LOG_UDP_TRACE(...) doip::Logger::getUdp()->trace(__VA_ARGS__) +#define LOG_UDP_DEBUG(...) doip::Logger::getUdp()->debug(__VA_ARGS__) +#define LOG_UDP_INFO(...) doip::Logger::getUdp()->info(__VA_ARGS__) +#define LOG_UDP_WARN(...) doip::Logger::getUdp()->warn(__VA_ARGS__) +#define LOG_UDP_ERROR(...) doip::Logger::getUdp()->error(__VA_ARGS__) +#define LOG_UDP_CRITICAL(...) doip::Logger::getUdp()->critical(__VA_ARGS__) // Logging macros for TCP socket -#define TCP_LOG_TRACE(...) doip::Logger::getTcp()->trace(__VA_ARGS__) -#define TCP_LOG_DEBUG(...) doip::Logger::getTcp()->debug(__VA_ARGS__) -#define TCP_LOG_INFO(...) doip::Logger::getTcp()->info(__VA_ARGS__) -#define TCP_LOG_WARN(...) doip::Logger::getTcp()->warn(__VA_ARGS__) -#define TCP_LOG_ERROR(...) doip::Logger::getTcp()->error(__VA_ARGS__) -#define TCP_LOG_CRITICAL(...) doip::Logger::getTcp()->critical(__VA_ARGS__) +#define LOG_TCP_TRACE(...) doip::Logger::getTcp()->trace(__VA_ARGS__) +#define LOG_TCP_DEBUG(...) doip::Logger::getTcp()->debug(__VA_ARGS__) +#define LOG_TCP_INFO(...) doip::Logger::getTcp()->info(__VA_ARGS__) +#define LOG_TCP_WARN(...) doip::Logger::getTcp()->warn(__VA_ARGS__) +#define LOG_TCP_ERROR(...) doip::Logger::getTcp()->error(__VA_ARGS__) +#define LOG_TCP_CRITICAL(...) doip::Logger::getTcp()->critical(__VA_ARGS__) // Colored logging macros -#define DOIP_LOG_SUCCESS(...) \ +#define LOG_DOIP_SUCCESS(...) \ doip::Logger::get()->info(std::string(doip::ansi::bold_green) + fmt::format(__VA_ARGS__) + doip::ansi::reset) -#define DOIP_LOG_ERROR_COLORED(...) \ +#define LOG_DOIP_ERROR_COLORED(...) \ doip::Logger::get()->error(std::string(doip::ansi::bold_red) + fmt::format(__VA_ARGS__) + doip::ansi::reset) -#define DOIP_LOG_PROTOCOL(...) \ +#define LOG_DOIP_PROTOCOL(...) \ doip::Logger::get()->info(std::string(doip::ansi::bold_blue) + fmt::format(__VA_ARGS__) + doip::ansi::reset) -#define DOIP_LOG_CONNECTION(...) \ +#define LOG_DOIP_CONNECTION(...) \ doip::Logger::get()->info(std::string(doip::ansi::bold_magenta) + fmt::format(__VA_ARGS__) + doip::ansi::reset) -#define DOIP_LOG_HIGHLIGHT(...) \ +#define LOG_DOIP_HIGHLIGHT(...) \ doip::Logger::get()->info(std::string(doip::ansi::bold_cyan) + fmt::format(__VA_ARGS__) + doip::ansi::reset) // Convenience macros for types with stream operators (using fmt::streamed) // These automatically wrap arguments with fmt::streamed() for seamless logging of DoIP types -#define DOIP_LOG_STREAM_INFO(obj, ...) DOIP_LOG_INFO(fmt::format("{} " __VA_ARGS__, fmt::streamed(obj))) -#define DOIP_LOG_STREAM_DEBUG(obj, ...) DOIP_LOG_DEBUG(fmt::format("{} " __VA_ARGS__, fmt::streamed(obj))) -#define DOIP_LOG_STREAM_WARN(obj, ...) DOIP_LOG_WARN(fmt::format("{} " __VA_ARGS__, fmt::streamed(obj))) -#define DOIP_LOG_STREAM_ERROR(obj, ...) DOIP_LOG_ERROR(fmt::format("{} " __VA_ARGS__, fmt::streamed(obj))) +#define LOG_DOIP_STREAM_INFO(obj, ...) LOG_DOIP_INFO(fmt::format("{} " __VA_ARGS__, fmt::streamed(obj))) +#define LOG_DOIP_STREAM_DEBUG(obj, ...) LOG_DOIP_DEBUG(fmt::format("{} " __VA_ARGS__, fmt::streamed(obj))) +#define LOG_DOIP_STREAM_WARN(obj, ...) LOG_DOIP_WARN(fmt::format("{} " __VA_ARGS__, fmt::streamed(obj))) +#define LOG_DOIP_STREAM_ERROR(obj, ...) LOG_DOIP_ERROR(fmt::format("{} " __VA_ARGS__, fmt::streamed(obj))) // Colored stream logging macros for DoIP types -#define DOIP_LOG_STREAM_SUCCESS(obj, ...) \ +#define LOG_DOIP_STREAM_SUCCESS(obj, ...) \ doip::Logger::get()->info(std::string(doip::ansi::bold_green) + fmt::format("{} " __VA_ARGS__, fmt::streamed(obj)) + doip::ansi::reset) -#define DOIP_LOG_STREAM_PROTOCOL(obj, ...) \ +#define LOG_DOIP_STREAM_PROTOCOL(obj, ...) \ doip::Logger::get()->info(std::string(doip::ansi::bold_blue) + fmt::format("{} " __VA_ARGS__, fmt::streamed(obj)) + doip::ansi::reset) -#define DOIP_LOG_STREAM_CONNECTION(obj, ...) \ +#define LOG_DOIP_STREAM_CONNECTION(obj, ...) \ doip::Logger::get()->info(std::string(doip::ansi::bold_magenta) + fmt::format("{} " __VA_ARGS__, fmt::streamed(obj)) + doip::ansi::reset) diff --git a/src/DoIPClient.cpp b/src/DoIPClient.cpp index 1fb5ada..bd7c19a 100644 --- a/src/DoIPClient.cpp +++ b/src/DoIPClient.cpp @@ -15,7 +15,7 @@ void DoIPClient::startTcpConnection() { _sockFd = socket(AF_INET, SOCK_STREAM, 0); if (_sockFd >= 0) { - TCP_LOG_INFO("Client TCP-Socket created successfully"); + LOG_TCP_INFO("Client TCP-Socket created successfully"); bool connectedFlag = false; const char *ipAddr = "127.0.0.1"; @@ -27,7 +27,7 @@ void DoIPClient::startTcpConnection() { _connected = connect(_sockFd, reinterpret_cast(&_serverAddr), sizeof(_serverAddr)); if (_connected != -1) { connectedFlag = true; - TCP_LOG_INFO("Connection to server established"); + LOG_TCP_INFO("Connection to server established"); } } } @@ -38,7 +38,7 @@ void DoIPClient::startUdpConnection() { _sockFd_udp = socket(AF_INET, SOCK_DGRAM, 0); if (_sockFd_udp >= 0) { - UDP_LOG_INFO("Client-UDP-Socket created successfully"); + LOG_UDP_INFO("Client-UDP-Socket created successfully"); _serverAddr.sin_family = AF_INET; _serverAddr.sin_port = htons(DOIP_UDP_DISCOVERY_PORT); @@ -57,7 +57,7 @@ void DoIPClient::startAnnouncementListener() { _sockFd_announcement = socket(AF_INET, SOCK_DGRAM, 0); if (_sockFd_announcement >= 0) { - UDP_LOG_INFO("Client-Announcement-Socket created successfully"); + LOG_UDP_INFO("Client-Announcement-Socket created successfully"); // Allow socket reuse for broadcast int reuse = 1; @@ -66,9 +66,9 @@ void DoIPClient::startAnnouncementListener() { // Enable broadcast reception int broadcast = 1; if (setsockopt(_sockFd_announcement, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast)) < 0) { - UDP_LOG_ERROR("Failed to enable broadcast reception: {}", strerror(errno)); + LOG_UDP_ERROR("Failed to enable broadcast reception: {}", strerror(errno)); } else { - UDP_LOG_INFO("Broadcast reception enabled for announcements"); + LOG_UDP_INFO("Broadcast reception enabled for announcements"); } _announcementAddr.sin_family = AF_INET; @@ -77,12 +77,12 @@ void DoIPClient::startAnnouncementListener() { // Bind to port 13401 for Vehicle Announcements if (bind(_sockFd_announcement, reinterpret_cast(&_announcementAddr), sizeof(_announcementAddr)) < 0) { - UDP_LOG_ERROR("Failed to bind announcement socket to port {}: {}", DOIP_UDP_TEST_EQUIPMENT_REQUEST_PORT, strerror(errno)); + LOG_UDP_ERROR("Failed to bind announcement socket to port {}: {}", DOIP_UDP_TEST_EQUIPMENT_REQUEST_PORT, strerror(errno)); } else { - UDP_LOG_INFO("Announcement socket bound to port {} successfully", DOIP_UDP_TEST_EQUIPMENT_REQUEST_PORT); + LOG_UDP_INFO("Announcement socket bound to port {} successfully", DOIP_UDP_TEST_EQUIPMENT_REQUEST_PORT); } } else { - UDP_LOG_ERROR("Failed to create announcement socket: {}", strerror(errno)); + LOG_UDP_ERROR("Failed to create announcement socket: {}", strerror(errno)); } } @@ -107,20 +107,20 @@ void DoIPClient::reconnectServer() { ssize_t DoIPClient::sendRoutingActivationRequest() { DoIPMessage routingActReq = message::makeRoutingActivationRequest(m_sourceAddress); - DOIP_LOG_INFO("TX: {}", fmt::streamed(routingActReq)); + LOG_DOIP_INFO("TX: {}", fmt::streamed(routingActReq)); return write(_sockFd, routingActReq.data(), routingActReq.size()); } ssize_t DoIPClient::sendDiagnosticMessage(const ByteArray &payload) { DoIPMessage msg = message::makeDiagnosticMessage(m_sourceAddress, m_logicalAddress, payload); - DOIP_LOG_INFO("TX: {}", fmt::streamed(msg)); + LOG_DOIP_INFO("TX: {}", fmt::streamed(msg)); return write(_sockFd, msg.data(), msg.size()); } ssize_t DoIPClient::sendAliveCheckResponse() { DoIPMessage msg = message::makeAliveCheckResponse(m_sourceAddress); - DOIP_LOG_INFO("TX: {}", fmt::streamed(msg)); + LOG_DOIP_INFO("TX: {}", fmt::streamed(msg)); return write(_sockFd, msg.data(), msg.size()); } @@ -132,7 +132,7 @@ void DoIPClient::receiveMessage() { ssize_t bytesRead = recv(_sockFd, _receivedData, _maxDataSize, 0); if (bytesRead < 0) { - DOIP_LOG_ERROR("Error receiving data from server"); + LOG_DOIP_ERROR("Error receiving data from server"); return; } @@ -141,7 +141,7 @@ void DoIPClient::receiveMessage() { emptyMessageCounter++; if (emptyMessageCounter == 5) { - DOIP_LOG_WARN("Received too many empty messages. Reconnect TCP connection"); + LOG_DOIP_WARN("Received too many empty messages. Reconnect TCP connection"); emptyMessageCounter = 0; reconnectServer(); } @@ -150,11 +150,11 @@ void DoIPClient::receiveMessage() { auto optMmsg = DoIPMessage::tryParse(_receivedData, static_cast(bytesRead)); if (!optMmsg.has_value()) { - DOIP_LOG_ERROR("Failed to parse DoIP message from received data"); + LOG_DOIP_ERROR("Failed to parse DoIP message from received data"); return; } DoIPMessage msg = optMmsg.value(); - TCP_LOG_INFO("RX: {}", fmt::streamed(msg)); + LOG_TCP_INFO("RX: {}", fmt::streamed(msg)); } void DoIPClient::receiveUdpMessage() { @@ -172,31 +172,31 @@ void DoIPClient::receiveUdpMessage() { if (bytesRead < 0) { if (errno == EAGAIN) { - UDP_LOG_WARN("Timeout waiting for UDP response"); + LOG_UDP_WARN("Timeout waiting for UDP response"); } else { - UDP_LOG_ERROR("Error receiving UDP message: {}", strerror(errno)); + LOG_UDP_ERROR("Error receiving UDP message: {}", strerror(errno)); } return; } - UDP_LOG_INFO("Received {} bytes from UDP", bytesRead); + LOG_UDP_INFO("Received {} bytes from UDP", bytesRead); auto optMmsg = DoIPMessage::tryParse(_receivedData, static_cast(bytesRead)); if (!optMmsg.has_value()) { - UDP_LOG_ERROR("Failed to parse DoIP message from UDP data"); + LOG_UDP_ERROR("Failed to parse DoIP message from UDP data"); return; } DoIPMessage msg = optMmsg.value(); - UDP_LOG_INFO("RX: {}", fmt::streamed(msg)); + LOG_UDP_INFO("RX: {}", fmt::streamed(msg)); } void DoIPClient::receiveVehicleAnnouncement() { unsigned int length = sizeof(_announcementAddr); int bytesRead; - UDP_LOG_DEBUG("Listening for Vehicle Announcements on port {}", DOIP_UDP_TEST_EQUIPMENT_REQUEST_PORT); + LOG_UDP_DEBUG("Listening for Vehicle Announcements on port {}", DOIP_UDP_TEST_EQUIPMENT_REQUEST_PORT); // Set socket to non-blocking mode for timeout struct timeval timeout; @@ -208,21 +208,21 @@ void DoIPClient::receiveVehicleAnnouncement() { reinterpret_cast(&_announcementAddr), &length); if (bytesRead < 0) { if (errno == EAGAIN) { - UDP_LOG_WARN("Timeout waiting for Vehicle Announcement"); + LOG_UDP_WARN("Timeout waiting for Vehicle Announcement"); } else { - UDP_LOG_ERROR("Error receiving Vehicle Announcement: {}", strerror(errno)); + LOG_UDP_ERROR("Error receiving Vehicle Announcement: {}", strerror(errno)); } return; } auto optMsg = DoIPMessage::tryParse(_receivedData, static_cast(bytesRead)); if (!optMsg.has_value()) { - UDP_LOG_ERROR("Failed to parse Vehicle Announcement message"); + LOG_UDP_ERROR("Failed to parse Vehicle Announcement message"); return; } DoIPMessage msg = optMsg.value(); - UDP_LOG_INFO("Vehicle Announcement received: {}", fmt::streamed(msg)); + LOG_UDP_INFO("Vehicle Announcement received: {}", fmt::streamed(msg)); // Parse and display the announcement information if (msg.getPayloadType() == DoIPPayloadType::VehicleIdentificationResponse) { @@ -236,15 +236,15 @@ ssize_t DoIPClient::sendVehicleIdentificationRequest(const char *inet_address) { int setAddressError = inet_aton(inet_address, &(_serverAddr.sin_addr)); if (setAddressError != 0) { - UDP_LOG_INFO("Address set successfully"); + LOG_UDP_INFO("Address set successfully"); } else { - UDP_LOG_ERROR("Could not set address. Try again"); + LOG_UDP_ERROR("Could not set address. Try again"); } int socketError = setsockopt(_sockFd_udp, SOL_SOCKET, SO_BROADCAST, &m_broadcast, sizeof(m_broadcast)); if (socketError == 0) { - UDP_LOG_INFO("Broadcast Option set successfully"); + LOG_UDP_INFO("Broadcast Option set successfully"); } DoIPMessage vehicleIdReq = message::makeVehicleIdentificationRequest(); @@ -252,7 +252,7 @@ ssize_t DoIPClient::sendVehicleIdentificationRequest(const char *inet_address) { ssize_t bytesSent = sendto(_sockFd_udp, vehicleIdReq.data(), vehicleIdReq.size(), 0, reinterpret_cast(&_serverAddr), sizeof(_serverAddr)); if (bytesSent > 0) { - DOIP_LOG_INFO("Sending Vehicle Identification Request"); + LOG_DOIP_INFO("Sending Vehicle Identification Request"); } return bytesSent; @@ -323,13 +323,13 @@ void DoIPClient::displayVIResponseInformation() { if (Logger::colorsSupported()) { ss << ansi::reset; } - DOIP_LOG_INFO(ss.str()); + LOG_DOIP_INFO(ss.str()); // output LogicalAddress ss = std::ostringstream{}; ss << "LogicalAddress: "; ss << m_logicalAddress; - DOIP_LOG_INFO(ss.str()); + LOG_DOIP_INFO(ss.str()); // output EID ss = std::ostringstream{}; @@ -343,7 +343,7 @@ void DoIPClient::displayVIResponseInformation() { if (Logger::colorsSupported()) { ss << ansi::reset; } - DOIP_LOG_INFO(ss.str()); + LOG_DOIP_INFO(ss.str()); // output GID ss = std::ostringstream{}; @@ -351,11 +351,11 @@ void DoIPClient::displayVIResponseInformation() { for (int i = 0; i < 6; i++) { ss << std::hex << std::setfill('0') << std::setw(2) << +GIDResult[i] << std::dec; } - DOIP_LOG_INFO(ss.str()); + LOG_DOIP_INFO(ss.str()); // output FurtherActionRequest ss = std::ostringstream{}; ss << "FurtherActionRequest: "; ss << std::hex << std::setfill('0') << std::setw(2) << FurtherActionReqResult << std::dec; - DOIP_LOG_INFO(ss.str()); + LOG_DOIP_INFO(ss.str()); } diff --git a/src/DoIPConnection.cpp b/src/DoIPConnection.cpp index 89c3cc3..f454ff2 100644 --- a/src/DoIPConnection.cpp +++ b/src/DoIPConnection.cpp @@ -26,17 +26,17 @@ void DoIPConnection::closeSocket() { * or -1 if error occurred */ int DoIPConnection::receiveTcpMessage() { - DOIP_LOG_INFO("Waiting for DoIP Header..."); + LOG_DOIP_INFO("Waiting for DoIP Header..."); uint8_t genericHeader[DOIP_HEADER_SIZE]; unsigned int readBytes = receiveFixedNumberOfBytesFromTCP(genericHeader, DOIP_HEADER_SIZE); if (readBytes == DOIP_HEADER_SIZE /*&& !m_aliveCheckTimer.hasTimeout()*/) { - DOIP_LOG_INFO("Received DoIP Header."); + LOG_DOIP_INFO("Received DoIP Header."); auto optHeader = DoIPMessage::tryParseHeader(genericHeader, DOIP_HEADER_SIZE); if (!optHeader.has_value()) { // m_stateMachine.processEvent(DoIPServerEvent::InvalidMessage); // TODO: Notify application of invalid message? - DOIP_LOG_ERROR("DoIP message header parsing failed"); + LOG_DOIP_ERROR("DoIP message header parsing failed"); closeSocket(); return -2; } @@ -44,13 +44,13 @@ int DoIPConnection::receiveTcpMessage() { auto plType = optHeader->first; auto payloadLength = optHeader->second; - DOIP_LOG_INFO("Payload Type: {}, length: {} ", fmt::streamed(plType), payloadLength); + LOG_DOIP_INFO("Payload Type: {}, length: {} ", fmt::streamed(plType), payloadLength); if (payloadLength > 0) { - DOIP_LOG_DEBUG("Waiting for {} bytes of payload...", payloadLength); + LOG_DOIP_DEBUG("Waiting for {} bytes of payload...", payloadLength); unsigned int receivedPayloadBytes = receiveFixedNumberOfBytesFromTCP(m_receiveBuf.data(), payloadLength); if (receivedPayloadBytes < payloadLength) { - DOIP_LOG_ERROR("DoIP message incomplete"); + LOG_DOIP_ERROR("DoIP message incomplete"); // m_stateMachine.processEvent(DoIPServerEvent::InvalidMessage); // todo: Notify application of invalid message? closeSocket(); @@ -58,7 +58,7 @@ int DoIPConnection::receiveTcpMessage() { } DoIPMessage msg = DoIPMessage(plType, m_receiveBuf.data(), receivedPayloadBytes); - DOIP_LOG_INFO("RX: {}", fmt::streamed(msg)); + LOG_DOIP_INFO("RX: {}", fmt::streamed(msg)); } DoIPMessage message(plType, m_receiveBuf.data(), payloadLength); @@ -99,7 +99,7 @@ size_t DoIPConnection::receiveFixedNumberOfBytesFromTCP(uint8_t *receivedData, s } void DoIPConnection::triggerDisconnection() { - DOIP_LOG_INFO("Application requested to disconnect Client from Server"); + LOG_DOIP_INFO("Application requested to disconnect Client from Server"); closeSocket(); } @@ -119,9 +119,9 @@ ssize_t DoIPConnection::sendMessage(const uint8_t *message, size_t messageLength ssize_t DoIPConnection::sendProtocolMessage(const DoIPMessage &msg) { ssize_t sentBytes = sendMessage(msg.data(), msg.size()); if (sentBytes < 0) { - DOIP_LOG_ERROR("Error sending message to client: {}", fmt::streamed(msg)); + LOG_DOIP_ERROR("Error sending message to client: {}", fmt::streamed(msg)); } else { - DOIP_LOG_INFO("Sent {} bytes to client: {}", sentBytes, fmt::streamed(msg)); + LOG_DOIP_INFO("Sent {} bytes to client: {}", sentBytes, fmt::streamed(msg)); } return sentBytes; } @@ -129,12 +129,12 @@ ssize_t DoIPConnection::sendProtocolMessage(const DoIPMessage &msg) { void DoIPConnection::closeConnection(DoIPCloseReason reason) { // Guard against recursive calls if (m_isClosing) { - DOIP_LOG_DEBUG("Connection already closing - ignoring recursive call"); + LOG_DOIP_DEBUG("Connection already closing - ignoring recursive call"); return; } m_isClosing = true; - DOIP_LOG_INFO("Closing connection, reason: {}", fmt::streamed(reason)); + LOG_DOIP_INFO("Closing connection, reason: {}", fmt::streamed(reason)); // Call base class to handle state machine and notification DoIPDefaultConnection::closeConnection(reason); diff --git a/src/DoIPDefaultConnection.cpp b/src/DoIPDefaultConnection.cpp index be90ad6..8378d25 100644 --- a/src/DoIPDefaultConnection.cpp +++ b/src/DoIPDefaultConnection.cpp @@ -29,7 +29,7 @@ DoIPDefaultConnection::DoIPDefaultConnection(UniqueServerModelPtr model) DoIPServerState::Finalize, [this](std::optional msg) { this->handleWaitAliveCheckResponse(DoIPServerEvent{}, msg); }, ConnectionTimers::AliveCheck, - [this]() { ++m_aliveCheckRetry; DOIP_LOG_WARN("Alive check #{}/{}", m_aliveCheckRetry, m_aliveCheckRetryCount); }), + [this]() { ++m_aliveCheckRetry; LOG_DOIP_WARN("Alive check #{}/{}", m_aliveCheckRetry, m_aliveCheckRetryCount); }), StateDescriptor( DoIPServerState::WaitDownstreamResponse, DoIPServerState::Finalize, @@ -50,33 +50,33 @@ DoIPDefaultConnection::DoIPDefaultConnection(UniqueServerModelPtr model) m_serverModel->onOpenConnection(*this); m_state = &STATE_DESCRIPTORS[0]; - DOIP_LOG_INFO("Default connection created, transitioning to SocketInitialized state..."); + LOG_DOIP_INFO("Default connection created, transitioning to SocketInitialized state..."); transitionTo(DoIPServerState::WaitRoutingActivation); } ssize_t DoIPDefaultConnection::sendProtocolMessage(const DoIPMessage &msg) { - DOIP_LOG_INFO("Default connection: Sending protocol message: {}", fmt::streamed(msg)); + LOG_DOIP_INFO("Default connection: Sending protocol message: {}", fmt::streamed(msg)); return static_cast(msg.size()); // Simulate sending by returning the message size } void DoIPDefaultConnection::closeConnection(DoIPCloseReason reason) { try { - DOIP_LOG_INFO("Default connection: Closing connection, reason: {}", fmt::streamed(reason)); + LOG_DOIP_INFO("Default connection: Closing connection, reason: {}", fmt::streamed(reason)); transitionTo(DoIPServerState::Closed); m_closeReason = reason; m_timerManager.stopAll(); notifyConnectionClosed(reason); } catch (const std::exception &e) { - DOIP_LOG_ERROR("Error notifying connection closed: {}", e.what()); + LOG_DOIP_ERROR("Error notifying connection closed: {}", e.what()); void *callstack[128]; int frames = backtrace(callstack, 128); char **strs = backtrace_symbols(callstack, frames); - DOIP_LOG_ERROR("Exception during closeConnection: {}", e.what()); - DOIP_LOG_ERROR("Stack trace:"); + LOG_DOIP_ERROR("Exception during closeConnection: {}", e.what()); + LOG_DOIP_ERROR("Stack trace:"); for (int i = 0; i < frames; ++i) { - DOIP_LOG_ERROR("{}", strs[i]); + LOG_DOIP_ERROR("{}", strs[i]); } free(strs); } @@ -111,15 +111,15 @@ void DoIPDefaultConnection::transitionTo(DoIPServerState newState) { return desc.state == newState; }); if (it != STATE_DESCRIPTORS.end()) { - DOIP_LOG_INFO("-> Transitioning from state {} to state {}", fmt::streamed(m_state->state), fmt::streamed(newState)); + LOG_DOIP_INFO("-> Transitioning from state {} to state {}", fmt::streamed(m_state->state), fmt::streamed(newState)); m_state = &(*it); startStateTimer(m_state); if (m_state->enterStateHandler) { - DOIP_LOG_INFO("Calling enterState handler"); + LOG_DOIP_INFO("Calling enterState handler"); m_state->enterStateHandler(); } } else { - DOIP_LOG_ERROR("Invalid state transition to {}", fmt::streamed(newState)); + LOG_DOIP_ERROR("Invalid state transition to {}", fmt::streamed(newState)); } } @@ -152,12 +152,12 @@ void DoIPDefaultConnection::startStateTimer(StateDescriptor const *stateDesc) { std::chrono::milliseconds duration = getTimerDuration(m_state); if (duration.count() == 0) { - DOIP_LOG_DEBUG("User-defined timer duration is zero, transitioning immediately to state {}", fmt::streamed(stateDesc->stateAfterTimeout)); + LOG_DOIP_DEBUG("User-defined timer duration is zero, transitioning immediately to state {}", fmt::streamed(stateDesc->stateAfterTimeout)); transitionTo(stateDesc->stateAfterTimeout); return; } - DOIP_LOG_DEBUG("Starting timer for state {}: Timer ID {}, duration {}ms", fmt::streamed(stateDesc->state), fmt::streamed(stateDesc->timer), duration.count()); + LOG_DOIP_DEBUG("Starting timer for state {}: Timer ID {}, duration {}ms", fmt::streamed(stateDesc->state), fmt::streamed(stateDesc->timer), duration.count()); std::function timeoutHandler = [this](ConnectionTimers timerId) { handleTimeout(timerId); }; if (stateDesc->timeoutHandler != nullptr) { @@ -168,16 +168,16 @@ void DoIPDefaultConnection::startStateTimer(StateDescriptor const *stateDesc) { m_state->timer, duration, timeoutHandler, false); if (id.has_value()) { - DOIP_LOG_DEBUG("Started timer {} for {}ms", fmt::streamed(m_state->timer), duration.count()); + LOG_DOIP_DEBUG("Started timer {} for {}ms", fmt::streamed(m_state->timer), duration.count()); } else { - DOIP_LOG_ERROR("Failed to start timer {}", fmt::streamed(m_state->timer)); + LOG_DOIP_ERROR("Failed to start timer {}", fmt::streamed(m_state->timer)); } } void DoIPDefaultConnection::restartStateTimer() { assert(m_state != nullptr); if (!m_timerManager.restartTimer(m_state->timer)) { - DOIP_LOG_ERROR("Failed to restart timer {}", fmt::streamed(m_state->timer)); + LOG_DOIP_ERROR("Failed to restart timer {}", fmt::streamed(m_state->timer)); } } @@ -203,7 +203,7 @@ void DoIPDefaultConnection::handleWaitRoutingActivation(DoIPServerEvent event, O bool rightPayloadType = (msg->getPayloadType() == DoIPPayloadType::RoutingActivationRequest); if (!hasAddress || !rightPayloadType) { - DOIP_LOG_WARN("Invalid Routing Activation Request message"); + LOG_DOIP_WARN("Invalid Routing Activation Request message"); closeConnection(DoIPCloseReason::InvalidMessage); return; } @@ -233,7 +233,7 @@ void DoIPDefaultConnection::handleRoutingActivated(DoIPServerEvent event, OptDoI restartStateTimer(); return; default: - DOIP_LOG_WARN("Received unsupported message type {} in Routing Activated state", fmt::streamed(message.getPayloadType())); + LOG_DOIP_WARN("Received unsupported message type {} in Routing Activated state", fmt::streamed(message.getPayloadType())); sendDiagnosticMessageResponse(DoIPAddress::ZeroAddress, DoIPNegativeDiagnosticAck::TransportProtocolError); // closeConnection(DoIPCloseReason::InvalidMessage); return; @@ -244,7 +244,7 @@ void DoIPDefaultConnection::handleRoutingActivated(DoIPServerEvent event, OptDoI return; } if (sourceAddress.value() != getClientAddress()) { - DOIP_LOG_WARN("Received diagnostic message from unexpected source address {}", fmt::streamed(sourceAddress.value())); + LOG_DOIP_WARN("Received diagnostic message from unexpected source address {}", fmt::streamed(sourceAddress.value())); sendDiagnosticMessageResponse(sourceAddress.value(), DoIPNegativeDiagnosticAck::InvalidSourceAddress); // closeConnection(DoIPCloseReason::InvalidMessage); return; @@ -264,7 +264,7 @@ void DoIPDefaultConnection::handleRoutingActivated(DoIPServerEvent event, OptDoI if (hasDownstreamHandler()) { auto result = notifyDownstreamRequest(message); - DOIP_LOG_DEBUG("Downstream req -> {}", fmt::streamed(result)); + LOG_DOIP_DEBUG("Downstream req -> {}", fmt::streamed(result)); if (result == DoIPDownstreamResult::Pending) { // wait for downstream response transitionTo(DoIPServerState::WaitDownstreamResponse); @@ -299,7 +299,7 @@ void DoIPDefaultConnection::handleWaitAliveCheckResponse(DoIPServerEvent event, transitionTo(DoIPServerState::RoutingActivated); return; default: - DOIP_LOG_WARN("Received unsupported message type {} in Wait Alive Check Response state", fmt::streamed(message.getPayloadType())); + LOG_DOIP_WARN("Received unsupported message type {} in Wait Alive Check Response state", fmt::streamed(message.getPayloadType())); sendDiagnosticMessageResponse(DoIPAddress::ZeroAddress, DoIPNegativeDiagnosticAck::TransportProtocolError); return; } @@ -310,7 +310,7 @@ void DoIPDefaultConnection::handleWaitDownstreamResponse(DoIPServerEvent event, (void)msg; // Unused parameter // Implementation of handling wait downstream response would go here - DOIP_LOG_CRITICAL("handleWaitDownstreamResponse NOT IMPL"); + LOG_DOIP_CRITICAL("handleWaitDownstreamResponse NOT IMPL"); } @@ -323,7 +323,7 @@ void DoIPDefaultConnection::handleFinalize(DoIPServerEvent event, OptDoIPMessage } void DoIPDefaultConnection::handleTimeout(ConnectionTimers timer_id) { - DOIP_LOG_WARN("Timeout '{}'", fmt::streamed(timer_id)); + LOG_DOIP_WARN("Timeout '{}'", fmt::streamed(timer_id)); switch (timer_id) { case ConnectionTimers::InitialInactivity: @@ -341,14 +341,14 @@ void DoIPDefaultConnection::handleTimeout(ConnectionTimers timer_id) { } break; case ConnectionTimers::DownstreamResponse: - DOIP_LOG_WARN("Downstream response timeout occurred"); + LOG_DOIP_WARN("Downstream response timeout occurred"); transitionTo(DoIPServerState::RoutingActivated); break; case ConnectionTimers::UserDefined: - DOIP_LOG_WARN("User-defined timer -> must be handled separately"); + LOG_DOIP_WARN("User-defined timer -> must be handled separately"); break; default: - DOIP_LOG_ERROR("Unhandled timeout for timer id {}", fmt::streamed(timer_id)); + LOG_DOIP_ERROR("Unhandled timeout for timer id {}", fmt::streamed(timer_id)); break; } } @@ -443,7 +443,7 @@ DoIPDownstreamResult DoIPDefaultConnection::notifyDownstreamRequest(const DoIPMe void DoIPDefaultConnection::receiveDownstreamResponse(const ByteArray &response, DoIPDownstreamResult result) { DoIPAddress sa = getServerAddress(); DoIPAddress ta = getClientAddress(); - DOIP_LOG_INFO("Downstream rsp: {} ({})", fmt::streamed(response), fmt::streamed(result)); + LOG_DOIP_INFO("Downstream rsp: {} ({})", fmt::streamed(response), fmt::streamed(result)); if (result == DoIPDownstreamResult::Handled) { sendProtocolMessage(message::makeDiagnosticMessage(sa, ta, response)); } else { diff --git a/src/DoIPServer.cpp b/src/DoIPServer.cpp index e7c01ec..bb74162 100644 --- a/src/DoIPServer.cpp +++ b/src/DoIPServer.cpp @@ -29,7 +29,7 @@ DoIPServer::~DoIPServer() { * High-level API: Stop the server and cleanup */ void DoIPServer::stop() { - DOIP_LOG_INFO("Stopping DoIP Server..."); + LOG_DOIP_INFO("Stopping DoIP Server..."); m_running.store(false); // Close sockets to unblock any pending accept/recv calls @@ -44,14 +44,14 @@ void DoIPServer::stop() { } m_workerThreads.clear(); - DOIP_LOG_INFO("DoIP Server stopped"); + LOG_DOIP_INFO("DoIP Server stopped"); } /* * Background thread: UDP message listener */ void DoIPServer::udpListenerThread() { - DOIP_LOG_INFO("UDP listener thread started"); + LOG_DOIP_INFO("UDP listener thread started"); while (m_running.load()) { ssize_t result = receiveUdpMessage(); @@ -60,11 +60,11 @@ void DoIPServer::udpListenerThread() { // The socket already has a timeout configured if (result < 0 && m_running.load()) { // Only log errors if we're still supposed to be running - UDP_LOG_DEBUG("UDP receive error, continuing..."); + LOG_UDP_DEBUG("UDP receive error, continuing..."); } } - DOIP_LOG_INFO("UDP listener thread stopped"); + LOG_DOIP_INFO("UDP listener thread stopped"); } @@ -73,37 +73,37 @@ void DoIPServer::udpListenerThread() { * Background thread: Handle individual TCP connection */ void DoIPServer::connectionHandlerThread(std::unique_ptr connection) { - TCP_LOG_INFO("Connection handler thread started"); + LOG_TCP_INFO("Connection handler thread started"); while (m_running.load() && connection->isSocketActive()) { int result = connection->receiveTcpMessage(); if (result < 0) { - TCP_LOG_INFO("Connection closed or error occurred"); + LOG_TCP_INFO("Connection closed or error occurred"); break; } } // Connection is automatically closed when unique_ptr goes out of scope - TCP_LOG_INFO("Connection handler thread stopped"); + LOG_TCP_INFO("Connection handler thread stopped"); } /* * Set up a tcp socket, so the socket is ready to accept a connection */ bool DoIPServer::setupTcpSocket() { - DOIP_LOG_DEBUG("Setting up TCP socket on port {}", DOIP_SERVER_PORT); + LOG_DOIP_DEBUG("Setting up TCP socket on port {}", DOIP_SERVER_PORT); m_tcp_sock = socket(AF_INET, SOCK_STREAM, 0); if (m_tcp_sock < 0) { - TCP_LOG_ERROR("Failed to create TCP socket: {}", strerror(errno)); + LOG_TCP_ERROR("Failed to create TCP socket: {}", strerror(errno)); return false; } // Allow socket reuse int reuse = 1; if (setsockopt(m_tcp_sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) { - TCP_LOG_WARN("Failed to set SO_REUSEADDR: {}", strerror(errno)); + LOG_TCP_WARN("Failed to set SO_REUSEADDR: {}", strerror(errno)); } m_serverAddress.sin_family = AF_INET; @@ -112,16 +112,16 @@ bool DoIPServer::setupTcpSocket() { // binds the socket to the address and port number if (bind(m_tcp_sock, reinterpret_cast(&m_serverAddress), sizeof(m_serverAddress)) < 0) { - TCP_LOG_ERROR("Failed to bind TCP socket: {}", strerror(errno)); + LOG_TCP_ERROR("Failed to bind TCP socket: {}", strerror(errno)); return false; } - TCP_LOG_INFO("TCP socket successfully bound to port {}", DOIP_SERVER_PORT); + LOG_TCP_INFO("TCP socket successfully bound to port {}", DOIP_SERVER_PORT); return true; } bool DoIPServer::setupUdpSocket() { - UDP_LOG_DEBUG("Setting up UDP socket on port {}", DOIP_UDP_DISCOVERY_PORT); + LOG_UDP_DEBUG("Setting up UDP socket on port {}", DOIP_UDP_DISCOVERY_PORT); m_udp_sock = socket(AF_INET, SOCK_DGRAM, 0); @@ -130,19 +130,19 @@ bool DoIPServer::setupUdpSocket() { m_serverAddress.sin_port = htons(DOIP_UDP_DISCOVERY_PORT); if (m_udp_sock < 0) { - UDP_LOG_ERROR("Failed to create UDP socket: {}", strerror(errno)); + LOG_UDP_ERROR("Failed to create UDP socket: {}", strerror(errno)); return false; } // binds the socket to any IP DoIPAddress and the Port Number 13400 if (bind(m_udp_sock, reinterpret_cast(&m_serverAddress), sizeof(m_serverAddress)) < 0) { - UDP_LOG_ERROR("Failed to bind UDP socket: {}", strerror(errno)); + LOG_UDP_ERROR("Failed to bind UDP socket: {}", strerror(errno)); return false; } // setting the IP DoIPAddress for Multicast setMulticastGroup("224.0.0.2"); - UDP_LOG_INFO("UDP socket successfully bound to port {} with multicast group", DOIP_UDP_DISCOVERY_PORT); + LOG_UDP_INFO("UDP socket successfully bound to port {} with multicast group", DOIP_UDP_DISCOVERY_PORT); return true; } @@ -178,18 +178,18 @@ ssize_t DoIPServer::receiveUdpMessage() { // Timeout - this is normal, just continue return 0; } else { - UDP_LOG_ERROR("Error receiving UDP message: {}", strerror(errno)); + LOG_UDP_ERROR("Error receiving UDP message: {}", strerror(errno)); return -1; } } // Don't log if no data received (can happen with some socket configurations) if (readBytes > 0) { - UDP_LOG_INFO("RX {} bytes from {}:{}", readBytes, + LOG_UDP_INFO("RX {} bytes from {}:{}", readBytes, inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port)); } else { // For debugging: log zero-byte messages at debug level - UDP_LOG_DEBUG("RX 0 bytes from {}:{}", + LOG_UDP_DEBUG("RX 0 bytes from {}:{}", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port)); return 0; // Return early for zero-byte messages } @@ -219,18 +219,18 @@ ssize_t DoIPServer::reactToReceivedUdpMessage(size_t bytesRead) { auto plType = optHeader->first; // auto payloadLength = optHeader->second; - UDP_LOG_INFO("RX: {}", fmt::streamed(plType)); + LOG_UDP_INFO("RX: {}", fmt::streamed(plType)); switch (plType) { case DoIPPayloadType::VehicleIdentificationRequest: { DoIPMessage msg = message::makeVehicleIdentificationResponse(m_VIN, m_gatewayAddress, m_EID, m_GID); - DOIP_LOG_PROTOCOL("TX {}", fmt::streamed(msg)); + LOG_DOIP_PROTOCOL("TX {}", fmt::streamed(msg)); ssize_t sendedBytes = sendUdpMessage(msg.data(), DOIP_HEADER_SIZE + msg.size()); return static_cast(sendedBytes); } default: { - DOIP_LOG_ERROR("Invalid payload type 0x{:04X} received (receiveUdpMessage())", static_cast(plType)); + LOG_DOIP_ERROR("Invalid payload type 0x{:04X} received (receiveUdpMessage())", static_cast(plType)); return sendNegativeUdpAck(DoIPNegativeAck::UnknownPayloadType); } } @@ -249,7 +249,7 @@ ssize_t DoIPServer::sendUdpMessage(const uint8_t *message, size_t messageLength) bool DoIPServer::setEIDdefault() { MacAddress mac = {0}; if (!getFirstMacAddress(mac)) { - DOIP_LOG_ERROR("Failed to get MAC address, using default EID"); + LOG_DOIP_ERROR("Failed to get MAC address, using default EID"); m_EID = DoIPEID::Zero; return false; } @@ -290,9 +290,9 @@ void DoIPServer::setAnnounceInterval(unsigned int Interval) { void DoIPServer::setAnnouncementMode(bool useLoopback) { m_useLoopbackAnnouncements = useLoopback; if (useLoopback) { - DOIP_LOG_INFO("Vehicle announcements will use loopback (127.0.0.1)"); + LOG_DOIP_INFO("Vehicle announcements will use loopback (127.0.0.1)"); } else { - DOIP_LOG_INFO("Vehicle announcements will use broadcast (255.255.255.255)"); + LOG_DOIP_INFO("Vehicle announcements will use broadcast (255.255.255.255)"); } } @@ -303,7 +303,7 @@ void DoIPServer::setMulticastGroup(const char *address) { int setPort = setsockopt(m_udp_sock, SOL_SOCKET, SO_REUSEADDR, &loop, sizeof(loop)); if (setPort < 0) { - UDP_LOG_ERROR("Setting Port Error"); + LOG_UDP_ERROR("Setting Port Error"); } struct ip_mreq mreq; @@ -315,7 +315,7 @@ void DoIPServer::setMulticastGroup(const char *address) { int setGroup = setsockopt(m_udp_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, reinterpret_cast(&mreq), sizeof(mreq)); if (setGroup < 0) { - UDP_LOG_ERROR("Setting address failed: {}", strerror(errno)); + LOG_UDP_ERROR("Setting address failed: {}", strerror(errno)); } } @@ -330,7 +330,7 @@ ssize_t DoIPServer::sendVehicleAnnouncement() { int setAddressError = inet_aton(address, &(m_clientAddress.sin_addr)); if (setAddressError != 0) { - DOIP_LOG_INFO("{} address set successfully: {}", + LOG_DOIP_INFO("{} address set successfully: {}", m_useLoopbackAnnouncements ? "Loopback" : "Broadcast", address); } @@ -338,7 +338,7 @@ ssize_t DoIPServer::sendVehicleAnnouncement() { // Only set broadcast option for broadcast mode int socketError = setsockopt(m_udp_sock, SOL_SOCKET, SO_BROADCAST, &m_broadcast, sizeof(m_broadcast)); if (socketError == 0) { - DOIP_LOG_INFO("Broadcast Option set successfully"); + LOG_DOIP_INFO("Broadcast Option set successfully"); } } @@ -351,10 +351,10 @@ ssize_t DoIPServer::sendVehicleAnnouncement() { sentBytes = sendto(m_udp_sock, msg.data(), msg.size(), 0, reinterpret_cast(&m_clientAddress), sizeof(m_clientAddress)); if (sentBytes > 0) { - UDP_LOG_INFO("Sent Vehicle Announcement"); + LOG_UDP_INFO("Sent Vehicle Announcement"); } else { - UDP_LOG_ERROR("Failed sending Vehicle Announcement: {}", strerror(errno)); - UDP_LOG_ERROR("Message: {}", fmt::streamed(msg)); + LOG_UDP_ERROR("Failed sending Vehicle Announcement: {}", strerror(errno)); + LOG_UDP_ERROR("Message: {}", fmt::streamed(msg)); } usleep(m_announceInterval * 1000); }