@iotize/tap
Version:
IoTize Device client for Javascript
218 lines (212 loc) • 8.15 kB
JavaScript
import { ConnectionState, ComProtocol } from '@iotize/tap/protocol/api';
import { Subject, defer, of, throwError } from 'rxjs';
import { createDebugger } from '@iotize/common/debug';
import { promiseTimeout } from '@iotize/common/promise';
import { TaskQueue } from '@iotize/common/task-manager';
import { share, timeoutWith, tap, finalize, switchMap } from 'rxjs/operators';
const prefix = '@iotize/tap/protocol/common';
const debug = createDebugger(prefix);
const TAG$1 = 'ComProtocol';
class AbstractComProtocol {
constructor() {
this.connectionState = ConnectionState.DISCONNECTED;
this._options = {
connect: {
timeout: 1000,
},
disconnect: {
timeout: 1000,
},
send: {
timeout: 500,
},
};
}
get options() {
return this._options;
}
set options(options) {
this._options = options;
}
getConnectionState() {
return this.connectionState;
}
isConnected() {
return this.connectionState == ConnectionState.CONNECTED;
}
setConnectionState(connectionState) {
debug(TAG$1, 'setConnectionState', ConnectionState[connectionState], 'Old: ', ConnectionState[this.connectionState]);
let event = {
newState: connectionState,
oldState: this.connectionState,
};
this.connectionState = connectionState;
if (this._connectionStateChange) {
this._connectionStateChange.next(event);
}
return this;
}
/**
* Must be implemented in childs
*/
receiveStream() {
throw new Error('Method not implemented.');
}
onConnectionStateChange() {
if (!this._connectionStateChange) {
this._connectionStateChange = new Subject();
}
return this._connectionStateChange;
}
}
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 = 'ComProtocol';
/**
*
*/
class QueueComProtocol extends AbstractComProtocol {
constructor() {
super();
this._jobQueue = new TaskQueue();
}
_send(data, options) {
return defer(() => __awaiter(this, void 0, void 0, function* () {
if (!this.isConnected()) {
throw ComProtocol.Errors.notConnected({
protocol: this,
});
}
let startTime = new Date();
yield promiseTimeout(options.timeout, this.write(data), (info) => {
return ComProtocol.Errors.timeoutError({
msg: `Communication timeout. Send request to device device timeout (${info.timeout}ms).`,
protocol: this,
timeout: info.timeout,
startTime: info.startTime,
});
});
let elapsedTime = new Date().getTime() - startTime.getTime();
let newTimeout = options.timeout - elapsedTime;
const result = yield promiseTimeout(newTimeout, this.read(), (info) => {
return ComProtocol.Errors.timeoutError({
msg: `Communication timeout. Read response from device timeout (${newTimeout}ms).`,
protocol: this,
timeout: newTimeout,
startTime: info.startTime,
});
});
return result;
})).pipe(share());
}
/**
* Cancel pending requests
*/
cancel() {
debug(TAG, `Canceling operations`);
this._jobQueue.cancelAll(ComProtocol.Errors.operationCanceled());
}
/**
*
* @param data
* @param options
*/
send(data, options) {
// debug(TAG, `QueueComProtocol`, `::send() ENQUEUE request (${data.length} bytes) ${toHexString(data)}. Queue position: ${this._queue.size}`);
if (true) {
// Copy data ? TODO can be removed ?
data = data.slice(0);
}
return this._jobQueue.addExecutor(() => {
let timeout = options ? options.timeout : this._options.send.timeout;
return this._send(data, {
timeout: timeout,
});
});
}
/**
* Connect with timeout
*
* If connect has already been called or is in progress / no further action
*
* @param options
*/
connect(options) {
if (this.connectionState === ConnectionState.CONNECTED) {
debug(TAG, `QueueComProtocol is already connected`);
return of(null);
}
if (this._connect$) {
debug(TAG, `QueueComProtocol is already connecting...`);
return this._connect$;
}
if (this._disconnect$) {
return throwError(ComProtocol.Errors.operationInProgress(`Failed to connect as disconnection is in progress`));
}
let connectTimeout = options
? options.timeout
: this._options.connect.timeout;
this._connect$ = defer(() => {
this.setConnectionState(ConnectionState.CONNECTING);
let startTime = new Date();
return this._connect(options).pipe(timeoutWith(connectTimeout, throwError(ComProtocol.Errors.timeoutError({
msg: 'Protocol connection timeout',
protocol: this,
timeout: connectTimeout,
startTime: startTime,
}))));
}).pipe(tap({
complete: () => {
this.setConnectionState(ConnectionState.CONNECTED);
},
error: () => {
this.setConnectionState(ConnectionState.DISCONNECTED);
},
}), finalize(() => {
this._connect$ = undefined;
}), share());
return this._connect$;
}
disconnect(options) {
if (this.connectionState === ConnectionState.DISCONNECTED) {
debug(TAG, `QueueComProtocol is already disconnected`);
return of();
}
if (this._disconnect$) {
return this._disconnect$;
}
if (this._connect$) {
return throwError(ComProtocol.Errors.operationInProgress(`Failed to disconnect as connection is in progress`));
}
let disconnectTimeout = options
? options.timeout
: this._options.disconnect.timeout;
this._disconnect$ = defer(() => __awaiter(this, void 0, void 0, function* () {
this.setConnectionState(ConnectionState.DISCONNECTING);
})).pipe(switchMap(() => {
let startTime = new Date();
return this._disconnect(options).pipe(timeoutWith(disconnectTimeout, throwError(ComProtocol.Errors.timeoutError({
msg: 'Protocol disconnection timeout',
protocol: this,
timeout: disconnectTimeout,
startTime: startTime,
}))), finalize(() => {
this.setConnectionState(ConnectionState.DISCONNECTED);
this._disconnect$ = undefined;
}));
}), share());
return this._disconnect$;
}
}
/**
* Generated bundle index. Do not edit.
*/
export { AbstractComProtocol, QueueComProtocol };
//# sourceMappingURL=iotize-tap-protocol-core.js.map