wesplot

package module
v1.0.0-rc1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 11, 2023 License: GPL-3.0 Imports: 23 Imported by: 0

README

wesplot

A live/real-time plotting tool that takes stdin data and pipes it into websocket and into a JavaScript interactive chart. The backend can run on both a local computer or on a remote server. The front-end runs in a browser which means it can run on any device (including mobile devices) that can connect to the backend.

demo1.webm

Features

Stdin → browser plotting live streamed or replayed data

Wesplot is designed to work with Unix pipelines. It streams data written to its standard input (stdin) to one or more browser windows via websockets, where it is then plotted as a scatter plot.

By leveraging the Unix pipeline, there is an endless amount of use cases for wesplot. Some simple examples are:

  1. Monitor live CPU, memory, IO, network usage: By using tools like sar, live system usage information can be streamed to stdout. If this information is parsed (usually with awk), it can be piped into wesplot and live system usage can be plotted directly against the browser. See Example use cases for some example commands.
  2. Plot CSV data directly from the terminal and generate publication-quality plots: By using cat, CSV files can be piped to wesplot. Wesplot can use values from one column to be the X-axis coordinates and the rest of the columns as different series.
  3. Visualize real-time data from remote devices: Wesplot can be running continuously on a remote device such as a server or an IoT device. For example, the current network throughput on a server can be piped to a persistent wesplot instance which can be connected to and visualized remotely. Another example could be an air-quality sensor that pipe its data to wesplot which can then be visualized remotely.
Customizable, interactive plots via command line and GUI

Wesplot is designed to export quality plots suitable for documentations, presentations, and publications. To support this, the chart title, axis labels, axis limits, and axis units can be customized both via command-line options as well as via the settings panel with the browser interface.

Simultaneous streams to multiple devices

Data piped to wesplot can be visualized simultaneously from multiple browser tabs and even multiple devices, including mobile devices such as tablets and phones. One creative use of this is to visualize data coming from a mobile robot with wesplot on a mobile device as it is being tested in the field.

Easy single binary installation with cross platform support

Wesplot is designed to be very simple to install. Simply download the executable and put it in your $PATH and you're good to go. It supports all major platform including Linux, OS X, and Windows for both x86 and ARM.

Installation instruction

  1. Download the appropriate version of wesplot for your OS and architecture from the latest release.
  2. Rename the executable to wesplot.
  3. Copy the downloaded executable to a place in your $PATH. For example, you can copy wesplot to /usr/local/bin.
  4. Make the wesplot binary executable via chmod +x wesplot.
Linux-specific instructions
TODO
OSX-specific instructions
  1. After following the instructions above, you must execute wesplot once.
  2. At this point, OS X will open a dialog box that says: "wesplot is from an unidentified developer" and prompt you to either cancel or move wesplot to trash. Click Cancel.
  3. Open the System Settings, go to the Privacy & Security in the side bar, then go to Security (scroll down). You should be able to see a line of text that says "wesplot was blocked from opening because it is not from an identified developer". Click Open Anyway next to that.
  4. Launching wesplot again from this point on will work.

You can blame Apple for this feature.

If the above instruction is out-of-date, please consult with Apple's official documentation on this: https://support.apple.com/en-ca/guide/mac-help/mh40616/mac.

If you don't trust the binary, you can always build from the source.

Example use cases

Here's a few use cases for monitoring system metrics. For more examples, see docs/example-usage, which includes:

Metric Command Comments
CPU usage (Linux via SAR) S_TIME_FORMAT=ISO sar 1 | awk '{ if ($NF ~ /^[0-9]+[.]?[0-9]*$/) print 100-$NF; fflush(); }' | wesplot -t "CPU Utilization" -c "CPU%" -M 0 -m 100
Memory usage (Linux via SAR) S_TIME_FORMAT=ISO sar -r 2 | awk '{ if ($4 ~ /^[0-9]+$/) print $4/1024; fflush() }' | wesplot -t "Memory usage" -u "MB" Ensure the 4th column of S_TIME_FORMAT=ISO sar -r 2 is kbmemused. May be slightly different from output of `free`.
Ping latency to 1.1.1.1 ping 1.1.1.1 | sed -u 's/^.*time=//g; s/ ms//g' | wesplot -t 'Ping to 1.1.1.1' -u "ms"

Frequently asked questions (FAQ)

How can I format the chart with axis labels, limits, titles, and so on?
Can I start multiple wesplot sessions?

Yes. Wesplot will automatically find a port starting from 5274 for up to 200 times. If all ports between 5274 and 5474 are taken, you can manually specify a port via the command line option --port. For example: wesplot --port 1234 will start wesplot on port 1234.

Can I view wesplot from multiple browser windows/tabs?

Yes. In fact the browser windows do not even have to reside on the same computer!

Can I plot multiple data with multiple columns on the same plot?
How do I set the time value for the data point to be 0 and subsequent data points to be relative from the first?
How can I plot data whose x values are not time values?
How can I plot data that already have timestamps as a column?
How can I plot data from a CSV or TSV file?
How can I plot data series spanning multiple lines in the same chart?
How can I save the live data as I'm plotting it?

Development setup

  • Make sure you have Python 3 installed.
  • Make sure you install Go.
  • Make sure you install nodejs and yarn classic (for now).
  • cd frontend; yarn to install the frontend dependencies.
  • Run make backend-dev which will start a development build of wesplot and it will plot a single signal (CPU usage from sar).
  • In a separate terminal, Run make frontend-dev which will start the front end development server.
  • Go to http://localhost:5273 to see the frontend.
    • Note that while you can run multiple wesplots on different ports with the binary, the development setup will only work with a single server as all front-end will listen to the server at the default port (5274).

