Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support older versions of tornado #82

Merged
merged 4 commits into from
Aug 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions rosboard/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import tornado.web
import tornado.websocket
import traceback
import types
import uuid

from . import __version__
Expand All @@ -16,6 +17,7 @@ def set_extra_headers(self, path):

class ROSBoardSocketHandler(tornado.websocket.WebSocketHandler):
sockets = set()

def initialize(self, node):
# store the instance of the ROS node that created this WebSocketHandler so we can access it later
self.node = node
Expand All @@ -32,6 +34,13 @@ def open(self):

self.set_nodelay(True)

# polyfill of is_closing() method for older versions of tornado
if not hasattr(self.ws_connection, "is_closing"):
self.ws_connection.is_closing = types.MethodType(
lambda self_: self_.stream.closed() or self_.client_terminated or self_.server_terminated,
self.ws_connection
)

self.update_intervals_by_topic = {} # this socket's throttle rate on each topic
self.last_data_times_by_topic = {} # last time this socket received data on each topic

Expand Down
8 changes: 7 additions & 1 deletion rosboard/html/js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,13 @@ let onTopics = function(topics) {
$('<a></a>')
.addClass("mdl-navigation__link")
.click(() => { initSubscribe({topicName: "_top", topicType: "rosboard_msgs/msg/ProcessList"}); })
.text("top")
.text("Processes")
.appendTo($("#topics-nav-system"));

$('<a></a>')
.addClass("mdl-navigation__link")
.click(() => { initSubscribe({topicName: "_system_stats", topicType: "rosboard_msgs/msg/SystemStats"}); })
.text("System stats")
.appendTo($("#topics-nav-system"));
}

Expand Down
10 changes: 5 additions & 5 deletions rosboard/html/js/viewers/GeometryViewer.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use strict";

class OdometryViewer extends Space2DViewer {
class GeometryViewer extends Space2DViewer {
_quatToEuler(q) {
let euler = {};

Expand Down Expand Up @@ -132,9 +132,9 @@ class OdometryViewer extends Space2DViewer {
}
}

OdometryViewer.friendlyName = "2D view";
GeometryViewer.friendlyName = "2D view";

OdometryViewer.supportedTypes = [
GeometryViewer.supportedTypes = [
"geometry_msgs/msg/Pose",
"geometry_msgs/msg/PoseStamped",
"geometry_msgs/msg/PoseWithCovariance",
Expand All @@ -145,6 +145,6 @@ OdometryViewer.supportedTypes = [
"nav_msgs/msg/Odometry",
];

OdometryViewer.maxUpdateRate = 10.0;
GeometryViewer.maxUpdateRate = 10.0;

Viewer.registerViewer(OdometryViewer);
Viewer.registerViewer(GeometryViewer);
30 changes: 30 additions & 0 deletions rosboard/rosboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from rosboard.serialization import ros2dict
from rosboard.subscribers.dmesg_subscriber import DMesgSubscriber
from rosboard.subscribers.processes_subscriber import ProcessesSubscriber
from rosboard.subscribers.system_stats_subscriber import SystemStatsSubscriber
from rosboard.subscribers.dummy_subscriber import DummySubscriber
from rosboard.handlers import ROSBoardSocketHandler, NoCacheStaticFileHandler

Expand Down Expand Up @@ -181,6 +182,12 @@ def sync_subs(self):
self.local_subs[topic_name] = DMesgSubscriber(self.on_dmesg)
continue

if topic_name == "_system_stats":
if topic_name not in self.local_subs:
rospy.loginfo("Subscribing to _system_stats [non-ros]")
self.local_subs[topic_name] = SystemStatsSubscriber(self.on_system_stats)
continue

if topic_name == "_top":
if topic_name not in self.local_subs:
rospy.loginfo("Subscribing to _top [non-ros]")
Expand Down Expand Up @@ -240,6 +247,29 @@ def sync_subs(self):

self.lock.release()

def on_system_stats(self, system_stats):
"""
system stats received. send it off to the client as a "fake" ROS message (which could at some point be a real ROS message)
"""
if self.event_loop is None:
return

msg_dict = {
"_topic_name": "_system_stats", # special non-ros topics start with _
"_topic_type": "rosboard_msgs/msg/SystemStats",
}

for key, value in system_stats.items():
msg_dict[key] = value

self.event_loop.add_callback(
ROSBoardSocketHandler.broadcast,
[
ROSBoardSocketHandler.MSG_MSG,
msg_dict
]
)

def on_top(self, processes):
"""
processes list received. send it off to the client as a "fake" ROS message (which could at some point be a real ROS message)
Expand Down
75 changes: 47 additions & 28 deletions rosboard/subscribers/system_stats_subscriber.py
Original file line number Diff line number Diff line change
@@ -1,50 +1,69 @@
#!/usr/bin/env python3

import psutil
import select
import subprocess
try:
import psutil
except (ImportError, ModuleNotFoundError) as e:
psutil = None

import time
import threading
import traceback

def mean(list):
return sum(list)/len(list)

class SystemStatsSubscriber(object):
def __init__(self, callback):
self.callback = callback
self.process = None
self.stop = False
threading.Thread(target = self.start, daemon = True).start()

def __del__(self):
if self.process:
self.process.terminate()
self.process = None
self.stop = True
pass

def unregister(self):
if self.process:
self.process.terminate()
self.process = None
self.stop = True
pass

def start(self):
try:
self.process = subprocess.Popen(['dmesg', '--follow'], stdout = subprocess.PIPE)
p = select.poll()
p.register(self.process.stdout, select.POLLIN)

while True:
time.sleep(0.1)

if self.process is None:
break
if psutil is None:
self.callback({"_error": "Please install psutil (sudo pip3 install --upgrade psutil) to use this feature."})
return

while not self.stop:
try:
p = psutil.Process()

with p.oneshot():
sensors_temperatures = psutil.sensors_temperatures()
cpu_percent = psutil.cpu_percent(percpu = True)
net_io_counters = psutil.net_io_counters()
virtual_memory = psutil.virtual_memory()
swap_memory = psutil.swap_memory()
disk_usage = psutil.disk_usage('/')

status = {}

status["cpu_percent"] = cpu_percent

if "coretemp" in sensors_temperatures:
status["temp_coretemp"] = mean(list(map(
lambda x:x.current,
sensors_temperatures["coretemp"]
)))

lines = []
while p.poll(1):
lines.append(self.process.stdout.readline().decode('utf-8').strip())
status["net_bytes_sent"] = net_io_counters.bytes_sent
status["net_bytes_recv"] = net_io_counters.bytes_recv
status["disk_usage_percent"] = disk_usage.percent
status["virtual_memory_percent"] = virtual_memory.percent
status["swap_memory_percent"] = swap_memory.percent

text = "\n".join(lines)
if len(text) > 0:
self.callback("\n".join(lines))
except:
traceback.print_exc()
except Exception as e:
traceback.print_exc()

self.callback(status)
time.sleep(3)

if __name__ == "__main__":
# Run test
Expand Down