Skip to content

Commit 080d2b7

Browse files
committed
support named triggers
1 parent 9054992 commit 080d2b7

3 files changed

Lines changed: 134 additions & 53 deletions

File tree

src/PPUC.cpp

Lines changed: 112 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "PPUC.h"
22

33
#include <algorithm>
4+
#include <cctype>
45
#include <cstring>
56
#include <set>
67
#include <unordered_map>
@@ -174,38 +175,102 @@ bool PPUC::AbortConfigurationEarly() const {
174175
return m_pRS485Comm->ShouldAbortConfigurationEarly();
175176
}
176177

177-
void PPUC::SendTriggerConfigBlock(const YAML::Node& items, uint32_t type,
178-
uint8_t board, uint32_t port) {
179-
if (items) {
180-
for (YAML::Node n_item : items) {
181-
if (AbortConfigurationEarly()) {
182-
return;
183-
}
184-
uint8_t index = 0;
185-
m_pRS485Comm->SendConfigEvent(
186-
new ConfigEvent(board, (uint8_t)CONFIG_TOPIC_TRIGGER, index++,
187-
(uint8_t)CONFIG_TOPIC_PORT, port));
188-
m_pRS485Comm->SendConfigEvent(
189-
new ConfigEvent(board, (uint8_t)CONFIG_TOPIC_TRIGGER, index++,
190-
(uint8_t)CONFIG_TOPIC_TYPE, type));
191-
std::string c_source = n_item["source"].as<std::string>();
192-
uint32_t source = EVENT_SOURCE_SWITCH;
193-
if (strcmp(c_source.c_str(), "S") == 0) {
194-
source = EVENT_SOURCE_SOLENOID;
195-
} else if (strcmp(c_source.c_str(), "L") == 0) {
196-
source = EVENT_SOURCE_LIGHT;
197-
}
198-
m_pRS485Comm->SendConfigEvent(
199-
new ConfigEvent(board, (uint8_t)CONFIG_TOPIC_TRIGGER, index++,
200-
(uint8_t)CONFIG_TOPIC_SOURCE, source));
201-
m_pRS485Comm->SendConfigEvent(new ConfigEvent(
202-
board, (uint8_t)CONFIG_TOPIC_TRIGGER, index++,
203-
(uint8_t)CONFIG_TOPIC_NUMBER, n_item["number"].as<uint32_t>()));
204-
m_pRS485Comm->SendConfigEvent(new ConfigEvent(
205-
board, (uint8_t)CONFIG_TOPIC_LAMPS, index++,
206-
(uint8_t)CONFIG_TOPIC_VALUE, n_item["value"].as<uint32_t>()));
207-
}
178+
namespace {
179+
uint32_t ResolvePwmType(const std::string& type) {
180+
if (type == "flasher") {
181+
return PWM_TYPE_FLASHER;
182+
}
183+
if (type == "lamp") {
184+
return PWM_TYPE_LAMP;
208185
}
186+
if (type == "motor") {
187+
return PWM_TYPE_MOTOR;
188+
}
189+
if (type == "shaker") {
190+
return PWM_TYPE_SHAKER;
191+
}
192+
return PWM_TYPE_SOLENOID;
193+
}
194+
195+
bool IsDecimalScalar(const std::string& value) {
196+
return !value.empty() &&
197+
std::all_of(value.begin(), value.end(), [](unsigned char c) {
198+
return std::isdigit(c) != 0;
199+
});
200+
}
201+
202+
uint32_t ResolveLedEffectMode(const YAML::Node& node) {
203+
const std::string value = node.as<std::string>();
204+
if (IsDecimalScalar(value)) {
205+
return node.as<uint32_t>();
206+
}
207+
208+
static const std::unordered_map<std::string, uint32_t> kModes = {
209+
{"static", 0}, {"blink", 1}, {"breath", 2},
210+
{"color_wipe", 3}, {"scan", 13}, {"running_lights", 18},
211+
{"twinkle_fade_random", 22}, {"sparkle", 23},
212+
{"strobe", 26}, {"chase_rainbow", 33}, {"running_color", 40},
213+
{"running_random", 42}, {"larson_scanner", 43},{"comet", 44},
214+
{"fireworks", 45}, {"fire_flicker", 48}, {"tricolor_chase", 54},
215+
{"twinklefox", 55}, {"rain", 56}, {"heartbeat", 64},
216+
{"multi_comet", 66}, {"popcorn", 68}, {"oscillator", 69},
217+
};
218+
219+
const auto it = kModes.find(value);
220+
if (it == kModes.end()) {
221+
throw YAML::Exception(node.Mark(), "unknown LED effect '" + value + "'");
222+
}
223+
return it->second;
224+
}
225+
226+
uint32_t ResolvePwmEffectMode(const YAML::Node& node) {
227+
const std::string value = node.as<std::string>();
228+
if (IsDecimalScalar(value)) {
229+
return node.as<uint32_t>();
230+
}
231+
232+
static const std::unordered_map<std::string, uint32_t> kModes = {
233+
{"sine", PWM_EFFECT_SINE},
234+
{"ramp_down_stop", PWM_EFFECT_RAMP_DOWN_STOP},
235+
{"impulse", PWM_EFFECT_IMPULSE},
236+
};
237+
238+
const auto it = kModes.find(value);
239+
if (it == kModes.end()) {
240+
throw YAML::Exception(node.Mark(), "unknown PWM effect '" + value + "'");
241+
}
242+
return it->second;
243+
}
244+
} // namespace
245+
246+
void SendNamedEffectTriggerConfig(RS485Comm* comm, const YAML::Node& effectNode,
247+
uint32_t type, uint8_t board,
248+
uint32_t port) {
249+
if (!comm || !effectNode || !effectNode["name"]) {
250+
return;
251+
}
252+
253+
const uint32_t value = effectNode["value"] ? effectNode["value"].as<uint32_t>()
254+
: 1u;
255+
const uint32_t number =
256+
HashNamedTriggerId(effectNode["name"].as<std::string>().c_str());
257+
258+
uint8_t index = 0;
259+
comm->SendConfigEvent(new ConfigEvent(board, (uint8_t)CONFIG_TOPIC_TRIGGER,
260+
index++, (uint8_t)CONFIG_TOPIC_PORT,
261+
port));
262+
comm->SendConfigEvent(new ConfigEvent(board, (uint8_t)CONFIG_TOPIC_TRIGGER,
263+
index++, (uint8_t)CONFIG_TOPIC_TYPE,
264+
type));
265+
comm->SendConfigEvent(new ConfigEvent(board, (uint8_t)CONFIG_TOPIC_TRIGGER,
266+
index++, (uint8_t)CONFIG_TOPIC_SOURCE,
267+
EVENT_SOURCE_EFFECT));
268+
comm->SendConfigEvent(new ConfigEvent(board, (uint8_t)CONFIG_TOPIC_TRIGGER,
269+
index++, (uint8_t)CONFIG_TOPIC_NUMBER,
270+
number));
271+
comm->SendConfigEvent(new ConfigEvent(board, (uint8_t)CONFIG_TOPIC_TRIGGER,
272+
index++, (uint8_t)CONFIG_TOPIC_VALUE,
273+
value));
209274
}
210275

211276
void PPUC::SendLedConfigBlock(const YAML::Node& items, uint32_t type,
@@ -534,14 +599,7 @@ bool PPUC::Connect() {
534599
n_pwmOutput["board"].as<uint8_t>(), (uint8_t)CONFIG_TOPIC_PWM,
535600
index++, (uint8_t)CONFIG_TOPIC_FAST_SWITCH, fastSwitch));
536601
std::string c_type = n_pwmOutput["type"].as<std::string>();
537-
uint32_t type = PWM_TYPE_SOLENOID; // "coil"
538-
if (strcmp(c_type.c_str(), "flasher") == 0) {
539-
type = PWM_TYPE_FLASHER;
540-
} else if (strcmp(c_type.c_str(), "lamp") == 0) {
541-
type = PWM_TYPE_LAMP;
542-
} else if (strcmp(c_type.c_str(), "motor") == 0) {
543-
type = PWM_TYPE_MOTOR;
544-
}
602+
uint32_t type = ResolvePwmType(c_type);
545603
m_pRS485Comm->SendConfigEvent(new ConfigEvent(
546604
n_pwmOutput["board"].as<uint8_t>(), (uint8_t)CONFIG_TOPIC_PWM,
547605
index++, (uint8_t)CONFIG_TOPIC_TYPE, type));
@@ -564,7 +622,7 @@ bool PPUC::Connect() {
564622
new ConfigEvent(n_pwmOutput["board"].as<uint8_t>(),
565623
(uint8_t)CONFIG_TOPIC_PWM_EFFECT, index++,
566624
(uint8_t)CONFIG_TOPIC_EFFECT,
567-
n_pwm_effect["effect"].as<uint32_t>()));
625+
ResolvePwmEffectMode(n_pwm_effect["effect"])));
568626
m_pRS485Comm->SendConfigEvent(
569627
new ConfigEvent(n_pwmOutput["board"].as<uint8_t>(),
570628
(uint8_t)CONFIG_TOPIC_PWM_EFFECT, index++,
@@ -598,10 +656,10 @@ bool PPUC::Connect() {
598656
? 255
599657
: n_pwm_effect["repeat"].as<uint32_t>()));
600658

601-
SendTriggerConfigBlock(n_pwm_effect["trigger"],
602-
CONFIG_TOPIC_PWM_EFFECT,
603-
n_pwmOutput["board"].as<uint8_t>(),
604-
n_pwmOutput["port"].as<uint32_t>());
659+
SendNamedEffectTriggerConfig(
660+
m_pRS485Comm, n_pwm_effect, CONFIG_TOPIC_PWM_EFFECT,
661+
n_pwmOutput["board"].as<uint8_t>(),
662+
n_pwmOutput["port"].as<uint32_t>());
605663
}
606664
}
607665

