@iotize/tap
Version:
IoTize Device client for Javascript
323 lines (317 loc) • 12.7 kB
JavaScript
import { Buffer } from 'buffer';
import { Subject } from 'rxjs';
import { createDebugger } from '@iotize/common/debug';
import { bufferToHexString } from '@iotize/common/byte-converter';
import { CodeError } from '@iotize/common/error';
const prefix = '@iotize/tap/relay';
const debug = createDebugger(prefix);
var __awaiter$1 = (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());
});
};
const TAG$1 = 'Relay';
class RelayServer {
constructor(_server, params) {
this._server = _server;
this._params = params || {
port: 2000,
};
this._clients = [];
}
get server() {
return this._server;
}
addClient(socket) {
this._clients.push(socket);
return this;
}
removeClient(client) {
let index = this._clients.indexOf(client);
if (index >= 0) {
client.close();
debug(TAG$1, `Removing client ${client.getClientName()}...`);
delete this._clients[index];
}
return this;
}
start() {
this._subscription = new Subject();
this._server
.connections()
.subscribe((socket) => __awaiter$1(this, void 0, void 0, function* () {
// Identify this client
let clientName = socket.getClientName();
debug(TAG$1, `[NEW_CLIENT] ${clientName}`);
yield socket.ready();
debug(TAG$1, `[NEW_CLIENT] ${clientName} IS READY`);
// Put this new client in the list
this.addClient(socket);
socket.events().subscribe({
next: (event) => {
switch (event.type) {
case 'data':
let messageString = this.messageToLog(event.payload);
debug(TAG$1, `[MESSAGE] ${messageString} from ${clientName}`);
this.broadcastMessage(event.payload, socket);
break;
case 'error':
debug(TAG$1, `[ERROR] ${clientName} `, event.payload);
// Do we have to remove client every time ?
this.removeClient(socket);
break;
case 'end':
debug(TAG$1, `[CLIENTEND] ${clientName}`);
this.removeClient(socket);
break;
}
},
});
}));
return this._server.listen(this._params);
}
stop() {
return __awaiter$1(this, void 0, void 0, function* () {
this._clients.forEach((client) => {
client.close();
});
if (this._server) {
yield this._server.stop();
}
if (this._subscription) {
this._subscription.complete();
}
});
}
broadcastMessage(message, sender) {
let messageString = this.messageToLog(message);
debug(TAG$1, `[BROADCAST] ${messageString} from ${sender.getClientName()}`);
if (this._clients.length == 0) {
debug(TAG$1, 'There is no client yet');
return;
}
// Send a message to all clients
this._clients.forEach(function (client) {
// Don't want to send it to sender
try {
if (client === sender) {
debug(TAG$1, `\t- Skip sender ${client.getClientName()}`);
return;
}
if (client.isWritable()) {
debug(TAG$1, `\t- Sent to ${client.getClientName()}`);
client.write(message);
}
else {
debug(TAG$1, `Client ${client.getClientName()} is not writable. Cannot send to it`);
}
}
catch (err) {
debug(`Cannot broadcast to ${client.getClientName()}`, err);
}
});
}
messageToLog(message) {
if (typeof message === 'string') {
return `"${message}"`;
}
else if (message instanceof Buffer) {
return bufferToHexString(message);
}
else {
return message;
}
}
}
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());
});
};
const TAG = 'Relay';
class ComRelayError extends CodeError {
static illegalArgument(message) {
return new ComRelayError(ComRelayError.Code.IllegalArgmentError, message);
}
constructor(code, msg) {
super(msg, code);
}
}
(function (ComRelayError) {
let Code;
(function (Code) {
Code["IllegalArgmentError"] = "IllegalArgmentError";
})(Code = ComRelayError.Code || (ComRelayError.Code = {}));
})(ComRelayError || (ComRelayError = {}));
/**
* This is an experimental feature.
*
* @experimental
*/
class ComRelay {
// private IRelayCallback mCallback;
// public final RelayServiceInfoModel mRelayInfo;
// private DefaultSocketServer mSocketServer;
// private WebSocketServer mWebSocketServer;
/**
* @param sourceProtocol protocol that will received commands
* @param targetProtocol protocol on wich commmand will be sent
*/
constructor(sourceProtocol, targetProtocol) {
if (sourceProtocol) {
this.setSourceProtocol(sourceProtocol);
}
if (targetProtocol) {
this.setTargetProtocol(targetProtocol);
}
}
get sourceProtocol() {
if (!this.mSourceProtocol) {
throw ComRelayError.illegalArgument('Missing source protocol');
}
return this.mSourceProtocol;
}
get targetProtocol() {
if (!this.mTargetProtocol) {
throw ComRelayError.illegalArgument('Missing target protocol');
}
return this.mTargetProtocol;
}
setSourceProtocol(protocol) {
if (this.mSourceProtocol) {
this.removeSourceProtocol();
}
this.mSourceProtocol = protocol;
return this;
}
removeSourceProtocol() {
if (this._sourceProtocolSubscription) {
this._sourceProtocolSubscription.unsubscribe();
this._sourceProtocolSubscription = undefined;
}
this.mSourceProtocol = undefined;
return this;
}
setTargetProtocol(protocol) {
this.mTargetProtocol = protocol;
return this;
}
start(options) {
return __awaiter(this, void 0, void 0, function* () {
return promiseTimout(options ? options.timeout || -1 : -1, (resolve, reject) => __awaiter(this, void 0, void 0, function* () {
try {
if (!this.sourceProtocol.isConnected()) {
debug(TAG, 'Connecting to source protocol...');
yield this.sourceProtocol.connect().toPromise();
}
if (!this.targetProtocol.isConnected()) {
debug(TAG, 'Connecting to target protocol...');
yield this.targetProtocol.connect().toPromise();
}
debug(TAG, 'Source/Target are now connected. Listening to messages...');
// We need to reconnect
this.listenToDataStream();
resolve();
}
catch (ex) {
reject(ex);
}
}));
});
}
/**
* Stop relay and disconnect target and source protocol according to given options
* @param options
*/
stop(options) {
var _a, _b, _c, _d;
if (!(options === null || options === void 0 ? void 0 : options.keepTargetProtocolConnected) &&
((_a = this.mTargetProtocol) === null || _a === void 0 ? void 0 : _a.isConnected())) {
(_b = this.mTargetProtocol) === null || _b === void 0 ? void 0 : _b.disconnect();
}
if (!(options === null || options === void 0 ? void 0 : options.keepSourceProtocolConnected) &&
((_c = this.mSourceProtocol) === null || _c === void 0 ? void 0 : _c.isConnected())) {
(_d = this.mSourceProtocol) === null || _d === void 0 ? void 0 : _d.disconnect();
}
}
listenToDataStream() {
// if (!this.mSourceProtocol!.isConnected()){
// await this.mSourceProtocol!.connect();
// }
return new Promise((resolve, reject) => {
if (!this.mSourceProtocol) {
return reject(ComRelayError.illegalArgument(`Missing source protocol`));
}
this._sourceProtocolSubscription = this.mSourceProtocol
.receiveStream()
.subscribe({
next: (data) => {
if (!data) {
debug(TAG, 'Received null event...');
return;
}
if (!this.mTargetProtocol) {
debug(TAG, 'Target protocol is null. Cannot redirect message to it');
return;
}
try {
debug(TAG, `Received new message`, bufferToHexString(data));
this.mTargetProtocol
.send(data)
.toPromise()
.then((response) => {
response = response || new Uint8Array();
debug(TAG, `Target protocol responded with: `, bufferToHexString(response));
return this.sourceProtocol.write(response);
})
.then(() => {
debug(TAG, `Successfully sent response!`);
})
.catch((err) => {
debug(TAG, `Cannot transfer response to source protocol: ${err} JSON=${JSON.stringify(err)}`, err);
});
}
catch (err) {
debug(TAG, `Source cannot transfer data to target due to error: ${err}`, err);
}
},
error: (err) => {
debug(TAG, `Error occured ${err}`);
},
complete: () => {
resolve();
},
});
});
}
onStop() {
// TODO cleanup
return this;
}
}
function promiseTimout(ms, callback) {
return new Promise(function (resolve, reject) {
debug(TAG, `promiseTimout start with ${ms} milliseconds`);
// Set up the real work
callback(resolve, reject);
// Set up the timeout
if (ms >= 0) {
setTimeout(function () {
reject('Promise timed out after ' + ms + ' ms');
}, ms);
}
});
}
/**
* Generated bundle index. Do not edit.
*/
export { ComRelay, ComRelayError, RelayServer };
//# sourceMappingURL=iotize-tap-relay-impl.js.map