Skip to main content

JavaScript

Full example for a direct connection to LA4 at 192.168.0.23:5000. A server listens for incoming events, a timer sends periodic heartbeats, and the main thread sends commands.

note

This example assumes LA4 is reachable at a known static IP. If you are not using a direct or static IP setup, you will need to scan the local network to discover LA4's address before connecting. See the network setup section for details.

const net = require('net');
const crypto = require('crypto');
const readline = require('readline');

class LA4Client {
static LA4_IP = '192.168.0.23';
static LA4_PORT = 5000;

static MY_IP = '192.168.0.100'; // IP of this machine — LA4 will reply here
static MY_PORT = 5000; // Port this machine listens on

static HEARTBEAT_FREQUENCY_SECONDS = 3;
static SEND_DATA_TIMEOUT_SECONDS = 2;

static SENDER_NAME = 'My PC App';
static SENDER_VERSION = '0.1.0';

constructor() {
this._senderId = crypto.randomBytes(4).toString('hex');
this._running = true;
}

run() {
this._startListener();
this._startHeartbeats();

// Wait for listener to start before sending commands
setTimeout(() => {
this.turnOnModelLight();

const rl = readline.createInterface({ input: process.stdin });
console.log('Press Enter to exit...');
rl.once('line', () => {
this._running = false;
rl.close();
process.exit(0);
});
}, 500);
}

_send(payload) {
return new Promise((resolve, reject) => {
const socket = new net.Socket();
socket.setTimeout(LA4Client.SEND_DATA_TIMEOUT_SECONDS * 1000);
socket.connect(LA4Client.LA4_PORT, LA4Client.LA4_IP, () => {
socket.write(JSON.stringify(payload), 'utf8', () => {
socket.destroy();
resolve();
});
});
socket.on('timeout', () => { socket.destroy(); reject(new Error('timeout')); });
socket.on('error', reject);
});
}

_basePayload() {
return {
id: this._senderId,
name: LA4Client.SENDER_NAME,
ip: LA4Client.MY_IP,
port: LA4Client.MY_PORT,
version: LA4Client.SENDER_VERSION,
coordinator: true,
};
}

_sendCommand(command) {
return this._send({ ...this._basePayload(), type_: 'cmd', command });
}

turnOnModelLight() {
const sequence = 'LED_FAN,1\\nMODEL_LIGHT,80';
const command = `echo "${sequence}" > /home/rigsters/la4_sequence.csv && boardiocli uSeq && boardiocli runSeq`;
this._sendCommand(command).catch(e => console.error(`Cannot send command - ${e.message}`));
}

_startHeartbeats() {
const tick = () => {
if (!this._running) return;
this._send(this._basePayload()).catch(e => {
if (e.code !== 'ECONNREFUSED' && e.message !== 'timeout') {
console.error(`Cannot send heartbeat to LA4 - ${e.message}`);
}
});
setTimeout(tick, LA4Client.HEARTBEAT_FREQUENCY_SECONDS * 1000);
};
setTimeout(tick, LA4Client.HEARTBEAT_FREQUENCY_SECONDS * 1000);
}

_startListener() {
const server = net.createServer(socket => {
let data = '';
socket.setEncoding('utf8');
socket.on('data', chunk => { data += chunk; });
socket.on('end', () => {
if (!data) return;
let event;
try {
event = JSON.parse(data);
} catch {
return;
}
const name = event.name ?? 'unknown';
switch (event.type_) {
case 'output':
console.log(`[${name}] output: ${event.output}`);
break;
case 'message':
console.log(`[${name}] message: ${event.message}`);
break;
case 'unit_info':
console.log(
`[${name}] unit info — ` +
`firmware: ${event.firmware_version}, ` +
`boardio: ${event.boardio_version}, ` +
`unit type: ${event.unit_type}`
);
break;
}
});
});

server.listen(LA4Client.MY_PORT, () => {
console.log(`Listening on port ${LA4Client.MY_PORT}...`);
});
}
}

new LA4Client().run();