From 8aef9698faa346b0eaeb4f211f4a58273457c5bd Mon Sep 17 00:00:00 2001 From: Cam Roots Date: Wed, 3 May 2023 20:33:25 +1200 Subject: [PATCH 1/4] Updated package versions --- android/.project | 4 +- android/build.gradle | 4 +- package.json | 5 +- yarn.lock | 168 +++++++++++++++++++++---------------------- 4 files changed, 91 insertions(+), 90 deletions(-) diff --git a/android/.project b/android/.project index ce0bee05..e80c71fe 100644 --- a/android/.project +++ b/android/.project @@ -16,12 +16,12 @@ - 1622026265925 + 1683102094740 30 org.eclipse.core.resources.regexFilterMatcher - node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ diff --git a/android/build.gradle b/android/build.gradle index ac4d6471..05f639d9 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -2,7 +2,7 @@ buildscript { ext { - buildToolsVersion = "29.0.3" + buildToolsVersion = "30.0.2" minSdkVersion = 21 compileSdkVersion = 30 targetSdkVersion = 30 @@ -13,7 +13,7 @@ buildscript { jcenter() } dependencies { - classpath("com.android.tools.build:gradle:4.1.0") + classpath('com.android.tools.build:gradle:4.2.2') // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } diff --git a/package.json b/package.json index 1bb0be7f..4cee8a43 100644 --- a/package.json +++ b/package.json @@ -25,10 +25,11 @@ "start:emulator:nologging": "APP_ENV=emulator.nologging react-native start", "update-splash": "configure-splash-screen --status-bar-hidden -p \"android\" --image-resize-mode \"cover\" --background-color \"#282F46\" --image-path \"./assets/splash.png\"", "build": "cd ./android && ./gradlew assembleRelease", + "build:debug": "cd ./android && ./gradlew assembleDebug", "clean:build": "npm run clean:deep && npm run build" }, "dependencies": { - "@bugsnag/react-native": "^7.9.4", + "@bugsnag/react-native": "^7.19.0", "@react-native-async-storage/async-storage": "^1.15.2", "@react-native-community/cli": "^5.0.1-alpha.2", "@react-native-community/datetimepicker": "^3.4.6", @@ -77,7 +78,7 @@ "path": "^0.12.7", "prop-types": "15.7.2", "react": "17.0.1", - "react-native": "0.64.0", + "react-native": "0.64.4", "react-native-ble-plx": "^2.0.2", "react-native-calendars": "^1.1257.0", "react-native-clean-project": "^3.6.4", diff --git a/yarn.lock b/yarn.lock index 27d21b24..bea84674 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1267,10 +1267,10 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@bugsnag/core@^7.10.0": - version "7.10.0" - resolved "https://registry.yarnpkg.com/@bugsnag/core/-/core-7.10.0.tgz#4b4856141a95a1feadb1afc74265dc6ad8d40cc0" - integrity sha512-sDa2nDxwsxHQx2/2/tsBWjYqH0TewCR8N/r5at6B+irwVkI0uts7Qc2JyqDTfiEiBXKVEXFK+fHTz1x9b8tsiA== +"@bugsnag/core@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@bugsnag/core/-/core-7.19.0.tgz#7663a4addb1322e8315a4012dc9db2aad3fea53b" + integrity sha512-2KGwdaLD9PhR7Wk7xPi3jGuGsKTatc/28U4TOZIDU3CgC2QhGjubwiXSECel5gwxhZ3jACKcMKSV2ovHhv1NrA== dependencies: "@bugsnag/cuid" "^3.0.0" "@bugsnag/safe-json-stringify" "^6.0.0" @@ -1283,72 +1283,72 @@ resolved "https://registry.yarnpkg.com/@bugsnag/cuid/-/cuid-3.0.0.tgz#2ee7642a30aee6dc86f5e7f824653741e42e5c35" integrity sha512-LOt8aaBI+KvOQGneBtpuCz3YqzyEAehd1f3nC5yr9TIYW1+IzYKa2xWS4EiMz5pPOnRPHkyyS5t/wmSmN51Gjg== -"@bugsnag/delivery-react-native@^7.10.0": - version "7.10.0" - resolved "https://registry.yarnpkg.com/@bugsnag/delivery-react-native/-/delivery-react-native-7.10.0.tgz#6c0b122869cf84aaae9ab4c1e2b591f53f4354ae" - integrity sha512-J1hTgZJWL7wvj0k6WC1/nGEJi4UgxrhheI0vB6bROsGo2t4l/aBY87XzhgfSH3W0ktPbCO8UqdCw1cfXlIP/Kw== - -"@bugsnag/plugin-console-breadcrumbs@^7.10.5": - version "7.10.5" - resolved "https://registry.yarnpkg.com/@bugsnag/plugin-console-breadcrumbs/-/plugin-console-breadcrumbs-7.10.5.tgz#1eed51691c423cea978ad900379ef6588748b229" - integrity sha512-bzjef7iUxis9oNB0Ss5vP86j9+0dXJQWMJ5wUlHQci4qI0BsTdRUoRM5Jti/k/PQzQDLVCxN/hIzA8e/DLyG/A== - -"@bugsnag/plugin-network-breadcrumbs@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@bugsnag/plugin-network-breadcrumbs/-/plugin-network-breadcrumbs-7.10.4.tgz#9cf5d40400114c0037003c4a3cde0b40314f1d6b" - integrity sha512-GkS3hToclhZd6U61GZPQi8iearAYDxOkgKgAVDQ4ql7F2W2XhMKSI/TvXXraNu0zVDKNhF1V0VmSFU340XPQJw== - -"@bugsnag/plugin-react-native-client-sync@^7.10.0": - version "7.10.0" - resolved "https://registry.yarnpkg.com/@bugsnag/plugin-react-native-client-sync/-/plugin-react-native-client-sync-7.10.0.tgz#8fd1113f7d740c5bc5e8891952d157f9d0bb1172" - integrity sha512-WdyYItuEGXNav8orMjIfnqCm1VNQpTiDhrCOmx0+Y/I6hPSTAEfNTAsX1/vpotNZi8+gN5W1AvySMkcyKGsBXg== - -"@bugsnag/plugin-react-native-event-sync@^7.10.0": - version "7.10.0" - resolved "https://registry.yarnpkg.com/@bugsnag/plugin-react-native-event-sync/-/plugin-react-native-event-sync-7.10.0.tgz#9b59687148ab8048b293a5a66133724aa89baf1c" - integrity sha512-+B/8Djtlqq+Le/cYO264K3hcELx4+UDO82IP1RDzTLt8lGw4+p3iTABtLrtzx7Xzl02Qt0yC/3haD6Y6u2e7SQ== - -"@bugsnag/plugin-react-native-global-error-handler@^7.10.0": - version "7.10.0" - resolved "https://registry.yarnpkg.com/@bugsnag/plugin-react-native-global-error-handler/-/plugin-react-native-global-error-handler-7.10.0.tgz#db6c81cd93b3e768fa3c780f4bc5207e06ecc71e" - integrity sha512-xM09XAsoVFQCIf0cHMOfSfAinkFK727eLmyZZ1JWihlTDQlCT9eQtzPs0HtT6iQh0RDOfkkyOCQ61N+Roy1BUQ== - -"@bugsnag/plugin-react-native-hermes@^7.10.0": - version "7.10.0" - resolved "https://registry.yarnpkg.com/@bugsnag/plugin-react-native-hermes/-/plugin-react-native-hermes-7.10.0.tgz#ac95098acf3fed9d257cf6653656faa54180075f" - integrity sha512-jh81BXgC88n2LVZ0/7ONop/smDHGE+XdP2PQNa5/OSrS6v4WngpE5Hiv396X0z/yG5w9cCT4SbcmeWep23SKDA== - -"@bugsnag/plugin-react-native-session@^7.10.0": - version "7.10.0" - resolved "https://registry.yarnpkg.com/@bugsnag/plugin-react-native-session/-/plugin-react-native-session-7.10.0.tgz#d2b1296a85a8854fae0b606952e21c01a08202a5" - integrity sha512-OTT68vzDBjKBkntmbHxQPJNXzoiOhM3OuWt9d+bXNgzpLj1hAIN59mVt/6REZnTXCqcgY6rpEFP0UuPehONn4Q== - -"@bugsnag/plugin-react-native-unhandled-rejection@^7.10.5": - version "7.10.5" - resolved "https://registry.yarnpkg.com/@bugsnag/plugin-react-native-unhandled-rejection/-/plugin-react-native-unhandled-rejection-7.10.5.tgz#8dc030920e01861fc9a486b38bc0221e5adbe3e7" - integrity sha512-esdBjxgGJ9mlYyBTtGXrLb2tJmGlVPS755VXhxMmR9zQ79QaWR10e0FIc+F9dQZeQRg1q/XfX6HOy81p/eanRg== - -"@bugsnag/plugin-react@^7.10.0": - version "7.10.0" - resolved "https://registry.yarnpkg.com/@bugsnag/plugin-react/-/plugin-react-7.10.0.tgz#eef05c2d5d2cce45f622cce152d93cf777edf022" - integrity sha512-o0oHd2BfGxFAQTrc1/u0T5RtWntOaeUREnXOfnrX2obi6JthWCcn44YUyUQ0IklcoZ+IAcLy27GNDggs5LfIew== - -"@bugsnag/react-native@^7.9.4": - version "7.10.5" - resolved "https://registry.yarnpkg.com/@bugsnag/react-native/-/react-native-7.10.5.tgz#7710ac4551a71853e9ba5c7725b3dca4d8487ac4" - integrity sha512-u2aWPHLWYOXO6kGbDCc7PvOUGqSxIypVq8l0YnnsoBpL+Iozksmo79R4uxFYEh1R55HUGyuFOH6qBafw3FVn2g== - dependencies: - "@bugsnag/core" "^7.10.0" - "@bugsnag/delivery-react-native" "^7.10.0" - "@bugsnag/plugin-console-breadcrumbs" "^7.10.5" - "@bugsnag/plugin-network-breadcrumbs" "^7.10.4" - "@bugsnag/plugin-react" "^7.10.0" - "@bugsnag/plugin-react-native-client-sync" "^7.10.0" - "@bugsnag/plugin-react-native-event-sync" "^7.10.0" - "@bugsnag/plugin-react-native-global-error-handler" "^7.10.0" - "@bugsnag/plugin-react-native-hermes" "^7.10.0" - "@bugsnag/plugin-react-native-session" "^7.10.0" - "@bugsnag/plugin-react-native-unhandled-rejection" "^7.10.5" +"@bugsnag/delivery-react-native@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@bugsnag/delivery-react-native/-/delivery-react-native-7.19.0.tgz#06e3b2d139a206c2d0ba2bdaa918219e17f067ce" + integrity sha512-Zzl3VOwLDU4KHmf3VweyfNeJcQgL0NzbWG+OCxjCYen093Q4sxNTpWAVBCrYPRjQ2Sq3+D3+YbQg5UUrHL7kig== + +"@bugsnag/plugin-console-breadcrumbs@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@bugsnag/plugin-console-breadcrumbs/-/plugin-console-breadcrumbs-7.19.0.tgz#89773558865f9ee4aa633408f7ab56a741e7b553" + integrity sha512-ZHqPAK0WpbvWjj2wwSV8+C8+K9TOyQsfZnRJ7lIadbeUUJORmFRnG0vUHKBvwxMP7bqCj8fOe/S0kKF3dfMMKA== + +"@bugsnag/plugin-network-breadcrumbs@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@bugsnag/plugin-network-breadcrumbs/-/plugin-network-breadcrumbs-7.19.0.tgz#eb1cca507d6181e09e1b81a0780bfda1d1435fe2" + integrity sha512-Farc0XuUoxv10kJE65zfgZlqujR7TDu8QjwxA4YDxEE41kFM8TAw0CAK15WkQK1UTsNACiiAETZGyU279eB65Q== + +"@bugsnag/plugin-react-native-client-sync@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@bugsnag/plugin-react-native-client-sync/-/plugin-react-native-client-sync-7.19.0.tgz#f54f44562697ab73e9529a5d6f8d5aa6d9e3b388" + integrity sha512-WyK5pZuIzqVrY0h0HimwuODCo9ty9AyDY3q1pmwjrz2y8JTT21nnwUtHybLsp5Rl2oJR4tG06QkWmazgHDkWdA== + +"@bugsnag/plugin-react-native-event-sync@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@bugsnag/plugin-react-native-event-sync/-/plugin-react-native-event-sync-7.19.0.tgz#be12dd1ca3def5e0ca046175fd2b7506778080e2" + integrity sha512-OD73WFkDJAq8AheN2Jap+d17M1mPbEBc1Aulz9FCLs//QwlM2IOij8oarB1iF/wgK6FnIgLFEBPTZpGHuZUsyQ== + +"@bugsnag/plugin-react-native-global-error-handler@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@bugsnag/plugin-react-native-global-error-handler/-/plugin-react-native-global-error-handler-7.19.0.tgz#cbf92d3b782bdc347b06ea2dcb9cbab2c22439df" + integrity sha512-zf+KIHqGEAs2ekAzJCTS0rM1nKrmpIfznBhn72xZJwyfYrh0wbvjZjClDEwxDZ24uNVUUHrIymzdqxpHqVb0lg== + +"@bugsnag/plugin-react-native-hermes@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@bugsnag/plugin-react-native-hermes/-/plugin-react-native-hermes-7.19.0.tgz#d7a5d7918cc679b5ba591fda9dcf40acc18f4de4" + integrity sha512-6SGTSR6NMS2t8j02ZQ6FlA+K/nKkZqvGA+8A7WS/0M8HAShzyoMpZH10kGrU2dcCaiEtmD2T6OGBSbpF+385Dg== + +"@bugsnag/plugin-react-native-session@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@bugsnag/plugin-react-native-session/-/plugin-react-native-session-7.19.0.tgz#52e472fdebfed06e3e6d2bcb6f3d80a07da2e2e2" + integrity sha512-PVwsUstedp9wTqJU/IKdCaMFKP2YrqHXoeBtqRTQ7FFyr0K8wsiW7nZP2jM31VS388hZWSWBlHQPA/3LZ49tNQ== + +"@bugsnag/plugin-react-native-unhandled-rejection@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@bugsnag/plugin-react-native-unhandled-rejection/-/plugin-react-native-unhandled-rejection-7.19.0.tgz#729adda2da1f3483de0195c6f5aee2fd59185b6e" + integrity sha512-+XDk0OoeM6MZhBh7kEalbRwFuhCZST6Y1jOostfz0fhrmT4FdgQYi1FWcPNsUTcjqv7M48pOFZNx8yWI0lGaYg== + +"@bugsnag/plugin-react@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@bugsnag/plugin-react/-/plugin-react-7.19.0.tgz#3f86c6ed2745cd60a4099d0e14ca46f2b9cf501f" + integrity sha512-owC4QXYJWGllMoOPcH5P7sbDIDuFLMCbjGAU6FwH5mBMObSQo+1ViSKImlTJQUFXATM8ySISTBVt7w3C6FFHng== + +"@bugsnag/react-native@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@bugsnag/react-native/-/react-native-7.19.0.tgz#edb786d9d495406d2be3df29473557b3d165da6e" + integrity sha512-/cwuMbO0zwmYCvcCbF43WwDmUOxxolSoNU5kUkZq+keAyM0gTt0cLvsJHGvRVpZBe2PXOLTEUPGVBrtf3OdLVg== + dependencies: + "@bugsnag/core" "^7.19.0" + "@bugsnag/delivery-react-native" "^7.19.0" + "@bugsnag/plugin-console-breadcrumbs" "^7.19.0" + "@bugsnag/plugin-network-breadcrumbs" "^7.19.0" + "@bugsnag/plugin-react" "^7.19.0" + "@bugsnag/plugin-react-native-client-sync" "^7.19.0" + "@bugsnag/plugin-react-native-event-sync" "^7.19.0" + "@bugsnag/plugin-react-native-global-error-handler" "^7.19.0" + "@bugsnag/plugin-react-native-hermes" "^7.19.0" + "@bugsnag/plugin-react-native-session" "^7.19.0" + "@bugsnag/plugin-react-native-unhandled-rejection" "^7.19.0" iserror "^0.0.2" "@bugsnag/safe-json-stringify@^6.0.0": @@ -2265,7 +2265,7 @@ slash "^3.0.0" xmldoc "^1.1.2" -"@react-native-community/cli-platform-android@^5.0.1", "@react-native-community/cli-platform-android@^5.0.1-alpha.0": +"@react-native-community/cli-platform-android@^5.0.1", "@react-native-community/cli-platform-android@^5.0.1-alpha.1": version "5.0.1" resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-android/-/cli-platform-android-5.0.1.tgz#7f761e1818e5a099877ec59a1b739553fd6a6905" integrity sha512-qv9GJX6BJ+Y4qvV34vgxKwwN1cnveXUdP6y2YmTW7XoAYs5YUzKqHajpY58EyucAL2y++6+573t5y4U/9IIoww== @@ -2294,10 +2294,10 @@ plist "^3.0.1" xcode "^2.0.0" -"@react-native-community/cli-platform-ios@^5.0.1-alpha.0": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-5.0.1.tgz#efa9c9b3bba0978d0a26d6442eefeffb5006a196" - integrity sha512-Nr/edBEYJfElgBNvjDevs2BuDicsvQaM8nYkTGgp33pyuCZRBxsYxQqfsNmnLalTzcYaebjWj6AnjUSxzQBWqg== +"@react-native-community/cli-platform-ios@^5.0.1-alpha.1": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-5.0.2.tgz#62485534053c0dad28a67de188248de177f4b0fb" + integrity sha512-IAJ2B3j2BTsQUJZ4R6cVvnTbPq0Vza7+dOgP81ISz2BKRtQ0VqNFv+VOALH2jLaDzf4t7NFlskzIXFqWqy2BLg== dependencies: "@react-native-community/cli-tools" "^5.0.1" chalk "^3.0.0" @@ -2353,7 +2353,7 @@ dependencies: ora "^3.4.0" -"@react-native-community/cli@^5.0.1-alpha.0", "@react-native-community/cli@^5.0.1-alpha.2": +"@react-native-community/cli@^5.0.1-alpha.1", "@react-native-community/cli@^5.0.1-alpha.2": version "5.0.1" resolved "https://registry.yarnpkg.com/@react-native-community/cli/-/cli-5.0.1.tgz#1f7a66d813d5daf102e593f3c550650fa0cc8314" integrity sha512-9VzSYUYSEqxEH5Ib2UNSdn2eyPiYZ4T7Y79o9DKtRBuSaUIwbCUdZtIm+UUjBpLS1XYBkW26FqL8/UdZDmQvXw== @@ -11563,7 +11563,7 @@ ms@^2.1.1: integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== "msupply-ble-service@file:.yalc/msupply-ble-service": - version "0.5.2" + version "0.5.3" dependencies: buffer "^6.0.3" @@ -13427,15 +13427,15 @@ react-native-vector-icons@^8.1.0: prop-types "^15.7.2" yargs "^16.1.1" -react-native@0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.64.0.tgz#c3bde5b638bf8bcf12bae6e094930d39cb942ab7" - integrity sha512-8dhSHBthgGwAjU+OjqUEA49229ThPMQH7URH0u8L0xoQFCnZO2sZ9Wc6KcbxI0x9KSmjCMFFZqRe3w3QsRv64g== +react-native@0.64.4: + version "0.64.4" + resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.64.4.tgz#f9870f6951378421881cc66f6b5a6451bef7254d" + integrity sha512-nxYt/NrTmGyW6+tOd+Hqp4O8uJ2LLZkN7ispMPDprAq7bwvLkF/GXmDQCZHAEyqXuhIztTtMX41KqFQ6UMCUJQ== dependencies: "@jest/create-cache-key-function" "^26.5.0" - "@react-native-community/cli" "^5.0.1-alpha.0" - "@react-native-community/cli-platform-android" "^5.0.1-alpha.0" - "@react-native-community/cli-platform-ios" "^5.0.1-alpha.0" + "@react-native-community/cli" "^5.0.1-alpha.1" + "@react-native-community/cli-platform-android" "^5.0.1-alpha.1" + "@react-native-community/cli-platform-ios" "^5.0.1-alpha.1" "@react-native/assets" "1.0.0" "@react-native/normalize-color" "1.0.0" "@react-native/polyfills" "1.0.0" From 6a74dd788fe2a51b39f5419cd846de80b3b70968 Mon Sep 17 00:00:00 2001 From: Cam Roots Date: Wed, 17 May 2023 23:55:19 +1200 Subject: [PATCH 2/4] Updated msupply-ble-service files via yalc add --- .../lib/commonjs/Bluetooth/BleService.js | 107 +++++++++++------ .../lib/commonjs/Bluetooth/BleService.js.map | 2 +- .../lib/commonjs/Bluetooth/types.js.map | 2 +- .../lib/module/Bluetooth/BleService.js | 108 ++++++++++++------ .../lib/module/Bluetooth/BleService.js.map | 2 +- .../lib/module/Bluetooth/types.js.map | 2 +- .../lib/typescript/Bluetooth/types.d.ts | 2 +- .yalc/msupply-ble-service/package.json | 5 +- .../src/Bluetooth/BleService.ts | 19 ++- .../src/Bluetooth/types.ts | 3 +- .yalc/msupply-ble-service/yalc.sig | 2 +- yalc.lock | 2 +- 12 files changed, 174 insertions(+), 82 deletions(-) diff --git a/.yalc/msupply-ble-service/lib/commonjs/Bluetooth/BleService.js b/.yalc/msupply-ble-service/lib/commonjs/Bluetooth/BleService.js index 56d887f5..47c49c8b 100644 --- a/.yalc/msupply-ble-service/lib/commonjs/Bluetooth/BleService.js +++ b/.yalc/msupply-ble-service/lib/commonjs/Bluetooth/BleService.js @@ -38,6 +38,9 @@ const dummyLogger = { /*do nothing*/ } }; +const RETRY_DELAY = 1000; + +const sleep = delay => new Promise(resolve => setTimeout(resolve, delay)); class BleService { constructor(manager, logger = dummyLogger) { @@ -48,16 +51,18 @@ class BleService { _defineProperty(this, "logger", void 0); _defineProperty(this, "connectToDevice", deviceId => { - this.logger.info('connectToDevice', { - deviceId - }); - return this.manager.connectToDevice(deviceId); + this.logger.debug(`${deviceId} Connect to device`); + + try { + return this.manager.connectToDevice(deviceId); + } catch (e) { + this.logger.error(`${deviceId} Error connecting to device. ${e.message}`); + throw e; + } }); _defineProperty(this, "connectAndDiscoverServices", async deviceDescriptor => { - this.logger.info('connectAndDiscoverServices', { - deviceDescriptor - }); + this.logger.info(`${deviceDescriptor} connectAndDiscoverServices`); const device = this.utils.deviceDescriptorToDevice(deviceDescriptor); // the Blue Maestro devices are incorrectly reporting connection status // thus: deviceIsConnected? { deviceIsConnected: true } // then if disconnecting [BleError: Device D7:D6:67:E0:02:34 is not connected] @@ -65,24 +70,27 @@ class BleService { // to work around this, we disconnect the device, ignoring any errors, before connecting again if (device.deviceType === _constants.BLUE_MAESTRO) { + this.logger.debug(`${deviceDescriptor} Connecting to BM device`); + try { await this.manager.cancelDeviceConnection(device.id); - } catch {// ignore error + } catch (e) { + this.logger.warn(`${deviceDescriptor} Error disconnecting! ${e.message}`); // ignore error } } else { + this.logger.debug(`${deviceDescriptor} Connecting to other device`); const deviceIsConnected = await this.manager.isDeviceConnected(device.id); if (deviceIsConnected) { + this.logger.debug(`${deviceDescriptor} Disconnecting`); await this.manager.cancelDeviceConnection(device.id); } } await this.connectToDevice(device.id); + this.logger.debug(`${device.id} Connected to ${deviceDescriptor}`); await this.manager.discoverAllServicesAndCharacteristicsForDevice(device.id); - this.logger.info('Discovered all services and characteristics for device', { - id: device.id, - manufacturer: device.deviceType.MANUFACTURER_ID - }); + this.logger.info(`${deviceDescriptor} Discovered all services and characteristics. id: ${device.id} manufacturer: ${device.deviceType.MANUFACTURER_ID}`); return device; }); @@ -91,7 +99,7 @@ class BleService { }); _defineProperty(this, "scanForSensors", callback => { - this.logger.info('scanning for sensors', {}); + this.logger.info('Scanning for sensors'); const scanOptions = { scanMode: _types.ScanMode.LowLatency }; @@ -120,8 +128,8 @@ class BleService { _defineProperty(this, "monitorCharacteristic", (device, callback, transactionId) => { return new Promise((resolve, reject) => { - const subscription = this.manager.monitorCharacteristicForDevice(device.id, device.deviceType.BLUETOOTH_UART_SERVICE_UUID, device.deviceType.BLUETOOTH_WRITE_CHARACTERISTIC_UUID, (_, result) => { - callback(result, resolve, reject, subscription); + const subscription = this.manager.monitorCharacteristicForDevice(device.id, device.deviceType.BLUETOOTH_UART_SERVICE_UUID, device.deviceType.BLUETOOTH_WRITE_CHARACTERISTIC_UUID, (error, result) => { + callback(result, resolve, reject, subscription, error); }, transactionId); }); }); @@ -142,7 +150,16 @@ class BleService { return result; }; - const monitoringCallback = (result, resolve, reject, subscription) => { + const monitoringCallback = (result, resolve, reject, subscription, error) => { + this.logger.debug(`${device.id} Monitor command: ${command}`); + this.logger.debug(`${device.id} Monitor callback result valid: ${Boolean(result === null || result === void 0 ? void 0 : result.value)}`); + + if (error) { + this.logger.debug(`${device.id} Monitor callback error name: ${error.name}`); + this.logger.debug(`${device.id} Monitor callback error message: ${error.message}`); + this.logger.debug(`${device.id} Monitor callback error reason: ${error.reason}`); + } + if (result !== null && result !== void 0 && result.value) { data.push(result.value); // return to wait for next chunk @@ -160,6 +177,7 @@ class BleService { return; } + this.logger.debug(`${device.id} Monitor callback. Data length: ${data.length}`); if (data.length === 0) throw new Error(' callback no data returned'); resolve(parser(data)); } catch (e) { @@ -173,6 +191,7 @@ class BleService { return Promise.all([monitor, this.writeCharacteristic(device, command)]).then(r => r[0]).catch(e => { this.manager.cancelTransaction(transactionId); + this.logger.error(`${device.id} writeAndMonitor rejected. ${e.message}`); throw new Error(` writeAndMonitor rejected, ${device.id} ${e.message}`); }); }); @@ -201,6 +220,7 @@ class BleService { }); _defineProperty(this, "clearLogs", async macAddress => { + this.logger.debug(`${macAddress} Clearing logs`); const device = await this.connectAndDiscoverServices(macAddress); if ((device === null || device === void 0 ? void 0 : device.deviceType) === _constants.BT510) { @@ -211,15 +231,13 @@ class BleService { }); _defineProperty(this, "downloadLogs", async macAddress => { + this.logger.debug(`${macAddress} Download logs`); const device = await this.connectAndDiscoverServices(macAddress); - this.logger.info('Download logs connected and discovered services', { - macAddress - }); + this.logger.info(`${macAddress} Download logs connected and discovered services`); const monitorCallback = data => { - this.logger.info('Write and monitor found some data!', { - data - }); + this.logger.info(`${macAddress} Write and monitor found some data! ${data.length}`); + this.logger.debug(`${macAddress} ${data.join('; ')}`); if (device.deviceType === _constants.BLUE_MAESTRO) { const buffer = _buffer.Buffer.concat(data.slice(1).map(datum => this.utils.bufferFromBase64(datum))); @@ -249,11 +267,14 @@ class BleService { if (device.deviceType === _constants.BT510) { // const FIFO = '0'; // const LIFO = '1'; + this.logger.debug(`${macAddress} Preparing to download logs`); + const prepareLogs = async () => { const prepCommand = _constants.BT510.COMMAND_PREPARE_LOG.replace('MODE', '0'); return await this.writeWithSingleResponse(device, prepCommand, data => { const info = this.utils.stringFromBase64(data); + this.logger.debug(`${macAddress} Prepare logs response: ${info}`); return JSON.parse(info).result !== 0; }); }; @@ -273,6 +294,7 @@ class BleService { while (await prepareLogs()) { const downloadCommand = _constants.BT510.COMMAND_DOWNLOAD.replace('NUMEVENTS', '500'); + this.logger.debug(`${macAddress} Sending download command`); const dataLog = await this.writeAndMonitor(device, downloadCommand, monitorCallback); const logBuffer = this.utils.bufferFromBase64(dataLog.data); const log = logBuffer.reduce((acc, _, index) => { @@ -291,10 +313,13 @@ class BleService { }, []); if (await ackLogs(dataLog.numEvents)) { + this.logger.debug(`${macAddress} Ack received`); sensorLog = sensorLog.concat(log); } } } catch (e) { + this.logger.error(`${macAddress} Error downloading logs. ${e.message}`); + if (sensorLog.length === 0) { throw new Error(`downloadLogs ${e.message}`); } // But if we partially succeeded, return that @@ -303,14 +328,22 @@ class BleService { return sensorLog; } else { - const command = _constants.BLUE_MAESTRO.COMMAND_DOWNLOAD.replace('NUMEVENTS', '500'); + try { + const command = _constants.BLUE_MAESTRO.COMMAND_DOWNLOAD.replace('NUMEVENTS', '500'); - const result = await this.writeAndMonitor(device, command, monitorCallback); - return result; + this.logger.debug(`${macAddress} Sending download command`); + const result = await this.writeAndMonitor(device, command, monitorCallback); + return result; + } catch (e) { + this.logger.error(`${macAddress} Error downloading logs! ${e.message}`); + } + + return []; } }); _defineProperty(this, "updateLogInterval", async (macAddress, logInterval, clearLogs = true) => { + this.logger.debug(`${macAddress} Update log interval`); const device = await this.connectAndDiscoverServices(macAddress); const command = device.deviceType.COMMAND_UPDATE_LOG_INTERVAL.replace('LOG_INTERVAL', logInterval.toString()); const result = await this.writeWithSingleResponse(device, command, data => { @@ -329,6 +362,7 @@ class BleService { }); _defineProperty(this, "blink", async macAddress => { + this.logger.debug(`${macAddress} Blink`); const device = await this.connectAndDiscoverServices(macAddress); const result = await this.writeWithSingleResponse(device, device.deviceType.COMMAND_BLINK, data => { const answer = this.utils.stringFromBase64(data); @@ -339,6 +373,7 @@ class BleService { }); _defineProperty(this, "getInfo", async macAddress => { + this.logger.debug(`${macAddress} Get info`); const device = await this.connectAndDiscoverServices(macAddress); const monitorResultCallback = data => { @@ -400,6 +435,7 @@ class BleService { }); _defineProperty(this, "toggleButton", async macAddress => { + this.logger.debug(`${macAddress} Toggle button`); const device = await this.connectAndDiscoverServices(macAddress); if (device.deviceType === _constants.BT510) { @@ -414,44 +450,49 @@ class BleService { }); _defineProperty(this, "getInfoWithRetries", async (macAddress, retriesLeft, error) => { - if (!retriesLeft) throw error; + if (!retriesLeft) { + this.logger.error(`${macAddress} getInfoWithRetries failed. ${error === null || error === void 0 ? void 0 : error.message}`); + throw error; + } + + await sleep(RETRY_DELAY); return this.getInfo(macAddress).catch(err => this.getInfoWithRetries(macAddress, retriesLeft - 1, err)); }); _defineProperty(this, "toggleButtonWithRetries", async (macAddress, retriesLeft, error) => { if (!retriesLeft) throw error; + await sleep(RETRY_DELAY); return this.toggleButton(macAddress).catch(err => this.toggleButtonWithRetries(macAddress, retriesLeft - 1, err)); }); _defineProperty(this, "downloadLogsWithRetries", async (macAddress, retriesLeft, error) => { - this.logger.info('Starting to download logs', { - macAddress, - retriesLeft, - error - }); + this.logger.info(`${macAddress} Download logs with retries`); + this.logger.debug(`${macAddress} Starting to download logs. There are ${retriesLeft} retries left. Error: ${error === null || error === void 0 ? void 0 : error.message}`); if (!retriesLeft) throw error; + await sleep(RETRY_DELAY); return this.downloadLogs(macAddress).catch(err => this.downloadLogsWithRetries(macAddress, retriesLeft - 1, err)); }); _defineProperty(this, "blinkWithRetries", async (macAddress, retriesLeft, error) => { if (!retriesLeft) throw error; + await sleep(RETRY_DELAY); return this.blink(macAddress).catch(err => this.blinkWithRetries(macAddress, retriesLeft - 1, err)); }); _defineProperty(this, "updateLogIntervalWithRetries", async (macAddress, logInterval, retriesLeft, clearLogs, error) => { if (!retriesLeft) throw error; + await sleep(RETRY_DELAY); return this.updateLogInterval(macAddress, logInterval, clearLogs).catch(err => this.updateLogIntervalWithRetries(macAddress, logInterval, retriesLeft - 1, clearLogs, err)); }); this.manager = manager; this.logger = logger; - console.log(`logger is ${JSON.stringify(logger)}`); manager.setLogLevel(_types.LogLevel.Verbose); // Caller passes in utils from the main app, // but we ignore it and use our own. // This needs to be fixed in the main app. this.utils = new _BTUtilService.BtUtilService(); - logger.info('BleService constructor called', {}); + logger.info('BleService constructor called'); } } diff --git a/.yalc/msupply-ble-service/lib/commonjs/Bluetooth/BleService.js.map b/.yalc/msupply-ble-service/lib/commonjs/Bluetooth/BleService.js.map index 9b16cb35..53f4ffbb 100644 --- a/.yalc/msupply-ble-service/lib/commonjs/Bluetooth/BleService.js.map +++ b/.yalc/msupply-ble-service/lib/commonjs/Bluetooth/BleService.js.map @@ -1 +1 @@ -{"version":3,"sources":["BleService.ts"],"names":["dummyLogger","trace","_message","_details","debug","info","warn","error","fatal","setLogLevel","_transportKey","_newLevel","BleService","constructor","manager","logger","deviceId","connectToDevice","deviceDescriptor","device","utils","deviceDescriptorToDevice","deviceType","BLUE_MAESTRO","cancelDeviceConnection","id","deviceIsConnected","isDeviceConnected","discoverAllServicesAndCharacteristicsForDevice","manufacturer","MANUFACTURER_ID","stopDeviceScan","callback","scanOptions","scanMode","ScanMode","LowLatency","filteredCallback","err","console","log","JSON","stringify","manufacturerData","mfgId","Buffer","from","readInt16LE","BT510","descriptor","deviceToDeviceDescriptor","startDeviceScan","command","writeCharacteristicWithoutResponseForDevice","BLUETOOTH_UART_SERVICE_UUID","BLUETOOTH_READ_CHARACTERISTIC_UUID","base64FromString","transactionId","Promise","resolve","reject","subscription","monitorCharacteristicForDevice","BLUETOOTH_WRITE_CHARACTERISTIC_UUID","_","result","Math","random","toString","substr","parser","data","done","alreadyDone","transmissionDone","val","str","stringFromBase64","pattern","RegExp","test","monitoringCallback","value","push","remove","length","Error","e","message","monitor","monitorCharacteristic","all","writeCharacteristic","then","r","catch","cancelTransaction","monitorCharacteristicCallback","macAddress","connectAndDiscoverServices","downloadLogs","writeWithSingleResponse","COMMAND_CLEAR","monitorCallback","buffer","concat","slice","map","datum","bufferFromBase64","ind","findIndex","i","readInt16BE","DELIMITER_A","DELIMITER_B","reduce","acc","index","time","temperature","TEMPERATURE_DIVISOR","parse","numEvents","Number","prepareLogs","prepCommand","COMMAND_PREPARE_LOG","replace","ackLogs","ackCommand","COMMAND_ACK_LOG","sensorLog","downloadCommand","COMMAND_DOWNLOAD","dataLog","writeAndMonitor","logBuffer","round","eventType","readInt8","logInterval","clearLogs","COMMAND_UPDATE_LOG_INTERVAL","match","COMMAND_BLINK","answer","monitorResultCallback","parsedBase64","defaultInfoLog","batteryLevel","isDisabled","blueMaestroBatteryLevel","batteryLevelStringOrNull","isNaN","normaliseNumber","bt510BatteryLevel","parsedInfo","batteryVoltageMv","min","parsedIsDisabled","COMMAND_INFO","COMMAND_DISABLE_BUTTON","retriesLeft","getInfo","getInfoWithRetries","toggleButton","toggleButtonWithRetries","downloadLogsWithRetries","blink","blinkWithRetries","updateLogInterval","updateLogIntervalWithRetries","LogLevel","Verbose","BtUtilService"],"mappings":";;;;;;;AAAA;;AAEA;;AACA;;AAEA;;;;AA4BA,MAAMA,WAAmB,GAAG;AAC1BC,EAAAA,KAAK,EAAE,CAACC,QAAD,EAAWC,QAAX,KAAwB;AAC7B;AACD,GAHyB;AAI1BC,EAAAA,KAAK,EAAE,CAACF,QAAD,EAAWC,QAAX,KAAwB;AAC7B;AACD,GANyB;AAO1BE,EAAAA,IAAI,EAAE,CAACH,QAAD,EAAWC,QAAX,KAAwB;AAC5B;AACD,GATyB;AAU1BG,EAAAA,IAAI,EAAE,CAACJ,QAAD,EAAWC,QAAX,KAAwB;AAC5B;AACD,GAZyB;AAa1BI,EAAAA,KAAK,EAAE,CAACL,QAAD,EAAWC,QAAX,KAAwB;AAC7B;AACD,GAfyB;AAgB1BK,EAAAA,KAAK,EAAE,CAACN,QAAD,EAAWC,QAAX,KAAwB;AAC7B;AACD,GAlByB;AAmB1BM,EAAAA,WAAW,EAAE,CAACC,aAAD,EAAgBC,SAAhB,KAA8B;AACzC;AACD;AArByB,CAA5B;;AAwBO,MAAMC,UAAN,CAAiB;AAItBC,EAAAA,WAAW,CAACC,OAAD,EAA4BC,MAAM,GAAGf,WAArC,EAAkD;AAAA;;AAAA;;AAAA;;AAAA,6CAY1CgB,QAAD,IAAiD;AACjE,WAAKD,MAAL,CAAYV,IAAZ,CAAiB,iBAAjB,EAAoC;AAAEW,QAAAA;AAAF,OAApC;AACA,aAAO,KAAKF,OAAL,CAAaG,eAAb,CAA6BD,QAA7B,CAAP;AACD,KAf4D;;AAAA,wDAiBhC,MAAOE,gBAAP,IAA0D;AACrF,WAAKH,MAAL,CAAYV,IAAZ,CAAiB,4BAAjB,EAA+C;AAAEa,QAAAA;AAAF,OAA/C;AACA,YAAMC,MAAM,GAAG,KAAKC,KAAL,CAAWC,wBAAX,CAAoCH,gBAApC,CAAf,CAFqF,CAGrF;AACA;AACA;AACA;AACA;;AACA,UAAIC,MAAM,CAACG,UAAP,KAAsBC,uBAA1B,EAAwC;AACtC,YAAI;AACF,gBAAM,KAAKT,OAAL,CAAaU,sBAAb,CAAoCL,MAAM,CAACM,EAA3C,CAAN;AACD,SAFD,CAEE,MAAM,CACN;AACD;AACF,OAND,MAMO;AACL,cAAMC,iBAAiB,GAAG,MAAM,KAAKZ,OAAL,CAAaa,iBAAb,CAA+BR,MAAM,CAACM,EAAtC,CAAhC;;AACA,YAAIC,iBAAJ,EAAuB;AACrB,gBAAM,KAAKZ,OAAL,CAAaU,sBAAb,CAAoCL,MAAM,CAACM,EAA3C,CAAN;AACD;AACF;;AACD,YAAM,KAAKR,eAAL,CAAqBE,MAAM,CAACM,EAA5B,CAAN;AAEA,YAAM,KAAKX,OAAL,CAAac,8CAAb,CAA4DT,MAAM,CAACM,EAAnE,CAAN;AACA,WAAKV,MAAL,CAAYV,IAAZ,CAAiB,wDAAjB,EAA2E;AACzEoB,QAAAA,EAAE,EAAEN,MAAM,CAACM,EAD8D;AAEzEI,QAAAA,YAAY,EAAEV,MAAM,CAACG,UAAP,CAAkBQ;AAFyC,OAA3E;AAIA,aAAOX,MAAP;AACD,KA7C4D;;AAAA,sCA+ClD,MAAY;AACrB,WAAKL,OAAL,CAAaiB,cAAb;AACD,KAjD4D;;AAAA,4CAmD3CC,QAAD,IAAkC;AACjD,WAAKjB,MAAL,CAAYV,IAAZ,CAAiB,sBAAjB,EAAyC,EAAzC;AACA,YAAM4B,WAAwB,GAAG;AAAEC,QAAAA,QAAQ,EAAEC,gBAASC;AAArB,OAAjC;;AACA,YAAMC,gBAAgB,GAAG,CAACC,GAAD,EAAuBnB,MAAvB,KAAuD;AAC9E,YAAImB,GAAJ,EAAS;AACPC,UAAAA,OAAO,CAACC,GAAR,CAAY,wBAAZ,EAAsCC,IAAI,CAACC,SAAL,CAAeJ,GAAf,CAAtC;AACD;;AAED,YAAInB,MAAJ,aAAIA,MAAJ,eAAIA,MAAM,CAAEwB,gBAAZ,EAA8B;AAC5B,gBAAMC,KAAK,GAAGC,eAAOC,IAAP,CAAY3B,MAAM,CAACwB,gBAAnB,EAAqC,QAArC,EAA+CI,WAA/C,CAA2D,CAA3D,CAAd;;AACA,cAAIH,KAAK,KAAKrB,wBAAaO,eAAvB,IAA0Cc,KAAK,KAAKI,iBAAMlB,eAA9D,EAA+E;AAC7E,kBAAMmB,UAAU,GAAG,KAAK7B,KAAL,CAAW8B,wBAAX,CAAoC/B,MAAM,CAACM,EAA3C,EAA+CmB,KAA/C,CAAnB;AAEAZ,YAAAA,QAAQ,CAACM,GAAD,EAAMW,UAAN,CAAR;AACD;AACF;AACF,OAbD;;AAcA,WAAKnC,OAAL,CAAaqC,eAAb,CAA6B,IAA7B,EAAmClB,WAAnC,EAAgDI,gBAAhD;AACD,KArE4D;;AAAA,iDAuEvC,OAAOlB,MAAP,EAA4BiC,OAA5B,KAAyE;AAC7F,aAAO,KAAKtC,OAAL,CAAauC,2CAAb,CACLlC,MAAM,CAACM,EADF,EAELN,MAAM,CAACG,UAAP,CAAkBgC,2BAFb,EAGLnC,MAAM,CAACG,UAAP,CAAkBiC,kCAHb,EAIL,KAAKnC,KAAL,CAAWoC,gBAAX,CAA4BJ,OAA5B,CAJK,CAAP;AAMD,KA9E4D;;AAAA,mDAgFrC,CACtBjC,MADsB,EAEtBa,QAFsB,EAGtByB,aAHsB,KAIiC;AACvD,aAAO,IAAIC,OAAJ,CAAY,CAACC,OAAD,EAAUC,MAAV,KAAqB;AACtC,cAAMC,YAAY,GAAG,KAAK/C,OAAL,CAAagD,8BAAb,CACnB3C,MAAM,CAACM,EADY,EAEnBN,MAAM,CAACG,UAAP,CAAkBgC,2BAFC,EAGnBnC,MAAM,CAACG,UAAP,CAAkByC,mCAHC,EAInB,CAACC,CAAD,EAAIC,MAAJ,KAAe;AACbjC,UAAAA,QAAQ,CAACiC,MAAD,EAASN,OAAT,EAAkBC,MAAlB,EAA0BC,YAA1B,CAAR;AACD,SANkB,EAOnBJ,aAPmB,CAArB;AASD,OAVM,CAAP;AAWD,KAhG4D;;AAAA,2CAmG7C,MAAc,MAAMS,IAAI,CAACC,MAAL,GAAcC,QAAd,CAAuB,EAAvB,EAA2BC,MAA3B,CAAkC,CAAlC,EAAqC,CAArC,CAnGyB;;AAAA,6CAqG3C,OAChBlD,MADgB,EAEhBiC,OAFgB,EAGhBkB,MAHgB,KAIuC;AACvD,YAAMC,IAAc,GAAG,EAAvB;AACA,UAAIC,IAAI,GAAG,CAAX;;AACA,YAAMC,WAAW,GAAG,MAAcD,IAAI,EAAtC;;AAEA,YAAME,gBAAgB,GAAIC,GAAD,IAA0B;AACjD,cAAMC,GAAG,GAAG,KAAKxD,KAAL,CAAWyD,gBAAX,CAA4BF,GAA5B,CAAZ;AACA,cAAMG,OAAO,GAAG,IAAIC,MAAJ,CAAW,MAAX,CAAhB,CAFiD,CAEb;;AACpC,cAAMd,MAAM,GAAGa,OAAO,CAACE,IAAR,CAAaJ,GAAb,CAAf;AACA,eAAOX,MAAP;AACD,OALD;;AAOA,YAAMgB,kBAAkF,GAAG,CACzFhB,MADyF,EAEzFN,OAFyF,EAGzFC,MAHyF,EAIzFC,YAJyF,KAKtF;AACH,YAAII,MAAJ,aAAIA,MAAJ,eAAIA,MAAM,CAAEiB,KAAZ,EAAmB;AACjBX,UAAAA,IAAI,CAACY,IAAL,CAAUlB,MAAM,CAACiB,KAAjB,EADiB,CAEjB;;AACA,cAAI/D,MAAM,CAACG,UAAP,KAAsBC,uBAAtB,IAAsC,CAACmD,gBAAgB,CAACT,MAAM,CAACiB,KAAR,CAA3D,EAA2E;AAC5E;;AACD,YAAI;AACFrB,UAAAA,YAAY,CAACuB,MAAb;;AACA,cAAIjE,MAAM,CAACG,UAAP,KAAsB0B,gBAAtB,IAA+ByB,WAAW,EAA9C,EAAkD;AAChD;AACA;AACA;AACA;AACA;AACD;;AACD,cAAIF,IAAI,CAACc,MAAL,KAAgB,CAApB,EAAuB,MAAM,IAAIC,KAAJ,CAAU,4BAAV,CAAN;AACvB3B,UAAAA,OAAO,CAACW,MAAM,CAACC,IAAD,CAAP,CAAP;AACD,SAXD,CAWE,OAAOgB,CAAP,EAAU;AACV3B,UAAAA,MAAM,CAAC,IAAI0B,KAAJ,CAAW,6BAA4BC,CAAC,CAACC,OAAQ,EAAjD,CAAD,CAAN;AACD;AACF,OAzBD,CAZuD,CAqCpD;;;AAEH,YAAM/B,aAAa,GAAG,KAAKA,aAAL,EAAtB;AACA,YAAMgC,OAAO,GAAG,KAAKC,qBAAL,CAA2BvE,MAA3B,EAAmC8D,kBAAnC,EAAuDxB,aAAvD,CAAhB,CAxCuD,CAyCvD;;AACA,aAAOC,OAAO,CAACiC,GAAR,CAAY,CAACF,OAAD,EAAU,KAAKG,mBAAL,CAAyBzE,MAAzB,EAAiCiC,OAAjC,CAAV,CAAZ,EACJyC,IADI,CACCC,CAAC,IAAIA,CAAC,CAAC,CAAD,CADP,EAEJC,KAFI,CAEER,CAAC,IAAI;AACV,aAAKzE,OAAL,CAAakF,iBAAb,CAA+BvC,aAA/B;AACA,cAAM,IAAI6B,KAAJ,CAAW,8BAA6BnE,MAAM,CAACM,EAAG,IAAG8D,CAAC,CAACC,OAAQ,EAA/D,CAAN;AACD,OALI,CAAP;AAMD,KAzJ4D;;AAAA,qDA2JnC,OACxBrE,MADwB,EAExBiC,OAFwB,EAGxBkB,MAHwB,KAI+B;AACvD,YAAM2B,6BAAqE,GAAG,CAC5EhC,MAD4E,EAE5EN,OAF4E,EAG5EC,MAH4E,EAI5EC,YAJ4E,KAKzE;AACHA,QAAAA,YAAY,SAAZ,IAAAA,YAAY,WAAZ,YAAAA,YAAY,CAAEuB,MAAd;;AACA,YAAInB,MAAJ,aAAIA,MAAJ,eAAIA,MAAM,CAAEiB,KAAZ,EAAmB;AACjB,cAAI;AACFvB,YAAAA,OAAO,CAACW,MAAM,CAACL,MAAM,CAACiB,KAAR,CAAP,CAAP;AACD,WAFD,CAEE,OAAOK,CAAP,EAAU;AACV3B,YAAAA,MAAM,CAAC,IAAI0B,KAAJ,CAAW,6BAA4BC,CAAC,CAACC,OAAQ,EAAjD,CAAD,CAAN;AACD;AACF,SAND,MAMO5B,MAAM,CAAC,IAAI0B,KAAJ,CAAW,wBAAX,CAAD,CAAN;AACR,OAdD,CADuD,CAepD;;;AAEH,YAAM7B,aAAa,GAAG,KAAKA,aAAL,EAAtB;AACA,YAAMgC,OAAO,GAAG,KAAKC,qBAAL,CACdvE,MADc,EAEd8E,6BAFc,EAGdxC,aAHc,CAAhB,CAlBuD,CAuBvD;;AACA,aAAOC,OAAO,CAACiC,GAAR,CAAY,CAACF,OAAD,EAAU,KAAKG,mBAAL,CAAyBzE,MAAzB,EAAiCiC,OAAjC,CAAV,CAAZ,EACJyC,IADI,CACCC,CAAC,IAAIA,CAAC,CAAC,CAAD,CADP,EAEJC,KAFI,CAEER,CAAC,IAAI;AACV,aAAKzE,OAAL,CAAakF,iBAAb,CAA+BvC,aAA/B;AACA,cAAM,IAAI6B,KAAJ,CAAW,sCAAqCnE,MAAM,CAACM,EAAG,IAAG8D,CAAC,CAACC,OAAQ,EAAvE,CAAN;AACD,OALI,CAAP;AAMD,KA7L4D;;AAAA,uCAuMjD,MAAOU,UAAP,IAAiD;AAC3D,YAAM/E,MAAM,GAAG,MAAM,KAAKgF,0BAAL,CAAgCD,UAAhC,CAArB;;AACA,UAAI,CAAA/E,MAAM,SAAN,IAAAA,MAAM,WAAN,YAAAA,MAAM,CAAEG,UAAR,MAAuB0B,gBAA3B,EAAkC;AAChC,cAAM,KAAKoD,YAAL,CAAkBF,UAAlB,CAAN;AACD,OAFD,MAEO;AACL,cAAM,KAAKG,uBAAL,CACJlF,MADI,EAEJI,wBAAa+E,aAFT,EAGJ/B,IAAI,IAAI,CAAC,CAAC,KAAKnD,KAAL,CAAWyD,gBAAX,CAA4BN,IAA5B,CAHN,CAAN;AAKD;AACF,KAlN4D;;AAAA,0CAoN9C,MAAO2B,UAAP,IAAwD;AACrE,YAAM/E,MAAM,GAAG,MAAM,KAAKgF,0BAAL,CAAgCD,UAAhC,CAArB;AACA,WAAKnF,MAAL,CAAYV,IAAZ,CAAiB,iDAAjB,EAAoE;AAAE6F,QAAAA;AAAF,OAApE;;AACA,YAAMK,eAA6E,GACjFhC,IADoF,IAEjF;AACH,aAAKxD,MAAL,CAAYV,IAAZ,CAAiB,oCAAjB,EAAuD;AAAEkE,UAAAA;AAAF,SAAvD;;AACA,YAAIpD,MAAM,CAACG,UAAP,KAAsBC,uBAA1B,EAAwC;AACtC,gBAAMiF,MAAM,GAAG3D,eAAO4D,MAAP,CACblC,IAAI,CAACmC,KAAL,CAAW,CAAX,EAAcC,GAAd,CAAkBC,KAAK,IAAI,KAAKxF,KAAL,CAAWyF,gBAAX,CAA4BD,KAA5B,CAA3B,CADa,CAAf;;AAGA,gBAAME,GAAG,GAAGN,MAAM,CAACO,SAAP,CACV,CAAC/C,CAAD,EAAIgD,CAAJ,KACGA,CAAC,GAAG,CAAJ,KAAU,CAAV,IAAeR,MAAM,CAACS,WAAP,CAAmBD,CAAnB,MAA0BzF,wBAAa2F,WAAvD,IACAV,MAAM,CAACS,WAAP,CAAmBD,CAAnB,MAA0BzF,wBAAa4F,WAH/B,CAAZ;AAMA,iBAAQX,MAAM,CAACE,KAAP,CAAa,CAAb,EAAgBI,GAAhB,CAAD,CAAiCM,MAAjC,CAAwC,CAACC,GAAD,EAAmBrD,CAAnB,EAAsBsD,KAAtB,KAAgC;AAC7E,gBAAIA,KAAK,GAAG,CAAR,KAAc,CAAlB,EAAqB,OAAOD,GAAP;AACrB,mBAAO,CACL,GAAGA,GADE,EAEL;AAAEE,cAAAA,IAAI,EAAE,EAAR;AAAYC,cAAAA,WAAW,EAAEhB,MAAM,CAACS,WAAP,CAAmBK,KAAnB,IAA4B/F,wBAAakG;AAAlE,aAFK,CAAP;AAID,WANM,EAMJ,EANI,CAAP;AAOD,SAjBD,MAiBO;AACL;AACA,gBAAMjB,MAAM,GAAG3D,eAAO4D,MAAP,CAAclC,IAAI,CAACoC,GAAL,CAASC,KAAK,IAAI,KAAKxF,KAAL,CAAWyF,gBAAX,CAA4BD,KAA5B,CAAlB,CAAd,CAAf;;AACA,gBAAM3C,MAAM,GAAGxB,IAAI,CAACiF,KAAL,CAAWlB,MAAM,CAACpC,QAAP,EAAX,CAAf;AACA,gBAAMuD,SAAS,GAAGC,MAAM,CAAC3D,MAAM,CAACA,MAAP,CAAc,CAAd,IAAmB,CAApB,CAAxB;AACA,iBAAO;AAAE0D,YAAAA,SAAF;AAAapD,YAAAA,IAAI,EAAEN,MAAM,CAACA,MAAP,CAAc,CAAd;AAAnB,WAAP;AACD;AACF,OA5BD,CAHqE,CA+BlE;;;AAEH,UAAI9C,MAAM,CAACG,UAAP,KAAsB0B,gBAA1B,EAAiC;AAC/B;AACA;AAEA,cAAM6E,WAAW,GAAG,YAA8B;AAChD,gBAAMC,WAAW,GAAG9E,iBAAM+E,mBAAN,CAA0BC,OAA1B,CAAkC,MAAlC,EAA0C,GAA1C,CAApB;;AAEA,iBAAQ,MAAM,KAAK3B,uBAAL,CAA6BlF,MAA7B,EAAqC2G,WAArC,EAAkDvD,IAAI,IAAI;AACtE,kBAAMlE,IAAI,GAAG,KAAKe,KAAL,CAAWyD,gBAAX,CAA4BN,IAA5B,CAAb;AACA,mBAAO9B,IAAI,CAACiF,KAAL,CAAWrH,IAAX,EAAiB4D,MAAjB,KAA4B,CAAnC;AACD,WAHa,CAAd;AAID,SAPD;;AAQA,cAAMgE,OAAO,GAAG,MAAON,SAAP,IAA+C;AAC7D,gBAAMO,UAAU,GAAGlF,iBAAMmF,eAAN,CAAsBH,OAAtB,CAA8B,WAA9B,EAA2CL,SAAS,CAACvD,QAAV,EAA3C,CAAnB;;AACA,iBAAQ,MAAM,KAAKiC,uBAAL,CAA6BlF,MAA7B,EAAqC+G,UAArC,EAAiD3D,IAAI,IAAI;AACrE,kBAAMlE,IAAI,GAAG,KAAKe,KAAL,CAAWyD,gBAAX,CAA4BN,IAA5B,CAAb;AACA,mBAAO9B,IAAI,CAACiF,KAAL,CAAWrH,IAAX,EAAiB4D,MAAjB,KAA4B0D,SAAnC;AACD,WAHa,CAAd;AAID,SAND;;AAQA,YAAIS,SAAS,GAAG,EAAhB;;AACA,YAAI;AACF,iBAAO,MAAMP,WAAW,EAAxB,EAA4B;AAC1B,kBAAMQ,eAAe,GAAGrF,iBAAMsF,gBAAN,CAAuBN,OAAvB,CAA+B,WAA/B,EAA4C,KAA5C,CAAxB;;AACA,kBAAMO,OAAO,GAAI,MAAM,KAAKC,eAAL,CACrBrH,MADqB,EAErBkH,eAFqB,EAGrB9B,eAHqB,CAAvB;AAKA,kBAAMkC,SAAS,GAAG,KAAKrH,KAAL,CAAWyF,gBAAX,CAA4B0B,OAAO,CAAChE,IAApC,CAAlB;AAEA,kBAAM/B,GAAG,GAAGiG,SAAS,CAACrB,MAAV,CAAiB,CAACC,GAAD,EAAmBrD,CAAnB,EAAsBsD,KAAtB,KAAgC;AAC3D,kBAAIA,KAAK,GAAG,CAAR,KAAc,CAAlB,EAAqB,OAAOD,GAAP,CADsC,CAE3D;;AACA,oBAAMG,WAAW,GACftD,IAAI,CAACwE,KAAL,CAAYD,SAAS,CAAC1F,WAAV,CAAsBuE,KAAK,GAAG,CAA9B,IAAmCtE,iBAAMyE,mBAA1C,GAAiE,EAA5E,IAAkF,EADpF;AAEA,oBAAMkB,SAAS,GAAGF,SAAS,CAACG,QAAV,CAAmBtB,KAAK,GAAG,CAA3B,CAAlB,CAL2D,CAM3D;;AACA,kBAAIqB,SAAS,KAAK,CAAlB,EAAqB;AACnB,uBAAO,CACL,GAAGtB,GADE,EAEL;AACEG,kBAAAA;AADF,iBAFK,CAAP;AAMD,eAPD,MAOO;AACL,uBAAO,CAAC,GAAGH,GAAJ,CAAP;AACD;AACF,aAjBW,EAiBT,EAjBS,CAAZ;;AAmBA,gBAAI,MAAMY,OAAO,CAACM,OAAO,CAACZ,SAAT,CAAjB,EAAsC;AACpCS,cAAAA,SAAS,GAAGA,SAAS,CAAC3B,MAAV,CAAiBjE,GAAjB,CAAZ;AACD;AACF;AACF,SAjCD,CAiCE,OAAO+C,CAAP,EAAU;AACV,cAAI6C,SAAS,CAAC/C,MAAV,KAAqB,CAAzB,EAA4B;AAC1B,kBAAM,IAAIC,KAAJ,CAAW,gBAAeC,CAAC,CAACC,OAAQ,EAApC,CAAN;AACD,WAHS,CAIV;;AACD;;AACD,eAAO4C,SAAP;AACD,OA7DD,MA6DO;AACL,cAAMhF,OAAO,GAAG7B,wBAAa+G,gBAAb,CAA8BN,OAA9B,CAAsC,WAAtC,EAAmD,KAAnD,CAAhB;;AACA,cAAM/D,MAAM,GAAI,MAAM,KAAKuE,eAAL,CAAqBrH,MAArB,EAA6BiC,OAA7B,EAAsCmD,eAAtC,CAAtB;AACA,eAAOtC,MAAP;AACD;AACF,KAvT4D;;AAAA,+CAyTzC,OAClBiC,UADkB,EAElB2C,WAFkB,EAGlBC,SAAS,GAAG,IAHM,KAIG;AACrB,YAAM3H,MAAM,GAAG,MAAM,KAAKgF,0BAAL,CAAgCD,UAAhC,CAArB;AAEA,YAAM9C,OAAO,GAAGjC,MAAM,CAACG,UAAP,CAAkByH,2BAAlB,CAA8Cf,OAA9C,CACd,cADc,EAEda,WAAW,CAACzE,QAAZ,EAFc,CAAhB;AAIA,YAAMH,MAAM,GAAG,MAAM,KAAKoC,uBAAL,CAA6BlF,MAA7B,EAAqCiC,OAArC,EAA8CmB,IAAI,IAAI;AACzE,cAAMlE,IAAI,GAAG,KAAKe,KAAL,CAAWyD,gBAAX,CAA4BN,IAA5B,CAAb;AACA,eACGpD,MAAM,CAACG,UAAP,KAAsB0B,gBAAtB,IAA+BP,IAAI,CAACiF,KAAL,CAAWrH,IAAX,EAAiB4D,MAAjB,KAA4B,IAA5D,IACA,CAAC,CAAC5D,IAAI,CAAC2I,KAAL,CAAW,WAAX,CAFJ;AAID,OANoB,CAArB,CAPqB,CAcrB;AACA;AACA;;AACA,UAAIF,SAAS,IAAI3H,MAAM,CAACG,UAAP,KAAsB0B,gBAAvC,EAA8C;AAC5C,cAAM,KAAKoD,YAAL,CAAkBF,UAAlB,CAAN;AACD;;AACD,UAAIjC,MAAJ,EAAY,OAAO,IAAP;AACZ,YAAM,IAAIqB,KAAJ,CAAW,iCAAX,CAAN;AACD,KAnV4D;;AAAA,mCAqVrD,MAAOY,UAAP,IAAoD;AAC1D,YAAM/E,MAAM,GAAG,MAAM,KAAKgF,0BAAL,CAAgCD,UAAhC,CAArB;AACA,YAAMjC,MAAM,GAAI,MAAM,KAAKoC,uBAAL,CACpBlF,MADoB,EAEpBA,MAAM,CAACG,UAAP,CAAkB2H,aAFE,EAGpB1E,IAAI,IAAI;AACN,cAAM2E,MAAM,GAAG,KAAK9H,KAAL,CAAWyD,gBAAX,CAA4BN,IAA5B,CAAf;AACA,eAAO,CAAC,CAAC2E,MAAM,CAACF,KAAP,CAAa,KAAb,CAAT;AACD,OANmB,CAAtB;AASA,UAAI/E,MAAJ,EAAY,OAAO,IAAP;AACZ,YAAM,IAAIqB,KAAJ,CAAW,wBAAX,CAAN;AACD,KAlW4D;;AAAA,qCAoWnD,MAAOY,UAAP,IAAoD;AAC5D,YAAM/E,MAAM,GAAG,MAAM,KAAKgF,0BAAL,CAAgCD,UAAhC,CAArB;;AACA,YAAMiD,qBAAqE,GAAG5E,IAAI,IAAI;AACpF,cAAM6E,YAAY,GAAG7E,IAAI,CAACoC,GAAL,CAAS,KAAKvF,KAAL,CAAWyD,gBAApB,CAArB;AACA,cAAMwE,cAAuB,GAAG;AAAEC,UAAAA,YAAY,EAAE,IAAhB;AAAsBC,UAAAA,UAAU,EAAE;AAAlC,SAAhC;;AACA,cAAMC,uBAAuB,GAAInJ,IAAD,IAAiC;AAC/D,gBAAMoJ,wBAAwB,GAAGpJ,IAAI,CAAC2I,KAAL,CAAW,sBAAX,CAAjC;AAEA,cAAI,CAACS,wBAAL,EAA+B,OAAOA,wBAAP;AAE/B,gBAAMH,YAAY,GAAG1B,MAAM,CAAC6B,wBAAwB,CAAC,CAAD,CAAxB,CAA4BT,KAA5B,CAAkC,YAAlC,CAAD,CAA3B;AAEA,iBAAOpB,MAAM,CAAC8B,KAAP,CAAaJ,YAAb,IACH,IADG,GAEH,KAAKlI,KAAL,CAAWuI,eAAX,CAA2BL,YAA3B,EAAyC,CAAC,EAAD,EAAK,GAAL,CAAzC,CAFJ;AAGD,SAVD;;AAYA,cAAMM,iBAAiB,GAAIvJ,IAAD,IAAiC;AACzD,cAAIiJ,YAAY,GAAG,IAAnB;;AACA,cAAIjJ,IAAJ,EAAU;AACR,kBAAMwJ,UAAU,GAAGpH,IAAI,CAACiF,KAAL,CAAWrH,IAAX,CAAnB;;AAEA,gBAAI,CAAAwJ,UAAU,SAAV,IAAAA,UAAU,WAAV,YAAAA,UAAU,CAAE5F,MAAZ,MAAuB,IAA3B,EAAiC;AAC/B,qBAAO,IAAP;AACD;;AAEDqF,YAAAA,YAAY,GAAG1B,MAAM,CAACiC,UAAU,CAACC,gBAAZ,CAArB;AACD,WARD,MAQO;AACL,mBAAO,IAAP;AACD;;AACD,iBAAOlC,MAAM,CAAC8B,KAAP,CAAaJ,YAAb,IACH,IADG,GAEH,KAAKlI,KAAL,CAAWuI,eAAX,CAA2BzF,IAAI,CAAC6F,GAAL,CAAST,YAAT,EAAuB,IAAvB,CAA3B,EAAyD,CAAC,IAAD,EAAO,IAAP,CAAzD,CAFJ;AAGD,SAhBD;;AAkBA,cAAMU,gBAAgB,GAAI3J,IAAD,IAA2B,CAAC,CAACA,IAAI,CAAC2I,KAAL,CAAW,gBAAX,CAAtD;;AAEA,YAAI7H,MAAM,CAACG,UAAP,KAAsBC,uBAA1B,EAAwC;AACtC,iBAAO6H,YAAY,CAAChC,MAAb,CAAoB,CAACC,GAAD,EAAMhH,IAAN,KAAe;AACxC,kBAAMkJ,UAAU,GAAGS,gBAAgB,CAAC3J,IAAD,CAAnC;AACA,kBAAMiJ,YAAY,GAAGE,uBAAuB,CAACnJ,IAAD,CAA5C;AACA,gBAAIkJ,UAAJ,EAAgB,OAAO,EAAE,GAAGlC,GAAL;AAAUkC,cAAAA;AAAV,aAAP;AAChB,gBAAID,YAAJ,EAAkB,OAAO,EAAE,GAAGjC,GAAL;AAAUiC,cAAAA;AAAV,aAAP;AAClB,mBAAOjC,GAAP;AACD,WANM,EAMJgC,cANI,CAAP;AAOD,SARD,MAQO;AACL,iBAAO;AAAEC,YAAAA,YAAY,EAAEM,iBAAiB,CAACR,YAAY,CAAC,CAAD,CAAb,CAAjC;AAAoDG,YAAAA,UAAU,EAAE;AAAhE,WAAP;AACD;AACF,OA9CD;;AAgDA,YAAMtF,MAAe,GAAI,MAAM,KAAKuE,eAAL,CAC7BrH,MAD6B,EAE7BA,MAAM,CAACG,UAAP,CAAkB2I,YAFW,EAG7Bd,qBAH6B,CAA/B;AAMA,aAAOlF,MAAP;AACD,KA7Z4D;;AAAA,0CA+Z9C,MAAOiC,UAAP,IAAoD;AACjE,YAAM/E,MAAM,GAAG,MAAM,KAAKgF,0BAAL,CAAgCD,UAAhC,CAArB;;AACA,UAAI/E,MAAM,CAACG,UAAP,KAAsB0B,gBAA1B,EAAiC;AAC/B;AACA,eAAO,IAAP;AACD;;AACD,YAAMiB,MAAM,GAAI,MAAM,KAAKoC,uBAAL,CACpBlF,MADoB,EAEpBI,wBAAa2I,sBAFO,EAGpB3F,IAAI,IAAI;AACN,eAAO,CAAC,CAAC,KAAKnD,KAAL,CAAWyD,gBAAX,CAA4BN,IAA5B,EAAkCyE,KAAlC,CAAwC,KAAxC,CAAT;AACD,OALmB,CAAtB;AAOA,aAAO/E,MAAP;AACD,KA7a4D;;AAAA,gDA+axC,OACnBiC,UADmB,EAEnBiE,WAFmB,EAGnB5J,KAHmB,KAIE;AACrB,UAAI,CAAC4J,WAAL,EAAkB,MAAM5J,KAAN;AAElB,aAAO,KAAK6J,OAAL,CAAalE,UAAb,EAAyBH,KAAzB,CAA+BzD,GAAG,IACvC,KAAK+H,kBAAL,CAAwBnE,UAAxB,EAAoCiE,WAAW,GAAG,CAAlD,EAAqD7H,GAArD,CADK,CAAP;AAGD,KAzb4D;;AAAA,qDA2bnC,OACxB4D,UADwB,EAExBiE,WAFwB,EAGxB5J,KAHwB,KAIH;AACrB,UAAI,CAAC4J,WAAL,EAAkB,MAAM5J,KAAN;AAElB,aAAO,KAAK+J,YAAL,CAAkBpE,UAAlB,EAA8BH,KAA9B,CAAoCzD,GAAG,IAC5C,KAAKiI,uBAAL,CAA6BrE,UAA7B,EAAyCiE,WAAW,GAAG,CAAvD,EAA0D7H,GAA1D,CADK,CAAP;AAGD,KArc4D;;AAAA,qDAucnC,OACxB4D,UADwB,EAExBiE,WAFwB,EAGxB5J,KAHwB,KAIC;AACzB,WAAKQ,MAAL,CAAYV,IAAZ,CAAiB,2BAAjB,EAA8C;AAAE6F,QAAAA,UAAF;AAAciE,QAAAA,WAAd;AAA2B5J,QAAAA;AAA3B,OAA9C;AACA,UAAI,CAAC4J,WAAL,EAAkB,MAAM5J,KAAN;AAElB,aAAO,KAAK6F,YAAL,CAAkBF,UAAlB,EAA8BH,KAA9B,CAAoCzD,GAAG,IAC5C,KAAKkI,uBAAL,CAA6BtE,UAA7B,EAAyCiE,WAAW,GAAG,CAAvD,EAA0D7H,GAA1D,CADK,CAAP;AAGD,KAld4D;;AAAA,8CAod1C,OACjB4D,UADiB,EAEjBiE,WAFiB,EAGjB5J,KAHiB,KAII;AACrB,UAAI,CAAC4J,WAAL,EAAkB,MAAM5J,KAAN;AAElB,aAAO,KAAKkK,KAAL,CAAWvE,UAAX,EAAuBH,KAAvB,CAA6BzD,GAAG,IACrC,KAAKoI,gBAAL,CAAsBxE,UAAtB,EAAkCiE,WAAW,GAAG,CAAhD,EAAmD7H,GAAnD,CADK,CAAP;AAGD,KA9d4D;;AAAA,0DAge9B,OAC7B4D,UAD6B,EAE7B2C,WAF6B,EAG7BsB,WAH6B,EAI7BrB,SAJ6B,EAK7BvI,KAL6B,KAMR;AACrB,UAAI,CAAC4J,WAAL,EAAkB,MAAM5J,KAAN;AAElB,aAAO,KAAKoK,iBAAL,CAAuBzE,UAAvB,EAAmC2C,WAAnC,EAAgDC,SAAhD,EAA2D/C,KAA3D,CAAiEzD,GAAG,IACzE,KAAKsI,4BAAL,CAAkC1E,UAAlC,EAA8C2C,WAA9C,EAA2DsB,WAAW,GAAG,CAAzE,EAA4ErB,SAA5E,EAAuFxG,GAAvF,CADK,CAAP;AAGD,KA5e4D;;AAC3D,SAAKxB,OAAL,GAAeA,OAAf;AACA,SAAKC,MAAL,GAAcA,MAAd;AACAwB,IAAAA,OAAO,CAACC,GAAR,CAAa,aAAYC,IAAI,CAACC,SAAL,CAAe3B,MAAf,CAAuB,EAAhD;AACAD,IAAAA,OAAO,CAACL,WAAR,CAAoBoK,gBAASC,OAA7B,EAJ2D,CAK3D;AACA;AACA;;AACA,SAAK1J,KAAL,GAAa,IAAI2J,4BAAJ,EAAb;AACAhK,IAAAA,MAAM,CAACV,IAAP,CAAY,+BAAZ,EAA6C,EAA7C;AACD;;AAdqB","sourcesContent":["import { BtUtilService } from '../BTUtilService';\n\nimport { Buffer } from 'buffer';\nimport { BLUE_MAESTRO, BT510 } from '../constants';\nimport { MacAddress } from '../types/common';\nimport {\n Characteristic,\n ScanOptions,\n ScanMode,\n TypedDevice,\n InfoLog,\n MonitorCharacteristicCallback,\n MonitorCharacteristicParser,\n ScanCallback,\n SensorLog,\n DataLog,\n LogLevel,\n Device,\n BleError,\n} from './types';\nimport { BluetoothManager, MockOrRealDevice } from './BleManager';\n\n// types copied from mobile/src/utilities/logging/\ntype Action = (message: string | Error, details?: Record) => void;\ninterface Logger {\n trace: Action;\n debug: Action;\n info: Action;\n warn: Action;\n error: Action;\n fatal: Action;\n setLogLevel: (transportKey: string, newLevel: number) => void;\n}\nconst dummyLogger: Logger = {\n trace: (_message, _details) => {\n /*do nothing*/\n },\n debug: (_message, _details) => {\n /*do nothing*/\n },\n info: (_message, _details) => {\n /*do nothing*/\n },\n warn: (_message, _details) => {\n /*do nothing*/\n },\n error: (_message, _details) => {\n /*do nothing*/\n },\n fatal: (_message, _details) => {\n /*do nothing*/\n },\n setLogLevel: (_transportKey, _newLevel) => {\n /*do nothing*/\n },\n};\n\nexport class BleService {\n manager: BluetoothManager;\n utils: BtUtilService;\n logger: Logger;\n constructor(manager: BluetoothManager, logger = dummyLogger) {\n this.manager = manager;\n this.logger = logger;\n console.log(`logger is ${JSON.stringify(logger)}`);\n manager.setLogLevel(LogLevel.Verbose);\n // Caller passes in utils from the main app,\n // but we ignore it and use our own.\n // This needs to be fixed in the main app.\n this.utils = new BtUtilService();\n logger.info('BleService constructor called', {});\n }\n\n connectToDevice = (deviceId: string): Promise => {\n this.logger.info('connectToDevice', { deviceId });\n return this.manager.connectToDevice(deviceId);\n };\n\n connectAndDiscoverServices = async (deviceDescriptor: string): Promise => {\n this.logger.info('connectAndDiscoverServices', { deviceDescriptor });\n const device = this.utils.deviceDescriptorToDevice(deviceDescriptor);\n // the Blue Maestro devices are incorrectly reporting connection status\n // thus: deviceIsConnected?\t{ deviceIsConnected: true }\n // then if disconnecting [BleError: Device D7:D6:67:E0:02:34 is not connected]\n // in which case an error is thrown when trying to connect: [BleError: Device ? is already connected]\n // to work around this, we disconnect the device, ignoring any errors, before connecting again\n if (device.deviceType === BLUE_MAESTRO) {\n try {\n await this.manager.cancelDeviceConnection(device.id);\n } catch {\n // ignore error\n }\n } else {\n const deviceIsConnected = await this.manager.isDeviceConnected(device.id);\n if (deviceIsConnected) {\n await this.manager.cancelDeviceConnection(device.id);\n }\n }\n await this.connectToDevice(device.id);\n\n await this.manager.discoverAllServicesAndCharacteristicsForDevice(device.id);\n this.logger.info('Discovered all services and characteristics for device', {\n id: device.id,\n manufacturer: device.deviceType.MANUFACTURER_ID,\n });\n return device;\n };\n\n stopScan = (): void => {\n this.manager.stopDeviceScan();\n };\n\n scanForSensors = (callback: ScanCallback): void => {\n this.logger.info('scanning for sensors', {});\n const scanOptions: ScanOptions = { scanMode: ScanMode.LowLatency };\n const filteredCallback = (err: BleError | null, device: Device | null): void => {\n if (err) {\n console.log('BleService Scan Error:', JSON.stringify(err));\n }\n\n if (device?.manufacturerData) {\n const mfgId = Buffer.from(device.manufacturerData, 'base64').readInt16LE(0);\n if (mfgId === BLUE_MAESTRO.MANUFACTURER_ID || mfgId === BT510.MANUFACTURER_ID) {\n const descriptor = this.utils.deviceToDeviceDescriptor(device.id, mfgId);\n\n callback(err, descriptor);\n }\n }\n };\n this.manager.startDeviceScan(null, scanOptions, filteredCallback);\n };\n\n writeCharacteristic = async (device: TypedDevice, command: string): Promise => {\n return this.manager.writeCharacteristicWithoutResponseForDevice(\n device.id,\n device.deviceType.BLUETOOTH_UART_SERVICE_UUID,\n device.deviceType.BLUETOOTH_READ_CHARACTERISTIC_UUID,\n this.utils.base64FromString(command)\n );\n };\n\n monitorCharacteristic = (\n device: TypedDevice,\n callback: MonitorCharacteristicCallback,\n transactionId: string\n ): Promise => {\n return new Promise((resolve, reject) => {\n const subscription = this.manager.monitorCharacteristicForDevice(\n device.id,\n device.deviceType.BLUETOOTH_UART_SERVICE_UUID,\n device.deviceType.BLUETOOTH_WRITE_CHARACTERISTIC_UUID,\n (_, result) => {\n callback(result, resolve, reject, subscription);\n },\n transactionId\n );\n });\n };\n\n // https://gist.github.com/gordonbrander/2230317\n transactionId = (): string => '_' + Math.random().toString(36).substr(2, 9);\n\n writeAndMonitor = async (\n device: TypedDevice,\n command: string,\n parser: MonitorCharacteristicParser\n ): Promise => {\n const data: string[] = [];\n let done = 0;\n const alreadyDone = (): number => done++;\n\n const transmissionDone = (val: string): boolean => {\n const str = this.utils.stringFromBase64(val);\n const pattern = new RegExp('.*}$'); // workaround for emacs web mode confused by bracket in a regexp literal\n const result = pattern.test(str);\n return result;\n };\n\n const monitoringCallback: MonitorCharacteristicCallback = (\n result,\n resolve,\n reject,\n subscription\n ) => {\n if (result?.value) {\n data.push(result.value);\n // return to wait for next chunk\n if (device.deviceType === BLUE_MAESTRO || !transmissionDone(result.value)) return;\n }\n try {\n subscription.remove();\n if (device.deviceType === BT510 && alreadyDone()) {\n // Don't call the parser more than once.\n // (Although it probably doesn't hurt anything,\n // since the Promise has already resolved and returned the result\n // to the caller)\n return;\n }\n if (data.length === 0) throw new Error(' callback no data returned');\n resolve(parser(data));\n } catch (e) {\n reject(new Error(` callback parsing failed, ${e.message}`));\n }\n }; // end monitoringCallback\n\n const transactionId = this.transactionId();\n const monitor = this.monitorCharacteristic(device, monitoringCallback, transactionId);\n // We only care about the result if both the write and monitor succeed.\n return Promise.all([monitor, this.writeCharacteristic(device, command)])\n .then(r => r[0])\n .catch(e => {\n this.manager.cancelTransaction(transactionId);\n throw new Error(` writeAndMonitor rejected, ${device.id} ${e.message}`);\n });\n };\n\n writeWithSingleResponse = async (\n device: TypedDevice,\n command: string,\n parser: MonitorCharacteristicParser\n ): Promise => {\n const monitorCharacteristicCallback: MonitorCharacteristicCallback = (\n result,\n resolve,\n reject,\n subscription\n ) => {\n subscription?.remove();\n if (result?.value) {\n try {\n resolve(parser(result.value));\n } catch (e) {\n reject(new Error(` callback parsing failed: ${e.message}`));\n }\n } else reject(new Error(` callback returns null`));\n }; // end monitorCharacteristicCallback\n\n const transactionId = this.transactionId();\n const monitor = this.monitorCharacteristic(\n device,\n monitorCharacteristicCallback,\n transactionId\n );\n // We only care about the result if both the write and monitor succeed.\n return Promise.all([monitor, this.writeCharacteristic(device, command)])\n .then(r => r[0])\n .catch(e => {\n this.manager.cancelTransaction(transactionId);\n throw new Error(` writeWithSingleResponse rejected, ${device.id} ${e.message}`);\n });\n };\n\n /** Facade for clearing logs.\n *\n * Connects with a sensor and clears all temperature logs.\n *\n * Returns a promise which resolves to boolean, which is ignored by the caller.\n *\n * @param {String} macAddress\n */\n clearLogs = async (macAddress: MacAddress): Promise => {\n const device = await this.connectAndDiscoverServices(macAddress);\n if (device?.deviceType === BT510) {\n await this.downloadLogs(macAddress);\n } else {\n await this.writeWithSingleResponse(\n device,\n BLUE_MAESTRO.COMMAND_CLEAR,\n data => !!this.utils.stringFromBase64(data)\n );\n }\n };\n\n downloadLogs = async (macAddress: MacAddress): Promise => {\n const device = await this.connectAndDiscoverServices(macAddress);\n this.logger.info('Download logs connected and discovered services', { macAddress });\n const monitorCallback: MonitorCharacteristicParser = (\n data: string[]\n ) => {\n this.logger.info('Write and monitor found some data!', { data });\n if (device.deviceType === BLUE_MAESTRO) {\n const buffer = Buffer.concat(\n data.slice(1).map(datum => this.utils.bufferFromBase64(datum))\n );\n const ind = buffer.findIndex(\n (_, i) =>\n (i % 2 === 0 && buffer.readInt16BE(i) === BLUE_MAESTRO.DELIMITER_A) ||\n buffer.readInt16BE(i) === BLUE_MAESTRO.DELIMITER_B\n );\n\n return (buffer.slice(0, ind) as Buffer).reduce((acc: SensorLog[], _, index) => {\n if (index % 2 !== 0) return acc;\n return [\n ...acc,\n { time: '', temperature: buffer.readInt16BE(index) / BLUE_MAESTRO.TEMPERATURE_DIVISOR },\n ];\n }, []);\n } else {\n // BT510\n const buffer = Buffer.concat(data.map(datum => this.utils.bufferFromBase64(datum)));\n const result = JSON.parse(buffer.toString());\n const numEvents = Number(result.result[0] / 8);\n return { numEvents, data: result.result[1] };\n }\n }; // end monitor callback\n\n if (device.deviceType === BT510) {\n // const FIFO = '0';\n // const LIFO = '1';\n\n const prepareLogs = async (): Promise => {\n const prepCommand = BT510.COMMAND_PREPARE_LOG.replace('MODE', '0');\n\n return (await this.writeWithSingleResponse(device, prepCommand, data => {\n const info = this.utils.stringFromBase64(data);\n return JSON.parse(info).result !== 0;\n })) as boolean;\n };\n const ackLogs = async (numEvents: number): Promise => {\n const ackCommand = BT510.COMMAND_ACK_LOG.replace('NUMEVENTS', numEvents.toString());\n return (await this.writeWithSingleResponse(device, ackCommand, data => {\n const info = this.utils.stringFromBase64(data);\n return JSON.parse(info).result === numEvents;\n })) as boolean;\n };\n\n let sensorLog = [] as SensorLog[];\n try {\n while (await prepareLogs()) {\n const downloadCommand = BT510.COMMAND_DOWNLOAD.replace('NUMEVENTS', '500');\n const dataLog = (await this.writeAndMonitor(\n device,\n downloadCommand,\n monitorCallback\n )) as DataLog;\n const logBuffer = this.utils.bufferFromBase64(dataLog.data);\n\n const log = logBuffer.reduce((acc: SensorLog[], _, index) => {\n if (index % 8 !== 0) return acc;\n //const time = logBuffer.readInt32LE(index);\n const temperature =\n Math.round((logBuffer.readInt16LE(index + 4) / BT510.TEMPERATURE_DIVISOR) * 10) / 10;\n const eventType = logBuffer.readInt8(index + 6);\n //const salt = logBuffer.readInt8(index + 7);\n if (eventType === 1) {\n return [\n ...acc,\n {\n temperature,\n },\n ];\n } else {\n return [...acc];\n }\n }, []);\n\n if (await ackLogs(dataLog.numEvents)) {\n sensorLog = sensorLog.concat(log);\n }\n }\n } catch (e) {\n if (sensorLog.length === 0) {\n throw new Error(`downloadLogs ${e.message}`);\n }\n // But if we partially succeeded, return that\n }\n return sensorLog;\n } else {\n const command = BLUE_MAESTRO.COMMAND_DOWNLOAD.replace('NUMEVENTS', '500');\n const result = (await this.writeAndMonitor(device, command, monitorCallback)) as SensorLog[];\n return result;\n }\n };\n\n updateLogInterval = async (\n macAddress: MacAddress,\n logInterval: number,\n clearLogs = true\n ): Promise => {\n const device = await this.connectAndDiscoverServices(macAddress);\n\n const command = device.deviceType.COMMAND_UPDATE_LOG_INTERVAL.replace(\n 'LOG_INTERVAL',\n logInterval.toString()\n );\n const result = await this.writeWithSingleResponse(device, command, data => {\n const info = this.utils.stringFromBase64(data);\n return (\n (device.deviceType === BT510 && JSON.parse(info).result === 'ok') ||\n !!info.match(/interval/i)\n );\n });\n // Clear logs if we haven't just downloaded\n // BlueMaestro automatically clears logs when log interval is set,\n // But we have to download all the logs to clear them on BT510\n if (clearLogs && device.deviceType === BT510) {\n await this.downloadLogs(macAddress);\n }\n if (result) return true;\n throw new Error(` command returned not OK result`);\n };\n\n blink = async (macAddress: MacAddress): Promise => {\n const device = await this.connectAndDiscoverServices(macAddress);\n const result = (await this.writeWithSingleResponse(\n device,\n device.deviceType.COMMAND_BLINK,\n data => {\n const answer = this.utils.stringFromBase64(data);\n return !!answer.match(/ok/i);\n }\n )) as boolean;\n\n if (result) return true;\n throw new Error(` acknowledgement false`);\n };\n\n getInfo = async (macAddress: MacAddress): Promise => {\n const device = await this.connectAndDiscoverServices(macAddress);\n const monitorResultCallback: MonitorCharacteristicParser = data => {\n const parsedBase64 = data.map(this.utils.stringFromBase64);\n const defaultInfoLog: InfoLog = { batteryLevel: null, isDisabled: true };\n const blueMaestroBatteryLevel = (info: string): number | null => {\n const batteryLevelStringOrNull = info.match(/Batt lvl: [0-9]{1,3}/);\n\n if (!batteryLevelStringOrNull) return batteryLevelStringOrNull;\n\n const batteryLevel = Number(batteryLevelStringOrNull[0].match(/[0-9]{1,3}/));\n\n return Number.isNaN(batteryLevel)\n ? null\n : this.utils.normaliseNumber(batteryLevel, [75, 100]);\n };\n\n const bt510BatteryLevel = (info: string): number | null => {\n let batteryLevel = null;\n if (info) {\n const parsedInfo = JSON.parse(info);\n\n if (parsedInfo?.result !== 'ok') {\n return null;\n }\n\n batteryLevel = Number(parsedInfo.batteryVoltageMv);\n } else {\n return null;\n }\n return Number.isNaN(batteryLevel)\n ? null\n : this.utils.normaliseNumber(Math.min(batteryLevel, 3000), [2250, 3000]);\n };\n\n const parsedIsDisabled = (info: string): boolean => !!info.match(/Btn on\\/off: 1/);\n\n if (device.deviceType === BLUE_MAESTRO) {\n return parsedBase64.reduce((acc, info) => {\n const isDisabled = parsedIsDisabled(info);\n const batteryLevel = blueMaestroBatteryLevel(info);\n if (isDisabled) return { ...acc, isDisabled };\n if (batteryLevel) return { ...acc, batteryLevel };\n return acc;\n }, defaultInfoLog);\n } else {\n return { batteryLevel: bt510BatteryLevel(parsedBase64[0]), isDisabled: true };\n }\n };\n\n const result: InfoLog = (await this.writeAndMonitor(\n device,\n device.deviceType.COMMAND_INFO,\n monitorResultCallback\n )) as InfoLog;\n\n return result;\n };\n\n toggleButton = async (macAddress: MacAddress): Promise => {\n const device = await this.connectAndDiscoverServices(macAddress);\n if (device.deviceType === BT510) {\n // Laird doesn't have this command\n return true;\n }\n const result = (await this.writeWithSingleResponse(\n device,\n BLUE_MAESTRO.COMMAND_DISABLE_BUTTON,\n data => {\n return !!this.utils.stringFromBase64(data).match(/ok/i);\n }\n )) as boolean;\n return result;\n };\n\n getInfoWithRetries = async (\n macAddress: MacAddress,\n retriesLeft: number,\n error: Error | null\n ): Promise => {\n if (!retriesLeft) throw error;\n\n return this.getInfo(macAddress).catch(err =>\n this.getInfoWithRetries(macAddress, retriesLeft - 1, err)\n );\n };\n\n toggleButtonWithRetries = async (\n macAddress: MacAddress,\n retriesLeft: number,\n error: Error | null\n ): Promise => {\n if (!retriesLeft) throw error;\n\n return this.toggleButton(macAddress).catch(err =>\n this.toggleButtonWithRetries(macAddress, retriesLeft - 1, err)\n );\n };\n\n downloadLogsWithRetries = async (\n macAddress: MacAddress,\n retriesLeft: number,\n error: Error | null\n ): Promise => {\n this.logger.info('Starting to download logs', { macAddress, retriesLeft, error });\n if (!retriesLeft) throw error;\n\n return this.downloadLogs(macAddress).catch(err =>\n this.downloadLogsWithRetries(macAddress, retriesLeft - 1, err)\n );\n };\n\n blinkWithRetries = async (\n macAddress: MacAddress,\n retriesLeft: number,\n error: Error | null\n ): Promise => {\n if (!retriesLeft) throw error;\n\n return this.blink(macAddress).catch(err =>\n this.blinkWithRetries(macAddress, retriesLeft - 1, err)\n );\n };\n\n updateLogIntervalWithRetries = async (\n macAddress: MacAddress,\n logInterval: number,\n retriesLeft: number,\n clearLogs: boolean,\n error: Error | null\n ): Promise => {\n if (!retriesLeft) throw error;\n\n return this.updateLogInterval(macAddress, logInterval, clearLogs).catch(err =>\n this.updateLogIntervalWithRetries(macAddress, logInterval, retriesLeft - 1, clearLogs, err)\n );\n };\n}\n"]} \ No newline at end of file +{"version":3,"sources":["BleService.ts"],"names":["dummyLogger","trace","_message","_details","debug","info","warn","error","fatal","setLogLevel","_transportKey","_newLevel","RETRY_DELAY","sleep","delay","Promise","resolve","setTimeout","BleService","constructor","manager","logger","deviceId","connectToDevice","e","message","deviceDescriptor","device","utils","deviceDescriptorToDevice","deviceType","BLUE_MAESTRO","cancelDeviceConnection","id","deviceIsConnected","isDeviceConnected","discoverAllServicesAndCharacteristicsForDevice","MANUFACTURER_ID","stopDeviceScan","callback","scanOptions","scanMode","ScanMode","LowLatency","filteredCallback","err","console","log","JSON","stringify","manufacturerData","mfgId","Buffer","from","readInt16LE","BT510","descriptor","deviceToDeviceDescriptor","startDeviceScan","command","writeCharacteristicWithoutResponseForDevice","BLUETOOTH_UART_SERVICE_UUID","BLUETOOTH_READ_CHARACTERISTIC_UUID","base64FromString","transactionId","reject","subscription","monitorCharacteristicForDevice","BLUETOOTH_WRITE_CHARACTERISTIC_UUID","result","Math","random","toString","substr","parser","data","done","alreadyDone","transmissionDone","val","str","stringFromBase64","pattern","RegExp","test","monitoringCallback","Boolean","value","name","reason","push","remove","length","Error","monitor","monitorCharacteristic","all","writeCharacteristic","then","r","catch","cancelTransaction","monitorCharacteristicCallback","macAddress","connectAndDiscoverServices","downloadLogs","writeWithSingleResponse","COMMAND_CLEAR","monitorCallback","join","buffer","concat","slice","map","datum","bufferFromBase64","ind","findIndex","_","i","readInt16BE","DELIMITER_A","DELIMITER_B","reduce","acc","index","time","temperature","TEMPERATURE_DIVISOR","parse","numEvents","Number","prepareLogs","prepCommand","COMMAND_PREPARE_LOG","replace","ackLogs","ackCommand","COMMAND_ACK_LOG","sensorLog","downloadCommand","COMMAND_DOWNLOAD","dataLog","writeAndMonitor","logBuffer","round","eventType","readInt8","logInterval","clearLogs","COMMAND_UPDATE_LOG_INTERVAL","match","COMMAND_BLINK","answer","monitorResultCallback","parsedBase64","defaultInfoLog","batteryLevel","isDisabled","blueMaestroBatteryLevel","batteryLevelStringOrNull","isNaN","normaliseNumber","bt510BatteryLevel","parsedInfo","batteryVoltageMv","min","parsedIsDisabled","COMMAND_INFO","COMMAND_DISABLE_BUTTON","retriesLeft","getInfo","getInfoWithRetries","toggleButton","toggleButtonWithRetries","downloadLogsWithRetries","blink","blinkWithRetries","updateLogInterval","updateLogIntervalWithRetries","LogLevel","Verbose","BtUtilService"],"mappings":"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","sourcesContent":["import { BtUtilService } from '../BTUtilService';\n\nimport { Buffer } from 'buffer';\nimport { BLUE_MAESTRO, BT510 } from '../constants';\nimport { MacAddress } from '../types/common';\nimport {\n Characteristic,\n ScanOptions,\n ScanMode,\n TypedDevice,\n InfoLog,\n MonitorCharacteristicCallback,\n MonitorCharacteristicParser,\n ScanCallback,\n SensorLog,\n DataLog,\n LogLevel,\n Device,\n BleError,\n} from './types';\nimport { BluetoothManager, MockOrRealDevice } from './BleManager';\n\n// types copied from mobile/src/utilities/logging/\ntype Action = (message: string | Error, details?: Record) => void;\ninterface Logger {\n trace: Action;\n debug: Action;\n info: Action;\n warn: Action;\n error: Action;\n fatal: Action;\n setLogLevel: (transportKey: string, newLevel: number) => void;\n}\nconst dummyLogger: Logger = {\n trace: (_message, _details) => {\n /*do nothing*/\n },\n debug: (_message, _details) => {\n /*do nothing*/\n },\n info: (_message, _details) => {\n /*do nothing*/\n },\n warn: (_message, _details) => {\n /*do nothing*/\n },\n error: (_message, _details) => {\n /*do nothing*/\n },\n fatal: (_message, _details) => {\n /*do nothing*/\n },\n setLogLevel: (_transportKey, _newLevel) => {\n /*do nothing*/\n },\n};\nconst RETRY_DELAY = 1000;\nconst sleep = (delay: number) => new Promise(resolve => setTimeout(resolve, delay));\nexport class BleService {\n manager: BluetoothManager;\n utils: BtUtilService;\n logger: Logger;\n\n constructor(manager: BluetoothManager, logger = dummyLogger) {\n this.manager = manager;\n this.logger = logger;\n manager.setLogLevel(LogLevel.Verbose);\n // Caller passes in utils from the main app,\n // but we ignore it and use our own.\n // This needs to be fixed in the main app.\n this.utils = new BtUtilService();\n logger.info('BleService constructor called');\n }\n\n connectToDevice = (deviceId: string): Promise => {\n this.logger.debug(`${deviceId} Connect to device`);\n try {\n return this.manager.connectToDevice(deviceId);\n } catch (e) {\n this.logger.error(`${deviceId} Error connecting to device. ${e.message}`);\n throw e;\n }\n };\n\n connectAndDiscoverServices = async (deviceDescriptor: string): Promise => {\n this.logger.info(`${deviceDescriptor} connectAndDiscoverServices`);\n const device = this.utils.deviceDescriptorToDevice(deviceDescriptor);\n // the Blue Maestro devices are incorrectly reporting connection status\n // thus: deviceIsConnected?\t{ deviceIsConnected: true }\n // then if disconnecting [BleError: Device D7:D6:67:E0:02:34 is not connected]\n // in which case an error is thrown when trying to connect: [BleError: Device ? is already connected]\n // to work around this, we disconnect the device, ignoring any errors, before connecting again\n if (device.deviceType === BLUE_MAESTRO) {\n this.logger.debug(`${deviceDescriptor} Connecting to BM device`);\n try {\n await this.manager.cancelDeviceConnection(device.id);\n } catch (e) {\n this.logger.warn(`${deviceDescriptor} Error disconnecting! ${e.message}`);\n // ignore error\n }\n } else {\n this.logger.debug(`${deviceDescriptor} Connecting to other device`);\n const deviceIsConnected = await this.manager.isDeviceConnected(device.id);\n if (deviceIsConnected) {\n this.logger.debug(`${deviceDescriptor} Disconnecting`);\n await this.manager.cancelDeviceConnection(device.id);\n }\n }\n await this.connectToDevice(device.id);\n this.logger.debug(`${device.id} Connected to ${deviceDescriptor}`);\n\n await this.manager.discoverAllServicesAndCharacteristicsForDevice(device.id);\n this.logger.info(\n `${deviceDescriptor} Discovered all services and characteristics. id: ${device.id} manufacturer: ${device.deviceType.MANUFACTURER_ID}`\n );\n\n return device;\n };\n\n stopScan = (): void => {\n this.manager.stopDeviceScan();\n };\n\n scanForSensors = (callback: ScanCallback): void => {\n this.logger.info('Scanning for sensors');\n const scanOptions: ScanOptions = { scanMode: ScanMode.LowLatency };\n const filteredCallback = (err: BleError | null, device: Device | null): void => {\n if (err) {\n console.log('BleService Scan Error:', JSON.stringify(err));\n }\n\n if (device?.manufacturerData) {\n const mfgId = Buffer.from(device.manufacturerData, 'base64').readInt16LE(0);\n if (mfgId === BLUE_MAESTRO.MANUFACTURER_ID || mfgId === BT510.MANUFACTURER_ID) {\n const descriptor = this.utils.deviceToDeviceDescriptor(device.id, mfgId);\n\n callback(err, descriptor);\n }\n }\n };\n this.manager.startDeviceScan(null, scanOptions, filteredCallback);\n };\n\n writeCharacteristic = async (device: TypedDevice, command: string): Promise => {\n return this.manager.writeCharacteristicWithoutResponseForDevice(\n device.id,\n device.deviceType.BLUETOOTH_UART_SERVICE_UUID,\n device.deviceType.BLUETOOTH_READ_CHARACTERISTIC_UUID,\n this.utils.base64FromString(command)\n );\n };\n\n monitorCharacteristic = (\n device: TypedDevice,\n callback: MonitorCharacteristicCallback,\n transactionId: string\n ): Promise => {\n return new Promise((resolve, reject) => {\n const subscription = this.manager.monitorCharacteristicForDevice(\n device.id,\n device.deviceType.BLUETOOTH_UART_SERVICE_UUID,\n device.deviceType.BLUETOOTH_WRITE_CHARACTERISTIC_UUID,\n (error, result) => {\n callback(result, resolve, reject, subscription, error);\n },\n transactionId\n );\n });\n };\n\n // https://gist.github.com/gordonbrander/2230317\n transactionId = (): string => '_' + Math.random().toString(36).substr(2, 9);\n\n writeAndMonitor = async (\n device: TypedDevice,\n command: string,\n parser: MonitorCharacteristicParser\n ): Promise => {\n const data: string[] = [];\n let done = 0;\n const alreadyDone = (): number => done++;\n\n const transmissionDone = (val: string): boolean => {\n const str = this.utils.stringFromBase64(val);\n const pattern = new RegExp('.*}$'); // workaround for emacs web mode confused by bracket in a regexp literal\n const result = pattern.test(str);\n return result;\n };\n\n const monitoringCallback: MonitorCharacteristicCallback = (\n result,\n resolve,\n reject,\n subscription,\n error\n ) => {\n this.logger.debug(`${device.id} Monitor command: ${command}`);\n this.logger.debug(`${device.id} Monitor callback result valid: ${Boolean(result?.value)}`);\n if (error) {\n this.logger.debug(`${device.id} Monitor callback error name: ${error.name}`);\n this.logger.debug(`${device.id} Monitor callback error message: ${error.message}`);\n this.logger.debug(`${device.id} Monitor callback error reason: ${error.reason}`);\n }\n\n if (result?.value) {\n data.push(result.value);\n // return to wait for next chunk\n if (device.deviceType === BLUE_MAESTRO || !transmissionDone(result.value)) return;\n }\n try {\n subscription.remove();\n if (device.deviceType === BT510 && alreadyDone()) {\n // Don't call the parser more than once.\n // (Although it probably doesn't hurt anything,\n // since the Promise has already resolved and returned the result\n // to the caller)\n return;\n }\n this.logger.debug(`${device.id} Monitor callback. Data length: ${data.length}`);\n\n if (data.length === 0) throw new Error(' callback no data returned');\n resolve(parser(data));\n } catch (e) {\n reject(new Error(` callback parsing failed, ${e.message}`));\n }\n }; // end monitoringCallback\n\n const transactionId = this.transactionId();\n const monitor = this.monitorCharacteristic(device, monitoringCallback, transactionId);\n\n // We only care about the result if both the write and monitor succeed.\n return Promise.all([monitor, this.writeCharacteristic(device, command)])\n .then(r => r[0])\n .catch(e => {\n this.manager.cancelTransaction(transactionId);\n this.logger.error(`${device.id} writeAndMonitor rejected. ${e.message}`);\n throw new Error(` writeAndMonitor rejected, ${device.id} ${e.message}`);\n });\n };\n\n writeWithSingleResponse = async (\n device: TypedDevice,\n command: string,\n parser: MonitorCharacteristicParser\n ): Promise => {\n const monitorCharacteristicCallback: MonitorCharacteristicCallback = (\n result,\n resolve,\n reject,\n subscription\n ) => {\n subscription?.remove();\n if (result?.value) {\n try {\n resolve(parser(result.value));\n } catch (e) {\n reject(new Error(` callback parsing failed: ${e.message}`));\n }\n } else reject(new Error(` callback returns null`));\n }; // end monitorCharacteristicCallback\n\n const transactionId = this.transactionId();\n const monitor = this.monitorCharacteristic(\n device,\n monitorCharacteristicCallback,\n transactionId\n );\n // We only care about the result if both the write and monitor succeed.\n return Promise.all([monitor, this.writeCharacteristic(device, command)])\n .then(r => r[0])\n .catch(e => {\n this.manager.cancelTransaction(transactionId);\n throw new Error(` writeWithSingleResponse rejected, ${device.id} ${e.message}`);\n });\n };\n\n /** Facade for clearing logs.\n *\n * Connects with a sensor and clears all temperature logs.\n *\n * Returns a promise which resolves to boolean, which is ignored by the caller.\n *\n * @param {String} macAddress\n */\n clearLogs = async (macAddress: MacAddress): Promise => {\n this.logger.debug(`${macAddress} Clearing logs`);\n const device = await this.connectAndDiscoverServices(macAddress);\n if (device?.deviceType === BT510) {\n await this.downloadLogs(macAddress);\n } else {\n await this.writeWithSingleResponse(\n device,\n BLUE_MAESTRO.COMMAND_CLEAR,\n data => !!this.utils.stringFromBase64(data)\n );\n }\n };\n\n downloadLogs = async (macAddress: MacAddress): Promise => {\n this.logger.debug(`${macAddress} Download logs`);\n const device = await this.connectAndDiscoverServices(macAddress);\n this.logger.info(`${macAddress} Download logs connected and discovered services`);\n const monitorCallback: MonitorCharacteristicParser = (\n data: string[]\n ) => {\n this.logger.info(`${macAddress} Write and monitor found some data! ${data.length}`);\n this.logger.debug(`${macAddress} ${data.join('; ')}`);\n if (device.deviceType === BLUE_MAESTRO) {\n const buffer = Buffer.concat(\n data.slice(1).map(datum => this.utils.bufferFromBase64(datum))\n );\n const ind = buffer.findIndex(\n (_, i) =>\n (i % 2 === 0 && buffer.readInt16BE(i) === BLUE_MAESTRO.DELIMITER_A) ||\n buffer.readInt16BE(i) === BLUE_MAESTRO.DELIMITER_B\n );\n\n return (buffer.slice(0, ind) as Buffer).reduce((acc: SensorLog[], _, index) => {\n if (index % 2 !== 0) return acc;\n return [\n ...acc,\n { time: '', temperature: buffer.readInt16BE(index) / BLUE_MAESTRO.TEMPERATURE_DIVISOR },\n ];\n }, []);\n } else {\n // BT510\n const buffer = Buffer.concat(data.map(datum => this.utils.bufferFromBase64(datum)));\n const result = JSON.parse(buffer.toString());\n const numEvents = Number(result.result[0] / 8);\n return { numEvents, data: result.result[1] };\n }\n }; // end monitor callback\n\n if (device.deviceType === BT510) {\n // const FIFO = '0';\n // const LIFO = '1';\n this.logger.debug(`${macAddress} Preparing to download logs`);\n const prepareLogs = async (): Promise => {\n const prepCommand = BT510.COMMAND_PREPARE_LOG.replace('MODE', '0');\n\n return (await this.writeWithSingleResponse(device, prepCommand, data => {\n const info = this.utils.stringFromBase64(data);\n this.logger.debug(`${macAddress} Prepare logs response: ${info}`);\n return JSON.parse(info).result !== 0;\n })) as boolean;\n };\n const ackLogs = async (numEvents: number): Promise => {\n const ackCommand = BT510.COMMAND_ACK_LOG.replace('NUMEVENTS', numEvents.toString());\n return (await this.writeWithSingleResponse(device, ackCommand, data => {\n const info = this.utils.stringFromBase64(data);\n return JSON.parse(info).result === numEvents;\n })) as boolean;\n };\n\n let sensorLog = [] as SensorLog[];\n try {\n while (await prepareLogs()) {\n const downloadCommand = BT510.COMMAND_DOWNLOAD.replace('NUMEVENTS', '500');\n this.logger.debug(`${macAddress} Sending download command`);\n const dataLog = (await this.writeAndMonitor(\n device,\n downloadCommand,\n monitorCallback\n )) as DataLog;\n const logBuffer = this.utils.bufferFromBase64(dataLog.data);\n\n const log = logBuffer.reduce((acc: SensorLog[], _, index) => {\n if (index % 8 !== 0) return acc;\n //const time = logBuffer.readInt32LE(index);\n const temperature =\n Math.round((logBuffer.readInt16LE(index + 4) / BT510.TEMPERATURE_DIVISOR) * 10) / 10;\n const eventType = logBuffer.readInt8(index + 6);\n //const salt = logBuffer.readInt8(index + 7);\n if (eventType === 1) {\n return [\n ...acc,\n {\n temperature,\n },\n ];\n } else {\n return [...acc];\n }\n }, []);\n\n if (await ackLogs(dataLog.numEvents)) {\n this.logger.debug(`${macAddress} Ack received`);\n sensorLog = sensorLog.concat(log);\n }\n }\n } catch (e) {\n this.logger.error(`${macAddress} Error downloading logs. ${e.message}`);\n if (sensorLog.length === 0) {\n throw new Error(`downloadLogs ${e.message}`);\n }\n // But if we partially succeeded, return that\n }\n return sensorLog;\n } else {\n try {\n const command = BLUE_MAESTRO.COMMAND_DOWNLOAD.replace('NUMEVENTS', '500');\n this.logger.debug(`${macAddress} Sending download command`);\n const result = (await this.writeAndMonitor(\n device,\n command,\n monitorCallback\n )) as SensorLog[];\n return result;\n } catch (e) {\n this.logger.error(`${macAddress} Error downloading logs! ${e.message}`);\n }\n return [] as SensorLog[];\n }\n };\n\n updateLogInterval = async (\n macAddress: MacAddress,\n logInterval: number,\n clearLogs = true\n ): Promise => {\n this.logger.debug(`${macAddress} Update log interval`);\n const device = await this.connectAndDiscoverServices(macAddress);\n\n const command = device.deviceType.COMMAND_UPDATE_LOG_INTERVAL.replace(\n 'LOG_INTERVAL',\n logInterval.toString()\n );\n const result = await this.writeWithSingleResponse(device, command, data => {\n const info = this.utils.stringFromBase64(data);\n return (\n (device.deviceType === BT510 && JSON.parse(info).result === 'ok') ||\n !!info.match(/interval/i)\n );\n });\n // Clear logs if we haven't just downloaded\n // BlueMaestro automatically clears logs when log interval is set,\n // But we have to download all the logs to clear them on BT510\n if (clearLogs && device.deviceType === BT510) {\n await this.downloadLogs(macAddress);\n }\n if (result) return true;\n throw new Error(` command returned not OK result`);\n };\n\n blink = async (macAddress: MacAddress): Promise => {\n this.logger.debug(`${macAddress} Blink`);\n const device = await this.connectAndDiscoverServices(macAddress);\n const result = (await this.writeWithSingleResponse(\n device,\n device.deviceType.COMMAND_BLINK,\n data => {\n const answer = this.utils.stringFromBase64(data);\n return !!answer.match(/ok/i);\n }\n )) as boolean;\n\n if (result) return true;\n throw new Error(` acknowledgement false`);\n };\n\n getInfo = async (macAddress: MacAddress): Promise => {\n this.logger.debug(`${macAddress} Get info`);\n const device = await this.connectAndDiscoverServices(macAddress);\n const monitorResultCallback: MonitorCharacteristicParser = data => {\n const parsedBase64 = data.map(this.utils.stringFromBase64);\n const defaultInfoLog: InfoLog = { batteryLevel: null, isDisabled: true };\n const blueMaestroBatteryLevel = (info: string): number | null => {\n const batteryLevelStringOrNull = info.match(/Batt lvl: [0-9]{1,3}/);\n\n if (!batteryLevelStringOrNull) return batteryLevelStringOrNull;\n\n const batteryLevel = Number(batteryLevelStringOrNull[0].match(/[0-9]{1,3}/));\n\n return Number.isNaN(batteryLevel)\n ? null\n : this.utils.normaliseNumber(batteryLevel, [75, 100]);\n };\n\n const bt510BatteryLevel = (info: string): number | null => {\n let batteryLevel: number | null = null;\n if (info) {\n const parsedInfo = JSON.parse(info);\n\n if (parsedInfo?.result !== 'ok') {\n return null;\n }\n\n batteryLevel = Number(parsedInfo.batteryVoltageMv);\n } else {\n return null;\n }\n return Number.isNaN(batteryLevel)\n ? null\n : this.utils.normaliseNumber(Math.min(batteryLevel, 3000), [2250, 3000]);\n };\n\n const parsedIsDisabled = (info: string): boolean => !!info.match(/Btn on\\/off: 1/);\n\n if (device.deviceType === BLUE_MAESTRO) {\n return parsedBase64.reduce((acc, info) => {\n const isDisabled = parsedIsDisabled(info);\n const batteryLevel = blueMaestroBatteryLevel(info);\n if (isDisabled) return { ...acc, isDisabled };\n if (batteryLevel) return { ...acc, batteryLevel };\n return acc;\n }, defaultInfoLog);\n } else {\n return { batteryLevel: bt510BatteryLevel(parsedBase64[0]), isDisabled: true };\n }\n };\n\n const result: InfoLog = (await this.writeAndMonitor(\n device,\n device.deviceType.COMMAND_INFO,\n monitorResultCallback\n )) as InfoLog;\n\n return result;\n };\n\n toggleButton = async (macAddress: MacAddress): Promise => {\n this.logger.debug(`${macAddress} Toggle button`);\n const device = await this.connectAndDiscoverServices(macAddress);\n if (device.deviceType === BT510) {\n // Laird doesn't have this command\n return true;\n }\n const result = (await this.writeWithSingleResponse(\n device,\n BLUE_MAESTRO.COMMAND_DISABLE_BUTTON,\n data => {\n return !!this.utils.stringFromBase64(data).match(/ok/i);\n }\n )) as boolean;\n return result;\n };\n\n getInfoWithRetries = async (\n macAddress: MacAddress,\n retriesLeft: number,\n error: Error | null\n ): Promise => {\n if (!retriesLeft) {\n this.logger.error(`${macAddress} getInfoWithRetries failed. ${error?.message}`);\n throw error;\n }\n\n await sleep(RETRY_DELAY);\n\n return this.getInfo(macAddress).catch(err =>\n this.getInfoWithRetries(macAddress, retriesLeft - 1, err)\n );\n };\n\n toggleButtonWithRetries = async (\n macAddress: MacAddress,\n retriesLeft: number,\n error: Error | null\n ): Promise => {\n if (!retriesLeft) throw error;\n\n await sleep(RETRY_DELAY);\n\n return this.toggleButton(macAddress).catch(err =>\n this.toggleButtonWithRetries(macAddress, retriesLeft - 1, err)\n );\n };\n\n downloadLogsWithRetries = async (\n macAddress: MacAddress,\n retriesLeft: number,\n error: Error | null\n ): Promise => {\n this.logger.info(`${macAddress} Download logs with retries`);\n this.logger.debug(\n `${macAddress} Starting to download logs. There are ${retriesLeft} retries left. Error: ${error?.message}`\n );\n if (!retriesLeft) throw error;\n\n await sleep(RETRY_DELAY);\n\n return this.downloadLogs(macAddress).catch(err =>\n this.downloadLogsWithRetries(macAddress, retriesLeft - 1, err)\n );\n };\n\n blinkWithRetries = async (\n macAddress: MacAddress,\n retriesLeft: number,\n error: Error | null\n ): Promise => {\n if (!retriesLeft) throw error;\n\n await sleep(RETRY_DELAY);\n\n return this.blink(macAddress).catch(err =>\n this.blinkWithRetries(macAddress, retriesLeft - 1, err)\n );\n };\n\n updateLogIntervalWithRetries = async (\n macAddress: MacAddress,\n logInterval: number,\n retriesLeft: number,\n clearLogs: boolean,\n error: Error | null\n ): Promise => {\n if (!retriesLeft) throw error;\n\n await sleep(RETRY_DELAY);\n\n return this.updateLogInterval(macAddress, logInterval, clearLogs).catch(err =>\n this.updateLogIntervalWithRetries(macAddress, logInterval, retriesLeft - 1, clearLogs, err)\n );\n };\n}\n"]} \ No newline at end of file diff --git a/.yalc/msupply-ble-service/lib/commonjs/Bluetooth/types.js.map b/.yalc/msupply-ble-service/lib/commonjs/Bluetooth/types.js.map index 60c75efe..ec138289 100644 --- a/.yalc/msupply-ble-service/lib/commonjs/Bluetooth/types.js.map +++ b/.yalc/msupply-ble-service/lib/commonjs/Bluetooth/types.js.map @@ -1 +1 @@ -{"version":3,"sources":["types.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA","sourcesContent":["import {\n ScanMode,\n Subscription,\n Characteristic,\n BleError,\n Device,\n ScanOptions,\n LogLevel,\n} from 'react-native-ble-plx';\n\nimport { BT510, BLUE_MAESTRO } from '../constants';\n\nexport type DeviceType = typeof BT510 | typeof BLUE_MAESTRO; // BT510 | BLUE_MAESTRO\n\nexport interface TypedDevice {\n id: string;\n deviceType: DeviceType; // BT510 | BLUE_MAESTRO\n}\n\nexport interface InfoLog {\n batteryLevel: null | number;\n isDisabled: boolean;\n}\n\nexport interface SensorLog {\n temperature: number;\n}\n\nexport interface DataLog {\n numEvents: number;\n data: string;\n}\n\nexport interface ScanCallback {\n (error: BleError | null, deviceDescriptor: string): void;\n}\n\nexport interface Resolver {\n (result: ResolverResult): void;\n}\n\nexport interface ErrorRejector {\n (error: Error): void;\n}\n\nexport interface MonitorCharacteristicCallback {\n (\n result: Characteristic | null,\n resolver: Resolver,\n rejector: ErrorRejector,\n subscription: Subscription\n ): void;\n}\n\nexport interface MonitorCharacteristicParser {\n (result: ParserInput): ParserResult;\n}\n\nexport { ScanMode, Subscription, Characteristic, BleError, Device, ScanOptions, LogLevel };\n"]} \ No newline at end of file +{"version":3,"sources":["types.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA","sourcesContent":["import {\n ScanMode,\n Subscription,\n Characteristic,\n BleError,\n Device,\n ScanOptions,\n LogLevel,\n} from 'react-native-ble-plx';\n\nimport { BT510, BLUE_MAESTRO } from '../constants';\n\nexport type DeviceType = typeof BT510 | typeof BLUE_MAESTRO; // BT510 | BLUE_MAESTRO\n\nexport interface TypedDevice {\n id: string;\n deviceType: DeviceType; // BT510 | BLUE_MAESTRO\n}\n\nexport interface InfoLog {\n batteryLevel: null | number;\n isDisabled: boolean;\n}\n\nexport interface SensorLog {\n temperature: number;\n}\n\nexport interface DataLog {\n numEvents: number;\n data: string;\n}\n\nexport interface ScanCallback {\n (error: BleError | null, deviceDescriptor: string): void;\n}\n\nexport interface Resolver {\n (result: ResolverResult): void;\n}\n\nexport interface ErrorRejector {\n (error: Error): void;\n}\n\nexport interface MonitorCharacteristicCallback {\n (\n result: Characteristic | null,\n resolver: Resolver,\n rejector: ErrorRejector,\n subscription: Subscription,\n error: BleError | null\n ): void;\n}\n\nexport interface MonitorCharacteristicParser {\n (result: ParserInput): ParserResult;\n}\n\nexport { ScanMode, Subscription, Characteristic, BleError, Device, ScanOptions, LogLevel };\n"]} \ No newline at end of file diff --git a/.yalc/msupply-ble-service/lib/module/Bluetooth/BleService.js b/.yalc/msupply-ble-service/lib/module/Bluetooth/BleService.js index cdca3f9a..a783743b 100644 --- a/.yalc/msupply-ble-service/lib/module/Bluetooth/BleService.js +++ b/.yalc/msupply-ble-service/lib/module/Bluetooth/BleService.js @@ -27,6 +27,10 @@ const dummyLogger = { /*do nothing*/ } }; +const RETRY_DELAY = 1000; + +const sleep = delay => new Promise(resolve => setTimeout(resolve, delay)); + export class BleService { constructor(manager, logger = dummyLogger) { _defineProperty(this, "manager", void 0); @@ -36,16 +40,18 @@ export class BleService { _defineProperty(this, "logger", void 0); _defineProperty(this, "connectToDevice", deviceId => { - this.logger.info('connectToDevice', { - deviceId - }); - return this.manager.connectToDevice(deviceId); + this.logger.debug(`${deviceId} Connect to device`); + + try { + return this.manager.connectToDevice(deviceId); + } catch (e) { + this.logger.error(`${deviceId} Error connecting to device. ${e.message}`); + throw e; + } }); _defineProperty(this, "connectAndDiscoverServices", async deviceDescriptor => { - this.logger.info('connectAndDiscoverServices', { - deviceDescriptor - }); + this.logger.info(`${deviceDescriptor} connectAndDiscoverServices`); const device = this.utils.deviceDescriptorToDevice(deviceDescriptor); // the Blue Maestro devices are incorrectly reporting connection status // thus: deviceIsConnected? { deviceIsConnected: true } // then if disconnecting [BleError: Device D7:D6:67:E0:02:34 is not connected] @@ -53,24 +59,27 @@ export class BleService { // to work around this, we disconnect the device, ignoring any errors, before connecting again if (device.deviceType === BLUE_MAESTRO) { + this.logger.debug(`${deviceDescriptor} Connecting to BM device`); + try { await this.manager.cancelDeviceConnection(device.id); - } catch {// ignore error + } catch (e) { + this.logger.warn(`${deviceDescriptor} Error disconnecting! ${e.message}`); // ignore error } } else { + this.logger.debug(`${deviceDescriptor} Connecting to other device`); const deviceIsConnected = await this.manager.isDeviceConnected(device.id); if (deviceIsConnected) { + this.logger.debug(`${deviceDescriptor} Disconnecting`); await this.manager.cancelDeviceConnection(device.id); } } await this.connectToDevice(device.id); + this.logger.debug(`${device.id} Connected to ${deviceDescriptor}`); await this.manager.discoverAllServicesAndCharacteristicsForDevice(device.id); - this.logger.info('Discovered all services and characteristics for device', { - id: device.id, - manufacturer: device.deviceType.MANUFACTURER_ID - }); + this.logger.info(`${deviceDescriptor} Discovered all services and characteristics. id: ${device.id} manufacturer: ${device.deviceType.MANUFACTURER_ID}`); return device; }); @@ -79,7 +88,7 @@ export class BleService { }); _defineProperty(this, "scanForSensors", callback => { - this.logger.info('scanning for sensors', {}); + this.logger.info('Scanning for sensors'); const scanOptions = { scanMode: ScanMode.LowLatency }; @@ -108,8 +117,8 @@ export class BleService { _defineProperty(this, "monitorCharacteristic", (device, callback, transactionId) => { return new Promise((resolve, reject) => { - const subscription = this.manager.monitorCharacteristicForDevice(device.id, device.deviceType.BLUETOOTH_UART_SERVICE_UUID, device.deviceType.BLUETOOTH_WRITE_CHARACTERISTIC_UUID, (_, result) => { - callback(result, resolve, reject, subscription); + const subscription = this.manager.monitorCharacteristicForDevice(device.id, device.deviceType.BLUETOOTH_UART_SERVICE_UUID, device.deviceType.BLUETOOTH_WRITE_CHARACTERISTIC_UUID, (error, result) => { + callback(result, resolve, reject, subscription, error); }, transactionId); }); }); @@ -130,7 +139,16 @@ export class BleService { return result; }; - const monitoringCallback = (result, resolve, reject, subscription) => { + const monitoringCallback = (result, resolve, reject, subscription, error) => { + this.logger.debug(`${device.id} Monitor command: ${command}`); + this.logger.debug(`${device.id} Monitor callback result valid: ${Boolean(result === null || result === void 0 ? void 0 : result.value)}`); + + if (error) { + this.logger.debug(`${device.id} Monitor callback error name: ${error.name}`); + this.logger.debug(`${device.id} Monitor callback error message: ${error.message}`); + this.logger.debug(`${device.id} Monitor callback error reason: ${error.reason}`); + } + if (result !== null && result !== void 0 && result.value) { data.push(result.value); // return to wait for next chunk @@ -148,6 +166,7 @@ export class BleService { return; } + this.logger.debug(`${device.id} Monitor callback. Data length: ${data.length}`); if (data.length === 0) throw new Error(' callback no data returned'); resolve(parser(data)); } catch (e) { @@ -161,6 +180,7 @@ export class BleService { return Promise.all([monitor, this.writeCharacteristic(device, command)]).then(r => r[0]).catch(e => { this.manager.cancelTransaction(transactionId); + this.logger.error(`${device.id} writeAndMonitor rejected. ${e.message}`); throw new Error(` writeAndMonitor rejected, ${device.id} ${e.message}`); }); }); @@ -189,6 +209,7 @@ export class BleService { }); _defineProperty(this, "clearLogs", async macAddress => { + this.logger.debug(`${macAddress} Clearing logs`); const device = await this.connectAndDiscoverServices(macAddress); if ((device === null || device === void 0 ? void 0 : device.deviceType) === BT510) { @@ -199,15 +220,13 @@ export class BleService { }); _defineProperty(this, "downloadLogs", async macAddress => { + this.logger.debug(`${macAddress} Download logs`); const device = await this.connectAndDiscoverServices(macAddress); - this.logger.info('Download logs connected and discovered services', { - macAddress - }); + this.logger.info(`${macAddress} Download logs connected and discovered services`); const monitorCallback = data => { - this.logger.info('Write and monitor found some data!', { - data - }); + this.logger.info(`${macAddress} Write and monitor found some data! ${data.length}`); + this.logger.debug(`${macAddress} ${data.join('; ')}`); if (device.deviceType === BLUE_MAESTRO) { const buffer = Buffer.concat(data.slice(1).map(datum => this.utils.bufferFromBase64(datum))); @@ -235,10 +254,13 @@ export class BleService { if (device.deviceType === BT510) { // const FIFO = '0'; // const LIFO = '1'; + this.logger.debug(`${macAddress} Preparing to download logs`); + const prepareLogs = async () => { const prepCommand = BT510.COMMAND_PREPARE_LOG.replace('MODE', '0'); return await this.writeWithSingleResponse(device, prepCommand, data => { const info = this.utils.stringFromBase64(data); + this.logger.debug(`${macAddress} Prepare logs response: ${info}`); return JSON.parse(info).result !== 0; }); }; @@ -256,6 +278,7 @@ export class BleService { try { while (await prepareLogs()) { const downloadCommand = BT510.COMMAND_DOWNLOAD.replace('NUMEVENTS', '500'); + this.logger.debug(`${macAddress} Sending download command`); const dataLog = await this.writeAndMonitor(device, downloadCommand, monitorCallback); const logBuffer = this.utils.bufferFromBase64(dataLog.data); const log = logBuffer.reduce((acc, _, index) => { @@ -274,10 +297,13 @@ export class BleService { }, []); if (await ackLogs(dataLog.numEvents)) { + this.logger.debug(`${macAddress} Ack received`); sensorLog = sensorLog.concat(log); } } } catch (e) { + this.logger.error(`${macAddress} Error downloading logs. ${e.message}`); + if (sensorLog.length === 0) { throw new Error(`downloadLogs ${e.message}`); } // But if we partially succeeded, return that @@ -286,13 +312,21 @@ export class BleService { return sensorLog; } else { - const command = BLUE_MAESTRO.COMMAND_DOWNLOAD.replace('NUMEVENTS', '500'); - const result = await this.writeAndMonitor(device, command, monitorCallback); - return result; + try { + const command = BLUE_MAESTRO.COMMAND_DOWNLOAD.replace('NUMEVENTS', '500'); + this.logger.debug(`${macAddress} Sending download command`); + const result = await this.writeAndMonitor(device, command, monitorCallback); + return result; + } catch (e) { + this.logger.error(`${macAddress} Error downloading logs! ${e.message}`); + } + + return []; } }); _defineProperty(this, "updateLogInterval", async (macAddress, logInterval, clearLogs = true) => { + this.logger.debug(`${macAddress} Update log interval`); const device = await this.connectAndDiscoverServices(macAddress); const command = device.deviceType.COMMAND_UPDATE_LOG_INTERVAL.replace('LOG_INTERVAL', logInterval.toString()); const result = await this.writeWithSingleResponse(device, command, data => { @@ -311,6 +345,7 @@ export class BleService { }); _defineProperty(this, "blink", async macAddress => { + this.logger.debug(`${macAddress} Blink`); const device = await this.connectAndDiscoverServices(macAddress); const result = await this.writeWithSingleResponse(device, device.deviceType.COMMAND_BLINK, data => { const answer = this.utils.stringFromBase64(data); @@ -321,6 +356,7 @@ export class BleService { }); _defineProperty(this, "getInfo", async macAddress => { + this.logger.debug(`${macAddress} Get info`); const device = await this.connectAndDiscoverServices(macAddress); const monitorResultCallback = data => { @@ -382,6 +418,7 @@ export class BleService { }); _defineProperty(this, "toggleButton", async macAddress => { + this.logger.debug(`${macAddress} Toggle button`); const device = await this.connectAndDiscoverServices(macAddress); if (device.deviceType === BT510) { @@ -396,44 +433,49 @@ export class BleService { }); _defineProperty(this, "getInfoWithRetries", async (macAddress, retriesLeft, error) => { - if (!retriesLeft) throw error; + if (!retriesLeft) { + this.logger.error(`${macAddress} getInfoWithRetries failed. ${error === null || error === void 0 ? void 0 : error.message}`); + throw error; + } + + await sleep(RETRY_DELAY); return this.getInfo(macAddress).catch(err => this.getInfoWithRetries(macAddress, retriesLeft - 1, err)); }); _defineProperty(this, "toggleButtonWithRetries", async (macAddress, retriesLeft, error) => { if (!retriesLeft) throw error; + await sleep(RETRY_DELAY); return this.toggleButton(macAddress).catch(err => this.toggleButtonWithRetries(macAddress, retriesLeft - 1, err)); }); _defineProperty(this, "downloadLogsWithRetries", async (macAddress, retriesLeft, error) => { - this.logger.info('Starting to download logs', { - macAddress, - retriesLeft, - error - }); + this.logger.info(`${macAddress} Download logs with retries`); + this.logger.debug(`${macAddress} Starting to download logs. There are ${retriesLeft} retries left. Error: ${error === null || error === void 0 ? void 0 : error.message}`); if (!retriesLeft) throw error; + await sleep(RETRY_DELAY); return this.downloadLogs(macAddress).catch(err => this.downloadLogsWithRetries(macAddress, retriesLeft - 1, err)); }); _defineProperty(this, "blinkWithRetries", async (macAddress, retriesLeft, error) => { if (!retriesLeft) throw error; + await sleep(RETRY_DELAY); return this.blink(macAddress).catch(err => this.blinkWithRetries(macAddress, retriesLeft - 1, err)); }); _defineProperty(this, "updateLogIntervalWithRetries", async (macAddress, logInterval, retriesLeft, clearLogs, error) => { if (!retriesLeft) throw error; + await sleep(RETRY_DELAY); return this.updateLogInterval(macAddress, logInterval, clearLogs).catch(err => this.updateLogIntervalWithRetries(macAddress, logInterval, retriesLeft - 1, clearLogs, err)); }); this.manager = manager; this.logger = logger; - console.log(`logger is ${JSON.stringify(logger)}`); manager.setLogLevel(LogLevel.Verbose); // Caller passes in utils from the main app, // but we ignore it and use our own. // This needs to be fixed in the main app. this.utils = new BtUtilService(); - logger.info('BleService constructor called', {}); + logger.info('BleService constructor called'); } } diff --git a/.yalc/msupply-ble-service/lib/module/Bluetooth/BleService.js.map b/.yalc/msupply-ble-service/lib/module/Bluetooth/BleService.js.map index 4c096991..76f5885b 100644 --- a/.yalc/msupply-ble-service/lib/module/Bluetooth/BleService.js.map +++ b/.yalc/msupply-ble-service/lib/module/Bluetooth/BleService.js.map @@ -1 +1 @@ -{"version":3,"sources":["BleService.ts"],"names":["BtUtilService","Buffer","BLUE_MAESTRO","BT510","ScanMode","LogLevel","dummyLogger","trace","_message","_details","debug","info","warn","error","fatal","setLogLevel","_transportKey","_newLevel","BleService","constructor","manager","logger","deviceId","connectToDevice","deviceDescriptor","device","utils","deviceDescriptorToDevice","deviceType","cancelDeviceConnection","id","deviceIsConnected","isDeviceConnected","discoverAllServicesAndCharacteristicsForDevice","manufacturer","MANUFACTURER_ID","stopDeviceScan","callback","scanOptions","scanMode","LowLatency","filteredCallback","err","console","log","JSON","stringify","manufacturerData","mfgId","from","readInt16LE","descriptor","deviceToDeviceDescriptor","startDeviceScan","command","writeCharacteristicWithoutResponseForDevice","BLUETOOTH_UART_SERVICE_UUID","BLUETOOTH_READ_CHARACTERISTIC_UUID","base64FromString","transactionId","Promise","resolve","reject","subscription","monitorCharacteristicForDevice","BLUETOOTH_WRITE_CHARACTERISTIC_UUID","_","result","Math","random","toString","substr","parser","data","done","alreadyDone","transmissionDone","val","str","stringFromBase64","pattern","RegExp","test","monitoringCallback","value","push","remove","length","Error","e","message","monitor","monitorCharacteristic","all","writeCharacteristic","then","r","catch","cancelTransaction","monitorCharacteristicCallback","macAddress","connectAndDiscoverServices","downloadLogs","writeWithSingleResponse","COMMAND_CLEAR","monitorCallback","buffer","concat","slice","map","datum","bufferFromBase64","ind","findIndex","i","readInt16BE","DELIMITER_A","DELIMITER_B","reduce","acc","index","time","temperature","TEMPERATURE_DIVISOR","parse","numEvents","Number","prepareLogs","prepCommand","COMMAND_PREPARE_LOG","replace","ackLogs","ackCommand","COMMAND_ACK_LOG","sensorLog","downloadCommand","COMMAND_DOWNLOAD","dataLog","writeAndMonitor","logBuffer","round","eventType","readInt8","logInterval","clearLogs","COMMAND_UPDATE_LOG_INTERVAL","match","COMMAND_BLINK","answer","monitorResultCallback","parsedBase64","defaultInfoLog","batteryLevel","isDisabled","blueMaestroBatteryLevel","batteryLevelStringOrNull","isNaN","normaliseNumber","bt510BatteryLevel","parsedInfo","batteryVoltageMv","min","parsedIsDisabled","COMMAND_INFO","COMMAND_DISABLE_BUTTON","retriesLeft","getInfo","getInfoWithRetries","toggleButton","toggleButtonWithRetries","downloadLogsWithRetries","blink","blinkWithRetries","updateLogInterval","updateLogIntervalWithRetries","Verbose"],"mappings":"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","sourcesContent":["import { BtUtilService } from '../BTUtilService';\n\nimport { Buffer } from 'buffer';\nimport { BLUE_MAESTRO, BT510 } from '../constants';\nimport { MacAddress } from '../types/common';\nimport {\n Characteristic,\n ScanOptions,\n ScanMode,\n TypedDevice,\n InfoLog,\n MonitorCharacteristicCallback,\n MonitorCharacteristicParser,\n ScanCallback,\n SensorLog,\n DataLog,\n LogLevel,\n Device,\n BleError,\n} from './types';\nimport { BluetoothManager, MockOrRealDevice } from './BleManager';\n\n// types copied from mobile/src/utilities/logging/\ntype Action = (message: string | Error, details?: Record) => void;\ninterface Logger {\n trace: Action;\n debug: Action;\n info: Action;\n warn: Action;\n error: Action;\n fatal: Action;\n setLogLevel: (transportKey: string, newLevel: number) => void;\n}\nconst dummyLogger: Logger = {\n trace: (_message, _details) => {\n /*do nothing*/\n },\n debug: (_message, _details) => {\n /*do nothing*/\n },\n info: (_message, _details) => {\n /*do nothing*/\n },\n warn: (_message, _details) => {\n /*do nothing*/\n },\n error: (_message, _details) => {\n /*do nothing*/\n },\n fatal: (_message, _details) => {\n /*do nothing*/\n },\n setLogLevel: (_transportKey, _newLevel) => {\n /*do nothing*/\n },\n};\n\nexport class BleService {\n manager: BluetoothManager;\n utils: BtUtilService;\n logger: Logger;\n constructor(manager: BluetoothManager, logger = dummyLogger) {\n this.manager = manager;\n this.logger = logger;\n console.log(`logger is ${JSON.stringify(logger)}`);\n manager.setLogLevel(LogLevel.Verbose);\n // Caller passes in utils from the main app,\n // but we ignore it and use our own.\n // This needs to be fixed in the main app.\n this.utils = new BtUtilService();\n logger.info('BleService constructor called', {});\n }\n\n connectToDevice = (deviceId: string): Promise => {\n this.logger.info('connectToDevice', { deviceId });\n return this.manager.connectToDevice(deviceId);\n };\n\n connectAndDiscoverServices = async (deviceDescriptor: string): Promise => {\n this.logger.info('connectAndDiscoverServices', { deviceDescriptor });\n const device = this.utils.deviceDescriptorToDevice(deviceDescriptor);\n // the Blue Maestro devices are incorrectly reporting connection status\n // thus: deviceIsConnected?\t{ deviceIsConnected: true }\n // then if disconnecting [BleError: Device D7:D6:67:E0:02:34 is not connected]\n // in which case an error is thrown when trying to connect: [BleError: Device ? is already connected]\n // to work around this, we disconnect the device, ignoring any errors, before connecting again\n if (device.deviceType === BLUE_MAESTRO) {\n try {\n await this.manager.cancelDeviceConnection(device.id);\n } catch {\n // ignore error\n }\n } else {\n const deviceIsConnected = await this.manager.isDeviceConnected(device.id);\n if (deviceIsConnected) {\n await this.manager.cancelDeviceConnection(device.id);\n }\n }\n await this.connectToDevice(device.id);\n\n await this.manager.discoverAllServicesAndCharacteristicsForDevice(device.id);\n this.logger.info('Discovered all services and characteristics for device', {\n id: device.id,\n manufacturer: device.deviceType.MANUFACTURER_ID,\n });\n return device;\n };\n\n stopScan = (): void => {\n this.manager.stopDeviceScan();\n };\n\n scanForSensors = (callback: ScanCallback): void => {\n this.logger.info('scanning for sensors', {});\n const scanOptions: ScanOptions = { scanMode: ScanMode.LowLatency };\n const filteredCallback = (err: BleError | null, device: Device | null): void => {\n if (err) {\n console.log('BleService Scan Error:', JSON.stringify(err));\n }\n\n if (device?.manufacturerData) {\n const mfgId = Buffer.from(device.manufacturerData, 'base64').readInt16LE(0);\n if (mfgId === BLUE_MAESTRO.MANUFACTURER_ID || mfgId === BT510.MANUFACTURER_ID) {\n const descriptor = this.utils.deviceToDeviceDescriptor(device.id, mfgId);\n\n callback(err, descriptor);\n }\n }\n };\n this.manager.startDeviceScan(null, scanOptions, filteredCallback);\n };\n\n writeCharacteristic = async (device: TypedDevice, command: string): Promise => {\n return this.manager.writeCharacteristicWithoutResponseForDevice(\n device.id,\n device.deviceType.BLUETOOTH_UART_SERVICE_UUID,\n device.deviceType.BLUETOOTH_READ_CHARACTERISTIC_UUID,\n this.utils.base64FromString(command)\n );\n };\n\n monitorCharacteristic = (\n device: TypedDevice,\n callback: MonitorCharacteristicCallback,\n transactionId: string\n ): Promise => {\n return new Promise((resolve, reject) => {\n const subscription = this.manager.monitorCharacteristicForDevice(\n device.id,\n device.deviceType.BLUETOOTH_UART_SERVICE_UUID,\n device.deviceType.BLUETOOTH_WRITE_CHARACTERISTIC_UUID,\n (_, result) => {\n callback(result, resolve, reject, subscription);\n },\n transactionId\n );\n });\n };\n\n // https://gist.github.com/gordonbrander/2230317\n transactionId = (): string => '_' + Math.random().toString(36).substr(2, 9);\n\n writeAndMonitor = async (\n device: TypedDevice,\n command: string,\n parser: MonitorCharacteristicParser\n ): Promise => {\n const data: string[] = [];\n let done = 0;\n const alreadyDone = (): number => done++;\n\n const transmissionDone = (val: string): boolean => {\n const str = this.utils.stringFromBase64(val);\n const pattern = new RegExp('.*}$'); // workaround for emacs web mode confused by bracket in a regexp literal\n const result = pattern.test(str);\n return result;\n };\n\n const monitoringCallback: MonitorCharacteristicCallback = (\n result,\n resolve,\n reject,\n subscription\n ) => {\n if (result?.value) {\n data.push(result.value);\n // return to wait for next chunk\n if (device.deviceType === BLUE_MAESTRO || !transmissionDone(result.value)) return;\n }\n try {\n subscription.remove();\n if (device.deviceType === BT510 && alreadyDone()) {\n // Don't call the parser more than once.\n // (Although it probably doesn't hurt anything,\n // since the Promise has already resolved and returned the result\n // to the caller)\n return;\n }\n if (data.length === 0) throw new Error(' callback no data returned');\n resolve(parser(data));\n } catch (e) {\n reject(new Error(` callback parsing failed, ${e.message}`));\n }\n }; // end monitoringCallback\n\n const transactionId = this.transactionId();\n const monitor = this.monitorCharacteristic(device, monitoringCallback, transactionId);\n // We only care about the result if both the write and monitor succeed.\n return Promise.all([monitor, this.writeCharacteristic(device, command)])\n .then(r => r[0])\n .catch(e => {\n this.manager.cancelTransaction(transactionId);\n throw new Error(` writeAndMonitor rejected, ${device.id} ${e.message}`);\n });\n };\n\n writeWithSingleResponse = async (\n device: TypedDevice,\n command: string,\n parser: MonitorCharacteristicParser\n ): Promise => {\n const monitorCharacteristicCallback: MonitorCharacteristicCallback = (\n result,\n resolve,\n reject,\n subscription\n ) => {\n subscription?.remove();\n if (result?.value) {\n try {\n resolve(parser(result.value));\n } catch (e) {\n reject(new Error(` callback parsing failed: ${e.message}`));\n }\n } else reject(new Error(` callback returns null`));\n }; // end monitorCharacteristicCallback\n\n const transactionId = this.transactionId();\n const monitor = this.monitorCharacteristic(\n device,\n monitorCharacteristicCallback,\n transactionId\n );\n // We only care about the result if both the write and monitor succeed.\n return Promise.all([monitor, this.writeCharacteristic(device, command)])\n .then(r => r[0])\n .catch(e => {\n this.manager.cancelTransaction(transactionId);\n throw new Error(` writeWithSingleResponse rejected, ${device.id} ${e.message}`);\n });\n };\n\n /** Facade for clearing logs.\n *\n * Connects with a sensor and clears all temperature logs.\n *\n * Returns a promise which resolves to boolean, which is ignored by the caller.\n *\n * @param {String} macAddress\n */\n clearLogs = async (macAddress: MacAddress): Promise => {\n const device = await this.connectAndDiscoverServices(macAddress);\n if (device?.deviceType === BT510) {\n await this.downloadLogs(macAddress);\n } else {\n await this.writeWithSingleResponse(\n device,\n BLUE_MAESTRO.COMMAND_CLEAR,\n data => !!this.utils.stringFromBase64(data)\n );\n }\n };\n\n downloadLogs = async (macAddress: MacAddress): Promise => {\n const device = await this.connectAndDiscoverServices(macAddress);\n this.logger.info('Download logs connected and discovered services', { macAddress });\n const monitorCallback: MonitorCharacteristicParser = (\n data: string[]\n ) => {\n this.logger.info('Write and monitor found some data!', { data });\n if (device.deviceType === BLUE_MAESTRO) {\n const buffer = Buffer.concat(\n data.slice(1).map(datum => this.utils.bufferFromBase64(datum))\n );\n const ind = buffer.findIndex(\n (_, i) =>\n (i % 2 === 0 && buffer.readInt16BE(i) === BLUE_MAESTRO.DELIMITER_A) ||\n buffer.readInt16BE(i) === BLUE_MAESTRO.DELIMITER_B\n );\n\n return (buffer.slice(0, ind) as Buffer).reduce((acc: SensorLog[], _, index) => {\n if (index % 2 !== 0) return acc;\n return [\n ...acc,\n { time: '', temperature: buffer.readInt16BE(index) / BLUE_MAESTRO.TEMPERATURE_DIVISOR },\n ];\n }, []);\n } else {\n // BT510\n const buffer = Buffer.concat(data.map(datum => this.utils.bufferFromBase64(datum)));\n const result = JSON.parse(buffer.toString());\n const numEvents = Number(result.result[0] / 8);\n return { numEvents, data: result.result[1] };\n }\n }; // end monitor callback\n\n if (device.deviceType === BT510) {\n // const FIFO = '0';\n // const LIFO = '1';\n\n const prepareLogs = async (): Promise => {\n const prepCommand = BT510.COMMAND_PREPARE_LOG.replace('MODE', '0');\n\n return (await this.writeWithSingleResponse(device, prepCommand, data => {\n const info = this.utils.stringFromBase64(data);\n return JSON.parse(info).result !== 0;\n })) as boolean;\n };\n const ackLogs = async (numEvents: number): Promise => {\n const ackCommand = BT510.COMMAND_ACK_LOG.replace('NUMEVENTS', numEvents.toString());\n return (await this.writeWithSingleResponse(device, ackCommand, data => {\n const info = this.utils.stringFromBase64(data);\n return JSON.parse(info).result === numEvents;\n })) as boolean;\n };\n\n let sensorLog = [] as SensorLog[];\n try {\n while (await prepareLogs()) {\n const downloadCommand = BT510.COMMAND_DOWNLOAD.replace('NUMEVENTS', '500');\n const dataLog = (await this.writeAndMonitor(\n device,\n downloadCommand,\n monitorCallback\n )) as DataLog;\n const logBuffer = this.utils.bufferFromBase64(dataLog.data);\n\n const log = logBuffer.reduce((acc: SensorLog[], _, index) => {\n if (index % 8 !== 0) return acc;\n //const time = logBuffer.readInt32LE(index);\n const temperature =\n Math.round((logBuffer.readInt16LE(index + 4) / BT510.TEMPERATURE_DIVISOR) * 10) / 10;\n const eventType = logBuffer.readInt8(index + 6);\n //const salt = logBuffer.readInt8(index + 7);\n if (eventType === 1) {\n return [\n ...acc,\n {\n temperature,\n },\n ];\n } else {\n return [...acc];\n }\n }, []);\n\n if (await ackLogs(dataLog.numEvents)) {\n sensorLog = sensorLog.concat(log);\n }\n }\n } catch (e) {\n if (sensorLog.length === 0) {\n throw new Error(`downloadLogs ${e.message}`);\n }\n // But if we partially succeeded, return that\n }\n return sensorLog;\n } else {\n const command = BLUE_MAESTRO.COMMAND_DOWNLOAD.replace('NUMEVENTS', '500');\n const result = (await this.writeAndMonitor(device, command, monitorCallback)) as SensorLog[];\n return result;\n }\n };\n\n updateLogInterval = async (\n macAddress: MacAddress,\n logInterval: number,\n clearLogs = true\n ): Promise => {\n const device = await this.connectAndDiscoverServices(macAddress);\n\n const command = device.deviceType.COMMAND_UPDATE_LOG_INTERVAL.replace(\n 'LOG_INTERVAL',\n logInterval.toString()\n );\n const result = await this.writeWithSingleResponse(device, command, data => {\n const info = this.utils.stringFromBase64(data);\n return (\n (device.deviceType === BT510 && JSON.parse(info).result === 'ok') ||\n !!info.match(/interval/i)\n );\n });\n // Clear logs if we haven't just downloaded\n // BlueMaestro automatically clears logs when log interval is set,\n // But we have to download all the logs to clear them on BT510\n if (clearLogs && device.deviceType === BT510) {\n await this.downloadLogs(macAddress);\n }\n if (result) return true;\n throw new Error(` command returned not OK result`);\n };\n\n blink = async (macAddress: MacAddress): Promise => {\n const device = await this.connectAndDiscoverServices(macAddress);\n const result = (await this.writeWithSingleResponse(\n device,\n device.deviceType.COMMAND_BLINK,\n data => {\n const answer = this.utils.stringFromBase64(data);\n return !!answer.match(/ok/i);\n }\n )) as boolean;\n\n if (result) return true;\n throw new Error(` acknowledgement false`);\n };\n\n getInfo = async (macAddress: MacAddress): Promise => {\n const device = await this.connectAndDiscoverServices(macAddress);\n const monitorResultCallback: MonitorCharacteristicParser = data => {\n const parsedBase64 = data.map(this.utils.stringFromBase64);\n const defaultInfoLog: InfoLog = { batteryLevel: null, isDisabled: true };\n const blueMaestroBatteryLevel = (info: string): number | null => {\n const batteryLevelStringOrNull = info.match(/Batt lvl: [0-9]{1,3}/);\n\n if (!batteryLevelStringOrNull) return batteryLevelStringOrNull;\n\n const batteryLevel = Number(batteryLevelStringOrNull[0].match(/[0-9]{1,3}/));\n\n return Number.isNaN(batteryLevel)\n ? null\n : this.utils.normaliseNumber(batteryLevel, [75, 100]);\n };\n\n const bt510BatteryLevel = (info: string): number | null => {\n let batteryLevel = null;\n if (info) {\n const parsedInfo = JSON.parse(info);\n\n if (parsedInfo?.result !== 'ok') {\n return null;\n }\n\n batteryLevel = Number(parsedInfo.batteryVoltageMv);\n } else {\n return null;\n }\n return Number.isNaN(batteryLevel)\n ? null\n : this.utils.normaliseNumber(Math.min(batteryLevel, 3000), [2250, 3000]);\n };\n\n const parsedIsDisabled = (info: string): boolean => !!info.match(/Btn on\\/off: 1/);\n\n if (device.deviceType === BLUE_MAESTRO) {\n return parsedBase64.reduce((acc, info) => {\n const isDisabled = parsedIsDisabled(info);\n const batteryLevel = blueMaestroBatteryLevel(info);\n if (isDisabled) return { ...acc, isDisabled };\n if (batteryLevel) return { ...acc, batteryLevel };\n return acc;\n }, defaultInfoLog);\n } else {\n return { batteryLevel: bt510BatteryLevel(parsedBase64[0]), isDisabled: true };\n }\n };\n\n const result: InfoLog = (await this.writeAndMonitor(\n device,\n device.deviceType.COMMAND_INFO,\n monitorResultCallback\n )) as InfoLog;\n\n return result;\n };\n\n toggleButton = async (macAddress: MacAddress): Promise => {\n const device = await this.connectAndDiscoverServices(macAddress);\n if (device.deviceType === BT510) {\n // Laird doesn't have this command\n return true;\n }\n const result = (await this.writeWithSingleResponse(\n device,\n BLUE_MAESTRO.COMMAND_DISABLE_BUTTON,\n data => {\n return !!this.utils.stringFromBase64(data).match(/ok/i);\n }\n )) as boolean;\n return result;\n };\n\n getInfoWithRetries = async (\n macAddress: MacAddress,\n retriesLeft: number,\n error: Error | null\n ): Promise => {\n if (!retriesLeft) throw error;\n\n return this.getInfo(macAddress).catch(err =>\n this.getInfoWithRetries(macAddress, retriesLeft - 1, err)\n );\n };\n\n toggleButtonWithRetries = async (\n macAddress: MacAddress,\n retriesLeft: number,\n error: Error | null\n ): Promise => {\n if (!retriesLeft) throw error;\n\n return this.toggleButton(macAddress).catch(err =>\n this.toggleButtonWithRetries(macAddress, retriesLeft - 1, err)\n );\n };\n\n downloadLogsWithRetries = async (\n macAddress: MacAddress,\n retriesLeft: number,\n error: Error | null\n ): Promise => {\n this.logger.info('Starting to download logs', { macAddress, retriesLeft, error });\n if (!retriesLeft) throw error;\n\n return this.downloadLogs(macAddress).catch(err =>\n this.downloadLogsWithRetries(macAddress, retriesLeft - 1, err)\n );\n };\n\n blinkWithRetries = async (\n macAddress: MacAddress,\n retriesLeft: number,\n error: Error | null\n ): Promise => {\n if (!retriesLeft) throw error;\n\n return this.blink(macAddress).catch(err =>\n this.blinkWithRetries(macAddress, retriesLeft - 1, err)\n );\n };\n\n updateLogIntervalWithRetries = async (\n macAddress: MacAddress,\n logInterval: number,\n retriesLeft: number,\n clearLogs: boolean,\n error: Error | null\n ): Promise => {\n if (!retriesLeft) throw error;\n\n return this.updateLogInterval(macAddress, logInterval, clearLogs).catch(err =>\n this.updateLogIntervalWithRetries(macAddress, logInterval, retriesLeft - 1, clearLogs, err)\n );\n };\n}\n"]} \ No newline at end of file +{"version":3,"sources":["BleService.ts"],"names":["BtUtilService","Buffer","BLUE_MAESTRO","BT510","ScanMode","LogLevel","dummyLogger","trace","_message","_details","debug","info","warn","error","fatal","setLogLevel","_transportKey","_newLevel","RETRY_DELAY","sleep","delay","Promise","resolve","setTimeout","BleService","constructor","manager","logger","deviceId","connectToDevice","e","message","deviceDescriptor","device","utils","deviceDescriptorToDevice","deviceType","cancelDeviceConnection","id","deviceIsConnected","isDeviceConnected","discoverAllServicesAndCharacteristicsForDevice","MANUFACTURER_ID","stopDeviceScan","callback","scanOptions","scanMode","LowLatency","filteredCallback","err","console","log","JSON","stringify","manufacturerData","mfgId","from","readInt16LE","descriptor","deviceToDeviceDescriptor","startDeviceScan","command","writeCharacteristicWithoutResponseForDevice","BLUETOOTH_UART_SERVICE_UUID","BLUETOOTH_READ_CHARACTERISTIC_UUID","base64FromString","transactionId","reject","subscription","monitorCharacteristicForDevice","BLUETOOTH_WRITE_CHARACTERISTIC_UUID","result","Math","random","toString","substr","parser","data","done","alreadyDone","transmissionDone","val","str","stringFromBase64","pattern","RegExp","test","monitoringCallback","Boolean","value","name","reason","push","remove","length","Error","monitor","monitorCharacteristic","all","writeCharacteristic","then","r","catch","cancelTransaction","monitorCharacteristicCallback","macAddress","connectAndDiscoverServices","downloadLogs","writeWithSingleResponse","COMMAND_CLEAR","monitorCallback","join","buffer","concat","slice","map","datum","bufferFromBase64","ind","findIndex","_","i","readInt16BE","DELIMITER_A","DELIMITER_B","reduce","acc","index","time","temperature","TEMPERATURE_DIVISOR","parse","numEvents","Number","prepareLogs","prepCommand","COMMAND_PREPARE_LOG","replace","ackLogs","ackCommand","COMMAND_ACK_LOG","sensorLog","downloadCommand","COMMAND_DOWNLOAD","dataLog","writeAndMonitor","logBuffer","round","eventType","readInt8","logInterval","clearLogs","COMMAND_UPDATE_LOG_INTERVAL","match","COMMAND_BLINK","answer","monitorResultCallback","parsedBase64","defaultInfoLog","batteryLevel","isDisabled","blueMaestroBatteryLevel","batteryLevelStringOrNull","isNaN","normaliseNumber","bt510BatteryLevel","parsedInfo","batteryVoltageMv","min","parsedIsDisabled","COMMAND_INFO","COMMAND_DISABLE_BUTTON","retriesLeft","getInfo","getInfoWithRetries","toggleButton","toggleButtonWithRetries","downloadLogsWithRetries","blink","blinkWithRetries","updateLogInterval","updateLogIntervalWithRetries","Verbose"],"mappings":"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","sourcesContent":["import { BtUtilService } from '../BTUtilService';\n\nimport { Buffer } from 'buffer';\nimport { BLUE_MAESTRO, BT510 } from '../constants';\nimport { MacAddress } from '../types/common';\nimport {\n Characteristic,\n ScanOptions,\n ScanMode,\n TypedDevice,\n InfoLog,\n MonitorCharacteristicCallback,\n MonitorCharacteristicParser,\n ScanCallback,\n SensorLog,\n DataLog,\n LogLevel,\n Device,\n BleError,\n} from './types';\nimport { BluetoothManager, MockOrRealDevice } from './BleManager';\n\n// types copied from mobile/src/utilities/logging/\ntype Action = (message: string | Error, details?: Record) => void;\ninterface Logger {\n trace: Action;\n debug: Action;\n info: Action;\n warn: Action;\n error: Action;\n fatal: Action;\n setLogLevel: (transportKey: string, newLevel: number) => void;\n}\nconst dummyLogger: Logger = {\n trace: (_message, _details) => {\n /*do nothing*/\n },\n debug: (_message, _details) => {\n /*do nothing*/\n },\n info: (_message, _details) => {\n /*do nothing*/\n },\n warn: (_message, _details) => {\n /*do nothing*/\n },\n error: (_message, _details) => {\n /*do nothing*/\n },\n fatal: (_message, _details) => {\n /*do nothing*/\n },\n setLogLevel: (_transportKey, _newLevel) => {\n /*do nothing*/\n },\n};\nconst RETRY_DELAY = 1000;\nconst sleep = (delay: number) => new Promise(resolve => setTimeout(resolve, delay));\nexport class BleService {\n manager: BluetoothManager;\n utils: BtUtilService;\n logger: Logger;\n\n constructor(manager: BluetoothManager, logger = dummyLogger) {\n this.manager = manager;\n this.logger = logger;\n manager.setLogLevel(LogLevel.Verbose);\n // Caller passes in utils from the main app,\n // but we ignore it and use our own.\n // This needs to be fixed in the main app.\n this.utils = new BtUtilService();\n logger.info('BleService constructor called');\n }\n\n connectToDevice = (deviceId: string): Promise => {\n this.logger.debug(`${deviceId} Connect to device`);\n try {\n return this.manager.connectToDevice(deviceId);\n } catch (e) {\n this.logger.error(`${deviceId} Error connecting to device. ${e.message}`);\n throw e;\n }\n };\n\n connectAndDiscoverServices = async (deviceDescriptor: string): Promise => {\n this.logger.info(`${deviceDescriptor} connectAndDiscoverServices`);\n const device = this.utils.deviceDescriptorToDevice(deviceDescriptor);\n // the Blue Maestro devices are incorrectly reporting connection status\n // thus: deviceIsConnected?\t{ deviceIsConnected: true }\n // then if disconnecting [BleError: Device D7:D6:67:E0:02:34 is not connected]\n // in which case an error is thrown when trying to connect: [BleError: Device ? is already connected]\n // to work around this, we disconnect the device, ignoring any errors, before connecting again\n if (device.deviceType === BLUE_MAESTRO) {\n this.logger.debug(`${deviceDescriptor} Connecting to BM device`);\n try {\n await this.manager.cancelDeviceConnection(device.id);\n } catch (e) {\n this.logger.warn(`${deviceDescriptor} Error disconnecting! ${e.message}`);\n // ignore error\n }\n } else {\n this.logger.debug(`${deviceDescriptor} Connecting to other device`);\n const deviceIsConnected = await this.manager.isDeviceConnected(device.id);\n if (deviceIsConnected) {\n this.logger.debug(`${deviceDescriptor} Disconnecting`);\n await this.manager.cancelDeviceConnection(device.id);\n }\n }\n await this.connectToDevice(device.id);\n this.logger.debug(`${device.id} Connected to ${deviceDescriptor}`);\n\n await this.manager.discoverAllServicesAndCharacteristicsForDevice(device.id);\n this.logger.info(\n `${deviceDescriptor} Discovered all services and characteristics. id: ${device.id} manufacturer: ${device.deviceType.MANUFACTURER_ID}`\n );\n\n return device;\n };\n\n stopScan = (): void => {\n this.manager.stopDeviceScan();\n };\n\n scanForSensors = (callback: ScanCallback): void => {\n this.logger.info('Scanning for sensors');\n const scanOptions: ScanOptions = { scanMode: ScanMode.LowLatency };\n const filteredCallback = (err: BleError | null, device: Device | null): void => {\n if (err) {\n console.log('BleService Scan Error:', JSON.stringify(err));\n }\n\n if (device?.manufacturerData) {\n const mfgId = Buffer.from(device.manufacturerData, 'base64').readInt16LE(0);\n if (mfgId === BLUE_MAESTRO.MANUFACTURER_ID || mfgId === BT510.MANUFACTURER_ID) {\n const descriptor = this.utils.deviceToDeviceDescriptor(device.id, mfgId);\n\n callback(err, descriptor);\n }\n }\n };\n this.manager.startDeviceScan(null, scanOptions, filteredCallback);\n };\n\n writeCharacteristic = async (device: TypedDevice, command: string): Promise => {\n return this.manager.writeCharacteristicWithoutResponseForDevice(\n device.id,\n device.deviceType.BLUETOOTH_UART_SERVICE_UUID,\n device.deviceType.BLUETOOTH_READ_CHARACTERISTIC_UUID,\n this.utils.base64FromString(command)\n );\n };\n\n monitorCharacteristic = (\n device: TypedDevice,\n callback: MonitorCharacteristicCallback,\n transactionId: string\n ): Promise => {\n return new Promise((resolve, reject) => {\n const subscription = this.manager.monitorCharacteristicForDevice(\n device.id,\n device.deviceType.BLUETOOTH_UART_SERVICE_UUID,\n device.deviceType.BLUETOOTH_WRITE_CHARACTERISTIC_UUID,\n (error, result) => {\n callback(result, resolve, reject, subscription, error);\n },\n transactionId\n );\n });\n };\n\n // https://gist.github.com/gordonbrander/2230317\n transactionId = (): string => '_' + Math.random().toString(36).substr(2, 9);\n\n writeAndMonitor = async (\n device: TypedDevice,\n command: string,\n parser: MonitorCharacteristicParser\n ): Promise => {\n const data: string[] = [];\n let done = 0;\n const alreadyDone = (): number => done++;\n\n const transmissionDone = (val: string): boolean => {\n const str = this.utils.stringFromBase64(val);\n const pattern = new RegExp('.*}$'); // workaround for emacs web mode confused by bracket in a regexp literal\n const result = pattern.test(str);\n return result;\n };\n\n const monitoringCallback: MonitorCharacteristicCallback = (\n result,\n resolve,\n reject,\n subscription,\n error\n ) => {\n this.logger.debug(`${device.id} Monitor command: ${command}`);\n this.logger.debug(`${device.id} Monitor callback result valid: ${Boolean(result?.value)}`);\n if (error) {\n this.logger.debug(`${device.id} Monitor callback error name: ${error.name}`);\n this.logger.debug(`${device.id} Monitor callback error message: ${error.message}`);\n this.logger.debug(`${device.id} Monitor callback error reason: ${error.reason}`);\n }\n\n if (result?.value) {\n data.push(result.value);\n // return to wait for next chunk\n if (device.deviceType === BLUE_MAESTRO || !transmissionDone(result.value)) return;\n }\n try {\n subscription.remove();\n if (device.deviceType === BT510 && alreadyDone()) {\n // Don't call the parser more than once.\n // (Although it probably doesn't hurt anything,\n // since the Promise has already resolved and returned the result\n // to the caller)\n return;\n }\n this.logger.debug(`${device.id} Monitor callback. Data length: ${data.length}`);\n\n if (data.length === 0) throw new Error(' callback no data returned');\n resolve(parser(data));\n } catch (e) {\n reject(new Error(` callback parsing failed, ${e.message}`));\n }\n }; // end monitoringCallback\n\n const transactionId = this.transactionId();\n const monitor = this.monitorCharacteristic(device, monitoringCallback, transactionId);\n\n // We only care about the result if both the write and monitor succeed.\n return Promise.all([monitor, this.writeCharacteristic(device, command)])\n .then(r => r[0])\n .catch(e => {\n this.manager.cancelTransaction(transactionId);\n this.logger.error(`${device.id} writeAndMonitor rejected. ${e.message}`);\n throw new Error(` writeAndMonitor rejected, ${device.id} ${e.message}`);\n });\n };\n\n writeWithSingleResponse = async (\n device: TypedDevice,\n command: string,\n parser: MonitorCharacteristicParser\n ): Promise => {\n const monitorCharacteristicCallback: MonitorCharacteristicCallback = (\n result,\n resolve,\n reject,\n subscription\n ) => {\n subscription?.remove();\n if (result?.value) {\n try {\n resolve(parser(result.value));\n } catch (e) {\n reject(new Error(` callback parsing failed: ${e.message}`));\n }\n } else reject(new Error(` callback returns null`));\n }; // end monitorCharacteristicCallback\n\n const transactionId = this.transactionId();\n const monitor = this.monitorCharacteristic(\n device,\n monitorCharacteristicCallback,\n transactionId\n );\n // We only care about the result if both the write and monitor succeed.\n return Promise.all([monitor, this.writeCharacteristic(device, command)])\n .then(r => r[0])\n .catch(e => {\n this.manager.cancelTransaction(transactionId);\n throw new Error(` writeWithSingleResponse rejected, ${device.id} ${e.message}`);\n });\n };\n\n /** Facade for clearing logs.\n *\n * Connects with a sensor and clears all temperature logs.\n *\n * Returns a promise which resolves to boolean, which is ignored by the caller.\n *\n * @param {String} macAddress\n */\n clearLogs = async (macAddress: MacAddress): Promise => {\n this.logger.debug(`${macAddress} Clearing logs`);\n const device = await this.connectAndDiscoverServices(macAddress);\n if (device?.deviceType === BT510) {\n await this.downloadLogs(macAddress);\n } else {\n await this.writeWithSingleResponse(\n device,\n BLUE_MAESTRO.COMMAND_CLEAR,\n data => !!this.utils.stringFromBase64(data)\n );\n }\n };\n\n downloadLogs = async (macAddress: MacAddress): Promise => {\n this.logger.debug(`${macAddress} Download logs`);\n const device = await this.connectAndDiscoverServices(macAddress);\n this.logger.info(`${macAddress} Download logs connected and discovered services`);\n const monitorCallback: MonitorCharacteristicParser = (\n data: string[]\n ) => {\n this.logger.info(`${macAddress} Write and monitor found some data! ${data.length}`);\n this.logger.debug(`${macAddress} ${data.join('; ')}`);\n if (device.deviceType === BLUE_MAESTRO) {\n const buffer = Buffer.concat(\n data.slice(1).map(datum => this.utils.bufferFromBase64(datum))\n );\n const ind = buffer.findIndex(\n (_, i) =>\n (i % 2 === 0 && buffer.readInt16BE(i) === BLUE_MAESTRO.DELIMITER_A) ||\n buffer.readInt16BE(i) === BLUE_MAESTRO.DELIMITER_B\n );\n\n return (buffer.slice(0, ind) as Buffer).reduce((acc: SensorLog[], _, index) => {\n if (index % 2 !== 0) return acc;\n return [\n ...acc,\n { time: '', temperature: buffer.readInt16BE(index) / BLUE_MAESTRO.TEMPERATURE_DIVISOR },\n ];\n }, []);\n } else {\n // BT510\n const buffer = Buffer.concat(data.map(datum => this.utils.bufferFromBase64(datum)));\n const result = JSON.parse(buffer.toString());\n const numEvents = Number(result.result[0] / 8);\n return { numEvents, data: result.result[1] };\n }\n }; // end monitor callback\n\n if (device.deviceType === BT510) {\n // const FIFO = '0';\n // const LIFO = '1';\n this.logger.debug(`${macAddress} Preparing to download logs`);\n const prepareLogs = async (): Promise => {\n const prepCommand = BT510.COMMAND_PREPARE_LOG.replace('MODE', '0');\n\n return (await this.writeWithSingleResponse(device, prepCommand, data => {\n const info = this.utils.stringFromBase64(data);\n this.logger.debug(`${macAddress} Prepare logs response: ${info}`);\n return JSON.parse(info).result !== 0;\n })) as boolean;\n };\n const ackLogs = async (numEvents: number): Promise => {\n const ackCommand = BT510.COMMAND_ACK_LOG.replace('NUMEVENTS', numEvents.toString());\n return (await this.writeWithSingleResponse(device, ackCommand, data => {\n const info = this.utils.stringFromBase64(data);\n return JSON.parse(info).result === numEvents;\n })) as boolean;\n };\n\n let sensorLog = [] as SensorLog[];\n try {\n while (await prepareLogs()) {\n const downloadCommand = BT510.COMMAND_DOWNLOAD.replace('NUMEVENTS', '500');\n this.logger.debug(`${macAddress} Sending download command`);\n const dataLog = (await this.writeAndMonitor(\n device,\n downloadCommand,\n monitorCallback\n )) as DataLog;\n const logBuffer = this.utils.bufferFromBase64(dataLog.data);\n\n const log = logBuffer.reduce((acc: SensorLog[], _, index) => {\n if (index % 8 !== 0) return acc;\n //const time = logBuffer.readInt32LE(index);\n const temperature =\n Math.round((logBuffer.readInt16LE(index + 4) / BT510.TEMPERATURE_DIVISOR) * 10) / 10;\n const eventType = logBuffer.readInt8(index + 6);\n //const salt = logBuffer.readInt8(index + 7);\n if (eventType === 1) {\n return [\n ...acc,\n {\n temperature,\n },\n ];\n } else {\n return [...acc];\n }\n }, []);\n\n if (await ackLogs(dataLog.numEvents)) {\n this.logger.debug(`${macAddress} Ack received`);\n sensorLog = sensorLog.concat(log);\n }\n }\n } catch (e) {\n this.logger.error(`${macAddress} Error downloading logs. ${e.message}`);\n if (sensorLog.length === 0) {\n throw new Error(`downloadLogs ${e.message}`);\n }\n // But if we partially succeeded, return that\n }\n return sensorLog;\n } else {\n try {\n const command = BLUE_MAESTRO.COMMAND_DOWNLOAD.replace('NUMEVENTS', '500');\n this.logger.debug(`${macAddress} Sending download command`);\n const result = (await this.writeAndMonitor(\n device,\n command,\n monitorCallback\n )) as SensorLog[];\n return result;\n } catch (e) {\n this.logger.error(`${macAddress} Error downloading logs! ${e.message}`);\n }\n return [] as SensorLog[];\n }\n };\n\n updateLogInterval = async (\n macAddress: MacAddress,\n logInterval: number,\n clearLogs = true\n ): Promise => {\n this.logger.debug(`${macAddress} Update log interval`);\n const device = await this.connectAndDiscoverServices(macAddress);\n\n const command = device.deviceType.COMMAND_UPDATE_LOG_INTERVAL.replace(\n 'LOG_INTERVAL',\n logInterval.toString()\n );\n const result = await this.writeWithSingleResponse(device, command, data => {\n const info = this.utils.stringFromBase64(data);\n return (\n (device.deviceType === BT510 && JSON.parse(info).result === 'ok') ||\n !!info.match(/interval/i)\n );\n });\n // Clear logs if we haven't just downloaded\n // BlueMaestro automatically clears logs when log interval is set,\n // But we have to download all the logs to clear them on BT510\n if (clearLogs && device.deviceType === BT510) {\n await this.downloadLogs(macAddress);\n }\n if (result) return true;\n throw new Error(` command returned not OK result`);\n };\n\n blink = async (macAddress: MacAddress): Promise => {\n this.logger.debug(`${macAddress} Blink`);\n const device = await this.connectAndDiscoverServices(macAddress);\n const result = (await this.writeWithSingleResponse(\n device,\n device.deviceType.COMMAND_BLINK,\n data => {\n const answer = this.utils.stringFromBase64(data);\n return !!answer.match(/ok/i);\n }\n )) as boolean;\n\n if (result) return true;\n throw new Error(` acknowledgement false`);\n };\n\n getInfo = async (macAddress: MacAddress): Promise => {\n this.logger.debug(`${macAddress} Get info`);\n const device = await this.connectAndDiscoverServices(macAddress);\n const monitorResultCallback: MonitorCharacteristicParser = data => {\n const parsedBase64 = data.map(this.utils.stringFromBase64);\n const defaultInfoLog: InfoLog = { batteryLevel: null, isDisabled: true };\n const blueMaestroBatteryLevel = (info: string): number | null => {\n const batteryLevelStringOrNull = info.match(/Batt lvl: [0-9]{1,3}/);\n\n if (!batteryLevelStringOrNull) return batteryLevelStringOrNull;\n\n const batteryLevel = Number(batteryLevelStringOrNull[0].match(/[0-9]{1,3}/));\n\n return Number.isNaN(batteryLevel)\n ? null\n : this.utils.normaliseNumber(batteryLevel, [75, 100]);\n };\n\n const bt510BatteryLevel = (info: string): number | null => {\n let batteryLevel: number | null = null;\n if (info) {\n const parsedInfo = JSON.parse(info);\n\n if (parsedInfo?.result !== 'ok') {\n return null;\n }\n\n batteryLevel = Number(parsedInfo.batteryVoltageMv);\n } else {\n return null;\n }\n return Number.isNaN(batteryLevel)\n ? null\n : this.utils.normaliseNumber(Math.min(batteryLevel, 3000), [2250, 3000]);\n };\n\n const parsedIsDisabled = (info: string): boolean => !!info.match(/Btn on\\/off: 1/);\n\n if (device.deviceType === BLUE_MAESTRO) {\n return parsedBase64.reduce((acc, info) => {\n const isDisabled = parsedIsDisabled(info);\n const batteryLevel = blueMaestroBatteryLevel(info);\n if (isDisabled) return { ...acc, isDisabled };\n if (batteryLevel) return { ...acc, batteryLevel };\n return acc;\n }, defaultInfoLog);\n } else {\n return { batteryLevel: bt510BatteryLevel(parsedBase64[0]), isDisabled: true };\n }\n };\n\n const result: InfoLog = (await this.writeAndMonitor(\n device,\n device.deviceType.COMMAND_INFO,\n monitorResultCallback\n )) as InfoLog;\n\n return result;\n };\n\n toggleButton = async (macAddress: MacAddress): Promise => {\n this.logger.debug(`${macAddress} Toggle button`);\n const device = await this.connectAndDiscoverServices(macAddress);\n if (device.deviceType === BT510) {\n // Laird doesn't have this command\n return true;\n }\n const result = (await this.writeWithSingleResponse(\n device,\n BLUE_MAESTRO.COMMAND_DISABLE_BUTTON,\n data => {\n return !!this.utils.stringFromBase64(data).match(/ok/i);\n }\n )) as boolean;\n return result;\n };\n\n getInfoWithRetries = async (\n macAddress: MacAddress,\n retriesLeft: number,\n error: Error | null\n ): Promise => {\n if (!retriesLeft) {\n this.logger.error(`${macAddress} getInfoWithRetries failed. ${error?.message}`);\n throw error;\n }\n\n await sleep(RETRY_DELAY);\n\n return this.getInfo(macAddress).catch(err =>\n this.getInfoWithRetries(macAddress, retriesLeft - 1, err)\n );\n };\n\n toggleButtonWithRetries = async (\n macAddress: MacAddress,\n retriesLeft: number,\n error: Error | null\n ): Promise => {\n if (!retriesLeft) throw error;\n\n await sleep(RETRY_DELAY);\n\n return this.toggleButton(macAddress).catch(err =>\n this.toggleButtonWithRetries(macAddress, retriesLeft - 1, err)\n );\n };\n\n downloadLogsWithRetries = async (\n macAddress: MacAddress,\n retriesLeft: number,\n error: Error | null\n ): Promise => {\n this.logger.info(`${macAddress} Download logs with retries`);\n this.logger.debug(\n `${macAddress} Starting to download logs. There are ${retriesLeft} retries left. Error: ${error?.message}`\n );\n if (!retriesLeft) throw error;\n\n await sleep(RETRY_DELAY);\n\n return this.downloadLogs(macAddress).catch(err =>\n this.downloadLogsWithRetries(macAddress, retriesLeft - 1, err)\n );\n };\n\n blinkWithRetries = async (\n macAddress: MacAddress,\n retriesLeft: number,\n error: Error | null\n ): Promise => {\n if (!retriesLeft) throw error;\n\n await sleep(RETRY_DELAY);\n\n return this.blink(macAddress).catch(err =>\n this.blinkWithRetries(macAddress, retriesLeft - 1, err)\n );\n };\n\n updateLogIntervalWithRetries = async (\n macAddress: MacAddress,\n logInterval: number,\n retriesLeft: number,\n clearLogs: boolean,\n error: Error | null\n ): Promise => {\n if (!retriesLeft) throw error;\n\n await sleep(RETRY_DELAY);\n\n return this.updateLogInterval(macAddress, logInterval, clearLogs).catch(err =>\n this.updateLogIntervalWithRetries(macAddress, logInterval, retriesLeft - 1, clearLogs, err)\n );\n };\n}\n"]} \ No newline at end of file diff --git a/.yalc/msupply-ble-service/lib/module/Bluetooth/types.js.map b/.yalc/msupply-ble-service/lib/module/Bluetooth/types.js.map index 07decc3a..4ef6c775 100644 --- a/.yalc/msupply-ble-service/lib/module/Bluetooth/types.js.map +++ b/.yalc/msupply-ble-service/lib/module/Bluetooth/types.js.map @@ -1 +1 @@ -{"version":3,"sources":["types.ts"],"names":["ScanMode","Subscription","Characteristic","BleError","Device","ScanOptions","LogLevel"],"mappings":"AAAA,SACEA,QADF,EAEEC,YAFF,EAGEC,cAHF,EAIEC,QAJF,EAKEC,MALF,EAMEC,WANF,EAOEC,QAPF,QAQO,sBARP;AA0DA,SAASN,QAAT,EAAmBC,YAAnB,EAAiCC,cAAjC,EAAiDC,QAAjD,EAA2DC,MAA3D,EAAmEC,WAAnE,EAAgFC,QAAhF","sourcesContent":["import {\n ScanMode,\n Subscription,\n Characteristic,\n BleError,\n Device,\n ScanOptions,\n LogLevel,\n} from 'react-native-ble-plx';\n\nimport { BT510, BLUE_MAESTRO } from '../constants';\n\nexport type DeviceType = typeof BT510 | typeof BLUE_MAESTRO; // BT510 | BLUE_MAESTRO\n\nexport interface TypedDevice {\n id: string;\n deviceType: DeviceType; // BT510 | BLUE_MAESTRO\n}\n\nexport interface InfoLog {\n batteryLevel: null | number;\n isDisabled: boolean;\n}\n\nexport interface SensorLog {\n temperature: number;\n}\n\nexport interface DataLog {\n numEvents: number;\n data: string;\n}\n\nexport interface ScanCallback {\n (error: BleError | null, deviceDescriptor: string): void;\n}\n\nexport interface Resolver {\n (result: ResolverResult): void;\n}\n\nexport interface ErrorRejector {\n (error: Error): void;\n}\n\nexport interface MonitorCharacteristicCallback {\n (\n result: Characteristic | null,\n resolver: Resolver,\n rejector: ErrorRejector,\n subscription: Subscription\n ): void;\n}\n\nexport interface MonitorCharacteristicParser {\n (result: ParserInput): ParserResult;\n}\n\nexport { ScanMode, Subscription, Characteristic, BleError, Device, ScanOptions, LogLevel };\n"]} \ No newline at end of file +{"version":3,"sources":["types.ts"],"names":["ScanMode","Subscription","Characteristic","BleError","Device","ScanOptions","LogLevel"],"mappings":"AAAA,SACEA,QADF,EAEEC,YAFF,EAGEC,cAHF,EAIEC,QAJF,EAKEC,MALF,EAMEC,WANF,EAOEC,QAPF,QAQO,sBARP;AA2DA,SAASN,QAAT,EAAmBC,YAAnB,EAAiCC,cAAjC,EAAiDC,QAAjD,EAA2DC,MAA3D,EAAmEC,WAAnE,EAAgFC,QAAhF","sourcesContent":["import {\n ScanMode,\n Subscription,\n Characteristic,\n BleError,\n Device,\n ScanOptions,\n LogLevel,\n} from 'react-native-ble-plx';\n\nimport { BT510, BLUE_MAESTRO } from '../constants';\n\nexport type DeviceType = typeof BT510 | typeof BLUE_MAESTRO; // BT510 | BLUE_MAESTRO\n\nexport interface TypedDevice {\n id: string;\n deviceType: DeviceType; // BT510 | BLUE_MAESTRO\n}\n\nexport interface InfoLog {\n batteryLevel: null | number;\n isDisabled: boolean;\n}\n\nexport interface SensorLog {\n temperature: number;\n}\n\nexport interface DataLog {\n numEvents: number;\n data: string;\n}\n\nexport interface ScanCallback {\n (error: BleError | null, deviceDescriptor: string): void;\n}\n\nexport interface Resolver {\n (result: ResolverResult): void;\n}\n\nexport interface ErrorRejector {\n (error: Error): void;\n}\n\nexport interface MonitorCharacteristicCallback {\n (\n result: Characteristic | null,\n resolver: Resolver,\n rejector: ErrorRejector,\n subscription: Subscription,\n error: BleError | null\n ): void;\n}\n\nexport interface MonitorCharacteristicParser {\n (result: ParserInput): ParserResult;\n}\n\nexport { ScanMode, Subscription, Characteristic, BleError, Device, ScanOptions, LogLevel };\n"]} \ No newline at end of file diff --git a/.yalc/msupply-ble-service/lib/typescript/Bluetooth/types.d.ts b/.yalc/msupply-ble-service/lib/typescript/Bluetooth/types.d.ts index 48499848..b1e67b32 100644 --- a/.yalc/msupply-ble-service/lib/typescript/Bluetooth/types.d.ts +++ b/.yalc/msupply-ble-service/lib/typescript/Bluetooth/types.d.ts @@ -26,7 +26,7 @@ export interface ErrorRejector { (error: Error): void; } export interface MonitorCharacteristicCallback { - (result: Characteristic | null, resolver: Resolver, rejector: ErrorRejector, subscription: Subscription): void; + (result: Characteristic | null, resolver: Resolver, rejector: ErrorRejector, subscription: Subscription, error: BleError | null): void; } export interface MonitorCharacteristicParser { (result: ParserInput): ParserResult; diff --git a/.yalc/msupply-ble-service/package.json b/.yalc/msupply-ble-service/package.json index bde547f2..6a8d2c95 100644 --- a/.yalc/msupply-ble-service/package.json +++ b/.yalc/msupply-ble-service/package.json @@ -1,10 +1,9 @@ { "name": "msupply-ble-service", - "version": "0.5.2", + "version": "0.5.3", "description": "Low level bluetooth library", "author": "Chester Wood (https://github.com/chetstone)", "license": "GPL-3.0-or-later", - "private": true, "main": "lib/commonjs/index", "module": "lib/module/index", "types": "lib/typescript/index.d.ts", @@ -87,5 +86,5 @@ "dependencies": { "buffer": "^6.0.3" }, - "yalcSig": "89487824f857c8b9bda3af069baebdad" + "yalcSig": "976a57bfd8696017226d857b4c611eb5" } diff --git a/.yalc/msupply-ble-service/src/Bluetooth/BleService.ts b/.yalc/msupply-ble-service/src/Bluetooth/BleService.ts index 19b2820b..30b07043 100644 --- a/.yalc/msupply-ble-service/src/Bluetooth/BleService.ts +++ b/.yalc/msupply-ble-service/src/Bluetooth/BleService.ts @@ -29,7 +29,7 @@ interface Logger { warn: Action; error: Action; fatal: Action; - setLogLevel: (logLevel: LogLevel) => void; + setLogLevel: (transportKey: string, newLevel: number) => void; } const dummyLogger: Logger = { trace: (_message, _details) => { @@ -50,7 +50,7 @@ const dummyLogger: Logger = { fatal: (_message, _details) => { /*do nothing*/ }, - setLogLevel: (_logLevel) => { + setLogLevel: (_transportKey, _newLevel) => { /*do nothing*/ }, }; @@ -160,8 +160,8 @@ export class BleService { device.id, device.deviceType.BLUETOOTH_UART_SERVICE_UUID, device.deviceType.BLUETOOTH_WRITE_CHARACTERISTIC_UUID, - (_, result) => { - callback(result, resolve, reject, subscription); + (error, result) => { + callback(result, resolve, reject, subscription, error); }, transactionId ); @@ -191,8 +191,17 @@ export class BleService { result, resolve, reject, - subscription + subscription, + error ) => { + this.logger.debug(`${device.id} Monitor command: ${command}`); + this.logger.debug(`${device.id} Monitor callback result valid: ${Boolean(result?.value)}`); + if (error) { + this.logger.debug(`${device.id} Monitor callback error name: ${error.name}`); + this.logger.debug(`${device.id} Monitor callback error message: ${error.message}`); + this.logger.debug(`${device.id} Monitor callback error reason: ${error.reason}`); + } + if (result?.value) { data.push(result.value); // return to wait for next chunk diff --git a/.yalc/msupply-ble-service/src/Bluetooth/types.ts b/.yalc/msupply-ble-service/src/Bluetooth/types.ts index 2a355f9f..7ce6ae1c 100644 --- a/.yalc/msupply-ble-service/src/Bluetooth/types.ts +++ b/.yalc/msupply-ble-service/src/Bluetooth/types.ts @@ -48,7 +48,8 @@ export interface MonitorCharacteristicCallback { result: Characteristic | null, resolver: Resolver, rejector: ErrorRejector, - subscription: Subscription + subscription: Subscription, + error: BleError | null ): void; } diff --git a/.yalc/msupply-ble-service/yalc.sig b/.yalc/msupply-ble-service/yalc.sig index ed11d939..ae1d61ea 100644 --- a/.yalc/msupply-ble-service/yalc.sig +++ b/.yalc/msupply-ble-service/yalc.sig @@ -1 +1 @@ -89487824f857c8b9bda3af069baebdad \ No newline at end of file +976a57bfd8696017226d857b4c611eb5 \ No newline at end of file diff --git a/yalc.lock b/yalc.lock index 5a681ba6..373a6e47 100644 --- a/yalc.lock +++ b/yalc.lock @@ -2,7 +2,7 @@ "version": "v1", "packages": { "msupply-ble-service": { - "signature": "89487824f857c8b9bda3af069baebdad", + "signature": "976a57bfd8696017226d857b4c611eb5", "file": true, "replaced": "link:../msupply-ble-service" } From 0a3aa231832bcfb7347342906252d71d0ff9cd27 Mon Sep 17 00:00:00 2001 From: Cam Roots Date: Thu, 18 May 2023 00:01:56 +1200 Subject: [PATCH 3/4] Added battery updating check --- .../Bluetooth/BatteryObserver/BatteryObserverSlice.ts | 11 +++++++++++ src/features/Bluetooth/Download/DownloadSlice.ts | 8 ++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/features/Bluetooth/BatteryObserver/BatteryObserverSlice.ts b/src/features/Bluetooth/BatteryObserver/BatteryObserverSlice.ts index b19dd9e3..a37fb809 100644 --- a/src/features/Bluetooth/BatteryObserver/BatteryObserverSlice.ts +++ b/src/features/Bluetooth/BatteryObserver/BatteryObserverSlice.ts @@ -18,6 +18,7 @@ import { SensorAction, SensorManager } from '~features/Entities'; import { getDependency } from '~features/utils/saga'; import { BleService } from 'msupply-ble-service'; import { isSensorDownloading } from '../Download/DownloadSlice'; +import { RootState } from '~common/store'; const INFO_RETRIES = 2; @@ -35,6 +36,16 @@ interface BatteryUpdatePayload { sensorId: string; } +export const isSensorUpdating = + (sensorId: string) => + (state: RootState): boolean => { + try { + return state.bluetooth.batteryObserver.updatingById[sensorId] || false; + } catch { + return false; + } + }; + const reducers = { start: (draftState: BatteryObserverState) => { draftState.isWatching = true; diff --git a/src/features/Bluetooth/Download/DownloadSlice.ts b/src/features/Bluetooth/Download/DownloadSlice.ts index 8f9702b3..e070031a 100644 --- a/src/features/Bluetooth/Download/DownloadSlice.ts +++ b/src/features/Bluetooth/Download/DownloadSlice.ts @@ -25,6 +25,7 @@ import { } from '~features'; import { FileLoggerService } from '~common/services'; import { RootState } from '~common/store'; +import { isSensorUpdating } from '../BatteryObserver/BatteryObserverSlice'; const DOWNLOAD_RETRIES = 3; interface DownloadSliceState { @@ -122,11 +123,14 @@ function* tryDownloadForSensor({ const sensor = yield call(sensorManager.getSensorById, sensorId); const [canDownload] = yield call(sensorManager.getCanDownload, sensorId); const isDownloading = yield select(isSensorDownloading(sensorId)); + const isUpdating = yield select(isSensorUpdating(sensorId)); + + const doDownload = canDownload && !isDownloading && !isUpdating; logger.debug( - `${sensorId} tryDownloadForSensor canDownload: ${canDownload} isDownloading: ${isDownloading}` + `${sensorId} tryDownloadForSensor canDownload: ${canDownload} isDownloading: ${isDownloading} isUpdating: ${isUpdating} doDownload: ${doDownload}` ); - if (canDownload && !isDownloading) { + if (doDownload) { yield put(DownloadAction.downloadStart(sensorId)); const { macAddress, logInterval, logDelay, programmedDate } = sensor; From 9d9adb038f1afdd5a83eb77db813e3749d0ae7cc Mon Sep 17 00:00:00 2001 From: Cam Roots Date: Sun, 21 May 2023 23:14:07 +1200 Subject: [PATCH 4/4] Fixed incorrect calling of updateLogIntervalWithRetries --- src/features/Bluetooth/Download/DownloadSlice.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/Bluetooth/Download/DownloadSlice.ts b/src/features/Bluetooth/Download/DownloadSlice.ts index e070031a..95890f52 100644 --- a/src/features/Bluetooth/Download/DownloadSlice.ts +++ b/src/features/Bluetooth/Download/DownloadSlice.ts @@ -159,7 +159,7 @@ function* tryDownloadForSensor({ ); yield call(downloadManager.saveLogs, sensorLogs); - if (numberOfLogsToSave) { + if (sensorLogs.length) { yield call( btService.updateLogIntervalWithRetries, macAddress,