ui.cpp
Go to the documentation of this file.
1 // console user interface
2 // Author: Max Schwarz <max.schwarz@uni-bonn.de>
3 
4 #include "ui.h"
5 #include "husl/husl.h"
6 
7 #include <cstdlib>
8 #include <ros/node_handle.h>
9 
10 #include <fmt/format.h>
11 
12 static unsigned int g_statusLines = 2;
13 static std::string g_windowTitle;
14 
15 void cleanup()
16 {
17  for(unsigned int i = 0; i < g_statusLines+1; ++i)
18  printf("\n");
19 
20  rosmon::Terminal term;
21 
22  // Switch cursor back on
23  term.setCursorVisible();
24 
25  // Switch character echo on
26  term.setEcho(true);
27 
28  // Restore window title (at least try)
29  if(!g_windowTitle.empty())
30  term.clearWindowTitle(g_windowTitle + "[-]");
31 }
32 
33 namespace rosmon
34 {
35 
36 UI::UI(monitor::Monitor* monitor, const FDWatcher::Ptr& fdWatcher)
37  : m_monitor(monitor)
38  , m_fdWatcher(fdWatcher)
39  , m_columns(80)
40  , m_selectedNode(-1)
41 {
42  std::atexit(cleanup);
43  m_monitor->logMessageSignal.connect(boost::bind(&UI::log, this, _1, _2));
44 
45  m_sizeTimer = ros::NodeHandle().createWallTimer(ros::WallDuration(2.0), boost::bind(&UI::checkWindowSize, this));
47 
49  setupColors();
50 
51  // Switch cursor off
53 
54  // Switch character echo off
55  m_term.setEcho(false);
56 
57  // Configure window title
58  std::string title = m_monitor->config()->windowTitle();
59  if(!title.empty())
60  {
61  m_term.setWindowTitle(title);
62  g_windowTitle = title;
63  }
64 
65  fdWatcher->registerFD(STDIN_FILENO, boost::bind(&UI::handleInput, this));
66 }
67 
69 {
70  m_fdWatcher->removeFD(STDIN_FILENO);
71 }
72 
74 {
75  // Sample colors from the HUSL space
76  int n = m_monitor->nodes().size();
77 
78  for(int i = 0; i < n; ++i)
79  {
80  float hue = i * 360 / n;
81  float sat = 100;
82  float lum = 20;
83 
84  float r, g, b;
85  HUSLtoRGB(&r, &g, &b, hue, sat, lum);
86 
87  r *= 255.0;
88  g *= 255.0;
89  b *= 255.0;
90 
91  unsigned int color =
92  std::min(255, std::max<int>(0, r))
93  | (std::min(255, std::max<int>(0, g)) << 8)
94  | (std::min(255, std::max<int>(0, b)) << 16);
95 
96  m_nodeColorMap[m_monitor->nodes()[i]->name()] = ChannelInfo(color);
97  }
98 }
99 
101 {
102  const int NODE_WIDTH = 13;
103 
104  unsigned int lines = 2;
105 
106  // Print menu if a node is selected
107  if(m_selectedNode != -1)
108  {
109  auto& selectedNode = m_monitor->nodes()[m_selectedNode];
110  std::string muteOption = isMuted(selectedNode->name()) ? "u: unmute" : "m: mute";
111 
112  fmt::print("Actions: s: start, k: stop, d: debug, {}", muteOption);
113  }
114  else
115  {
116  fmt::print("Global shortcuts: <node key>: show node menu, F9: mute all, F10: unmute all ");
117  if (anyMuted())
118  {
121  fmt::print("! Caution: Nodes muted !");
123  }
124  }
125 
126  fmt::print("\n");
127 
128  int col = 0;
129 
130  char key = 'a';
131  int i = 0;
132 
133  for(auto& node : m_monitor->nodes())
134  {
135  // Print key with grey background
136  Terminal::SimpleColor keyForegroundColor = Terminal::Black;
137  Terminal::SimpleColor keyBackgroundColor = Terminal::White;
138  uint32_t keyBackgroundColor256 = 0xC8C8C8;
139 
140  if(isMuted(node->name()))
141  {
142  keyBackgroundColor = Terminal::Red;
143  keyForegroundColor = Terminal::White;
144  keyBackgroundColor256 = 0x0000A5;
145  }
146 
147  m_term.setSimpleForeground(keyForegroundColor);
148 
149  if(m_term.has256Colors())
150  m_term.setBackgroundColor(keyBackgroundColor256);
151  else
152  m_term.setSimpleBackground(keyBackgroundColor);
153 
154  fmt::print("{:c}", key);
155 
156  switch(node->state())
157  {
160  break;
163  break;
166  break;
169  break;
170  }
171 
172  std::string label = node->name().substr(0, NODE_WIDTH);
173  if(i == m_selectedNode)
174  fmt::print("[{:^{}}]", label, NODE_WIDTH);
175  else
176  fmt::print(" {:^{}} ", label, NODE_WIDTH);
178 
179  // Primitive wrapping control
180  const int BLOCK_WIDTH = NODE_WIDTH + 3;
181  col += BLOCK_WIDTH;
182 
183  if(col + 1 + BLOCK_WIDTH < m_columns)
184  {
185  printf(" ");
186  col += 1;
187  }
188  else if(col == m_columns)
189  {
190  col = 0;
191  lines++;
192  }
193  else if(col + 1 + BLOCK_WIDTH > m_columns)
194  {
195  col = 0;
196  lines++;
197  printf("\n\033[K");
198  }
199 
200  if(key == 'z')
201  key = 'A';
202  else if(key == 'Z')
203  key = '0';
204  else if(key == '9')
205  key = ' ';
206  else if(key != ' ')
207  ++key;
208 
209  ++i;
210  }
211 
212  for(unsigned int i = lines; i < g_statusLines; ++i)
213  printf("\n\033[K");
214 
215  g_statusLines = std::max(lines, g_statusLines);
216 }
217 
218 void UI::log(const std::string& channel, const std::string& str)
219 {
220  if (isMuted(channel))
221  return;
222 
223  std::string clean = str;
224 
225  auto it = m_nodeColorMap.find(channel);
226 
227  if(m_term.has256Colors())
228  {
229  if(it != m_nodeColorMap.end())
230  {
231  m_term.setBackgroundColor(it->second.labelColor);
233  }
234  }
235  else
236  {
238  }
239 
240  fmt::print("{:>20}:", channel);
243  putchar(' ');
244 
245  unsigned int len = clean.length();
246  while(len != 0 && (clean[len-1] == '\n' || clean[len-1] == '\r'))
247  len--;
248 
249  if(it != m_nodeColorMap.end())
250  {
251  it->second.parser.apply(&m_term);
252  it->second.parser.parse(clean);
253  }
254 
255  fwrite(clean.c_str(), 1, len, stdout);
257  putchar('\n');
260  fflush(stdout);
261 }
262 
264 {
265  if(!m_term.interactive())
266  return;
267 
268  // We currently are at the beginning of the status line.
269  putchar('\n');
271  drawStatusLine();
272 
273  // Move back
277  fflush(stdout);
278 }
279 
281 {
282  int rows, columns;
283  if(m_term.getSize(&columns, &rows))
284  m_columns = columns;
285 }
286 
288 {
289  int c = m_term.readKey();
290  if(c < 0)
291  return;
292 
293  if(m_selectedNode == -1)
294  {
295  int nodeIndex = -1;
296 
297  // Check for Mute all keys first
298 
299  if(c == Terminal::SK_F9)
300  {
301  muteAll();
302  return;
303  }
304  if(c == Terminal::SK_F10)
305  {
306  unmuteAll();
307  return;
308  }
309 
310  if(c >= 'a' && c <= 'z')
311  nodeIndex = c - 'a';
312  else if(c >= 'A' && c <= 'Z')
313  nodeIndex = 26 + c - 'A';
314  else if(c >= '0' && c <= '9')
315  nodeIndex = 26 + 26 + c - '0';
316 
317  if(nodeIndex < 0 || (size_t)nodeIndex >= m_monitor->nodes().size())
318  return;
319 
320  m_selectedNode = nodeIndex;
321  }
322  else
323  {
324  auto& node = m_monitor->nodes()[m_selectedNode];
325 
326  switch(c)
327  {
328  case 's':
329  node->start();
330  break;
331  case 'k':
332  node->stop();
333  break;
334  case 'd':
335  node->launchDebugger();
336  break;
337  case 'm':
338  mute(node->name());
339  break;
340  case 'u':
341  unmute(node->name());
342  break;
343  }
344 
345  m_selectedNode = -1;
346  }
347 }
348 
349 }
const std::vector< NodeMonitor::Ptr > & nodes() const
Definition: monitor.h:40
bool anyMuted()
Definition: ui.h:46
~UI()
Definition: ui.cpp:68
std::map< std::string, ChannelInfo > m_nodeColorMap
Definition: ui.h:74
int m_selectedNode
Definition: ui.h:76
SimpleColor
Simple colors.
Definition: terminal.h:29
bool interactive() const
Definition: terminal.h:156
bool getSize(int *columns, int *rows)
Get current window size.
Definition: terminal.cpp:380
void setupColors()
Definition: ui.cpp:73
void setSimpleBackground(SimpleColor color)
Definition: terminal.cpp:332
void cleanup()
Definition: ui.cpp:15
void moveCursorToStartOfLine()
Move cursor to start of the line.
Definition: terminal.cpp:375
FDWatcher::Ptr m_fdWatcher
Definition: ui.h:65
void setBackgroundColor(uint32_t color)
Set 24-bit background color.
Definition: terminal.cpp:257
UI(monitor::Monitor *monitor, const FDWatcher::Ptr &fdWatcher)
Definition: ui.cpp:36
void handleInput()
Definition: ui.cpp:287
ROSCPP_DECL std::string clean(const std::string &name)
monitor::Monitor * m_monitor
Definition: ui.h:64
void mute(const std::string &s)
Definition: ui.h:52
void setEcho(bool on)
Definition: terminal.cpp:293
void setSimplePair(SimpleColor fg, SimpleColor bg)
Definition: terminal.cpp:341
boost::signals2::signal< void(std::string, std::string)> logMessageSignal
Definition: monitor.h:48
ros::WallTimer m_sizeTimer
Definition: ui.h:70
Idle (e.g. exited with code 0)
Definition: node_monitor.h:33
void update()
Definition: ui.cpp:263
void log(const std::string &channel, const std::string &str)
Definition: ui.cpp:218
void HUSLtoRGB(float *r, float *g, float *b, float h, float s, float l)
Definition: husl.c:56
void clearToEndOfLine()
Clear characters from cursor to end of line.
Definition: terminal.cpp:359
static std::string g_windowTitle
Definition: ui.cpp:13
void drawStatusLine()
Definition: ui.cpp:100
Terminal m_term
Definition: ui.h:67
string b
Definition: busy_node.py:4
void setStandardColors()
Reset fg + bg to standard terminal colors.
Definition: terminal.cpp:350
void checkWindowSize()
Definition: ui.cpp:280
Waiting for automatic restart after crash.
Definition: node_monitor.h:36
bool isMuted(const std::string &s)
Definition: ui.h:49
void unmute(const std::string &s)
Definition: ui.h:55
static unsigned int g_statusLines
Definition: ui.cpp:12
void setCursorVisible()
restore cursor
Definition: terminal.cpp:236
void setSimpleForeground(SimpleColor color)
Definition: terminal.cpp:323
void unmuteAll()
Definition: ui.h:61
Encapsulates terminal control.
Definition: terminal.h:20
void moveCursorUp(int numLines)
Move cursor up by numLines.
Definition: terminal.cpp:367
bool has256Colors() const
Definition: terminal.cpp:223
void setWindowTitle(const std::string &title)
Definition: terminal.cpp:393
void setCursorInvisible()
hide cursor
Definition: terminal.cpp:228
Crashed (i.e. exited with code != 0)
Definition: node_monitor.h:35
launch::LaunchConfig::ConstPtr config() const
Definition: monitor.h:45
void muteAll()
Definition: ui.h:58
void clearWindowTitle(const std::string &backup)
Definition: terminal.cpp:406
int m_columns
Definition: ui.h:69


rosmon_core
Author(s): Max Schwarz
autogenerated on Wed Jul 10 2019 03:10:12