TypeScript / JavaScript API
CFG=.../config.mk, memory.yml, Linux FPGA Manager and device-tree overlays. It is not backward-compatible with 0.x instruments.
Goal
Build a browser or Node client for a running instrument.
The TypeScript client communicates with koheron-server over WebSockets and uses the generated drivers.json command metadata.
A web interface usually contains:
- HTML and CSS for the user interface.
- TypeScript wrappers for C++ drivers.
- Application code that connects UI events to instrument commands.
Add web files to an instrument
The top-level Makefile already initializes WEB_FILES with the shared SDK files web/main.css and web/koheron.ts.
Add instrument-specific files in config.mk:
WEB_FILES += $(PROJECT_PATH)/web/index.html
WEB_FILES += $(PROJECT_PATH)/web/instrument.ts
WEB_FILES += $(PROJECT_PATH)/web/app.ts
WEB_FILES += $(PROJECT_PATH)/web/main.css
The web build runs tsc in the SDK web Docker image (koheron-web:node20 by default), targets ES5 output, writes TypeScript output to tmp/<project>/web/app.js, and copies non-TypeScript web assets into tmp/<project>/web/ before they are packaged in the instrument archive. Asset basenames must be unique because the archive is packaged flat.
Initialize the client
const client = new Client(location.hostname, 5);
window.addEventListener('load', async () => {
await client.init();
// create drivers and UI controllers here
});
window.onbeforeunload = () => {
client.exit();
};
The second Client argument is the WebSocket pool size.
Write a TypeScript driver wrapper
A TypeScript wrapper maps a C++ driver class to client commands.
class FFT {
private driver: Driver;
private id: number;
private cmds: HashTable<ICommand>;
constructor(private client: Client) {
this.driver = this.client.getDriver('FFT');
this.id = this.driver.id;
this.cmds = this.driver.getCmds();
}
setInputChannel(channel: number): void {
this.client.send(Command(this.id, this.cmds['set_input_channel'], channel));
}
getFftSize(): Promise<number> {
return this.client.readUint32(Command(this.id, this.cmds['get_fft_size']));
}
readPsd(): Promise<Float32Array> {
return this.client.readFloat32Array(Command(this.id, this.cmds['read_psd']));
}
}
The driver name and command names must match the exported C++ class and public method names in drivers.json.
Read command results
Read helpers support Promise-style use:
const fftSize = await fft.getFftSize();
and callback-style use where supported by the read helper:
client.readUint32(Command(id, cmds['get_fft_size']), value => {
console.log(value);
});
Use a read helper that matches the C++ return type:
uint32_t:readUint32()int32_t:readInt32()float:readFloat32()double:readFloat64()bool:readBool()std::array<uint32_t, N>:readUint32Array()std::array<int32_t, N>:readInt32Array()std::array<float, N>:readFloat32Array()std::array<double, N>:readFloat64Array()std::vector<uint32_t>:readUint32Vector()std::vector<float>:readFloat32Vector()std::vector<double>:readFloat64Vector()std::tuple<...>:readTuple(cmd, fmt)
Application skeleton
class App {
private fft: FFT;
constructor(private window: Window, private document: Document, private ip: string) {
const client = new Client(ip, 5);
window.addEventListener('load', async () => {
await client.init();
this.fft = new FFT(client);
console.log(await this.fft.getFftSize());
});
window.onbeforeunload = () => {
client.exit();
};
}
}
const app = new App(window, document, location.hostname);
Dedicated sockets
The WebSocket pool supports dedicated sockets for long-running tasks. Use the shared pool for normal short commands and dedicated sockets for streams or operations that should not block other UI actions.

