@md8n I’ve haven’t looked into the WebUI part but did take a look at the ESP32 part of the Maslow code (specifically Maslow.cpp
), I’m a bit more comfortable with C in embedded systems and python, not so much with javascript.
About how to make it more testable: this is definately doable. In previous projects, the first step was separating business logic from the hardware layer. Separating code like the maslow state machine, calibration calculations etc. from the hardware (and FluidNC framework) would allow mocking external dependencies for testing. For instance encoder values or current feedback; instead of directly calling hardware functions:
void updateEncoder() {
int value = readEncoder(); // Direct hardware call
processEncoderValue(value);
}
We could abstract the hardware layer:
class EncoderInterface {
public:
virtual int read() = 0;
};
class HardwareEncoder : public EncoderInterface {
public:
int read() override {
return readEncoder();
}
};
void updateEncoder(EncoderInterface &encoder) {
int value = encoder.read();
processEncoderValue(value);
}
This allows us to use a mock during testing:
class MockEncoder : public EncoderInterface {
public:
int read() override {
return 42; // Simulated test value
}
};
MockEncoder mock;
updateEncoder(mock);
Maybe the excisting FluidNC test framework can be leveraged (they have a CI pipeline using Github Actions). For WebUI, a similar idea could be used—splitting business logic from UX code.
There are drawbacks, including extra abstraction layers consuming ESP32 memory, might make the code harder for new contributors to learn. Adding tetst cases effectively doubles the codebase, increasing maintenance. Questions is if the benefits (more robust software, easier debugging, and easier maintenance on the long term) compensate for the significant effort involved.
Always fun to do is asking ChatGPT to take a look at the code It came up with the following:
- Modularization: Break large functions like
update
and home
into smaller, focused ones. Also, split the large Maslow.cpp
file into smaller, logically grouped files to make collaboration and version control easier. In line with @dlang efforts in this Github issue
- Error Handling: Add systematic retry mechanisms or fallbacks.
- Constants: Replace hardcoded values with named constants or parameters.
- Synchronization: Use thread-safe mechanisms for shared resources.
- Naming Conventions: Ensure consistent naming across the codebase.
- Redundancy: Refactor duplicated code (e.g.,
computeBL
, computeBR
) into reusable functions.
- Encapsulation: Use accessors/mutators for public class members like
axis_homed
.
- Testability: Abstract hardware dependencies with interfaces or mocks to decouple logic from hardware.
- Remove Dead Code: Identify and clean up unused or outdated sections of the code to improve clarity and maintainability.
Sounds like good suggestions to me, especially curious about why it came up with the synchronization part. I have spent time in mutex/semaphore hell, it was a nasty burning sensation.
I’m just scratching the surface of the software and haven’t looked to deeply into the GitHub issues and forum posts so I’m likely bringing up points already discussed.
That said, I’d love to contribute (disclaimer: have to find the time ).
I’ll start a GitHub discussion to avoid cluttering the forum. In the mean time, I’m trying to reproduce the Wifi connection issues (no luck yet).