@@ -709,7 +767,7 @@ bool PPUC::Connect() {
709767
new ConfigEvent(n_ledStripe["board"].as<uint8_t>(),
710768
(uint8_t)CONFIG_TOPIC_LED_EFFECT, index++,
711769
(uint8_t)CONFIG_TOPIC_EFFECT,
712-
n_led_effect["effect"].as<uint32_t>()));
770+
ResolveLedEffectMode(n_led_effect["effect"])));
713771
m_pRS485Comm->SendConfigEvent(
714772
new ConfigEvent(n_ledStripe["board"].as<uint8_t>(),
715773
(uint8_t)CONFIG_TOPIC_LED_EFFECT, index++,
@@ -738,10 +796,10 @@ bool PPUC::Connect() {
738796
? 255
739797
: n_led_effect["repeat"].as<uint32_t>()));
740798

741-
SendTriggerConfigBlock(n_led_effect["trigger"],
742-
CONFIG_TOPIC_LED_EFFECT,
743-
n_ledStripe["board"].as<uint8_t>(),
744-
n_ledStripe["port"].as<uint32_t>());
799+
SendNamedEffectTriggerConfig(
800+
m_pRS485Comm, n_led_effect, CONFIG_TOPIC_LED_EFFECT,
801+
n_ledStripe["board"].as<uint8_t>(),
802+
n_ledStripe["port"].as<uint32_t>());
745803
}
746804
}
747805

