Skip to main content

Tools — CLI and viewer

Besides the library, the Java reactor ships two standalone applications: osf-cli — a scriptable command line for inspecting, exporting and converting OSF files — and osf-viewer — a JavaFX viewer that plots many channels at once. Both build only on the public library API (see Reading and Writing) and double as complete usage examples for it.

Baseline: Java 21, Maven reactor. A full build (mvn -f implementations/java/pom.xml package) produces both tools in one pass; reactor details are on the Building page.

osf-cli

osf-cli is a picocli application with four subcommands. The entry class OsfCli registers info, channels, dump and convert; every command carries -h/--help and -V/--version (via mixinStandardHelpOptions).

Build as a runnable jar

The osf-cli module is bundled into a self-contained runnable jar via the maven-shade-plugin (main class com.optimeas.osf.cli.OsfCli, finalName osf-cli):

mvn -f implementations/java/pom.xml -pl osf-cli -am package
java -jar implementations/java/osf-cli/target/osf-cli.jar --help

The shade jar bundles the OSF library and picocli, so it runs with no extra classpath. For brevity, osf below stands for java -jar …/osf-cli.jar.

info — metadata and channel summary

osf info measurement.osf

Loads the file (OSF4, OSF5, or transparent OSFZ) and prints the format, file metadata, compression status, and one line per channel:

format: OSF5
creator: optiMEAS
created_utc: 2026-01-15T09:30:00Z
compressed: false
channels: 3
[0] temperature type=double mode=equidistant samples=10000 unit=°C
[1] status type=string mode=variable samples=42 unit=
[2] position type=gps_location mode=timestamped samples=500 unit=

The format: line is OSF4 or OSF5; the metadata is taken verbatim from the metablock (creator, created_utc, location, …); a compressed input prints compressed: true (gzip).

channels — channel table

osf channels measurement.osf --sort NAME

Prints an aligned table (columns index, name, datatype, mode, samples, unit). --sort accepts INDEX (default) or NAME:

index name datatype mode samples unit
------------------------------------------------------------------------------------------
0 temperature double equidistant 10000 °C
2 position gps_location timestamped 500
1 status string variable 42

dump — channel data to CSV

dump writes channel values as CSV — by default all chartable channels (numeric + bool; string/binary/gps are skipped).

OptionEffect
--channel <name|index>Select a channel by name or integer index; repeatable. Omitted: all chartable channels
--format <csv|unified-csv>csv (default): one block per channel; unified-csv: one wide table
--timestamp-format <…>DATETIME (default), SECONDS, ISO8601, NANOSECONDS
--out <file>Write to a file instead of stdout

Timestamp formats: DATETIME = uuuu-MM-dd HH:mm:ss.SSS (UTC, ms), SECONDS = decimal seconds with 9 fractional digits, ISO8601 = uuuu-MM-dd'T'HH:mm:ss'Z', NANOSECONDS = raw nanosecond integer. Integral double values render without a decimal point (1 not 1.0).

Per-channel CSV (default) — each block with a # channel: header and timestamp,value rows:

osf dump measurement.osf --channel temperature --timestamp-format SECONDS
# channel: temperature
timestamp,value
0.000000000,21.5
0.001000000,21.6

Unified CSV — one wide table with a row per distinct timestamp; cells are blank where a channel has no sample at that time:

osf dump measurement.osf --format unified-csv --channel 0 --channel 3 --out values.csv
timestamp,temperature,humidity
1970-01-01 00:00:00.000,21.5,48
1970-01-01 00:00:00.001,21.6,

Channel names containing a comma, quote, or newline are CSV-quoted.

convert — to OSF5 (optionally compressed)

convert reads any input (OSF4/OSF5, possibly compressed) and writes it as OSF5:

osf convert old-osf4.osf new.osf # OSF4 → OSF5
osf convert measurement.osf measurement.osfz --compress # OSF5 → gzip (OSFZ)
OptionEffect
--compressWrap the output in gzip (produces an OSFZ file)
--writer <BLOCK|STREAMING>Writer back-end; BLOCK (default) buffers in memory and writes in one pass, STREAMING replays sample-by-sample through a FileChannel

STREAMING does not support --compress; combining the two falls back to BLOCK with a note. On success convert prints wrote <file> (N channels). This makes the command the simplest OSF4→OSF5 converter.

Full source: osf-cli/src/main/java/com/optimeas/osf/cli/.

osf-viewer

osf-viewer is a JavaFX application that displays many channels of an OSF file at once. Its core idea is a min/max-per-pixel decimation: no matter how many millions of samples land in one screen column, an outlier is never dropped.

Running it

The viewer runs most easily through the JavaFX Maven plugin (main class com.optimeas.osf.viewer.ViewerApp):

mvn -pl osf-viewer -f implementations/java/pom.xml javafx:run

Optionally, open a file immediately at startup — the first launch argument is interpreted as a path:

mvn -pl osf-viewer -f implementations/java/pom.xml javafx:run \
-Djavafx.args="measurement.osf"

A 1000×700 window titled "OSF Viewer" opens, with a toolbar, channel list, plot area, and status bar.

User interface

  • ToolbarOpen… opens a file dialog (filters *.osf, *.osfz); Zoom Reset returns the visible range to the full time extent of all chartable channels.
  • Channel list (left) — a table with columns Plot, Name, DataType, Mode, Samples, Unit. The Plot checkbox toggles a channel into the drawing; for non-chartable channels (string, binary, GPS) it is disabled and carries the tooltip "not plotted in v1".
  • Plot area (center) — the actual trace rendering; it fills the remaining space and repaints on every resize.
  • Status bar (bottom) — load status and cursor readout.

Loading always happens in the background (a JavaFX Task) so the UI stays responsive while large files are read.

Decimation — min/max per pixel

For each pixel column the Decimator determines the minimum AND maximum of all samples falling into that time window and draws a vertical stroke from minY to maxY. Even under extreme compression every peak stays visible — unlike simply dropping intermediate points. Sample assignment uses a binary search (lowerBound) over the ascending timestamps, so it is fast even for millions of samples. Each selected channel autoscales its Y axis independently (over the cached value range); colours rotate through a palette of six easily distinguishable tones.

Interaction

  • Pan — drag horizontally with the mouse button held to shift the time window.
  • Zoom — mouse wheel; scrolling up zooms in (factor 0.8), down zooms out (factor 1.25), each around the cursor position so the time under the cursor stays fixed.
  • Cursor readout — on mouse move the status bar shows the cursor time and, for each selected channel, the nearest sample value.

Coordinate mapping (time↔X, value↔Y with an inverted Y axis) is encapsulated in AxisTransform, the readout and drawing logic in PlotCanvas; both are deliberately kept apart from the pure model layer (ViewerModel, Decimator, AxisTransform — no JavaFX imports) and are therefore testable without a running JavaFX runtime. See Architecture and Internals for that split.

Full source: osf-viewer/src/main/java/com/optimeas/osf/viewer/.

Next

This document is licensed under CC BY 4.0. Attribution: optiMEAS GmbH and optiMEAS Switzerland GmbH.