Skip to content

Commit 8d43ce0

Browse files
committed
refactor: Events, API modules (formerly MessageListerner)
1 parent 9af08f8 commit 8d43ce0

15 files changed

+412
-199
lines changed

ApiModule.cpp

+131
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
#include <ApiModule.h>
2+
#include <ArduinoJson.h>
3+
#include <LogStore.h>
4+
#include <System.h>
5+
6+
ApiModule::ApiModule(std::string msgType) {
7+
LogStore::info("[ApiModule::constructor] handle for " + msgType +
8+
" message type");
9+
ApiModule::registeredApiModules.insert({msgType, this});
10+
}
11+
12+
// STATIC DEF
13+
std::map<std::string, ApiModule *> ApiModule::registeredApiModules;
14+
15+
std::string ApiModule::dispatchApiRequest(std::string apiRequestMsg) {
16+
JsonDocument apiRequest;
17+
// convert to a json object
18+
DeserializationError error = deserializeJson(apiRequest, apiRequestMsg);
19+
std::string interfaceType = "<type>";
20+
LogStore::dbg("[MessageInterface::onMessage] received message on " +
21+
interfaceType + " interface: " + apiRequestMsg);
22+
// root msg level props
23+
// std::string msgContext = msgRoot["context"];
24+
// API module, submodule, command
25+
JsonObject apiData = apiRequest["api"];
26+
std::string apiModuleName = apiData["module"];
27+
std::string apiSubmodule = apiData["submod"];
28+
std::string apiCmd = apiData["cmd"];
29+
std::string apiInput = apiData["input"]; // API module/submodule input data
30+
31+
// TODO
32+
// std::string msgSrc = type; // interface type (LORA,WS)
33+
// std::string msgCtx = msgRoot["ctx"];
34+
// std::string clientKey = msgCtx["clientKey"];
35+
// int msgTime = msgRoot["time"];
36+
// if (msgTime) {
37+
// System::time.sync(msgTime);
38+
// }
39+
// std::string msgMode = msgRoot["mode"]; // sync or async
40+
// bool bypassEvtQueue(msgMode == "sync");
41+
// EventQueue::pushEvent(event, bypassEvtQueue);
42+
// find corresponding sub service
43+
auto apiModule = ApiModule::registeredApiModules.find(apiModuleName);
44+
if (apiModule != ApiModule::registeredApiModules.end()) {
45+
LogStore::info(
46+
"[ApiModule::dispatchApiRequest] API request dispatched to module " +
47+
apiModuleName);
48+
// std::string dataOut = apiModule->second->onApiCall(incomingMsg,
49+
// clientKey);
50+
// // default empty reply
51+
// std::string outgoingMsg("");
52+
// bool expectedReply = timestamp > 0 || dataOut.length() > 0;
53+
// // fill with data from service if any
54+
// if (expectedReply) {
55+
// JsonDocument replyData;
56+
// deserializeJson(replyData, dataOut);
57+
// // Build the JSON reply including service output
58+
// // and additional fields (msgType, timestamp)
59+
// JsonDocument replyRoot;
60+
// replyRoot["handle"] = handle;
61+
// if (timestamp > 0) {
62+
// LogStore::info("[ApiModule::extractMsg] timestamp
63+
// provided " +
64+
// std::to_string(timestamp) +
65+
// " => expected reply confirmation");
66+
// replyRoot["timestamp"] = timestamp;
67+
// }
68+
// if (dataOut.length() > 0) {
69+
// replyRoot["data"] = replyData;
70+
// }
71+
// serializeJsonPretty(replyRoot, outgoingMsg);
72+
// }
73+
// return outgoingMsg;
74+
} else {
75+
LogStore::info("[ApiModule::dispatchMsg] unregistered API module: " +
76+
apiModuleName);
77+
}
78+
return "";
79+
}
80+
81+
// std::string ApiModule::dispatchMsg(std::string incomingMsg,
82+
// std::string clientKey) {
83+
// JsonDocument msgRoot;
84+
// // convert to a json object
85+
// DeserializationError error = deserializeJson(msgRoot, incomingMsg);
86+
87+
// // root level props common to all services
88+
// std::string handle = msgRoot["handle"];
89+
// int timestamp = msgRoot["timestamp"];
90+
// if (timestamp) {
91+
// System::time.sync(timestamp);
92+
// }
93+
// // find corresponding sub service
94+
// auto msgHandler = ApiModule::registeredApiModules.find(handle);
95+
// if (msgHandler != ApiModule::registeredApiModules.end()) {
96+
// // LogStore::info("[ApiModule::dispatchMsg] dispatch to message
97+
// // handler " +msgType); forward message to subservice handler
98+
// std::string dataOut = msgHandler->second->onCall(incomingMsg,
99+
// clientKey);
100+
// // default empty reply
101+
// std::string outgoingMsg("");
102+
// bool expectedReply = timestamp > 0 || dataOut.length() > 0;
103+
// // fill with data from service if any
104+
// if (expectedReply) {
105+
// JsonDocument replyData;
106+
// deserializeJson(replyData, dataOut);
107+
// // Build the JSON reply including service output
108+
// // and additional fields (msgType, timestamp)
109+
// JsonDocument replyRoot;
110+
// replyRoot["handle"] = handle;
111+
// if (timestamp > 0) {
112+
// LogStore::info("[ApiModule::extractMsg] timestamp provided
113+
// " +
114+
// std::to_string(timestamp) +
115+
// " => expected reply confirmation");
116+
// replyRoot["timestamp"] = timestamp;
117+
// }
118+
// if (dataOut.length() > 0) {
119+
// replyRoot["data"] = replyData;
120+
// }
121+
// serializeJsonPretty(replyRoot, outgoingMsg);
122+
// }
123+
// return outgoingMsg;
124+
// } else {
125+
// LogStore::info("[ApiModule::dispatchMsg] no registered message
126+
// "
127+
// "handler matching " +
128+
// handle);
129+
// }
130+
// return "";
131+
// }

