tassello 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,17 @@ change the plugin API (`tassello/sdk`, `tassello/server`, `tassello/vite`), as n
5
5
 
6
6
  ## Unreleased
7
7
 
8
+ - System Monitor widget: the robot's computer, every second. The CPU's load, of each core (those
9
+ offline too), their frequencies and the load average; the memory, its cache and the swap; the
10
+ temperatures of the thermal zones and hwmon chips, by chip with their hottest, against their
11
+ own limits; the fans; a Raspberry Pi's throttling (under-voltage, frequency capped, throttled,
12
+ soft temperature limit, now and since boot); the GPUs: a Jetson's (devfreq), NVIDIA's
13
+ (`nvidia-smi`), AMD's (amdgpu); the power rails: a Jetson's INA3221 (L4T 32's too), hwmon's
14
+ power sensors, Intel's RAPL when readable; the disks' traffic and busy time, and the file
15
+ systems' space; the networks' traffic, of the device's own interfaces or those chosen. Charts
16
+ of the last 5 minutes, kept by the server, with the values at the pointer (or the arrow keys).
17
+ The board, its OS, uptime, and a Jetson's L4T release and power mode.
18
+ - Plugins: `connection.subscribeSystem`, and the types of `src/shared/system.ts`.
8
19
  - Rosbags widget: the robot's rosbags, as `ros2 bag` makes and reads them. A folder's bags, the
9
20
  newest first, with their time span, size, storage and compression, and each topic's type,
10
21
  count and rate, from their `metadata.yaml` (Humble's and Jazzy's), else from their files (a
package/README.md CHANGED
@@ -13,8 +13,9 @@ and plugins add more.
13
13
  images, command buttons, a systemd application manager, a behavior tree inspector and
14
14
  editor, the ROS log of every node, the robot's diagnostics, its topics, services and actions
15
15
  as the `ros2` command line shows them, its nodes' parameters, its rosbags (recorded, listed,
16
- downloaded and played back), a file manager, a terminal, and its networks: Wi-Fi
17
- joined, connections switched, the robot as an access point.
16
+ downloaded and played back), a file manager, a terminal, its networks (Wi-Fi
17
+ joined, connections switched, the robot as an access point), and its computer: CPU, memory,
18
+ temperatures, a Raspberry Pi's throttling, a Jetson's GPU and power, disks and networks.
18
19
  - **Plugins**: widgets of your own, with server-side code if they need it, as npm or ROS packages.
19
20
  - **One process**: a ROS 2 node in Node.js. Nothing to install on the robot but Node.js.
20
21
 
@@ -136,6 +137,7 @@ after the others on the devices already arranged.
136
137
  | **Rosbags** | The robot's rosbags, as `ros2 bag` makes and reads them: those of a folder, the newest first, with when, how long and how big, their storage and compression, each topic's type, count and rate (`ros2 bag info`); downloaded, deleted, or played on the robot's topics (`ros2 bag play`: paused, sought, sped up, looped, some topics only). New ones recorded (`ros2 bag record`): topics chosen from the graph, all of them, or matching an expression, with rosbag2's options: the storage (MCAP, SQLite) and its preset, a new file by size or duration, compression of each file or message. Every page sees the recordings and the playback, the behavior tree inspectors' too |
137
138
  | **File Manager** | The robot's files: folders unfolded in place, places (home, the ROS workspaces, bookmarks) and the disks' free space, an entry's details and permissions, what takes a folder's space. Make, rename, cut, copy, paste, drag onto folders, delete, download (folders as `.tar.gz`) and upload (drop files and folders onto it). Text files edited in tabs as gedit does: highlighting, search and replace, undo; images and PGM maps shown |
138
139
  | **Terminal** | A shell on the robot, or a command (`htop`, `tmux attach`), in a full terminal: colours, full screen programs, the mouse. It outlives the page: reloaded, reconnected or shown again, it is as it was. On touch screens, the keys phones lack, as Termux has them: Esc, Tab, Ctrl, Alt, Home, End, the arrows, Page Up and Down, and while typing the terminal fills the screen above the on-screen keyboard |
