328 #include <pthread.h> |
328 #include <pthread.h> |
329 |
329 |
330 #include "MQTTClient.h" |
330 #include "MQTTClient.h" |
331 #include "MQTTClientPersistence.h" |
331 #include "MQTTClientPersistence.h" |
332 |
332 |
333 #define _Log(level, ...) \\ |
333 #define _Log(level, ...) \\ |
334 {{ \\ |
334 {{ \\ |
335 /* char mstr[256]; */ \\ |
335 /* char mstr[256]; */ \\ |
336 /* snprintf(mstr, 255, __VA_ARGS__); */ \\ |
336 /* snprintf(mstr, 255, __VA_ARGS__); */ \\ |
337 /* LogMessage(level, mstr, strlen(mstr)); */ \\ |
337 /* LogMessage(level, mstr, strlen(mstr)); */ \\ |
338 printf(__VA_ARGS__); \\ |
338 printf(__VA_ARGS__); \\ |
339 }} |
339 }} |
340 |
340 |
341 #define LogInfo(...) _Log(LOG_INFO, __VA_ARGS__); |
341 #define LogInfo(...) _Log(LOG_INFO, __VA_ARGS__); |
342 #define LogError(...) _Log(LOG_CRITICAL, __VA_ARGS__); |
342 #define LogError(...) _Log(LOG_CRITICAL, __VA_ARGS__); |
343 #define LogWarning(...) _Log(LOG_WARNING, __VA_ARGS__); |
343 #define LogWarning(...) _Log(LOG_WARNING, __VA_ARGS__); |
344 |
|
345 static MQTTClient client; |
|
346 static MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer5; |
|
347 static pthread_mutex_t clientMutex; // mutex to keep PLC data consistent |
|
348 |
344 |
349 void trace_callback(enum MQTTCLIENT_TRACE_LEVELS level, char* message) |
345 void trace_callback(enum MQTTCLIENT_TRACE_LEVELS level, char* message) |
350 {{ |
346 {{ |
351 LogWarning("Paho MQTT Trace : %d, %s\\n", level, message); |
347 LogWarning("Paho MQTT Trace : %d, %s\\n", level, message); |
352 }} |
348 }} |
356 static C_type MQTT_##c_loc_name##_buf = 0; \\ |
352 static C_type MQTT_##c_loc_name##_buf = 0; \\ |
357 C_type *c_loc_name = &PLC_##c_loc_name##_buf; |
353 C_type *c_loc_name = &PLC_##c_loc_name##_buf; |
358 |
354 |
359 {decl} |
355 {decl} |
360 |
356 |
361 #define INIT_TOPIC(topic, iec_type, c_loc_name) \\ |
357 static MQTTClient client; |
|
358 #ifdef USE_MQTT_5 |
|
359 static MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer5; |
|
360 #else |
|
361 static MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer; |
|
362 #endif |
|
363 static pthread_mutex_t clientMutex; // mutex to keep PLC data consistent |
|
364 |
|
365 #define INIT_TOPIC(topic, iec_type, c_loc_name) \\ |
362 {{#topic, &MQTT_##c_loc_name##_buf, iec_type##_ENUM}}, |
366 {{#topic, &MQTT_##c_loc_name##_buf, iec_type##_ENUM}}, |
363 |
367 |
364 static struct {{ |
368 static struct {{ |
365 const char *topic; //null terminated topic string |
369 const char *topic; //null terminated topic string |
366 void *mqtt_pdata; //data from/for MQTT stack |
370 void *mqtt_pdata; //data from/for MQTT stack |
370 }}; |
374 }}; |
371 |
375 |
372 static int _connect_mqtt(void) |
376 static int _connect_mqtt(void) |
373 {{ |
377 {{ |
374 int rc; |
378 int rc; |
|
379 |
|
380 #ifdef USE_MQTT_5 |
375 MQTTProperties props = MQTTProperties_initializer; |
381 MQTTProperties props = MQTTProperties_initializer; |
376 MQTTProperties willProps = MQTTProperties_initializer; |
382 MQTTProperties willProps = MQTTProperties_initializer; |
377 MQTTResponse response = MQTTResponse_initializer; |
383 MQTTResponse response = MQTTResponse_initializer; |
378 |
384 |
379 response = MQTTClient_connect5(client, &conn_opts, &props, &willProps); |
385 response = MQTTClient_connect5(client, &conn_opts, &props, &willProps); |
380 rc = response.reasonCode; |
386 rc = response.reasonCode; |
381 MQTTResponse_free(response); |
387 MQTTResponse_free(response); |
|
388 #else |
|
389 rc = MQTTClient_connect(client, &conn_opts); |
|
390 #endif |
382 |
391 |
383 return rc; |
392 return rc; |
384 }} |
393 }} |
385 |
394 |
386 void __cleanup_{locstr}(void) |
395 void __cleanup_{locstr}(void) |
387 {{ |
396 {{ |
388 int rc; |
397 int rc; |
389 |
398 |
390 /* TODO stop publish thread */ |
399 /* TODO stop publish thread */ |
391 |
400 |
|
401 #ifdef USE_MQTT_5 |
392 if (rc = MQTTClient_disconnect5(client, 5000, MQTTREASONCODE_SUCCESS, NULL) != MQTTCLIENT_SUCCESS) |
402 if (rc = MQTTClient_disconnect5(client, 5000, MQTTREASONCODE_SUCCESS, NULL) != MQTTCLIENT_SUCCESS) |
|
403 #else |
|
404 if (rc = MQTTClient_disconnect(client, 5000) != MQTTCLIENT_SUCCESS) |
|
405 #endif |
393 {{ |
406 {{ |
394 LogError("MQTT Failed to disconnect, return code %d\\n", rc); |
407 LogError("MQTT Failed to disconnect, return code %d\\n", rc); |
395 }} |
408 }} |
396 MQTTClient_destroy(&client); |
409 MQTTClient_destroy(&client); |
397 }} |
410 }} |
413 MQTTClient_free(topicName); |
426 MQTTClient_free(topicName); |
414 return 1; |
427 return 1; |
415 }} |
428 }} |
416 |
429 |
417 #define INIT_NoAuth() \\ |
430 #define INIT_NoAuth() \\ |
418 LogInfo("MQTT Init no auth"); |
431 LogInfo("MQTT Init no auth\\n"); |
419 |
432 |
420 #define INIT_x509(PrivateKey, Certificate) \\ |
433 #define INIT_x509(PrivateKey, Certificate) \\ |
421 LogInfo("MQTT Init x509 %s,%s", PrivateKey, Certificate); |
434 LogInfo("MQTT Init x509 %s,%s\\n", PrivateKey, Certificate); |
422 /* TODO */ |
435 /* TODO */ |
423 |
436 |
424 #define INIT_UserPassword(User, Password) \\ |
437 #define INIT_UserPassword(User, Password) \\ |
425 LogInfo("MQTT Init UserPassword %s,%s", User, Password); \\ |
438 LogInfo("MQTT Init UserPassword %s,%s\\n", User, Password); \\ |
426 conn_opts.username = User; \\ |
439 conn_opts.username = User; \\ |
427 conn_opts.password = Password; |
440 conn_opts.password = Password; |
428 |
441 |
|
442 #ifdef USE_MQTT_5 |
|
443 #define MY_SUBSCRIBE(Topic, QoS) \\ |
|
444 MQTTResponse response = MQTTClient_subscribe5(client, #Topic, QoS, NULL, NULL); \\ |
|
445 rc = response.reasonCode; \\ |
|
446 MQTTResponse_free(response); |
|
447 #else |
|
448 #define MY_SUBSCRIBE(Topic, QoS) \\ |
|
449 rc = MQTTClient_subscribe(client, #Topic, QoS); |
|
450 #endif |
|
451 |
429 #define INIT_SUBSCRIPTION(Topic, QoS) \\ |
452 #define INIT_SUBSCRIPTION(Topic, QoS) \\ |
430 {{ \\ |
453 {{ \\ |
431 MQTTResponse response = MQTTClient_subscribe5(client, #Topic, QoS, NULL, NULL); \\ |
454 MY_SUBSCRIBE(Topic, QoS) \\ |
432 rc = response.reasonCode; \\ |
|
433 MQTTResponse_free(response); \\ |
|
434 if (rc != MQTTCLIENT_SUCCESS) \\ |
455 if (rc != MQTTCLIENT_SUCCESS) \\ |
435 {{ \\ |
456 {{ \\ |
436 LogError("MQTT client failed to subscribe to '%s', return code %d\\n", #Topic, rc);\\ |
457 LogError("MQTT client failed to subscribe to '%s', return code %d\\n", #Topic, rc); \\ |
437 }} \\ |
458 }} \\ |
438 }} |
459 }} |
439 |
460 |
440 int __init_{locstr}(int argc,char **argv) |
461 int __init_{locstr}(int argc,char **argv) |
441 {{ |
462 {{ |
443 char *clientID = "{clientID}"; |
464 char *clientID = "{clientID}"; |
444 int rc; |
465 int rc; |
445 |
466 |
446 MQTTClient_createOptions createOpts = MQTTClient_createOptions_initializer; |
467 MQTTClient_createOptions createOpts = MQTTClient_createOptions_initializer; |
447 |
468 |
|
469 #ifdef USE_MQTT_5 |
448 conn_opts.MQTTVersion = MQTTVERSION_5; |
470 conn_opts.MQTTVersion = MQTTVERSION_5; |
449 conn_opts.cleanstart = 1; |
471 conn_opts.cleanstart = 1; |
450 |
472 |
451 createOpts.MQTTVersion = MQTTVERSION_5; |
473 createOpts.MQTTVersion = MQTTVERSION_5; |
|
474 #else |
|
475 conn_opts.cleansession = 1; |
|
476 #endif |
452 |
477 |
453 MQTTClient_setTraceCallback(trace_callback); |
478 MQTTClient_setTraceCallback(trace_callback); |
454 MQTTClient_setTraceLevel(MQTTCLIENT_TRACE_ERROR); |
479 MQTTClient_setTraceLevel(MQTTCLIENT_TRACE_ERROR); |
455 |
480 |
456 |
481 |
463 }} |
488 }} |
464 |
489 |
465 rc = MQTTClient_setCallbacks(client, NULL, connectionLost, messageArrived, NULL); |
490 rc = MQTTClient_setCallbacks(client, NULL, connectionLost, messageArrived, NULL); |
466 if (rc != MQTTCLIENT_SUCCESS) |
491 if (rc != MQTTCLIENT_SUCCESS) |
467 {{ |
492 {{ |
468 LogError("MQTT Failed to set callbacks %d", rc); |
493 LogError("MQTT Failed to set callbacks, return code %d\\n", rc); |
469 return rc; |
494 return rc; |
470 }} |
495 }} |
471 |
496 |
472 {init} |
|
473 |
|
474 rc = _connect_mqtt(); |
497 rc = _connect_mqtt(); |
475 |
498 |
476 if (rc != MQTTCLIENT_SUCCESS) {{ |
499 if (rc != MQTTCLIENT_SUCCESS) {{ |
477 LogError("MQTT Init Failed %d", rc); |
500 LogError("MQTT Connect Failed, return code %d\\n", rc); |
478 return rc; |
501 return rc; |
479 }} |
502 }} |
|
503 |
|
504 {init} |
|
505 |
480 /* TODO start publish thread */ |
506 /* TODO start publish thread */ |
481 |
507 |
482 return 0; |
508 return 0; |
483 }} |
509 }} |
484 |
510 |
502 /* TODO free mutex */ |
528 /* TODO free mutex */ |
503 /* TODO unblock publish thread */ |
529 /* TODO unblock publish thread */ |
504 }} |
530 }} |
505 |
531 |
506 """ |
532 """ |
507 |
533 |
508 formatdict = dict( |
534 formatdict = dict( |
509 locstr = locstr, |
535 locstr = locstr, |
510 uri = config["URI"], |
536 uri = config["URI"], |
511 clientID = config["clientID"], |
537 clientID = config["clientID"], |
512 decl = "", |
538 decl = "", |
513 topics = "", |
539 topics = "", |
514 cleanup = "", |
540 cleanup = "", |
515 init = "", |
541 init = "", |
516 retrieve = "", |
542 retrieve = "", |
517 publish = "" |
543 publish = "" |
518 ) |
544 ) |
|
545 |
|
546 |
|
547 # Use Config's "MQTTVersion" to switch between protocol version at build time |
|
548 if config["UseMQTT5"]: |
|
549 formatdict["decl"] += """ |
|
550 #define USE_MQTT_5""".format(**config) |
519 |
551 |
520 AuthType = config["AuthType"] |
552 AuthType = config["AuthType"] |
521 if AuthType == "x509": |
553 if AuthType == "x509": |
522 formatdict["init"] += """ |
554 formatdict["init"] += """ |
523 INIT_x509("{PrivateKey}", "{Certificate}")""".format(**config) |
555 INIT_x509("{PrivateKey}", "{Certificate}")""".format(**config) |
547 INIT_SUBSCRIPTION({Topic}, {QoS})""".format(**locals()) |
579 INIT_SUBSCRIPTION({Topic}, {QoS})""".format(**locals()) |
548 formatdict["retrieve"] += """ |
580 formatdict["retrieve"] += """ |
549 READ_VALUE({c_loc_name}, {C_type})""".format(**locals()) |
581 READ_VALUE({c_loc_name}, {C_type})""".format(**locals()) |
550 |
582 |
551 if direction == "output": |
583 if direction == "output": |
|
584 # TODO: publish at init |
552 # formatdict["init"] += " NOTHING ! publish doesn't need init. " |
585 # formatdict["init"] += " NOTHING ! publish doesn't need init. " |
553 formatdict["publish"] += """ |
586 formatdict["publish"] += """ |
554 WRITE_VALUE({c_loc_name}, {C_type})""".format(**locals()) |
587 WRITE_VALUE({c_loc_name}, {C_type})""".format(**locals()) |
555 |
588 |
556 Ccode = template.format(**formatdict) |
589 Ccode = template.format(**formatdict) |
557 |
590 |
558 return Ccode |
591 return Ccode |
559 |
592 |
560 if __name__ == "__main__": |
593 if __name__ == "__main__": |
561 |
594 |
562 import wx.lib.mixins.inspection as wit |
595 import wx.lib.mixins.inspection as wit |
570 |
603 |
571 config={} |
604 config={} |
572 config["URI"] = sys.argv[1] if argc>1 else "tcp://localhost:1883" |
605 config["URI"] = sys.argv[1] if argc>1 else "tcp://localhost:1883" |
573 config["clientID"] = sys.argv[2] if argc>2 else "" |
606 config["clientID"] = sys.argv[2] if argc>2 else "" |
574 config["AuthType"] = None |
607 config["AuthType"] = None |
|
608 config["UseMQTT5"] = True |
575 |
609 |
576 if argc > 3: |
610 if argc > 3: |
577 AuthType = sys.argv[3] |
611 AuthType = sys.argv[3] |
578 config["AuthType"] = AuthType |
612 config["AuthType"] = AuthType |
579 for (name, default), value in zip_longest(authParams[AuthType], sys.argv[4:]): |
613 for (name, default), value in zip_longest(authParams[AuthType], sys.argv[4:]): |
593 mqtttestpanel = MQTTClientPanel(test_panel, modeldata, print, lambda:config) |
627 mqtttestpanel = MQTTClientPanel(test_panel, modeldata, print, lambda:config) |
594 |
628 |
595 def OnGenerate(evt): |
629 def OnGenerate(evt): |
596 dlg = wx.FileDialog( |
630 dlg = wx.FileDialog( |
597 frame, message="Generate file as ...", defaultDir=os.getcwd(), |
631 frame, message="Generate file as ...", defaultDir=os.getcwd(), |
598 defaultFile="", |
632 defaultFile="", |
599 wildcard="C (*.c)|*.c", style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT |
633 wildcard="C (*.c)|*.c", style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT |
600 ) |
634 ) |
601 |
635 |
602 if dlg.ShowModal() == wx.ID_OK: |
636 if dlg.ShowModal() == wx.ID_OK: |
603 path = dlg.GetPath() |
637 path = dlg.GetPath() |