From ec42177b24b47ba68b3ae49634c942c76a9f92d4 Mon Sep 17 00:00:00 2001 From: Arseniy-Movshev Date: Sun, 19 Mar 2023 11:14:04 +0000 Subject: Restructure to split to daemon and qml accessor - add separate subdirs for daemon and qml - add some generic qml boilerplate - add a really lazy 'last value' loader implementation - license the project as gplv3 (add copyright notices and license text) --- CMakeLists.txt | 32 ++++------ LICENSE | 16 ++--- README | 9 +++ daemon/CMakeLists.txt | 12 ++++ daemon/logger.cpp | 105 +++++++++++++++++++++++++++++++ daemon/logger.h | 47 ++++++++++++++ daemon/sensorPlugins/heartrateSensor.cpp | 64 +++++++++++++++++++ daemon/sensorPlugins/heartrateSensor.h | 45 +++++++++++++ daemon/sensorPlugins/stepCounter.cpp | 71 +++++++++++++++++++++ daemon/sensorPlugins/stepCounter.h | 43 +++++++++++++ daemon/sensorlogd.cpp | 34 ++++++++++ healthd.cpp | 24 ------- logger.cpp | 94 --------------------------- logger.h | 37 ----------- qmlplugin/CMakeLists.txt | 16 +++++ qmlplugin/qmldir | 3 + qmlplugin/sensorlogdqmlplugin.cpp | 25 ++++++++ qmlplugin/sensorlogdqmlplugin.h | 27 ++++++++ qmlplugin/stepsDataLoader.cpp | 45 +++++++++++++ qmlplugin/stepsDataLoader.h | 26 ++++++++ sensorPlugins/heartrateSensor.cpp | 53 ---------------- sensorPlugins/heartrateSensor.h | 35 ----------- sensorPlugins/stepCounter.cpp | 61 ------------------ sensorPlugins/stepCounter.h | 33 ---------- sensorlogd.cpp | 24 ------- 25 files changed, 592 insertions(+), 389 deletions(-) create mode 100644 README create mode 100644 daemon/CMakeLists.txt create mode 100644 daemon/logger.cpp create mode 100644 daemon/logger.h create mode 100644 daemon/sensorPlugins/heartrateSensor.cpp create mode 100644 daemon/sensorPlugins/heartrateSensor.h create mode 100644 daemon/sensorPlugins/stepCounter.cpp create mode 100644 daemon/sensorPlugins/stepCounter.h create mode 100644 daemon/sensorlogd.cpp delete mode 100644 healthd.cpp delete mode 100644 logger.cpp delete mode 100644 logger.h create mode 100644 qmlplugin/CMakeLists.txt create mode 100644 qmlplugin/qmldir create mode 100644 qmlplugin/sensorlogdqmlplugin.cpp create mode 100644 qmlplugin/sensorlogdqmlplugin.h create mode 100644 qmlplugin/stepsDataLoader.cpp create mode 100644 qmlplugin/stepsDataLoader.h delete mode 100644 sensorPlugins/heartrateSensor.cpp delete mode 100644 sensorPlugins/heartrateSensor.h delete mode 100644 sensorPlugins/stepCounter.cpp delete mode 100644 sensorPlugins/stepCounter.h delete mode 100644 sensorlogd.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index c4c22c1..881247f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,27 +5,19 @@ project(sensorlogd ) find_package(ECM REQUIRED NO_MODULE) +find_package(AsteroidApp REQUIRED) -set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ASTEROID_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake) +set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ASTEROID_MODULE_PATH}) -find_package(Qt5 COMPONENTS Core DBus Qml Positioning Sensors REQUIRED) +include(FeatureSummary) +include(GNUInstallDirs) +include(ECMFindQmlModule) +include(ECMGeneratePkgConfigFile) +include(AsteroidCMakeSettings) +include(AsteroidCMakeSettings) -add_executable(healthd - sensorlogd.cpp - logger.cpp - logger.h - sensorPlugins/stepCounter.cpp - sensorPlugins/stepCounter.h - sensorPlugins/heartrateSensor.cpp - sensorPlugins/heartrateSensor.h -) -set_target_properties(healthd PROPERTIES AUTOMOC ON) -#add_compile_definitions(Q_DECLARE_PRIVATE_SUPPORTS_UNIQUE_PTR=${Q_DECLARE_PRIVATE_SUPPORTS_UNIQUE_PTR}) -target_link_libraries(healthd PRIVATE Qt5::Core Qt5::DBus Qt5::Qml Qt5::Positioning Qt5::Sensors) -install(TARGETS healthd) -# configure_file( -# "org.asteroid.steroid.trackrecorder.service.in" -# "org.asteroid.steroid.trackrecorder.service" -# ) -# install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.asteroid.steroid.trackrecorder.service DESTINATION /usr/lib/systemd/user) +find_package(Qt5 COMPONENTS Core DBus Qml Positioning Sensors REQUIRED) + +add_subdirectory(daemon) +add_subdirectory(qmlplugin) diff --git a/LICENSE b/LICENSE index ef7e7ef..f288702 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ -GNU GENERAL PUBLIC LICENSE + GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 - Copyright (C) 2007 Free Software Foundation, Inc. + Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -631,8 +631,8 @@ to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. - {one line to give the program's name and a brief idea of what it does.} - Copyright (C) {year} {name of author} + + Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -645,14 +645,14 @@ the "copyright" line and a pointer to where the full notice is found. GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program. If not, see . + along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - {project} Copyright (C) {year} {fullname} + Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. @@ -664,11 +664,11 @@ might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see -. +. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read -. +. diff --git a/README b/README new file mode 100644 index 0000000..e082a4e --- /dev/null +++ b/README @@ -0,0 +1,9 @@ +# Asteroid-sensorlogd +asteroid-sensorlogd is an extensible sensor logger which is intended for health tracking, which depends on nemo's MCE and Asteroid's QtSensors which was modified to add heartrate capability. +The project comes as two parts: the sensor logging daemon, which runs in the background to trigger sensor recordings, and the qml module which provides easy access to it. There is currently no facility for the data to be accessible to other languages. +## Recording triggers and power management +Sensorlogd will only record data when the CPU is already running. It does not schedule any wakeups, so if your device isn't woken up already, there will be no recordings. This is intended as a crude form of wear detection and as a power-saving measure. Better solutions can probably be implemented. + +When sensorlogd receives the `displayOn` signal from MCE, it assumes this means 'the system has come out of suspend, so the system clock has significantly checked since the last time the daemon was aware of the time'. Each sensor plugin will then check if enough time has elapsed between readings. +## File format +For each given sensor, sensorlogd will create log files named `[year]-[month]-[day].log`, eg. `2023-03-12.log`. Each day a new file will be created. This structure is intended to make it fast to retrieve data for a single day with minimal seeking. diff --git a/daemon/CMakeLists.txt b/daemon/CMakeLists.txt new file mode 100644 index 0000000..7d38132 --- /dev/null +++ b/daemon/CMakeLists.txt @@ -0,0 +1,12 @@ +add_executable(healthd + sensorlogd.cpp + logger.cpp + logger.h + sensorPlugins/stepCounter.cpp + sensorPlugins/stepCounter.h + sensorPlugins/heartrateSensor.cpp + sensorPlugins/heartrateSensor.h +) +set_target_properties(healthd PROPERTIES AUTOMOC ON) +#add_compile_definitions(Q_DECLARE_PRIVATE_SUPPORTS_UNIQUE_PTR=${Q_DECLARE_PRIVATE_SUPPORTS_UNIQUE_PTR}) +target_link_libraries(healthd PRIVATE Qt5::Core Qt5::DBus Qt5::Qml Qt5::Positioning Qt5::Sensors) diff --git a/daemon/logger.cpp b/daemon/logger.cpp new file mode 100644 index 0000000..93f764f --- /dev/null +++ b/daemon/logger.cpp @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2023 Arseniy Movshev + * This file is part of sensorlogd, a sensor logger for the AsteroidOS smartwatch OS. + * + * sensorlogd is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * sensorlogd is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "logger.h" + +#include "sensorPlugins/stepCounter.h" +#include "sensorPlugins/heartrateSensor.h" + +Logger::Logger(QObject *parent) : + QObject(parent){ + m_iface = new QDBusInterface("com.nokia.mce","/com/nokia/mce/signal", "com.nokia.mce.signal", QDBusConnection::systemBus()); + QSettings settings; + +//intialise HRM + if (heartrateSensorEnabled) { //add check for HRM + m_heartrateSensor = new HeartrateSensorPlugin(this,settings.value("stepsInterval",600000).toInt()); + } + +//initialise step counter + if (stepCounterEnabled) { //add check for step sensor + m_stepCounter = new StepCounterPlugin(this,settings.value("stepsInterval",600000).toInt()); + } + + if(!m_iface->isValid()) { + qDebug() << "interface is not valid"; + qDebug() << m_iface->lastError(); + } + if(connect(m_iface, SIGNAL(display_status_ind(QString)), this, SLOT(displayOn(QString)))) { //this fires when the display turns on + qDebug() << "healthd connected display_status signal to slot"; + } + qDebug() << "healthd sensors logger initialised"; +} + +void Logger::displayOn(QString displayState) { + if (displayState == "off") + return; + qDebug() << "display on detected"; + uint currTime = QDateTime::currentMSecsSinceEpoch(); + + if (heartrateSensorEnabled) { + m_heartrateSensor->timeUpdate(); + } + + if (stepCounterEnabled) { + m_stepCounter->timeUpdate(); + } +} + +void fileAddRecord(QString sensorPrefix, QString logdata, QDateTime recordTime) { //adds a record to today's log file for the given sensor + qDebug() << fileNameForDate(recordTime.date(), sensorPrefix); + QFile file(fileNameForDate(recordTime.date(), sensorPrefix)); + if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) { + qDebug() << "failed to open file"; + return; + } + file.seek(file.size()); + QTextStream out(&file); + out << QString::number(recordTime.currentSecsSinceEpoch()) + ":" + logdata + "\n"; + file.close(); +} +bool dayFileExists(QString sensorPrefix, QDateTime dateTime) { + return QFile::exists(fileNameForDate(dateTime.date(), sensorPrefix)); +} + +QStringList fileGetPrevRecord(QString sensorPrefix, QDateTime recordTime) { + qDebug() << fileNameForDate(recordTime.date(), sensorPrefix); + QFile file(fileNameForDate(recordTime.date(), sensorPrefix)); + if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) { + qDebug() << "failed to open file"; + return {0,0}; + } + QTextStream inStream(&file); + QString line; + int i; + while(!inStream.atEnd()) + { + line = inStream.readLine(); + qDebug() << line; + i++; + } + file.close(); + return line.split(":"); +} + +QString fileNameForDate(QDate date, QString prefix) { + return QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + "/asteroid-healthloggerd/" + prefix + "/" + date.toString("yyyy-MM-dd.log"); +} diff --git a/daemon/logger.h b/daemon/logger.h new file mode 100644 index 0000000..14d097f --- /dev/null +++ b/daemon/logger.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2023 Arseniy Movshev + * This file is part of sensorlogd, a sensor logger for the AsteroidOS smartwatch OS. + * + * sensorlogd is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * sensorlogd is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + */ + +#ifndef LOGGER_H +#define LOGGER_H + +#include +#include +#include +#include +#include +#include + +#include "sensorPlugins/stepCounter.h" +#include "sensorPlugins/heartrateSensor.h" + +class Logger : public QObject +{ + Q_OBJECT +public: + explicit Logger(QObject *parent = 0); + virtual ~Logger() {}; + +private slots: + void displayOn(QString displayState); + +private: + QDBusInterface *m_iface; + bool heartrateSensorEnabled = true; + HeartrateSensorPlugin *m_heartrateSensor; + bool stepCounterEnabled = true; + StepCounterPlugin *m_stepCounter; + +}; + void fileAddRecord(QString sensorPrefix, QString logdata, QDateTime recordTime = QDateTime::currentDateTime()); //adds a record to today's log file for the given sensor + bool dayFileExists(QString sensorPrefix, QDateTime date = QDateTime::currentDateTime()); //check if today has a log file for the given sensor + QStringList fileGetPrevRecord(QString sensorPrefix, QDateTime recordTime = QDateTime::currentDateTime()); //works backwards to find the last record in today's file before the given time - returns nothing if no file is found. + QString fileNameForDate(QDate date, QString prefix); + +#endif // LOGGER_H diff --git a/daemon/sensorPlugins/heartrateSensor.cpp b/daemon/sensorPlugins/heartrateSensor.cpp new file mode 100644 index 0000000..cf3bfb4 --- /dev/null +++ b/daemon/sensorPlugins/heartrateSensor.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2023 Arseniy Movshev + * This file is part of sensorlogd, a sensor logger for the AsteroidOS smartwatch OS. + * + * sensorlogd is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * sensorlogd is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + */ + + +#include +#include +#include +#include +#include + +#include "../logger.h" + +#include "heartrateSensor.h" + +HeartrateSensorPlugin::HeartrateSensorPlugin(QObject *parent, int initInterval) : + QObject(parent){ + interval = initInterval; + + hrmSensor = new QHrmSensor(this); + connect(hrmSensor,SIGNAL(readingChanged()),this,SLOT(finishRecording())); + + qDebug() << "heartrate sensor is enabled. interval is (ms) " << interval; + recordIntervalTimer = new QTimer(this); + connect(recordIntervalTimer,SIGNAL(timeout()),this,SLOT(triggerRecording())); + recordIntervalTimer->setSingleShot(true); + recordIntervalTimer->start(interval); + lastRecordTime = QDateTime::currentDateTime(); +} + +void HeartrateSensorPlugin::timeUpdate() { + uint elapsed = QDateTime::currentMSecsSinceEpoch() - lastRecordTime.toMSecsSinceEpoch(); + qDebug() << "time until next steps recording" << recordIntervalTimer->remainingTime() << " elapsed = " << elapsed << " lastRecordTime " << lastRecordTime.toMSecsSinceEpoch(); + if (elapsed > interval) { //if too much time has passed, reset the timer and record + triggerRecording(); + lastRecordTime = QDateTime::currentDateTime(); + } else { //otherwise, restart the timer and compensate for time spent in suspend + recordIntervalTimer->start(interval - elapsed); + } +} + +void HeartrateSensorPlugin::triggerRecording() { + qDebug() << "heartrate interval recording"; + recordIntervalTimer->start(interval); + hrmSensor->start(); +} + +void HeartrateSensorPlugin::finishRecording() { + qDebug() << "bpm update received"; + int bpm = hrmSensor->reading()->bpm(); + qDebug() << QDateTime::currentDateTime().toString("hh:mm:ss") << " : " << bpm << hrmSensor->status() << hrmSensor->isActive(); + if ((bpm == 0) || (hrmSensor->status() < 3)) { + qDebug() << "hrm sensor accuracy insufficient. waiting."; + return; + } + fileAddRecord(sensorPathPrefix,QString::number(bpm)); + hrmSensor->stop(); +} diff --git a/daemon/sensorPlugins/heartrateSensor.h b/daemon/sensorPlugins/heartrateSensor.h new file mode 100644 index 0000000..7788d7e --- /dev/null +++ b/daemon/sensorPlugins/heartrateSensor.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2023 Arseniy Movshev + * This file is part of sensorlogd, a sensor logger for the AsteroidOS smartwatch OS. + * + * sensorlogd is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * sensorlogd is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + */ + +#ifndef HEARTRATESENSOR_H +#define HEARTRATESENSOR_H + +#include +#include +#include +#include + +#include + +class HeartrateSensorPlugin : public QObject +{ + Q_OBJECT +public: + explicit HeartrateSensorPlugin(QObject *parent = 0, int initInterval = 600000); + virtual ~HeartrateSensorPlugin() {}; + + void timeUpdate(); + +public slots: + void triggerRecording(); + +private slots: + void finishRecording(); + +private: + QDateTime lastRecordTime; + int interval; + QTimer *recordIntervalTimer; + QHrmSensor *hrmSensor; + + const QString sensorPathPrefix = "heartrateMonitor"; +}; + +#endif // HEARTRATESENSOR_H diff --git a/daemon/sensorPlugins/stepCounter.cpp b/daemon/sensorPlugins/stepCounter.cpp new file mode 100644 index 0000000..8bfed03 --- /dev/null +++ b/daemon/sensorPlugins/stepCounter.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2023 Arseniy Movshev + * This file is part of sensorlogd, a sensor logger for the AsteroidOS smartwatch OS. + * + * sensorlogd is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * sensorlogd is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#include "../logger.h" + +#include "stepCounter.h" + +StepCounterPlugin::StepCounterPlugin(QObject *parent, int initInterval) : + QObject(parent){ + interval = initInterval; + + stepcounterSensor = new QStepCounterSensor(this); + stepcounterSensor->start(); + + qDebug() << "step counter sensor is enabled. interval is (ms) " << interval; + recordIntervalTimer = new QTimer(this); + connect(recordIntervalTimer,SIGNAL(timeout()),this,SLOT(triggerRecording())); + recordIntervalTimer->setSingleShot(true); + recordIntervalTimer->start(interval); + + QDateTime currDateTime = QDateTime::currentDateTime(); + + if (dayFileExists(sensorPathPrefix)) { + QStringList lastLineData = fileGetPrevRecord(sensorPathPrefix); + lastRecordTime = QDateTime::fromSecsSinceEpoch(lastLineData[0].toInt()); + if (stepcounterSensor->reading()->steps() == 0) { + stepsOffset = -(lastLineData[1].toInt()); + } else { + + } + } else { + //if it's a new day, we 'reset' the counter. this is crude - we should really check for a boot here, since certain machines have capability of counting steps when powered down. + stepsOffset = stepcounterSensor->reading()->steps(); + } +} + +void StepCounterPlugin::timeUpdate() { + QDateTime currDateTime = QDateTime::currentDateTime(); + if (lastRecordTime.date() < currDateTime.date()) { + stepsOffset = stepcounterSensor->reading()->steps(); //this 'resets' the reading whenever the screen is first turned on after midnight. this means that, in the morning, the step count will always be zero, but steps taken just before midnight are still counted and not discarded. + } + uint elapsed = currDateTime.toMSecsSinceEpoch() - lastRecordTime.toMSecsSinceEpoch(); + qDebug() << "time until next steps recording" << recordIntervalTimer->remainingTime() << " elapsed = " << elapsed << " lastRecordTime " << lastRecordTime.toMSecsSinceEpoch(); + if (elapsed > interval) { //if too much time has passed, reset the timer and record + triggerRecording(); + lastRecordTime = QDateTime::currentDateTime(); + } else { //otherwise, restart the timer and compensate for time spent in suspend + recordIntervalTimer->start(interval - elapsed); + } +} + +void StepCounterPlugin::triggerRecording() { + qDebug() << "stepcounter interval recording"; + int steps = stepcounterSensor->reading()->steps(); + qDebug() << QDateTime::currentDateTime().toString("hh:mm:ss") << " : " << steps << stepcounterSensor->isActive(); + //we probably ought to do some error checking here + fileAddRecord(sensorPathPrefix,QString::number(steps - stepsOffset)); +} diff --git a/daemon/sensorPlugins/stepCounter.h b/daemon/sensorPlugins/stepCounter.h new file mode 100644 index 0000000..a221561 --- /dev/null +++ b/daemon/sensorPlugins/stepCounter.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2023 Arseniy Movshev + * This file is part of sensorlogd, a sensor logger for the AsteroidOS smartwatch OS. + * + * sensorlogd is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * sensorlogd is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + */ + +#ifndef STEPCOUNTER_H +#define STEPCOUNTER_H + +#include +#include +#include +#include + +#include + +class StepCounterPlugin : public QObject +{ + Q_OBJECT +public: + explicit StepCounterPlugin(QObject *parent = 0, int initInterval = 600000); + virtual ~StepCounterPlugin() {}; + + void timeUpdate(); + +public slots: + void triggerRecording(); + +private: + QDateTime lastRecordTime; + int interval; + QTimer *recordIntervalTimer; + QStepCounterSensor *stepcounterSensor; + int stepsOffset; //this is subtracted from the raw sensor value to compensate for daily step resets and boot offsets. + + const QString sensorPathPrefix = "stepCounter"; +}; + +#endif // STEPCOUNTER_H diff --git a/daemon/sensorlogd.cpp b/daemon/sensorlogd.cpp new file mode 100644 index 0000000..8b7395a --- /dev/null +++ b/daemon/sensorlogd.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023 Arseniy Movshev + * This file is part of sensorlogd, a sensor logger for the AsteroidOS smartwatch OS. + * + * sensorlogd is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * sensorlogd is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + */ + +#include +#include +#include +#include "logger.h" + +int main(int argc, char **argv) +{ + QCoreApplication qcoreapp(argc, argv); + if (!QDBusConnection::systemBus().isConnected()) { + fprintf(stderr, "Cannot connect to the D-Bus system bus.\n"); + return 3; + } + if (!QDBusConnection::sessionBus().isConnected()) { + fprintf(stderr, "Cannot connect to the D-Bus session bus.\n"); + return 2; + } + Logger sensorsLogger; + QCoreApplication::setOrganizationName("asteroid"); + QCoreApplication::setOrganizationDomain("asteroidos.org"); + QCoreApplication::setApplicationName("healthd"); + qDebug() << "healthd started"; + qcoreapp.exec(); + return 0; +} diff --git a/healthd.cpp b/healthd.cpp deleted file mode 100644 index 884b55c..0000000 --- a/healthd.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include -#include -#include -#include "logger.h" - -int main(int argc, char **argv) -{ - QCoreApplication qcoreapp(argc, argv); - if (!QDBusConnection::systemBus().isConnected()) { - fprintf(stderr, "Cannot connect to the D-Bus system bus.\n"); - return 3; - } - if (!QDBusConnection::sessionBus().isConnected()) { - fprintf(stderr, "Cannot connect to the D-Bus session bus.\n"); - return 2; - } - Logger sensorsLogger; - QCoreApplication::setOrganizationName("asteroid"); - QCoreApplication::setOrganizationDomain("asteroidos.org"); - QCoreApplication::setApplicationName("healthd"); - qDebug() << "healthd started"; - qcoreapp.exec(); - return 0; -} diff --git a/logger.cpp b/logger.cpp deleted file mode 100644 index 8fd5953..0000000 --- a/logger.cpp +++ /dev/null @@ -1,94 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "logger.h" - -#include "sensorPlugins/stepCounter.h" -#include "sensorPlugins/heartrateSensor.h" - -Logger::Logger(QObject *parent) : - QObject(parent){ - m_iface = new QDBusInterface("com.nokia.mce","/com/nokia/mce/signal", "com.nokia.mce.signal", QDBusConnection::systemBus()); - QSettings settings; - -//intialise HRM - if (heartrateSensorEnabled) { //add check for HRM - m_heartrateSensor = new HeartrateSensorPlugin(this,settings.value("stepsInterval",600000).toInt()); - } - -//initialise step counter - if (stepCounterEnabled) { //add check for step sensor - m_stepCounter = new StepCounterPlugin(this,settings.value("stepsInterval",600000).toInt()); - } - - if(!m_iface->isValid()) { - qDebug() << "interface is not valid"; - qDebug() << m_iface->lastError(); - } - if(connect(m_iface, SIGNAL(display_status_ind(QString)), this, SLOT(displayOn(QString)))) { //this fires when the display turns on - qDebug() << "healthd connected display_status signal to slot"; - } - qDebug() << "healthd sensors logger initialised"; -} - -void Logger::displayOn(QString displayState) { - if (displayState == "off") - return; - qDebug() << "display on detected"; - uint currTime = QDateTime::currentMSecsSinceEpoch(); - - if (heartrateSensorEnabled) { - m_heartrateSensor->timeUpdate(); - } - - if (stepCounterEnabled) { - m_stepCounter->timeUpdate(); - } -} - -void fileAddRecord(QString sensorPrefix, QString logdata, QDateTime recordTime) { //adds a record to today's log file for the given sensor - qDebug() << fileNameForDate(recordTime.date(), sensorPrefix); - QFile file(fileNameForDate(recordTime.date(), sensorPrefix)); - if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) { - qDebug() << "failed to open file"; - return; - } - file.seek(file.size()); - QTextStream out(&file); - out << QString::number(recordTime.currentSecsSinceEpoch()) + ":" + logdata + "\n"; - file.close(); -} -bool dayFileExists(QString sensorPrefix, QDateTime dateTime) { - return QFile::exists(fileNameForDate(dateTime.date(), sensorPrefix)); -} - -QStringList fileGetPrevRecord(QString sensorPrefix, QDateTime recordTime) { - qDebug() << fileNameForDate(recordTime.date(), sensorPrefix); - QFile file(fileNameForDate(recordTime.date(), sensorPrefix)); - if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) { - qDebug() << "failed to open file"; - return {0,0}; - } - QTextStream inStream(&file); - QString line; - int i; - while(!inStream.atEnd()) - { - line = inStream.readLine(); - qDebug() << line; - i++; - } - file.close(); - return line.split(":"); -} - -QString fileNameForDate(QDate date, QString prefix) { - return QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + "/asteroid-healthloggerd/" + prefix + "/" + date.toString("yyyy-MM-dd.log"); -} diff --git a/logger.h b/logger.h deleted file mode 100644 index e56a51a..0000000 --- a/logger.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef LOGGER_H -#define LOGGER_H - -#include -#include -#include -#include -#include -#include - -#include "sensorPlugins/stepCounter.h" -#include "sensorPlugins/heartrateSensor.h" - -class Logger : public QObject -{ - Q_OBJECT -public: - explicit Logger(QObject *parent = 0); - virtual ~Logger() {}; - -private slots: - void displayOn(QString displayState); - -private: - QDBusInterface *m_iface; - bool heartrateSensorEnabled = true; - HeartrateSensorPlugin *m_heartrateSensor; - bool stepCounterEnabled = true; - StepCounterPlugin *m_stepCounter; - -}; - void fileAddRecord(QString sensorPrefix, QString logdata, QDateTime recordTime = QDateTime::currentDateTime()); //adds a record to today's log file for the given sensor - bool dayFileExists(QString sensorPrefix, QDateTime date = QDateTime::currentDateTime()); //check if today has a log file for the given sensor - QStringList fileGetPrevRecord(QString sensorPrefix, QDateTime recordTime = QDateTime::currentDateTime()); //works backwards to find the last record in today's file before the given time - returns nothing if no file is found. - QString fileNameForDate(QDate date, QString prefix); - -#endif // LOGGER_H diff --git a/qmlplugin/CMakeLists.txt b/qmlplugin/CMakeLists.txt new file mode 100644 index 0000000..cb65f44 --- /dev/null +++ b/qmlplugin/CMakeLists.txt @@ -0,0 +1,16 @@ +add_library( + sensorlogdqmlplugin + sensorlogdqmlplugin.cpp + sensorlogdqmlplugin.h + stepsDataLoader.cpp + stepsDataLoader.h +) + +target_link_libraries(sensorlogdqmlplugin + Qt5::Qml +) + +install(TARGETS sensorlogdqmlplugin + DESTINATION ${INSTALL_QML_IMPORT_DIR}/org/asteroid/sensorlogd) +install(FILES qmldir + DESTINATION ${INSTALL_QML_IMPORT_DIR}/org/asteroid/sensorlogd) diff --git a/qmlplugin/qmldir b/qmlplugin/qmldir new file mode 100644 index 0000000..3a7ddb6 --- /dev/null +++ b/qmlplugin/qmldir @@ -0,0 +1,3 @@ +module org.asteroid.sensorlogd + +plugin sensorlogdqmlplugin diff --git a/qmlplugin/sensorlogdqmlplugin.cpp b/qmlplugin/sensorlogdqmlplugin.cpp new file mode 100644 index 0000000..107c2be --- /dev/null +++ b/qmlplugin/sensorlogdqmlplugin.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2023 Arseniy Movshev + * This file is part of sensorlogd, a sensor logger for the AsteroidOS smartwatch OS. + * + * sensorlogd is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * sensorlogd is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + */ + + +#include "sensorlogdqmlplugin.h" +#include +#include "stepsDataLoader.h" + +LogdPlugin::LogdPlugin(QObject *parent) : QQmlExtensionPlugin(parent) +{ +} + +void LogdPlugin::registerTypes(const char *uri) +{ + Q_ASSERT(uri == QLatin1String("org.asteroid.sensorlogd")); + qmlRegisterType(uri, 1, 0, "StepsDataLoader"); +} + diff --git a/qmlplugin/sensorlogdqmlplugin.h b/qmlplugin/sensorlogdqmlplugin.h new file mode 100644 index 0000000..420bf46 --- /dev/null +++ b/qmlplugin/sensorlogdqmlplugin.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2023 Arseniy Movshev + * This file is part of sensorlogd, a sensor logger for the AsteroidOS smartwatch OS. + * + * sensorlogd is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * sensorlogd is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + */ + +#ifndef SENSORLOGDQMLPLUGIN_H +#define SENSORLOGDQMLPLUGIN_H + +#include + +class LogdPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface") + +public: + explicit LogdPlugin(QObject *parent = 0); + void registerTypes(const char *uri); +}; + +#endif // SENSORLOGDQMLPLUGIN_H + diff --git a/qmlplugin/stepsDataLoader.cpp b/qmlplugin/stepsDataLoader.cpp new file mode 100644 index 0000000..1180f0b --- /dev/null +++ b/qmlplugin/stepsDataLoader.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2023 Arseniy Movshev + * This file is part of sensorlogd, a sensor logger for the AsteroidOS smartwatch OS. + * + * sensorlogd is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * sensorlogd is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#include "stepsDataLoader.h" + +StepsDataLoader::StepsDataLoader() : QObject() +{ +} + +int StepsDataLoader::getTodayData() { // This is obvious garbage. This should really be abstracted and cached, so that every page doesn't have to reload the file from scratch. +// The intention is to also add graph functionality at some point. The graph will be simplifying the data before loading it in - it would be worth caching the simplified data when it comes to that as well. + QFile file(fileNameForDate(QDate::currentDate(), "stepCounter")); + if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) { + qDebug() << "failed to open file"; + return 0; + } + QTextStream inStream(&file); + QString line; + int i; + while(!inStream.atEnd()) + { + line = inStream.readLine(); + qDebug() << line; + i++; + } + file.close(); + return line.split(":")[1].toInt(); +} + +QString fileNameForDate(QDate date, QString prefix) { + return QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + "/asteroid-healthloggerd/" + prefix + "/" + date.toString("yyyy-MM-dd.log"); +} diff --git a/qmlplugin/stepsDataLoader.h b/qmlplugin/stepsDataLoader.h new file mode 100644 index 0000000..7be8bfc --- /dev/null +++ b/qmlplugin/stepsDataLoader.h @@ -0,0 +1,26 @@ +/*/* + * Copyright (C) 2023 Arseniy Movshev + * This file is part of sensorlogd, a sensor logger for the AsteroidOS smartwatch OS. + * + * sensorlogd is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * sensorlogd is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + */ + +#ifndef STEPSDATALOADER_H +#define STEPSDATALOADER_H + +#include + +class StepsDataLoader : public QObject +{ + Q_OBJECT + +public: + explicit StepsDataLoader(); + Q_INVOKABLE int getTodayData(); +}; +QString fileNameForDate(QDate date, QString prefix); + +#endif // STEPSDATALOADER_H diff --git a/sensorPlugins/heartrateSensor.cpp b/sensorPlugins/heartrateSensor.cpp deleted file mode 100644 index 3477e9a..0000000 --- a/sensorPlugins/heartrateSensor.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include -#include -#include -#include -#include - -#include "../logger.h" - -#include "heartrateSensor.h" - -HeartrateSensorPlugin::HeartrateSensorPlugin(QObject *parent, int initInterval) : - QObject(parent){ - interval = initInterval; - - hrmSensor = new QHrmSensor(this); - connect(hrmSensor,SIGNAL(readingChanged()),this,SLOT(finishRecording())); - - qDebug() << "heartrate sensor is enabled. interval is (ms) " << interval; - recordIntervalTimer = new QTimer(this); - connect(recordIntervalTimer,SIGNAL(timeout()),this,SLOT(triggerRecording())); - recordIntervalTimer->setSingleShot(true); - recordIntervalTimer->start(interval); - lastRecordTime = QDateTime::currentDateTime(); -} - -void HeartrateSensorPlugin::timeUpdate() { - uint elapsed = QDateTime::currentMSecsSinceEpoch() - lastRecordTime.toMSecsSinceEpoch(); - qDebug() << "time until next steps recording" << recordIntervalTimer->remainingTime() << " elapsed = " << elapsed << " lastRecordTime " << lastRecordTime.toMSecsSinceEpoch(); - if (elapsed > interval) { //if too much time has passed, reset the timer and record - triggerRecording(); - lastRecordTime = QDateTime::currentDateTime(); - } else { //otherwise, restart the timer and compensate for time spent in suspend - recordIntervalTimer->start(interval - elapsed); - } -} - -void HeartrateSensorPlugin::triggerRecording() { - qDebug() << "heartrate interval recording"; - recordIntervalTimer->start(interval); - hrmSensor->start(); -} - -void HeartrateSensorPlugin::finishRecording() { - qDebug() << "bpm update received"; - int bpm = hrmSensor->reading()->bpm(); - qDebug() << QDateTime::currentDateTime().toString("hh:mm:ss") << " : " << bpm << hrmSensor->status() << hrmSensor->isActive(); - if ((bpm == 0) || (hrmSensor->status() < 3)) { - qDebug() << "hrm sensor accuracy insufficient. waiting."; - return; - } - fileAddRecord(sensorPathPrefix,QString::number(bpm)); - hrmSensor->stop(); -} diff --git a/sensorPlugins/heartrateSensor.h b/sensorPlugins/heartrateSensor.h deleted file mode 100644 index 13c1b9d..0000000 --- a/sensorPlugins/heartrateSensor.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef HEARTRATESENSOR_H -#define HEARTRATESENSOR_H - -#include -#include -#include -#include - -#include - -class HeartrateSensorPlugin : public QObject -{ - Q_OBJECT -public: - explicit HeartrateSensorPlugin(QObject *parent = 0, int initInterval = 600000); - virtual ~HeartrateSensorPlugin() {}; - - void timeUpdate(); - -public slots: - void triggerRecording(); - -private slots: - void finishRecording(); - -private: - QDateTime lastRecordTime; - int interval; - QTimer *recordIntervalTimer; - QHrmSensor *hrmSensor; - - const QString sensorPathPrefix = "heartrateMonitor"; -}; - -#endif // HEARTRATESENSOR_H diff --git a/sensorPlugins/stepCounter.cpp b/sensorPlugins/stepCounter.cpp deleted file mode 100644 index 1d1fb7a..0000000 --- a/sensorPlugins/stepCounter.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include -#include -#include -#include -#include - -#include "../logger.h" - -#include "stepCounter.h" - -StepCounterPlugin::StepCounterPlugin(QObject *parent, int initInterval) : - QObject(parent){ - interval = initInterval; - - stepcounterSensor = new QStepCounterSensor(this); - stepcounterSensor->start(); - - qDebug() << "step counter sensor is enabled. interval is (ms) " << interval; - recordIntervalTimer = new QTimer(this); - connect(recordIntervalTimer,SIGNAL(timeout()),this,SLOT(triggerRecording())); - recordIntervalTimer->setSingleShot(true); - recordIntervalTimer->start(interval); - - QDateTime currDateTime = QDateTime::currentDateTime(); - - if (dayFileExists(sensorPathPrefix)) { - QStringList lastLineData = fileGetPrevRecord(sensorPathPrefix); - lastRecordTime = QDateTime::fromSecsSinceEpoch(lastLineData[0].toInt()); - if (stepcounterSensor->reading()->steps() == 0) { - stepsOffset = -(lastLineData[1].toInt()); - } else { - - } - } else { - //if it's a new day, we 'reset' the counter. this is crude - we should really check for a boot here, since certain machines have capability of counting steps when powered down. - stepsOffset = stepcounterSensor->reading()->steps(); - } -} - -void StepCounterPlugin::timeUpdate() { - QDateTime currDateTime = QDateTime::currentDateTime(); - if (lastRecordTime.date() < currDateTime.date()) { - stepsOffset = stepcounterSensor->reading()->steps(); //this 'resets' the reading whenever the screen is first turned on after midnight. this means that, in the morning, the step count will always be zero, but steps taken just before midnight are still counted and not discarded. - } - uint elapsed = currDateTime.toMSecsSinceEpoch() - lastRecordTime.toMSecsSinceEpoch(); - qDebug() << "time until next steps recording" << recordIntervalTimer->remainingTime() << " elapsed = " << elapsed << " lastRecordTime " << lastRecordTime.toMSecsSinceEpoch(); - if (elapsed > interval) { //if too much time has passed, reset the timer and record - triggerRecording(); - lastRecordTime = QDateTime::currentDateTime(); - } else { //otherwise, restart the timer and compensate for time spent in suspend - recordIntervalTimer->start(interval - elapsed); - } -} - -void StepCounterPlugin::triggerRecording() { - qDebug() << "stepcounter interval recording"; - int steps = stepcounterSensor->reading()->steps(); - qDebug() << QDateTime::currentDateTime().toString("hh:mm:ss") << " : " << steps << stepcounterSensor->isActive(); - //we probably ought to do some error checking here - fileAddRecord(sensorPathPrefix,QString::number(steps - stepsOffset)); -} diff --git a/sensorPlugins/stepCounter.h b/sensorPlugins/stepCounter.h deleted file mode 100644 index a7cb580..0000000 --- a/sensorPlugins/stepCounter.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef STEPCOUNTER_H -#define STEPCOUNTER_H - -#include -#include -#include -#include - -#include - -class StepCounterPlugin : public QObject -{ - Q_OBJECT -public: - explicit StepCounterPlugin(QObject *parent = 0, int initInterval = 600000); - virtual ~StepCounterPlugin() {}; - - void timeUpdate(); - -public slots: - void triggerRecording(); - -private: - QDateTime lastRecordTime; - int interval; - QTimer *recordIntervalTimer; - QStepCounterSensor *stepcounterSensor; - int stepsOffset; //this is subtracted from the raw sensor value to compensate for daily step resets and boot offsets. - - const QString sensorPathPrefix = "stepCounter"; -}; - -#endif // STEPCOUNTER_H diff --git a/sensorlogd.cpp b/sensorlogd.cpp deleted file mode 100644 index 884b55c..0000000 --- a/sensorlogd.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include -#include -#include -#include "logger.h" - -int main(int argc, char **argv) -{ - QCoreApplication qcoreapp(argc, argv); - if (!QDBusConnection::systemBus().isConnected()) { - fprintf(stderr, "Cannot connect to the D-Bus system bus.\n"); - return 3; - } - if (!QDBusConnection::sessionBus().isConnected()) { - fprintf(stderr, "Cannot connect to the D-Bus session bus.\n"); - return 2; - } - Logger sensorsLogger; - QCoreApplication::setOrganizationName("asteroid"); - QCoreApplication::setOrganizationDomain("asteroidos.org"); - QCoreApplication::setApplicationName("healthd"); - qDebug() << "healthd started"; - qcoreapp.exec(); - return 0; -} -- cgit v1.2.3-54-g00ecf