initial commit
This commit is contained in:
280
lib/xiao_connector/cubit/xiao_connector_cubit.dart
Normal file
280
lib/xiao_connector/cubit/xiao_connector_cubit.dart
Normal file
@@ -0,0 +1,280 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_blue_plus_windows/flutter_blue_plus_windows.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
import 'package:xiao_pet_tracker/bootstrap.dart';
|
||||
import 'package:xiao_pet_tracker/objectbox.dart';
|
||||
import 'package:xiao_pet_tracker/objectbox.g.dart';
|
||||
import 'package:xiao_pet_tracker/xiao_connector/models/capture_box.dart';
|
||||
import 'package:xiao_pet_tracker/xiao_connector/models/capture_point.dart';
|
||||
import 'package:xiao_pet_tracker/xiao_connector/utils/utils.dart';
|
||||
|
||||
part 'xiao_connector_state.dart';
|
||||
|
||||
class XiaoConnectorCubit extends Cubit<XiaoConnectorState> {
|
||||
XiaoConnectorCubit() : super(const XiaoConnectorState());
|
||||
|
||||
late BluetoothDevice device;
|
||||
List<BluetoothService> _services = [];
|
||||
late BluetoothCharacteristic _senseService;
|
||||
|
||||
late StreamSubscription<List<int>> dataCapturingSubscription;
|
||||
|
||||
final ObjectBox _objectBox = getIt<ObjectBox>();
|
||||
late final Box<CapturePoint> _capturePointsBox;
|
||||
late final Box<CaptureBox> _captureBoxes;
|
||||
|
||||
bool gotRotation = false;
|
||||
bool gotAcceleration = false;
|
||||
bool gotTimeStamp = false;
|
||||
|
||||
int _rotX = 0;
|
||||
int _rotY = 0;
|
||||
int _rotZ = 0;
|
||||
int _accX = 0;
|
||||
int _accY = 0;
|
||||
int _accZ = 0;
|
||||
int _millisecondsSinceEpochSend = 0;
|
||||
String _captureType = '';
|
||||
String _uuid = '';
|
||||
|
||||
bool isRecording = false;
|
||||
|
||||
Future<void> init() async {
|
||||
_capturePointsBox = _objectBox.store.box<CapturePoint>();
|
||||
_captureBoxes = _objectBox.store.box<CaptureBox>();
|
||||
}
|
||||
|
||||
Future<void> connect() async {
|
||||
emit(state.copyWith(status: XiaoConnectorStatus.loading));
|
||||
|
||||
try {
|
||||
// scan for devices for 15 seconds
|
||||
await FlutterBluePlus.startScan(timeout: const Duration(seconds: 15));
|
||||
} catch (e) {
|
||||
emit(state.copyWith(status: XiaoConnectorStatus.failure));
|
||||
throw Exception('Start Scan Error: $e');
|
||||
}
|
||||
|
||||
FlutterBluePlus.scanResults.listen(
|
||||
(results) async {
|
||||
for (final r in results) {
|
||||
if (kDebugMode) {
|
||||
print(r);
|
||||
}
|
||||
|
||||
// set the xiao sense name
|
||||
if (r.device.advName == 'Go Bluetooth 2') {
|
||||
await FlutterBluePlus.stopScan();
|
||||
device = r.device;
|
||||
|
||||
await device.connect();
|
||||
|
||||
try {
|
||||
_services = await device.discoverServices();
|
||||
} catch (e) {
|
||||
emit(state.copyWith(status: XiaoConnectorStatus.failure));
|
||||
throw Exception('Discover Services Error: $e');
|
||||
}
|
||||
|
||||
device.connectionState
|
||||
.listen((BluetoothConnectionState blueState) async {
|
||||
if (blueState == BluetoothConnectionState.disconnected) {
|
||||
debugPrint('DISCONNECTED!');
|
||||
emit(state.copyWith(status: XiaoConnectorStatus.initial));
|
||||
}
|
||||
});
|
||||
|
||||
// device.cancelWhenDisconnected(deviceConnectionStream);
|
||||
emit(state.copyWith(status: XiaoConnectorStatus.connected));
|
||||
}
|
||||
}
|
||||
},
|
||||
onDone: () {
|
||||
debugPrint('DONE');
|
||||
emit(state.copyWith(status: XiaoConnectorStatus.initial));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> setCapturingOn() async {
|
||||
final senseService = _services
|
||||
.where(
|
||||
(s) => s.serviceUuid == Guid('4c534d36-4453-3354-5253-657276696365'),
|
||||
)
|
||||
.first
|
||||
.characteristics
|
||||
.where(
|
||||
(c) =>
|
||||
c.characteristicUuid ==
|
||||
Guid('63617074-7572-696E-6753-657276696365'),
|
||||
)
|
||||
.first;
|
||||
|
||||
await senseService.write([1]);
|
||||
}
|
||||
|
||||
Uint8List int64BigEndianBytes(int value) =>
|
||||
Uint8List(8)..buffer.asByteData().setInt64(0, value);
|
||||
|
||||
Future<void> setTime(int millisSinceEpoch) async {
|
||||
final senseService = _services
|
||||
.where(
|
||||
(s) => s.serviceUuid == Guid('4c534d36-4453-3354-5253-657276696365'),
|
||||
)
|
||||
.first
|
||||
.characteristics
|
||||
.where(
|
||||
(c) =>
|
||||
c.characteristicUuid ==
|
||||
Guid('756E6978-5469-6D65-5374-616D70527374'),
|
||||
)
|
||||
.first;
|
||||
|
||||
final List<int> bytes = int64BigEndianBytes(millisSinceEpoch);
|
||||
await senseService.write(bytes);
|
||||
}
|
||||
|
||||
Future<void> startCapturing() async {
|
||||
_senseService = _services
|
||||
.where(
|
||||
(s) => s.serviceUuid == Guid('4c534d36-4453-3354-5253-657276696365'),
|
||||
)
|
||||
.first
|
||||
.characteristics
|
||||
.where(
|
||||
(c) =>
|
||||
c.characteristicUuid ==
|
||||
Guid('61636365-6c65-7261-7469-6f6e44617461'),
|
||||
)
|
||||
.first;
|
||||
|
||||
dataCapturingSubscription = _senseService.onValueReceived.listen((value) {
|
||||
// rotation
|
||||
if (value.last == 1) {
|
||||
_rotX = fromBytesToInt32(value[0], value[1], value[2], value[3]);
|
||||
_rotY = fromBytesToInt32(value[4], value[5], value[6], value[7]);
|
||||
_rotZ = fromBytesToInt32(value[8], value[9], value[10], value[11]);
|
||||
gotRotation = true;
|
||||
}
|
||||
|
||||
// acceleration
|
||||
if (value.last == 2) {
|
||||
_accX = fromBytesToInt32(value[0], value[1], value[2], value[3]);
|
||||
_accY = fromBytesToInt32(value[4], value[5], value[6], value[7]);
|
||||
_accZ = fromBytesToInt32(value[8], value[9], value[10], value[11]);
|
||||
gotAcceleration = true;
|
||||
}
|
||||
|
||||
if (value.last == 3) {
|
||||
final timeStamp = fromBytesToInt64(
|
||||
value[0],
|
||||
value[1],
|
||||
value[2],
|
||||
value[3],
|
||||
value[4],
|
||||
value[5],
|
||||
value[6],
|
||||
value[7],
|
||||
);
|
||||
_millisecondsSinceEpochSend = timeStamp;
|
||||
gotTimeStamp = true;
|
||||
}
|
||||
|
||||
if (gotAcceleration && gotRotation && gotTimeStamp) {
|
||||
final capturePoint = CapturePoint(
|
||||
type: _captureType,
|
||||
uuid: _uuid,
|
||||
rotationX: _rotX,
|
||||
rotationY: _rotY,
|
||||
rotationZ: _rotZ,
|
||||
accelerationX: _accX,
|
||||
accelerationY: _accY,
|
||||
accelerationZ: _accZ,
|
||||
millisecondsSinceEpochReceived:
|
||||
DateTime.now().toUtc().millisecondsSinceEpoch,
|
||||
millisecondsSinceEpochSend: _millisecondsSinceEpochSend,
|
||||
);
|
||||
emit(state.copyWith(lastCapturedPoint: capturePoint));
|
||||
|
||||
if (isRecording) {
|
||||
_writeToObjectBox(capturePoint: capturePoint);
|
||||
}
|
||||
|
||||
gotAcceleration = false;
|
||||
gotRotation = false;
|
||||
gotTimeStamp = false;
|
||||
}
|
||||
});
|
||||
await setCapturingOn();
|
||||
|
||||
await setTime(DateTime.now().millisecondsSinceEpoch);
|
||||
|
||||
device.cancelWhenDisconnected(dataCapturingSubscription);
|
||||
|
||||
await _senseService.setNotifyValue(true);
|
||||
|
||||
emit(state.copyWith(status: XiaoConnectorStatus.capturing));
|
||||
}
|
||||
|
||||
Future<void> stopCapturing() async {
|
||||
await dataCapturingSubscription.cancel();
|
||||
|
||||
await _senseService.setNotifyValue(false);
|
||||
|
||||
emit(state.copyWith(status: XiaoConnectorStatus.connected));
|
||||
}
|
||||
|
||||
Future<void> disconnectDevice() async {
|
||||
await device.disconnect();
|
||||
_services = [];
|
||||
emit(state.copyWith(status: XiaoConnectorStatus.initial));
|
||||
}
|
||||
|
||||
void toggleRecording({required String captureType}) {
|
||||
isRecording = !isRecording;
|
||||
if (isRecording) {
|
||||
_captureType = captureType;
|
||||
_uuid = const Uuid().v4();
|
||||
_captureBoxes.put(
|
||||
CaptureBox(
|
||||
type: _captureType,
|
||||
uuid: _uuid,
|
||||
startTime: DateTime.now(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _writeToObjectBox({
|
||||
required CapturePoint capturePoint,
|
||||
}) {
|
||||
_capturePointsBox.put(capturePoint);
|
||||
}
|
||||
|
||||
List<CapturePoint> getCapturePointsOfUuid(String uuid) {
|
||||
final query =
|
||||
_capturePointsBox.query(CapturePoint_.uuid.equals(uuid)).build();
|
||||
|
||||
final points = query.find();
|
||||
return points;
|
||||
}
|
||||
|
||||
void deleteRecording(String uuid) {
|
||||
_capturePointsBox.query(CapturePoint_.uuid.equals(uuid)).build().remove();
|
||||
_captureBoxes.query(CaptureBox_.uuid.equals(uuid)).build().remove();
|
||||
}
|
||||
|
||||
List<CaptureBox> getAllCaptureBoxes() {
|
||||
final query = (_captureBoxes.query()
|
||||
..order(CaptureBox_.startTime, flags: Order.descending))
|
||||
.build();
|
||||
final boxes = query.find();
|
||||
return boxes;
|
||||
}
|
||||
|
||||
List<CapturePoint> getAllCapturePoints() => _capturePointsBox.getAll();
|
||||
}
|
||||
40
lib/xiao_connector/cubit/xiao_connector_state.dart
Normal file
40
lib/xiao_connector/cubit/xiao_connector_state.dart
Normal file
@@ -0,0 +1,40 @@
|
||||
part of 'xiao_connector_cubit.dart';
|
||||
|
||||
enum XiaoConnectorStatus {
|
||||
initial,
|
||||
loading,
|
||||
connected,
|
||||
capturing,
|
||||
failure,
|
||||
}
|
||||
|
||||
extension XiaoConnectorStatusX on XiaoConnectorStatus {
|
||||
bool get isInitial => this == XiaoConnectorStatus.initial;
|
||||
bool get isLoading => this == XiaoConnectorStatus.loading;
|
||||
bool get isConnected => this == XiaoConnectorStatus.connected;
|
||||
bool get isCapturing => this == XiaoConnectorStatus.capturing;
|
||||
bool get isFailure => this == XiaoConnectorStatus.failure;
|
||||
}
|
||||
|
||||
final class XiaoConnectorState extends Equatable {
|
||||
const XiaoConnectorState({
|
||||
this.status = XiaoConnectorStatus.initial,
|
||||
this.lastCapturedPoint,
|
||||
});
|
||||
|
||||
final XiaoConnectorStatus status;
|
||||
final CapturePoint? lastCapturedPoint;
|
||||
|
||||
XiaoConnectorState copyWith({
|
||||
XiaoConnectorStatus? status,
|
||||
CapturePoint? lastCapturedPoint,
|
||||
}) {
|
||||
return XiaoConnectorState(
|
||||
status: status ?? this.status,
|
||||
lastCapturedPoint: lastCapturedPoint ?? this.lastCapturedPoint,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [status, lastCapturedPoint];
|
||||
}
|
||||
Reference in New Issue
Block a user