From db9dcbed36617661c10775192d62a5854307c074 Mon Sep 17 00:00:00 2001 From: denes Date: Wed, 17 Apr 2019 15:03:53 +0200 Subject: [PATCH] Copying over accelerometer_app as a starting point Verbatim copy from the SDK[1]'s sample/cceleromater_app directory [1] git@bitbucket.org:suunto/movesense-device-lib.git --- AccelerometerSampleService.cpp | 252 +++++++++++++++++++++++++++ AccelerometerSampleService.h | 123 +++++++++++++ App.cpp | 43 +++++ CMakeLists.txt | 14 ++ app_root.yaml | 26 +++ wbresources/AccelerometerSample.yaml | 61 +++++++ wbresources/resources.cpp | 1 + 7 files changed, 520 insertions(+) create mode 100644 AccelerometerSampleService.cpp create mode 100644 AccelerometerSampleService.h create mode 100644 App.cpp create mode 100644 CMakeLists.txt create mode 100644 app_root.yaml create mode 100644 wbresources/AccelerometerSample.yaml create mode 100644 wbresources/resources.cpp diff --git a/AccelerometerSampleService.cpp b/AccelerometerSampleService.cpp new file mode 100644 index 0000000..f4c0fc4 --- /dev/null +++ b/AccelerometerSampleService.cpp @@ -0,0 +1,252 @@ +#include "AccelerometerSampleService.h" +#include "app-resources/resources.h" +#include "common/core/debug.h" +#include "meas_acc/resources.h" +#include "whiteboard/builtinTypes/UnknownStructure.h" + +#include +#include + +const char* const AccelerometerSampleService::LAUNCHABLE_NAME = "SampleA"; +#define SAMPLE_RATE 13 +static const whiteboard::ExecutionContextId sExecutionContextId = + WB_RES::LOCAL::SAMPLE_ACCELEROMETER_DATA::EXECUTION_CONTEXT; + +static const whiteboard::LocalResourceId sProviderResources[] = { + WB_RES::LOCAL::SAMPLE_ACCELEROMETER_DATA::LID, +}; + +AccelerometerSampleService::AccelerometerSampleService() + : ResourceClient(WBDEBUG_NAME(__FUNCTION__), sExecutionContextId), + ResourceProvider(WBDEBUG_NAME(__FUNCTION__), sExecutionContextId), + LaunchableModule(LAUNCHABLE_NAME, sExecutionContextId), + isRunning(false) +{ +} + +AccelerometerSampleService::~AccelerometerSampleService() +{ +} + +bool AccelerometerSampleService::initModule() +{ + if (registerProviderResources(sProviderResources) != whiteboard::HTTP_CODE_OK) + { + return false; + } + + mModuleState = WB_RES::ModuleStateValues::INITIALIZED; + return true; +} + +void AccelerometerSampleService::deinitModule() +{ + unregisterProviderResources(sProviderResources); + mModuleState = WB_RES::ModuleStateValues::UNINITIALIZED; +} + +/** @see whiteboard::ILaunchableModule::startModule */ +bool AccelerometerSampleService::startModule() +{ + mModuleState = WB_RES::ModuleStateValues::STARTED; + + return true; +} + +void AccelerometerSampleService::onUnsubscribeResult(whiteboard::RequestId requestId, + whiteboard::ResourceId resourceId, + whiteboard::Result resultCode, + const whiteboard::Value& rResultData) +{ + DEBUGLOG("AccelerometerSampleService::onUnsubscribeResult() called."); +} + +void AccelerometerSampleService::onSubscribeResult(whiteboard::RequestId requestId, + whiteboard::ResourceId resourceId, + whiteboard::Result resultCode, + const whiteboard::Value& rResultData) +{ + DEBUGLOG("AccelerometerSampleService::onSubscribeResult() called. resourceId: %u, result: %d", resourceId.localResourceId, (uint32_t)resultCode); + + whiteboard::Request relatedIncomingRequest; + bool relatedRequestFound = mOngoingRequests.get(requestId, relatedIncomingRequest); + + if (relatedRequestFound) + { + returnResult(relatedIncomingRequest, wb::HTTP_CODE_OK); + } +} + +whiteboard::Result AccelerometerSampleService::startRunning(whiteboard::RequestId& remoteRequestId) +{ + if (isRunning) + { + return whiteboard::HTTP_CODE_OK; + } + + DEBUGLOG("AccelerometerSampleService::startRunning()"); + + // Reset max acceleration members + mMaxAccelerationSq = FLT_MIN; + mSamplesIncluded = 0; + + wb::Result result = getResource("Meas/Acc/13", mMeasAccResourceId); + if (!wb::RETURN_OKC(result)) + { + return result; + } + + result = asyncSubscribe(mMeasAccResourceId, AsyncRequestOptions(&remoteRequestId, 0, true)); + + if (!wb::RETURN_OKC(result)) + { + DEBUGLOG("asyncSubscribe threw error: %u", result); + return whiteboard::HTTP_CODE_BAD_REQUEST; + } + isRunning = true; + + return whiteboard::HTTP_CODE_OK; +} + +whiteboard::Result AccelerometerSampleService::stopRunning() +{ + if (!isRunning) + { + return whiteboard::HTTP_CODE_OK; + } + + if (isResourceSubscribed(WB_RES::LOCAL::SAMPLE_ACCELEROMETER_DATA::ID) == wb::HTTP_CODE_OK) + { + DEBUGLOG("AccelerometerSampleService::stopRunning() skipping. Subscribers still exist."); + return whiteboard::HTTP_CODE_OK; + } + + DEBUGLOG("AccelerometerSampleService::stopRunning()"); + + // Unsubscribe the LinearAcceleration resource, when unsubscribe is done, we get callback + wb::Result result = asyncUnsubscribe(mMeasAccResourceId, NULL); + if (!wb::RETURN_OKC(result)) + { + DEBUGLOG("asyncUnsubscribe threw error: %u", result); + } + isRunning = false; + releaseResource(mMeasAccResourceId); + + return whiteboard::HTTP_CODE_OK; +} + +// This callback is called when the resource we have subscribed notifies us +void AccelerometerSampleService::onNotify(whiteboard::ResourceId resourceId, const whiteboard::Value& value, + const whiteboard::ParameterList& parameters) +{ + DEBUGLOG("onNotify() called."); + + // Confirm that it is the correct resource + switch (resourceId.localResourceId) + { + case WB_RES::LOCAL::MEAS_ACC_SAMPLERATE::LID: + { + const WB_RES::AccData& linearAccelerationValue = + value.convertTo(); + + if (linearAccelerationValue.arrayAcc.size() <= 0) + { + // No value, do nothing... + return; + } + + const whiteboard::Array& arrayData = linearAccelerationValue.arrayAcc; + + uint32_t relativeTime = linearAccelerationValue.timestamp; + + for (size_t i = 0; i < arrayData.size(); i++) + { + mSamplesIncluded++; + + whiteboard::FloatVector3D accValue = arrayData[i]; + float accelerationSq = accValue.mX * accValue.mX + + accValue.mY * accValue.mY + + accValue.mZ * accValue.mZ; + + if (mMaxAccelerationSq < accelerationSq) + mMaxAccelerationSq = accelerationSq; + } + + // 13Hz 5sec + if (mSamplesIncluded > 5 * 13) + { + // Reset counter and notify our subscribers + WB_RES::SampleDataValue sampleDataValue; + sampleDataValue.relativeTime = relativeTime; + sampleDataValue.value = sqrtf(mMaxAccelerationSq); + + // Reset members + mSamplesIncluded = 0; + mMaxAccelerationSq = FLT_MIN; + + // and update our WB resource. This causes notification to be fired to our subscribers + updateResource(WB_RES::LOCAL::SAMPLE_ACCELEROMETER_DATA(), + ResponseOptions::Empty, sampleDataValue); + } + } + break; + } +} + +void AccelerometerSampleService::onSubscribe(const whiteboard::Request& request, + const whiteboard::ParameterList& parameters) +{ + switch (request.getResourceConstId()) + { + case WB_RES::LOCAL::SAMPLE_ACCELEROMETER_DATA::ID: + { + // Someone subscribed to our service. Start collecting data and notifying when our service changes state (every 10 seconds) + whiteboard::RequestId remoteRequestId; + whiteboard::Result result = startRunning(remoteRequestId); + + if (isRunning) + { + return returnResult(request, whiteboard::HTTP_CODE_OK); + } + + if (!whiteboard::RETURN_OK(result)) + { + return returnResult(request, result); + } + bool queueResult = mOngoingRequests.put(remoteRequestId, request); + (void)queueResult; + WB_ASSERT(queueResult); + break; + } + default: + ASSERT(0); // Should not happen + } +} + +void AccelerometerSampleService::onUnsubscribe(const whiteboard::Request& request, + const whiteboard::ParameterList& parameters) +{ + switch (request.getResourceConstId()) + { + case WB_RES::LOCAL::SAMPLE_ACCELEROMETER_DATA::ID: + stopRunning(); + returnResult(request, wb::HTTP_CODE_OK); + break; + + default: + returnResult(request, wb::HTTP_CODE_BAD_REQUEST); + ASSERT(0); // Should not happen + } +} + +void AccelerometerSampleService::onRemoteWhiteboardDisconnected(whiteboard::WhiteboardId whiteboardId) +{ + DEBUGLOG("AccelerometerSampleService::onRemoteWhiteboardDisconnected()"); + stopRunning(); +} + +void AccelerometerSampleService::onClientUnavailable(whiteboard::ClientId clientId) +{ + DEBUGLOG("AccelerometerSampleService::onClientUnavailable()"); + stopRunning(); +} \ No newline at end of file diff --git a/AccelerometerSampleService.h b/AccelerometerSampleService.h new file mode 100644 index 0000000..f8f264e --- /dev/null +++ b/AccelerometerSampleService.h @@ -0,0 +1,123 @@ +#pragma once + +#include +#include +#include +#include + +#include "wb-resources/resources.h" + +class AccelerometerSampleService FINAL : private whiteboard::ResourceClient, + private whiteboard::ResourceProvider, + public whiteboard::LaunchableModule + +{ +public: + /** Name of this class. Used in StartupProvider list. */ + static const char* const LAUNCHABLE_NAME; + AccelerometerSampleService(); + ~AccelerometerSampleService(); + +private: + /** @see whiteboard::ILaunchableModule::initModule */ + virtual bool initModule() OVERRIDE; + + /** @see whiteboard::ILaunchableModule::deinitModule */ + virtual void deinitModule() OVERRIDE; + + /** @see whiteboard::ILaunchableModule::startModule */ + virtual bool startModule() OVERRIDE; + + /** @see whiteboard::ILaunchableModule::stopModule */ + virtual void stopModule() OVERRIDE { mModuleState = WB_RES::ModuleStateValues::STOPPED; } + + /** + * Subscribe notification callback. + * + * @param request Request information + * @param parameters List of parameters + */ + virtual void onSubscribe(const whiteboard::Request& request, + const whiteboard::ParameterList& parameters) OVERRIDE; + + /** + * Unsubscribe notification callback. + * + * @param request Request information + * @param parameters List of parameters + */ + virtual void onUnsubscribe(const whiteboard::Request& request, + const whiteboard::ParameterList& parameters) OVERRIDE; + + /** + * Whiteboard disconnect notification handler. + * + * This can be used for example to cleanup possible subscription related information of clients from + * the remote whiteboard. + * + * @param whiteboardId ID of the whiteboard that has been disconnected. + * + * @see whiteboard::ResourceProvider::onSubscribe + * @see whiteboard::ResourceProvider::onUnsubscribe + */ + virtual void onRemoteWhiteboardDisconnected(whiteboard::WhiteboardId whiteboardId) OVERRIDE; + + /** + * Local client 'disconnect' notification handler. + * + * This can be used for example to cleanup possible subscription related information of the client. + * + * @see whiteboard::ResourceProvider::onSubscribe + * @see whiteboard::ResourceProvider::onUnsubscribe + */ + virtual void onClientUnavailable(whiteboard::ClientId clientId) OVERRIDE; + + /** + * Callback for resource notifications. + * Note that this function will not be called for notifications that are + * of types WB_RESOURCE_NOTIFICATION_TYPE_INSERT or WB_RESOURCE_NOTIFICATION_TYPE_DELETE, + * just for notifications that are of type WB_RESOURCE_NOTIFICATION_TYPE_UPDATE. + * + * @param resourceId Resource id associated with the update + * @param rValue Current value of the resource + */ + virtual void onNotify(whiteboard::ResourceId resourceId, const whiteboard::Value& value, + const whiteboard::ParameterList& parameters); + + /** + * Callback for asynchronous UNSUBSCRIBE requests + * + * @param requestId ID of the request + * @param resourceId Successful request contains ID of the resource + * @param resultCode Result code of the request + * @param rResultData Successful result contains the request result + */ + virtual void onUnsubscribeResult(whiteboard::RequestId requestId, + whiteboard::ResourceId resourceId, + whiteboard::Result resultCode, + const whiteboard::Value& rResultData); + /** + * Callback for asynchronous SUBSCRIBE requests + * + * @param requestId ID of the request + * @param resourceId Successful request contains ID of the resource + * @param resultCode Result code of the request + * @param rResultData Successful result contains the request result + */ + virtual void onSubscribeResult(whiteboard::RequestId requestId, + whiteboard::ResourceId resourceId, + whiteboard::Result resultCode, + const whiteboard::Value& rResultData); + +private: + whiteboard::Result startRunning(whiteboard::RequestId& remoteRequestId); + whiteboard::Result stopRunning(); + + whiteboard::ResourceId mMeasAccResourceId; + + whiteboard::RequestMap<2, void> mOngoingRequests; // For storing relations of incoming & outgoing requests + + bool isRunning; + size_t mSamplesIncluded; + float mMaxAccelerationSq; +}; diff --git a/App.cpp b/App.cpp new file mode 100644 index 0000000..971be29 --- /dev/null +++ b/App.cpp @@ -0,0 +1,43 @@ +#include "AccelerometerSampleService.h" +#include "movesense.h" + +MOVESENSE_APPLICATION_STACKSIZE(1024) + +MOVESENSE_PROVIDERS_BEGIN(1) + +MOVESENSE_PROVIDER_DEF(AccelerometerSampleService) + +MOVESENSE_PROVIDERS_END(1) + +MOVESENSE_FEATURES_BEGIN() +// Explicitly enable or disable Movesense framework core modules. +// List of modules and their default state is found in documentation +OPTIONAL_CORE_MODULE(DataLogger, true) +OPTIONAL_CORE_MODULE(Logbook, true) +OPTIONAL_CORE_MODULE(LedService, true) +OPTIONAL_CORE_MODULE(IndicationService, true) +OPTIONAL_CORE_MODULE(BleService, true) +OPTIONAL_CORE_MODULE(EepromService, true) +OPTIONAL_CORE_MODULE(BypassService, false) +OPTIONAL_CORE_MODULE(SystemMemoryService, false) +OPTIONAL_CORE_MODULE(DebugService, false) +OPTIONAL_CORE_MODULE(BleStandardHRS, false) +OPTIONAL_CORE_MODULE(BleNordicUART, false) +OPTIONAL_CORE_MODULE(CustomGattService, false) + +// NOTE: It is inadvisable to enable both Logbook/DataLogger and EepromService without +// explicit definition of Logbook memory are (see LOGBOOK_MEMORY_AREA macro in movesense.h and eeprom_logbook_app). +// Default setting is for Logbook to use the whole EEPROM memory area. + +// NOTE: If building a simulator build, these macros are obligatory! +DEBUGSERVICE_BUFFER_SIZE(6, 120); // 6 lines, 120 characters total +DEBUG_EEPROM_MEMORY_AREA(false, 0, 0) +LOGBOOK_MEMORY_AREA(0, 384 * 1024); + +APPINFO_NAME("Sample Accelerometer"); +APPINFO_VERSION("1.1.0"); +APPINFO_COMPANY("Movesense"); + +// NOTE: SERIAL_COMMUNICATION macro has been DEPRECATED +BLE_COMMUNICATION(true) +MOVESENSE_FEATURES_END() diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..2b2680a --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.4) +enable_language(C CXX ASM) + +if(NOT DEFINED MOVESENSE_CORE_LIBRARY) + # Give error that user must provide path to movescount-core library + message(FATAL_ERROR "Path to movesense-core library not set. Add -DMOVESENSE_CORE_LIBRARY= to cmake command line") +endif() + +if(NOT IS_ABSOLUTE ${MOVESENSE_CORE_LIBRARY}) + set(MOVESENSE_CORE_LIBRARY ${CMAKE_BINARY_DIR}/${MOVESENSE_CORE_LIBRARY}) +endif() + +include(${MOVESENSE_CORE_LIBRARY}/MovesenseFromStaticLib.cmake REQUIRED) + diff --git a/app_root.yaml b/app_root.yaml new file mode 100644 index 0000000..da3cfcc --- /dev/null +++ b/app_root.yaml @@ -0,0 +1,26 @@ +# Type of the document +wbres: + version: "2.0" + type: "root" + +# Execution context definitions +executionContexts: + application: + numberOfDpcs: 8 + numberOfRequests: 20 + numberOfResponses: 25 + externalThread: true # we run this execution context in main thread + stackSize: 768 + priority: normal + meas: + numberOfDpcs: 9 + numberOfRequests: 25 + numberOfResponses: 10 + stackSize: 768 + priority: normal + + +apis: + AccelerometerSample.*: + apiId: 100 + defaultExecutionContext: meas diff --git a/wbresources/AccelerometerSample.yaml b/wbresources/AccelerometerSample.yaml new file mode 100644 index 0000000..f4177bf --- /dev/null +++ b/wbresources/AccelerometerSample.yaml @@ -0,0 +1,61 @@ +# This file defines Suunto Whiteboard API for Movesense Hackathon 2016 sample +# +swagger: '2.0' + +# Document metadata +info: + version: "0.0.0" + title: Movesense Accelerometer sample app API + description: | + This file defines API for Movesense Accelerometer Sample service + + See http://movesense.com for more information. + termsOfService: http://suunto.com/wbapi/terms/ + contact: + name: Suunto team + url: http://developer.suunto.com + +# Paths +paths: + /Sample/Accelerometer/Data/Subscription: + post: + description: | + Subscribe to periodic Data values. + See documentation on product-specific measurement periods at + http://XXX.suunto.com/XXX. If measurement source is unavailable, the + Measurement member will be empty. If present, Measurement member + contains single measurement result. + responses: + 200: + description: Operation completed successfully + x-std-errors: + description: See common error codes http://developer.suunto.com/api/std-errors#subscribe + x-notification: + description: New value + schema: + $ref: '#/definitions/SampleDataValue' + delete: + description: | + Unsubscribe from sample data values. + responses: + 200: + description: Operation completed successfully + x-std-errors: + description: See common error codes http://developer.suunto.com/api/std-errors#unsubscribe + +definitions: + SampleDataValue: + required: + - RelativeTime + - Value + properties: + RelativeTime: + description: Relative time of measurement + type: integer + format: uint32 + x-unit: millisecond + Value: + description: Sample Data Value (in practice max absolute acceleration, over about 5 seconds when subscribed) + type: number + format: float + x-unit: m/s^2 diff --git a/wbresources/resources.cpp b/wbresources/resources.cpp new file mode 100644 index 0000000..32e1151 --- /dev/null +++ b/wbresources/resources.cpp @@ -0,0 +1 @@ +// This file is needed by CMAKE. It can't generate empty libraries.