UNPKG

@balte/emberplus-connection

Version:
303 lines (302 loc) 13.1 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ServerEvents = exports.EmberServer = void 0; const events_1 = require("events"); const S101Server_1 = require("../Socket/S101Server"); const model_1 = require("../../model"); const types_1 = require("../../types/types"); const util_1 = require("../Lib/util"); const ber_1 = require("../../encodings/ber"); const Command_1 = require("../../model/Command"); const Connection_1 = require("../../model/Connection"); const InvocationResult_1 = require("../../model/InvocationResult"); const ServerEvents = null; exports.ServerEvents = ServerEvents; class EmberServer extends events_1.EventEmitter { constructor(port, address) { super(); this.tree = {}; this._clients = new Set(); this._subscriptions = {}; this.address = address; this.port = port; this._server = new S101Server_1.S101Server(port, address); this._server.on('connection', (client) => { this._clients.add(client); client.on('emberTree', (tree) => this._handleIncoming(tree, client)); client.on('error', (e) => { this.emit('clientError', client, e); }); client.on('disconnected', () => { this._clearSubscription(client); this._clients.delete(client); }); }); } init(tree) { const setParent = (parent, child) => { child.parent = parent; if (child.children) { for (const c of Object.values(child.children)) { setParent(child, c); } } }; for (const rootEl of Object.values(tree)) { if (rootEl.children) { for (const c of Object.values(rootEl.children)) { setParent(rootEl, c); } } } this.tree = tree; this._server.listen(); } discard() { var _a; this._clients.forEach((c) => { c.removeAllListeners(); }); this._clients.clear(); (_a = this._server.server) === null || _a === void 0 ? void 0 : _a.close(); } update(element, update) { if (element.contents.type === model_1.ElementType.Matrix) { const matrix = element; const matrixUpdate = update; if (matrixUpdate.connections) { for (const connection of Object.values(matrixUpdate.connections)) { this.updateMatrixConnection(matrix, connection); } } } for (const [key, value] of Object.entries(update)) { element.contents[key] = value; } const el = util_1.toQualifiedEmberNode(element); const data = ber_1.berEncode([el], types_1.RootType.Elements); let elPath = el.path; if (el.contents.type !== model_1.ElementType.Node && !('targets' in update || 'sources' in update)) { elPath = elPath.slice(0, -2); // remove the last element number } for (const [path, clients] of Object.entries(this._subscriptions)) { if (elPath === path) { clients.forEach((client) => { client.sendBER(data); }); } } } updateMatrixConnection(element, update) { var _a; if (!element.contents.connections) element.contents.connections = {}; let connection = element.contents.connections[update.target]; if (!connection) { element.contents.connections[update.target] = new Connection_1.ConnectionImpl(update.target, []); connection = element.contents.connections[update.target]; } switch (update.operation) { case Connection_1.ConnectionOperation.Connect: for (const source of update.sources || []) { if (!connection.sources.find((v) => v === source)) { connection.sources.push(source); } } break; case Connection_1.ConnectionOperation.Disconnect: for (const source of update.sources || []) { (_a = connection.sources) === null || _a === void 0 ? void 0 : _a.forEach((oldSource, i) => { if (source === oldSource) { connection.sources.splice(i, 1); } }); } break; case Connection_1.ConnectionOperation.Absolute: default: connection.sources = update.sources; break; } const qualified = util_1.toQualifiedEmberNode(element); qualified.contents = new model_1.MatrixImpl(qualified.contents.identifier, undefined, undefined, { [connection.target]: connection }); const data = ber_1.berEncode([qualified], types_1.RootType.Elements); for (const [path, clients] of Object.entries(this._subscriptions)) { if (qualified.path === path) { clients.forEach((client) => { client.sendBER(data); }); } } } _handleIncoming(incoming, client) { for (const rootEl of Object.values(incoming.value)) { if (rootEl.contents.type === model_1.ElementType.Command) { // command on root this._handleCommand('', rootEl, client); } else { this._handleNode(rootEl.path || '', rootEl, client); } } } _handleNode(path, el, client) { const children = Object.values(el.children || {}); if (children[0] && children[0].contents.type === model_1.ElementType.Command) { this._handleCommand(path, children[0], client); return; } else if (el.contents.type === model_1.ElementType.Matrix && 'connections' in el.contents) { this._handleMatrix(path, el); } if (!el.children) { if (el.contents.type === model_1.ElementType.Parameter) { this._handleSetValue(path, el, client); } } else { for (const c of children) { this._handleNode(path + c.number, c, client); } } } _handleMatrix(path, el) { if (this.onMatrixOperation) { const tree = this.getElementByPath(path); if (!tree || tree.contents.type !== model_1.ElementType.Matrix || !el.contents.connections) return; this.onMatrixOperation(tree, el.contents.connections); } } _handleSetValue(path, el, client) { return __awaiter(this, void 0, void 0, function* () { const tree = this.getElementByPath(path); if (!tree || tree.contents.type !== model_1.ElementType.Parameter || el.contents.value === undefined) return; let success = false; if (this.onSetValue) { success = yield this.onSetValue(tree, el.contents.value); } if (!success) { const qualified = util_1.toQualifiedEmberNode(tree); const encoded = ber_1.berEncode([qualified], types_1.RootType.Elements); client.sendBER(encoded); } }); } _handleCommand(path, el, client) { return __awaiter(this, void 0, void 0, function* () { const tree = path ? this.getElementByPath(path) : this.tree; if (!tree) return; if (el.contents.number === Command_1.CommandType.Subscribe) { this._subscribe(path, client); } else if (el.contents.number === Command_1.CommandType.Unsubscribe) { this._unsubscribe(path, client); } else if (el.contents.number === Command_1.CommandType.GetDirectory) { this._subscribe(path, client); // send updates to client this._handleGetDirectory(tree, el.contents.dirFieldMask || Command_1.FieldFlags.Default, client); } else if (el.contents.number === Command_1.CommandType.Invoke) { let result; if (this.onInvocation) { result = yield this.onInvocation(tree, el); } else { result = new InvocationResult_1.InvocationResultImpl(el.contents.invocation.id || -1, false); } const encoded = ber_1.berEncode(result, types_1.RootType.InvocationResult); client.sendBER(encoded); } }); } getElementByPath(path) { const getNext = (elements, i) => Object.values(elements || {}).find((r) => r.number === Number(i) || r.contents.identifier === i || r.contents.description === i); const getNextChild = (node, i) => node.children && getNext(node.children, i); const numberedPath = []; const pathArr = path.split('.'); const i = pathArr.shift(); let tree = getNext(this.tree, i); if (tree === null || tree === void 0 ? void 0 : tree.number) numberedPath.push(tree === null || tree === void 0 ? void 0 : tree.number); while (pathArr.length) { const i = pathArr.shift(); if (!i) break; if (!tree) break; const next = getNextChild(tree, i); if (!next) { // not found return; } tree = next; if (!tree) return; if (tree === null || tree === void 0 ? void 0 : tree.number) numberedPath.push(tree === null || tree === void 0 ? void 0 : tree.number); } return tree; } _subscribe(path, client) { this._subscriptions[path] = [...(this._subscriptions[path] || []), client]; } _unsubscribe(path, client) { if (!this._subscriptions[path]) return; this._subscriptions[path].forEach((c, i) => { if (c === client) { this._subscriptions[path].splice(i, 1); } }); } _clearSubscription(client) { for (const path of Object.keys(this._subscriptions)) { this._unsubscribe(path, client); } } _handleGetDirectory(tree, _dirFieldMasks, client) { if (tree === this.tree) { // getDir on root const response = Object.assign({}, this.tree); for (const [i, rootEl] of Object.entries(this.tree)) { response[i] = new model_1.NumberedTreeNodeImpl(rootEl.number, rootEl.contents); } const data = ber_1.berEncode(response, types_1.RootType.Elements); client.sendBER(data); } else { const qualified = util_1.toQualifiedEmberNode(tree); qualified.children = {}; // destroy ref to this.tree if ('children' in tree && tree.children) { for (const [i, child] of Object.entries(tree.children)) { if (child.contents.type === model_1.ElementType.Matrix) { // matrix should not have connections, targets and sources: qualified.children[i] = new model_1.NumberedTreeNodeImpl(child.number, new model_1.MatrixImpl(child.contents.identifier, undefined, undefined, undefined, child.contents.description, child.contents.matrixType, child.contents.addressingMode, child.contents.targetCount, child.contents.sourceCount, child.contents.maximumTotalConnects, child.contents.maximumConnectsPerTarget, child.contents.parametersLocation, child.contents.gainParameterNumber, child.contents.labels, child.contents.schemaIdentifiers, child.contents.templateReference)); } else { qualified.children[i] = new model_1.NumberedTreeNodeImpl(child.number, child.contents); } } } const data = ber_1.berEncode([qualified], types_1.RootType.Elements); client.sendBER(data); } } } exports.EmberServer = EmberServer;