nesnes / teleplot

Ridiculously-simple telemetry viewer. Plots charts in browser from any apps (bash, C++, and more!)
MIT License
103 stars 15 forks source link
Teleplot logo

Teleplot

A ridiculously simple tool to plot telemetry data from a running program and trigger function calls.

echo "myData:4" | nc -u -w0 127.0.0.1 47269

Test it online

Go to teleplot.fr

Supports

Teleplot project received the generous technical support of Wandercraft.

Start the server

As a binary

cd server
npm i
sudo npm run-script make
./build/teleplot

Current target is x64 and configurable in package.json -> pkg/targets

Using node

cd server
npm i
node main.js

Open your navigator at 127.0.0.1:8080

Using docker

cd server
docker build -t teleplot .
docker run -d -p 8080:8080 -p 47269:47269/udp teleplot

Open your navigator at 127.0.0.1:8080

Using docker-compose

cd server
docker-compose build
docker-compose up

Open your navigator at 127.0.0.1:8080

Telemetry Format

A telemetry gets published by sending a text-based UDP packet on the port 47269. As it's a trivial thing to do on the vast majority of languages, it makes it very easy to publish from anywhere.

The telemetry format is inspired by statsd and to some extents compatible with it.

The expected format is A:B:C§D|E where:

Examples:

Plot XY rather than time-based

Using the xy flag, and providing a value in both B and C field, teleplot will display an YX line chart.

A timestamp can be associated with the xy point by adding an extra :1627551892437 after the C field.

The YX line chart will only be displayed when at least two values have been received.

Using clr flag when sending a telemetry will clear the previous values. This is useful when streaming cyclic data like lidar points.

Publishing text format telemetries

Publishing multiple points

/!\ here, many values will be received at the same time by teleplot, therefore you must precise their timestamps.

Multiple values of a single telemetry can be sent in a single packet if separated by a ;

Publishing multiple telemetries

Multiple telemetries can be sent in a single packet if separated by a \n

myValue:1234
mySecondValue:1234:m/s
myThirdValue:1627551892437:1234
state:state_a|t
trajectory:1:1;2:2;3:3;4:4|xy
trajectoryTimestamped:1:1:1627551892437;2:2:1627551892448;3:3:1627551892459|xy

Notice that your data needs to fit in a single UPD packet whick can be limited to 512(Internet), 1432(Intranets) or 8932(Jumbo frames) Bytes depending on the network.

Prevent auto-plot of telemetry

By default, teleplot will display all the incoming telemetry as a chart, while this is handy for new user with small amount of data, this might not be desired with lots of data. The np (for no-plot) flag can be used to prevent this behavior:

Publishing 3D telemetries

To send 3D shapes to teleplot, use this syntax : 3D|A:B:C|E, where A is the name of the shape telemetry B is the timestamp of the telemetry and is optional C is a text representing the shape E is containing flags and is optional

Writing A (the name of the shape telemetry)

if A contains a comma, the text after the comma will be considered as a 'widget label', if multiple telemtries are sent with the same widget label, they will automatically be displayed on the same widget. The text before the comma will be considered as the name of the shape telemetry.

If A doesn't contain a comma, its whole text will be considered as the name of the shape telemetry.

Writing C (the text representing the shape)

C is built as a concatenation of the shape properties followed by their values, the whole with colons in between.

LIST OF PROPERTIES :

=== Sphere only ===

=== Cube only ===

If you don't want to send all the arguments of a certain property, you still have to add colons ( ex : position::-1:1, here the x position is not given but colons are still present )

for unspecified properties, teleplot will use the ones from the previous shape state.

If it is the first time teleplot receives data of a certain shape, the property "shape" (cube or sphere) has to be given and missing properties will be replaced by default ones.

Also, the shape, color and precision properties can not be changed later on.

Some examples

Creating a simple sphere and cube :