ApiModule.h

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#pragma once
2+
#include <map>
3+
4+
/*
5+
* Each inheriting class will be registered as service and must be singleton
6+
* When api call or request is received, it will be dispatched to matching
7+
declared service instance
8+
* Services API is exposed through remote interfaces (WS, LORA)
9+
* Services handle and respond to API requests received from clients
10+
* In case of response, services are not aware of
11+
* - interface types: WS/LORA
12+
* - client id
13+
* which are handled at interface level
14+
* When notifying client of an event, service must target subscriber
15+
which is identified depends on interface type
16+
* - LORA: deviceId
17+
* - WS: opened socket (clientKey)
18+
*
19+
*/
20+
// class ApiRequestHandler {
21+
class ApiModule {
22+
static std::map<std::string, ApiModule *> registeredApiModules;
23+
// static std::map<std::string, ApiModule *> serviceHandlers;
24+
// static std::map<int, ApiModule *> clientServiceInstances;
25+
26+
protected:
27+
ApiModule(std::string serviceId);
28+
29+
public:
30+
static std::string dispatchApiRequest(std::string apiRequestMsg);
31+
// TO BE IMPLEMENTED IN CHILD CLASS
32+
33+
virtual std::string onApiCall(std::string rawMsg) = 0;
34+
};

Events.cpp

+36-10
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,39 @@
33
#include <LoraInterface.h>
44
#include <WsInterface.h>
55

6+
// std::map<std::string, EventType> EventTypeMap{{"LOG", LOG},
7+
// {"PIN", PIN},
8+
// {"MSGNOT", MSGNOT},
9+
// {"MSGFWD", MSGFWD},
10+
// {"MSGREP", MSGREP}};
11+
std::map<EventType, std::string> EventTypeMap{{LOG, "LOG"}, {PIN, "PIN"}};
12+
613
/*
714
* EventTrigger
815
*/
916

10-
std::vector<EventTrigger *> EventTrigger::publishers;
17+
std::queue<Event> EventQueue::events;
1118