Building the production binary from source

  • Make sure you have all development dependencies installed.
  • Run make prod.
  • The resulting binary will be in build/wesplot.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var Version string = "?? (source-build)"

Functions

func Filter

func Filter[T any](slice []T, predicate func(T) bool) []T

func Min

func Min[T Number](a T, b T) T

func NowXGenerator

func NowXGenerator(line []float64) float64

Generates the current unix timestamp in seconds.

Types

type CsvStringReader

type CsvStringReader struct {
	// contains filtered or unexported fields
}

This implements a StringDataReader and reads an io.Reader using the Golang csv module. This means the input data must strictly conform to CSV data. If the input data is not exactly CSV (for example separated by one or more spaces), use the RelaxedStringReader.

func NewCsvStringReader

func NewCsvStringReader(input io.Reader) *CsvStringReader

func (*CsvStringReader) Read

func (r *CsvStringReader) Read(ctx context.Context) ([]string, error)

type DataBroadcaster

type DataBroadcaster struct {
	// contains filtered or unexported fields
}

func NewDataBroadcaster

func NewDataBroadcaster(input DataRowReader, bufferCapacity int, teeMode bool) *DataBroadcaster

func (*DataBroadcaster) DeregisterChannel

func (d *DataBroadcaster) DeregisterChannel(ctx context.Context, c chan<- DataRow)

Deregister a channel to get data updates. Called when a websocket client disconnects or when the input stream closes. Note: the channel shouldn't be closed until this method returns (if the input is still open), as it may cause panics otherwise.

- ctx: is the HTTP call context. - c: is the channel to send data on. This should be the same channel as the one passed to RegisterChannel to successfully deregister.

This method will panic if c is not registered. This indicates a programming error.

func (*DataBroadcaster) RegisterChannel

func (d *DataBroadcaster) RegisterChannel(ctx context.Context, c chan<- DataRow)

Register a new channel. Called from the HTTP server when a new websocket connection is initiated.

- ctx: is the HTTP call context. - c: is the channel to send data on. This should be a buffered channel to ensure the DataBroadcaster is not blocked, as if any channel is blocked, everything is blocked.

func (*DataBroadcaster) Start

func (d *DataBroadcaster) Start(ctx context.Context)

func (*DataBroadcaster) Wait

func (d *DataBroadcaster) Wait()

type DataRow

type DataRow struct {
	X  float64
	Ys []float64
	// contains filtered or unexported fields
}

type DataRowReader

type DataRowReader interface {
	Read(context.Context) (DataRow, error)
	ColumnNames() []string
}

When Read is called, return the DataRow.

type HttpServer

type HttpServer struct {
	// contains filtered or unexported fields
}

func NewHttpServer

func NewHttpServer(dataBroadcaster *DataBroadcaster, host string, port uint16, metadata Metadata, flushInterval time.Duration) *HttpServer

func (*HttpServer) Run

func (s *HttpServer) Run() error

type Metadata

type Metadata struct {
	WindowSize     int
	XIsTimestamp   bool
	RelativeStart  bool
	WesplotOptions WesplotOptions
}

type Number

type Number interface {
	constraints.Float | constraints.Integer
}

type RelaxedStringReader

type RelaxedStringReader struct {
	// contains filtered or unexported fields
}

This is a more relaxed reader that can split on spaces or commas. However, it does not follow string CSV formatting. This is the default.

func NewRelaxedStringReader

func NewRelaxedStringReader(input io.Reader) *RelaxedStringReader

func (*RelaxedStringReader) Read

func (r *RelaxedStringReader) Read(ctx context.Context) ([]string, error)

type StreamEndedMessage

type StreamEndedMessage struct {
	StreamEnded bool
	StreamError error
}

type StringReader

type StringReader interface {
	Read(context.Context) ([]string, error)
}

When Read is called, return an array of strings which are the columns.

type TextToDataRowReader

type TextToDataRowReader struct {
	// The input reader object (either CsvStringReader or RelaxedStringReader)
	Input StringReader

	// The x column index. If this is <0, X is generated via XGenerator, which
	// corresponds to the current time stamp in seconds. Note this column
	// will be put into DataRow.X while the rest of the row except this column
	// will be put into DataRow.Ys.
	XIndex int

	// The generator function. Defaults to NowXGenerator.
	XGenerator func([]float64) float64

	// The labels of the columns excluding the X column.
	Columns []string

	// If the input row has a different length than Columns, ignore the row.
	ExpectExactColumnCount bool
}

Creates a DataRowReader based on text input. Unrecognized/unparsable lines will be ignored and logged via warnings.

func (*TextToDataRowReader) ColumnNames

func (r *TextToDataRowReader) ColumnNames() []string

func (*TextToDataRowReader) Read

type ThreadUnsafeRing

type ThreadUnsafeRing[T any] struct {
	// contains filtered or unexported fields
}

A terrible implementation of a ring, based on the Golang ring which is not thread-safe nor offers a nice API.

I can't believe there are no simple ring buffer data structure in Golang, with generics.

func NewRing

func NewRing[T any](capacity int) *ThreadUnsafeRing[T]

func (*ThreadUnsafeRing[T]) Push

func (r *ThreadUnsafeRing[T]) Push(data T)

func (*ThreadUnsafeRing[T]) ReadAllOrdered

func (r *ThreadUnsafeRing[T]) ReadAllOrdered() []T

type WesplotOptions

type WesplotOptions struct {
	Title   string
	Columns []string
	XLabel  string
	YLabel  string
	YMin    *float64 `json:",omitempty"`
	YMax    *float64 `json:",omitempty"`
	YUnit   string
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL