@mentra/bluetooth-sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +278 -0
- package/android/build.gradle +167 -0
- package/android/lc3Lib/CMakeLists.txt +6 -0
- package/android/lc3Lib/build.gradle +109 -0
- package/android/lc3Lib/proguard-rules.pro +33 -0
- package/android/lc3Lib/src/main/AndroidManifest.xml +2 -0
- package/android/lc3Lib/src/main/cpp/CMakeLists.txt +8 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/CMakeLists.txt +30 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/jni/CMakeLists.txt +7 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/jni/ogg_opus_encoder.cc +96 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/jni/ogg_opus_encoder.h +53 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/Makefile.am +6 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/config_types.h +26 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/config_types.h.in +26 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/ogg.h +209 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/os_types.h +158 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus.h +981 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_custom.h +342 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_defines.h +799 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_multistream.h +660 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_projection.h +568 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_types.h +166 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/ogg_opus_encoder.cc +268 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/ogg_opus_encoder.h +115 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/opus_tools/opus_header.h +59 -0
- package/android/lc3Lib/src/main/cpp/liblc3/CMakeLists.txt +38 -0
- package/android/lc3Lib/src/main/cpp/liblc3/include/lc3.h +309 -0
- package/android/lc3Lib/src/main/cpp/liblc3/include/lc3_private.h +162 -0
- package/android/lc3Lib/src/main/cpp/liblc3/include/rnnoise.h +114 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/CMakeLists.txt +19 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/attdet.c +92 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/attdet.h +44 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bits.c +375 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bits.h +315 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bwdet.c +129 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bwdet.h +69 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/common.h +148 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/energy.c +70 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/energy.h +43 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/fastmath.h +158 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/lc3.c +702 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf.c +893 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf.h +111 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf_arm.h +506 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf_neon.h +281 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/makefile.mk +35 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/mdct.c +452 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/mdct.h +57 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/mdct_neon.h +296 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/meson.build +46 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/plc.c +61 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/plc.h +57 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/sns.c +880 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/sns.h +103 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/spec.c +904 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/spec.h +119 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tables.c +3457 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tables.h +94 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tns.c +457 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tns.h +99 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3.cpp +159 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/_kiss_fft_guts.h +182 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/arch.h +261 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/celt_lpc.c +279 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/celt_lpc.h +59 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/common.h +48 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/compile.sh +3 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/denoise.c +646 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/kiss_fft.c +601 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/kiss_fft.h +203 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/opus_types.h +159 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/pitch.c +526 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/pitch.h +149 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn.c +178 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn.h +69 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_data.c +11051 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_data.h +34 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_reader.c +168 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_train.py +66 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/tansig_table.h +45 -0
- package/android/lc3Lib/src/main/java/com/mentra/lc3Lib/Lc3Cpp.java +38 -0
- package/android/lc3Lib/third_party/CMakeLists.txt +23 -0
- package/android/lc3Lib/third_party/CMakeLists_libogg.txt +18 -0
- package/android/lc3Lib/third_party/CMakeLists_libopus.txt +18 -0
- package/android/lc3Lib/third_party/CMakeLists_opus-tools.txt +23 -0
- package/android/lc3Lib/third_party/opus_tools/src/AUTHORS +5 -0
- package/android/lc3Lib/third_party/opus_tools/src/CMakeLists.txt +4 -0
- package/android/lc3Lib/third_party/opus_tools/src/COPYING +371 -0
- package/android/lc3Lib/third_party/opus_tools/src/ChangeLog +3 -0
- package/android/lc3Lib/third_party/opus_tools/src/Makefile.am +42 -0
- package/android/lc3Lib/third_party/opus_tools/src/Makefile.unix +23 -0
- package/android/lc3Lib/third_party/opus_tools/src/NEWS +0 -0
- package/android/lc3Lib/third_party/opus_tools/src/README +0 -0
- package/android/lc3Lib/third_party/opus_tools/src/autogen.sh +115 -0
- package/android/lc3Lib/third_party/opus_tools/src/configure.ac +293 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/arch.h +239 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/audio-in.c +1046 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/diag_range.c +245 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/diag_range.h +28 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/info_opus.c +320 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/info_opus.h +51 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/lpc.c +157 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/lpc.h +27 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/opus_header.c +286 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/opus_header.h +59 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/opusdec.c +884 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/opusenc.c +1021 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/opusenc.h +101 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/opusinfo.c +639 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/opusinfo.h +51 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/os_support.h +167 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/resample.c +1137 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/speex_resampler.h +344 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/stack_alloc.h +115 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/wav_io.c +125 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/wav_io.h +62 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/wave_out.c +223 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/wave_out.h +60 -0
- package/android/settings.gradle +1 -0
- package/android/src/main/AndroidManifest.xml +40 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +528 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +610 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +1616 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +314 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothModels.kt +1710 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +554 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/ObservableStore.kt +68 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/controllers/ControllerManager.kt +158 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/controllers/R1.kt +934 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/services/Foreground.kt +175 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/services/PhoneMic.kt +1109 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G1.java +3974 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G2.kt +3551 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Mach1.java +1327 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.java +6959 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraNex.kt +1655 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraosBle.java +53675 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SGCManager.kt +202 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Simulated.kt +224 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/stt/STTTools.kt +340 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/stt/SherpaOnnxTranscriber.kt +465 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/stt/VadGateSpeechPolicy.kt +221 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/AES.java +106 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/AudioSessionMonitor.kt +189 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/BitmapJavaUtils.java +169 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/BlePhotoUploadService.java +179 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/Constants.kt +44 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/G1Text.kt +436 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/IncidentLogBleRelayNaming.java +29 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/IncidentLogBleUploadService.java +76 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/K900ProtocolUtils.java +791 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/MessageChunker.java +186 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/NexSGCUtils.kt +589 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/PhoneAudioMonitor.kt +303 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/SmartGlassesConnectionState.java +9 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/audio/ByteUtilAudioPlayer.java +655 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/audio/Lc3Player.java +441 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/audio/PCMAudioPlayer.java +431 -0
- package/android/src/main/res/values/strings.xml +4 -0
- package/app.plugin.js +1 -0
- package/build/BluetoothSdk.types.d.ts +498 -0
- package/build/BluetoothSdk.types.d.ts.map +1 -0
- package/build/BluetoothSdk.types.js +23 -0
- package/build/BluetoothSdk.types.js.map +1 -0
- package/build/BluetoothSdkModule.d.ts +71 -0
- package/build/BluetoothSdkModule.d.ts.map +1 -0
- package/build/BluetoothSdkModule.js +106 -0
- package/build/BluetoothSdkModule.js.map +1 -0
- package/build/index.d.ts +3 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +5 -0
- package/build/index.js.map +1 -0
- package/expo-module.config.json +9 -0
- package/ios/BluetoothSdkModule.swift +665 -0
- package/ios/MentraBluetoothSDK.podspec +77 -0
- package/ios/Packages/CoreObjC/CoreObjC.xcodeproj/project.pbxproj +213 -0
- package/ios/Packages/CoreObjC/PcmConverter.h +22 -0
- package/ios/Packages/CoreObjC/PcmConverter.m +266 -0
- package/ios/Packages/CoreObjC/attdet.c +92 -0
- package/ios/Packages/CoreObjC/attdet.h +44 -0
- package/ios/Packages/CoreObjC/bits.c +375 -0
- package/ios/Packages/CoreObjC/bits.h +315 -0
- package/ios/Packages/CoreObjC/bwdet.c +129 -0
- package/ios/Packages/CoreObjC/bwdet.h +69 -0
- package/ios/Packages/CoreObjC/common.h +151 -0
- package/ios/Packages/CoreObjC/energy.c +70 -0
- package/ios/Packages/CoreObjC/energy.h +43 -0
- package/ios/Packages/CoreObjC/fastmath.h +158 -0
- package/ios/Packages/CoreObjC/lc3.c +704 -0
- package/ios/Packages/CoreObjC/lc3.h +313 -0
- package/ios/Packages/CoreObjC/lc3_cpp.h +283 -0
- package/ios/Packages/CoreObjC/lc3_private.h +163 -0
- package/ios/Packages/CoreObjC/ltpf.c +905 -0
- package/ios/Packages/CoreObjC/ltpf.h +111 -0
- package/ios/Packages/CoreObjC/ltpf_arm.h +506 -0
- package/ios/Packages/CoreObjC/ltpf_neon.h +281 -0
- package/ios/Packages/CoreObjC/makefile.mk +35 -0
- package/ios/Packages/CoreObjC/mdct.c +469 -0
- package/ios/Packages/CoreObjC/mdct.h +57 -0
- package/ios/Packages/CoreObjC/mdct_neon.h +296 -0
- package/ios/Packages/CoreObjC/meson.build +61 -0
- package/ios/Packages/CoreObjC/plc.c +61 -0
- package/ios/Packages/CoreObjC/plc.h +57 -0
- package/ios/Packages/CoreObjC/rnnoise.h +114 -0
- package/ios/Packages/CoreObjC/sns.c +880 -0
- package/ios/Packages/CoreObjC/sns.h +103 -0
- package/ios/Packages/CoreObjC/spec.c +907 -0
- package/ios/Packages/CoreObjC/spec.h +119 -0
- package/ios/Packages/CoreObjC/tables.c +3457 -0
- package/ios/Packages/CoreObjC/tables.h +94 -0
- package/ios/Packages/CoreObjC/tns.c +457 -0
- package/ios/Packages/CoreObjC/tns.h +99 -0
- package/ios/Packages/SherpaOnnx/Model/joiner.onnx +0 -0
- package/ios/Packages/SherpaOnnx/Model/tokens.txt +502 -0
- package/ios/Packages/SherpaOnnx/SherpaOnnx.swift +1659 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Headers/cargs.h +162 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Headers/sherpa-onnx/c-api/c-api.h +1852 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Headers/sherpa-onnx/c-api/cxx-api.h +674 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Info.plist +44 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/libsherpa-onnx.a +0 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/sherpa-onnx.a +0 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/libsherpa-onnx.a +0 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/sherpa-onnx.a +0 -0
- package/ios/Packages/VAD/Common/VAD/VADQuality.swift +41 -0
- package/ios/Packages/VAD/Common/VAD/VADState.swift +26 -0
- package/ios/Packages/VAD/Common/VAD/VADStrategy.swift +29 -0
- package/ios/Packages/VAD/Common/VAD/VADType.swift +14 -0
- package/ios/Packages/VAD/Data/Configuration.swift +68 -0
- package/ios/Packages/VAD/Data/FrameSize.swift +39 -0
- package/ios/Packages/VAD/Data/Record.swift +13 -0
- package/ios/Packages/VAD/Data/Result.swift +22 -0
- package/ios/Packages/VAD/Data/SampleRate.swift +48 -0
- package/ios/Packages/VAD/Silero/Model/silero_vad.onnx +0 -0
- package/ios/Packages/VAD/Silero/SileroVAD.swift +284 -0
- package/ios/Packages/VAD/Silero/SileroVADStrategy.swift +64 -0
- package/ios/Packages/libbz2/module.modulemap +5 -0
- package/ios/Packages/libbz2/shim.h +1 -0
- package/ios/Source/Bridge.swift +353 -0
- package/ios/Source/Bridging-Header.h +16 -0
- package/ios/Source/DeviceManager.swift +1478 -0
- package/ios/Source/DeviceStore.swift +295 -0
- package/ios/Source/MentraBluetoothSDK.swift +2439 -0
- package/ios/Source/ObservableStore.swift +88 -0
- package/ios/Source/PrivacyInfo.xcprivacy +23 -0
- package/ios/Source/controllers/ControllerManager.swift +187 -0
- package/ios/Source/controllers/R1.swift +827 -0
- package/ios/Source/services/PhoneMic.swift +662 -0
- package/ios/Source/sgcs/Frame.swift +651 -0
- package/ios/Source/sgcs/G1.swift +2505 -0
- package/ios/Source/sgcs/G2.swift +3730 -0
- package/ios/Source/sgcs/Mach1.swift +543 -0
- package/ios/Source/sgcs/MentraLive.swift +4708 -0
- package/ios/Source/sgcs/MentraNex.swift +2479 -0
- package/ios/Source/sgcs/SGCManager.swift +243 -0
- package/ios/Source/sgcs/Simulated.swift +270 -0
- package/ios/Source/sgcs/mentraos_ble.pb.swift +3811 -0
- package/ios/Source/stt/STTTools.swift +154 -0
- package/ios/Source/stt/SherpaOnnxTranscriber.swift +401 -0
- package/ios/Source/utils/AudioSessionMonitor.swift +283 -0
- package/ios/Source/utils/Constants.swift +74 -0
- package/ios/Source/utils/Enums.swift +95 -0
- package/ios/Source/utils/G1Text.swift +2067 -0
- package/ios/Source/utils/JSCExperiment.swift +241 -0
- package/ios/Source/utils/MemoryMonitor.swift +44 -0
- package/ios/Source/utils/MessageChunker.swift +164 -0
- package/ios/Source/utils/Models.swift +135 -0
- package/ios/Source/utils/PhoneAudioMonitor.swift +226 -0
- package/ios/Source/utils/TarBz2Extractor.swift +206 -0
- package/package.json +78 -0
- package/plugin/build/index.d.ts +6 -0
- package/plugin/build/index.js +12 -0
- package/plugin/build/withAndroid.d.ts +4 -0
- package/plugin/build/withAndroid.js +80 -0
- package/plugin/build/withIos.d.ts +4 -0
- package/plugin/build/withIos.js +64 -0
- package/src/BluetoothSdk.types.ts +581 -0
- package/src/BluetoothSdkModule.ts +259 -0
- package/src/index.ts +4 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
//
|
|
2
|
+
// JSCExperiment.swift
|
|
3
|
+
// MentraOS
|
|
4
|
+
//
|
|
5
|
+
// Test/benchmark infrastructure. Self-contained file, never invoked in
|
|
6
|
+
// normal user flow. Triggered only when (a) MENTRA_RUN_JSC_BENCH env
|
|
7
|
+
// var is set at app launch, or (b) the user taps a JSC button on the
|
|
8
|
+
// (Super-Mode-gated) stress-test screen.
|
|
9
|
+
//
|
|
10
|
+
// Spike: measure the actual memory cost of N concurrent JSContexts on iOS.
|
|
11
|
+
// We want to know whether "Pebble-style native JSC per miniapp" is viable
|
|
12
|
+
// for our N-concurrent-miniapp use case (Pebble runs only 1 at a time, so
|
|
13
|
+
// they have no data on this).
|
|
14
|
+
//
|
|
15
|
+
// Each spawned context:
|
|
16
|
+
// - Has its own JSVirtualMachine (full isolation, separate heap)
|
|
17
|
+
// - Has __dispatch as a single bridge function (no per-method bindings,
|
|
18
|
+
// to avoid the production crash Pebble documented in CrashReproducer.kt)
|
|
19
|
+
// - Runs a representative idle workload: setInterval ping every 5 s
|
|
20
|
+
// - Names itself "MentraJS: <id>" for Safari Web Inspector
|
|
21
|
+
// - Is NOT inspectable in release builds
|
|
22
|
+
//
|
|
23
|
+
// Measure resident memory before and after spawning N contexts.
|
|
24
|
+
|
|
25
|
+
import Foundation
|
|
26
|
+
import JavaScriptCore
|
|
27
|
+
import os.log
|
|
28
|
+
|
|
29
|
+
private let jscLog = OSLog(subsystem: "com.mentra.mentra", category: "JSCSpike")
|
|
30
|
+
|
|
31
|
+
/// Log to BOTH jlog (forwarded into JS) AND os_log AND a flat file in
|
|
32
|
+
/// the app's Documents directory. Release builds don't pipe RN's
|
|
33
|
+
/// console.log or Swift print() to syslog, and os_log filtering through
|
|
34
|
+
/// idevicesyslog is unreliable. The Documents file is the agentic-test
|
|
35
|
+
/// path: `xcrun devicectl device copy from --domain-type appDataContainer
|
|
36
|
+
/// --domain-identifier com.mentra.mentra --source Documents/jsc-spike.log`.
|
|
37
|
+
private let logFileURL: URL = {
|
|
38
|
+
let docs = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
|
|
39
|
+
return docs.appendingPathComponent("jsc-spike.log")
|
|
40
|
+
}()
|
|
41
|
+
|
|
42
|
+
private func jlog(_ message: String) {
|
|
43
|
+
Bridge.log(message)
|
|
44
|
+
os_log("%{public}@", log: jscLog, type: .info, message)
|
|
45
|
+
let stamped = "[\(ISO8601DateFormatter().string(from: Date()))] \(message)\n"
|
|
46
|
+
if let data = stamped.data(using: .utf8) {
|
|
47
|
+
if FileManager.default.fileExists(atPath: logFileURL.path) {
|
|
48
|
+
if let handle = try? FileHandle(forWritingTo: logFileURL) {
|
|
49
|
+
defer { try? handle.close() }
|
|
50
|
+
try? handle.seekToEnd()
|
|
51
|
+
try? handle.write(contentsOf: data)
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
try? data.write(to: logFileURL, options: .atomic)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@objc public class JSCExperiment: NSObject {
|
|
60
|
+
/// Called from BluetoothSdkModule init. If MENTRA_RUN_JSC_BENCH env var
|
|
61
|
+
/// is set, kick off the benchmark after a 5s settle window so the app
|
|
62
|
+
/// is fully booted (RN bridge up, Metro pull done if dev, etc).
|
|
63
|
+
@objc public static func maybeAutoBenchmark() {
|
|
64
|
+
guard ProcessInfo.processInfo.environment["MENTRA_RUN_JSC_BENCH"] != nil else { return }
|
|
65
|
+
os_log("🧪 MENTRA_RUN_JSC_BENCH set — auto-running benchmark in 5s",
|
|
66
|
+
log: jscLog, type: .info)
|
|
67
|
+
DispatchQueue.global(qos: .userInitiated).asyncAfter(deadline: .now() + 5.0) {
|
|
68
|
+
runBenchmark()
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/// id → (context, virtualMachine, timer for the workload)
|
|
73
|
+
private static var contexts: [String: (JSContext, JSVirtualMachine, Timer)] = [:]
|
|
74
|
+
private static let queue = DispatchQueue(label: "com.mentra.jsc-experiment")
|
|
75
|
+
|
|
76
|
+
/// Spawn N JSContexts at once. Each gets its own VM + a representative
|
|
77
|
+
/// idle workload. Returns the count successfully spawned.
|
|
78
|
+
@objc public static func spawn(count: Int) -> Int {
|
|
79
|
+
let beforeMB = MemoryMonitor.currentMemoryMB()
|
|
80
|
+
jlog("🧪 JSCExperiment.spawn(\(count)) starting; baseline \(String(format: "%.1f", beforeMB)) MB")
|
|
81
|
+
var spawned = 0
|
|
82
|
+
for i in 0..<count {
|
|
83
|
+
let id = "spike-\(UUID().uuidString.prefix(8))-\(i)"
|
|
84
|
+
if spawnOne(id: id) {
|
|
85
|
+
spawned += 1
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
let afterMB = MemoryMonitor.currentMemoryMB()
|
|
89
|
+
let perMB = spawned > 0 ? (afterMB - beforeMB) / Double(spawned) : 0
|
|
90
|
+
jlog("🧪 JSCExperiment: spawned \(spawned)/\(count) contexts; total alive \(contexts.count); mem \(String(format: "%.1f", beforeMB))→\(String(format: "%.1f", afterMB)) MB (\(String(format: "%+.2f", perMB)) MB/ctx)")
|
|
91
|
+
return spawned
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/// Spawn one named context. Returns true on success.
|
|
95
|
+
@objc public static func spawnOne(id: String) -> Bool {
|
|
96
|
+
return queue.sync {
|
|
97
|
+
// Each context gets its own VM = full heap isolation between miniapps.
|
|
98
|
+
// (JSContexts that share a VM share the heap; we want isolation.)
|
|
99
|
+
let vm = JSVirtualMachine()!
|
|
100
|
+
let ctx = JSContext(virtualMachine: vm)!
|
|
101
|
+
|
|
102
|
+
ctx.name = "MentraJS: \(id)"
|
|
103
|
+
// Inspectable in dev builds only. iOS 16.4+ guarded.
|
|
104
|
+
#if DEBUG
|
|
105
|
+
if #available(iOS 16.4, *) {
|
|
106
|
+
ctx.isInspectable = true
|
|
107
|
+
}
|
|
108
|
+
#endif
|
|
109
|
+
|
|
110
|
+
// Single-dispatcher bridge. Per Pebble's CrashReproducer doc, never
|
|
111
|
+
// bind individual native callbacks as JSValue properties — JSC's GC
|
|
112
|
+
// races with ARC and crashes. One C-callable function only.
|
|
113
|
+
let dispatch: @convention(block) (String, String, [Any]?) -> Any? = { iface, method, args in
|
|
114
|
+
// Stub: real impl routes to native services.
|
|
115
|
+
// For the spike we just need __dispatch to exist so the
|
|
116
|
+
// workload can call it without throwing.
|
|
117
|
+
return NSNull()
|
|
118
|
+
}
|
|
119
|
+
ctx.setObject(dispatch, forKeyedSubscript: "__dispatch" as NSString)
|
|
120
|
+
|
|
121
|
+
// Representative idle workload — what a typical miniapp's
|
|
122
|
+
// background JS does: hold a tiny bit of state, register a
|
|
123
|
+
// listener, periodically poke the bridge.
|
|
124
|
+
let workload = """
|
|
125
|
+
(function () {
|
|
126
|
+
var counter = 0;
|
|
127
|
+
var state = { id: \"\(id)\", startedAt: Date.now(), notes: [] };
|
|
128
|
+
var i = 0;
|
|
129
|
+
function tick() {
|
|
130
|
+
counter++;
|
|
131
|
+
state.notes.push({ at: Date.now(), n: counter });
|
|
132
|
+
if (state.notes.length > 100) state.notes.shift();
|
|
133
|
+
__dispatch('noop', 'tick', [counter]);
|
|
134
|
+
}
|
|
135
|
+
tick();
|
|
136
|
+
// Schedule a periodic tick. We use the host-provided
|
|
137
|
+
// setInterval (a JS shim that calls into native).
|
|
138
|
+
if (typeof setInterval === 'function') {
|
|
139
|
+
setInterval(tick, 5000);
|
|
140
|
+
}
|
|
141
|
+
})();
|
|
142
|
+
"""
|
|
143
|
+
ctx.evaluateScript(workload)
|
|
144
|
+
|
|
145
|
+
// Workload uses setInterval which is a JS shim that we'd build
|
|
146
|
+
// for real — for the spike we just install a Timer-based
|
|
147
|
+
// setInterval that fires the JS callback. The JS code already
|
|
148
|
+
// expects setInterval to exist as a global.
|
|
149
|
+
let setInterval: @convention(block) (JSValue, Double) -> Int = { fn, ms in
|
|
150
|
+
let id = Int.random(in: 1...Int.max)
|
|
151
|
+
// Stub: we won't actually fire in the spike, but the JS
|
|
152
|
+
// callback existing is what we want for memory measurement.
|
|
153
|
+
_ = fn // hold a ref to keep the JS callback alive
|
|
154
|
+
_ = ms
|
|
155
|
+
return id
|
|
156
|
+
}
|
|
157
|
+
ctx.setObject(setInterval, forKeyedSubscript: "setInterval" as NSString)
|
|
158
|
+
|
|
159
|
+
// Idle workload Timer on the native side that does NOTHING —
|
|
160
|
+
// it just exists so the context isn't optimized away. The real
|
|
161
|
+
// JS callback (registered above) will be hit by a real
|
|
162
|
+
// setInterval impl in production.
|
|
163
|
+
let timer = Timer.scheduledTimer(withTimeInterval: 5.0, repeats: true) { _ in
|
|
164
|
+
// no-op — keep ref alive
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
contexts[id] = (ctx, vm, timer)
|
|
168
|
+
return true
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/// Tear down all contexts. Important: cancel timers, drop refs, force GC.
|
|
173
|
+
@objc public static func killAll() {
|
|
174
|
+
queue.sync {
|
|
175
|
+
for (_, entry) in contexts {
|
|
176
|
+
entry.2.invalidate()
|
|
177
|
+
}
|
|
178
|
+
let count = contexts.count
|
|
179
|
+
contexts.removeAll()
|
|
180
|
+
jlog("📊 JSCExperiment: killed all \(count) contexts")
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/// How many contexts are alive right now.
|
|
185
|
+
@objc public static func aliveCount() -> Int {
|
|
186
|
+
return queue.sync { contexts.count }
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/// Convenience: spawn N + return memory delta vs baseline. Caller
|
|
190
|
+
/// records baseline via MemoryMonitor.currentMemoryMB() before calling.
|
|
191
|
+
@objc public static func spawnAndMeasure(count: Int, baselineMB: Double) -> [String: Any] {
|
|
192
|
+
let beforeMB = MemoryMonitor.currentMemoryMB()
|
|
193
|
+
let spawned = spawn(count: count)
|
|
194
|
+
// Tiny settle delay so allocations complete before we read.
|
|
195
|
+
Thread.sleep(forTimeInterval: 0.5)
|
|
196
|
+
let afterMB = MemoryMonitor.currentMemoryMB()
|
|
197
|
+
let perContextMB = spawned > 0 ? (afterMB - beforeMB) / Double(spawned) : 0
|
|
198
|
+
let result: [String: Any] = [
|
|
199
|
+
"spawned": spawned,
|
|
200
|
+
"totalAlive": aliveCount(),
|
|
201
|
+
"baselineMB": baselineMB,
|
|
202
|
+
"beforeMB": beforeMB,
|
|
203
|
+
"afterMB": afterMB,
|
|
204
|
+
"deltaMB": afterMB - beforeMB,
|
|
205
|
+
"perContextMB": perContextMB,
|
|
206
|
+
]
|
|
207
|
+
jlog("📊 JSCExperiment.spawnAndMeasure: \(result)")
|
|
208
|
+
return result
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/// Run a full benchmark sweep: spawn 1, 5, 10, 25, 50 in waves with
|
|
212
|
+
/// 2s settle between, log resident MB before and after each. All
|
|
213
|
+
/// output via jlog so it reaches syslog in release builds.
|
|
214
|
+
@objc public static func runBenchmark() {
|
|
215
|
+
DispatchQueue.global(qos: .userInitiated).async {
|
|
216
|
+
killAll()
|
|
217
|
+
// Clear log file for a fresh run.
|
|
218
|
+
try? FileManager.default.removeItem(at: logFileURL)
|
|
219
|
+
Thread.sleep(forTimeInterval: 1.0)
|
|
220
|
+
let baseline = MemoryMonitor.currentMemoryMB()
|
|
221
|
+
jlog("🧪 JSC-BENCH start; baseline \(String(format: "%.1f", baseline)) MB; logFile=\(logFileURL.path)")
|
|
222
|
+
let waves = [1, 5, 10, 25, 50]
|
|
223
|
+
var prevAlive = 0
|
|
224
|
+
for target in waves {
|
|
225
|
+
let need = target - prevAlive
|
|
226
|
+
if need > 0 {
|
|
227
|
+
let beforeMB = MemoryMonitor.currentMemoryMB()
|
|
228
|
+
_ = spawn(count: need)
|
|
229
|
+
Thread.sleep(forTimeInterval: 1.0)
|
|
230
|
+
let afterMB = MemoryMonitor.currentMemoryMB()
|
|
231
|
+
let perContextOverall = (afterMB - baseline) / Double(target)
|
|
232
|
+
jlog("🧪 JSC-BENCH wave: target=\(target) alive=\(aliveCount()) before=\(String(format: "%.1f", beforeMB))MB after=\(String(format: "%.1f", afterMB))MB delta=\(String(format: "%+.1f", afterMB - beforeMB))MB cumPerCtx=\(String(format: "%.2f", perContextOverall))MB")
|
|
233
|
+
prevAlive = target
|
|
234
|
+
}
|
|
235
|
+
Thread.sleep(forTimeInterval: 2.0)
|
|
236
|
+
}
|
|
237
|
+
let final = MemoryMonitor.currentMemoryMB()
|
|
238
|
+
jlog("🧪 JSC-BENCH done; alive=\(aliveCount()) final=\(String(format: "%.1f", final))MB total-delta=\(String(format: "%+.1f", final - baseline))MB")
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
//
|
|
2
|
+
// MemoryMonitor.swift
|
|
3
|
+
// MentraOS
|
|
4
|
+
//
|
|
5
|
+
// Memory usage monitoring for leak detection
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import Foundation
|
|
9
|
+
|
|
10
|
+
class MemoryMonitor {
|
|
11
|
+
private static var timer: Timer?
|
|
12
|
+
private static var startMemoryMB: Double = 0
|
|
13
|
+
|
|
14
|
+
static func start(intervalSeconds: TimeInterval = 30) {
|
|
15
|
+
stop()
|
|
16
|
+
startMemoryMB = currentMemoryMB()
|
|
17
|
+
Bridge.log("📊 Memory Monitor started - baseline: \(String(format: "%.1f", startMemoryMB)) MB")
|
|
18
|
+
|
|
19
|
+
DispatchQueue.main.async {
|
|
20
|
+
timer = Timer.scheduledTimer(withTimeInterval: intervalSeconds, repeats: true) { _ in
|
|
21
|
+
let current = currentMemoryMB()
|
|
22
|
+
let delta = current - startMemoryMB
|
|
23
|
+
let trend = delta > 50 ? "🔴" : delta > 20 ? "🟡" : "⚪"
|
|
24
|
+
Bridge.log("📊 Memory: \(String(format: "%.1f", current)) MB (Δ \(String(format: "%+.1f", delta)) MB) \(trend)")
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
static func stop() {
|
|
30
|
+
timer?.invalidate()
|
|
31
|
+
timer = nil
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
static func currentMemoryMB() -> Double {
|
|
35
|
+
var info = mach_task_basic_info()
|
|
36
|
+
var count = mach_msg_type_number_t(MemoryLayout<mach_task_basic_info>.size) / 4
|
|
37
|
+
let result = withUnsafeMutablePointer(to: &info) {
|
|
38
|
+
$0.withMemoryRebound(to: integer_t.self, capacity: 1) {
|
|
39
|
+
task_info(mach_task_self_, task_flavor_t(MACH_TASK_BASIC_INFO), $0, &count)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return result == KERN_SUCCESS ? Double(info.resident_size) / 1024 / 1024 : 0
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Handles chunking of large messages that exceed BLE transmission limits.
|
|
5
|
+
* Messages are split at the JSON layer to work within MCU protocol constraints.
|
|
6
|
+
*
|
|
7
|
+
* Uses compact keys to minimize overhead per chunk:
|
|
8
|
+
* t = "ck" (chunk type identifier)
|
|
9
|
+
* id = chunk session ID
|
|
10
|
+
* c = chunk index (0-based)
|
|
11
|
+
* n = total number of chunks
|
|
12
|
+
* d = chunk data payload
|
|
13
|
+
*
|
|
14
|
+
* Each chunk after C-wrapping + K900 framing must fit within the BES2700's
|
|
15
|
+
* 253-byte BLE write limit. With compact keys, 80 bytes of raw data produces
|
|
16
|
+
* a final packed size of ~245 bytes worst-case (with heavy JSON escaping).
|
|
17
|
+
*/
|
|
18
|
+
class MessageChunker {
|
|
19
|
+
// Threshold: if C-wrapped message exceeds this, chunking is triggered.
|
|
20
|
+
// BES2700 limit is 253 bytes; anything over ~200 bytes packed needs chunking.
|
|
21
|
+
private static let MESSAGE_SIZE_THRESHOLD = 200
|
|
22
|
+
|
|
23
|
+
/// Maximum raw bytes per chunk. After double JSON escaping + compact envelope
|
|
24
|
+
/// + C-wrapper + K900 framing, 80 bytes stays under the 253-byte BLE limit.
|
|
25
|
+
private static let CHUNK_DATA_SIZE = 80
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Check if a message needs to be chunked
|
|
29
|
+
* @param message The complete message string (already C-wrapped)
|
|
30
|
+
* @return true if message exceeds threshold and needs chunking
|
|
31
|
+
*/
|
|
32
|
+
static func needsChunking(_ message: String?) -> Bool {
|
|
33
|
+
guard let message = message else {
|
|
34
|
+
return false
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let messageBytes = message.data(using: .utf8)?.count ?? 0
|
|
38
|
+
let needsChunking = messageBytes > MESSAGE_SIZE_THRESHOLD
|
|
39
|
+
|
|
40
|
+
if needsChunking {
|
|
41
|
+
print("MessageChunker: Message size \(messageBytes) exceeds threshold \(MESSAGE_SIZE_THRESHOLD), will chunk")
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return needsChunking
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Create chunks from a message that's too large for single transmission.
|
|
49
|
+
* Uses compact keys to minimize per-chunk overhead.
|
|
50
|
+
* @param originalJson The original JSON string to be sent (before C-wrapping)
|
|
51
|
+
* @param messageId The message ID for ACK tracking (if applicable)
|
|
52
|
+
* @return Array of chunk dictionaries ready to be C-wrapped and sent
|
|
53
|
+
*/
|
|
54
|
+
static func createChunks(originalJson: String, messageId: Int64 = -1) -> [[String: Any]] {
|
|
55
|
+
guard let messageData = originalJson.data(using: .utf8) else {
|
|
56
|
+
print("MessageChunker: Failed to convert message to data")
|
|
57
|
+
return []
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
var chunks: [[String: Any]] = []
|
|
61
|
+
let totalBytes = messageData.count
|
|
62
|
+
|
|
63
|
+
// Compact chunk session ID: messageId_timestamp (no "chunk_" prefix)
|
|
64
|
+
let chunkId = "\(messageId)_\(Int(Date().timeIntervalSince1970 * 1000))"
|
|
65
|
+
|
|
66
|
+
// Calculate total chunks needed
|
|
67
|
+
let totalChunks = Int(ceil(Double(totalBytes) / Double(CHUNK_DATA_SIZE)))
|
|
68
|
+
|
|
69
|
+
print("MessageChunker: Creating \(totalChunks) chunks for message of size \(totalBytes) bytes")
|
|
70
|
+
|
|
71
|
+
for i in 0 ..< totalChunks {
|
|
72
|
+
let startIndex = i * CHUNK_DATA_SIZE
|
|
73
|
+
let endIndex = min(startIndex + CHUNK_DATA_SIZE, totalBytes)
|
|
74
|
+
let chunkRange = startIndex ..< endIndex
|
|
75
|
+
|
|
76
|
+
// Extract chunk data as string
|
|
77
|
+
let chunkData = messageData.subdata(in: chunkRange)
|
|
78
|
+
guard let chunkString = String(data: chunkData, encoding: .utf8) else {
|
|
79
|
+
print("MessageChunker: Failed to convert chunk \(i) to string")
|
|
80
|
+
continue
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Create chunk dictionary with compact keys
|
|
84
|
+
var chunk: [String: Any] = [
|
|
85
|
+
"t": "ck",
|
|
86
|
+
"id": chunkId,
|
|
87
|
+
"c": i,
|
|
88
|
+
"n": totalChunks,
|
|
89
|
+
"d": chunkString,
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
// Add message ID to final chunk only for ACK tracking
|
|
93
|
+
if i == totalChunks - 1, messageId != -1 {
|
|
94
|
+
chunk["mId"] = messageId
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
chunks.append(chunk)
|
|
98
|
+
|
|
99
|
+
print("MessageChunker: Created chunk \(i)/\(totalChunks - 1) with \(chunkData.count) bytes")
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return chunks
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Check if a received message is a chunked message.
|
|
107
|
+
* Supports both verbose ("type":"chunked_msg") and compact ("t":"ck") formats.
|
|
108
|
+
* @param json The received dictionary (after C-unwrapping)
|
|
109
|
+
* @return true if this is a chunked message
|
|
110
|
+
*/
|
|
111
|
+
static func isChunkedMessage(_ json: [String: Any]?) -> Bool {
|
|
112
|
+
guard let json = json else {
|
|
113
|
+
return false
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
let type = json["type"] as? String ?? json["t"] as? String ?? ""
|
|
117
|
+
return type == "chunked_msg" || type == "ck"
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Extract chunk information from a chunked message.
|
|
122
|
+
* Supports both verbose and compact key formats.
|
|
123
|
+
*/
|
|
124
|
+
static func getChunkInfo(_ json: [String: Any]) -> ChunkInfo? {
|
|
125
|
+
guard isChunkedMessage(json) else {
|
|
126
|
+
return nil
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Support both verbose and compact keys
|
|
130
|
+
guard let chunkId = (json["chunkId"] as? String) ?? (json["id"] as? String),
|
|
131
|
+
let chunkIndex = (json["chunk"] as? Int) ?? (json["c"] as? Int),
|
|
132
|
+
let totalChunks = (json["total"] as? Int) ?? (json["n"] as? Int),
|
|
133
|
+
let data = (json["data"] as? String) ?? (json["d"] as? String)
|
|
134
|
+
else {
|
|
135
|
+
print("MessageChunker: Failed to extract chunk info from JSON")
|
|
136
|
+
return nil
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
let messageId = json["mId"] as? Int64 ?? -1
|
|
140
|
+
|
|
141
|
+
return ChunkInfo(
|
|
142
|
+
chunkId: chunkId,
|
|
143
|
+
chunkIndex: chunkIndex,
|
|
144
|
+
totalChunks: totalChunks,
|
|
145
|
+
data: data,
|
|
146
|
+
messageId: messageId
|
|
147
|
+
)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Container for chunk information
|
|
152
|
+
*/
|
|
153
|
+
struct ChunkInfo {
|
|
154
|
+
let chunkId: String
|
|
155
|
+
let chunkIndex: Int
|
|
156
|
+
let totalChunks: Int
|
|
157
|
+
let data: String
|
|
158
|
+
let messageId: Int64
|
|
159
|
+
|
|
160
|
+
var isFinalChunk: Bool {
|
|
161
|
+
return chunkIndex == totalChunks - 1
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Models.swift
|
|
3
|
+
// MentraOS_Manager
|
|
4
|
+
//
|
|
5
|
+
// Created by Matthew Fosse on 3/3/25.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import Foundation
|
|
9
|
+
|
|
10
|
+
struct AiResponseToG1Model {
|
|
11
|
+
var lines: [String]
|
|
12
|
+
var totalPages: UInt8
|
|
13
|
+
var newScreen: Bool
|
|
14
|
+
var currentPage: UInt8 {
|
|
15
|
+
didSet {
|
|
16
|
+
print("SET : currentPage :\(currentPage)")
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
var maxPages: UInt8
|
|
21
|
+
var status: DisplayStatus
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
struct ThirdPartyCloudApp {
|
|
25
|
+
let packageName: String
|
|
26
|
+
let name: String
|
|
27
|
+
let description: String
|
|
28
|
+
let webhookURL: String
|
|
29
|
+
let logoURL: String
|
|
30
|
+
let isRunning: Bool
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/// NCSNotification structure
|
|
34
|
+
struct NCSNotification: Codable {
|
|
35
|
+
let msgId: Int
|
|
36
|
+
let type: Int
|
|
37
|
+
let appIdentifier: String
|
|
38
|
+
let title: String
|
|
39
|
+
let subtitle: String
|
|
40
|
+
let message: String
|
|
41
|
+
let timeS: Int
|
|
42
|
+
let date: String
|
|
43
|
+
let displayName: String
|
|
44
|
+
|
|
45
|
+
enum CodingKeys: String, CodingKey {
|
|
46
|
+
case msgId = "msg_id"
|
|
47
|
+
case type
|
|
48
|
+
case appIdentifier = "app_identifier"
|
|
49
|
+
case title
|
|
50
|
+
case subtitle
|
|
51
|
+
case message
|
|
52
|
+
case timeS = "time_s"
|
|
53
|
+
case date
|
|
54
|
+
case displayName = "display_name"
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
init(msgId: Int, appIdentifier: String, title: String, subtitle: String, message: String, displayName: String, type: Int = 1) {
|
|
58
|
+
self.msgId = msgId
|
|
59
|
+
self.type = type
|
|
60
|
+
self.appIdentifier = appIdentifier
|
|
61
|
+
self.title = title
|
|
62
|
+
self.subtitle = subtitle
|
|
63
|
+
self.message = message
|
|
64
|
+
timeS = Int(Date().timeIntervalSince1970)
|
|
65
|
+
|
|
66
|
+
let dateFormatter = DateFormatter()
|
|
67
|
+
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
|
|
68
|
+
date = dateFormatter.string(from: Date())
|
|
69
|
+
|
|
70
|
+
self.displayName = displayName
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/// Notification structure
|
|
75
|
+
struct G1Notification: Codable {
|
|
76
|
+
let ncsNotification: NCSNotification
|
|
77
|
+
let type: String = "Add"
|
|
78
|
+
|
|
79
|
+
enum CodingKeys: String, CodingKey {
|
|
80
|
+
case ncsNotification = "ncs_notification"
|
|
81
|
+
case type
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/// Convert to JSON dictionary
|
|
85
|
+
func toJson() -> [String: Any] {
|
|
86
|
+
guard let data = try? JSONEncoder().encode(self) else { return [:] }
|
|
87
|
+
guard let jsonObject = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
|
|
88
|
+
return [:]
|
|
89
|
+
}
|
|
90
|
+
return jsonObject
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/// Convert to JSON Data
|
|
94
|
+
func toData() -> Data? {
|
|
95
|
+
guard let jsonString = try? JSONEncoder().encode(self) else { return nil }
|
|
96
|
+
return jsonString
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/// Build notification chunks
|
|
100
|
+
func constructNotification() async -> [[UInt8]] {
|
|
101
|
+
guard let jsonData = toData() else { return [] }
|
|
102
|
+
|
|
103
|
+
let maxChunkSize = 176 // 180 - 4 bytes for header
|
|
104
|
+
var chunks: [Data] = []
|
|
105
|
+
|
|
106
|
+
// Split data into chunks
|
|
107
|
+
var offset = 0
|
|
108
|
+
while offset < jsonData.count {
|
|
109
|
+
let endIndex = min(offset + maxChunkSize, jsonData.count)
|
|
110
|
+
let chunkData = jsonData.subdata(in: offset ..< endIndex)
|
|
111
|
+
chunks.append(chunkData)
|
|
112
|
+
offset = endIndex
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
let totalChunks = UInt8(chunks.count)
|
|
116
|
+
var encodedChunks: [[UInt8]] = []
|
|
117
|
+
|
|
118
|
+
// Create packets with headers
|
|
119
|
+
for (index, chunk) in chunks.enumerated() {
|
|
120
|
+
let notifyId: UInt8 = 0 // Set appropriate notification ID
|
|
121
|
+
let header: [UInt8] = [0x4B, notifyId, totalChunks, UInt8(index)]
|
|
122
|
+
|
|
123
|
+
// Convert chunk data to array of bytes
|
|
124
|
+
var chunkBytes = [UInt8](chunk)
|
|
125
|
+
|
|
126
|
+
// Combine header and chunk
|
|
127
|
+
var encodedChunk = header
|
|
128
|
+
encodedChunk.append(contentsOf: chunkBytes)
|
|
129
|
+
|
|
130
|
+
encodedChunks.append(encodedChunk)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return encodedChunks
|
|
134
|
+
}
|
|
135
|
+
}
|