IdeasXWSCView.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. #!/usr/bin/env python`
  2. import sys
  3. import time
  4. import sip
  5. from PyQt5 import QtCore, QtGui, QtWidgets
  6. from mainwindow import Ui_MainWindow
  7. from ideasxdevice import Ui_IdeasXDevice
  8. from IdeasXWSCBackend import IdeasXWSCNetworkThread
  9. from ParsingTools import ParsingTools
  10. from encoderconfigurationdialog import Ui_SwitchConfigDialog
  11. class IdeasXSwitchDialog(QtWidgets.QDialog):
  12. def __init__(self, switch, assignedKey):
  13. super(IdeasXSwitchDialog, self).__init__()
  14. self.__ui = Ui_SwitchConfigDialog()
  15. self.__ui.setupUi(self)
  16. self.__ui.buttonApply.clicked.connect(self.submitOnClose)
  17. self.key = assignedKey[0]
  18. self.enable = assignedKey[1]
  19. self.switch = switch
  20. self.__ui.checkSwitchEnable.setChecked(self.enable)
  21. self.__ui.lineSwitchKey.setText(self.key)
  22. def submitOnClose(self):
  23. self.key = self.__ui.lineSwitchKey.text()
  24. self.enable = self.__ui.checkSwitchEnable.isChecked()
  25. self.accept()
  26. class IdeasXDeviceManager():
  27. def __init__(self, deviceClass, wsc):
  28. self.__devices = {}
  29. self.__deviceClass = deviceClass
  30. self.__deviceLayout = QtWidgets.QVBoxLayout()
  31. self.__deviceLayout.setAlignment(QtCore.Qt.AlignTop)
  32. self.__deviceLayout.setContentsMargins(9, 0, 9, 0)
  33. self.__deviceLayout.setSpacing(0)
  34. self.__wsc = wsc
  35. def refreshDevices(self, devices):
  36. for deviceMAC in list(devices.keys()):
  37. if deviceMAC in self.__devices.keys():
  38. print("Updating Device")
  39. self.__devices[deviceMAC].updateDevice(devices[deviceMAC])
  40. else:
  41. print("Adding Device")
  42. self.__devices[deviceMAC] = self.__deviceClass(devices[deviceMAC], self.__wsc)
  43. self.__deviceLayout.addWidget(self.__devices[deviceMAC])
  44. for deviceMAC in list(self.__devices.keys()):
  45. if deviceMAC not in devices.keys():
  46. print("Removing Device")
  47. self.removeDevice(deviceMAC)
  48. def removeDevice(self, deviceMAC):
  49. self.__deviceLayout.removeWidget(self.__devices[deviceMAC])
  50. sip.delete(self.__devices[deviceMAC])
  51. self.__devices.pop(deviceMAC)
  52. def returnLayout(self):
  53. return self.__deviceLayout
  54. def filterDevices(self, searchPhase):
  55. print("This currently doesn't work")
  56. def printDevices(self):
  57. print(self.__devices)
  58. class IdeasXEncoder(QtWidgets.QWidget):
  59. sendCommand = QtCore.pyqtSignal(['QString'], name='sendCommand')
  60. def __init__(self, encoder, wsc):
  61. # This should become a static variable for the class
  62. self.__pathToIcon = {'network': './icon/network/',
  63. 'battery': './icon/battery/',
  64. 'battery_charging': './icon/battery/',
  65. 'switch': './icon/switch/'
  66. }
  67. self.__icon = {'network': ['network-wireless-offline-symbolic.png',
  68. 'network-wireless-signal-weak-symbolic.png',
  69. 'network-wireless-signal-ok-symbolic.png',
  70. 'network-wireless-signal-good-symbolic.png',
  71. 'network-wireless-signal-excellent-symbolic.png'],
  72. 'battery': ['battery-empty-symbolic.png',
  73. 'battery-caution-symbolic.png',
  74. 'battery-low-symbolic.png',
  75. 'battery-good-symbolic.png',
  76. 'battery-full-symbolic.png'],
  77. 'battery_charging': ['battery-empty-charging-symbolic.png',
  78. 'battery-caution-charging-symbolic.png',
  79. 'battery-low-charging-symbolic.png',
  80. 'battery-good-charging-symbolic.png',
  81. 'battery-full-charged-symbolic.png'],
  82. 'switch': ['switch-one-enabled.png',
  83. 'switch-one-disabled.png',
  84. 'switch-two-enabled.png',
  85. 'switch-two-disabled.png',
  86. 'switch-adaptive-enabled.png',
  87. 'switch-adaptive-disabled.png']
  88. }
  89. self.__deviceType = 'encoder'
  90. self.__wsc = wsc
  91. # Setup UI components
  92. super(IdeasXEncoder, self).__init__()
  93. self.__ui = Ui_IdeasXDevice()
  94. self.__ui.setupUi(self)
  95. self._parserTools = ParsingTools()
  96. self.updateDevice(encoder)
  97. self.updateSwitchIcons()
  98. # Setup Signals
  99. self.setupMenu()
  100. self.__ui.buttonActivate.clicked.connect(self.activateEncoder)
  101. self.__ui.buttonSwitchOne.clicked.connect(lambda: self.openSwitchDialog(self.__wsc.keyEmulator.switchOne,
  102. self.__wsc.keyEmulator.getAssignedKey(self.__strModuleID, self.__wsc.keyEmulator.switchOne)))
  103. self.__ui.buttonSwitchTwo.clicked.connect(lambda: self.openSwitchDialog(self.__wsc.keyEmulator.switchTwo,
  104. self.__wsc.keyEmulator.getAssignedKey(self.__strModuleID, self.__wsc.keyEmulator.switchTwo)))
  105. def openSwitchDialog(self, switch, assignedKey):
  106. dialog = IdeasXSwitchDialog(switch, assignedKey)
  107. if dialog.exec_():
  108. if dialog.key != None and len(dialog.key) == 1:
  109. self.__wsc.keyEmulator.assignKey(self.__strModuleID, dialog.switch, dialog.key, dialog.enable)
  110. self.updateSwitchIcons()
  111. def setupSwitchIcon(self, path):
  112. icon = QtGui.QIcon()
  113. iconPath = self.__pathToIcon['switch']
  114. iconPath = iconPath + path
  115. icon.addPixmap(QtGui.QPixmap(iconPath), QtGui.QIcon.Normal, QtGui.QIcon.Off)
  116. return icon
  117. def setupMenu(self):
  118. shutdownAction = QtWidgets.QAction('Shutdown Encoder', self)
  119. shutdownAction.triggered.connect(lambda: self.__wsc.shutdownDevice(self.__strModuleID, None))
  120. testKeysAction = QtWidgets.QAction("Test Keys", self)
  121. testKeysAction.triggered.connect(self.testKeys)
  122. deviceMenu = QtWidgets.QMenu()
  123. #deviceMenu.addSection("General Actions")
  124. #deviceMenu.addAction("Pair Encoder with Actuator")
  125. #deviceMenu.addAction("Train Adaptive Switch")
  126. #deviceMenu.addAction("Configure Module")
  127. deviceMenu.addSection("Encoder Commands")
  128. deviceMenu.addAction(shutdownAction)
  129. #deviceMenu.addAction("Restart Encoder")
  130. #deviceMenu.addAction("Update Firmware")
  131. deviceMenu.addSection("Engineering Tools")
  132. deviceMenu.addAction(testKeysAction)
  133. self.__ui.buttonMenu.setPopupMode(2)
  134. self.__ui.buttonMenu.setMenu(deviceMenu)
  135. self.__ui.buttonMenu.setStyleSheet("* { padding-right: 3px } QToolButton::menu-indicator { image: none }")
  136. def activateEncoder(self):
  137. if self.__ui.buttonActivate.text() == "Activate":
  138. print("Activating Encoder: " + self.__ui.labelModuleID.text())
  139. self.__ui.buttonActivate.setText("Deactivate")
  140. else:
  141. print("Deactivating Encoder: " + self.__ui.labelModuleID.text())
  142. self.__ui.buttonActivate.setText("Activate")
  143. def updateDevice(self, encoder):
  144. self.__rssi = encoder['rssi']
  145. self.__soc = self._parserTools.calculateSOC(encoder['soc'])
  146. self.__vcell = self._parserTools.calculateVCell(encoder['vcell'])
  147. self.__strModuleID = self._parserTools.macToString(encoder['module_id'])
  148. self.__updateTime = encoder['time']
  149. self.setModuleID(self.__strModuleID)
  150. self.setSOCIcon(self.__soc)
  151. self.setRSSIIcon(self.__rssi)
  152. self.setStatusTime(self.__updateTime)
  153. def setModuleID(self, strModuleID):
  154. self.__ui.labelModuleID.setText(strModuleID)
  155. def setSOCIcon(self, soc):
  156. if soc >= 75:
  157. batteryIcon = 4
  158. elif soc >= 50 and soc < 75:
  159. batteryIcon = 3
  160. elif soc >= 25 and soc < 50:
  161. batteryIcon = 2
  162. elif soc >=10 and soc < 25:
  163. batteryIcon = 1
  164. elif soc < 10:
  165. batteryIcon = 0
  166. batteryIcon = self.__pathToIcon['battery']+self.__icon['battery'][batteryIcon]
  167. self.__ui.labelBattery.setPixmap(QtGui.QPixmap(batteryIcon))
  168. self.__ui.labelBattery.setToolTip(str(soc) + "%")
  169. def setStatusTime(self, updateTime):
  170. # set last update time or date
  171. lastUpdate = time.ctime(updateTime).split(" ")
  172. currentTime = time.ctime().split(" ")
  173. if currentTime[1] != lastUpdate[1] or currentTime[2] != lastUpdate[2] or currentTime[4] != lastUpdate[4]:
  174. lastUpdate = lastUpdate[1] + " " + lastUpdate[2] + " " + lastUpdate[4]
  175. else:
  176. lastUpdate = lastUpdate[3]
  177. self.__ui.labelStatus.setText("Last Update: " + lastUpdate)
  178. def setRSSIIcon(self, rssi):
  179. # set rssi icon
  180. if rssi >= -50:
  181. rssiIcon = 4
  182. elif rssi >= -60 and rssi < -50:
  183. rssiIcon = 3
  184. elif rssi >= -70 and rssi < -60:
  185. rssiIcon = 2
  186. elif rssi < -70:
  187. rssiIcon = 1
  188. rssiIcon = self.__pathToIcon['network'] + self.__icon['network'][rssiIcon]
  189. self.__ui.labelSignal.setPixmap(QtGui.QPixmap(rssiIcon))
  190. self.__ui.labelSignal.setToolTip(str(rssi) + " dBm")
  191. def updateSwitchIcons(self):
  192. keys = self.__wsc.keyEmulator.getAssignedKeys(self.__strModuleID)
  193. if keys[self.__wsc.keyEmulator.switchOne][1]:
  194. iconPath = self.__icon['switch'][0]
  195. else:
  196. iconPath = self.__icon['switch'][1]
  197. self.__ui.buttonSwitchOne.setIcon(self.setupSwitchIcon(iconPath))
  198. if keys[self.__wsc.keyEmulator.switchTwo][1]:
  199. iconPath = self.__icon['switch'][2]
  200. else:
  201. iconPath = self.__icon['switch'][3]
  202. self.__ui.buttonSwitchTwo.setIcon(self.setupSwitchIcon(iconPath))
  203. if keys[self.__wsc.keyEmulator.switchAdaptive][1]:
  204. iconPath = self.__icon['switch'][4]
  205. else:
  206. iconPath = self.__icon['switch'][5]
  207. self.__ui.buttonSwitchAdaptive.setIcon(self.setupSwitchIcon(iconPath))
  208. def testKeys(self):
  209. time.sleep(3)
  210. for payload in [1, 0, 2, 0, 4, 0]:
  211. self.__wsc.keyEmulator.emulateKey(self.__strModuleID, payload)
  212. time.sleep(0.1)
  213. class IdeasXMainWindow(QtWidgets.QMainWindow):
  214. def __init__(self, wsc):
  215. super(IdeasXMainWindow, self).__init__()
  216. self.__ui = Ui_MainWindow()
  217. self.__ui.setupUi(self)
  218. self.__wsc = wsc
  219. p = self.__ui.contentEncoder.palette()
  220. p.setColor(self.backgroundRole(), QtCore.Qt.white)
  221. self.__ui.contentEncoder.setPalette(p)
  222. self.__ui.statusMessageWidget = QtWidgets.QLabel()
  223. self.__ui.statusMessageWidget.setText("Starting WSC...")
  224. self.__ui.statusMessageWidget.setAlignment(QtCore.Qt.AlignLeft)
  225. self.__ui.statusbar.addWidget(self.__ui.statusMessageWidget, 1)
  226. self.__org = 'IdeasX'
  227. self.__app = 'Workstation-Client'
  228. self.restoreSettings()
  229. self.__ui.buttonSettings.clicked.connect(self.updateBrokerSettings)
  230. def setEncoderLayout(self, layout):
  231. self.__ui.contentEncoder.setLayout(layout)
  232. def setStatusBarMessage(self, msg):
  233. self.__ui.statusbar.clearMessage()
  234. self.__ui.statusMessageWidget.setText(msg)
  235. def setStatusBarUpdate(self, msg):
  236. self.__ui.statusbar.showMessage(msg)
  237. def saveSettings(self):
  238. '''
  239. Saves various backend and front information via Qt's system agnoistic classes.
  240. The following information is saved and restored:
  241. 1) MainWindow size and position
  242. 2) Switch configuration and assigned keys
  243. 3) Encoder Nicknames
  244. 4) Broker URL and Ports
  245. '''
  246. # MainWindow Settings
  247. settings = QtCore.QSettings(self.__org, self.__app)
  248. settings.beginGroup("MainWindow")
  249. settings.setValue("size", self.size())
  250. settings.setValue("pos", self.pos())
  251. settings.endGroup()
  252. def updateBrokerSettings(self):
  253. print(self.__ui.networkBroker.text())
  254. self.__NetworkBroker = self.__ui.networkBroker.text()
  255. self.__LocalBroker = self.__ui.localBroker.text()
  256. self.__NetworkPort = int(self.__ui.networkPort.text())
  257. self.__LocalPort = int(self.__ui.localPort.text())
  258. settings = QtCore.QSettings(self.__org, self.__app)
  259. settings.beginGroup("Broker")
  260. settings.setValue('NetworkBroker', self.__NetworkBroker)
  261. settings.setValue('NetworkPort', self.__NetworkPort)
  262. settings.setValue('LocalBroker', self.__LocalBroker)
  263. settings.setValue('LocalPort', self.__LocalPort)
  264. settings.endGroup()
  265. self.saveSettings()
  266. #self.__ui.statusbar.showMessage("Updated Broker settings. Please Restart the WSC.")
  267. self.__wsc.guiRestartWSC()
  268. def restoreSettings(self):
  269. settings = QtCore.QSettings(self.__org, self.__app)
  270. settings.beginGroup("MainWindow")
  271. self.resize(settings.value("size", QtCore.QSize(525, 648)))
  272. self.move(settings.value("pos", QtCore.QPoint(0, 0)))
  273. settings.endGroup()
  274. settings.beginGroup("Broker")
  275. self.__NetworkBroker = settings.value('NetworkBroker', 'ideasx.duckdns.org')
  276. self.__NetworkPort = settings.value('NetworkPort', 1883)
  277. self.__LocalBroker = settings.value('LocalBroker', '10.42.0.1')
  278. self.__LocalPort = settings.value('LocalPort', 1883)
  279. settings.endGroup()
  280. self.__ui.networkBroker.setText(self.__NetworkBroker)
  281. self.__ui.networkPort.setText(str(self.__NetworkPort))
  282. self.__ui.localBroker.setText(self.__LocalBroker)
  283. self.__ui.localPort.setText(str(self.__LocalPort))
  284. settings.beginGroup('OTAServer')
  285. self.__OTAServer = settings.value('OTAServer', 'ideasx.duckdns.org')
  286. settings.endGroup()
  287. def closeEvent(self, event):
  288. self.saveSettings()
  289. super(IdeasXMainWindow, self).closeEvent(event)
  290. if __name__ == "__main__":
  291. app = QtWidgets.QApplication(sys.argv)
  292. app.setWindowIcon(QtGui.QIcon('icon/logo/ideasx.png'))
  293. wsc = IdeasXWSCNetworkThread()
  294. mainWindow = IdeasXMainWindow(wsc)
  295. encoderManager = IdeasXDeviceManager(IdeasXEncoder, wsc)
  296. mainWindow.setEncoderLayout(encoderManager.returnLayout())
  297. wsc.encoderUpdate.connect(encoderManager.refreshDevices)
  298. wsc.networkStatus.connect(mainWindow.setStatusBarMessage)
  299. wsc.networkUpdate.connect(mainWindow.setStatusBarUpdate)
  300. #wsc.guiStartWorkstationClient('ideasx.duckdns.org')
  301. wsc.guiStartWorkstationClient()
  302. #timer = QtCore.QTimer()
  303. #timer.timeout.connect(mainWindow.hideEncoder)
  304. #timer.start(1000)
  305. #time.sleep(0.5)
  306. mainWindow.show()
  307. sys.exit(app.exec_())