curses_display.py 8.47 KB
Newer Older
Mario Hock's avatar
Mario Hock committed
1
2
3
4
5
6
7
8
9
10
11
# -*- coding:utf-8 -*-

'''
Curses display for »cpunetlog«.

This code is inspired and partially copied from »bwtop« written by
"Mahmoud Adel <mahmoud.adel2@gmail.com>" and distributed under the MIT license.
'''

import curses
import time
Mario Hock's avatar
Mario Hock committed
12
import helpers
Mario Hock's avatar
Mario Hock committed
13
14
15
16
17
18
19
20

## XXX disable colors
disablecolorskipped = True

stdscr = None

## some "constants"/preferences
nics = None
Mario Hock's avatar
Mario Hock committed
21
nic_speeds = None
Mario Hock's avatar
Mario Hock committed
22
divisor = 1000000.0
Mario Hock's avatar
Mario Hock committed
23
rounding_digits = 2
Mario Hock's avatar
Mario Hock committed
24
unit = "MBits"
Mario Hock's avatar
Mario Hock committed
25

26
LOGGING_STATE_COLORS = {"Active": 3, "(Active)": 2, "Disabled": 1, "Standby": 4, "Enabled": 3}
Mario Hock's avatar
Mario Hock committed
27
28
29
30
31


## Reference to the logging manager, to display its state.
logging_manager = None

Mario Hock's avatar
Mario Hock committed
32
## GUI, positions of fields
Mario Hock's avatar
Mario Hock committed
33
34
35
36
LABEL_Sent = 18
LABEL_Received = 48
LABEL_CPU_UTIL = LABEL_Sent
LABEL_CPU_USER = LABEL_Received
Mario Hock's avatar
Mario Hock committed
37
LABEL_CPU_SYSTEM = 64
Mario Hock's avatar
Mario Hock committed
38

Mario Hock's avatar
Mario Hock committed
39

Mario Hock's avatar
..    
Mario Hock committed
40
## TODO ideas..
Mario Hock's avatar
Mario Hock committed
41
42
43
44
#   - Add an option to set a fixed max. net-speed manually (for comparison)
#       --> Maybe change colors, when speed higher than this max
#           (and switch to a scale with the real max. value for the nic.)
#
Mario Hock's avatar
..    
Mario Hock committed
45
#   - total (GB transferred)
Mario Hock's avatar
..    
Mario Hock committed
46
#   - other (CPU usage)  -- maybe compacter design for user/sys/other
Mario Hock's avatar
..    
Mario Hock committed
47
48
#   - "Total" field for CPU usage (as well)
#   - Also draw a bar for the totals?
Mario Hock's avatar
..    
Mario Hock committed
49
50
51

## TODO idea: smoothing..?

Mario Hock's avatar
Mario Hock committed
52

Mario Hock's avatar
Mario Hock committed
53

Mario Hock's avatar
Mario Hock committed
54
55
56
57
def _format_net_speed(speed):
    return str( round(speed / divisor, rounding_digits) )


Mario Hock's avatar
Mario Hock committed
58
59
60
61
def _display_cpu_bar(y, x, cpu):
    # Constants
    CPU_BAR_COLORS = ( curses.color_pair(3) | curses.A_REVERSE,    # user
                       curses.color_pair(4) | curses.A_REVERSE,    # system
62
                       curses.color_pair(5) | curses.A_REVERSE,    # softirq
Mario Hock's avatar
Mario Hock committed
63
                       curses.color_pair(6) | curses.A_REVERSE,    # other
Mario Hock's avatar
Mario Hock committed
64
65
66
67
                       curses.color_pair(3) )                      # idle

    # Calculate proportions
    cpu_util = 100-cpu.idle
68
69
    other = 100 - sum( (cpu.user, cpu.system, cpu.softirq, cpu.idle) )
    proportions = [cpu.user, cpu.system, cpu.softirq, other, cpu.idle]
