libepaper 2.0.0
A C++23 library for controlling Waveshare e-paper displays on Raspberry Pi, featuring transparent sleep/wake management and a fluent builder API.
Loading...
Searching...
No Matches
mock_driver.hpp
Go to the documentation of this file.
1#pragma once
2
8#include <chrono>
9#include <cstddef>
10#include <cstdint>
11#include <expected>
12#include <filesystem>
13#include <format>
14#include <span>
15#include <vector>
16
17// Forward declare stb_image_write function
18extern "C" auto stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes)
19 -> int;
20
21namespace epaper {
22
45public:
54 explicit MockDriver(Device &device, std::size_t width = 600, std::size_t height = 300,
55 bool save_images = true) noexcept
56 : spi_(device.get_spi()), cs_(device.get_output(Pin{0})), // Dummy CS pin
57 dc_(device.get_output(Pin{1})), // Dummy DC pin
58 rst_(device.get_output(Pin{2})), // Dummy RST pin
59 busy_(device.get_input(Pin{3})), // Dummy BUSY pin
60 width_(width), height_(height), mode_(DisplayMode::BlackWhite), save_images_(save_images),
61 output_dir_("mock_outputs") {
62 if (save_images_) {
63 std::filesystem::create_directories(output_dir_);
64 }
65 }
66
67 ~MockDriver() = default;
68
69 // Lifecycle
70 [[nodiscard]] auto init(DisplayMode mode) -> std::expected<void, Error> {
71 ++init_count_;
72 if (init_should_fail_) {
73 return std::unexpected(Error(ErrorCode::DriverInitFailed, "MockDriver: init configured to fail"));
74 }
75 mode_ = mode;
76 initialized_ = true;
77 is_asleep_ = false;
78 return {};
79 }
80
81 [[nodiscard]] auto clear() -> std::expected<void, Error> {
82 ++clear_count_;
83 if (!initialized_) {
84 return std::unexpected(Error(ErrorCode::DriverNotInitialized, "MockDriver: not initialized"));
85 }
86 return {};
87 }
88
89 [[nodiscard]] auto display(std::span<const std::byte> buffer) -> std::expected<void, Error> {
90 ++display_count_;
91 if (!initialized_) {
92 return std::unexpected(Error(ErrorCode::DriverNotInitialized, "MockDriver: not initialized"));
93 }
94 if (display_should_fail_) {
95 return std::unexpected(Error(ErrorCode::RefreshFailed, "MockDriver: display configured to fail"));
96 }
97
98 // Auto-wake if asleep
99 if (is_asleep_) {
100 if (auto result = wake(); !result) {
101 return result;
102 }
103 }
104
105 // Store buffer for verification
106 last_buffer_ = std::vector<std::byte>(buffer.begin(), buffer.end());
107
108 // Save time-stamped image if enabled
109 if (save_images_) {
110 save_buffer_as_png(buffer);
111 }
112
113 return {};
114 }
115
116 [[nodiscard]] auto display_planes(std::span<const std::span<const std::byte>> planes) -> std::expected<void, Error> {
117 if (planes.empty()) {
118 return std::unexpected(Error(ErrorCode::InvalidDimensions, "MockDriver: no planes provided"));
119 }
120
121 if (planes.size() == 1) {
122 return display(planes[0]);
123 }
124
125 // Combine planes into a single contiguous buffer for saving
126 // This supports BWR/BWY modes where pixel_codec expects [Plane1][Plane2] layout
127 std::vector<std::byte> combined_buffer;
128 // Pre-calculate size to avoid reallocations
129 std::size_t total_size = 0;
130 for (const auto &plane : planes) {
131 total_size += plane.size();
132 }
133 combined_buffer.reserve(total_size);
134
135 for (const auto &plane : planes) {
136 combined_buffer.insert(combined_buffer.end(), plane.begin(), plane.end());
137 }
138
139 return display(combined_buffer);
140 }
141
142 // Power management
143 [[nodiscard]] auto sleep() -> std::expected<void, Error> {
144 ++sleep_count_;
145 if (sleep_should_fail_) {
146 return std::unexpected(Error(ErrorCode::TransferFailed, "MockDriver: sleep configured to fail"));
147 }
148 is_asleep_ = true;
149 return {};
150 }
151
152 [[nodiscard]] auto wake() -> std::expected<void, Error> {
153 ++wake_count_;
154 if (wake_should_fail_) {
155 return std::unexpected(Error(ErrorCode::DriverInitFailed, "MockDriver: wake configured to fail"));
156 }
157 is_asleep_ = false;
158 return {};
159 }
160
161 [[nodiscard]] auto power_off() -> std::expected<void, Error> {
162 // Mock: no-op, always succeeds
163 return {};
164 }
165
166 [[nodiscard]] auto power_on() -> std::expected<void, Error> {
167 // Mock: no-op, always succeeds
168 return {};
169 }
170
171 // Capabilities
172 [[nodiscard]] auto width() const noexcept -> std::size_t { return width_; }
173
174 [[nodiscard]] auto height() const noexcept -> std::size_t { return height_; }
175
176 [[nodiscard]] auto mode() const noexcept -> DisplayMode { return mode_; }
177
178 [[nodiscard]] auto buffer_size() const noexcept -> std::size_t {
179 const auto bpp = bits_per_pixel(mode_);
180 return (width_ * height_ * bpp + 7) / 8; // Round up to nearest byte
181 }
182
183 [[nodiscard]] auto supports_partial_refresh() const noexcept -> bool { return false; }
184
185 [[nodiscard]] auto supports_power_control() const noexcept -> bool { return false; }
186
187 [[nodiscard]] auto supports_wake() const noexcept -> bool { return true; }
188
189 // Test configuration methods
190 auto configure_init_failure(bool should_fail) noexcept -> void { init_should_fail_ = should_fail; }
191
192 auto configure_display_failure(bool should_fail) noexcept -> void { display_should_fail_ = should_fail; }
193
194 auto configure_sleep_failure(bool should_fail) noexcept -> void { sleep_should_fail_ = should_fail; }
195
196 auto configure_wake_failure(bool should_fail) noexcept -> void { wake_should_fail_ = should_fail; }
197
198 // Test verification methods
199 [[nodiscard]] auto init_called() const noexcept -> bool { return init_count_ > 0; }
200
201 [[nodiscard]] auto display_called() const noexcept -> bool { return display_count_ > 0; }
202
203 [[nodiscard]] auto sleep_called() const noexcept -> bool { return sleep_count_ > 0; }
204
205 [[nodiscard]] auto wake_called() const noexcept -> bool { return wake_count_ > 0; }
206
207 [[nodiscard]] auto clear_called() const noexcept -> bool { return clear_count_ > 0; }
208
209 [[nodiscard]] auto init_count() const noexcept -> std::size_t { return init_count_; }
210
211 [[nodiscard]] auto display_count() const noexcept -> std::size_t { return display_count_; }
212
213 [[nodiscard]] auto sleep_count() const noexcept -> std::size_t { return sleep_count_; }
214
215 [[nodiscard]] auto wake_count() const noexcept -> std::size_t { return wake_count_; }
216
217 [[nodiscard]] auto clear_count() const noexcept -> std::size_t { return clear_count_; }
218
219 [[nodiscard]] auto is_asleep() const noexcept -> bool { return is_asleep_; }
220
221 [[nodiscard]] auto is_initialized() const noexcept -> bool { return initialized_; }
222
223 [[nodiscard]] auto last_buffer() const noexcept -> const std::vector<std::byte> & { return last_buffer_; }
224
225 auto reset_counts() noexcept -> void {
226 init_count_ = 0;
227 display_count_ = 0;
228 sleep_count_ = 0;
229 wake_count_ = 0;
230 clear_count_ = 0;
231 }
232
233 auto set_output_directory(std::string_view dir) -> void {
234 output_dir_ = dir;
235 if (save_images_) {
236 std::filesystem::create_directories(output_dir_);
237 }
238 }
239
240 auto enable_image_saving(bool enable) -> void { save_images_ = enable; }
241
242private:
243 auto save_buffer_as_png(std::span<const std::byte> buffer) const -> void {
244 // Ensure output directory exists
245 std::filesystem::create_directories(output_dir_);
246
247 // Generate time-stamped filename
248 const auto now = std::chrono::system_clock::now();
249 const auto timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
250 const auto filename = std::format("{}/frame_{:013d}.png", output_dir_, timestamp);
251
252 // Convert framebuffer to RGB24 format for PNG
253 auto render_width = width_;
254 auto render_height = height_;
255
256 if (!buffer.empty() && height_ > 0) {
257 const auto bytes_per_row = buffer.size() / height_;
258 const auto mode_bpp = bits_per_pixel(mode_);
259 if (mode_bpp == 1 && bytes_per_row * height_ == buffer.size()) {
260 render_width = bytes_per_row * 8;
261 }
262 if (mode_bpp == 2 && bytes_per_row * height_ == buffer.size()) {
263 render_width = bytes_per_row * 4;
264 }
265 }
266 std::vector<std::uint8_t> rgb_data;
267 rgb_data.reserve(render_width * render_height * 3);
268
269 for (std::size_t y = 0; y < render_height; ++y) {
270 for (std::size_t x = 0; x < render_width; ++x) {
271 const auto color = get_pixel_from_buffer(mode_, buffer, render_width, render_height, x, y);
272 const auto rgb = color_to_rgb(color);
273 rgb_data.push_back(rgb.r);
274 rgb_data.push_back(rgb.g);
275 rgb_data.push_back(rgb.b);
276 }
277 }
278
279 // Write PNG file
280 stbi_write_png(filename.c_str(), static_cast<int>(render_width), static_cast<int>(render_height), 3,
281 rgb_data.data(), static_cast<int>(render_width * 3));
282 }
283
284 // HAL hardware components
285 Device::HalSpi spi_;
286 Device::HalOutput cs_;
287 Device::HalOutput dc_;
288 Device::HalOutput rst_;
289 Device::HalInput busy_;
290
291 // Display configuration
292 std::size_t width_;
293 std::size_t height_;
294 DisplayMode mode_{};
295 bool initialized_{};
296 bool is_asleep_{};
297
298 // Failure configuration
299 bool init_should_fail_{};
300 bool display_should_fail_{};
301 bool sleep_should_fail_{};
302 bool wake_should_fail_{};
303
304 // Call tracking
305 std::size_t init_count_{};
306 std::size_t display_count_{};
307 std::size_t sleep_count_{};
308 std::size_t wake_count_{};
309 std::size_t clear_count_{};
310
311 // Buffer storage for verification
312 std::vector<std::byte> last_buffer_;
313
314 // Image saving
315 bool save_images_;
316 std::string output_dir_;
317};
318
319template <> struct driver_traits<MockDriver> {
320 static constexpr DisplayMode max_mode = DisplayMode::Spectra6;
321 static constexpr bool supports_grayscale = true;
322 static constexpr bool supports_partial_refresh = true;
323 static constexpr bool supports_power_control = true;
324 static constexpr bool supports_wake_from_sleep = true;
325 static constexpr std::size_t max_width = 800;
326 static constexpr std::size_t max_height = 600;
327};
328
329} // namespace epaper
Compile-time driver capability traits system.
Definition device.hpp:158
Definition mock_driver.hpp:44
auto init_count() const noexcept -> std::size_t
Definition mock_driver.hpp:209
auto clear_called() const noexcept -> bool
Definition mock_driver.hpp:207
auto is_asleep() const noexcept -> bool
Definition mock_driver.hpp:219
auto display_planes(std::span< const std::span< const std::byte > > planes) -> std::expected< void, Error >
Definition mock_driver.hpp:116
auto is_initialized() const noexcept -> bool
Definition mock_driver.hpp:221
auto wake_called() const noexcept -> bool
Definition mock_driver.hpp:205
auto wake_count() const noexcept -> std::size_t
Definition mock_driver.hpp:215
auto sleep_called() const noexcept -> bool
Definition mock_driver.hpp:203
auto init_called() const noexcept -> bool
Definition mock_driver.hpp:199
auto display_count() const noexcept -> std::size_t
Definition mock_driver.hpp:211
auto power_off() -> std::expected< void, Error >
Definition mock_driver.hpp:161
auto clear() -> std::expected< void, Error >
Definition mock_driver.hpp:81
auto configure_wake_failure(bool should_fail) noexcept -> void
Definition mock_driver.hpp:196
auto supports_partial_refresh() const noexcept -> bool
Definition mock_driver.hpp:183
auto init(DisplayMode mode) -> std::expected< void, Error >
Definition mock_driver.hpp:70
~MockDriver()=default
auto display_called() const noexcept -> bool
Definition mock_driver.hpp:201
auto power_on() -> std::expected< void, Error >
Definition mock_driver.hpp:166
auto clear_count() const noexcept -> std::size_t
Definition mock_driver.hpp:217
auto configure_display_failure(bool should_fail) noexcept -> void
Definition mock_driver.hpp:192
auto display(std::span< const std::byte > buffer) -> std::expected< void, Error >
Definition mock_driver.hpp:89
auto height() const noexcept -> std::size_t
Definition mock_driver.hpp:174
auto reset_counts() noexcept -> void
Definition mock_driver.hpp:225
auto mode() const noexcept -> DisplayMode
Definition mock_driver.hpp:176
auto set_output_directory(std::string_view dir) -> void
Definition mock_driver.hpp:233
auto wake() -> std::expected< void, Error >
Definition mock_driver.hpp:152
auto configure_init_failure(bool should_fail) noexcept -> void
Definition mock_driver.hpp:190
auto width() const noexcept -> std::size_t
Definition mock_driver.hpp:172
MockDriver(Device &device, std::size_t width=600, std::size_t height=300, bool save_images=true) noexcept
Construct MockDriver using Linux Device with custom dimensions.
Definition mock_driver.hpp:54
auto buffer_size() const noexcept -> std::size_t
Definition mock_driver.hpp:178
auto sleep_count() const noexcept -> std::size_t
Definition mock_driver.hpp:213
auto supports_wake() const noexcept -> bool
Definition mock_driver.hpp:187
auto enable_image_saving(bool enable) -> void
Definition mock_driver.hpp:240
auto sleep() -> std::expected< void, Error >
Definition mock_driver.hpp:143
auto supports_power_control() const noexcept -> bool
Definition mock_driver.hpp:185
auto last_buffer() const noexcept -> const std::vector< std::byte > &
Definition mock_driver.hpp:223
auto configure_sleep_failure(bool should_fail) noexcept -> void
Definition mock_driver.hpp:194
Definition device.hpp:38
auto stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes) -> int
Definition color.hpp:5
@ DriverNotInitialized
Driver has not been initialized.
@ InvalidDimensions
Invalid image dimensions.
@ DriverInitFailed
Driver initialization failed.
@ TransferFailed
SPI/data transfer failed.
@ RefreshFailed
Display refresh operation failed.
constexpr auto bits_per_pixel(DisplayMode mode) noexcept -> std::uint8_t
Get bits per pixel for a display mode.
Definition driver.hpp:60
constexpr auto color_to_rgb(Color color) noexcept -> RGB
Convert a Color enum to RGB values.
Definition pixel_codec.hpp:154
DisplayMode
Definition driver.hpp:46
@ Spectra6
6-color: Black, White, Red, Yellow, Blue, Green (3-bit)
@ BlackWhite
1-bit black and white (2 colors)
auto get_pixel_from_buffer(DisplayMode mode, std::span< const std::byte > buffer, std::size_t width, std::size_t height, std::size_t x, std::size_t y) noexcept -> Color
Read a pixel from a buffer based on display mode.
Definition pixel_codec.hpp:569
Centralized pixel encoding/decoding utilities for framebuffers.
Definition errors.hpp:140
Driver capabilities trait template.
Definition capabilities.hpp:110