@@ -912,6 +970,11 @@ void PPUC::SetSwitchState(int number, int state) {
912970
state == 0 ? 0 : 1);
913971
}
914972

973+
void PPUC::TriggerEvent(uint8_t source, int number, int value) {
974+
m_pRS485Comm->QueueEvent(new Event(source, static_cast<uint16_t>(number),
975+
static_cast<uint8_t>(value)));
976+
}
977+
915978
bool PPUC::IsSwitchVirtualized(int number) {
916979
return m_pRS485Comm->IsSwitchVirtualized(static_cast<uint16_t>(number));
917980
}

src/PPUC.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ class PPUCAPI PPUC {
5555
void SetLampState(int number, int state);
5656
void SetGIState(int string, int brightness);
5757
void SetSwitchState(int number, int state);
58+
void TriggerEvent(uint8_t source, int number, int value);
5859
bool IsSwitchVirtualized(int number);
5960
bool IsBoardVirtualized(uint8_t board);
6061
PPUCSwitchState* GetNextSwitchState();
@@ -87,8 +88,6 @@ class PPUCAPI PPUC {
8788
bool m_forceHardReset = false;
8889
std::set<uint8_t> m_skippedBoards;
8990

90-
void SendTriggerConfigBlock(const YAML::Node& items, uint32_t type,
91-
uint8_t board, uint32_t port);
9291
void SendLedConfigBlock(const YAML::Node& items, uint32_t type, uint8_t board,
9392
uint32_t port);
9493
bool AbortConfigurationEarly() const;

src/RS485Comm.cpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1551,8 +1551,27 @@ bool RS485Comm::ReceiveSwitchStateFrame(uint8_t expectedBoard,
15511551
}
15521552

15531553
bool RS485Comm::SendEvent(Event* event) {
1554-
(void)event;
1555-
return false;
1554+
if (!event || m_pSerialPort == NULL) {
1555+
return false;
1556+
}
1557+
1558+
uint8_t frame[ppuc::v2::kTriggerFrameBytes];
1559+
frame[0] = ppuc::v2::kSyncByte;
1560+
frame[1] = ppuc::v2::ComposeTypeAndFlags(ppuc::v2::kFrameTrigger,
1561+
ppuc::v2::kFlagNone);
1562+
frame[2] = ppuc::v2::kNoBoard;
1563+
frame[3] = m_sequence++;
1564+
frame[4] = m_epoch;
1565+
frame[5] = event->sourceId;
1566+
frame[6] = static_cast<uint8_t>((event->eventId >> 8) & 0xFFu);
1567+
frame[7] = static_cast<uint8_t>(event->eventId & 0xFFu);
1568+
frame[8] = event->value;
1569+
const uint16_t crc =
1570+
ppuc::v2::Crc16Ccitt(frame, ppuc::v2::kHeaderBytes +
1571+
ppuc::v2::kTriggerPayloadBytes);
1572+
frame[9] = static_cast<uint8_t>((crc >> 8) & 0xFFu);
1573+
frame[10] = static_cast<uint8_t>(crc & 0xFFu);
1574+
return WriteBytes("trigger frame", frame, sizeof(frame));
15561575
}
15571576

15581577
Event* RS485Comm::receiveEvent() {

0 commit comments

Comments
 (0)