firmware-base/experiments/main.cpp
2025-05-23 16:53:09 +02:00

218 lines
9.4 KiB
C++
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// feature_di_component_demo.cpp -------------------------------------------
// Build the "lean" variant : g++ -std=c++20 -O2 -DDEMO_MAIN feature_di_component_demo.cpp -o demo
// Build the "Modbus" variant: g++ -std=c++20 -O2 -DDEMO_MAIN -DENABLE_MODBUS feature_di_component_demo.cpp -o demo_net
// @link ://chatgpt.com/share/6803c6b7-0468-8001-857f-352a31c8c9bd
// ───────────────────────────────────────────────────────────────────────────
#include <Arduino.h> // Added for Serial
#include <cstdint>
#include <cstddef>
#include <array>
// #include <iostream> // Replaced by Arduino.h
#include <type_traits>
#include <string_view> // For info() return type example
#include <cstring> // For strcmp
/*──────────────────── 1. Tiny utility -----------------------------------*/
struct RegisterMap {
const uint16_t* data;
std::size_t size;
};
/*──────────────────── 2. "Feature" concept (any module that has setup/loop/info)*/
// Renamed and updated SFINAE check
template<typename, typename = std::void_t<>>
struct is_valid_feature : std::false_type {};
template<typename F>
struct is_valid_feature<F, std::void_t<
decltype(std::declval<F&>().setup()),
decltype(std::declval<F&>().loop()),
decltype(std::declval<const F&>().info()) // Added const info() check
>> : std::true_type {};
// Updated helper function
template<typename F> constexpr bool is_valid_feature_func() { return is_valid_feature<F>::value; }
/*──────────────────── 3. Individual feature packs -----------------------*/
// 3a. Core sensor logic ────────────────────────────────────────────────
struct CoreFeature {
static constexpr bool has_network = false;
void setup() { Serial.println(F("[Core] setup")); } // Use Serial
void loop () { /*Serial.println(F("[Core] loop"));*/ } // Commented out for less noise
// Added info method
void info() const { Serial.println(F("[Core] Info: Basic temperature sensing.")); } // Use Serial
float readCelsius() const { return 25.0f; }
};
// 3b. Optional Modbus/Network feature ──────────────────────────────────
struct ModbusFeature {
static constexpr bool has_network = true;
std::array<uint16_t,2> regs{ 250 /*25 °C×10*/, 0 };
void setup() { Serial.println(F("[Modbus] setup")); } // Use Serial
void loop () { /*Serial.println(F("[Modbus] loop"));*/ } // Commented out for less noise
// Added info method
void info() const { // Use Serial
Serial.print(F("[Modbus] Info: Modbus TCP/IP enabled. Address: "));
Serial.println(deviceAddress());
}
RegisterMap registerMapping() const { return { regs.data(), regs.size() }; }
uint16_t deviceAddress() const { return 42; }
void onRegisterWrite(uint16_t r, uint16_t v) {
if (r < regs.size()) regs[r] = v;
}
};
// 3c. Default Logger feature ─────────────────────────────────────────────
struct LoggerFeature {
static constexpr bool has_network = true; // Assuming logger might send logs over network
void setup() { Serial.println(F("[Logger] setup")); } // Use Serial
void loop () { /*Serial.println(F("[Logger] loop"));*/ } // Commented out for less noise
// Added info method
void info() const { Serial.println(F("[Logger] Info: Basic console logging active.")); } // Use Serial
};
/*──────────────────── 4. Generic "Device" aggregator ───────────────────*/
// Use static_assert with the updated SFINAE check
// Inherit LoggerFeature by default
template<typename... Fs>
class Device : public LoggerFeature, public Fs...
{
// Ensure the default and all *additional* base classes Fs satisfy the requirements
static_assert(is_valid_feature_func<LoggerFeature>(), "Default LoggerFeature must implement setup(), loop(), and info()");
// Use C++17 fold expression within the static_assert for additional features
static_assert(sizeof...(Fs) == 0 || (is_valid_feature_func<Fs>() && ...), "All additional features must implement setup(), loop(), and info()");
public:
void setup() {
LoggerFeature::setup(); // Explicitly call setup for the default feature
if constexpr (sizeof...(Fs) > 0) { // Check if there are other features
(Fs::setup(), ...); // Call setup for the rest using fold-expression
}
}
void loop () {
LoggerFeature::loop(); // Explicitly call loop for the default feature
if constexpr (sizeof...(Fs) > 0) {
(Fs::loop (), ...); // Call loop for the rest
}
}
// Added aggregated info method
void info() const {
// Call info() for LoggerFeature and all Fs... using a fold expression
(LoggerFeature::info(), (Fs::info(), ...));
}
// Include LoggerFeature's network status in the calculation
static constexpr bool has_network = LoggerFeature::has_network || (false || ... || Fs::has_network);
};
/*──────────────────── 5. Choose the feature bundle (application layer) ───*/
// LoggerFeature is now implicitly included by Device
#ifdef ENABLE_MODBUS
using TemperatureSensor = Device<CoreFeature, ModbusFeature>;
#else
using TemperatureSensor = Device<CoreFeature>;
#endif
/*──────────────────── Arduino Entry Points -------------------------------*/
// Instantiate the device globally
TemperatureSensor device;
// Buffer for serial commands
#define SERIAL_CMD_BUFFER_SIZE 64
char serialCmdBuffer[SERIAL_CMD_BUFFER_SIZE];
uint8_t serialCmdBufferIdx = 0;
// Helper function to print commands
void printHelp() {
Serial.println(F("--- Serial Commands ---"));
Serial.println(F("1 - Show device info"));
Serial.println(F("? - Print this help message"));
}
void setup() {
// Initialize Serial for logging, if needed (optional)
Serial.begin(115200);
// Wait for serial port to connect (needed for native USB)
// On some boards, this delay is needed to allow the Serial Monitor to connect
// On others (like ESP32), Serial begins immediately. Adjust if needed.
delay(1000);
while (!Serial && millis() < 3000); // Wait up to 3 seconds
Serial.println(F("\n--- Serial Interface Ready ---"));
printHelp();
// <<< Hypothetical serial connection point >>>
Serial.println(F("--- Device Info ---"));
device.info(); // Call the aggregated info method
Serial.println(F("--- Device Setup ---"));
device.setup(); // Call the aggregated setup
Serial.println(F("--- Setup Complete ---"));
}
void loop() {
device.loop(); // Call the aggregated loop
// --- Handle Serial Input ---
while (Serial.available() > 0) {
char receivedChar = Serial.read();
// Handle backspace
if (receivedChar == '\b' || receivedChar == 127) {
if (serialCmdBufferIdx > 0) {
serialCmdBufferIdx--;
Serial.print("\b \b"); // Move cursor back, print space, move back again
}
continue; // Continue to next character
}
// Echo character back (optional, good for interactive use)
Serial.print(receivedChar);
// Process command on newline or carriage return
if (receivedChar == '\n' || receivedChar == '\r') {
serialCmdBuffer[serialCmdBufferIdx] = '\0'; // Null-terminate the string
Serial.println(); // Move to next line after command received
if (serialCmdBufferIdx > 0) { // Process if buffer is not empty
// --- Process Commands ---
if (strcmp(serialCmdBuffer, "1") == 0) {
Serial.println(F("--- Device Info ---"));
device.info();
} else if (strcmp(serialCmdBuffer, "?") == 0) {
printHelp();
} else {
Serial.print(F("Unknown command: '"));
Serial.print(serialCmdBuffer);
Serial.println(F("'"));
printHelp(); // Show help on unknown command
}
}
// Reset buffer index for the next command
serialCmdBufferIdx = 0;
serialCmdBuffer[0] = '\0';
} else if (serialCmdBufferIdx < SERIAL_CMD_BUFFER_SIZE - 1) {
// Add character to buffer if it's not newline/CR and buffer has space
serialCmdBuffer[serialCmdBufferIdx++] = receivedChar;
}
// Ignore characters if buffer is full until newline/CR is received
}
// Add delay or other logic if needed for stability
// delay(10);
}
// ───────────────────────────────────────────────────────────────────────────