diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 975fd78..870f1a5 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -2,21 +2,27 @@ PODS: - dive_computer (0.0.1): - Flutter - Flutter (1.0.0) + - flutter_blue_plus (0.0.1): + - Flutter DEPENDENCIES: - dive_computer (from `.symlinks/plugins/dive_computer/ios`) - Flutter (from `Flutter`) + - flutter_blue_plus (from `.symlinks/plugins/flutter_blue_plus/ios`) EXTERNAL SOURCES: dive_computer: :path: ".symlinks/plugins/dive_computer/ios" Flutter: :path: Flutter + flutter_blue_plus: + :path: ".symlinks/plugins/flutter_blue_plus/ios" SPEC CHECKSUMS: - dive_computer: b7f216c7462ea4d565c0cd0d23395df3309bbe56 + dive_computer: b5812e10488f2173633747360efad9e8ed9f2dee Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 + flutter_blue_plus: 4837da7d00cf5d441fdd6635b3a57f936778ea96 PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796 -COCOAPODS: 1.13.0 +COCOAPODS: 1.15.2 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 8cf5511..e48e786 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -198,7 +198,7 @@ 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - 4784EFBF2FF99F6D74E8C8ED /* [CP] Embed Pods Frameworks */, + 7DE1D0F21F9C4C7940F5E24A /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -216,7 +216,7 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = YES; - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 331C8080294A63A400263BE5 = { @@ -308,7 +308,7 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; - 4784EFBF2FF99F6D74E8C8ED /* [CP] Embed Pods Frameworks */ = { + 7DE1D0F21F9C4C7940F5E24A /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 87131a0..8e3ca5d 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ UIApplicationSupportsIndirectInputEvents + NSBluetoothAlwaysUsageDescription + This app always needs Bluetooth to function + NSBluetoothPeripheralUsageDescription + This app needs Bluetooth Peripheral to function + NSLocationAlwaysAndWhenInUseUsageDescription + This app always needs location and when in use to function + NSLocationAlwaysUsageDescription + This app always needs location to function + NSLocationWhenInUseUsageDescription + This app needs location when in use to function diff --git a/example/lib/bluetooth_provider.dart b/example/lib/bluetooth_provider.dart new file mode 100644 index 0000000..e43e1e4 --- /dev/null +++ b/example/lib/bluetooth_provider.dart @@ -0,0 +1,108 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter_blue_plus/flutter_blue_plus.dart'; + +class BluetoothProvider with ChangeNotifier { + BluetoothAdapterState _adapterState = BluetoothAdapterState.unknown; + late StreamSubscription _adapterStateStateSubscription; + late StreamSubscription> _scanResultsSubscription; + late StreamSubscription _isScanningSubscription; + List _systemDevices = []; + List _scanResults = []; + bool _isScanning = false; + + BluetoothProvider() { + FlutterBluePlus.setLogLevel(LogLevel.none, color: false); + _initBLE(); + } + + @override + void dispose() { + _scanResultsSubscription.cancel(); + _isScanningSubscription.cancel(); + _adapterStateStateSubscription.cancel(); + super.dispose(); + } + + void _initBLE() async { + if (await FlutterBluePlus.isSupported == false) { + print("Bluetooth not supported by this device"); + return; + } + + _adapterStateStateSubscription = + FlutterBluePlus.adapterState.listen((state) { + _adapterState = state; + + if (state == BluetoothAdapterState.on) { + FlutterBluePlus.startScan(timeout: const Duration(seconds: 15)); + + try { + FlutterBluePlus.systemDevices.then((systemDevices) { + if (systemDevices.isNotEmpty) { + _systemDevices = systemDevices; + notifyListeners(); + } + }); + } catch (e, s) { + print('Error while getting system devices: $e, $s'); + } + } else { + print('Bluetooth adapter is ${state.toString().split('.')[1]}'); + } + }, onError: (e, s) { + print('Error while listening to adapter state stream: $e, $s'); + }); + + if (Platform.isAndroid) { + await FlutterBluePlus.turnOn(); + } + + _scanResultsSubscription = FlutterBluePlus.scanResults.listen((results) { + if (results.isNotEmpty) { + _scanResults = results; + notifyListeners(); + } + }, onError: (e, s) { + print('Error while listening to scan results stream: $e, $s'); + }); + + _isScanningSubscription = FlutterBluePlus.isScanning.listen((state) { + _isScanning = state; + notifyListeners(); + }, onError: (e, s) { + print('Error while listening to isScanning stream: $e, $s'); + }); + } + + Future scanForDevices() async { + if (_adapterState == BluetoothAdapterState.on) { + print('Scanning for devices...'); + try { + FlutterBluePlus.systemDevices.then((systemDevices) { + if (systemDevices.isNotEmpty) { + _systemDevices = systemDevices; + notifyListeners(); + } + }); + } catch (e, s) { + print('Error while getting system devices: $e, $s'); + } + + if (_isScanning == false) { + FlutterBluePlus.startScan(timeout: const Duration(seconds: 15)); + } + } + + return Future.delayed(const Duration(milliseconds: 500)); + } + + List get systemDevices => + _systemDevices.where((device) => device.platformName.isNotEmpty).toList(); + + List get scanResults => _scanResults + .where((device) => device.device.platformName.isNotEmpty) + .toList(); +} diff --git a/example/lib/main.dart b/example/lib/main.dart index 3b3fdc4..f96733b 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,8 +1,28 @@ +import 'dart:io'; + +import 'package:dive_computer/framework/interfaces/ble_interface.dart'; import 'package:flutter/material.dart'; import 'package:dive_computer/dive_computer.dart'; +import 'package:flutter_blue_plus/flutter_blue_plus.dart'; +//import 'package:provider/provider.dart'; + +//import './bluetooth_provider.dart'; + +void main() async { + WidgetsFlutterBinding.ensureInitialized(); -void main() { - runApp(const MyApp()); + if (await FlutterBluePlus.isSupported == false) { + log.fine('Bluetooth not supported by this device'); + return; + } + + runApp( + //ChangeNotifierProvider( + // create: (context) => BluetoothProvider(), + // child: const MyApp(), + //), + const MyApp(), + ); } class MyApp extends StatefulWidget { @@ -15,16 +35,19 @@ class MyApp extends StatefulWidget { class _MyAppState extends State { final dc = DiveComputer.instance; + late final Future> _bleDevices; late final Future> supportedComputers; @override void initState() { - super.initState(); + _bleDevices = dc.fetchBleDevices(); dc.enableDebugLogging(); dc.openConnection(); supportedComputers = dc.supportedComputers; + + super.initState(); } @override @@ -33,6 +56,14 @@ class _MyAppState extends State { super.dispose(); } + _example() { + if (Platform.isLinux || Platform.isMacOS || Platform.isWindows) { + return _desktop(); + } else { + return _mobile(); + } + } + @override Widget build(BuildContext context) { return MaterialApp( @@ -40,53 +71,253 @@ class _MyAppState extends State { appBar: AppBar( title: const Text('libdivecomputer ffi example'), ), - body: Container( - padding: const EdgeInsets.all(10), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text('Supported dive computers:', - style: TextStyle( - fontWeight: FontWeight.bold, - )), - const SizedBox(height: 10), - Expanded( - child: FutureBuilder( - future: supportedComputers, - builder: (context, snapshot) { - if (snapshot.hasError) { - return Text('Error: ${snapshot.error}'); - } - - if (snapshot.hasData) { - final computers = snapshot.data as List; - return ListView.builder( - itemCount: computers.length, - itemBuilder: (context, index) { - final computer = computers[index]; - return GestureDetector( - onTap: () async { - final dives = await dc.download( - computer, - computer.transports.first, - "exampleFingerprint", - ); - // ignore: use_build_context_synchronously - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: - Text('Downloaded ${dives.length} dives'), - ), - ); - }, - child: Text(computer.toString()), + body: _example(), + ), + ); + } + + _desktop() { + return Container( + padding: const EdgeInsets.all(10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text('Supported dive computers:', + style: TextStyle( + fontWeight: FontWeight.bold, + )), + const SizedBox(height: 10), + Expanded( + child: FutureBuilder( + future: supportedComputers, + builder: (context, snapshot) { + if (snapshot.hasError) { + return Text('Error: ${snapshot.error}'); + } + + if (snapshot.hasData) { + final computers = snapshot.data as List; + return ListView.builder( + itemCount: computers.length, + itemBuilder: (context, index) { + final computer = computers[index]; + return GestureDetector( + onTap: () async { + final dives = await dc.download( + computer, + computer.transports.first, + "exampleFingerprint", + ); + // ignore: use_build_context_synchronously + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Downloaded ${dives.length} dives'), + ), ); }, + child: Text(computer.toString()), ); - } + }, + ); + } + + return const Text('Loading...'); + }, + ), + ), + ], + ), + ); + } - return const Text('Loading...'); + _mobile() { + return FutureBuilder>( + future: _bleDevices, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center( + child: CircularProgressIndicator(), + ); + } else { + if (snapshot.error != null) { + return const Center( + child: Text( + 'Something went wrong!', + ), + ); + } else { + final bleDevices = snapshot.data![0] as List; + return ListView.builder( + itemCount: bleDevices.length, + itemBuilder: (context, index) { + final device = bleDevices[index]; + return ListTile( + title: Text(device.advertisementName), + onTap: () async { + // final dives = await dc.download( + // snapshot.data![1][0] as Computer, + // ComputerTransport.ble, + // device, + // "exampleFingerprint", + // ); + // ignore: use_build_context_synchronously + // ScaffoldMessenger.of(context).showSnackBar( + // SnackBar( + // content: Text('Downloaded ${dives.length} dives'), + // ), + // ); }, + ); + }, + ); + + // return Consumer( + // builder: (context, bluetoothProvider, child) { + // return RefreshIndicator( + // onRefresh: bluetoothProvider.scanForDevices, + // child: ListView( + // children: [ + // ...bluetoothProvider.systemDevices.map( + // (device) => DiveComputerTile( + // diveComputerName: device.platformName, + // isNewDC: false, + // onPressed: () async { + // // First, check if connection to device has been established + // if (!FlutterBluePlus.connectedDevices + // .contains(device)) { + // device.connectionState + // .listen((BluetoothConnectionState state) { + // print('Connection state: $state'); + // }); + + // await device.connect(); + // } + + // // Check if device is supported by dive_computer and device supports ble + // final computers = + // snapshot.data![1] as List; + // Computer? computer; + // for (var c in computers) { + // if (device.platformName.contains(c.product)) { + // computer = c; + // break; + // } + // } + + // if (computer == null) { + // print('${device.platformName} not supported'); + // return; + // } + + // ComputerTransport? transport; + // if (computer.transports + // .contains(ComputerTransport.ble)) { + // // flutter_blue_plus supports only BLE, not Bluetooth + // transport = ComputerTransport.ble; + // } + + // if (transport == null) { + // print( + // 'No BLE or Bluetooth support for ${device.platformName}'); + // return; + // } + + // // subsurface BLE/Bluetooth import process + // // run, downloadfromdcthread.cpp line 87 + // // 1. get dc_descriptor_t for device <- downloadfromdcthread.cpp line 90 <- LIBDIVECOMPUTER + // // 2. do_libdivecomputer_import <- downloadfromdcthread.cpp line 117 + // // a. dc_context_new, libdivecompute.c line 1512 <- LIBDIVECOMPUTER + // // b. divecomputer_device_open, libdivecompute.c line 1525 + // // BT: (most likely not supported with iOS and definitly not supported by flutter_blue_plus) + // // I. rfcomm_stream_open, libdivecompute.c line 1420 + // // BLE: + // // I. ble_packet_open, libdivecompute.c line 1432 + // // a. qt_ble_open, qtserialbluetooth.cpp line 303 + // // 1. connectToDevice, qt-ble.cpp line 585 + // // 2. discoverServices, qt-ble.cpp line 630 + // // 3. select_preferred_service, qt-ble.cpp line 638 <- The service which is providing read and write access + // // 4. get ClientCharacteristicConfiguration descriptor and write 0x0100 to enable notifications, qt-ble.cpp line 658-677 + // // 5. assign BLEObject to iostream, qt-ble.cpp line 689 + // // b. dc_custom_open, qtserialbluetooth.cpp line 308 <- LIBDIVECOMPUTER + // // 1. dc_iostream_allocate, custom.c line 84 <- LIBDIVECOMPUTER + + // // From here the process is the same as for other transports + // // c. dc_device_open, libdivecompute.c line 1531 <- LIBDIVECOMPUTER + // // d. do_device_import, libdivecomputer.c line 1541 + // // I. dc_device_set_events, libdivecomputer.c line 1178 <- LIBDIVECOMPUTER + // // II. dc_device_set_cancel, libdivecomputer.c line 1183 <- LIBDIVECOMPUTER + // // III. in case no dump dc_device_foreach, libdivecomputer.c line 1210 <- LIBDIVECOMPUTER + + // // Download dives from device + // final dives = await dc.download( + // computer, + // transport, + // device, + // "exampleFingerprint", + // ); + + // // ignore: use_build_context_synchronously + // ScaffoldMessenger.of(context).showSnackBar( + // SnackBar( + // content: + // Text('Downloaded ${dives.length} dives'), + // ), + // ); + // }, + // ), + // ), + // ...bluetoothProvider.scanResults.map( + // (result) => DiveComputerTile( + // diveComputerName: result.device.platformName, + // isNewDC: true, + // onPressed: () => result.device.connect(), + // ), + // ), + // ], + // ), + // ); + // }, + // ); + } + } + }, + ); + } +} + +class DiveComputerTile extends StatelessWidget { + final String diveComputerName; + final bool isNewDC; + final Function onPressed; + + const DiveComputerTile({ + super.key, + required this.diveComputerName, + required this.isNewDC, + required this.onPressed, + }); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(2.0), + child: Card( + child: Padding( + padding: const EdgeInsets.only(left: 10.0), + child: Row( + children: [ + Expanded( + child: SizedBox( + child: Text(diveComputerName), + ), + ), + TextButton( + onPressed: () => onPressed(), + child: Text( + isNewDC ? 'Connect' : 'Import', + style: TextStyle( + color: isNewDC ? Colors.red : Colors.green, + ), ), ), ], diff --git a/example/macos/Flutter/GeneratedPluginRegistrant.swift b/example/macos/Flutter/GeneratedPluginRegistrant.swift index cccf817..e56ee82 100644 --- a/example/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/example/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,8 @@ import FlutterMacOS import Foundation +import flutter_blue_plus func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + FlutterBluePlusPlugin.register(with: registry.registrar(forPlugin: "FlutterBluePlusPlugin")) } diff --git a/example/pubspec.lock b/example/pubspec.lock index 4207914..a7456ea 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -60,15 +60,23 @@ packages: dependency: transitive description: name: ffi - sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" + sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.2" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" + flutter_blue_plus: + dependency: "direct main" + description: + name: flutter_blue_plus + sha256: a4e2e7d536f24dfc265a4eb43b63a1293bf6d972ddd243dc3ed2d900b38d9a4d + url: "https://pub.dev" + source: hosted + version: "1.31.16" flutter_lints: dependency: "direct dev" description: @@ -146,6 +154,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.11.0" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" path: dependency: transitive description: @@ -162,6 +178,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.8" + provider: + dependency: "direct main" + description: + name: provider + sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c + url: "https://pub.dev" + source: hosted + version: "6.1.2" sky_engine: dependency: transitive description: flutter @@ -232,5 +256,5 @@ packages: source: hosted version: "13.0.0" sdks: - dart: ">=3.2.3 <4.0.0" + dart: ">=3.3.0-279.1.beta <4.0.0" flutter: ">=3.3.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 9d9e061..fe45392 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -19,6 +19,8 @@ dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: ../ + provider: ^6.1.1 + flutter_blue_plus: ^1.31.16 dev_dependencies: flutter_test: diff --git a/ffigen.yaml b/ffigen.yaml index 13ef9be..30cd596 100644 --- a/ffigen.yaml +++ b/ffigen.yaml @@ -13,6 +13,10 @@ headers: - 'src/libdivecomputer/include/libdivecomputer/parser.h' - 'src/libdivecomputer/include/libdivecomputer/usb.h' - 'src/libdivecomputer/include/libdivecomputer/usbhid.h' + - 'src/libdivecomputer/include/libdivecomputer/ble.h' + - 'src/libdivecomputer/include/libdivecomputer/bluetooth.h' + - 'src/libdivecomputer/include/libdivecomputer/custom.h' + - 'src/libdivecomputer/src/iostream-private.h' preamble: | // ignore_for_file: always_specify_types, unused_field, unused_element // ignore_for_file: camel_case_types diff --git a/ios/Classes/array.c b/ios/Classes/array.c new file mode 100644 index 0000000..11aac65 --- /dev/null +++ b/ios/Classes/array.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/array.c" \ No newline at end of file diff --git a/ios/Classes/atomics_cobalt.c b/ios/Classes/atomics_cobalt.c new file mode 100644 index 0000000..ac50521 --- /dev/null +++ b/ios/Classes/atomics_cobalt.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/atomics_cobalt.c" \ No newline at end of file diff --git a/ios/Classes/atomics_cobalt_parser.c b/ios/Classes/atomics_cobalt_parser.c new file mode 100644 index 0000000..5dbd71f --- /dev/null +++ b/ios/Classes/atomics_cobalt_parser.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/atomics_cobalt_parser.c" \ No newline at end of file diff --git a/ios/Classes/citizen_aqualand.c b/ios/Classes/citizen_aqualand.c new file mode 100644 index 0000000..d762fc3 --- /dev/null +++ b/ios/Classes/citizen_aqualand.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/citizen_aqualand.c" \ No newline at end of file diff --git a/ios/Classes/citizen_aqualand_parser.c b/ios/Classes/citizen_aqualand_parser.c new file mode 100644 index 0000000..4dae348 --- /dev/null +++ b/ios/Classes/citizen_aqualand_parser.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/citizen_aqualand_parser.c" \ No newline at end of file diff --git a/ios/Classes/cochran_commander.c b/ios/Classes/cochran_commander.c new file mode 100644 index 0000000..15e3903 --- /dev/null +++ b/ios/Classes/cochran_commander.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/cochran_commander.c" \ No newline at end of file diff --git a/ios/Classes/cochran_commander_parser.c b/ios/Classes/cochran_commander_parser.c new file mode 100644 index 0000000..7ee3478 --- /dev/null +++ b/ios/Classes/cochran_commander_parser.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/cochran_commander_parser.c" \ No newline at end of file diff --git a/ios/Classes/config.h b/ios/Classes/config.h new file mode 100644 index 0000000..c26960d --- /dev/null +++ b/ios/Classes/config.h @@ -0,0 +1,158 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +#define HAVE_CONFIG_H 1 + +/* Enable logging. */ +#define ENABLE_LOGGING 1 + +/* Enable pseudo terminal support. */ +/* #undef ENABLE_PTY */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_AF_IRDA_H */ + +/* BlueZ library */ +/* #undef HAVE_BLUEZ */ + +/* Define to 1 if you have the 'clock_gettime' function. */ +#define HAVE_CLOCK_GETTIME 1 + +/* Define to 1 if you have the declaration of 'optreset', and to 0 if you + don't. */ +#define HAVE_DECL_OPTRESET 1 + +/* Define to 1 if you have the declaration of 'strerror_r', and to 0 if you + don't. */ +#define HAVE_DECL_STRERROR_R 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_GETOPT_H 1 + +/* Define to 1 if you have the 'getopt_long' function. */ +#define HAVE_GETOPT_LONG 1 + +/* Define to 1 if you have the 'gmtime_r' function. */ +#define HAVE_GMTIME_R 1 + +/* hidapi library */ +/* #undef HAVE_HIDAPI */ + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_IOKIT_SERIAL_IOSS_H 1 + +/* libusb library */ +/* #undef HAVE_LIBUSB */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_LINUX_IRDA_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_LINUX_SERIAL_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_LINUX_TYPES_H */ + +/* Define to 1 if you have the 'localtime_r' function. */ +#define HAVE_LOCALTIME_R 1 + +/* Define to 1 if you have the 'mach_absolute_time' function. */ +#define HAVE_MACH_ABSOLUTE_TIME 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MACH_MACH_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_PTHREAD_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDIO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define if you have 'strerror_r'. */ +#define HAVE_STRERROR_R 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if 'tm_gmtoff' is a member of 'struct tm'. */ +#define HAVE_STRUCT_TM_TM_GMTOFF 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SOCKET_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the 'timegm' function. */ +#define HAVE_TIMEGM 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define if a version suffix is present. */ +#define HAVE_VERSION_SUFFIX 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_WINSOCK2_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_WS2BTH_H */ + +/* Define to 1 if you have the '_mkgmtime' function. */ +/* #undef HAVE__MKGMTIME */ + +/* Define to the sub-directory where libtool stores uninstalled libraries. */ +#define LT_OBJDIR ".libs/" + +/* Name of package */ +#define PACKAGE "libdivecomputer" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "libdivecomputer" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "libdivecomputer 0.9.0-devel" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "libdivecomputer" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "0.9.0-devel" + +/* Define to 1 if all of the C89 standard headers exist (not just the ones + required in a freestanding environment). This macro is provided for + backward compatibility; new code need not use it. */ +#define STDC_HEADERS 1 + +/* Define to 1 if strerror_r returns char *. */ +/* #undef STRERROR_R_CHAR_P */ + +/* Version number of package */ +#define VERSION "0.9.0-devel" diff --git a/ios/Classes/cressi_ady_parser.c b/ios/Classes/cressi_ady_parser.c new file mode 100644 index 0000000..2bcd171 --- /dev/null +++ b/ios/Classes/cressi_ady_parser.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/cressi_edy_parser.c" \ No newline at end of file diff --git a/ios/Classes/cressi_edy.c b/ios/Classes/cressi_edy.c new file mode 100644 index 0000000..fad1d26 --- /dev/null +++ b/ios/Classes/cressi_edy.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/cressi_edy.c" \ No newline at end of file diff --git a/ios/Classes/cressi_goa.c b/ios/Classes/cressi_goa.c new file mode 100644 index 0000000..16804db --- /dev/null +++ b/ios/Classes/cressi_goa.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/cressi_goa.c" \ No newline at end of file diff --git a/ios/Classes/cressi_goa_parser.c b/ios/Classes/cressi_goa_parser.c new file mode 100644 index 0000000..0249645 --- /dev/null +++ b/ios/Classes/cressi_goa_parser.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/cressi_goa_parser.c" \ No newline at end of file diff --git a/ios/Classes/cressi_leonardo.c b/ios/Classes/cressi_leonardo.c new file mode 100644 index 0000000..0920e12 --- /dev/null +++ b/ios/Classes/cressi_leonardo.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/cressi_leonardo.c" \ No newline at end of file diff --git a/ios/Classes/cressi_leonardo_parser.c b/ios/Classes/cressi_leonardo_parser.c new file mode 100644 index 0000000..c059391 --- /dev/null +++ b/ios/Classes/cressi_leonardo_parser.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/cressi_leonardo_parser.c" \ No newline at end of file diff --git a/ios/Classes/custom.c b/ios/Classes/custom.c new file mode 100644 index 0000000..a07eab4 --- /dev/null +++ b/ios/Classes/custom.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/custom.c" diff --git a/ios/Classes/deepblu_cosmiq.c b/ios/Classes/deepblu_cosmiq.c new file mode 100644 index 0000000..0560d94 --- /dev/null +++ b/ios/Classes/deepblu_cosmiq.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/deepblu_cosmiq.c" \ No newline at end of file diff --git a/ios/Classes/deepblu_cosmiq_parser.c b/ios/Classes/deepblu_cosmiq_parser.c new file mode 100644 index 0000000..9bd2470 --- /dev/null +++ b/ios/Classes/deepblu_cosmiq_parser.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/deepblu_cosmiq_parser.c" \ No newline at end of file diff --git a/ios/Classes/deepsix_excursion.c b/ios/Classes/deepsix_excursion.c new file mode 100644 index 0000000..34830f7 --- /dev/null +++ b/ios/Classes/deepsix_excursion.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/deepsix_excursion.c" \ No newline at end of file diff --git a/ios/Classes/deepsix_excursion_parser.c b/ios/Classes/deepsix_excursion_parser.c new file mode 100644 index 0000000..3be7ed2 --- /dev/null +++ b/ios/Classes/deepsix_excursion_parser.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/deepsix_excursion_parser.c" \ No newline at end of file diff --git a/ios/Classes/dive_computer.c b/ios/Classes/dive_computer.c new file mode 100644 index 0000000..d3075c4 --- /dev/null +++ b/ios/Classes/dive_computer.c @@ -0,0 +1,17 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/context.c" +#include "../../src/libdivecomputer/src/device.c" +#include "../../src/libdivecomputer/src/iterator.c" +#include "../../src/libdivecomputer/src/buffer.c" +#include "../../src/libdivecomputer/src/descriptor.c" +#include "../../src/libdivecomputer/src/iostream.c" +#include "../../src/libdivecomputer/src/checksum.c" +#include "../../src/libdivecomputer/src/ringbuffer.c" +#include "../../src/libdivecomputer/src/rbstream.c" +#include "../../src/libdivecomputer/src/common.c" +#include "../../src/libdivecomputer/src/datetime.c" +#include "../../src/libdivecomputer/src/packet.c" +#include "../../src/libdivecomputer/src/aes.c" +#include "../../src/libdivecomputer/src/platform.c" +#include "../../src/libdivecomputer/src/timer.c" +#include "../../src/libdivecomputer/src/parser.c" \ No newline at end of file diff --git a/ios/Classes/diverite_nitekq.c b/ios/Classes/diverite_nitekq.c new file mode 100644 index 0000000..055442e --- /dev/null +++ b/ios/Classes/diverite_nitekq.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/diverite_nitekq.c" \ No newline at end of file diff --git a/ios/Classes/diverite_nitekq_parser.c b/ios/Classes/diverite_nitekq_parser.c new file mode 100644 index 0000000..993af29 --- /dev/null +++ b/ios/Classes/diverite_nitekq_parser.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/diverite_nitekq_parser.c" \ No newline at end of file diff --git a/ios/Classes/divesoft_freedom.c b/ios/Classes/divesoft_freedom.c new file mode 100644 index 0000000..d1912c9 --- /dev/null +++ b/ios/Classes/divesoft_freedom.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/divesoft_freedom.c" \ No newline at end of file diff --git a/ios/Classes/divesoft_freedom_parser.c b/ios/Classes/divesoft_freedom_parser.c new file mode 100644 index 0000000..a425ce2 --- /dev/null +++ b/ios/Classes/divesoft_freedom_parser.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/divesoft_freedom_parser.c" \ No newline at end of file diff --git a/ios/Classes/divesystem_idive.c b/ios/Classes/divesystem_idive.c new file mode 100644 index 0000000..eecf385 --- /dev/null +++ b/ios/Classes/divesystem_idive.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/divesystem_idive.c" \ No newline at end of file diff --git a/ios/Classes/divesystem_idive_parser.c b/ios/Classes/divesystem_idive_parser.c new file mode 100644 index 0000000..db9909a --- /dev/null +++ b/ios/Classes/divesystem_idive_parser.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/divesystem_idive_parser.c" \ No newline at end of file diff --git a/ios/Classes/hdlc.c b/ios/Classes/hdlc.c new file mode 100644 index 0000000..86f4f40 --- /dev/null +++ b/ios/Classes/hdlc.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/hdlc.c" \ No newline at end of file diff --git a/ios/Classes/hw_frog.c b/ios/Classes/hw_frog.c new file mode 100644 index 0000000..0cf425a --- /dev/null +++ b/ios/Classes/hw_frog.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/hw_frog.c" \ No newline at end of file diff --git a/ios/Classes/hw_ostc.c b/ios/Classes/hw_ostc.c new file mode 100644 index 0000000..75e9ad9 --- /dev/null +++ b/ios/Classes/hw_ostc.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/hw_ostc.c" \ No newline at end of file diff --git a/ios/Classes/hw_ostc3.c b/ios/Classes/hw_ostc3.c new file mode 100644 index 0000000..e95c6e7 --- /dev/null +++ b/ios/Classes/hw_ostc3.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/hw_ostc3.c" \ No newline at end of file diff --git a/ios/Classes/hw_ostc_parser.c b/ios/Classes/hw_ostc_parser.c new file mode 100644 index 0000000..219de9f --- /dev/null +++ b/ios/Classes/hw_ostc_parser.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/hw_ostc_parser.c" \ No newline at end of file diff --git a/ios/Classes/ihex.c b/ios/Classes/ihex.c new file mode 100644 index 0000000..7160ec4 --- /dev/null +++ b/ios/Classes/ihex.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/ihex.c" \ No newline at end of file diff --git a/ios/Classes/liquivision_lynx.c b/ios/Classes/liquivision_lynx.c new file mode 100644 index 0000000..25d9b3a --- /dev/null +++ b/ios/Classes/liquivision_lynx.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/liquivision_lynx.c" \ No newline at end of file diff --git a/ios/Classes/liquivision_lynx_parser.c b/ios/Classes/liquivision_lynx_parser.c new file mode 100644 index 0000000..c3a5d40 --- /dev/null +++ b/ios/Classes/liquivision_lynx_parser.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/liquivision_lynx_parser.c" \ No newline at end of file diff --git a/ios/Classes/mares_common.c b/ios/Classes/mares_common.c new file mode 100644 index 0000000..98bd71e --- /dev/null +++ b/ios/Classes/mares_common.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/mares_common.c" \ No newline at end of file diff --git a/ios/Classes/mares_darwin.c b/ios/Classes/mares_darwin.c new file mode 100644 index 0000000..4df7462 --- /dev/null +++ b/ios/Classes/mares_darwin.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/mares_darwin.c" \ No newline at end of file diff --git a/ios/Classes/mares_darwin_parser.c b/ios/Classes/mares_darwin_parser.c new file mode 100644 index 0000000..00b1387 --- /dev/null +++ b/ios/Classes/mares_darwin_parser.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/mares_darwin_parser.c" \ No newline at end of file diff --git a/ios/Classes/mares_iconhd.c b/ios/Classes/mares_iconhd.c new file mode 100644 index 0000000..a65c583 --- /dev/null +++ b/ios/Classes/mares_iconhd.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/mares_iconhd.c" \ No newline at end of file diff --git a/ios/Classes/mares_iconhd_parser.c b/ios/Classes/mares_iconhd_parser.c new file mode 100644 index 0000000..e1f81c7 --- /dev/null +++ b/ios/Classes/mares_iconhd_parser.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/mares_iconhd_parser.c" \ No newline at end of file diff --git a/ios/Classes/mares_nemo.c b/ios/Classes/mares_nemo.c new file mode 100644 index 0000000..5073887 --- /dev/null +++ b/ios/Classes/mares_nemo.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/mares_nemo.c" \ No newline at end of file diff --git a/ios/Classes/mares_nemo_parser.c b/ios/Classes/mares_nemo_parser.c new file mode 100644 index 0000000..4db5af1 --- /dev/null +++ b/ios/Classes/mares_nemo_parser.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/mares_nemo_parser.c" \ No newline at end of file diff --git a/ios/Classes/mares_puck.c b/ios/Classes/mares_puck.c new file mode 100644 index 0000000..4e8607c --- /dev/null +++ b/ios/Classes/mares_puck.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/mares_puck.c" \ No newline at end of file diff --git a/ios/Classes/mclean_extreme.c b/ios/Classes/mclean_extreme.c new file mode 100644 index 0000000..0dbabd5 --- /dev/null +++ b/ios/Classes/mclean_extreme.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/mclean_extreme.c" \ No newline at end of file diff --git a/ios/Classes/mclean_extreme_parser.c b/ios/Classes/mclean_extreme_parser.c new file mode 100644 index 0000000..d17a471 --- /dev/null +++ b/ios/Classes/mclean_extreme_parser.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/mclean_extreme_parser.c" \ No newline at end of file diff --git a/ios/Classes/oceanic_atom2.c b/ios/Classes/oceanic_atom2.c new file mode 100644 index 0000000..794c001 --- /dev/null +++ b/ios/Classes/oceanic_atom2.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/oceanic_atom2.c" \ No newline at end of file diff --git a/ios/Classes/oceanic_atom2_parser.c b/ios/Classes/oceanic_atom2_parser.c new file mode 100644 index 0000000..33695b7 --- /dev/null +++ b/ios/Classes/oceanic_atom2_parser.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/oceanic_atom2_parser.c" \ No newline at end of file diff --git a/ios/Classes/oceanic_common.c b/ios/Classes/oceanic_common.c new file mode 100644 index 0000000..359abd4 --- /dev/null +++ b/ios/Classes/oceanic_common.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/oceanic_common.c" \ No newline at end of file diff --git a/ios/Classes/oceanic_veo250.c b/ios/Classes/oceanic_veo250.c new file mode 100644 index 0000000..e195327 --- /dev/null +++ b/ios/Classes/oceanic_veo250.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/oceanic_veo250.c" \ No newline at end of file diff --git a/ios/Classes/oceanic_veo250_parser.c b/ios/Classes/oceanic_veo250_parser.c new file mode 100644 index 0000000..abb9492 --- /dev/null +++ b/ios/Classes/oceanic_veo250_parser.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/oceanic_veo250_parser.c" \ No newline at end of file diff --git a/ios/Classes/oceans_s1.c b/ios/Classes/oceans_s1.c new file mode 100644 index 0000000..4f6106a --- /dev/null +++ b/ios/Classes/oceans_s1.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/oceans_s1.c" \ No newline at end of file diff --git a/ios/Classes/oceans_s1_common.c b/ios/Classes/oceans_s1_common.c new file mode 100644 index 0000000..1ef9fcb --- /dev/null +++ b/ios/Classes/oceans_s1_common.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/oceans_s1_common.c" \ No newline at end of file diff --git a/ios/Classes/oceans_s1_parser.c b/ios/Classes/oceans_s1_parser.c new file mode 100644 index 0000000..870a5c8 --- /dev/null +++ b/ios/Classes/oceans_s1_parser.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/oceans_s1_parser.c" \ No newline at end of file diff --git a/ios/Classes/ocenic_vtpro.c b/ios/Classes/ocenic_vtpro.c new file mode 100644 index 0000000..5313627 --- /dev/null +++ b/ios/Classes/ocenic_vtpro.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/oceanic_vtpro.c" \ No newline at end of file diff --git a/ios/Classes/ocenic_vtpro_parser.c b/ios/Classes/ocenic_vtpro_parser.c new file mode 100644 index 0000000..8c33ecd --- /dev/null +++ b/ios/Classes/ocenic_vtpro_parser.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/oceanic_vtpro_parser.c" \ No newline at end of file diff --git a/ios/Classes/reefnet_sensus.c b/ios/Classes/reefnet_sensus.c new file mode 100644 index 0000000..17ad4f3 --- /dev/null +++ b/ios/Classes/reefnet_sensus.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/reefnet_sensus.c" \ No newline at end of file diff --git a/ios/Classes/reefnet_sensus_parser.c b/ios/Classes/reefnet_sensus_parser.c new file mode 100644 index 0000000..1ad60dd --- /dev/null +++ b/ios/Classes/reefnet_sensus_parser.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/reefnet_sensus_parser.c" \ No newline at end of file diff --git a/ios/Classes/reefnet_sensuspro.c b/ios/Classes/reefnet_sensuspro.c new file mode 100644 index 0000000..18a7641 --- /dev/null +++ b/ios/Classes/reefnet_sensuspro.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/reefnet_sensuspro.c" \ No newline at end of file diff --git a/ios/Classes/reefnet_sensuspro_parser.c b/ios/Classes/reefnet_sensuspro_parser.c new file mode 100644 index 0000000..c312291 --- /dev/null +++ b/ios/Classes/reefnet_sensuspro_parser.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/reefnet_sensuspro_parser.c" \ No newline at end of file diff --git a/ios/Classes/reefnet_sensusultra.c b/ios/Classes/reefnet_sensusultra.c new file mode 100644 index 0000000..4f12834 --- /dev/null +++ b/ios/Classes/reefnet_sensusultra.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/reefnet_sensusultra.c" \ No newline at end of file diff --git a/ios/Classes/reefnet_sensusultra_parser.c b/ios/Classes/reefnet_sensusultra_parser.c new file mode 100644 index 0000000..8144fd2 --- /dev/null +++ b/ios/Classes/reefnet_sensusultra_parser.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/reefnet_sensusultra_parser.c" \ No newline at end of file diff --git a/ios/Classes/seac_screen.c b/ios/Classes/seac_screen.c new file mode 100644 index 0000000..d6af7e0 --- /dev/null +++ b/ios/Classes/seac_screen.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/seac_screen.c" \ No newline at end of file diff --git a/ios/Classes/seac_screen_parser.c b/ios/Classes/seac_screen_parser.c new file mode 100644 index 0000000..267f566 --- /dev/null +++ b/ios/Classes/seac_screen_parser.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/seac_screen_parser.c" \ No newline at end of file diff --git a/ios/Classes/shearwater_common.c b/ios/Classes/shearwater_common.c new file mode 100644 index 0000000..5386313 --- /dev/null +++ b/ios/Classes/shearwater_common.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/shearwater_common.c" \ No newline at end of file diff --git a/ios/Classes/shearwater_petrel.c b/ios/Classes/shearwater_petrel.c new file mode 100644 index 0000000..2e8e3dc --- /dev/null +++ b/ios/Classes/shearwater_petrel.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/shearwater_petrel.c" \ No newline at end of file diff --git a/ios/Classes/shearwater_predator.c b/ios/Classes/shearwater_predator.c new file mode 100644 index 0000000..8e08581 --- /dev/null +++ b/ios/Classes/shearwater_predator.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/shearwater_predator.c" \ No newline at end of file diff --git a/ios/Classes/shearwater_predator_parser.c b/ios/Classes/shearwater_predator_parser.c new file mode 100644 index 0000000..8d63c24 --- /dev/null +++ b/ios/Classes/shearwater_predator_parser.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/shearwater_predator_parser.c" \ No newline at end of file diff --git a/ios/Classes/sporasub_sp2.c b/ios/Classes/sporasub_sp2.c new file mode 100644 index 0000000..dfe9acb --- /dev/null +++ b/ios/Classes/sporasub_sp2.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/sporasub_sp2.c" \ No newline at end of file diff --git a/ios/Classes/sporasub_sp2_parser.c b/ios/Classes/sporasub_sp2_parser.c new file mode 100644 index 0000000..6702d88 --- /dev/null +++ b/ios/Classes/sporasub_sp2_parser.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/sporasub_sp2_parser.c" \ No newline at end of file diff --git a/ios/Classes/suunto_common.c b/ios/Classes/suunto_common.c new file mode 100644 index 0000000..7b401c2 --- /dev/null +++ b/ios/Classes/suunto_common.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/suunto_common.c" \ No newline at end of file diff --git a/ios/Classes/suunto_common2.c b/ios/Classes/suunto_common2.c new file mode 100644 index 0000000..7948a4b --- /dev/null +++ b/ios/Classes/suunto_common2.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/suunto_common2.c" \ No newline at end of file diff --git a/ios/Classes/suunto_d9.c b/ios/Classes/suunto_d9.c new file mode 100644 index 0000000..65df56d --- /dev/null +++ b/ios/Classes/suunto_d9.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/suunto_d9.c" \ No newline at end of file diff --git a/ios/Classes/suunto_d9_parser.c b/ios/Classes/suunto_d9_parser.c new file mode 100644 index 0000000..ed62ced --- /dev/null +++ b/ios/Classes/suunto_d9_parser.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/suunto_d9_parser.c" \ No newline at end of file diff --git a/ios/Classes/suunto_eon.c b/ios/Classes/suunto_eon.c new file mode 100644 index 0000000..358a872 --- /dev/null +++ b/ios/Classes/suunto_eon.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/suunto_eon.c" \ No newline at end of file diff --git a/ios/Classes/suunto_eon_parser.c b/ios/Classes/suunto_eon_parser.c new file mode 100644 index 0000000..ae8f88a --- /dev/null +++ b/ios/Classes/suunto_eon_parser.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/suunto_eon_parser.c" \ No newline at end of file diff --git a/ios/Classes/suunto_eonsteel.c b/ios/Classes/suunto_eonsteel.c new file mode 100644 index 0000000..de3d35e --- /dev/null +++ b/ios/Classes/suunto_eonsteel.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/suunto_eonsteel.c" \ No newline at end of file diff --git a/ios/Classes/suunto_eonsteel_parser.c b/ios/Classes/suunto_eonsteel_parser.c new file mode 100644 index 0000000..e833e48 --- /dev/null +++ b/ios/Classes/suunto_eonsteel_parser.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/suunto_eonsteel_parser.c" \ No newline at end of file diff --git a/ios/Classes/suunto_solution.c b/ios/Classes/suunto_solution.c new file mode 100644 index 0000000..c5e2d14 --- /dev/null +++ b/ios/Classes/suunto_solution.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/suunto_solution.c" \ No newline at end of file diff --git a/ios/Classes/suunto_solution_parser.c b/ios/Classes/suunto_solution_parser.c new file mode 100644 index 0000000..b30fdf8 --- /dev/null +++ b/ios/Classes/suunto_solution_parser.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/suunto_solution_parser.c" \ No newline at end of file diff --git a/ios/Classes/suunto_vyper.c b/ios/Classes/suunto_vyper.c new file mode 100644 index 0000000..c50becb --- /dev/null +++ b/ios/Classes/suunto_vyper.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/suunto_vyper.c" \ No newline at end of file diff --git a/ios/Classes/suunto_vyper2.c b/ios/Classes/suunto_vyper2.c new file mode 100644 index 0000000..1e9e3a4 --- /dev/null +++ b/ios/Classes/suunto_vyper2.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/suunto_vyper2.c" \ No newline at end of file diff --git a/ios/Classes/suunto_vyper_parser.c b/ios/Classes/suunto_vyper_parser.c new file mode 100644 index 0000000..0089749 --- /dev/null +++ b/ios/Classes/suunto_vyper_parser.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/suunto_vyper_parser.c" \ No newline at end of file diff --git a/ios/Classes/tecdiving_divecomputereu.c b/ios/Classes/tecdiving_divecomputereu.c new file mode 100644 index 0000000..d872b71 --- /dev/null +++ b/ios/Classes/tecdiving_divecomputereu.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/tecdiving_divecomputereu.c" \ No newline at end of file diff --git a/ios/Classes/tecdiving_divecomputereu_parser.c b/ios/Classes/tecdiving_divecomputereu_parser.c new file mode 100644 index 0000000..0fa0ddc --- /dev/null +++ b/ios/Classes/tecdiving_divecomputereu_parser.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/tecdiving_divecomputereu_parser.c" \ No newline at end of file diff --git a/ios/Classes/uwatec_aladin.c b/ios/Classes/uwatec_aladin.c new file mode 100644 index 0000000..a0a1df6 --- /dev/null +++ b/ios/Classes/uwatec_aladin.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/uwatec_aladin.c" \ No newline at end of file diff --git a/ios/Classes/uwatec_memomouse.c b/ios/Classes/uwatec_memomouse.c new file mode 100644 index 0000000..0f0b6ef --- /dev/null +++ b/ios/Classes/uwatec_memomouse.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/uwatec_memomouse.c" \ No newline at end of file diff --git a/ios/Classes/uwatec_memomouse_parser.c b/ios/Classes/uwatec_memomouse_parser.c new file mode 100644 index 0000000..8abab6d --- /dev/null +++ b/ios/Classes/uwatec_memomouse_parser.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/uwatec_memomouse_parser.c" \ No newline at end of file diff --git a/ios/Classes/uwatec_smart.c b/ios/Classes/uwatec_smart.c new file mode 100644 index 0000000..9a0d225 --- /dev/null +++ b/ios/Classes/uwatec_smart.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/uwatec_smart.c" \ No newline at end of file diff --git a/ios/Classes/uwatec_smart_parser.c b/ios/Classes/uwatec_smart_parser.c new file mode 100644 index 0000000..92e2f1e --- /dev/null +++ b/ios/Classes/uwatec_smart_parser.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/uwatec_smart_parser.c" \ No newline at end of file diff --git a/ios/Classes/zeagle_n2ition3.c b/ios/Classes/zeagle_n2ition3.c new file mode 100644 index 0000000..4905c58 --- /dev/null +++ b/ios/Classes/zeagle_n2ition3.c @@ -0,0 +1,2 @@ +#include "config.h" +#include "../../src/libdivecomputer/src/zeagle_n2ition3.c" \ No newline at end of file diff --git a/ios/dive_computer.podspec b/ios/dive_computer.podspec index 989935f..e7d49b8 100644 --- a/ios/dive_computer.podspec +++ b/ios/dive_computer.podspec @@ -25,6 +25,7 @@ DiveComputer FFI plugin for Flutter. # Flutter.framework does not contain a i386 slice. s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', + 'HEADER_SEARCH_PATHS' => '"${PODS_TARGET_SRCROOT}/../src/libdivecomputer/include" "${PODS_TARGET_SRCROOT}/../src/libdivecomputer_config"', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386', } diff --git a/lib/framework/dive_computer_ffi.dart b/lib/framework/dive_computer_ffi.dart index 91d11a4..74c128f 100644 --- a/lib/framework/dive_computer_ffi.dart +++ b/lib/framework/dive_computer_ffi.dart @@ -2,16 +2,16 @@ import 'dart:developer' as developer; import 'dart:ffi' as ffi; import 'dart:io'; -import 'package:dive_computer/framework/interfaces/dive_computer_interfaces.dart'; -import 'package:dive_computer/framework/utils/transports_bitmask.dart'; -import 'package:dive_computer/framework/utils/utils.dart'; -import 'package:dive_computer/types/computer.dart'; -import 'package:dive_computer/types/dive.dart'; import 'package:ffi/ffi.dart'; import 'package:flutter/foundation.dart'; import 'package:logging/logging.dart' as logging; +import './interfaces/dive_computer_interfaces.dart'; import './dive_computer_ffi_bindings_generated.dart'; +import './utils/transports_bitmask.dart'; +import './utils/utils.dart'; +import '../types/computer.dart'; +import '../types/dive.dart'; final log = logging.Logger('DiveComputerFfi'); @@ -53,6 +53,10 @@ class DiveComputerFfi { log.fine('Loading complete'); } + void dispose() { + _interfaces.dispose(); + } + static final context = calloc>(); static late final ffi.DynamicLibrary _library; @@ -155,7 +159,7 @@ class DiveComputerFfi { final computerDescriptor = _computerDescriptorCache[computer]!; final ffi.Pointer iostream = - _interfaces.connect(transport, computerDescriptor); + _interfaces.connect(computer, transport, computerDescriptor, context); final device = calloc>(); try { diff --git a/lib/framework/dive_computer_ffi_bindings_generated.dart b/lib/framework/dive_computer_ffi_bindings_generated.dart index e231185..520f500 100644 --- a/lib/framework/dive_computer_ffi_bindings_generated.dart +++ b/lib/framework/dive_computer_ffi_bindings_generated.dart @@ -1731,6 +1731,273 @@ class DiveComputerFfiBindings { late final _dc_usbhid_open = _dc_usbhid_openPtr.asFunction< int Function(ffi.Pointer>, ffi.Pointer, ffi.Pointer)>(); + + /// Convert a bluetooth address to a string. + /// + /// The bluetooth address is formatted as XX:XX:XX:XX:XX:XX, where each + /// XX is a hexadecimal number specifying an octet of the 48-bit address. + /// The minimum size for the buffer is #DC_BLUETOOTH_SIZE bytes. + /// + /// @param[in] address A bluetooth address. + /// @param[in] str The memory buffer to store the result. + /// @param[in] size The size of the memory buffer. + /// @returns The null-terminated string on success, or NULL on failure. + ffi.Pointer dc_bluetooth_addr2str( + int address, + ffi.Pointer str, + int size, + ) { + return _dc_bluetooth_addr2str( + address, + str, + size, + ); + } + + late final _dc_bluetooth_addr2strPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function(dc_bluetooth_address_t, + ffi.Pointer, ffi.Size)>>('dc_bluetooth_addr2str'); + late final _dc_bluetooth_addr2str = _dc_bluetooth_addr2strPtr.asFunction< + ffi.Pointer Function(int, ffi.Pointer, int)>(); + + /// Convert a string to a bluetooth address. + /// + /// The string is expected to be in the format XX:XX:XX:XX:XX:XX, where + /// each XX is a hexadecimal number specifying an octet of the 48-bit + /// address. + /// + /// @param[in] address A null-terminated string. + /// @returns The bluetooth address on success, or zero on failure. + int dc_bluetooth_str2addr( + ffi.Pointer address, + ) { + return _dc_bluetooth_str2addr( + address, + ); + } + + late final _dc_bluetooth_str2addrPtr = _lookup< + ffi.NativeFunction< + dc_bluetooth_address_t Function( + ffi.Pointer)>>('dc_bluetooth_str2addr'); + late final _dc_bluetooth_str2addr = _dc_bluetooth_str2addrPtr + .asFunction)>(); + + /// Get the address of the bluetooth device. + /// + /// @param[in] device A valid bluetooth device. + int dc_bluetooth_device_get_address( + ffi.Pointer device, + ) { + return _dc_bluetooth_device_get_address( + device, + ); + } + + late final _dc_bluetooth_device_get_addressPtr = _lookup< + ffi.NativeFunction< + dc_bluetooth_address_t Function( + ffi.Pointer)>>( + 'dc_bluetooth_device_get_address'); + late final _dc_bluetooth_device_get_address = + _dc_bluetooth_device_get_addressPtr + .asFunction)>(); + + /// Get the name of the bluetooth device. + /// + /// @param[in] device A valid bluetooth device. + ffi.Pointer dc_bluetooth_device_get_name( + ffi.Pointer device, + ) { + return _dc_bluetooth_device_get_name( + device, + ); + } + + late final _dc_bluetooth_device_get_namePtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer)>>( + 'dc_bluetooth_device_get_name'); + late final _dc_bluetooth_device_get_name = + _dc_bluetooth_device_get_namePtr.asFunction< + ffi.Pointer Function(ffi.Pointer)>(); + + /// Destroy the bluetooth device and free all resources. + /// + /// @param[in] device A valid bluetooth device. + void dc_bluetooth_device_free( + ffi.Pointer device, + ) { + return _dc_bluetooth_device_free( + device, + ); + } + + late final _dc_bluetooth_device_freePtr = _lookup< + ffi.NativeFunction< + ffi.Void Function( + ffi.Pointer)>>('dc_bluetooth_device_free'); + late final _dc_bluetooth_device_free = _dc_bluetooth_device_freePtr + .asFunction)>(); + + /// Create an iterator to enumerate the bluetooth devices. + /// + /// @param[out] iterator A location to store the iterator. + /// @param[in] context A valid context object. + /// @param[in] descriptor A valid device descriptor or NULL. + /// @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code + /// on failure. + int dc_bluetooth_iterator_new( + ffi.Pointer> iterator, + ffi.Pointer context, + ffi.Pointer descriptor, + ) { + return _dc_bluetooth_iterator_new( + iterator, + context, + descriptor, + ); + } + + late final _dc_bluetooth_iterator_newPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer>, + ffi.Pointer, + ffi.Pointer)>>('dc_bluetooth_iterator_new'); + late final _dc_bluetooth_iterator_new = + _dc_bluetooth_iterator_newPtr.asFunction< + int Function(ffi.Pointer>, + ffi.Pointer, ffi.Pointer)>(); + + /// Open an bluetooth connection. + /// + /// @param[out] iostream A location to store the bluetooth connection. + /// @param[in] context A valid context object. + /// @param[in] address The bluetooth device address. + /// @param[in] port The bluetooth port number. + /// @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code + /// on failure. + int dc_bluetooth_open( + ffi.Pointer> iostream, + ffi.Pointer context, + int address, + int port, + ) { + return _dc_bluetooth_open( + iostream, + context, + address, + port, + ); + } + + late final _dc_bluetooth_openPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer>, + ffi.Pointer, + dc_bluetooth_address_t, + ffi.UnsignedInt)>>('dc_bluetooth_open'); + late final _dc_bluetooth_open = _dc_bluetooth_openPtr.asFunction< + int Function(ffi.Pointer>, + ffi.Pointer, int, int)>(); + + /// Create a custom I/O stream. + /// + /// @param[out] iostream A location to store the custom I/O stream. + /// @param[in] context A valid context object. + /// @param[in] callbacks The callback functions to call. + /// @param[in] userdata User data to pass to the callback functions. + /// @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code + /// on failure. + int dc_custom_open( + ffi.Pointer> iostream, + ffi.Pointer context, + int transport, + ffi.Pointer callbacks, + ffi.Pointer userdata, + ) { + return _dc_custom_open( + iostream, + context, + transport, + callbacks, + userdata, + ); + } + + late final _dc_custom_openPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer>, + ffi.Pointer, + ffi.Int32, + ffi.Pointer, + ffi.Pointer)>>('dc_custom_open'); + late final _dc_custom_open = _dc_custom_openPtr.asFunction< + int Function( + ffi.Pointer>, + ffi.Pointer, + int, + ffi.Pointer, + ffi.Pointer)>(); + + ffi.Pointer dc_iostream_allocate( + ffi.Pointer context, + ffi.Pointer vtable, + int transport, + ) { + return _dc_iostream_allocate( + context, + vtable, + transport, + ); + } + + late final _dc_iostream_allocatePtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ffi.Int32)>>('dc_iostream_allocate'); + late final _dc_iostream_allocate = _dc_iostream_allocatePtr.asFunction< + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer, int)>(); + + void dc_iostream_deallocate( + ffi.Pointer iostream, + ) { + return _dc_iostream_deallocate( + iostream, + ); + } + + late final _dc_iostream_deallocatePtr = _lookup< + ffi.NativeFunction)>>( + 'dc_iostream_deallocate'); + late final _dc_iostream_deallocate = _dc_iostream_deallocatePtr + .asFunction)>(); + + int dc_iostream_isinstance( + ffi.Pointer iostream, + ffi.Pointer vtable, + ) { + return _dc_iostream_isinstance( + iostream, + vtable, + ); + } + + late final _dc_iostream_isinstancePtr = _lookup< + ffi.NativeFunction< + ffi.Int Function(ffi.Pointer, + ffi.Pointer)>>('dc_iostream_isinstance'); + late final _dc_iostream_isinstance = _dc_iostream_isinstancePtr.asFunction< + int Function( + ffi.Pointer, ffi.Pointer)>(); } abstract class dc_status_t { @@ -1867,16 +2134,23 @@ abstract class dc_loglevel_t { static const int DC_LOGLEVEL_ALL = 5; } -typedef dc_logfunc_t = ffi.Pointer< - ffi.NativeFunction< - ffi.Void Function( - ffi.Pointer context, - ffi.Int32 loglevel, - ffi.Pointer file, - ffi.UnsignedInt line, - ffi.Pointer function, - ffi.Pointer message, - ffi.Pointer userdata)>>; +typedef dc_logfunc_t = ffi.Pointer>; +typedef dc_logfunc_tFunction = ffi.Void Function( + ffi.Pointer context, + ffi.Int32 loglevel, + ffi.Pointer file, + ffi.UnsignedInt line, + ffi.Pointer function, + ffi.Pointer message, + ffi.Pointer userdata); +typedef Dartdc_logfunc_tFunction = void Function( + ffi.Pointer context, + int loglevel, + ffi.Pointer file, + int line, + ffi.Pointer function, + ffi.Pointer message, + ffi.Pointer userdata); final class dc_iterator_t extends ffi.Opaque {} @@ -1979,7 +2253,110 @@ final class _opaque_pthread_t extends ffi.Struct { external ffi.Array __opaque; } -final class dc_iostream_t extends ffi.Opaque {} +final class dc_iostream_t extends ffi.Struct { + external ffi.Pointer vtable; + + external ffi.Pointer context; + + @ffi.Int32() + external int transport; +} + +final class dc_iostream_vtable_t extends ffi.Struct { + @ffi.Size() + external int size; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer iostream, ffi.Int timeout)>> + set_timeout; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer iostream, ffi.UnsignedInt value)>> + set_break; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer iostream, ffi.UnsignedInt value)>> + set_dtr; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer iostream, ffi.UnsignedInt value)>> + set_rts; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Int32 Function(ffi.Pointer iostream, + ffi.Pointer value)>> get_lines; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Int32 Function(ffi.Pointer iostream, + ffi.Pointer value)>> get_available; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer iostream, + ffi.UnsignedInt baudrate, + ffi.UnsignedInt databits, + ffi.Int32 parity, + ffi.Int32 stopbits, + ffi.Int32 flowcontrol)>> configure; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer iostream, ffi.Int timeout)>> poll; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer iostream, + ffi.Pointer data, + ffi.Size size, + ffi.Pointer actual)>> read; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer iostream, + ffi.Pointer data, + ffi.Size size, + ffi.Pointer actual)>> write; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer iostream, + ffi.UnsignedInt request, + ffi.Pointer data, + ffi.Size size)>> ioctl; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Int32 Function(ffi.Pointer iostream)>> flush; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer iostream, ffi.Int32 direction)>> purge; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Int32 Function(ffi.Pointer iostream, + ffi.UnsignedInt milliseconds)>> sleep; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Int32 Function(ffi.Pointer iostream)>> close; +} /// The parity checking scheme. abstract class dc_parity_t { @@ -2076,6 +2453,7 @@ final class dc_datetime_t extends ffi.Struct { } typedef dc_ticks_t = ffi.LongLong; +typedef Dartdc_ticks_t = int; abstract class dc_event_type_t { static const int DC_EVENT_WAITING = 1; @@ -2121,20 +2499,38 @@ final class dc_event_vendor_t extends ffi.Struct { external int size; } -typedef dc_cancel_callback_t = ffi.Pointer< - ffi.NativeFunction userdata)>>; -typedef dc_event_callback_t = ffi.Pointer< - ffi.NativeFunction< - ffi.Void Function(ffi.Pointer device, ffi.Int32 event, - ffi.Pointer data, ffi.Pointer userdata)>>; -typedef dc_dive_callback_t = ffi.Pointer< - ffi.NativeFunction< - ffi.Int Function( - ffi.Pointer data, - ffi.UnsignedInt size, - ffi.Pointer fingerprint, - ffi.UnsignedInt fsize, - ffi.Pointer userdata)>>; +typedef dc_cancel_callback_t + = ffi.Pointer>; +typedef dc_cancel_callback_tFunction = ffi.Int Function( + ffi.Pointer userdata); +typedef Dartdc_cancel_callback_tFunction = int Function( + ffi.Pointer userdata); +typedef dc_event_callback_t + = ffi.Pointer>; +typedef dc_event_callback_tFunction = ffi.Void Function( + ffi.Pointer device, + ffi.Int32 event, + ffi.Pointer data, + ffi.Pointer userdata); +typedef Dartdc_event_callback_tFunction = void Function( + ffi.Pointer device, + int event, + ffi.Pointer data, + ffi.Pointer userdata); +typedef dc_dive_callback_t + = ffi.Pointer>; +typedef dc_dive_callback_tFunction = ffi.Int Function( + ffi.Pointer data, + ffi.UnsignedInt size, + ffi.Pointer fingerprint, + ffi.UnsignedInt fsize, + ffi.Pointer userdata); +typedef Dartdc_dive_callback_tFunction = int Function( + ffi.Pointer data, + int size, + ffi.Pointer fingerprint, + int fsize, + ffi.Pointer userdata); final class dc_serial_device_t extends ffi.Opaque {} @@ -2473,10 +2869,12 @@ final class UnnamedStruct6 extends ffi.Struct { final class dc_parser_t extends ffi.Opaque {} -typedef dc_sample_callback_t = ffi.Pointer< - ffi.NativeFunction< - ffi.Void Function(ffi.Int32 type, ffi.Pointer value, - ffi.Pointer userdata)>>; +typedef dc_sample_callback_t + = ffi.Pointer>; +typedef dc_sample_callback_tFunction = ffi.Void Function(ffi.Int32 type, + ffi.Pointer value, ffi.Pointer userdata); +typedef Dartdc_sample_callback_tFunction = void Function(int type, + ffi.Pointer value, ffi.Pointer userdata); /// USB control transfer. final class dc_usb_control_t extends ffi.Struct { @@ -2540,6 +2938,104 @@ final class dc_usbhid_desc_t extends ffi.Struct { final class dc_usbhid_device_t extends ffi.Opaque {} +typedef dc_bluetooth_address_t = ffi.UnsignedLongLong; +typedef Dartdc_bluetooth_address_t = int; + +final class dc_bluetooth_device_t extends ffi.Opaque {} + +final class dc_custom_cbs_t extends ffi.Struct { + external ffi.Pointer< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer userdata, ffi.Int timeout)>> set_timeout; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer userdata, ffi.UnsignedInt value)>> + set_break; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer userdata, ffi.UnsignedInt value)>> set_dtr; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer userdata, ffi.UnsignedInt value)>> set_rts; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Int32 Function(ffi.Pointer userdata, + ffi.Pointer value)>> get_lines; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer userdata, ffi.Pointer value)>> + get_available; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer userdata, + ffi.UnsignedInt baudrate, + ffi.UnsignedInt databits, + ffi.Int32 parity, + ffi.Int32 stopbits, + ffi.Int32 flowcontrol)>> configure; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer userdata, ffi.Int timeout)>> poll; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer userdata, + ffi.Pointer data, + ffi.Size size, + ffi.Pointer actual)>> read; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer userdata, + ffi.Pointer data, + ffi.Size size, + ffi.Pointer actual)>> write; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer userdata, + ffi.UnsignedInt request, + ffi.Pointer data, + ffi.Size size)>> ioctl; + + external ffi.Pointer< + ffi + .NativeFunction userdata)>> + flush; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer userdata, ffi.Int32 direction)>> purge; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Int32 Function(ffi.Pointer userdata, + ffi.UnsignedInt milliseconds)>> sleep; + + external ffi.Pointer< + ffi + .NativeFunction userdata)>> + close; +} + const int __DARWIN_ONLY_64_BIT_INO_T = 1; const int __DARWIN_ONLY_UNIX_CONFORMANCE = 1; @@ -2863,3 +3359,7 @@ const int DC_GASMIX_UNKNOWN = 4294967295; const int DC_IOCTL_USB_CONTROL_READ = 1073771776; const int DC_IOCTL_USB_CONTROL_WRITE = 2147513600; + +const int DC_IOCTL_BLE_GET_NAME = 1073766912; + +const int DC_BLUETOOTH_SIZE = 18; diff --git a/lib/framework/dive_computer_interface.dart b/lib/framework/dive_computer_interface.dart index 6e872c9..fa9b681 100644 --- a/lib/framework/dive_computer_interface.dart +++ b/lib/framework/dive_computer_interface.dart @@ -1,3 +1,4 @@ +import 'package:dive_computer/framework/interfaces/ble_interface.dart'; import 'package:dive_computer/types/computer.dart'; import 'package:dive_computer/types/dive.dart'; @@ -23,4 +24,8 @@ abstract class DiveComputerInterface { ]) { throw UnimplementedError(); } + + Future> fetchBleDevices() { + throw UnimplementedError(); + } } diff --git a/lib/framework/dive_computer_isolate.dart b/lib/framework/dive_computer_isolate.dart index 8814497..7ab7978 100644 --- a/lib/framework/dive_computer_isolate.dart +++ b/lib/framework/dive_computer_isolate.dart @@ -1,12 +1,15 @@ import 'dart:async'; +import 'dart:io'; import 'dart:isolate'; import 'dart:developer' as developer; import 'package:dive_computer/framework/dive_computer_interface.dart'; import 'package:dive_computer/framework/dive_computer_ffi.dart'; +import 'package:dive_computer/framework/interfaces/ble_interface.dart'; import 'package:dive_computer/types/computer.dart'; import 'package:dive_computer/types/dive.dart'; import 'package:flutter/foundation.dart'; +import 'package:flutter_blue_plus/flutter_blue_plus.dart'; import 'package:logging/logging.dart'; enum DiveComputerMethod { @@ -15,6 +18,7 @@ enum DiveComputerMethod { enableDebugLogging, supportedComputers, download, + fetchBleDevices, } typedef IsolateMessage = (DiveComputerMethod method, List args); @@ -28,6 +32,7 @@ class DiveComputer implements DiveComputerInterface { Completer>? _supportedComputers; Completer>? _downloadedDives; + Completer>? _bleDevices; DiveComputer._() { _receivePort = ReceivePort(); @@ -46,6 +51,8 @@ class DiveComputer implements DiveComputerInterface { _supportedComputers?.complete(message); } else if (message is List) { _downloadedDives?.complete(message); + } else if (message is List) { + _bleDevices?.complete(message); } else if (message is Error || message is Exception) { if (_supportedComputers?.isCompleted == false) { _supportedComputers?.completeError(message); @@ -97,17 +104,27 @@ class DiveComputer implements DiveComputerInterface { )); return (_downloadedDives = Completer()).future; } + + @override + Future> fetchBleDevices() async { + await _send((DiveComputerMethod.fetchBleDevices, [])); + return (_bleDevices = Completer()).future; + } } _spawnIsolate(SendPort sendPort) { developer.log( - 'Spawning DiveComputerFfi in an Isolate', + 'Spawning DiveComputerFfi and BleInterface in an Isolate', name: 'DiveComputerIsolate', ); Object? initializationError; try { DiveComputerFfi.initialize(); + + if (Platform.isAndroid || Platform.isIOS) { + BleInterface.initialize(); + } } catch (e) { initializationError = e; } @@ -126,6 +143,7 @@ _spawnIsolate(SendPort sendPort) { break; case DiveComputerMethod.enableDebugLogging: DiveComputerFfi.enableDebugLogging(Level.FINEST); + BleInterface.enableDebugLogging(Level.FINEST); break; case DiveComputerMethod.supportedComputers: final computers = DiveComputerFfi.supportedComputers; @@ -134,11 +152,19 @@ _spawnIsolate(SendPort sendPort) { case DiveComputerMethod.download: final computer = message.$2[0] as Computer; final transport = message.$2[1] as ComputerTransport; - final lastFingerprint = message.$2[2] as String?; + final lastFingerprint = message.$2[3] as String?; DiveComputerFfi.divesCallback = (dives) { sendPort.send(dives); }; - DiveComputerFfi.download(computer, transport, lastFingerprint); + DiveComputerFfi.download( + computer, + transport, + lastFingerprint, + ); + break; + case DiveComputerMethod.fetchBleDevices: + final devices = BleInterface.fetchDevices(); + sendPort.send(devices); break; default: throw UnimplementedError('Message not implemented: $message'); diff --git a/lib/framework/interfaces/ble_interface.dart b/lib/framework/interfaces/ble_interface.dart new file mode 100644 index 0000000..3427403 --- /dev/null +++ b/lib/framework/interfaces/ble_interface.dart @@ -0,0 +1,106 @@ +import 'dart:async'; +import 'dart:developer' as developer; + +import 'package:flutter_blue_plus/flutter_blue_plus.dart'; +import 'package:logging/logging.dart' as logging; + +final log = logging.Logger('DiveComputerFfi'); + +class BleInterface { + static void initialize() async { + logging.hierarchicalLoggingEnabled = true; + log.onRecord.listen((e) { + developer.log( + e.message, + time: e.time, + sequenceNumber: e.sequenceNumber, + level: e.level.value, + name: e.loggerName, + zone: e.zone, + error: e.error, + stackTrace: e.stackTrace, + ); + }); + + FlutterBluePlus.setLogLevel(LogLevel.debug, color: false); + + _adapterState = BluetoothAdapterState.unknown; + + // if (await FlutterBluePlus.isSupported == false) { + // log.fine('Bluetooth not supported by this device'); + // return; + // } + + _adapterStateStateSubscription = _subscribeToBleAdapterState(); + _scanResultsSubscription = _subscribeToScanResults(); + } + + void dispose() { + _scanResultsSubscription.cancel(); + _adapterStateStateSubscription.cancel(); + //_isScanningSubscription.cancel(); + } + + static late BluetoothAdapterState _adapterState; + static late final StreamSubscription + _adapterStateStateSubscription; + static late final StreamSubscription> + _scanResultsSubscription; + //static late StreamSubscription _isScanningSubscription; + static List _bleDevices = []; + + static void enableDebugLogging([logging.Level level = logging.Level.INFO]) { + log.level = level; + } + + static StreamSubscription + _subscribeToBleAdapterState() { + return FlutterBluePlus.adapterState.listen((BluetoothAdapterState state) { + log.fine('Current BLE adapter state: $state'); + + if (state == BluetoothAdapterState.on) { + _adapterState = state; + } else { + log.warning("Can't connect to BLE devices, adapter is $state"); + } + }); + } + + static StreamSubscription> _subscribeToScanResults() { + return FlutterBluePlus.onScanResults.listen( + (results) { + if (results.isNotEmpty) { + _bleDevices = results.map((result) => BleDevice(result)).toList(); + } + }, + onError: (e) => log.severe('Error while listening to scan results: $e'), + ); + } + + static List fetchDevices() { + if (_adapterState == BluetoothAdapterState.on) { + log.fine('Scan for BLE devices'); + FlutterBluePlus.startScan(timeout: const Duration(seconds: 15)); + } + + return _bleDevices; + } + + // BluetoothAdapterState _adapterState = BluetoothAdapterState.unknown; + // late StreamSubscription _adapterStateStateSubscription; + // late StreamSubscription> _scanResultsSubscription; + // late StreamSubscription _isScanningSubscription; + // List _systemDevices = []; + // List _scanResults = []; + // bool _isScanning = false; +} + +class BleDevice { + late String _advertisementName; + + BleDevice(ScanResult result) { + _advertisementName = result.advertisementData.advName; + } + + String get advertisementName => _advertisementName; +} diff --git a/lib/framework/interfaces/dive_computer_interfaces.dart b/lib/framework/interfaces/dive_computer_interfaces.dart index 467f8ea..1f916ab 100644 --- a/lib/framework/interfaces/dive_computer_interfaces.dart +++ b/lib/framework/interfaces/dive_computer_interfaces.dart @@ -1,31 +1,46 @@ import 'dart:ffi' as ffi; import 'package:ffi/ffi.dart'; +import 'package:flutter_blue_plus/flutter_blue_plus.dart'; import 'package:logging/logging.dart' as logging; import '../utils/utils.dart'; import '../dive_computer_ffi_bindings_generated.dart'; +import '../../types/ble_object.dart'; import '../../types/computer.dart'; +import '../../types/dc_iostream_t.dart' as io; class Interfaces { final DiveComputerFfiBindings bindings; final ffi.Pointer> context; final logging.Logger log; + final BLEObject _bleObject = BLEObject(); + Interfaces({ required this.bindings, required this.context, required this.log, }); + void dispose() { + _bleObject.dispose(); + } + ffi.Pointer connect( - ComputerTransport transport, ffi.Pointer computer) { + Computer computer, + ComputerTransport transport, + ffi.Pointer computerDescriptor, + ffi.Pointer> context, + ) { switch (transport) { case ComputerTransport.serial: - return _connectSerial(computer); + return _connectSerial(computerDescriptor); case ComputerTransport.usb: - return _connectUsb(computer); + return _connectUsb(computerDescriptor); case ComputerTransport.usbhid: - return _connectUsbHid(computer); + return _connectUsbHid(computerDescriptor); + case ComputerTransport.ble: + //return _connectBle(device, context); default: throw UnimplementedError(); } @@ -180,4 +195,82 @@ class Interfaces { return iostream.value; } + + ffi.Pointer _connectBle( + BluetoothDevice? device, + ffi.Pointer> context, + ) { + handleResult( + device == null || device.platformName.isEmpty + ? dc_status_t.DC_STATUS_NODEVICE + : dc_status_t.DC_STATUS_SUCCESS, + 'No device found'); + + //DONE 1. get dc_descriptor_t for device <- downloadfromdcthread.cpp line 90 <- LIBDIVECOMPUTER + // 2. do_libdivecomputer_import <- downloadfromdcthread.cpp line 117 + //DONE a. dc_context_new, libdivecompute.c line 1512 <- LIBDIVECOMPUTER + // b. divecomputer_device_open, libdivecompute.c line 1525 + // BT: (most likely not supported with iOS and definitly not supported by flutter_blue_plus) + // I. rfcomm_stream_open, libdivecompute.c line 1420 + // BLE: + // I. ble_packet_open, libdivecompute.c line 1432 + // a. qt_ble_open, qtserialbluetooth.cpp line 303 + // 1. connectToDevice, qt-ble.cpp line 585 + // 2. discoverServices, qt-ble.cpp line 630 + //DONE 3. select_preferred_service, qt-ble.cpp line 638 <- The service which is providing read and write access + //DONE 4. get ClientCharacteristicConfiguration descriptor and write 0x0100 to enable notifications, qt-ble.cpp line 658-677 + // 5. assign BLEObject to iostream, qt-ble.cpp line 689 + // b. dc_custom_open, qtserialbluetooth.cpp line 308 <- LIBDIVECOMPUTER + // 1. dc_iostream_allocate, custom.c line 84 <- LIBDIVECOMPUTER + + final iostream = calloc>(); + _bleOpen(device!, context, iostream); + + log.info('Opening BLE device for ${device.platformName}'); + + return iostream.value; + } + + void _bleOpen( + BluetoothDevice device, + ffi.Pointer> context, + ffi.Pointer> iostream, + ) async { + int status = dc_status_t.DC_STATUS_SUCCESS; + _bleObject.selectPreferredService(device, status); + handleResult(status, 'select preferred service'); + + _bleObject.enableNotifications(status); + handleResult(status, 'enable notifications'); + + final io_str = calloc(); + final vtable = calloc(); + final ffi_iostream = calloc>(); + ffi_iostream.value = io_str; + ffi_iostream.value.ref.vtable = vtable; + + ffi_iostream.value.ref.context = context.value; + ffi_iostream.value.ref.transport = dc_transport_t.DC_TRANSPORT_BLE; + ffi_iostream.value.ref.vtable.ref.set_timeout = ffi.Pointer.fromFunction( + BLEObject.set_timeout, dc_status_t.DC_STATUS_TIMEOUT); + ffi_iostream.value.ref.vtable.ref.set_break = ffi.nullptr; + ffi_iostream.value.ref.vtable.ref.set_dtr = ffi.nullptr; + ffi_iostream.value.ref.vtable.ref.set_rts = ffi.nullptr; + ffi_iostream.value.ref.vtable.ref.get_lines = ffi.nullptr; + ffi_iostream.value.ref.vtable.ref.get_available = ffi.nullptr; + ffi_iostream.value.ref.vtable.ref.configure = ffi.nullptr; + ffi_iostream.value.ref.vtable.ref.poll = ffi + .nullptr; //ffi.Pointer.fromFunction(_bleObject.poll, dc_status_t.DC_STATUS_IO); + ffi_iostream.value.ref.vtable.ref.read = + ffi.Pointer.fromFunction(BLEObject.read, dc_status_t.DC_STATUS_IO); + ffi_iostream.value.ref.vtable.ref.write = + ffi.Pointer.fromFunction(BLEObject.write, dc_status_t.DC_STATUS_IO); + ffi_iostream.value.ref.vtable.ref.ioctl = ffi.nullptr; + ffi_iostream.value.ref.vtable.ref.flush = ffi.nullptr; + ffi_iostream.value.ref.vtable.ref.purge = ffi.nullptr; + ffi_iostream.value.ref.vtable.ref.sleep = ffi.nullptr; + ffi_iostream.value.ref.vtable.ref.close = ffi.nullptr; + + iostream = ffi_iostream.cast>(); + } } diff --git a/lib/types/ble_object.dart b/lib/types/ble_object.dart new file mode 100644 index 0000000..1db9241 --- /dev/null +++ b/lib/types/ble_object.dart @@ -0,0 +1,243 @@ +import 'dart:async'; +import 'dart:ffi'; +import 'dart:typed_data'; + +import 'package:dive_computer/framework/utils/utils.dart'; +import 'package:flutter/services.dart'; +import 'package:logging/logging.dart' as logging; +import 'package:flutter_blue_plus/flutter_blue_plus.dart'; + +import './dc_iostream_t.dart' as io; +import '../framework/dive_computer_ffi_bindings_generated.dart'; + +final log = logging.Logger('DiveComputerFfi'); + +// Unfortunatly we need to use global variables here because +// Pointer.fromFunction is accepting only static functions +int _timeout = 12000; // 12 seconds from BLE_TIMEOUT +BluetoothService? _prefeeredService; +final List _receivedPackets = []; + +class BLEObject { + final List>> _characteristicSubscriptions = []; + + void dispose() { + for (StreamSubscription> subscription + in _characteristicSubscriptions) { + subscription.cancel(); + } + } + + // ignore: non_constant_identifier_names + static int set_timeout(Pointer iostream, int timeout) { + log.info('Set BLE timeout to $timeout'); + _timeout = timeout; + + return dc_status_t.DC_STATUS_SUCCESS; + } + + static int write(Pointer iostream, Pointer data, + int size, Pointer actual) { + //Subsurface qt-ble.cpp write, line 300 + + if (actual != nullptr) { + actual.value = 0; + } + + if (_prefeeredService == null) { + handleResult(dc_status_t.DC_STATUS_IO, 'No preferred service'); + } + + if (_receivedPackets.isNotEmpty) { + log.info('Write HIT with still incoming packets in queue'); + do { + _receivedPackets.removeAt(0); + } while (_receivedPackets.isNotEmpty); + } + + for (BluetoothCharacteristic characteristic + in _prefeeredService!.characteristics) { + if (!_isWriteCharacteristic(characteristic)) { + continue; + } + + Uint8List bytes = data.cast().asTypedList(size); + + characteristic.write(bytes, + withoutResponse: characteristic.properties.writeWithoutResponse); + if (actual != nullptr) { + actual.value = size; + } + + return dc_status_t.DC_STATUS_SUCCESS; + } + + return dc_status_t.DC_STATUS_IO; + } + + static int read(Pointer iostream, Pointer data, + int size, Pointer actual) { + //Subsurface qt-ble.cpp read, line 350 + + if (actual != nullptr) { + actual.value = 0; + } + + final status = poll(_timeout); + if (status != dc_status_t.DC_STATUS_SUCCESS) { + return status; + } + + if (_receivedPackets.isNotEmpty) { + Uint8List packet = _receivedPackets.removeAt(0); + + // Did we get more than asked for? + // + // Put back the left-over at the beginning of the + // received packet list, and truncate the packet + // we got to just the part asked for. + if (packet.length > size) { + _receivedPackets.insert(0, packet.sublist(size)); + packet = packet.sublist(0, size); + } + + Uint8List dataAsList = data.cast().asTypedList(packet.length); + dataAsList.setRange(0, packet.length, packet); + + if (actual != nullptr) { + actual.value += packet.length; + } + + return dc_status_t.DC_STATUS_SUCCESS; + } + + return dc_status_t.DC_STATUS_SUCCESS; + } + + static int poll(int timeout) { + //Subsurface qt-ble.cpp poll, line 332 + + if (_receivedPackets.isEmpty) { + final characteristics = _prefeeredService!.characteristics; + if (characteristics.isEmpty) { + return dc_status_t.DC_STATUS_IO; + } + + _waitFor(_receivedPackets.isEmpty, timeout); + if (_receivedPackets.isEmpty) { + return dc_status_t.DC_STATUS_TIMEOUT; + } + } + + return dc_status_t.DC_STATUS_SUCCESS; + } + + void selectPreferredService(BluetoothDevice device, int status) async { + //Subsurface qt-ble.cpp select_preferred_service + + final services = await device.discoverServices(); + for (BluetoothService service in services) { + bool hasRead = false; + bool hasWrite = false; + for (BluetoothCharacteristic characteristic in service.characteristics) { + // Check for read access + if (_isReadCharacteristic(characteristic)) { + hasRead = true; + } + // Check for write access + if (_isWriteCharacteristic(characteristic)) { + hasWrite = true; + } + } + + if (hasWrite && hasRead) { + log.info('Set preferred service ${service.serviceUuid.toString()}'); + _prefeeredService = service; + break; + } + } + + status = dc_status_t.DC_STATUS_SUCCESS; + if (_prefeeredService == null) { + status = dc_status_t.DC_STATUS_IO; + } + + if (_prefeeredService != null) { + for (BluetoothCharacteristic characteristic + in _prefeeredService!.characteristics) { + characteristic.onValueReceived.listen((value) { + _characteristcStateChanged(characteristic, Uint8List.fromList(value)); + }); + } + } + } + + void enableNotifications(int status) async { + //Subsurface qt-ble.cpp line 650 + //Currently we ignore Heinrichs Weikamp dive computer + + if (_prefeeredService == null) { + status = dc_status_t.DC_STATUS_IO; + return; + } + + for (BluetoothCharacteristic characteristic + in _prefeeredService!.characteristics) { + if (!_isReadCharacteristic(characteristic)) { + continue; + } + + final descriptors = characteristic.descriptors; + BluetoothDescriptor clientDescriptor = descriptors.first; + + // Get client characteristic configuration descriptor + for (BluetoothDescriptor descriptor in descriptors) { + if (descriptor.uuid == Guid("00002902-0000-1000-8000-00805f9b34fb")) { + clientDescriptor = descriptor; + break; + } + } + + try { + // writing 0x0100 to the client characteristic configuration descriptor + //await clientDescriptor.write([0x0100]); + log.info( + 'Set notify value for ${characteristic.characteristicUuid.toString()}'); + final result = await characteristic.setNotifyValue(true); + status = + result ? dc_status_t.DC_STATUS_SUCCESS : dc_status_t.DC_STATUS_IO; + } catch (e, s) { + status = dc_status_t.DC_STATUS_IO; + } + } + } + + static void _waitFor(bool expression, int timeout) async { + //Subsurface qt-ble.cpp WAITFOR, line 36 + + if (expression) { + return; + } + + final stopwatch = Stopwatch()..start(); + do { + await Future.delayed(const Duration(milliseconds: 10)); + } while (!expression && stopwatch.elapsedMilliseconds < timeout); + } + + static bool _isReadCharacteristic(BluetoothCharacteristic characteristic) { + return characteristic.properties.notify || + characteristic.properties.indicate; + } + + static bool _isWriteCharacteristic(BluetoothCharacteristic characteristic) { + return characteristic.properties.write || + characteristic.properties.writeWithoutResponse; + } + + void _characteristcStateChanged( + BluetoothCharacteristic characteristic, Uint8List value) { + //Subsurface qt-ble.cpp _characteristcStateChanged, line 65 + _receivedPackets.add(value); + } +} diff --git a/lib/types/computer.dart b/lib/types/computer.dart index bbabdad..19bf459 100644 --- a/lib/types/computer.dart +++ b/lib/types/computer.dart @@ -1,9 +1,13 @@ class Computer { - Computer(this.vendor, this.product, {this.transports = const []}); - final String vendor, product; final List transports; + Computer( + this.vendor, + this.product, { + this.transports = const [], + }); + @override String toString() => '$vendor $product [${transports.map((t) => t.name).join(', ')}]'; diff --git a/lib/types/dc_iostream_t.dart b/lib/types/dc_iostream_t.dart new file mode 100644 index 0000000..0d8559e --- /dev/null +++ b/lib/types/dc_iostream_t.dart @@ -0,0 +1,84 @@ +// ignore_for_file: camel_case_types, non_constant_identifier_names + +import 'dart:ffi'; + +import '../framework/dive_computer_ffi_bindings_generated.dart'; + +final class dc_iostream_vtable_t extends Struct { + @Uint64() + external int size; + + external Pointer< + NativeFunction< + Int32 Function(Pointer iostream, Int32 timeout)>> + set_timeout; + external Pointer< + NativeFunction< + dc_status_t Function( + Pointer iostream, Uint32 value)>> set_break; + external Pointer< + NativeFunction< + dc_status_t Function( + Pointer iostream, Uint32 value)>> set_dtr; + external Pointer< + NativeFunction< + dc_status_t Function( + Pointer iostream, Uint32 value)>> set_rts; + external Pointer< + NativeFunction< + dc_status_t Function( + Pointer iostream, Pointer value)>> + get_lines; + external Pointer< + NativeFunction< + dc_status_t Function( + Pointer iostream, Pointer value)>> + get_available; + external Pointer< + NativeFunction< + dc_status_t Function( + Pointer iostream, + Uint32 baudrate, + Uint32 databits, + dc_parity_t parity, + dc_stopbits_t stopbits, + dc_flowcontrol_t flowcontrol)>> configure; + external Pointer< + NativeFunction< + Int32 Function(Pointer iostream, Int32 value)>> poll; + external Pointer< + NativeFunction< + Int32 Function(Pointer iostream, Pointer data, + Uint64 size, Pointer actual)>> read; + external Pointer< + NativeFunction< + Int32 Function(Pointer iostream, Pointer data, + Uint64 size, Pointer actual)>> write; + external Pointer< + NativeFunction< + dc_status_t Function(Pointer iostream, Int32 request, + Pointer data, Uint64 size)>> ioctl; + external Pointer< + NativeFunction iostream)>> + flush; + external Pointer< + NativeFunction< + dc_status_t Function( + Pointer iostream, dc_direction_t direction)>> + purge; + external Pointer< + NativeFunction< + dc_status_t Function( + Pointer iostream, Uint32 milliseconds)>> sleep; + external Pointer< + NativeFunction iostream)>> + close; +} + +final class dc_iostream_t extends Struct { + external Pointer vtable; + external Pointer context; + + @Int32() + external int transport; +} diff --git a/pubspec.yaml b/pubspec.yaml index 128faad..86345bd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -18,6 +18,7 @@ dependencies: logging: '>=1.2.0 <2.0.0' path: '>=1.8.0 <2.0.0' plugin_platform_interface: '>=2.0.2 <3.0.0' + flutter_blue_plus: ^1.31.15 dev_dependencies: ffigen: ">=10.0.0 <12.0.0"