Mario Hock's avatar
Mario Hock committed
70
71
72
73
74
75
76
77
78
79
80
81

    # Prepare text.
    text = '{0:.2%}'.format((cpu_util)/100.0)
    split_text = helpers.split_proprtionally(text, proportions, 20)

    # Write text on screen (curses).
    stdscr.move(y,x)
    for s, options in zip(split_text, CPU_BAR_COLORS):
        stdscr.addstr(s, options)



Mario Hock's avatar
Mario Hock committed
82
83
84
85
86
87
88
89
90
91
def _display_logging_state(y, x):
    if ( not logging_manager ):
        stdscr.addstr(y, x, 'Disabled', curses.A_BOLD)

    else:
        state = logging_manager.get_logging_state()
        color = LOGGING_STATE_COLORS[state]

        stdscr.addstr(y, x, state, curses.A_BOLD | curses.color_pair(color))

Mario Hock's avatar
Mario Hock committed
92
93
94

def init():
    global stdscr
Mario Hock's avatar
Mario Hock committed
95
96
97
98
    global nic_speeds

    if not nic_speeds:
        nic_speeds = helpers.get_nic_speeds()
Mario Hock's avatar
Mario Hock committed
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114

    stdscr = curses.initscr()
    curses.noecho()
    curses.cbreak()
    stdscr.keypad(True)
    curses.curs_set(False)
    stdscr.nodelay(True)

    curses.start_color()
    curses.use_default_colors()
    if not disablecolorskipped:
        curses.init_pair(1, -1, -1)
        curses.init_pair(2, -1, -1)
        curses.init_pair(3, -1, -1)
        curses.init_pair(4, -1, -1)
    else:
Mario Hock's avatar
Mario Hock committed
115
116
117
118
119
120
        curses.init_pair(1, curses.COLOR_MAGENTA, -1)         # "CPUX", "ethX"
        curses.init_pair(2, curses.COLOR_BLUE, -1)            # "util", "sent", "received", ...
        curses.init_pair(3, curses.COLOR_GREEN, -1)           # <values>
        curses.init_pair(4, curses.COLOR_YELLOW, -1)          # "Total", (<cpu-system> ?)
        curses.init_pair(5, curses.COLOR_CYAN, -1)            # <cpu-system> / <cpu-other>
        curses.init_pair(6, curses.COLOR_WHITE, -1)           # <cpu-system> / <cpu-other>
Mario Hock's avatar
Mario Hock committed
121
        curses.init_pair(7, curses.COLOR_RED, -1)
Mario Hock's avatar
Mario Hock committed
122
123
124
125
126
127
128
129
130
131

    ## Show some output to avoid upsetting the user
    stdscr.addstr(3, 3, "loading ...", curses.A_BOLD)
    stdscr.refresh()



def display(measurement):
    global stdscr

Mario Hock's avatar
Mario Hock committed
132
    ## Press 'q' to quit.
Mario Hock's avatar
Mario Hock committed
133
134
135
136
    pressedkey = stdscr.getch()
    if pressedkey == ord('q'):
        return False

Mario Hock's avatar
Mario Hock committed
137
    ## Header
Mario Hock's avatar
Mario Hock committed
138
139
140
    stdscr.clear()
    stdscr.border(0)
    timenow = time.strftime("%H:%M:%S")
Mario Hock's avatar
Mario Hock committed
141
    stdscr.addstr(1, 1, 'CPUnetLOG', curses.A_BOLD)
Mario Hock's avatar
Mario Hock committed
142
143
144
145
    stdscr.addstr(1, LABEL_Sent, 'Time: {}'.format( timenow ), curses.A_BOLD)
    stdscr.addstr(1, 39, 'Interval: {}s'.format( round(measurement.timespan, 1) ), curses.A_BOLD)
    stdscr.addstr(1, 62, 'Logging: ', curses.A_BOLD)
    _display_logging_state(1, 71)
Mario Hock's avatar
Mario Hock committed
146
147
    stdscr.refresh()

Mario Hock's avatar
Mario Hock committed
148
149
150
151
152
    y = 3

    ## CPU ##
    num=1
    for cpu in measurement.cpu_times_percent:
