ftrace.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. #!/usr/bin/env python2
  2. #-*- coding: utf-8 -*-
  3. import wx
  4. import sys
  5. import datetime
  6. import multiprocessing
  7. import pickle
  8. import re
  9. import socket
  10. import select
  11. import yaml
  12. import rosnode
  13. import rosgraph
  14. try:
  15. from xmlrpc.client import ServerProxy
  16. except ImportError:
  17. from xmlrpclib import ServerProxy
  18. # Class main Frame
  19. class MyFrame(wx.Frame):
  20. # Constructor(MyFrame): function for initial display
  21. def __init__(self, parent, id, title):
  22. wx.Frame.__init__(self, parent, id, title)
  23. # setup base panel
  24. panel = wx.Panel(self, -1)
  25. vbox = wx.BoxSizer(wx.VERTICAL)
  26. panel.SetSizer(vbox)
  27. # setup upper panel(for graphic display base)
  28. self.hpanel_ = wx.Panel(panel, -1)
  29. self.hbox_ = wx.BoxSizer(wx.HORIZONTAL)
  30. self.hpanel_.SetSizer(self.hbox_)
  31. self.clpanel_ = wx.Panel(self.hpanel_, -1)
  32. self.clbox_ = wx.BoxSizer(wx.VERTICAL)
  33. self.clpanel_.SetSizer(self.clbox_)
  34. self.hbox_.Add(self.clpanel_, flag=wx.ALL, border=2)
  35. self.cgpanel_ = wx.Panel(self.hpanel_, -1)
  36. self.cgbox_ = wx.BoxSizer(wx.VERTICAL)
  37. self.cgpanel_.SetSizer(self.cgbox_)
  38. self.hbox_.Add(self.cgpanel_, flag=wx.ALL, border=2)
  39. vbox.Add(self.hpanel_, flag=wx.ALL, border=2)
  40. # setup middle panel(for comment display base)
  41. self.lpanel_ = wx.Panel(panel, -1)
  42. self.lbox_ = wx.BoxSizer(wx.HORIZONTAL)
  43. self.lpanel_.SetSizer(self.lbox_)
  44. vbox.Add(self.lpanel_, wx.ALL, border=8)
  45. # setup lower panel(for button display base)
  46. spanel = wx.Panel(panel, -1)
  47. hbox = wx.BoxSizer(wx.HORIZONTAL)
  48. self.sbtn = wx.Button(spanel, -1, "Start/Stop")
  49. self.sbtn.Bind(wx.EVT_BUTTON, self.OnUpdateCont)
  50. hbox.Add(self.sbtn, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL, border=2)
  51. vbtn = wx.Button(spanel, -1, "CPU/Node")
  52. vbtn.Bind(wx.EVT_BUTTON, self.OnChangeView)
  53. hbox.Add(vbtn, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL, border=2)
  54. ebtn = wx.Button(spanel, -1, "Close")
  55. ebtn.Bind(wx.EVT_BUTTON, self.OnExit)
  56. hbox.Add(ebtn, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL, border=2)
  57. spanel.SetSizer(hbox)
  58. vbox.Add(spanel, flag=wx.ALL, border=10)
  59. # initial settings
  60. self.labels_ = []
  61. self.cgpanels_ = []
  62. self.cpucount_ = multiprocessing.cpu_count()
  63. self.pids_ = []
  64. self.view_ = 0 # 0=CPUno, 1=ROSnode(pid)
  65. self.itmcount_ = 0 # disply-item count (except time)
  66. self.itmcolors_ = [
  67. '#cc0000', '#00cc00', '#0000cc',
  68. '#cccc00', '#cc00cc', '#00cccc',
  69. '#990000', '#009900', '#000099',
  70. '#999900', '#990099', '#009999',
  71. '#660000', '#006600', '#000066',
  72. '#666600', '#660066', '#006666',
  73. '#330000', '#003300', '#000033',
  74. '#333300', '#330033', '#003333']
  75. self.bgcol_ = '#ffffff' # background color for graph drawing
  76. self.cgtw_, self.cgth_ = wx.StaticText(self.cgpanel_, -1, " "*320).GetSize() # get size for graphic drawing area
  77. # initial display
  78. self.ChangeView()
  79. self.SetSize(panel.GetBestSize())
  80. self.Centre()
  81. self.Show(True)
  82. # Timer setting
  83. self.timer = wx.Timer(self)
  84. self.Bind(wx.EVT_TIMER, self.OnTimer)
  85. # parameter settings
  86. self.update = False # 1=update executing
  87. self.sock = None # comm with proc_manager.py
  88. self.dtimespan = 3.0 # period for graphic disply [s]
  89. self.dtime = 0 # latest time for drawing data [s]
  90. # Function(MyFrame): Change display mode of CPUno/ROSnode
  91. def ChangeView(self):
  92. # erase current format
  93. self.clbox_.Clear(True)
  94. self.cgbox_.Clear(True)
  95. self.lbox_.Clear(True)
  96. self.lbox_.Add(wx.StaticText(self.lpanel_, -1, " "), flag=wx.ALL, border=2)
  97. self.labels_ = []
  98. self.cgpanels_ = []
  99. self.pids_ = self.getROSNodes()
  100. # redraw new format
  101. if self.view_ == 0:
  102. self.itmcount_ = self.cpucount_
  103. for cpuno in range(0, self.cpucount_):
  104. text = wx.StaticText(self.clpanel_, -1, "CPU%02d:" % cpuno)
  105. self.clbox_.Add(text, flag=wx.ALIGN_RIGHT|wx.ALL, border=4)
  106. self.labels_.append(cpuno)
  107. for (name, pid) in self.pids_:
  108. text = wx.StaticText(self.lpanel_, -1, u"■%d" % pid)
  109. text.SetForegroundColour(self.GetColor(0, pid))
  110. text.SetToolTip(wx.ToolTip(name))
  111. self.lbox_.Add(text, flag=wx.ALL, border=2)
  112. else:
  113. self.itmcount_ = len(self.pids_)
  114. for (name, pid) in self.pids_:
  115. text = wx.StaticText(self.clpanel_, -1, "%s:" % name)
  116. text.SetToolTip(wx.ToolTip("pid=%d" % pid))
  117. self.clbox_.Add(text, flag=wx.ALIGN_RIGHT|wx.ALL, border=4)
  118. self.labels_.append(pid)
  119. for cpuno in range(0, self.cpucount_):
  120. text = wx.StaticText(self.lpanel_, -1, u"■CPU%02d" % cpuno)
  121. text.SetForegroundColour(self.GetColor(cpuno, 0))
  122. self.lbox_.Add(text, flag=wx.ALL, border=2)
  123. text = wx.StaticText(self.clpanel_, -1, "Time:") # add time line
  124. self.clbox_.Add(text, flag=wx.ALIGN_RIGHT|wx.ALL, border=4)
  125. for n in range(0, self.itmcount_ + 1):
  126. panel = wx.Panel(self.cgpanel_, -1, size=(self.cgtw_, self.cgth_))
  127. panel.SetBackgroundColour(self.bgcol_)
  128. self.cgbox_.Add(panel, flag=wx.ALL, border=4)
  129. self.cgpanels_.append(panel)
  130. self.clbox_.Layout()
  131. self.cgbox_.Layout()
  132. self.lbox_.Layout()
  133. self.hpanel_.GetParent().Fit()
  134. self.hpanel_.GetParent().GetParent().Fit()
  135. # Function(MyFrame): Start/Stop ftrace funvtion by Start/Stop button
  136. def OnUpdateCont(self, event):
  137. if self.update == False:
  138. # connect to proc_manager
  139. self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
  140. try:
  141. self.sock.connect("/tmp/autoware_proc_manager")
  142. except socket.error:
  143. print "ERROR:[OnUpdateCont-01] cannot connect to proc_manager"
  144. return
  145. try:
  146. order = { "name":"ftrace_cont", "interval":0.1, "pids":map(lambda n:n[1], self.pids_) }
  147. self.sock.send(yaml.dump(order))
  148. except socket.error:
  149. self.sock.close()
  150. self.sock = None
  151. print "ERROR:[OnUpdateCont-02] cannot send to proc_manager"
  152. return
  153. # start ftrace
  154. self.sbtn.SetBackgroundColour("#CCFFCC")
  155. self.update = True
  156. self.dtime = 0
  157. self.timer.Start(100)
  158. else:
  159. # stop ftrace
  160. self.sbtn.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND))
  161. self.update = False
  162. self.timer.Stop()
  163. if self.sock is not None:
  164. self.sock.close()
  165. self.sock = None
  166. # Function(MyFrame): Change CPU/ROSnode display format by CPU/Proc button
  167. def OnChangeView(self, event):
  168. self.view_ = 1 if self.view_ == 0 else 0
  169. self.ChangeView()
  170. # Function(MyFrame): Exit by close button
  171. def OnExit(self, event):
  172. sys.exit(0)
  173. # Function(MyFrame): Timer interrupt(for receive ftrace data and draw)
  174. def OnTimer(self, event):
  175. dat = ""
  176. while True:
  177. (r, _, _) = select.select([self.sock], [], [], 0.01) # wait10ms
  178. if len(r) <= 0:
  179. break
  180. d = self.sock.recv(1000000)
  181. if len(d) <= 0:
  182. break
  183. dat += d
  184. if len(dat) > 0:
  185. #print "DEBUG:[OnTimer] rlen=%d" % len(dat)
  186. dat = pickle.loads(dat)
  187. self.UpdateGraph(dat)
  188. # Function(MyFrame): Draw graph
  189. def UpdateGraph(self, dat):
  190. # get first and last time for new data
  191. etime = 0 # last time(right side for graph)
  192. for cpuno in dat:
  193. for d in dat[cpuno]:
  194. if d[1] > etime:
  195. etime = d[1]
  196. stime = etime - self.dtimespan # first time(left side for graph)
  197. ctime = datetime.datetime.now() # get current time(for time scale diplay)
  198. # shift previous drawing
  199. if self.itmcount_ > 0:
  200. # calcurate graphic shift value
  201. sft = self.cgtw_ * (etime - self.dtime) / self.dtimespan # shift [pixel]
  202. if sft > self.cgtw_:
  203. sft = self.cgtw_ # clear all area
  204. for n in range(0, self.itmcount_):
  205. dc = wx.PaintDC(self.cgpanels_[n])
  206. dc.SetPen(wx.Pen(self.bgcol_))
  207. dc.SetBrush(wx.Brush(self.bgcol_))
  208. # shift previous drawing
  209. dc.Blit(0, 0, self.cgtw_ - sft, self.cgth_, dc, sft, 0)
  210. # clear new data drawing area
  211. dc.DrawRectangle(self.cgtw_ - sft, 0, sft, self.cgth_)
  212. self.dtime = etime
  213. # draw graph of new data
  214. for cpuno in range(0, self.cpucount_):
  215. pid = 0 # previous pid
  216. ptm = 0 # previous time
  217. for d in dat[cpuno]:
  218. cid = d[0] # current pid
  219. ctm = d[1] # current time
  220. col = '' # drawing color(depend on previous pid)
  221. for (_, p) in self.pids_:
  222. if p == pid:
  223. col = self.GetColor(cpuno, pid)
  224. break
  225. if len(col) == 0:
  226. if self.view_ == 0 and pid != 0:
  227. col = "#cccccc"
  228. else:
  229. ptm = ctm
  230. pid = cid
  231. continue
  232. if self.view_ == 0:
  233. n = cpuno
  234. else:
  235. n = self.labels_.index(pid)
  236. dc = wx.PaintDC(self.cgpanels_[n])
  237. dc.SetPen(wx.Pen(col))
  238. dc.SetBrush(wx.Brush(col))
  239. w = int(self.cgtw_ * (ctm - ptm) / self.dtimespan + 1)
  240. p = int(self.cgtw_ * (ptm - stime) / self.dtimespan)
  241. if (p + w) > (self.cgtw_ - 1):
  242. p = self.cgtw_ - w - 1
  243. dc.DrawRectangle(p, 0, w, self.cgth_)
  244. ptm = ctm
  245. pid = cid
  246. # draw scale and time
  247. dc = wx.PaintDC(self.cgpanels_[self.itmcount_])
  248. dc.SetPen(wx.Pen(self.bgcol_))
  249. dc.SetBrush(wx.Brush(self.bgcol_))
  250. dc.DrawRectangle(0, 0, self.cgtw_, self.cgth_) # clear drawing area
  251. dc.SetTextForeground((0,0,0))
  252. tint = self.cgtw_ / self.dtimespan # period for time display
  253. tofs = self.cgtw_ * (1 - (ctime.microsecond / self.dtimespan / 1000000)) # offset for drawing
  254. tmin = ctime.minute
  255. tsec = ctime.second
  256. dc.SetPen(wx.Pen('blue'))
  257. for t in range(-10, 30): # draw scale
  258. x = tofs - (t / 10.0) * tint
  259. dc.DrawLine(x, 0, x, 4)
  260. if (t % 5) == 0:
  261. dc.DrawLine(x - 1, 0, x - 1, 4)
  262. for t in range(0, 4): # draw time
  263. ttext = "%02d:%02d" % (tmin, tsec)
  264. txtw,_ = dc.GetTextExtent(ttext) # get text width for time display
  265. dc.DrawText(ttext, tofs - (txtw / 2), 0) # draw time
  266. tofs -= tint
  267. tsec -= 1
  268. if tsec < 0:
  269. tsec = 59
  270. tmin -= 1
  271. if tmin < 0:
  272. tmin = 59
  273. # Function(MyFrame): Get drawing color
  274. def GetColor(self, cpuno, pid):
  275. if self.view_ == 0:
  276. i = 0
  277. for (_, p) in self.pids_:
  278. if p == pid:
  279. return self.itmcolors_[i % len(self.itmcolors_)]
  280. i += 1
  281. return self.itmcolors_[0]
  282. else:
  283. return self.itmcolors_[cpuno % len(self.itmcolors_)]
  284. # Function(MyFrame): Get ROSnode pid
  285. def getROSNodes(self):
  286. nodes = []
  287. try:
  288. nodenames = rosnode.get_node_names(None)
  289. except Exception as inst:
  290. print "ERROR:[getROSNodes-01] ", inst
  291. return nodes
  292. for nodename in nodenames:
  293. #rosnode.rosnode_info(nodename)
  294. api = rosnode.get_api_uri(rosgraph.Master("/rosnode"), nodename)
  295. if api:
  296. try:
  297. node = ServerProxy(api)
  298. code, msg, pid = node.getPid("/rosnode")
  299. if code == 1:
  300. res = re.search("^(.*)_[0-9]+$", nodename)
  301. while res is not None:
  302. nodename = res.group(1)
  303. res = re.search("^(.*)_[0-9]+$", nodename)
  304. nodes.append((nodename, pid))
  305. except:
  306. pass
  307. return nodes
  308. # Main
  309. if __name__ == "__main__":
  310. myapp = wx.App(0)
  311. frame = MyFrame(None, -1, "Ftrace")
  312. frame.Show()
  313. myapp.MainLoop()