UNPKG

@iotile/iotile-device

Version:

A typescript library for interfacing with IOTile BLE devices

340 lines 18.8 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 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) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; Object.defineProperty(exports, "__esModule", { value: true }); var config_1 = require("../../config"); var error_space_1 = require("../../common/error-space"); var iotile_common_1 = require("@iotile/iotile-common"); var types_1 = require("./types"); var utilities_1 = require("./utilities"); var utc_reconstruction_1 = require("./utc-reconstruction"); var POD1M = /** @class */ (function (_super) { __extends(POD1M, _super); function POD1M(device, adapter) { var _this = _super.call(this, config_1.catPOD1M) || this; _this.device = device; _this.adapter = adapter; return _this; } POD1M.prototype.getShockInfo = function (shock) { return __awaiter(this, void 0, void 0, function () { var _a, peak, duration, dVx, dVy, dVz, peakVal, peakAxis; return __generator(this, function (_b) { switch (_b.label) { case 0: return [4 /*yield*/, this.adapter.typedRPC(12, 0x8004, 'BB', 'HHlll', [1, shock])]; case 1: _a = _b.sent(), peak = _a[0], duration = _a[1], dVx = _a[2], dVy = _a[3], dVz = _a[4]; peakVal = peak >> 2; peakAxis = peak & 3; return [2 /*return*/, new types_1.ShockInfo(peakVal, peakAxis, duration, dVx, dVy, dVz)]; } }); }); }; POD1M.prototype.getAccelerometerStatus = function () { return __awaiter(this, void 0, void 0, function () { var _a, last_err, shock_counter, tile_state, _unused, state, flags, x, y, z, _unused2, _unused3, TILE_STATE_TABLE, status_1, err_1; return __generator(this, function (_b) { switch (_b.label) { case 0: _b.trys.push([0, 2, , 3]); return [4 /*yield*/, this.adapter.typedRPC(12, 0x8006, "", "LLBBBBHHHBB", [], 3.0)]; case 1: _a = _b.sent(), last_err = _a[0], shock_counter = _a[1], tile_state = _a[2], _unused = _a[3], state = _a[4], flags = _a[5], x = _a[6], y = _a[7], z = _a[8], _unused2 = _a[9], _unused3 = _a[10]; TILE_STATE_TABLE = { 0: "initializing", 1: "capturing", 2: "streaming" }; status_1 = { 'tile_state': TILE_STATE_TABLE[tile_state], 'recording': !!(flags & (1 << 0)), 'settled': !!(flags & (1 << 2)), 'streaming': !!(flags & (1 << 4)), }; return [2 /*return*/, status_1]; case 2: err_1 = _b.sent(); this.logError("Couldn't get accelerometer tile status: ", err_1); throw new error_space_1.ConnectionError("Lost connection to accelerometer tile"); case 3: return [2 /*return*/]; } }); }); }; /** * Download all trip data from a POD-1M. * * This method will download waveforms, trip details and environmental data from a POD-1M. * If the method returns then all trip information was downloaded successfully. Any error * downloading data from the device will cause an exception to be thrown indicating what * went wrong. * * The download process proceeds as follows: * * 1. Sort and then receive up to 100 waveforms from the device. If we are given a * highestReceivedWaveform ID, then all waveforms older than this will be dropped. * On newer device firmware, the dropping will happen inside the device itself. * On older device firmware (Pre POD-1Mv2), we will drop the waveforms after receiving * them. * 2. Check if any of the waveforms need their UTC timestamps fixed up * 3. If UTC fixup is needed, roll back the environmental streamer to the beginning * to make sure that the device sends us all potential waveform timestamp markers * to allow us to assign utc timestamps. * 4. Download environmental, system and trip data from the device. * 5. If UTC fixup is needed, assign utc timestamps to all waveforms stamped in uptime. * 6. Create IOTileEvent objects for each waveform * 7. Return the list of received reports and the IOTileEvent objects which are guaranteed * to have their timestamps in UTC. * * Since many of these steps are fast, not all of them are included in the progress bar. * This routine will call ProgressNotifier.startOne()/finishOne() a total of 6 times so * that is the amount of space in the progress bar that should be allocated for this routine. * * If this routine is called with the possibility of a sub-progress bar, then it will show * progress separately for each report received and for all waveforms as a single progress * bar. * * @param progress A progress notifier that can be used to update a ProgressModal as we go. */ POD1M.prototype.downloadData = function (progress, highestReceivedWaveform) { return __awaiter(this, void 0, void 0, function () { var missingUTCCount, rawWaveforms, decodedWaveforms, key, reports, dropped, waveformEvents; return __generator(this, function (_a) { switch (_a.label) { case 0: missingUTCCount = 0; return [4 /*yield*/, this.getCompressedWaveforms(progress, highestReceivedWaveform)]; case 1: rawWaveforms = _a.sent(); decodedWaveforms = utilities_1.decompressWaveforms(rawWaveforms); for (key in decodedWaveforms) { if (decodedWaveforms[key].utcTimestamp == null) missingUTCCount += 1; } if (!(missingUTCCount > 0)) return [3 /*break*/, 4]; this.logWarning("Found " + missingUTCCount + " waveforms without UTC timestamps, will need to reconstruct timestamps"); return [4 /*yield*/, this.device.acknowledgeStreamerRPC(0, 1, true)]; case 2: _a.sent(); return [4 /*yield*/, this.device.acknowledgeStreamerRPC(2, 1, true)]; case 3: _a.sent(); return [3 /*break*/, 5]; case 4: this.logInfo("All waveforms had UTC timestamps, no reconstruction necessary"); _a.label = 5; case 5: return [4 /*yield*/, this.downloadReports(progress)]; case 6: reports = _a.sent(); if (missingUTCCount > 0) { utc_reconstruction_1.ensureUTCTimestamps(decodedWaveforms, reports); dropped = utc_reconstruction_1.dropNonUTCTimestamps(decodedWaveforms); if (dropped > 0) { this.logError("Dropped " + dropped + " waveforms whose timestamps could not be determined"); } } waveformEvents = utilities_1.createWaveformEvents(decodedWaveforms); return [2 /*return*/, [reports, waveformEvents]]; } }); }); }; POD1M.prototype.sortReadings = function (skipID) { return __awaiter(this, void 0, void 0, function () { var highestN, count; return __generator(this, function (_a) { switch (_a.label) { case 0: if (skipID == null) { skipID = 0; } highestN = 100; return [4 /*yield*/, this.adapter.errorHandlingRPC(12, 0x803a, "LHB", "LL", [skipID, highestN, 0])]; case 1: count = (_a.sent())[0]; return [2 /*return*/, count]; } }); }); }; /** * Download the top 100 waveforms from the POD-1M device. * * This routine will consume 3 progress steps. * */ POD1M.prototype.getCompressedWaveforms = function (notifier, skipID) { return __awaiter(this, void 0, void 0, function () { var expected, received, dropped, _a, err, count, filler, subNotifier, waveforms, header, _b, fmtCode, _unused, compressedSize, uniqueId, timestamp, crcCode, _unused2, _unused3, _unused4, _unused5, waveform, err_2, status_2; return __generator(this, function (_c) { switch (_c.label) { case 0: // Put accelerometer into streaming mode notifier.startOne("Entering Streaming Mode", 1); return [4 /*yield*/, this.adapter.errorHandlingRPC(12, 0x8038, "", "L", [])]; case 1: _c.sent(); notifier.finishOne(); expected = 100; received = 0; dropped = 0; _c.label = 2; case 2: _c.trys.push([2, 11, 12, 19]); notifier.startOne('Sorting Waveform Readings', 1); return [4 /*yield*/, this.sortReadings(skipID)]; case 3: expected = _c.sent(); notifier.finishOne(); /* * Make sure that we have the tracing interface opened so that we receive traced waveforms * and make sure that there is no stale data in it. */ return [4 /*yield*/, this.adapter.enableTracing()]; case 4: /* * Make sure that we have the tracing interface opened so that we receive traced waveforms * and make sure that there is no stale data in it. */ _c.sent(); this.adapter.clearTrace(); return [4 /*yield*/, this.adapter.typedRPC(12, 0x803e, "", "HHH", [], 1.0)]; case 5: _a = _c.sent(), err = _a[0], count = _a[1], filler = _a[2]; if (err) { this.logError("Unable to stream sorted waveforms, error code: " + err); throw new iotile_common_1.BaseError('Unable to Stream Waveforms', 'Unable to stream sorted waveforms from device. Please disconnect and try again.'); } subNotifier = notifier.startOne("Downloading " + count + " Waveforms from Device", count); if (subNotifier == null) subNotifier = new iotile_common_1.ProgressNotifier(); this.logInfo("Receiving " + count + " waveform(s) from device."); if (skipID != null) this.logInfo("Dropping waveforms older than " + skipID); waveforms = {}; received; _c.label = 6; case 6: if (!(received < count)) return [3 /*break*/, 10]; return [4 /*yield*/, this.adapter.waitForTracingData(20)]; case 7: header = _c.sent(); _b = iotile_common_1.unpackArrayBuffer("BBHLLLBBBB", header), fmtCode = _b[0], _unused = _b[1], compressedSize = _b[2], uniqueId = _b[3], timestamp = _b[4], crcCode = _b[5], _unused2 = _b[6], _unused3 = _b[7], _unused4 = _b[8], _unused5 = _b[9]; this.logDebug("Received header " + received + ": uniqueId: " + uniqueId + ", compressedSize: " + compressedSize + ", fmtCode: " + fmtCode); return [4 /*yield*/, this.adapter.waitForTracingData(compressedSize)]; case 8: waveform = _c.sent(); this.logDebug("Received " + compressedSize + " bytes of waveform data"); /** * If we have received old waveforms, don't forward them on */ if (skipID != null && uniqueId <= skipID) { dropped += 1; return [3 /*break*/, 9]; } waveforms[uniqueId] = { "timestamp": timestamp, "crcCode": crcCode, "rawWaveform": waveform }; subNotifier.finishOne(); _c.label = 9; case 9: received++; return [3 /*break*/, 6]; case 10: notifier.finishOne(); this.logInfo("Dropped " + dropped + " waveforms that were older than our skipID"); return [2 /*return*/, waveforms]; case 11: err_2 = _c.sent(); notifier.addError("Error receiving waveforms, successfully received " + received + " of an expected " + expected); this.logError('Error getting compressed waveforms: ', err_2); throw err_2; case 12: return [4 /*yield*/, this.getAccelerometerStatus()]; case 13: status_2 = _c.sent(); _c.label = 14; case 14: if (!status_2.streaming) return [3 /*break*/, 17]; return [4 /*yield*/, iotile_common_1.delay(500)]; case 15: _c.sent(); return [4 /*yield*/, this.getAccelerometerStatus()]; case 16: status_2 = _c.sent(); return [3 /*break*/, 14]; case 17: // Put accelerometer tile back in sleep mode return [4 /*yield*/, this.adapter.errorHandlingRPC(12, 0x8039, "", "L", [])]; case 18: // Put accelerometer tile back in sleep mode _c.sent(); return [7 /*endfinally*/]; case 19: return [2 /*return*/]; } }); }); }; POD1M.prototype.downloadReports = function (notifier) { return __awaiter(this, void 0, void 0, function () { var options, result; return __generator(this, function (_a) { switch (_a.label) { case 0: options = { expectedStreamers: { 0: 'Environmental Report', 1: 'System Report', 2: 'Trip Report' }, requireAll: false }; return [4 /*yield*/, this.device.receiveReports(options, notifier)]; case 1: result = _a.sent(); return [2 /*return*/, result.reports]; } }); }); }; return POD1M; }(iotile_common_1.LoggingBase)); exports.POD1M = POD1M; //# sourceMappingURL=device.js.map