beckhoff-twincat-scope
v1.0.0
Published
Parser for Beckhoff TwinCAT Scope-View .svdx / .svb recording files. Exports channel/acquisition data to CSV and other formats.
Maintainers
Readme
beckhoff-twincat-scope
A TypeScript toolkit for Beckhoff TwinCAT Scope-View: parses .svdx and .svb recordings, streams CSV, and generates .tcscopex project configurations.
- Decodes both recording formats with a single shared data model
- Restores absolute Windows FILETIME timestamps per sample (100 ns resolution)
- Streams CSV output — long, wide, or one file per channel
- Generates
.tcscopexfiles (single, or multi-file ZIP bundle with.tcmproj) - Pattern expander (
{i:1:8}) for repetitive symbol structures - Library API and
svdx-parseCLI - ESM, Node ≥ 18
Install
npm install beckhoff-twincat-scopeCLI quick reference
npx svdx-parse <input> [options]| Flag | Default | Description |
|---|---|---|
| -o, --out <path> | out.csv | Output file (long/wide) or directory (per-channel) |
| -m, --mode <mode> | long | long | wide | per-channel |
| -c, --channels <names> | all | Comma-separated channel allowlist |
| -t, --time-fmt <fmt> | delta_ms | iso | epoch_ms | delta_ms |
| -d, --decimate <N> | 1 | Keep every Nth sample |
| --delimiter <ch> | ; | Field delimiter |
| --precision <N> | lossless | Float decimals |
| -V, --version | — | Print version |
| -h, --help | — | Print help |
# Long-format export with ISO timestamps
npx svdx-parse recording.svdx --out long.csv --time-fmt iso
# Wide-format, two channels, comma delimiter
npx svdx-parse recording.svb \
--mode wide \
--channels ActPos,SetPos \
--delimiter , \
--out wide.csv
# One CSV per channel, decimated 10x
npx svdx-parse recording.svdx \
--mode per-channel \
--decimate 10 \
--out ./csv-outLibrary
import { readScopeFile, writeCsv } from 'beckhoff-twincat-scope';
const rec = await readScopeFile('recording.svdx'); // auto-detects .svdx / .svb
await writeCsv(rec, 'out.csv', {
mode: 'long',
timeFormat: 'delta_ms',
delimiter: ';',
});Inspect in memory
import { readScopeFile, fileTimeToDate } from 'beckhoff-twincat-scope';
const rec = await readScopeFile('recording.svdx');
console.log(rec.name, rec.startTime, rec.channels.length);
for (const ch of rec.channels) {
const first = fileTimeToDate(ch.samples.timestampsTicks[0]);
console.log(`${ch.name} (${ch.dataType}): ${ch.samples.values.length} samples, starts ${first.toISOString()}`);
}Parse a buffer directly
import { readFile } from 'node:fs/promises';
import { parseSvdx, parseSvb } from 'beckhoff-twincat-scope';
const svdx = parseSvdx(await readFile('recording.svdx'));
const svb = parseSvb(await readFile('recording.svb'));Generate a .tcscopex project
import { writeTcScopeX, expandPattern } from 'beckhoff-twincat-scope';
const acquisitions = expandPattern({
name: 'Axis_{i:1:4}_ActPos',
symbolName: 'Axes.Axis_{i:1:4}.fActPos',
amsNetId: '5.62.123.45.1.1',
targetPort: 851,
indexOffset: 0,
dataType: 'REAL64',
variableSize: 8,
baseSampleTimeTicks: 10_000n, // 1 ms
});
await writeTcScopeX(
{ name: 'Motion Demo', acquisitions },
'./motion-demo.tcscopex',
);
// Or a multi-file ZIP bundle with a .tcmproj index:
import { writeTcScopeBundle } from 'beckhoff-twincat-scope';
await writeTcScopeBundle(
[
{ name: 'Group A', acquisitions },
{ name: 'Group B', acquisitions },
],
'./scope-project.zip',
);Round-trip an existing .svdx configuration into a fresh .tcscopex:
import { readSvdx, specFromRecording, writeTcScopeX } from 'beckhoff-twincat-scope';
const rec = await readSvdx('previous-run.svdx');
await writeTcScopeX(specFromRecording(rec, { name: 'Cloned' }), './cloned.tcscopex');Public API
import {
// Readers
readScopeFile, readSvdx, readSvb, parseSvdx, parseSvb, parseSvdxXmlConfig,
// CSV writer
writeCsv,
// .tcscopex generator
writeTcScopeX, writeTcScopeBundle,
buildTcScopeXXml, buildTcmprojXml,
specFromRecording, expandPattern, expandSpecPatterns,
// Helpers
fileTimeToDate, dateToFileTime,
// Constants
VERSION,
// Types
type ScopeRecording, type Channel, type ChannelSamples, type ScopeDataType,
type SvdxConfig, type SvdxAcquisition, type SvdxAcquisitionInterpreter,
type CsvMode, type CsvOptions, type TimeFormat,
type ScopeProjectSpec, type ScopeProjectAcquisition,
type WriteTcScopeXOptions, type BundleOptions,
} from 'beckhoff-twincat-scope';Data model
Each Channel carries the channel metadata plus a samples block:
interface ChannelSamples {
readonly timestampsTicks: BigInt64Array; // absolute FILETIME ticks
readonly values: Float64Array | BigInt64Array | BigUint64Array;
}Timestamps are stored as Windows FILETIME (100 ns since 1601-01-01 UTC) so samples from a .svdx and its matching .svb line up on the same absolute timeline. Convert with fileTimeToDate / dateToFileTime.
Supported ScopeDataType values: BIT, BIT8, BITARR8, BITARR16, BITARR32, INT8, INT16, INT32, INT64, UINT8, UINT16, UINT32, UINT64, REAL32, REAL64. 64-bit integer channels come back as BigInt64Array / BigUint64Array; everything else as Float64Array.
Documentation
Full guides, CLI reference, and API docs: https://philippleidig.github.io/twincat-scope-export-parser/
