Untitled

 avatar
unknown
plain_text
a year ago
9.1 kB
4
Indexable
import 'dart:developer';
import 'dart:async';
import 'dart:io';
import 'package:meta/meta.dart';

import 'package:flutter/material.dart';
import 'package:self_science_station/model/metric_model.dart';
import 'package:self_science_station/model/sensor_model.dart';
import 'package:self_science_station/utils/utils.dart';
import 'package:self_science_station/sensor/polar_api.dart';
import 'package:self_science_station/provider/sensor_provider.dart';
import 'package:provider/provider.dart';

import 'package:polar/polar.dart';

// SensorInstance is the class for all sensor instances and parent-class for all mobile SDKs/APIs

class SensorInstance {
  //final sp = Provider.of<SensorProvider>(context, listen: false);

  // // The list storing all the child instances
  // static List<SensorInstance> sensorInstances = [];

  // // Lists of the Sensor Model details -- no need for this i believe
  // final List<SensorInstanceModel> _sensorInstances = [];
  // final List<SensorTypeModel> _sensorTypes = [];
  bool isConnected = false;
  bool isConnecting = false;

  // InstanceModel and TypeModel are final because their values should not be change once the instance is created
  final SensorProvider sp;
  final SensorInstanceModel _sInstance;
  final SensorTypeModel _sType;

  // Allows for executing callbacks for a given sensor state
  final Map<SensorState, Function> _callbacks = {};

  // SensorState and Availability will describe the accessibility of the sensor
  SensorState sState = SensorState.init;
  SensorAvailability sAvailability = SensorAvailability.init;

  // Constructor (without subclass-instance creation)
  SensorInstance(this.sp, this._sInstance, this._sType) {
    print("New Sensor created in SensorProvider without any subclass assigned");
    print(
        "New sensor instance instance created: name ${_sType.vendor} ${_sType.name}: ${_sInstance.deviceID}");
    _onSensorChange();
  }

  // Factory constructor creating a subclass instance for the particular sensor
  factory SensorInstance.create(SensorProvider sProvider,
      SensorInstanceModel sInstance, SensorTypeModel sType) {
    // try {
    // // Add sensor type to the list of sensor types
    // if (_sensorTypes.any((s) => s.id == sType.id)) {
    //   // Find the sensor in the list and remove it -- why is this step necessary? to sort list?
    //   _sensorTypes.removeWhere((s) => s.id == sType.id);
    // }
    // _sensorTypes.add(sType);
    // // Add sensor instance to the list of sensor instances
    // if (_sensorInstances.any((s) => s.deviceID == sInstance.deviceID)) {
    //   // Find the sensor in the list and remove it -- why is this step necessary? to sort list?
    //   _sensorInstances.removeWhere((s) => s.deviceID == sInstance.deviceID);
    // }
    // _sensorInstances.add(sInstance);
    // } catch (e) {
    //   log('Error parsing sensor: ${e.toString()}');
    // }

    // Initialize sensor instance with state and device information
    SensorState sState = SensorState.disconnected;
    SensorAvailability sAvailability = SensorAvailability.available;
    String sVendor = sType.vendor;
    String sDeviceName = sType.name;
    String sDeviceID = sInstance.deviceID;

    // Create and return a child-instance for the particular sensor based on Vendor
    SensorInstance sDevice;
    switch (sVendor) {
      case "fitbit":
        // TODO:
        log("Fitbit Sensor");
        log("Fitbit is webbased");
        sDevice = SensorInstance(sProvider, sInstance, sType);
        break;

      case "withings":
        // TODO:
        log("Withings Sensor");
        log("Withings is webbased");
        sDevice = SensorInstance(sProvider, sInstance, sType);
        break;

      case "polar":
        log("Polar Sensor");
        sDevice = PolarAPI(
            sProvider, sInstance, sType, sInstance.deviceID, sType.name);
        break;

      default:
        // TODO:
        log("This sensor has not been initialized correctly.");
        sDevice = SensorInstance(sProvider, sInstance, sType);
        break;
    }
    // // Add the new sensor instance to the static list of instances
    //SensorProvider.addSensorInstanceToList(sDevice);
    return sDevice;
  }

  // // A method to add a child instance to the list
  // static void addSensorInstanceToList(SensorInstance sensor) {
  //   sensorInstances.add(sensor);
  // }
  void _onSensorChange() {
    sp.updateEverything();
  }

  Future<bool> startRecording(String? recordingIdentifier) {
    print(
        "Recording from sensor ${_sInstance.id} with ID $recordingIdentifier");
    onRecordingStarted();
    return Future.value(true);
  }

