@showcomposer/datalib
Version:
communication libary for ShowComposer
241 lines (240 loc) • 7.88 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
// import dependencies
const nodelogging_1 = require("@hibas123/nodelogging");
const EventEmitter = require("events");
const get = require("get-value");
const merge = require("merge-deep");
const net = require("net");
const readline = require("readline");
const set = require("set-value");
const sendTypes = ["INIT", "INIT_REUSE", "PING", "SET", "ASSIGN", "DEL", "SUB", "UNSUB", "DUMP", "SMSG"];
const responseTypes = ["INIT_ACK", "PONG", "SET_RES", "ASSIGN_RES", "DEL_RES", "SUB_RES", "UNSUB_RES", "DUMP_RES"];
const dataTypes = ["STATIC", "LIVE", "TICK", "LINK"];
class Datalib {
constructor(host = "127.0.0.1", port = 6789) {
this.data = {};
this.reqId = 0;
this.reqWait = 0;
this.reqArray = {};
this.subArray = {};
this.socket = net.createConnection(port, host, () => {
nodelogging_1.Logging.log("Client connected to " + host + ":" + port);
});
// Read line from Socket
// Prepare Input processing
this.inputReader = readline.createInterface({
input: this.socket,
});
this.inputReader.on("line", (l) => {
this.handleLine(l);
});
this.socket.setNoDelay();
// Handle closing
this.socket.on("close", () => {
nodelogging_1.Logging.log("disconnected");
});
}
// Build pkg
send(type = "PING", payload = "", cb = (res) => undefined) {
this.reqId++;
this.socket.write(this.reqId + " " + type + " " + payload + "\r\n");
this.reqArray[this.reqId] = cb;
}
set(key = "", value, sType = "LIVE", cb) {
let payload = sType + " " + key;
if (sType === "TICK") {
value = new Date().getTime();
}
if (typeof value !== "undefined") {
payload += ":" + this.stringToBase64(value);
}
if (cb) {
payload += " 1";
this.send("SET", payload, cb);
}
else {
this.send("SET", payload);
}
set(this.data, key, value || true);
}
getLocal(key) {
return get(this.data, key);
}
assign(key = "", value = {}, sType = "LIVE", cb) {
let payload = sType + " " + key;
if (typeof value !== "undefined") {
payload += " " + this.POJOtoBase64(value);
}
if (cb) {
payload += "1";
this.send("ASSIGN", payload, cb);
}
else {
this.send("ASSIGN", payload);
}
// Set changes local
const deepAssObject = {};
set(deepAssObject, key, value);
this.data = merge(this.data, deepAssObject);
}
subscribe(key = ".") {
const ee = new EventEmitter();
this.send("SUB", key, (res) => {
if (res.length < 2) {
// TODO: Throw Error;
}
this.subArray[res[2]] = ee;
});
return ee;
}
dump(key, cb) {
this.send("DUMP", key, (res) => {
if (res[2]) {
const o = this.base64toPOJO(res[2]);
set(this.data, key, o);
cb(o);
}
else {
nodelogging_1.Logging.error("DUMP: Invalid response");
}
});
}
end() {
this.socket.end();
}
handleLine(c) {
const m = c.toString("utf8").split(" ");
// Check if message has enough params
if (m.length < 2) {
return;
}
// Check if reqId is okay
const id = parseInt(m[0], 10);
if (isNaN(id)) {
return;
}
if (!m[2]) {
m[2] = "";
}
// Determine if it's new req or response
if (responseTypes.includes(m[1])) {
// It's a response
// Check if id exists and handle
if (this.reqArray[m[0]]) {
this.reqArray[m[0]](m);
delete this.reqArray[m[0]];
// here comes response dispatching
}
}
// Message is a request, handle/dispatch
if (sendTypes.includes(m[1])) {
switch (m[1]) {
case "PING":
this.sendRes(m[0]);
break;
case "SMSG":
this.handleSMSG(m);
break;
case "SET":
let res = false;
if (m[4] === "1") {
res = true;
}
// Ignore data types for the client
// m[3] is necessary as it contains data
if (!m[3]) {
if (res) {
this.sendRes(id, "SET_RES", "E NO_DATA");
}
// Logging.warning("SET_RES " + id + " E NO_DATA");
return;
}
// Execute Set command and return/log response.
const p = m[3].split(":");
const key = p[0];
const value = this.base64toString(p[1]) || true;
set(this.data, key, value);
if (res) {
this.sendRes(id, "SET_RES", "0");
}
break;
}
}
// Else: drop
}
// Build pkg res
sendRes(id = 0, type = "PONG", payload = "") {
this.socket.write(id + " " + type + " " + payload + "\r\n");
}
// Handle messages for subscriptions
handleSMSG(m) {
if (m < 6) {
return;
}
const subId = m[2];
if (!this.subArray[subId]) {
return;
}
const ee = this.subArray[subId];
switch (m[3]) {
case "SET":
const payload = m[5].split(":");
let d;
if (payload.length > 1) {
d = this.base64toString(payload[1]);
}
else {
d = true;
}
set(this.data, payload[0], d);
// Emit data event with key and data value
ee.emit("data", payload[0], d);
break;
case "ASSIGN":
if (m < 7) {
nodelogging_1.Logging.debug("Invalid SMSG ASSIGN");
return;
}
const key = m[5];
const value = this.base64toPOJO(m[6]);
// Set changes local
const deepAssObject = {};
set(deepAssObject, key, value);
this.data = merge(this.data, deepAssObject);
ee.emit("data", key, value);
break;
}
}
base64toPOJO(encoded) {
const buff = Buffer.from(encoded, "base64");
const text = buff.toString("utf8");
return JSON.parse(text);
}
POJOtoBase64(obj) {
const str = JSON.stringify(obj);
if (typeof str !== "string") {
nodelogging_1.Logging.error("Invalid object on POJOtoBase64");
return;
}
const buff = Buffer.from(str, "utf8");
const b64 = buff.toString("base64");
return b64;
}
base64toString(encoded) {
const buff = Buffer.from(encoded, "base64");
return buff.toString("utf8");
}
stringToBase64(str) {
if (typeof str !== "string") {
if (!(str = str.toString())) {
nodelogging_1.Logging.error("Invalid value on stringToBase64");
return;
}
}
const buff = Buffer.from(str, "utf8");
const b64 = buff.toString("base64");
return b64;
}
}
exports.Datalib = Datalib;