Mario Hock's avatar
Mario Hock committed
153
        # static labels
Mario Hock's avatar
Mario Hock committed
154
        stdscr.addstr(y, 1, 'CPU{0}'.format( num ), curses.color_pair(1))
Mario Hock's avatar
Mario Hock committed
155
        stdscr.addstr(y, LABEL_CPU_UTIL, 'util: ', curses.color_pair(2))
Mario Hock's avatar
Mario Hock committed
156
        stdscr.addstr(y, LABEL_CPU_UTIL+26, '|', curses.color_pair(2))
157
158
        #stdscr.addstr(y, LABEL_CPU_USER, 'type: ', curses.color_pair(2))
        #stdscr.addstr(y, LABEL_CPU_SYSTEM, 'system: ', curses.color_pair(2))
Mario Hock's avatar
Mario Hock committed
159
160

        # CPU bar
Mario Hock's avatar
Mario Hock committed
161
        _display_cpu_bar( y, LABEL_CPU_UTIL+6, cpu )
Mario Hock's avatar
Mario Hock committed
162
163

        # user/system
164
165
166
167
168
169
170
171
        #stdscr.addstr(y, LABEL_CPU_USER+6, '{0:.2%}'.format( cpu.user/100.0 ), curses.color_pair(3))
        #stdscr.addstr(y, LABEL_CPU_SYSTEM+8, '{0:.2%}'.format( cpu.system/100.0 ), curses.color_pair(3))
        cpu_sorted = helpers.sort_named_tuple(cpu)
        stdscr.addstr(y, LABEL_CPU_USER, cpu_sorted[0][0] + ": ", curses.color_pair(4))
        stdscr.addstr("{:.2%}".format(cpu_sorted[0][1]/100), curses.color_pair(3))

        stdscr.addstr(y, LABEL_CPU_SYSTEM, cpu_sorted[1][0] + ": ", curses.color_pair(4))
        stdscr.addstr("{:.2%}".format(cpu_sorted[1][1]/100), curses.color_pair(3))
Mario Hock's avatar
Mario Hock committed
172
173
174
175
176
177
178
179
180
181

        num += 1
        y += 1




    ## Network ##

    y += 1
Mario Hock's avatar
Mario Hock committed
182
    stdscr.hline(y, 1, "-", 78)
Mario Hock's avatar
Mario Hock committed
183
184
    y += 1

Mario Hock's avatar
Mario Hock committed
185
186
187
188
189
190
191
192
193
194
    # display all nics (if not set otherwise)
    if nics:
        active_nics = nics
    else:
        active_nics = measurement.net_io.keys()

    sum_sending = 0
    sum_receiving = 0

    ## display the values
Mario Hock's avatar
Mario Hock committed
195
    for nic in sorted(active_nics):
Mario Hock's avatar
Mario Hock committed
196
197
        values = measurement.net_io[nic]

Mario Hock's avatar
Mario Hock committed
198
199
        _send = values.ratio["bytes_sent"] * 8  # Bits/s
        _recv = values.ratio["bytes_recv"] * 8  # Bits/s
Mario Hock's avatar
Mario Hock committed
200
        sending = _format_net_speed( _send )
Mario Hock's avatar
Mario Hock committed
201
        send_ratio = _send/nic_speeds[nic]
Mario Hock's avatar
Mario Hock committed
202
        receiving = _format_net_speed( _recv )
Mario Hock's avatar
Mario Hock committed
203
        receive_ratio = _recv/nic_speeds[nic]
Mario Hock's avatar
Mario Hock committed
204
205
206

        sum_sending += _send
        sum_receiving += _recv
Mario Hock's avatar
Mario Hock committed
207
208

        stdscr.addstr(y, 1, '{0}'.format(nic), curses.color_pair(1))
Mario Hock's avatar
Mario Hock committed
209
        stdscr.addstr(y, LABEL_Sent, 'Sent: ', curses.color_pair(2))
Mario Hock's avatar
Mario Hock committed
210
        stdscr.addstr(y, LABEL_Sent+26, "|", curses.color_pair(2))
