Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial support for Snell Kahuna K360 Tally #589

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
"find-package-json": "^1.2.0",
"find-remove": "^4.0.4",
"fs-extra": "^10.1.0",
"hexdump-nodejs": "^0.1.0",
"jquery": "^3.6.0",
"jsonwebtoken": "^9.0.0",
"jspack": "^0.0.4",
Expand Down
115 changes: 115 additions & 0 deletions src/sources/SnellK360.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { logger } from "..";
import { RegisterTallyInput } from "../_decorators/RegisterTallyInput.decorator";
import { FreePort, UsePort } from "../_decorators/UsesPort.decorator";
import { Source } from '../_models/Source';
import { TallyInputConfigField } from "../_types/TallyInputConfigField";
import { TallyInput } from './_Source';
import packet from 'packet';
import net from "net";
import dgram from "dgram";
import { jspack } from "jspack";
import hexdump from 'hexdump-nodejs';

@RegisterTallyInput("59a3c890", "Snell K360", "Uses port 50009 or 50001? normally", [
{ fieldName: 'ip', fieldLabel: 'IP Address', fieldType: 'text' },
{ fieldName: 'port', fieldLabel: 'Port', fieldType: 'port' },
])
export class SnellK360Source extends TallyInput {
private client: any;
private port: number; // AnalogWay Livecore TCP port number
private last_heartbeat: number;
// private tallydata_snellk360: any[] = [];
private heartbeat_interval: NodeJS.Timer;
constructor(source: Source) {
super(source);
this.port = source.data.port;

this.client = new net.Socket();

let parser = packet.createParser();
// parser.packet('k360', "b8 => type, b16 => zero1, b8 => unknown1, b8 => unknown2, b8 => me, b16 => address, b32 => tally, b8[12]{0}z|str('ascii') => label");
parser.packet('k360', "b8 => command, b16 => zero1, b8 => direction, b8 => type, b8 => me, b16 => address, b8{b1 => tally8, b1 => tally7, b1 => tally6, b1 => tally5, b1 => tally4, b1 => tally3, b1 => tally2, b1 => tally1}, b8 => maybetally1, b8 => maybetally2, b8 => maybetally3, b8[12]{0}z|str('ascii') => label");

this.client.on('connect', () => {
this.connected.next(true);

this.last_heartbeat = Date.now();
this.heartbeat_interval = setInterval(() => {
if (Date.now() - this.last_heartbeat > 5000) {
clearInterval(this.heartbeat_interval);
logger(`Source: ${source.name} Snell K360 connection heartbeat timed out`, 'error');
// this.client.end();
// this.client.destroy();
// this.connected.next(false);
}
}, 1000);
Comment on lines +36 to +45
Copy link
Author

@peternewman peternewman Aug 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I copied this from the AnalogWayLivecore but I see there's also some more central/higher level stuff done too so maybe this isn't required...

});

this.client.on('data', (data) => {
logger(`Source: ${source.name} Snell K360 data received ${data.length} bytes.`, 'info-quiet');
// logger('\n' + hexdump(data), 'info-quiet');
parser.extract('k360', (result) => {
this.last_heartbeat = Date.now();
if (result.command == 241) {
logger(`Got "${result.label}" AKA ${result.address} on ME ${result.me} of direction ${result.direction} and type ${result.type}`, 'info-quiet');

if (result.zero1 != 0) {
logger(`Got non-zero zero1`, 'info-quiet');
logger(hexdump(data), 'info-quiet');
}

if (result.direction == 0 && result.type == 1) {
this.addAddress(result.label, result.address);

const busses = [];
if (result.tally8) {
busses.push("program");
}
if (result.tally7) {
busses.push("preview");
}
// TODO: Handle MEs/Stores etc?
this.setBussesForAddress(result.address, busses);

this.sendTallyData();
}
} else {
logger(`Got unexpected command ${result.command}`, 'info-quiet');
logger(hexdump(data), 'info-quiet');
}
// logger(`Remaining buffer size ${data.length}`, 'info-quiet');
});

parser.parse(data);
});

this.client.on('close', () => {
this.connected.next(false);
});

this.client.on('error', (error) => {
logger(`Source: ${source.name} Snell K360 Connection Error occurred: ${error}`, 'error');
});

this.connect();
}


private connect(): void {
this.client.connect(this.port, this.source.data.ip);
}


public reconnect(): void {
this.connect();
}


public exit(): void {
super.exit();
clearInterval(this.heartbeat_interval);
this.client.end();
this.client.destroy();
this.connected.next(false);
}
}