1 #pragma once
2
3 #include "DelayedLink.h"
4 #include "Plugins.h"
5
6 #ifndef byte
7 typedef unsigned char byte;
8 #endif
9
10 namespace Plugins {
11
12 extern std::mutex PythonMutex; // controls access to Python
13
14 class CPluginMessageBase
15 {
16 public:
~CPluginMessageBase(void)17 virtual ~CPluginMessageBase(void) {};
18
19 CPlugin* m_pPlugin;
20 std::string m_Name;
21 int m_HwdID;
22 int m_Unit;
23 bool m_Delay;
24 time_t m_When;
25
26 protected:
CPluginMessageBase(CPlugin * pPlugin)27 CPluginMessageBase(CPlugin* pPlugin) : m_pPlugin(pPlugin), m_HwdID(pPlugin->m_HwdID), m_Unit(-1), m_Delay(false)
28 {
29 m_Name = __func__;
30 m_When = time(0);
31 };
32 virtual void ProcessLocked() = 0;
33 public:
Name()34 virtual const char* Name() { return m_Name.c_str(); };
Plugin()35 virtual const CPlugin* Plugin() { return m_pPlugin; };
Process()36 virtual void Process()
37 {
38 std::lock_guard<std::mutex> l(PythonMutex);
39 m_pPlugin->RestoreThread();
40 ProcessLocked();
41 m_pPlugin->ReleaseThread();
42 };
43 };
44
45 // Handles lifecycle management of the Python Connection object
46 class CHasConnection
47 {
48 public:
CHasConnection(PyObject * Connection)49 CHasConnection(PyObject* Connection) : m_pConnection(Connection)
50 {
51 Py_XINCREF(m_pConnection);
52 };
~CHasConnection()53 ~CHasConnection()
54 {
55 Py_XDECREF(m_pConnection);
56 }
57 PyObject* m_pConnection;
58 };
59
60 class InitializeMessage : public CPluginMessageBase
61 {
62 public:
InitializeMessage(CPlugin * pPlugin)63 InitializeMessage(CPlugin* pPlugin) : CPluginMessageBase(pPlugin) { m_Name = __func__; };
Process()64 virtual void Process()
65 {
66 std::lock_guard<std::mutex> l(PythonMutex);
67 m_pPlugin->Initialise();
68 };
ProcessLocked()69 virtual void ProcessLocked() {};
70 };
71
72 // Base callback message class
73 class CCallbackBase : public CPluginMessageBase
74 {
75 protected:
76 std::string m_Callback;
77 virtual void ProcessLocked() = 0;
78 public:
CCallbackBase(CPlugin * pPlugin,const std::string & Callback)79 CCallbackBase(CPlugin* pPlugin, const std::string &Callback) : CPluginMessageBase(pPlugin), m_Callback(Callback) {};
Callback(PyObject * pParams)80 virtual void Callback(PyObject* pParams) { if (m_Callback.length()) m_pPlugin->Callback(m_Callback, pParams); };
PythonName()81 virtual const char* PythonName() { return m_Callback.c_str(); };
82 };
83
84 class onStartCallback : public CCallbackBase
85 {
86 public:
onStartCallback(CPlugin * pPlugin)87 onStartCallback(CPlugin* pPlugin) : CCallbackBase(pPlugin, "onStart") { m_Name = __func__; };
88 protected:
ProcessLocked()89 virtual void ProcessLocked()
90 {
91 m_pPlugin->Start();
92 Callback(NULL);
93 };
94 };
95
96 class onHeartbeatCallback : public CCallbackBase
97 {
98 public:
onHeartbeatCallback(CPlugin * pPlugin)99 onHeartbeatCallback(CPlugin* pPlugin) : CCallbackBase(pPlugin, "onHeartbeat") { m_Name = __func__; };
100 protected:
ProcessLocked()101 virtual void ProcessLocked()
102 {
103 Callback(NULL);
104 };
105 };
106
107 #ifdef _WIN32
string_to_wstring(const std::string & str,int codepage)108 static std::wstring string_to_wstring(const std::string &str, int codepage)
109 {
110 if (str.empty()) return std::wstring();
111 int sz = MultiByteToWideChar(codepage, 0, &str[0], (int)str.size(), 0, 0);
112 std::wstring res(sz, 0);
113 MultiByteToWideChar(codepage, 0, &str[0], (int)str.size(), &res[0], sz);
114 return res;
115 }
116
wstring_to_string(const std::wstring & wstr,int codepage)117 static std::string wstring_to_string(const std::wstring &wstr, int codepage)
118 {
119 if (wstr.empty()) return std::string();
120 int sz = WideCharToMultiByte(codepage, 0, &wstr[0], (int)wstr.size(), 0, 0, 0, 0);
121 std::string res(sz, 0);
122 WideCharToMultiByte(codepage, 0, &wstr[0], (int)wstr.size(), &res[0], sz, 0, 0);
123 return res;
124 }
125
get_utf8_from_ansi(const std::string & utf8,int codepage)126 static std::string get_utf8_from_ansi(const std::string &utf8, int codepage)
127 {
128 std::wstring utf16 = string_to_wstring(utf8, codepage);
129 std::string ansi = wstring_to_string(utf16, CP_UTF8);
130 return ansi;
131 }
132 #endif
133
134 class onConnectCallback : public CCallbackBase, public CHasConnection
135 {
136 public:
onConnectCallback(CPlugin * pPlugin,PyObject * Connection)137 onConnectCallback(CPlugin* pPlugin, PyObject* Connection) : CCallbackBase(pPlugin, "onConnect"), CHasConnection(Connection) { m_Name = __func__; };
onConnectCallback(CPlugin * pPlugin,PyObject * Connection,const int Code,const std::string & Text)138 onConnectCallback(CPlugin* pPlugin, PyObject* Connection, const int Code, const std::string &Text) : CCallbackBase(pPlugin, "onConnect"), CHasConnection(Connection), m_Status(Code), m_Text(Text) { m_Name = __func__; };
139 int m_Status;
140 std::string m_Text;
141 protected:
ProcessLocked()142 virtual void ProcessLocked()
143 {
144 #ifdef _WIN32
145 std::string textUTF8 = get_utf8_from_ansi(m_Text, GetACP());
146 #else
147 std::string textUTF8 = m_Text; // TODO: Is it safe to assume non-Windows will always be UTF-8?
148 #endif
149 Callback(Py_BuildValue("Ois", m_pConnection, m_Status, textUTF8.c_str())); // 0 is success else socket failure code
150 };
151 };
152
153 class onDisconnectCallback : public CCallbackBase, public CHasConnection
154 {
155 public:
onDisconnectCallback(CPlugin * pPlugin,PyObject * Connection)156 onDisconnectCallback(CPlugin* pPlugin, PyObject* Connection) : CCallbackBase(pPlugin, "onDisconnect"), CHasConnection(Connection) { m_Name = __func__; };
157 protected:
ProcessLocked()158 virtual void ProcessLocked()
159 {
160 Callback(Py_BuildValue("(O)", m_pConnection)); // 0 is success else socket failure code
161 };
162 };
163
164 class onDeviceAddedCallback : public CCallbackBase
165 {
166 public:
onDeviceAddedCallback(CPlugin * pPlugin,int Unit)167 onDeviceAddedCallback(CPlugin* pPlugin, int Unit) : CCallbackBase(pPlugin, "onDeviceAdded") { m_Unit = Unit; };
168 protected:
ProcessLocked()169 virtual void ProcessLocked()
170 {
171 m_pPlugin->onDeviceAdded(m_Unit);
172
173 PyObject* pParams = Py_BuildValue("(i)", m_Unit);
174 Callback(pParams);
175 };
176 };
177
178 class onDeviceModifiedCallback : public CCallbackBase
179 {
180 public:
onDeviceModifiedCallback(CPlugin * pPlugin,int Unit)181 onDeviceModifiedCallback(CPlugin* pPlugin, int Unit) : CCallbackBase(pPlugin, "onDeviceModified") { m_Unit = Unit; };
182 protected:
ProcessLocked()183 virtual void ProcessLocked()
184 {
185 m_pPlugin->onDeviceModified(m_Unit);
186
187 PyObject* pParams = Py_BuildValue("(i)", m_Unit);
188 Callback(pParams);
189 };
190 };
191
192 class onDeviceRemovedCallback : public CCallbackBase
193 {
194 public:
onDeviceRemovedCallback(CPlugin * pPlugin,int Unit)195 onDeviceRemovedCallback(CPlugin* pPlugin, int Unit) : CCallbackBase(pPlugin, "onDeviceRemoved") { m_Unit = Unit; };
196 protected:
ProcessLocked()197 virtual void ProcessLocked()
198 {
199 PyObject* pParams = Py_BuildValue("(i)", m_Unit);
200 Callback(pParams);
201
202 m_pPlugin->onDeviceRemoved(m_Unit);
203 };
204 };
205
206 class onCommandCallback : public CCallbackBase
207 {
208 public:
onCommandCallback(CPlugin * pPlugin,int Unit,const std::string & Command,const int level,const std::string & color)209 onCommandCallback(CPlugin* pPlugin, int Unit, const std::string& Command, const int level, const std::string &color) : CCallbackBase(pPlugin, "onCommand")
210 {
211 m_Name = __func__;
212 m_Unit = Unit;
213 m_fLevel = -273.15f;
214 m_Command = Command;
215 m_iLevel = level;
216 m_iColor = color;
217 };
onCommandCallback(CPlugin * pPlugin,int Unit,const std::string & Command,const float level)218 onCommandCallback(CPlugin* pPlugin, int Unit, const std::string& Command, const float level) : CCallbackBase(pPlugin, "onCommand")
219 {
220 m_Name = __func__;
221 m_Unit = Unit;
222 m_fLevel = level;
223 m_Command = Command;
224 m_iLevel = -1;
225 m_iColor = "";
226 };
227 std::string m_Command;
228 std::string m_iColor;
229 int m_iLevel;
230 float m_fLevel;
231
232 protected:
ProcessLocked()233 virtual void ProcessLocked()
234 {
235 PyObject* pParams;
236 if (m_fLevel != -273.15f)
237 {
238 pParams = Py_BuildValue("isfs", m_Unit, m_Command.c_str(), m_fLevel, "");
239 }
240 else
241 {
242 pParams = Py_BuildValue("isis", m_Unit, m_Command.c_str(), m_iLevel, m_iColor.c_str());
243 }
244 Callback(pParams);
245 };
246 };
247
248 class onSecurityEventCallback : public CCallbackBase
249 {
250 public:
onSecurityEventCallback(CPlugin * pPlugin,int Unit,const int level,const std::string & Description)251 onSecurityEventCallback(CPlugin* pPlugin, int Unit, const int level, const std::string& Description) : CCallbackBase(pPlugin, "onSecurityEvent")
252 {
253 m_Name = __func__;
254 m_Unit = Unit;
255 m_iLevel = level;
256 m_Description = Description;
257 };
258 int m_iLevel;
259 std::string m_Description;
260
261 protected:
ProcessLocked()262 virtual void ProcessLocked()
263 {
264 PyObject* pParams = Py_BuildValue("iis", m_Unit, m_iLevel, m_Description.c_str());
265 Callback(pParams);
266 };
267 };
268
269 class onMessageCallback : public CCallbackBase, public CHasConnection
270 {
271 public:
onMessageCallback(CPlugin * pPlugin,PyObject * Connection,const std::string & Buffer)272 onMessageCallback(CPlugin* pPlugin, PyObject* Connection, const std::string& Buffer) : CCallbackBase(pPlugin, "onMessage"), CHasConnection(Connection), m_Data(NULL)
273 {
274 m_Name = __func__;
275 m_Buffer.reserve(Buffer.length());
276 m_Buffer.assign((const byte*)Buffer.c_str(), (const byte*)Buffer.c_str()+Buffer.length());
277 };
onMessageCallback(CPlugin * pPlugin,PyObject * Connection,const std::vector<byte> & Buffer)278 onMessageCallback(CPlugin* pPlugin, PyObject* Connection, const std::vector<byte>& Buffer) : CCallbackBase(pPlugin, "onMessage"), CHasConnection(Connection), m_Data(NULL)
279 {
280 m_Name = __func__;
281 m_Buffer = Buffer;
282 };
onMessageCallback(CPlugin * pPlugin,PyObject * Connection,PyObject * pData)283 onMessageCallback(CPlugin* pPlugin, PyObject* Connection, PyObject* pData) : CCallbackBase(pPlugin, "onMessage"), CHasConnection(Connection)
284 {
285 m_Name = __func__;
286 m_Data = pData;
287 };
288 std::vector<byte> m_Buffer;
289 PyObject* m_Data;
290
291 protected:
ProcessLocked()292 virtual void ProcessLocked()
293 {
294 PyObject* pParams = NULL;
295
296 // Data is stored in a single vector of bytes
297 if (m_Buffer.size())
298 {
299 pParams = Py_BuildValue("Oy#", m_pConnection, &m_Buffer[0], m_Buffer.size());
300 Callback(pParams);
301 }
302
303 // Data is in a dictionary
304 if (m_Data)
305 {
306 pParams = Py_BuildValue("OO", m_pConnection, m_Data);
307 Callback(pParams);
308 Py_XDECREF(m_Data);
309 }
310 }
311 };
312
313 class onNotificationCallback : public CCallbackBase
314 {
315 public:
onNotificationCallback(CPlugin * pPlugin,const std::string & Subject,const std::string & Text,const std::string & Name,const std::string & Status,int Priority,const std::string & Sound,const std::string & ImageFile)316 onNotificationCallback(CPlugin* pPlugin,
317 const std::string& Subject,
318 const std::string& Text,
319 const std::string& Name,
320 const std::string& Status,
321 int Priority,
322 const std::string& Sound,
323 const std::string& ImageFile) : CCallbackBase(pPlugin, "onNotification")
324 {
325 m_Name = __func__;
326 m_Subject = Subject;
327 m_Text = Text;
328 m_SuppliedName = Name;
329 m_Status = Status;
330 m_Priority = Priority;
331 m_Sound = Sound;
332 m_ImageFile = ImageFile;
333 };
334
335 std::string m_Subject;
336 std::string m_Text;
337 std::string m_SuppliedName;
338 std::string m_Status;
339 int m_Priority;
340 std::string m_Sound;
341 std::string m_ImageFile;
342
343 protected:
ProcessLocked()344 virtual void ProcessLocked()
345 {
346 PyObject* pParams = Py_BuildValue("ssssiss", m_SuppliedName.c_str(), m_Subject.c_str(), m_Text.c_str(), m_Status.c_str(), m_Priority, m_Sound.c_str(), m_ImageFile.c_str());
347 Callback(pParams);
348 };
349 };
350
351 class onStopCallback : public CCallbackBase
352 {
353 public:
onStopCallback(CPlugin * pPlugin)354 onStopCallback(CPlugin* pPlugin) : CCallbackBase(pPlugin, "onStop") { m_Name = __func__; };
355 protected:
ProcessLocked()356 virtual void ProcessLocked()
357 {
358 Callback(NULL);
359 m_pPlugin->Stop();
360 };
361 };
362
363 // Base directive message class
364 class CDirectiveBase : public CPluginMessageBase
365 {
366 protected:
367 virtual void ProcessLocked() = 0;
368 public:
CDirectiveBase(CPlugin * pPlugin)369 CDirectiveBase(CPlugin* pPlugin) : CPluginMessageBase(pPlugin) {};
Process()370 virtual void Process() {
371 std::lock_guard<std::mutex> l(PythonMutex);
372 m_pPlugin->RestoreThread();
373 ProcessLocked();
374 m_pPlugin->ReleaseThread();
375 };
376 };
377
378 class ProtocolDirective : public CDirectiveBase, public CHasConnection
379 {
380 public:
ProtocolDirective(CPlugin * pPlugin,PyObject * Connection)381 ProtocolDirective(CPlugin* pPlugin, PyObject* Connection) : CDirectiveBase(pPlugin), CHasConnection(Connection) { m_Name = __func__; };
ProcessLocked()382 virtual void ProcessLocked() { m_pPlugin->ConnectionProtocol(this); };
383 };
384
385 class ConnectDirective : public CDirectiveBase, public CHasConnection
386 {
387 public:
ConnectDirective(CPlugin * pPlugin,PyObject * Connection)388 ConnectDirective(CPlugin* pPlugin, PyObject* Connection) : CDirectiveBase(pPlugin), CHasConnection(Connection) { m_Name = __func__; };
ProcessLocked()389 virtual void ProcessLocked() { m_pPlugin->ConnectionConnect(this); };
390 };
391
392 class ListenDirective : public CDirectiveBase, public CHasConnection
393 {
394 public:
ListenDirective(CPlugin * pPlugin,PyObject * Connection)395 ListenDirective(CPlugin* pPlugin, PyObject* Connection) : CDirectiveBase(pPlugin), CHasConnection(Connection) { m_Name = __func__; };
ProcessLocked()396 virtual void ProcessLocked() { m_pPlugin->ConnectionListen(this); };
397 };
398
399 class DisconnectDirective : public CDirectiveBase, public CHasConnection
400 {
401 public:
DisconnectDirective(CPlugin * pPlugin,PyObject * Connection)402 DisconnectDirective(CPlugin* pPlugin, PyObject* Connection) : CDirectiveBase(pPlugin), CHasConnection(Connection) { m_Name = __func__; };
ProcessLocked()403 virtual void ProcessLocked() { m_pPlugin->ConnectionDisconnect(this); };
404 };
405
406 class WriteDirective : public CDirectiveBase, public CHasConnection
407 {
408 public:
409 PyObject* m_Object;
WriteDirective(CPlugin * pPlugin,PyObject * Connection,PyObject * pData,const int Delay)410 WriteDirective(CPlugin* pPlugin, PyObject* Connection, PyObject* pData, const int Delay) : CDirectiveBase(pPlugin), CHasConnection(Connection)
411 {
412 m_Name = __func__;
413 m_Object = pData;
414 if (m_Object)
415 Py_INCREF(m_Object);
416 if (Delay)
417 {
418 m_When += Delay;
419 m_Delay=true;
420 }
421 };
~WriteDirective()422 ~WriteDirective()
423 {
424 if (m_Object)
425 Py_DECREF(m_Object);
426 }
427
ProcessLocked()428 virtual void ProcessLocked() { m_pPlugin->ConnectionWrite(this); };
429 };
430
431 class SettingsDirective : public CDirectiveBase
432 {
433 public:
SettingsDirective(CPlugin * pPlugin)434 SettingsDirective(CPlugin* pPlugin) : CDirectiveBase(pPlugin) { m_Name = __func__; };
ProcessLocked()435 virtual void ProcessLocked() { m_pPlugin->LoadSettings(); };
436 };
437
438 class PollIntervalDirective : public CDirectiveBase
439 {
440 public:
PollIntervalDirective(CPlugin * pPlugin,const int PollInterval)441 PollIntervalDirective(CPlugin* pPlugin, const int PollInterval) : CDirectiveBase(pPlugin), m_Interval(PollInterval) { m_Name = __func__; };
442 int m_Interval;
ProcessLocked()443 virtual void ProcessLocked() {m_pPlugin->PollInterval(m_Interval); };
444 };
445
446 class NotifierDirective : public CDirectiveBase
447 {
448 public:
NotifierDirective(CPlugin * pPlugin,const char * Name)449 NotifierDirective(CPlugin* pPlugin, const char* Name) : CDirectiveBase(pPlugin), m_NotifierName(Name) { m_Name = __func__; };
450 std::string m_NotifierName;
ProcessLocked()451 virtual void ProcessLocked() { m_pPlugin->Notifier(m_NotifierName); };
452 };
453
454 // Base event message class
455 class CEventBase : public CPluginMessageBase
456 {
457 protected:
458 virtual void ProcessLocked() = 0;
459 public:
CEventBase(CPlugin * pPlugin)460 CEventBase(CPlugin* pPlugin) : CPluginMessageBase(pPlugin) {};
461 };
462
463 class ReadEvent : public CEventBase, public CHasConnection
464 {
465 public:
CEventBase(pPlugin)466 ReadEvent(CPlugin* pPlugin, PyObject* Connection, const int ByteCount, const unsigned char* Data, const int ElapsedMs = -1) : CEventBase(pPlugin), CHasConnection(Connection)
467 {
468 m_Name = __func__;
469 m_ElapsedMs = ElapsedMs;
470 m_Buffer.reserve(ByteCount);
471 m_Buffer.assign(Data, Data + ByteCount);
472 };
473 std::vector<byte> m_Buffer;
474 int m_ElapsedMs;
ProcessLocked()475 virtual void ProcessLocked()
476 {
477 m_pPlugin->WriteDebugBuffer(m_Buffer, true);
478 m_pPlugin->ConnectionRead(this);
479 };
480 };
481
482 class DisconnectedEvent : public CEventBase, public CHasConnection
483 {
484 public:
DisconnectedEvent(CPlugin * pPlugin,PyObject * Connection)485 DisconnectedEvent(CPlugin* pPlugin, PyObject* Connection) : CEventBase(pPlugin), CHasConnection(Connection), bNotifyPlugin(true) { m_Name = __func__; };
DisconnectedEvent(CPlugin * pPlugin,PyObject * Connection,bool NotifyPlugin)486 DisconnectedEvent(CPlugin* pPlugin, PyObject* Connection, bool NotifyPlugin) : CEventBase(pPlugin), CHasConnection(Connection), bNotifyPlugin(NotifyPlugin) { m_Name = __func__; };
ProcessLocked()487 virtual void ProcessLocked() { m_pPlugin->DisconnectEvent(this); };
488 bool bNotifyPlugin;
489 };
490 }
491