// Copyright 2017 Paul Nettle. // // This file is part of Gobbledegook. // // Gobbledegook 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. // // Gobbledegook 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 Gobbledegook. If not, see . // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // >> // >>> INSIDE THIS FILE // >> // // This is our representation of a GATT Characteristic which is intended to be used in our server description // // >> // >>> DISCUSSION // >> // // See the discussion at the top of GattDescriptor.cpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #pragma once #include #include #include #include "TickEvent.h" #include "Utils.h" #include "GattInterface.h" namespace ggk { // --------------------------------------------------------------------------------------------------------------------------------- // Forward declarations // --------------------------------------------------------------------------------------------------------------------------------- struct GattCharacteristic; struct GattDescriptor; struct GattProperty; struct DBusObject; // --------------------------------------------------------------------------------------------------------------------------------- // Useful Lambdas // --------------------------------------------------------------------------------------------------------------------------------- #define DESCRIPTOR_UPDATED_VALUE_CALLBACK_LAMBDA [] \ ( \ const GattDescriptor &self, \ GDBusConnection *pConnection, \ void *pUserData \ ) -> bool #define DESCRIPTOR_EVENT_CALLBACK_LAMBDA [] \ ( \ const GattDescriptor &self, \ const TickEvent &event, \ GDBusConnection *pConnection, \ void *pUserData \ ) #define DESCRIPTOR_METHOD_CALLBACK_LAMBDA [] \ ( \ const GattDescriptor &self, \ GDBusConnection *pConnection, \ const std::string &methodName, \ GVariant *pParameters, \ GDBusMethodInvocation *pInvocation, \ void *pUserData \ ) // --------------------------------------------------------------------------------------------------------------------------------- // Representation of a Bluetooth GATT Descriptor // --------------------------------------------------------------------------------------------------------------------------------- struct GattDescriptor : GattInterface { // Our interface type static constexpr const char *kInterfaceType = "GattDescriptor"; typedef void (*MethodCallback)(const GattDescriptor &self, GDBusConnection *pConnection, const std::string &methodName, GVariant *pParameters, GDBusMethodInvocation *pInvocation, void *pUserData); typedef void (*EventCallback)(const GattDescriptor &self, const TickEvent &event, GDBusConnection *pConnection, void *pUserData); typedef bool (*UpdatedValueCallback)(const GattDescriptor &self, GDBusConnection *pConnection, void *pUserData); // // Standard constructor // // Construct a GattDescriptor // // Genreally speaking, these objects should not be constructed directly. Rather, use the `gattDescriptorBegin()` method // in `GattCharacteristic`. GattDescriptor(DBusObject &owner, GattCharacteristic &characteristic, const std::string &name); virtual ~GattDescriptor() {} // Returns a string identifying the type of interface virtual const std::string getInterfaceType() const { return GattDescriptor::kInterfaceType; } // Returning the owner pops us one level up the hierarchy // // This method compliments `GattCharacteristic::gattDescriptorBegin()` GattCharacteristic &gattDescriptorEnd(); // Locates a D-Bus method within this D-Bus interface and invokes the method virtual bool callMethod(const std::string &methodName, GDBusConnection *pConnection, GVariant *pParameters, GDBusMethodInvocation *pInvocation, gpointer pUserData) const; // Adds an event to the descriptor and returns a refereence to 'this` to enable method chaining in the server description // // NOTE: We specifically overload this method in order to accept our custom EventCallback type and transform it into a // TickEvent::Callback type. We also return our own type. This simplifies the server description by allowing call to chain. GattDescriptor &onEvent(int tickFrequency, void *pUserData, EventCallback callback); // Ticks events within this descriptor // // Note: we specifically override this method in order to translate the generic TickEvent::Callback into our own EventCallback virtual void tickEvents(GDBusConnection *pConnection, void *pUserData) const; // Specialized support for Descriptor ReadlValue method // // Defined as: array{byte} ReadValue(dict options) // // D-Bus breakdown: // // Input args: options - "a{sv}" // Output args: value - "ay" GattDescriptor &onReadValue(MethodCallback callback); // Specialized support for Descriptor WriteValue method // // Defined as: void WriteValue(array{byte} value, dict options) // // D-Bus breakdown: // // Input args: value - "ay" // options - "a{sv}" // Output args: void GattDescriptor &onWriteValue(MethodCallback callback); // Custom support for handling updates to our descriptor's value // // Defined as: (NOT defined by Bluetooth or BlueZ - this method is internal only) // // This method is called by our framework whenever a Descriptor's value is updated. If you need to perform any actions // when a value is updatd, this is a good place to do that work. // // If you need to perform the same action(s) when a value is updated from the client (via `onWriteValue`) or from this server, // then it may be beneficial to call this method from within your onWriteValue callback to reduce duplicated code. See // `callOnUpdatedValue` for more information. GattDescriptor &onUpdatedValue(UpdatedValueCallback callback); // Calls the onUpdatedValue method, if one was set. // // Returns false if there was no method set, otherwise, returns the boolean result of the method call. // // If you need to perform the same action(s) when a value is updated from the client (via onWriteValue) or from this server, // then it may be beneficial to place those actions in the `onUpdatedValue` method and call it from from within your // `onWriteValue` callback to reduce duplicated code. To call the `onUpdatedValue` method from within your `onWriteValue`, you // can use this pattern: // // .onUpdatedValue(DESCRIPTOR_UPDATED_VALUE_CALLBACK_LAMBDA // { // // Update your value // ... // // // Call the onUpdateValue method that was set in the same Descriptor // self.callOnUpdatedValue(pConnection, pUserData); // }) bool callOnUpdatedValue(GDBusConnection *pConnection, void *pUserData) const; protected: GattCharacteristic &characteristic; UpdatedValueCallback pOnUpdatedValueFunc; }; }; // namespace ggk