Skip to content

Commit 1cec79c

Browse files
Merge pull request psmgeelen#10 from GreenWizard2015/feature/add-power-control
Pump Power Level Control Feature
2 parents 3f1d094 + 632f423 commit 1cec79c

28 files changed

+482
-81
lines changed

controller/tea_poor/lib/Arduino/WaterPumpController.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ void WaterPumpController::setup() {
2020
stop();
2121
}
2222

23-
void WaterPumpController::start() {
23+
void WaterPumpController::start(int power) {
2424
_isRunning = true;
2525
digitalWrite(_brakePin, LOW); // release breaks
26-
analogWrite(_powerPin, 255);
26+
analogWrite(_powerPin, power);
2727
}
2828

2929
void WaterPumpController::stop() {

controller/tea_poor/lib/Arduino/WaterPumpController.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,13 @@ class WaterPumpController: public IWaterPump {
77
const int _directionPin;
88
const int _brakePin;
99
const int _powerPin;
10-
const int _maxPower = 255;
1110
bool _isRunning = false;
1211
public:
1312
WaterPumpController(int directionPin, int brakePin, int powerPin);
1413
virtual ~WaterPumpController() override;
1514

1615
virtual void setup() override;
17-
virtual void start() override;
16+
virtual void start(int power) override;
1817
virtual void stop() override;
1918

2019
virtual bool isRunning() const override { return _isRunning; }
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#ifndef ADJUSTEDWATERPUMP_H
2+
#define ADJUSTEDWATERPUMP_H
3+
#include <IWaterPump.h>
4+
#include <math.h>
5+
6+
// lightweight wrapper around IWaterPump
7+
// its purpose is to adjust power value to the range of 0..255, for now
8+
class AdjustedWaterPump: public IWaterPump {
9+
private:
10+
const IWaterPumpPtr _pump;
11+
public:
12+
AdjustedWaterPump(IWaterPumpPtr pump) : _pump(pump) {}
13+
virtual ~AdjustedWaterPump() override {}
14+
15+
virtual void setup() override { _pump->setup(); }
16+
virtual void stop() override { _pump->stop(); }
17+
virtual bool isRunning() const override { return _pump->isRunning(); }
18+
19+
virtual void start(int powerInPercents) override {
20+
// convert percents to 0..255 range, using float
21+
const float power = (255.0f / 100.0f) * (float)powerInPercents;
22+
_pump->start(floor(power));
23+
}
24+
};
25+
26+
#endif

controller/tea_poor/lib/CommandProcessor/CommandProcessor.cpp renamed to controller/tea_poor/lib/Core/CommandProcessor.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,17 @@ std::string CommandProcessor::status() {
4444
return response.str();
4545
}
4646

47-
std::string CommandProcessor::pour_tea(const char *milliseconds) {
47+
std::string CommandProcessor::pour_tea(const char *milliseconds, const char *power) {
4848
if (!isValidIntNumber(milliseconds, _waterPumpSafeThreshold + 1)) {
4949
// send error message as JSON
5050
return std::string("{ \"error\": \"invalid milliseconds value\" }");
5151
}
52+
if (!isValidIntNumber(power, 101)) {
53+
// send error message as JSON
54+
return std::string("{ \"error\": \"invalid power value\" }");
55+
}
5256
// start pouring tea
53-
_waterPump->start( atoi(milliseconds), _env->time() );
57+
_waterPump->start( atoi(milliseconds), atoi(power) );
5458
return status();
5559
}
5660

controller/tea_poor/lib/CommandProcessor/CommandProcessor.h renamed to controller/tea_poor/lib/Core/CommandProcessor.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class CommandProcessor {
2020
{}
2121

2222
std::string status();
23-
std::string pour_tea(const char *milliseconds);
23+
std::string pour_tea(const char *milliseconds, const char *power);
2424
std::string stop();
2525
private:
2626
const int _waterPumpSafeThreshold;

controller/tea_poor/lib/WaterPumpScheduler/WaterPumpScheduler.cpp renamed to controller/tea_poor/lib/Core/WaterPumpScheduler.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
#include "WaterPumpScheduler.h"
22

3-
WaterPumpScheduler::WaterPumpScheduler(IWaterPumpPtr waterPump, unsigned long forceStopIntervalMs) :
3+
WaterPumpScheduler::WaterPumpScheduler(IWaterPumpPtr waterPump, IEnvironmentPtr env, unsigned long forceStopIntervalMs) :
44
_waterPump(waterPump),
5+
_env(env),
56
_forceStopIntervalMs(forceStopIntervalMs)
67
{
78
}
@@ -12,17 +13,18 @@ void WaterPumpScheduler::setup() {
1213
_waterPump->setup();
1314
}
1415

15-
void WaterPumpScheduler::start(unsigned long runTimeMs, unsigned long currentTimeMs) {
16-
_stopTime = currentTimeMs + runTimeMs;
17-
_waterPump->start();
16+
void WaterPumpScheduler::start(unsigned long runTimeMs, int power) {
17+
_stopTime = _env->time() + runTimeMs;
18+
_waterPump->start(power);
1819
}
1920

2021
void WaterPumpScheduler::stop() {
2122
_waterPump->stop();
2223
_stopTime = 0; // a bit of paranoia :)
2324
}
2425

25-
void WaterPumpScheduler::tick(unsigned long currentTimeMs) {
26+
void WaterPumpScheduler::tick() {
27+
const auto currentTimeMs = _env->time();
2628
if (_stopTime <= currentTimeMs) {
2729
stop();
2830
_stopTime = currentTimeMs + _forceStopIntervalMs; // force stop after X milliseconds

controller/tea_poor/lib/WaterPumpScheduler/WaterPumpScheduler.h renamed to controller/tea_poor/lib/Core/WaterPumpScheduler.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,30 @@
33

44
#include <IWaterPump.h>
55
#include <IWaterPumpSchedulerAPI.h>
6+
#include <IEnvironment.h>
67

78
// This class is responsible for scheduling water pump
89
// It is used to make sure that water pump is running for a limited time
910
// It is also ensuring that water pump is stopped if not needed
1011
class WaterPumpScheduler : public IWaterPumpSchedulerAPI {
1112
private:
1213
IWaterPumpPtr _waterPump;
14+
IEnvironmentPtr _env;
1315
unsigned long _stopTime = 0;
1416
// each X milliseconds will force stop water pump
1517
unsigned long _forceStopIntervalMs;
1618
public:
17-
WaterPumpScheduler(IWaterPumpPtr waterPump, unsigned long forceStopIntervalMs);
18-
WaterPumpScheduler(IWaterPumpPtr waterPump) : WaterPumpScheduler(waterPump, 1000) {}
19+
WaterPumpScheduler(IWaterPumpPtr waterPump, IEnvironmentPtr env, unsigned long forceStopIntervalMs);
20+
// forceStopIntervalMs is set to 1000ms by default
21+
WaterPumpScheduler(IWaterPumpPtr waterPump, IEnvironmentPtr env) : WaterPumpScheduler(waterPump, env, 1000) {}
1922
~WaterPumpScheduler();
2023

2124
void setup();
2225
// for simplicity and testability we are passing current time as parameter
23-
void tick(unsigned long currentTimeMs);
26+
void tick();
2427

2528
// Public API
26-
void start(unsigned long runTimeMs, unsigned long currentTimeMs) override;
29+
void start(unsigned long runTimeMs, int power) override;
2730
void stop() override;
2831
WaterPumpStatus status() override;
2932
};

controller/tea_poor/lib/interfaces/IWaterPump.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ class IWaterPump {
88
virtual ~IWaterPump() {}
99

1010
virtual void setup() = 0;
11-
virtual void start() = 0;
11+
virtual void start(int power) = 0;
1212
virtual void stop() = 0;
1313

1414
virtual bool isRunning() const = 0;

controller/tea_poor/lib/interfaces/IWaterPumpSchedulerAPI.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class IWaterPumpSchedulerAPI {
2222
public:
2323
virtual ~IWaterPumpSchedulerAPI() {}
2424
virtual void stop() = 0;
25-
virtual void start(unsigned long runTimeMs, unsigned long currentTimeMs) = 0;
25+
virtual void start(unsigned long runTimeMs, int power) = 0;
2626
virtual WaterPumpStatus status() = 0;
2727
};
2828

controller/tea_poor/src/main.cpp

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include <Arduino.h>
22
#include <memory>
33
#include <WaterPumpController.h>
4+
#include <AdjustedWaterPump.h>
45
#include <WaterPumpScheduler.h>
56
#include <RemoteControl.h>
67
#include <CommandProcessor.h>
@@ -9,13 +10,15 @@
910
#include <sstream>
1011
#include <ArduinoEnvironment.h>
1112

12-
IEnvironmentPtr env = std::make_shared<ArduinoEnvironment>();
13+
const auto env = std::make_shared<ArduinoEnvironment>();
1314

1415
// Setting up water pump
15-
auto waterPump = std::make_shared<WaterPumpScheduler>(
16-
std::make_shared<WaterPumpController>(
17-
WATER_PUMP_DIRECTION_PIN, WATER_PUMP_BRAKE_PIN, WATER_PUMP_POWER_PIN
18-
)
16+
const auto waterPump = std::make_shared<WaterPumpScheduler>(
17+
std::make_shared<AdjustedWaterPump>(
18+
std::make_shared<WaterPumpController>(
19+
WATER_PUMP_DIRECTION_PIN, WATER_PUMP_BRAKE_PIN, WATER_PUMP_POWER_PIN
20+
)
21+
), env
1922
);
2023

2124
// build command processor
@@ -46,8 +49,11 @@ RemoteControl remoteControl(
4649
app.get("/pour_tea", [](Request &req, Response &res) {
4750
char milliseconds[64];
4851
req.query("milliseconds", milliseconds, 64);
52+
53+
char power[64];
54+
req.query("powerLevel", power, 64);
4955

50-
const auto response = commandProcessor.pour_tea(milliseconds);
56+
const auto response = commandProcessor.pour_tea(milliseconds, power);
5157
withExtraHeaders(res);
5258
res.print(response.c_str());
5359
});
@@ -73,6 +79,6 @@ void setup() {
7379
}
7480

7581
void loop() {
76-
waterPump->tick(millis());
82+
waterPump->tick();
7783
remoteControl.process();
7884
};

controller/tea_poor/test/test_native/main.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// include tests
44
#include "tests/WaterPumpScheduler_test.h"
55
#include "tests/CommandProcessor_test.h"
6+
#include "tests/AdjustedWaterPump_test.h"
67

78
int main(int argc, char **argv) {
89
::testing::InitGoogleTest(&argc, argv);
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#include <gtest/gtest.h>
2+
#include "mocks/FakeWaterPump.h"
3+
#include <AdjustedWaterPump.h>
4+
5+
// test that pumps power passed as percents is converted to 0..255 range
6+
TEST(AdjustedWaterPump, test_pumps_power_passed_as_percents_is_converted_to_0_255_range) {
7+
const auto fakeWaterPump = std::make_shared<FakeWaterPump>();
8+
AdjustedWaterPump adjustedWaterPump(fakeWaterPump);
9+
// list of pairs: (powerInPercents, expectedPower)
10+
const std::vector<std::pair<int, int>> tests = {
11+
{0, 0}, {1, 2}, {2, 5},
12+
{50, 127}, {100, 255}
13+
};
14+
for(const auto& test: tests) {
15+
adjustedWaterPump.start(test.first);
16+
ASSERT_EQ(fakeWaterPump->power(), test.second);
17+
}
18+
}

controller/tea_poor/test/test_native/tests/CommandProcessor_test.h

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,53 +3,68 @@
33
#include "mocks/FakeWaterPumpSchedulerAPI.h"
44
#include "mocks/FakeEnvironment.h"
55

6+
const auto VALID_POWER = "100";
67
const auto INVALID_TIME_ERROR_MESSAGE = "{ \"error\": \"invalid milliseconds value\" }";
8+
const auto INVALID_POWER_ERROR_MESSAGE = "{ \"error\": \"invalid power value\" }";
79
// test that pour_tea() method returns error message if milliseconds:
810
// - greater than threshold
911
// - less than 0
1012
// - empty string
1113
// - not a number
1214
TEST(CommandProcessor, pour_tea_invalid_milliseconds) {
1315
CommandProcessor commandProcessor(123, nullptr, nullptr);
14-
ASSERT_EQ(commandProcessor.pour_tea("1234"), INVALID_TIME_ERROR_MESSAGE);
15-
ASSERT_EQ(commandProcessor.pour_tea("-1"), INVALID_TIME_ERROR_MESSAGE);
16-
ASSERT_EQ(commandProcessor.pour_tea(""), INVALID_TIME_ERROR_MESSAGE);
17-
ASSERT_EQ(commandProcessor.pour_tea("abc"), INVALID_TIME_ERROR_MESSAGE);
16+
ASSERT_EQ(commandProcessor.pour_tea("1234", VALID_POWER), INVALID_TIME_ERROR_MESSAGE);
17+
ASSERT_EQ(commandProcessor.pour_tea("-1", VALID_POWER), INVALID_TIME_ERROR_MESSAGE);
18+
ASSERT_EQ(commandProcessor.pour_tea("", VALID_POWER), INVALID_TIME_ERROR_MESSAGE);
19+
ASSERT_EQ(commandProcessor.pour_tea("abc", VALID_POWER), INVALID_TIME_ERROR_MESSAGE);
20+
}
21+
22+
// test that pour_tea() method returns error message if power:
23+
// - greater than 100
24+
// - less than 0
25+
// - empty string
26+
// - not a number
27+
TEST(CommandProcessor, pour_tea_invalid_power) {
28+
CommandProcessor commandProcessor(123, nullptr, nullptr);
29+
ASSERT_EQ(commandProcessor.pour_tea("123", "101"), INVALID_POWER_ERROR_MESSAGE);
30+
ASSERT_EQ(commandProcessor.pour_tea("123", "-1"), INVALID_POWER_ERROR_MESSAGE);
31+
ASSERT_EQ(commandProcessor.pour_tea("123", ""), INVALID_POWER_ERROR_MESSAGE);
32+
ASSERT_EQ(commandProcessor.pour_tea("123", "abc"), INVALID_POWER_ERROR_MESSAGE);
1833
}
1934

2035
// for simplicity of the UI, we should accept as valid 0 and exactly threshold value
2136
TEST(CommandProcessor, pour_tea_valid_boundary_values) {
22-
auto env = std::make_shared<FakeEnvironment>();
23-
auto waterPump = std::make_shared<FakeWaterPumpSchedulerAPI>();
37+
const auto env = std::make_shared<FakeEnvironment>();
38+
const auto waterPump = std::make_shared<FakeWaterPumpSchedulerAPI>(env);
2439
CommandProcessor commandProcessor(123, env, waterPump);
25-
26-
ASSERT_NE(commandProcessor.pour_tea("0"), INVALID_TIME_ERROR_MESSAGE);
27-
ASSERT_NE(commandProcessor.pour_tea("123"), INVALID_TIME_ERROR_MESSAGE);
40+
41+
ASSERT_NE(commandProcessor.pour_tea("0", VALID_POWER), INVALID_TIME_ERROR_MESSAGE);
42+
ASSERT_NE(commandProcessor.pour_tea("123", VALID_POWER), INVALID_TIME_ERROR_MESSAGE);
2843
}
2944

30-
// test that start pouring tea by calling pour_tea() method and its stops after T milliseconds
45+
// test that start pouring tea by calling pour_tea() method with specified parameters
3146
TEST(CommandProcessor, pour_tea) {
32-
auto env = std::make_shared<FakeEnvironment>();
47+
const auto env = std::make_shared<FakeEnvironment>();
48+
const auto waterPump = std::make_shared<FakeWaterPumpSchedulerAPI>(env);
3349
env->time(2343);
34-
auto waterPump = std::make_shared<FakeWaterPumpSchedulerAPI>();
3550
CommandProcessor commandProcessor(10000, env, waterPump);
36-
const auto response = commandProcessor.pour_tea("1234");
37-
ASSERT_EQ(waterPump->_log, "start(1234, 2343)\n");
51+
const auto response = commandProcessor.pour_tea("1234", "23");
52+
ASSERT_EQ(waterPump->_log, "start(1234, 23, 2343)\n");
3853
}
3954

4055
// test that stop() method stops pouring tea
4156
TEST(CommandProcessor, stop) {
42-
auto env = std::make_shared<FakeEnvironment>();
43-
auto waterPump = std::make_shared<FakeWaterPumpSchedulerAPI>();
57+
const auto env = std::make_shared<FakeEnvironment>();
58+
const auto waterPump = std::make_shared<FakeWaterPumpSchedulerAPI>(env);
4459
CommandProcessor commandProcessor(123, env, waterPump);
4560
const auto response = commandProcessor.stop();
4661
ASSERT_EQ(waterPump->_log, "stop()\n");
4762
}
4863

4964
// test that status() method returns JSON string with water pump status
5065
TEST(CommandProcessor, status) {
51-
auto env = std::make_shared<FakeEnvironment>();
52-
auto waterPump = std::make_shared<FakeWaterPumpSchedulerAPI>();
66+
const auto env = std::make_shared<FakeEnvironment>();
67+
const auto waterPump = std::make_shared<FakeWaterPumpSchedulerAPI>(env);
5368
CommandProcessor commandProcessor(123, env, waterPump);
5469
const auto response = commandProcessor.status();
5570
ASSERT_EQ(response, "{"
@@ -65,11 +80,11 @@ TEST(CommandProcessor, status) {
6580

6681
// test that status() method returns JSON string with actual time left
6782
TEST(CommandProcessor, status_running) {
68-
auto env = std::make_shared<FakeEnvironment>();
69-
auto waterPump = std::make_shared<FakeWaterPumpSchedulerAPI>();
83+
const auto env = std::make_shared<FakeEnvironment>();
84+
const auto waterPump = std::make_shared<FakeWaterPumpSchedulerAPI>(env);
7085
CommandProcessor commandProcessor(12345, env, waterPump);
7186

72-
commandProcessor.pour_tea("1123");
87+
commandProcessor.pour_tea("1123", "100");
7388

7489
env->time(123);
7590
waterPump->_status.isRunning = true;

0 commit comments

Comments
 (0)