emulator.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. import sys
  2. # from http.server import BaseHTTPRequestHandler, HTTPServer
  3. # from subprocess import PIPE, Popen
  4. from threading import Thread
  5. from time import sleep
  6. import websocket
  7. from os import path, chdir
  8. import ctypes
  9. import wx
  10. pwd = path.dirname(path.realpath(__file__))
  11. chdir(pwd)
  12. libmsp430 = ctypes.cdll.LoadLibrary(path.join(pwd, "libmsp430.so"))
  13. run_emu = libmsp430.run
  14. run_emu.restype = ctypes.c_int
  15. run_emu.argtypes = [ctypes.c_uint]
  16. class Emulator:
  17. EVENT_CONSOLE = 0
  18. EVENT_SERIAL = 1
  19. def __init__(self, emu_dir, ws_port=59981, load=None, callback=None):
  20. # self.process = Popen([path.join(emu_dir, 'MSP430'), str(ws_port)], stdout=PIPE, stderr=PIPE)
  21. self.process = Thread(target=run_emu, args=(ws_port, ))
  22. self.process.start()
  23. sleep(3)
  24. self.ws = websocket.WebSocketApp(f"ws://127.0.0.1:{ws_port}",
  25. subprotocols={"emu-protocol"},
  26. on_open=self._ws_open,
  27. on_data=self._ws_msg,
  28. on_error=self._ws_err,
  29. on_close=self._ws_close
  30. )
  31. self.load = load
  32. self.started = False
  33. self.start_errors = 0
  34. self.callback = callback
  35. Thread(target=self.ws.run_forever).start()
  36. def wait(self):
  37. self.process.wait()
  38. def load_file(self, fname):
  39. with open(fname, 'rb') as f:
  40. fdata = f.read()
  41. name = path.basename(fname)
  42. payload = b'\x00' # opcode
  43. payload += len(fdata).to_bytes(2, byteorder='big')
  44. payload += len(name).to_bytes(2, byteorder='big')
  45. payload += name.encode() + fdata
  46. self.ws.send(payload, websocket.ABNF.OPCODE_BINARY)
  47. def _ws_open(self):
  48. self.started = True
  49. if self.load is not None:
  50. self.load_file(self.load)
  51. def _ws_msg(self, data, frame, x):
  52. opcode = data[0]
  53. if opcode == 0:
  54. return
  55. elif opcode == 1:
  56. message = data[1:-1].decode()
  57. if "Type 'h' for" in message:
  58. return
  59. if callable(self.callback):
  60. self.callback(self.EVENT_CONSOLE, message)
  61. print(message, end=None)
  62. return
  63. elif opcode == 2:
  64. message = data[1:-1].decode()
  65. if callable(self.callback):
  66. self.callback(self.EVENT_SERIAL, message)
  67. return
  68. else:
  69. pass
  70. def _ws_err(self, err):
  71. if not self.started:
  72. self.start_errors += 1
  73. if self.start_errors < 5:
  74. print(f"Failed to connect to emulator backend attempt {self.start_errors}")
  75. sleep(1)
  76. self.ws.run_forever()
  77. raise ConnectionError("Failed to connect to emulation backend after 5 tries")
  78. raise err
  79. def _ws_close(self):
  80. if not self.started:
  81. return
  82. pass
  83. def __del__(self):
  84. self.close()
  85. def emulation_pause(self):
  86. self.ws.send(b'\x02', websocket.ABNF.OPCODE_BINARY)
  87. def emulation_start(self):
  88. self.ws.send(b'\x01', websocket.ABNF.OPCODE_BINARY)
  89. def close(self):
  90. self.ws.close()
  91. self.process.join()
  92. class EmulatorWindow(wx.Frame):
  93. def __init__(self, parent, title):
  94. wx.Frame.__init__(self, parent, title=title)
  95. self.control = wx.TextCtrl(self, size=wx.Size(400, 450),
  96. style=wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_DONTWRAP)
  97. self.serial = wx.TextCtrl(self, size=wx.Size(400, 450), style=wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_DONTWRAP)
  98. self.serial_input = wx.TextCtrl(self, style=wx.TE_DONTWRAP)
  99. self.CreateStatusBar() # A Statusbar in the bottom of the window
  100. filemenu = wx.Menu()
  101. menuFile = filemenu.Append(wx.ID_OPEN, "&Firmware", " Open firmware")
  102. menuReset = filemenu.Append(wx.ID_CLOSE_ALL, "&Reset", " Reset Emulator")
  103. menuExit = filemenu.Append(wx.ID_EXIT, "E&xit", " Terminate the program")
  104. self.Bind(wx.EVT_MENU, self.OnOpen, menuFile)
  105. self.Bind(wx.EVT_MENU, self.RestartEmulator, menuReset)
  106. self.Bind(wx.EVT_MENU, self.OnExit, menuExit)
  107. menuBar = wx.MenuBar()
  108. menuBar.Append(filemenu, "&File")
  109. self.SetMenuBar(menuBar)
  110. self.sizer2 = wx.BoxSizer(wx.HORIZONTAL)
  111. btn_start_emu = wx.Button(self, -1, "S&tart")
  112. self.Bind(wx.EVT_BUTTON, self.OnStart, btn_start_emu)
  113. btn_stop_emu = wx.Button(self, -1, "P&ause")
  114. self.Bind(wx.EVT_BUTTON, self.OnPause, btn_stop_emu)
  115. self.sizer2.Add(btn_start_emu, 1, wx.EXPAND)
  116. self.sizer2.Add(btn_stop_emu, 1, wx.EXPAND)
  117. self.sizer = wx.BoxSizer(wx.VERTICAL)
  118. self.sizer3 = wx.BoxSizer(wx.HORIZONTAL)
  119. btn_start_emu = wx.Button(self, -1, "Send")
  120. self.sizer3.Add(self.serial_input, 1, wx.EXPAND)
  121. self.sizer3.Add(btn_start_emu, 0)
  122. self.sizer0 = wx.BoxSizer(wx.VERTICAL)
  123. self.sizer0.Add(self.serial, 1, wx.EXPAND)
  124. self.sizer0.Add(self.sizer3, 0, wx.EXPAND)
  125. panel = wx.Panel(self, size=wx.Size(275, 375))
  126. img = wx.Bitmap("msp430.png", wx.BITMAP_TYPE_PNG)
  127. wx.StaticBitmap(panel, -1, img, (0, 0), (img.GetWidth(), img.GetHeight()))
  128. self.diagram = DrawRect(panel, -1, size=wx.Size(275, 375))
  129. #
  130. # dc = wx.WindowDC(panel)
  131. # dc.SetPen(wx.WHITE_PEN)
  132. # dc.SetBrush(wx.WHITE_BRUSH)
  133. # dc.DrawRectangle(50, 50, 500, 500)
  134. self.sizer1 = wx.BoxSizer(wx.HORIZONTAL)
  135. self.sizer1.Add(panel, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL)
  136. self.sizer1.Add(self.control, 1, wx.EXPAND)
  137. self.sizer1.Add(self.sizer0, 1, wx.EXPAND)
  138. self.sizer.Add(self.sizer1, 1, wx.EXPAND)
  139. self.sizer.Add(self.sizer2, 0, wx.EXPAND)
  140. self.SetSizer(self.sizer)
  141. self.SetAutoLayout(1)
  142. self.sizer.Fit(self)
  143. self.Show()
  144. self.control.WriteText("Initialising Emulator..\n")
  145. self.load = None
  146. if len(sys.argv) >= 2:
  147. if path.exists(sys.argv[1]):
  148. self.load = sys.argv[1]
  149. self.emu = Emulator('emulator', load=self.load, callback=self.callback)
  150. def callback(self, event, data):
  151. if event == Emulator.EVENT_CONSOLE:
  152. wx.CallAfter(self.control.AppendText, data)
  153. if event == Emulator.EVENT_SERIAL:
  154. wx.CallAfter(self.serial.AppendText, data)
  155. def RestartEmulator(self, e):
  156. self.control.AppendText("Stopping Emulator..")
  157. self.emu.close()
  158. self.control.Clear()
  159. self.serial.Clear()
  160. self.control.WriteText("Initialising Emulator..\n")
  161. self.emu = Emulator('emulator', load=self.load, callback=self.callback)
  162. def OnOpen(self, e):
  163. with wx.FileDialog(self, "Open Firmware", wildcard="ELF files (*.elf)|*.elf",
  164. style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) as fileDialog:
  165. if fileDialog.ShowModal() == wx.ID_CANCEL:
  166. return
  167. self.load = fileDialog.GetPath()
  168. self.RestartEmulator(None)
  169. def OnPause(self, e):
  170. self.emu.emulation_pause()
  171. def OnStart(self, e):
  172. self.emu.emulation_start()
  173. def OnExit(self, e):
  174. self.emu.close()
  175. self.Close(True)
  176. class DrawRect(wx.Panel):
  177. """ class MyPanel creates a panel to draw on, inherits wx.Panel """
  178. def __init__(self, parent, id, **kwargs):
  179. # create a panel
  180. wx.Panel.__init__(self, parent, id, **kwargs)
  181. # self.SetBackgroundColour("white")
  182. self.Bind(wx.EVT_PAINT, self.OnPaint)
  183. def OnPaint(self, evt):
  184. """set up the device context (DC) for painting"""
  185. self.dc = wx.PaintDC(self)
  186. self.dc.SetPen(wx.Pen("green",style=wx.TRANSPARENT))
  187. self.dc.SetBrush(wx.Brush("green", wx.SOLID))
  188. # set x, y, w, h for rectangle
  189. self.dc.DrawRectangle(83, 356, 8, 15)
  190. self.dc.SetPen(wx.Pen("red",style=wx.TRANSPARENT))
  191. self.dc.SetBrush(wx.Brush("red", wx.SOLID))
  192. self.dc.DrawRectangle(70, 356, 8, 15)
  193. del self.dc
  194. if __name__ == '__main__':
  195. app = wx.App(False)
  196. frame = EmulatorWindow(None, "MSP430 Emulator")
  197. app.MainLoop()