12-
void EventTrigger::pushEvent(std::string eventData, EventType eventType) {
13-
// LogStore::info("[EventTrigger::pushEvent] eventType: " +
14-
// std::to_string(eventType) + ", eventData: " +
15-
// eventData,
16-
// true);
17-
EventHandler::dispatchEvt(eventData, eventType);
19+
void EventQueue::pushEvent(Event evt, bool bypassEvtQueue) {
20+
// will directly call evtDispatch synchronously
21+
if (bypassEvtQueue) {
22+
EventHandler::dispatchEvt(evt);
23+
} else {
24+
EventQueue::events.push(evt);
25+
}
26+
};
27+
28+
void EventQueue::watchEvents() {
29+
if (EventQueue::events.size() > 0) {
30+
Event evt = EventQueue::events.front();
31+
bool dispatched = EventHandler::dispatchEvt(evt);
32+
if (!dispatched) {
33+
LogStore::dbg("[EventQueue::watchEvents] undispatched event " + evt.type +
34+
" => put in "
35+
"waiting queue ");
36+
}
37+
EventQueue::events.pop();
38+
}
1839
};
1940

2041
/*
@@ -23,17 +44,22 @@ void EventTrigger::pushEvent(std::string eventData, EventType eventType) {
2344

2445
std::vector<EventHandler *> EventHandler::subscribers;
2546

26-
EventHandler::EventHandler(EventType evtType) : evtType(evtType) {
47+
EventHandler::EventHandler(std::string evtType) : evtType(evtType) {
2748
subscribers.push_back(this);
2849
// LogStore::dbg("[EventHandler::constructor] instances count " +
2950
// std::to_string(subscribers.size()));
3051
};
3152

32-
void EventHandler::dispatchEvt(std::string eventData, EventType eventType) {
53+
bool EventHandler::dispatchEvt(Event evt) {
3354
// LogStore::dbg("[EventHandler::dispatchEvt] " + eventData, true);
55+
bool dispatched = false;
3456
for (EventHandler *instance : EventHandler::subscribers) {
35-
instance->onEvent(eventData);
57+
if (evt.type == instance->evtType) {
58+
dispatched = true;
59+
instance->onEvent(evt.data);
60+
}
3661
}
62+
return dispatched;
3763
};
3864

3965
void EventHandler::onEvent(std::string eventData) {

Events.h

+86-25
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,102 @@
11
#pragma once
22
#include <MessageInterface.h>
3+
#include <map>
4+
#include <queue>
35
#include <string>
46
#include <vector>
57

68
/*
7-
#1
8-
-- msg --> [MSGINTERFACE] -- MsgInEvt --> [IncomingMessageHandler]
9-
-- evt --> [EVTFORWARDER] -- MsgOutEvt --> [MSGINTERFACE]
10-
#2
11-
-- msg --> [MSGINTERFACE] --> [MSGLISTENER] -->
12-
13-
149
EVT/MSG
1510
1611
[LocalEventQueue] -- evt --> [EventHandler<forwarder>] -->
1712
[RemoteMessageInterface] --> msg [RemoteMessageInterface] --> msg -->
1813
[MessageListener<NotMsg>] --> NotEvt
19-
*/
2014
21-
enum EventType { LogEvt, PinEvt, NotEvt, IncomingMsgEvt, OutgoingMsgEvt };
15+
Events are queud in EventQueue by EventQueue and dequeud when sent over
16+
specific interface to specific client When new event is pushed in the queue, the
17+
QueueProcessor is informed, so it can schedule event notification The
18+
QueueProcessor is able to know which remote client has subscribed to which event
19+
and relay information. Once all client subscribers have been notified event can
20+
be removed from main Queue, and moved to SentQueue or RetryQueue if failed to be
21+
transmitted which will reschedule event notification at later moment.
22+
EventNotification can require receipt confirmation.
23+
24+
std::string examples:
25+
- logEvt
26+
- pinEvt
27+
28+
EventQueue
29+
LogEventQueue
30+
PinEventQueue
31+
32+
Any class
33+
willing to receive events will have to inherit EventHandler class. Depending on
34+
EventType, it will be LogEventListener, PinEventListener For remote listeners:
35+
LogEventRemoteListener, PinEventRemoteListener Any remote client subscribing for
36+
an event type will instantiate corresponding class
37+
- LogEventRemoteListener:LogEventListener:EventHandler
38+
- PinEventRemoteListener:PinEventListener:EventHandler
39+
Usecase examples:
40+
2 local process FsLog and SerialLog subscribes to LogEvt => 2 LogEventListener
41+
1 remote client subscribes to LogEvt => 1 LogEventRemoteListener
42+
1 local process PirAlarm subscribe to PinEvt => 1 PinEventListener
43+
1 remote process subscribe to PinEvt => 1 PinEventRemoteListener
44+
SubTotal:
45+
- 3 LogEventListener (2 LogEventListener + 1 LogEventRemoteListener)
46+
- 2 PinEventListener (1 PinEventListener + 1 PinEventRemoteListener)
47+
Total: 5 EventListeners (3 LogEventListener + 2 PinEventListener)
48+
49+
SUMMARY:
50+
EventType:
51+
- LogEventType:EventType,
52+
- PinEventType:EventType
53+
EventQueue:
54+
- LogEventQueue: EventQueue,
55+
- PinEventQueue: EventQueue
56+
EventQueue:
57+
- LogEventPublisher:EventQueue,
58+
- PinEventPublisher:EventQueue
59+
EventHandler:
60+
- LogEventListener:EventHandler
61+
- PinEventListener:EventHandler
62+
63+
SIMPLIFICATION
64+
EventType: LogEventType, PinEventType
65+
EventQueue storing EventType
66+
EventQueue<EventType>: publish events (LogEventType or PinEventType) to
67+
EventQueue EventHandler<EventType>: subscribes to events notfications
68+
RemoteEventListener<EventType>: additional logic to notify clients over an
69+
interface MessageNotificationHandler: handling remote client subscription,
70+
instancing RemoteEventListener
2271
23-
/**
24-
To be inherited from any class pushing events
2572
*/
26-
class EventTrigger {
27-
static std::vector<EventTrigger *> publishers;
73+
74+
// local: triggered from within system
75+
// remote/external/injected: received from external source (through messages)
76+
// and injected in queue
77+
enum EventOrigin { LOCAL, REMOTE };
78+
79+
typedef struct {
80+
EventOrigin origin = EventOrigin::LOCAL;
81+
int timestamp = 0;
82+
int priority = 0;
83+
} EventContext;
84+
85+
typedef struct {
86+
std::string type;
87+
EventContext context;
88+
std::string data;
89+
} Event;
90+
91+
enum EventType { LOG, PIN, TST, MSGNOT, MSGFWD, MSGREP };
92+
extern std::map<EventType, std::string> EventTypeMap;
93+
94+
class EventQueue {
95+
static std::queue<Event> events;
2896

2997
public:
30-
static void pushEvent(std::string eventData, EventType eventType);
98+
static void pushEvent(Event evt, bool bypassEvtQueue = false);
99+
static void watchEvents();
31100
};
32101

33102
/*
@@ -37,24 +106,16 @@ class EventTrigger {
37106
class EventHandler {
38107
public:
39108
static std::vector<EventHandler *> subscribers;
40-
EventType evtType;
109+
std::string evtType;
41110

42111
protected:
43-
EventHandler(EventType evt);
112+
EventHandler(std::string evt);
44113

45114
public:
46-
static void dispatchEvt(std::string eventData, EventType eventType);
115+
static bool dispatchEvt(Event evt);
47116

48117
// static void notify(int pinId, int pinVal);
49118
virtual void onEvent(std::string eventData);
50119
};
51120

52-
/*
53-
bridge between publishers and listeners
54-
*/
55-
class EventQueue {
56-
void enqueue();
57-
void dequeue();
58-
};
59-
60121
class RemoteEventListener : public EventHandler {};

0 commit comments

Comments
 (0)