wsc_backend.py 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. #!/usr/bin/python
  2. '''
  3. WORKSTATION CLIENT BACKEND
  4. '''
  5. import sys
  6. try:
  7. import paho.mqtt.client as mqtt
  8. except ImportError:
  9. # This part is only required to run the example from within the examples
  10. # directory when the module itself is not installed.
  11. #
  12. # If you have the module installed, just use "import paho.mqtt.client"
  13. import os
  14. import inspect
  15. cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"../src")))
  16. if cmd_subfolder not in sys.path:
  17. sys.path.insert(0, cmd_subfolder)
  18. import paho.mqtt.client as mqtt
  19. # NOTE: This needs to be replaced with a QT, TKinker alternative.
  20. # from wx.lib.pubsub import pub
  21. from packet_handling import decode_client_list, decode_data_packet
  22. try:
  23. from ctype_bindings import SwitchPro6
  24. windows = True
  25. except AttributeError:
  26. print("Windows win32 API missing.\n Button presses will be still be represented in print statements.")
  27. global print_debug
  28. windows = False
  29. #------------------------------------------------------------------------------
  30. # WORKSTATIONCLIENT CLASS
  31. class WorkstationClientClass():
  32. def __init__(self, client_id=None, debug=True, mqttdebug=False):
  33. self.client_id = client_id
  34. self.debug = debug
  35. self.mqttdebug = mqttdebug
  36. self.mode_color = 'debug'
  37. self.data_topics = '/modules/+/data'
  38. self.clientlist_topic = '/workstations/modulehealth'
  39. self.clientlist = None
  40. self.subscribed_modules = []
  41. if windows:
  42. self.switchpro6 = SwitchPro6()
  43. self._mqttc = mqtt.Client(self.client_id, clean_session=True, userdata=None,
  44. protocol='MQTTv311')
  45. self._mqttc.message_callback_add(self.clientlist_topic, self.mqtt_on_client_list)
  46. self._mqttc.message_callback_add(self.data_topics, self.mqtt_on_data)
  47. self._mqttc.on_connect = self.mqtt_on_connect
  48. self._mqttc.on_disconnect = self.mqtt_on_disconnect
  49. if self.mqttdebug:
  50. self._mqttc.on_log = self.mqtt_on_log
  51. #------------------------------------------------------------------------------
  52. # callback functions
  53. def mqtt_on_connect(self, mqttc, backend_data, flags, rc):
  54. # pub.sendMessage('status.connection', status = [mqttc._host, mqttc._port, rc])
  55. if self.debug:
  56. if rc == 0:
  57. print('Connected to %s: %s' % (mqttc._host, mqttc._port))
  58. else:
  59. print('rc: ' + str(rc))
  60. def mqtt_on_client_list(self, mqttc, backend_data, msg):
  61. self.clientlist = decode_client_list(msg.payload)
  62. for active_module_id in self.subscribed_modules:
  63. for dead_module_id in self.clientlist[self.clientlist['alive']==0]['module_id']:
  64. if active_module_id == dead_module_id:
  65. if self.debug:
  66. print("Conflicting ID: " + str(active_module_id)+ \
  67. "\nAttempting to kill now.")
  68. self.unsubscribe_to_module(active_module_id)
  69. if self.debug:
  70. print(self.clientlist)
  71. # pub.sendMessage('data.clientlist', data = self.clientlist)
  72. def mqtt_on_data(self, mqttc, backend_data, msg):
  73. #pub.sendMessage('data.module', data=msg.payload)
  74. data = decode_data_packet(msg.payload)
  75. if windows:
  76. self.switchpro6.keymap_press(data[0])
  77. if self.debug:
  78. print(msg.topic+" "+str(msg.qos)+" "+str(msg.payload)+" "+str(data))
  79. def mqtt_on_log(self, mqttc, backend_data, level, string):
  80. print(string)
  81. def mqtt_on_disconnect(self, mqttc, backend_data, rc):
  82. # pub.sendMessage('status.connection', status = [None, None, rc])
  83. if self.debug:
  84. if rc != 0:
  85. print("Client disconnected and its a mystery why!")
  86. else:
  87. print("Client successfully disconnected.")
  88. #------------------------------------------------------------------------------
  89. # General API Calls
  90. def start_workstation_client(self, ip="127.0.0.1", port=1883, keepalive=60):
  91. self.ip = ip
  92. self.port = port
  93. self.keepalive = keepalive
  94. self.clientlist = None
  95. self._mqttc.connect(ip, port, keepalive) # connect to broker
  96. self._mqttc.subscribe(self.clientlist_topic, 2) # subscribe to client list
  97. self._mqttc.loop_start() # Start a thread handling network traffic
  98. def subscribe_to_module(self, module_id):
  99. module_found = False
  100. if type(module_id) == int:
  101. module_id = str(module_id)
  102. if len(module_id) < 8:
  103. module_id = '0'*(8-len(module_id)) + module_id
  104. for active_module_id in self.clientlist[self.clientlist['alive']==1]['module_id']:
  105. if int(module_id) == active_module_id:
  106. module_found = True
  107. module_topic = '/modules/'+module_id+'/data'
  108. result, mid = self._mqttc.subscribe(module_topic, qos=0)
  109. # pub.sendMessage('status.subscribe', status=[int(module_id), result, mid])
  110. if result == 0:
  111. if int(module_id) not in self.subscribed_modules:
  112. self.subscribed_modules.append(int(module_id))
  113. #pub.sendMessage('data.subscribed_modules', data=self.subscribed_modules)
  114. if self.debug:
  115. print("Module %s was successfully added" % module_id)
  116. else:
  117. if self.debug:
  118. print("Module is already subscribed")
  119. return result, mid
  120. if module_found == False:
  121. if self.debug:
  122. print("Module is not alive or in client list.")
  123. print("Module ID: " + str(module_id))
  124. return 3, None
  125. def unsubscribe_to_module(self, module_id):
  126. if type(module_id) == int:
  127. module_id = str(module_id)
  128. if len(module_id) < 8:
  129. module_id = '0'*(8-len(module_id)) + module_id
  130. if int(module_id) in self.subscribed_modules:
  131. module_topic = '/modules/'+module_id+'/data'
  132. result, mid = self._mqttc.unsubscribe(module_topic)
  133. # pub.sendMessage('status.unsubscribe', status=[int(module_id), result, mid])
  134. if result == 0:
  135. self.subscribed_modules.remove(int(module_id))
  136. #pub.sendMessage('data.subscribed_modules', data=self.subscribed_modules) # send all the subs
  137. if self.debug:
  138. print("Module %s was successfully removed" % module_id)
  139. return result, mid
  140. else:
  141. if self.debug:
  142. print("Module is not subscribed")
  143. print("Subscribed Modules:\n", self.subscribed_modules)
  144. return 3, None
  145. #-----------------------------------------------------------------------------
  146. # Puck Connectivity
  147. # The Puck will function under the following methods.
  148. # 1. The puck will make the system aware of it's presence through a health
  149. # message to the database client. There needs to be sometype of addition
  150. # flag or delimiter for the database client to know it is a puck.
  151. # 2. The backend needs to forward this flag via pubsub.
  152. # 3. There needs to be function to pair a module to a puck with the following
  153. # house keeping skills:
  154. # a. Automatically unpairs if module or puck dies
  155. # b. will not pair if puck or module is already connected to computer or
  156. # paired with another computer.
  157. # c. sends a message to puck to listen to the topic of a specific module.
  158. # d. confirms puck is listening through sometype of verification QoS = 2?
  159. #
  160. def pair_module_to_puck(self, module_id, puck_id, pair=True):
  161. if type(module_id) == int:
  162. module_id = str(module_id)
  163. if len(module_id)<8:
  164. module_id = '0'*(8-len(module_id)) + module_id
  165. if type(puck_id) == int:
  166. puck_id = str(puck_id)
  167. if len(puck_id)<8:
  168. puck_id = '0'*(8-len(puck_id)) + puck_id
  169. module_found = False
  170. # update to insure puck is alive
  171. # modifiy so pandas only is loooking at modules
  172. for active_module_id in self.clientlist[self.clientlist['alive']==1]['module_id']:
  173. if int(module_id) == active_module_id :
  174. module_found = True
  175. puck_topic = '/pucks/'+puck_id+'/command'
  176. if pair:
  177. payload = 'a/modules/'+str(module_id)+'/data'
  178. else:
  179. payload = 'd/modules/'+str(module_id)+'/data'
  180. result, mid = self._mqttc.publish(puck_topic, payload, 0, retain=False)
  181. if result == 0:
  182. # self.subscribed_modules.remove(int(module_id))
  183. # pub.sendMessage('data.paired_modules', data=self.subscribed_modules) # send all the subs
  184. if self.debug:
  185. print("Module %s was successfully paired" % module_id)
  186. return result, mid
  187. if module_found == False:
  188. if self.debug:
  189. print("Module is not alive or in client list.")
  190. print("Module ID: " + str(module_id))
  191. return 3, None
  192. if __name__ == "__main__":
  193. wsc = WorkstationClientClass()
  194. wsc.start_workstation_client(ip="server.ideasx.tech")