modified: mmgen/node_tools/Sound.py

modified:   mmgen/node_tools/Util.py
	modified:   mmnode-netrate
	modified:   mmnode-peerblocks
	modified:   mmnode-play-sound
This commit is contained in:
The MMGen Project 2020-05-12 16:40:08 +00:00
commit 9f801a9cb3
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
5 changed files with 138 additions and 95 deletions

View file

@ -42,22 +42,22 @@ def parse_repeat_spec(rs):
def init_sound():
def _restore_sound():
# msg('Restoring sound volume')
do_system('alsactl restore -f ' + _alsa_config_file)
do_system('sudo alsactl restore -f ' + _alsa_config_file)
os.unlink(_alsa_config_file)
import atexit
atexit.register(_restore_sound)
do_system('alsactl store -f ' + _alsa_config_file)
do_system('sudo alsactl store -f ' + _alsa_config_file)
def play_sound(fn,vol,repeat_spec='',remote_host='',kill_flg=None,testing=False):
if not remote_host:
do_system('alsactl store -f ' + _alsa_config_file)
do_system('sudo alsactl store -f ' + _alsa_config_file)
for k in 'Master','Speaker','Headphone':
do_system(('amixer -q set %s on' % k),testing)
do_system(('sudo amixer -q set %s on' % k),testing)
# do_system('amixer -q set Headphone off')
vols = dict([(k,int(_dvols[k] * float(vol) / 100)) for k in _dvols])
for k in vols:
do_system('amixer -q set %s %s' % (k,vols[k]),testing)
do_system('sudo amixer -q set %s %s' % (k,vols[k]),testing)
fn = os.path.expanduser(fn)
cmd = (
@ -72,9 +72,9 @@ def play_sound(fn,vol,repeat_spec='',remote_host='',kill_flg=None,testing=False)
do_system(cmd,testing)
if kill_flg.wait(interval):
if not remote_host:
do_system('alsactl restore -f ' + _alsa_config_file)
do_system('sudo alsactl restore -f ' + _alsa_config_file)
return
else: # Play once
do_system(cmd,testing)
if not remote_host:
do_system('alsactl restore -f ' + _alsa_config_file)
do_system('sudo alsactl restore -f ' + _alsa_config_file)

View file

@ -20,6 +20,7 @@ mmgen.node_tools.Util: utility functions for MMGen node tools
"""
import time,subprocess
from mmgen.util import msg
def get_hms(t=None,utc=False,no_secs=False):
secs = t or time.time()

View file

@ -1,7 +1,7 @@
#!/usr/bin/env python3
#
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
# Copyright (C)2013-2017 Philemon <mmgen-py@yandex.com>
# Copyright (C)2013-2020 The MMGen Project <mmgen@tuta.io>
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
@ -37,24 +37,33 @@ opts_data = {
cmd_args = opts.init(opts_data)
ERASE_LINE,CUR_UP = '\033[K','\033[1A'
c = rpc_init()
def do_loop():
def get_data():
d = c.getnettotals()
async def main():
from mmgen.rpc import rpc_init
c = await rpc_init()
async def get_data():
d = await c.call('getnettotals')
return [float(e) for e in (d['totalbytesrecv'],d['totalbytessent'],d['timemillis'])]
r,s,t = get_data()
time.sleep(0.2)
rs = None
while True:
rs,ss,ts = r,s,t
r,s,t = get_data()
td = t-ts
sys.stderr.write('\rrcvd: {:9.2f} kB/s\nsent: {:9.2f} kB/s '.format((r-rs)/td,(s-ss)/td))
r,s,t = await get_data()
if rs is not None:
sys.stderr.write(
'\rrcvd: {:9.2f} kB/s\nsent: {:9.2f} kB/s '.format(
(r-rs)/(t-ts),
(s-ss)/(t-ts) ))
time.sleep(2)
sys.stderr.write('{}{}{}'.format(ERASE_LINE,CUR_UP,ERASE_LINE))
if rs is not None:
sys.stderr.write('{}{}{}'.format(ERASE_LINE,CUR_UP,ERASE_LINE))
rs,ss,ts = r,s,t
try:
do_loop()
run_session(main(),do_rpc_init=False)
except KeyboardInterrupt:
sys.stderr.write('\n')

View file

@ -1,7 +1,7 @@
#!/usr/bin/env python3
#
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
# Copyright (C)2013-2017 Philemon <mmgen-py@yandex.com>
# Copyright (C)2013-2020 The MMGen Project <mmgen@tuta.io>
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
@ -20,7 +20,7 @@
mmgen-peerblocks: List blocks in flight, disconnect stalling nodes
"""
import time,threading
import asyncio
from mmgen.common import *
opts_data = {
@ -34,85 +34,119 @@ opts_data = {
}
}
cmd_args = opts.init(opts_data)
opts.init(opts_data)
colors = ['\033[38;5;%s;1m' % c for c in (238,240,242,244,246,247,249,251,253,255)]
_red,_reset = '\033[31m','\033[0m'
async def inflight_display(rpc):
ERASE_ALL,ERASE_LINE,CUR_HOME,CUR_HIDE,CUR_SHOW = \
'\033[J','\033[K','\033[H','\033[?25l','\033[?25h'
def gen_peers(peerinfo):
global min_height
min_height = None
for d in peerinfo:
if 'inflight' in d and d['inflight']:
blks = d['inflight']
if not min_height or min_height > blks[0]:
min_height = blks[0]
blks_trunc = ' '.join(map(str,blks))[:term_width-6].split()
trim = blks_trunc[-1] != str(blks[len(blks_trunc)-1])
blks = blks[:len(blks_trunc)-trim]
else:
blks = []
yield { 'id': d['id'], 'data': blks }
import atexit
def at_exit():
import os
os.system('stty sane')
sys.stderr.write('\n')
atexit.register(at_exit)
def gen_line(peer):
for blk in peer['data']:
if blk == min_height:
yield RED + str(blk) + RESET
else:
yield COLORS[blk % 10] + str(blk) + RESET
bc = rpc_init()
msg_r(CUR_HOME+ERASE_ALL)
def do_display():
from mmgen.term import get_terminal_size
global data
term_width = get_terminal_size()[0]
count = 1
while True:
twid = get_terminal_size()[0]
data = bc.getpeerinfo()
min_t = None
lines = []
with lock:
msg('{}{}{}ACTIVE PEERS ({}) - poll {}'.format(
CUR_HOME,ERASE_ALL,CUR_HOME,len(data),count))
for d in data:
line = { 'id': d['id'], 'data': [] }
if 'inflight' in d and d['inflight']:
blks = [str(e) for e in d['inflight']]
min_p = min(e for e in d['inflight'])
if not min_t or min_t > min_p: min_t = min_p
line_d = ' '.join(blks)[:twid-6]
blks = blks[:len(line_d) - len(line_d.replace(' ','')) + 1]
blks[-1] = blks[-1][:len(line_d.split(' ')[-1])]
line['data'] = [[colors[int(i)%10],i,_reset] for i in blks if i]
else:
line['data'] = []
lines.append(line)
for line in lines:
d = ' '.join([(a,_red)[int(b)==min_t]+b+c for a,b,c in line['data']])
sys.stderr.write('\r{} {:>3}: {}\n'.format(ERASE_LINE,line['id'],d))
msg_r(ERASE_ALL+'Hit ENTER for disconnect prompt: ')
time.sleep(2)
info = await rpc.call('getpeerinfo')
msg_r(CUR_HOME+ERASE_ALL+CUR_HOME)
msg(f'ACTIVE PEERS ({len(info)}) - poll {count}')
for peer in gen_peers(info):
sys.stderr.write('\r{} {:>3}: {}\n'.format(
ERASE_LINE,
peer['id'],
' '.join(gen_line(peer)) ))
msg_r(ERASE_ALL+'Hit ENTER for disconnect prompt: ')
await asyncio.sleep(2)
count += 1
lock = threading.Lock()
data = {}
async def do_inflight(rpc):
task = asyncio.ensure_future(inflight_display(rpc)) # Python 3.7+: create_task()
from select import select
t = threading.Thread(target=do_display,name='display')
t.daemon = True
t.start()
def do_loop():
global data
while True:
input()
with lock:
ids = [str(d['id']) for d in data]
msg('{}{}{}ACTIVE PEERS ({})'.format(CUR_HOME,ERASE_ALL,CUR_HOME,len(data)))
msg(' '+'\n '.join(['{:>3}: {:30} {}'.format(*[d[k] for k in ('id','addr','subver')]) for d in data]))
reply = input('Enter a peer number to disconnect> ')
if reply == '':
pass
elif reply in ids:
idx = ids.index(reply)
msg("Disconnecting peer {} ('{}')".format(reply,data[idx]['addr']))
bc.disconnectnode(data[idx]['addr'])
time.sleep(1.5)
else:
msg("'{}': invalid peer number".format(reply))
time.sleep(0.5)
key = select([sys.stdin], [], [], 0.1)[0]
if key:
sys.stdin.read(1)
task.cancel()
break
await asyncio.sleep(0.1)
try:
await task
except asyncio.CancelledError:
pass
async def do_disconnect_menu(rpc):
while True:
peerinfo = await rpc.call('getpeerinfo')
ids = [str(d['id']) for d in peerinfo]
msg_r(CUR_HOME+ERASE_ALL+CUR_HOME)
msg(f'ACTIVE PEERS ({len(peerinfo)})')
if peerinfo:
msg('\n'.join([f"{d['id']:>3}: {d['addr']:30} {d['subver']}" for d in peerinfo]))
reply = input("Peer number to disconnect, ENTER to quit menu, 'u' to update peer list> ")
if reply == '':
return
elif reply == 'u':
msg(f'Updating peer list')
await asyncio.sleep(0.5)
elif reply in ids:
addr = peerinfo[ids.index(reply)]['addr']
msg(f'Disconnecting peer {reply} ({addr})')
try:
await rpc.call('disconnectnode',addr)
except RPCFailure:
msg(f'Unable to disconnect peer {addr}')
await asyncio.sleep(1.5)
else:
msg(f'{reply!r}: invalid peer number')
await asyncio.sleep(0.5)
async def main():
msg_r(CUR_HOME+ERASE_ALL)
from mmgen.rpc import rpc_init
rpc = await rpc_init()
while True:
await do_inflight(rpc)
await do_disconnect_menu(rpc)
RED,RESET = ('\033[31m','\033[0m')
COLORS = ['\033[38;5;%s;1m' % c for c in (238,240,242,244,246,247,249,251,253,255)]
ERASE_ALL,ERASE_LINE,CUR_HOME,CUR_HIDE,CUR_SHOW = (
'\033[J','\033[K','\033[H','\033[?25l','\033[?25h')
try:
do_loop()
except KeyboardInterrupt:
pass
run_session(main(),do_rpc_init=False)
except:
from subprocess import run
run(['stty','sane'])
msg('')

View file

@ -1,7 +1,7 @@
#!/usr/bin/env python3
#
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
# Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
# Copyright (C)2013-2020 The MMGen Project <mmgen@tuta.io>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -32,6 +32,7 @@ opts_data = {
'usage': '[opts]',
'options': """
-h, --help Print this help message
-t, --testing Test, don't execute shell commands
-v, --volume= n Adjust sound volume by percentage 'n' (default: {})
""".format(volume)
}
@ -40,10 +41,8 @@ opts_data = {
args = opts.init(opts_data)
if opt.volume:
volume = opt.volume
try:
volume = int(volume)
assert 1 <= volume <= 120
assert 1 <= int(opt.volume) <= 120
except:
die(1,'Sound volume must be an integer between 1 and 120')
@ -53,4 +52,4 @@ if len(args) != 1:
try: os.stat(args[0])
except: die(1,"Couldn't stat file '{}'".format(args[0]))
play_sound(args[0],volume)
play_sound(args[0],int(opt.volume),testing=opt.testing)