carp_mobile_sensing 0.30.4
carp_mobile_sensing: ^0.30.4 copied to clipboard

Mobile Sensing Framework for Flutter. A software framework for collecting sensor data from the phone and attached wearable devices via probes. Can be extended.

example/lib/main.dart

/*
 * Copyright 2021 Copenhagen Center for Health Technology (CACHET) at the
 * Technical University of Denmark (DTU).
 * Use of this source code is governed by a MIT-style license that can be
 * found in the LICENSE file.
 */
import 'package:carp_core/carp_core.dart';
import 'package:carp_mobile_sensing/carp_mobile_sensing.dart';
import 'package:flutter/material.dart';

void main() => runApp(CARPMobileSensingApp());

class CARPMobileSensingApp extends StatelessWidget {
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'CARP Mobile Sensing Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: ConsolePage(title: 'CARP Mobile Sensing Demo'),
    );
  }
}

class ConsolePage extends StatefulWidget {
  final String title;
  ConsolePage({Key? key, required this.title}) : super(key: key);
  Console createState() => Console();
}

/// A simple UI with a console that logs/prints the sensed data in a json format.
class Console extends State<ConsolePage> {
  String _log = '';
  Sensing? sensing;

  void initState() {
    super.initState();
    sensing = Sensing();
    Settings().init().then((future) {
      sensing!.init().then((future) {
        log('Setting up study protocol: ${sensing!.protocol}');
      });
    });
  }

  void dispose() {
    sensing!.stop();
    super.dispose();
  }

  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: SingleChildScrollView(
        child: StreamBuilder(
          stream: sensing?.controller?.data,
          builder: (context, AsyncSnapshot<DataPoint> snapshot) {
            if (snapshot.hasData) _log += '${toJsonString(snapshot.data!)}\n';
            return Text(_log);
          },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: restart,
        tooltip: 'Restart study & probes',
        child: sensing!.isRunning ? Icon(Icons.pause) : Icon(Icons.play_arrow),
      ),
    );
  }

  void log(String msg) {
    setState(() {
      _log += '$msg\n';
    });
  }

  void clearLog() {
    setState(() {
      _log += '';
    });
  }

  void restart() {
    setState(() {
      if (sensing!.isRunning) {
        sensing!.pause();
        log('\nSensing paused ...');
      } else {
        sensing!.resume();
        log('\nSensing resumed ...');
      }
    });
  }
}

/// This class handles sensing with the following business logic flow:
///
///  * create a [StudyProtocol]
///  * deploy the protocol
///  * register devices
///  * get the deployment configuration
///  * mark the deployment successful
///  * create and initialize a [StudyDeploymentController]
///  * start/pause/resume/stop sensing via this study controller
///
/// This example is useful for creating a Business Logical Object (BLOC) in a
/// Flutter app. See e.g. the CARP Mobile Sensing App.
class Sensing {
  StudyProtocol? protocol;
  StudyDeploymentStatus? _status;
  StudyDeploymentController? controller;

  /// Initialize sensing.
  Future init() async {
    Settings().debugLevel = DebugLevel.DEBUG;

    // get the protocol from the local protocol manager (defined below)
    protocol = await LocalStudyProtocolManager().getStudyProtocol('ignored');

    // deploy this protocol using the on-phone deployment service
    _status =
        await SmartphoneDeploymentService().createStudyDeployment(protocol!);

    String studyDeploymentId = _status!.studyDeploymentId;
    String deviceRolename = _status!.masterDeviceStatus!.device.roleName;

    // create and configure a client manager for this phone
    SmartPhoneClientManager client = SmartPhoneClientManager();
    await client.configure();

    controller = await client.addStudy(studyDeploymentId, deviceRolename);

    // configure the controller
    await controller!.configure(
      privacySchemaName: PrivacySchema.DEFAULT,
      transformer: ((datum) => datum),
    );
    // controller.resume();

    // listening on the data stream and print them as json to the debug console
    controller!.data.listen((data) => print(toJsonString(data)));
  }

  /// Get the status of the study deployment.
  StudyDeploymentStatus? get status => _status;

  /// Is sensing running, i.e. has the study executor been resumed?
  bool get isRunning =>
      (controller != null) && controller!.executor!.state == ProbeState.resumed;

  /// Resume sensing
  void resume() async => controller!.resume();

  /// Pause sensing
  void pause() async => controller!.pause();

  /// Stop sensing.
  void stop() async => controller!.stop();
}

/// This is a simple local [StudyProtocolManager].
///
/// This class shows how to configure a [StudyProtocol] with [Tigger]s,
/// [TaskDescriptor]s and [Measure]s.
class LocalStudyProtocolManager implements StudyProtocolManager {
  Future initialize() async {}

  /// Create a new CAMS study protocol.
  Future<StudyProtocol> getStudyProtocol(String studyId) async {
    StudyProtocol protocol = StudyProtocol(
      ownerId: 'AB',
      name: 'Track patient movement',
    );

    // Define which devices are used for data collection.
    Smartphone phone = Smartphone();
    DeviceDescriptor eSense = DeviceDescriptor(roleName: 'esense');

    protocol
      ..addMasterDevice(phone)
      ..addConnectedDevice(eSense);

    // add default measures from the SensorSamplingPackage
    protocol.addTriggeredTask(
        ImmediateTrigger(),
        AutomaticTask()
          ..measures = SamplingPackageRegistry().debug().getMeasureList(
            types: [
              // SensorSamplingPackage.ACCELEROMETER,
              // SensorSamplingPackage.GYROSCOPE,
              SensorSamplingPackage.PERIODIC_ACCELEROMETER,
              SensorSamplingPackage.PERIODIC_GYROSCOPE,
              SensorSamplingPackage.PEDOMETER,
              SensorSamplingPackage.LIGHT,
            ],
          ),
        phone);

    // add default measures from the DeviceSamplingPackage
    protocol.addTriggeredTask(
        ImmediateTrigger(),
        AutomaticTask()
          ..measures = SamplingPackageRegistry().debug().getMeasureList(
            types: [
              DeviceSamplingPackage.MEMORY,
              DeviceSamplingPackage.DEVICE,
              DeviceSamplingPackage.BATTERY,
              DeviceSamplingPackage.SCREEN,
            ],
          ),
        phone);

    // add a random trigger to collect device info at random times
    protocol.addTriggeredTask(
        RandomRecurrentTrigger(
          startTime: Time(hour: 22, minute: 00),
          endTime: Time(hour: 22, minute: 30),
          minNumberOfTriggers: 2,
          maxNumberOfTriggers: 8,
        ),
        AutomaticTask()
          ..measures = SamplingPackageRegistry().debug().getMeasureList(
            types: [
              DeviceSamplingPackage.DEVICE,
            ],
          ),
        phone);

    return protocol;
  }

  Future<bool> saveStudyProtocol(String studyId, StudyProtocol protocol) async {
    throw UnimplementedError();
  }
}