Mario Hock's avatar
Mario Hock committed
211
        stdscr.addstr(y, LABEL_Received, 'Received: ', curses.color_pair(2))
Mario Hock's avatar
Mario Hock committed
212
        stdscr.addstr(y, LABEL_Received+30, "|", curses.color_pair(2))
Mario Hock's avatar
Mario Hock committed
213

Mario Hock's avatar
Mario Hock committed
214
        ## TODO rewrite in nice ^^ [see _display_cpu_bar()]
Mario Hock's avatar
Mario Hock committed
215
216
217
218
        ## XXX prototypical "inline"-coloring
        _snd_str = '{0} {1}/s'.format(sending, unit, send_ratio)
        _snd_str += " " * (20-len(_snd_str))
        _load_len = int(send_ratio * 20)
Mario Hock's avatar
Mario Hock committed
219
220
        stdscr.addstr(y, LABEL_Sent+6, _snd_str[0:_load_len], curses.color_pair(3)|curses.A_REVERSE)
        stdscr.addstr(y, LABEL_Sent+6+_load_len, _snd_str[_load_len:], curses.color_pair(3))
Mario Hock's avatar
Mario Hock committed
221
222
223
224

        _recv_str = '{0} {1}/s'.format(receiving, unit, send_ratio)
        _recv_str += " " * (20-len(_recv_str))
        _load_len = int(receive_ratio * 20)
Mario Hock's avatar
Mario Hock committed
225
226
        stdscr.addstr(y, LABEL_Received+10, _recv_str[0:_load_len], curses.color_pair(3)|curses.A_REVERSE)
        stdscr.addstr(y, LABEL_Received+10+_load_len, _recv_str[_load_len:], curses.color_pair(3))
Mario Hock's avatar
Mario Hock committed
227

Mario Hock's avatar
Mario Hock committed
228
        ## XXX TESTING
Mario Hock's avatar
Mario Hock committed
229
230
231
232
233
        #y += 1
        #stdscr.addstr(y, 25, "|" + " "*20 + "|", curses.color_pair(2))
        #stdscr.addstr(y, 26, " " * int(send_ratio * 20), curses.color_pair(3)|curses.A_REVERSE)
        #stdscr.addstr(y, 59, "|" + " "*20 + "|", curses.color_pair(2))
        #stdscr.addstr(y, 60, " " * int(receive_ratio * 20), curses.color_pair(3)|curses.A_REVERSE)
Mario Hock's avatar
Mario Hock committed
234

Mario Hock's avatar
Mario Hock committed
235
236
237
238
239
        y += 1

    ## Total
    y+=1
    stdscr.addstr(y, 1, 'Total:', curses.color_pair(4))
Mario Hock's avatar
Mario Hock committed
240
241
242
243
    stdscr.addstr(y, LABEL_Sent, 'Sent:', curses.color_pair(2))
    stdscr.addstr(y, LABEL_Sent+6, '{0} {1}/s'.format(_format_net_speed(sum_sending), unit), curses.color_pair(3))
    stdscr.addstr(y, LABEL_Received, 'Received:', curses.color_pair(2))
    stdscr.addstr(y, LABEL_Received+10, '{0} {1}/s'.format(_format_net_speed(sum_receiving),unit), curses.color_pair(3))
Mario Hock's avatar
Mario Hock committed
244

Mario Hock's avatar
Mario Hock committed
245
246
247
248
249
250
251
252
253


    ## Show logging comment
    comment = logging_manager.get_logging_comment()
    if ( comment ):
        y += 3
        stdscr.addstr(y, 3, 'Comment: ', curses.A_BOLD)
        stdscr.addstr(y, 3+9, comment)

Mario Hock's avatar
Mario Hock committed
254
255
256
257
258
259
260
261
262
263
264
265
266
    stdscr.refresh()

    return True


def close():
    global stdscr

    curses.nocbreak()
    stdscr.keypad(False)
    curses.echo()
    curses.curs_set(True)
    curses.endwin()