cube without timestamp :
- `3D|mySimpleCube:S:cube:P:1:1:1:R:0:0:0:W:2:H:2:D:2:C:#2ecc71`

sphere with timestamp : 
- `3D|mySimpleSphere:1627551892437:S:sphere:P::2::RA:2:C:red`

Creating a cube that grows and rotates :

In the first request we send, we need to specify the shape:
- `3D|my_super_cube:S:cube:W:1:D:1:H:1:C:blue`

Then we can specify only properties that change :
- `3D|my_super_cube:W:1.2:R::0.2:`
- `3D|my_super_cube:W:1.4:R::0.4:`
- `3D|my_super_cube:W:1.6:R::0.6:`

Creating a simple sphere and cube and display them on the same widget by default :

cube with widget label 'widget0' :
- `3D|myCube,widget0:S:cube`

sphere with same widget label : 
- `3D|mySphere,widget0:S:sphere`

/!\ Despite the examples above, it might be a better idea to send every property everytime, as if teleplot refreshes or if it wasn't lauched before you sent certain properties, it will not have any way to be aware of the properties you sent previously. Therefore, it will have to use default values, or may not display anything at all, if it is not informed of the shape type for instance.

/!\ If you send shapes at a too high frequency, teleplot will quickly be overloaded, also teleplot can only draw 3D shapes at a maximum speed of 50-60 frames per second, therefore it is not recommended to send more than 60 shapes per seconds.

Publish telemetries

Bash

echo "myValue:1234" | nc -u -w0 127.0.0.1 47269

C++

Copy Teleplot.h (from clients/cpp) in your project and use its object.

#include <math.h>
#include "Teleplot.h"
Teleplot teleplot("127.0.0.1");

int main(int argc, char* argv[])
{
    for(float i=0;i<1000;i+=0.1)
    {
        // Use instanciated object
        teleplot.update("sin", sin(i));
        teleplot.update("cos", cos(i), 10); // Limit at 10Hz

        // Use static localhost object
        Teleplot::localhost().update("tan", tan(i));

        usleep(10000);
    }
    return 0;
}

Python

import socket
import math
import time

teleplotAddr = ("127.0.0.1",47269)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

def sendTelemetry(name, value):
    now = time.time() * 1000
    msg = name+":"+str(now)+":"+str(value)
    sock.sendto(msg.encode(), teleplotAddr)

i=0
while i < 1000:

    sendTelemetry("sin", math.sin(i))
    sendTelemetry("cos", math.cos(i))

    i+=0.1
    time.sleep(0.01)

Not listed?

You just need to send a UDP packet with the proper text in it. Open your web browser, search for my_language send UDP packet, and copy-paste the first sample you find before editing it with the following options:

Remote function calls

Remote function calls is an optional feature that opens an UDP socket between the program and the Teleplot server to pull the list of registered functions and call them.

Register a function

C++

Copy Telecmd.h (from clients/cpp) in your project and use its object. Telecmd.h and Teleplot.h can be used at the same time.

#include "Telecmd.h"

int main(int argc, char* argv[])
{
    bool keepRunning = true;

    Telecmd::localhost().registerCmd("sayHello",[](std::string params){
        std::cout << "Hello " << params << std::endl;
    });

    Telecmd::localhost().registerCmd("stop",[&](std::string){
        std::cout << "Stopping..." << std::endl;
        keepRunning = false
    });

    // Main program loop
    while(keepRunning){
        Telecmd::localhost().run();
    }
    return 0;
}

Call a function

Functions can be called from the Teleplot interface and will be auto-discovered, however, they can also be triggered by a simple UDP packet:

Send a text log

Along with telemetries, you can also send text logs to be display in a console-like manner:

echo ">:Hello world" | nc -u -w0 127.0.0.1 47269

By adding a millisecond timestamp to your log, you can sync them with the charts.

echo ">1627551892437:Hello world" | nc -u -w0 127.0.0.1 47269