edouard@4081: # mqtt/client.py
edouard@4081: 
edouard@4081: from __future__ import absolute_import
edouard@4081: 
edouard@4081: import os
edouard@4081: 
edouard@4081: from POULibrary import POULibrary
edouard@4081: import util.paths as paths
edouard@4081: 
edouard@4081: mqtt_python_lib_code = """
edouard@4081: def MQTT_publish(clientname, topic, payload, QoS = 1, Retained = False):
edouard@4081:     c_function_name = "__mqtt_python_publish_" + clientname
edouard@4081:     c_function = getattr(PLCBinary, c_function_name)
edouard@4081:     c_function.restype = ctypes.c_int # error or 0
edouard@4081:     c_function.argtypes = [
edouard@4081:         ctypes.c_char_p,  # topic
edouard@4081:         ctypes.c_char_p,  # data
edouard@4081:         ctypes.c_uint32,  # datalen
edouard@4081:         ctypes.c_uint8,   # QoS
edouard@4081:         ctypes.c_uint8,   # Retained
edouard@4081:     ]
edouard@4081:     res = c_function(topic, payload, len(payload), QoS, Retained)
edouard@4081:     return res
edouard@4081: 
edouard@4099: # C per client CallBack type for __mqtt_python_onmsg_{name}
edouard@4099: mqtt_c_cb_onmsg_type = ctypes.CFUNCTYPE(ctypes.c_int,                   # return
edouard@4099:                                         ctypes.c_char_p,                # topic
edouard@4099:                                         ctypes.POINTER(ctypes.c_char),  # data
edouard@4099:                                         ctypes.c_uint32)                # data length
edouard@4099: 
edouard@4099: # C per client CallBack type for __mqtt_python_resub_{name}
edouard@4099: mqtt_c_cb_resub_type = ctypes.CFUNCTYPE(ctypes.c_int)                  # return
edouard@4081: 
edouard@4081: # CallBacks management
edouard@4081: # - each call to MQTT_subscribe registers a callback
edouard@4081: MQTT_subscribers_cbs = {}
edouard@4081: 
edouard@4081: # - one callback registered to C side per client
edouard@4081: MQTT_client_cbs = {}
edouard@4081: 
edouard@4100: def mqtt_per_client_cb_factory(clientname):
edouard@4099:     def per_client_onmsg_cb(topic, dataptr, datalen):
edouard@4081:         payload = ctypes.string_at(dataptr, datalen)
edouard@4100:         subscriber,_Qos = MQTT_subscribers_cbs[clientname].get(topic, None)
edouard@4081:         if subscriber:
edouard@4081:             subscriber(topic, payload)
edouard@4081:             return 0
edouard@4081:         return 1
edouard@4099:     def per_client_resub_cb():
edouard@4100:         for topic,(_cb,QoS) in MQTT_subscribers_cbs[clientname].items():
edouard@4099:             _MQTT_subscribe(clientname, topic, QoS)
edouard@4099:         return 1
edouard@4099:     return per_client_onmsg_cb,per_client_resub_cb
edouard@4081:     
edouard@4099: def _MQTT_subscribe(clientname, topic, QoS):
edouard@4081:     c_function_name = "__mqtt_python_subscribe_" + clientname
edouard@4081:     c_function = getattr(PLCBinary, c_function_name)
edouard@4081:     c_function.restype = ctypes.c_int # error or 0
edouard@4081:     c_function.argtypes = [
edouard@4081:         ctypes.c_char_p,  # topic
edouard@4081:         ctypes.c_uint8]   # QoS
edouard@4081: 
edouard@4099:     return c_function(topic, QoS)
edouard@4081: 
edouard@4099: def MQTT_subscribe(clientname, topic, cb, QoS = 1):
edouard@4099:     global MQTT_client_cbs, MQTT_subscribers_cbs
edouard@4099: 
edouard@4099:     MQTT_subscribers_cbs.setdefault(clientname, {})[topic] = (cb, QoS)
edouard@4099:     res = _MQTT_subscribe(clientname, topic, QoS)
edouard@4099: 
edouard@4099:     c_cbs = MQTT_client_cbs.get(clientname, None)
edouard@4099:     if c_cbs is None:
edouard@4099:         cb_onmsg, cb_resub = mqtt_per_client_cb_factory(clientname)
edouard@4099:         c_cbs = (mqtt_c_cb_onmsg_type(cb_onmsg),
edouard@4099:                  mqtt_c_cb_resub_type(cb_resub))
edouard@4099:         MQTT_client_cbs[clientname] = c_cbs
edouard@4081:         register_c_function = getattr(PLCBinary, "__mqtt_python_callback_setter_"+clientname )
edouard@4099:         register_c_function.argtypes = [mqtt_c_cb_onmsg_type, mqtt_c_cb_resub_type]
edouard@4099:         register_c_function(*c_cbs)
edouard@4081: 
edouard@4081:     return res
edouard@4081: 
edouard@4081: """
edouard@4081: 
edouard@4081: class MQTTLibrary(POULibrary):
edouard@4081:     
edouard@4081:     def GetLibraryPath(self):
edouard@4081:         return paths.AbsNeighbourFile(__file__, "pous.xml")
edouard@4081: 
edouard@4081:     def Generate_C(self, buildpath, varlist, IECCFLAGS):
edouard@4081: 
edouard@4081:         runtimefile_path = os.path.join(buildpath, "runtime_00_mqtt.py")
edouard@4081:         runtimefile = open(runtimefile_path, 'w')
edouard@4081:         runtimefile.write(mqtt_python_lib_code)
edouard@4081:         runtimefile.close()
edouard@4081:         return ((["mqtt"], [], False), "",
edouard@4081:                 ("runtime_00_mqtt.py", open(runtimefile_path, "rb")))