  Future<bool> stopRecording() {
    print("Stopping recording from sensor ${_sInstance.id}");
    onRecordingStopped();
    return Future.value(true);
  }

  Future<void> emptyRecordings() async {
    print('Emptying recordings for sensor ${_sInstance.id}');
    return Future.value();
  }

  Future<bool> isRecording() {
    print("Checking if recording from sensor ${_sInstance.id}");
    return Future.value(false);
  }

  Future<bool> connectSensor() async {
    print("Connecting sensor ${_sInstance.id}");
    onConnected();
    return Future.value(true);
  }

  // Tries to connect, waits for specified timeout, bails if hasn't connected yet
  void connectSensorWithTimeout(Duration timeout) async {
    print("Connecting sensor ${_sInstance.id} with timeout $timeout");
    onConnecting();
    Future.delayed(timeout, () {
      if (!isConnected) {
        onConnectFail();
      }
    });
  }

  Future<bool> disconnectSensor() {
    print("Disconnecting sensor ${_sInstance.id}");
    onDisconnected();
    return Future.value(true);
  }

  Future<void> resetSensor() {
    print("Resetting sensor ${_sInstance.id}");
    return Future.value();
  }

  StreamSubscription<dynamic> scanForSensor(Function onDeviceFound) {
    print("Scanning for sensor ${_sInstance.id}");
    onDeviceFound();
    return const Stream.empty().listen((event) {});
  }

  @protected
  void onRecordingStarted() {
    print("Started recording from sensor ${_sInstance.id}");
    final prevState = sState;
    if (sState == SensorState.connected_idle) {
      sState = SensorState.connected_acquiring;
    } else if (sState == SensorState.disconnected) {
      sState = SensorState.disconnected_acquiring;
    }
    handleStateChange(prevState, sState);
  }

  @protected
  void onRecordingStopped() {
    print("Stopped recording from sensor ${_sInstance.id}");
    final prevState = sState;
    if (sState == SensorState.connected_acquiring) {
      sState = SensorState.connected_idle;
    } else if (sState == SensorState.disconnected_acquiring) {
      sState = SensorState.disconnected;
    }
    handleStateChange(prevState, sState);
  }

  @protected
  void onConnecting() {
    print("Connecting sensor ${_sInstance.id}");
    isConnecting = true;
    isConnected = false;
    final prevState = sState;
    sState = SensorState.connecting;
    handleStateChange(prevState, sState);
  }

  @protected
  void onConnected() {
    print("Connected sensor ${_sInstance.id}");
    isConnected = true;
    isConnecting = false;
    final prevState = sState;
    sState = SensorState.connected_idle;
    handleStateChange(prevState, sState);
  }

  @protected
  void onDisconnected() {
    print("Disconnected sensor ${_sInstance.id}");
    isConnected = false;
    isConnecting = false;
    final prevState = sState;
    sState = SensorState.disconnected;
    handleStateChange(prevState, sState);
  }

  @protected
  void onConnectFail() {
    print("Failed to connect to sensor ${_sInstance.id}");
    onDisconnected();
  }

  @protected
  void onSensorError() {
    print("Error on sensor ${_sInstance.id}!");
    isConnected = false;
    isConnecting = false;
    final prevState = sState;
    sState = SensorState.error;
    handleStateChange(prevState, sState);
  }

  void handleStateChange(SensorState prevState, SensorState newState) {
    // Call the callback for the new state
    if (_callbacks.containsKey(newState)) {
      _callbacks[newState]!(prevState, newState);
    }
    sp.updateEverything();
  }

  // Trigger a runtime callback when the sensor enters any state in the list provided
  void registerCallback(List<SensorState> states, Function callback) {
    for (final state in states) {
      _callbacks[state] = callback;
    }
  }

  Future<List<int>> fetchData(String? recordingIdentifier) {
    print(
        'Fetching from sensor ${_sInstance.id} for recording $recordingIdentifier');
    return Future.value([]);
  }

  SensorInstanceModel getSensorInstance() {
    return _sInstance;
  }

  SensorTypeModel getTypeModel() {
    return _sType;
  }

  void checkSensorAvailability() {
    //TODO:
    print("Checking Sensor Availability");
  }
}

// Structs

enum SensorState {
  connecting,
  connected_idle,
  connected_acquiring,
  disconnected_acquiring, // offline acquisition
  disconnected,
  error,
  init
}

enum SensorAvailability {
  available, // ready for use
  unavailable, // in use
  faulty, // cannot be used
  init
}
Editor is loading...
Leave a Comment