140
+ | **System Monitor** | The robot's computer, as top, sensors and a Jetson's jtop show it: the CPU's load, of each core, and their frequencies; the memory and swap; the temperatures by chip, and the fans; a Raspberry Pi's throttling (under-voltage, capped frequency, now and since boot); the GPUs (a Jetson's, NVIDIA's, AMD's) and the power rails (a Jetson's); the disks' traffic and space; the networks' traffic. With charts of the last 5 minutes |
139
141
  | **Networking** | The robot's networks, through NetworkManager, as a phone's settings show them: the Wi-Fi networks in range, joined with their password; the connections saved, switched between, edited (their addresses by DHCP or static), forgotten; the robot as an access point, its network shared by a QR code; the interfaces, their state and addresses. Asks before a change that would cut the page off from the robot |
140
142
 
141
143
  Their properties (topics, frames, speeds, colours, ...) are edited in the side panel of edit mode.
@@ -466,6 +468,21 @@ Notes on some of them:
466
468
  Without it, the widget says the networks cannot be changed. *Share…* reads the passwords with
467
469
  `nmcli --show-secrets`, which needs the same right; those a desktop's keyring keeps cannot be
468
470
  read.
471
+ - **System Monitor** reads `/proc` and `/sys` every second, for every page at once, while one
472
+ shows it, and keeps the last 5 minutes for the charts (a page opened later has them at once).
473
+ The temperatures are the kernel's thermal zones and its hwmon chips' sensors (the chips that
474
+ are a thermal zone again, once), grouped by chip with the hottest of each; a sensor is hot from
475
+ its own limits (else 80 °C), and critical at its own critical (else 95 °C). A **Raspberry Pi**'s
476
+ throttling is its firmware's (`get_throttled` in `/sys`, else `vcgencmd get_throttled`): under-
477
+ voltage, frequency capped, throttled, soft temperature limit, now and since boot. A **Jetson**'s
478
+ GPU is devfreq's (its load and clock), its power the INA3221's rails (`VDD_IN` the whole
479
+ board's), its L4T release and power mode (`nvpmodel -q`) are shown with the board. NVIDIA's
480
+ GPUs are read with `nvidia-smi` every 2 s, AMD's from amdgpu's files; an Intel CPU's package
481
+ power from RAPL, if its counters are readable (root's only, since Linux 5.10). Cores taken
482
+ offline (a Jetson's power modes) show as such. The disks are the whole ones (not partitions,
483
+ loop devices, device mapper or RAID), their space the File Manager's; the networks are the
484
+ device's own interfaces (not docker's bridges, VPN clients' tunnels: their traffic is the
485
+ devices' too), or those of its *interfaces* (`wlan0, tun*`). Each section can be hidden.
469
486
 
470
487
  ## Plugins
471
488
 
@@ -517,6 +534,7 @@ own pages only too: those whose origin is the host they reached (the `Host` head
517
534
  ├─ python3: the terminals' pseudo-terminals
518
535
  ├─ nmcli: the networks (NetworkManager)
519
536
  ├─ ros2 bag: rosbags recorded and played
537
+ ├─ /proc, /sys, nvidia-smi: the system monitor
520
538
  └─ the plugins' server sides
521
539
  ```
522
540
 
@@ -554,6 +572,9 @@ Safety and bandwidth are the server's concern:
554
572
  one recording per folder, for every page (the behavior tree inspectors' among them), one
555
573
  playback. A folder of bags is read once for every page showing it, every 2 s, a bag's metadata
556
574
  again only when its files change; the recordings' sizes every second.
575
+ - The system monitor's sensors are read once for every page, every second while one watches and
576
+ for 30 s after the last leaves; they are looked for again every 30 s (USB ones come and go).
577
+ Each page gets a sample a second, about 4 KB.
557
578
  - The File Manager's folders are read again every 3 s, and answered "unchanged" while they are;
558
579
  the largest are listed in part (5,000 entries), and only the rows in sight are drawn. Uploads
559
580
  and downloads are streamed, folders archived by `tar` as they are sent; saves replace files at
@@ -33,6 +33,7 @@ import { Rosbags } from './rosbags.js';
33
33
  import { Rosout } from './rosout.js';
34
34
  import { Session } from './session.js';
35
35
  import { FrameStreams } from './streams.js';
36
+ import { SystemMonitor } from './system.js';
36
37
  import { SystemdServices } from './systemd.js';
37
38
  import { Teleop } from './teleop.js';
38
39
  import { Terminals } from './terminals.js';
@@ -112,6 +113,7 @@ export async function startServer(options) {
112
113
  const diagnostics = new Diagnostics(node, logger);
113
114
  const terminals = new Terminals(logger);
114
115
  const networking = new Networking(logger);
116
+ const system = new SystemMonitor(logger);
115
117
  // Next to the panels
116
118
  const uploadsDir = path.join(path.dirname(panelsDir), 'images');
117
119
  const rosbags = new Rosbags(ros, logger);
@@ -171,6 +173,7 @@ export async function startServer(options) {
171
173
  actions,
172
174
  parameters,
173
175
  rosbags,
176
+ system,
174
177
  plugins: host,
175
178
  hostname: os.hostname(),
176
179
  }, ownPage(req.headers), req.socket.localAddress ?? null);
@@ -207,6 +210,7 @@ export async function startServer(options) {
207
210
  systemd.stopAll();
208
211
  terminals.stopAll();
209
212
  networking.stopAll();
213
+ system.stop();
210
214
  // Stops the robot if a page was driving it, while the node still runs
211
215
  for (const session of sessions) {
212
216
  session.close();
@@ -20,6 +20,7 @@ import type { Rosbags } from './rosbags.ts';
20
20
  import type { RosGraph } from './ros_graph.ts';
21
21
  import type { Rosout } from './rosout.ts';
22
22
  import type { FrameStreams } from './streams.ts';
23
+ import type { SystemMonitor } from './system.ts';
23
24
  import type { SystemdServices } from './systemd.ts';
24
25
  import type { Teleop } from './teleop.ts';
25
26
  import type { Terminals } from './terminals.ts';
@@ -52,6 +53,7 @@ export interface SessionServices {
52
53
  actions: Actions;
53
54
  parameters: Parameters;
54
55
  rosbags: Rosbags;
56
+ system: SystemMonitor;
55
57
  plugins: PluginHost;
56
58
  hostname: string;
57
59
  }
@@ -468,6 +468,12 @@ export class Session {
468
468
  case 'bags.action':
469
469
  this.answer(message.id, Promise.resolve().then(() => services.rosbags.act(checkBagsRequest(message.request), this.ownPage)));
470
470
  return;
471
+ case 'system.subscribe': {
472
+ const id = subscriptionId(message.id);
473
+ this.unsubscribe(id);
474
+ this.subscriptions.set(id, services.system.watch((update) => this.send({ type: 'system', id, ...update })));
475
+ return;
476
+ }
471
477
  case 'action.subscribe': {
472
478
  const id = subscriptionId(message.id);
473
479
  this.unsubscribe(id);
@@ -0,0 +1,40 @@
1
+ import type { SystemUpdate } from '../shared/protocol.ts';
2
+ import { type Logger } from './util.ts';
3
+ export type SystemUpdateBody = Omit<SystemUpdate, 'type' | 'id'>;
4
+ type Listener = (update: SystemUpdateBody) => void;
5
+ export declare class SystemMonitor {
6
+ private readonly logger;
7
+ private readonly listeners;
8
+ private readonly history;
9
+ private info;
10
+ private infoJson;
11
+ private infoAt;
12
+ private space;
13
+ private spaceJson;
14
+ private spaceAt;
15
+ private sensors;
16
+ private sensorsAt;
17
+ private previous;
18
+ private latest;
19
+ private nvidia;
20
+ private vcgencmd;
21
+ private underVoltageSeen;
22
+ private timer;
23
+ private linger;
24
+ private running;
25
+ private ticking;
26
+ constructor(logger: Logger);
27
+ watch(listener: Listener): () => void;
28
+ stop(): void;
29
+ private send;
30
+ private tick;
31
+ private refresh;
32
+ private sample;
33
+ private temperatures;
34
+ private fans;
35
+ private frequencies;
36
+ private power;
37
+ private gpus;
38
+ private throttled;
39
+ }
40
+ export {};