@robotical/ricjs
Version:
Javascript/TS library for Robotical RIC
266 lines • 11 kB
JavaScript
"use strict";
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// RICJS
// Communications Library
//
// Rob Dobson & Chris Greening 2020-2022
// (C) 2020-2022
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const RICConnEvents_1 = require("./RICConnEvents");
const RICLog_1 = tslib_1.__importDefault(require("./RICLog"));
const RICUtils_1 = tslib_1.__importDefault(require("./RICUtils"));
class RICChannelWebBLE {
constructor() {
// Device and characteristics
this._bleDevice = null;
this._characteristicTx = null;
this._characteristicRx = null;
// Message handler
this._ricMsgHandler = null;
// Conn event fn
this._onConnEvent = null;
// Last message tx time
this._msgTxTimeLast = Date.now();
this._msgTxMinTimeBetweenMs = 1;
this.maxRetries = 1;
// Connected flag and retries
this._isConnected = false;
this._maxConnRetries = 3;
// Event listener fn
this._eventListenerFn = null;
// File Handler parameters
this._requestedBatchAckSize = 10;
this._requestedFileBlockSize = 500;
}
fhBatchAckSize() {
return this._requestedBatchAckSize;
}
fhFileBlockSize() {
return this._requestedFileBlockSize;
}
// Set message handler
setMsgHandler(ricMsgHandler) {
this._ricMsgHandler = ricMsgHandler;
}
// BLE interfaces are automatically subscribed to publish messages
requiresSubscription() {
return false;
}
// isEnabled
isEnabled() {
if (navigator.bluetooth) {
RICLog_1.default.error("Web Bluetooth is supported in your browser.");
return true;
}
else {
window.alert("Web Bluetooth API is not available.\n" +
'Please make sure the "Experimental Web Platform features" flag is enabled.');
return false;
}
}
// isConnected
isConnected() {
return this._bleDevice !== null && this._isConnected;
}
// Set onConnEvent handler
setOnConnEvent(connEventFn) {
this._onConnEvent = connEventFn;
}
// Disconnection event
onDisconnected(event) {
const device = event.target;
RICLog_1.default.debug(`RICChannelWebBLE.onDisconnected ${device.name}`);
if (this._bleDevice) {
this._bleDevice.removeEventListener("gattserverdisconnected", this._eventListenerFn);
}
this._isConnected = false;
if (this._onConnEvent) {
this._onConnEvent(RICConnEvents_1.RICConnEvent.CONN_DISCONNECTED_RIC);
}
}
// Get connected locator
getConnectedLocator() {
return this._bleDevice || "";
}
// Connect to a device
async connect(locator) {
// RICLog.debug(`Selected device: ${deviceID}`);
this._bleDevice = locator;
if (this._bleDevice && this._bleDevice.gatt) {
try {
// Connect
for (let connRetry = 0; connRetry < this._maxConnRetries; connRetry++) {
// Connect
await RICUtils_1.default.withTimeout(2000, this._bleDevice.gatt.connect());
RICLog_1.default.debug(`RICChannelWebBLE.connect - ${this._bleDevice.gatt.connected ? "OK" : "FAILED"} connection to device ${this._bleDevice.name}`);
// Get service
try {
const service = await this._bleDevice.gatt.getPrimaryService(RICChannelWebBLE.RICServiceUUID);
RICLog_1.default.debug(`RICChannelWebBLE.connect - found service: ${service.uuid}`);
try {
// Get Tx and Rx characteristics
this._characteristicTx = await service.getCharacteristic(RICChannelWebBLE.RICCmdUUID);
RICLog_1.default.debug(`RICChannelWebBLE.connect - found char ${this._characteristicTx.uuid}`);
this._characteristicRx = await service.getCharacteristic(RICChannelWebBLE.RICRespUUID);
RICLog_1.default.debug(`RICChannelWebBLE.connect - found char ${this._characteristicRx.uuid}`);
// Notifications of received messages
try {
await this._characteristicRx.startNotifications();
RICLog_1.default.debug("RICChannelWebBLE.connect - notifications started");
this._characteristicRx.addEventListener("characteristicvaluechanged", this._onMsgRx.bind(this));
}
catch (error) {
RICLog_1.default.debug("RICChannelWebBLE.connnect - addEventListener failed " + error);
}
// Connected ok
RICLog_1.default.debug(`RICChannelWebBLE.connect ${this._bleDevice.name}`);
// Add disconnect listener
this._eventListenerFn = this.onDisconnected.bind(this);
this._bleDevice.addEventListener("gattserverdisconnected", this._eventListenerFn);
// Connected
this._isConnected = true;
return true;
}
catch (error) {
RICLog_1.default.error(`RICChannelWebBLE.connect - cannot find characteristic: ${error}`);
}
}
catch (error) {
if (connRetry === this._maxConnRetries - 1) {
RICLog_1.default.error(`RICChannelWebBLE.connect - cannot get service ${error}`);
}
else {
RICLog_1.default.debug(`RICChannelWebBLE.connect - cannot get service - retryIdx ${connRetry} ${error}`);
}
}
}
}
catch (error) {
RICLog_1.default.warn(`RICChannelWebBLE.connect - cannot connect ${error}`);
}
// Disconnect
if (this._bleDevice &&
this._bleDevice.gatt &&
this._bleDevice.gatt.connected) {
try {
await this._bleDevice.gatt.disconnect();
}
catch (error) {
RICLog_1.default.warn(`RICChannelWebBLE.connect - cannot disconnect ${error}`);
}
}
}
return false;
}
// Disconnect
async disconnect() {
if (this._bleDevice && this._bleDevice.gatt) {
try {
RICLog_1.default.debug(`RICChannelWebBLE.disconnect GATT`);
await this._bleDevice.gatt.disconnect();
}
catch (error) {
RICLog_1.default.debug(`RICChannelWebBLE.disconnect ${error}`);
}
}
}
pauseConnection(pause) {
RICLog_1.default.verbose(`pauseConnection ${pause} - no effect for this channel type`);
return;
}
// Handle notifications
_onMsgRx(event) {
// Get characteristic
const characteristic = event.target;
// Get value
const value = characteristic.value;
if (value !== undefined) {
const msg = new Uint8Array(value.buffer);
// Handle message
if (this._ricMsgHandler) {
try {
this._ricMsgHandler.handleNewRxMsg(msg);
}
catch (error) {
RICLog_1.default.debug(`RICChannelWebBLE.onMsgRx ${error}`);
}
}
}
}
// Send a message
async sendTxMsg(msg
// _sendWithResponse: boolean
) {
// Check valid
if (this._bleDevice === null) {
return false;
}
// Retry upto maxRetries
for (let retryIdx = 0; retryIdx < this.maxRetries; retryIdx++) {
// Check for min time between messages
while (Date.now() - this._msgTxTimeLast < this._msgTxMinTimeBetweenMs) {
await new Promise((resolve) => setTimeout(resolve, 5));
}
this._msgTxTimeLast = Date.now();
// Write to the characteristic
try {
if (this._characteristicTx) {
if (this._characteristicTx.writeValueWithoutResponse) {
await this._characteristicTx.writeValueWithoutResponse(msg);
}
else if (this._characteristicTx.writeValue) {
await this._characteristicTx.writeValue(msg);
}
else if (this._characteristicTx.writeValueWithResponse) {
await this._characteristicTx.writeValueWithResponse(msg);
}
}
break;
}
catch (error) {
if (retryIdx === this.maxRetries - 1) {
RICLog_1.default.info(`RICChannelWebBLE.sendTxMsg ${error} retried ${retryIdx} times`);
}
}
}
return true;
}
// Send message without awaiting response
async sendTxMsgNoAwait(msg
// _sendWithResponse: boolean
) {
// Check valid
if (this._bleDevice === null) {
return false;
}
// Check for min time between messages
while (Date.now() - this._msgTxTimeLast < this._msgTxMinTimeBetweenMs) {
await new Promise((resolve) => setTimeout(resolve, 5));
}
this._msgTxTimeLast = Date.now();
// Write to the characteristic
if (this._characteristicTx) {
if (this._characteristicTx.writeValueWithoutResponse) {
this._characteristicTx.writeValueWithoutResponse(msg);
}
else if (this._characteristicTx.writeValue) {
this._characteristicTx.writeValue(msg);
}
else if (this._characteristicTx.writeValueWithResponse) {
this._characteristicTx.writeValueWithResponse(msg);
}
return true;
}
return false;
}
}
exports.default = RICChannelWebBLE;
// BLE UUIDS
RICChannelWebBLE.RICServiceUUID = "aa76677e-9cfd-4626-a510-0d305be57c8d";
RICChannelWebBLE.RICCmdUUID = "aa76677e-9cfd-4626-a510-0d305be57c8e";
RICChannelWebBLE.RICRespUUID = "aa76677e-9cfd-4626-a510-0d305be57c8f";
//# sourceMappingURL=RICChannelWebBLE.js.map