/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
*
* @file Object.cpp
*
* Created on: Nov 8, 2016
* Project: sdbus-c++
* Description: High-level D-Bus IPC C++ library based on sd-bus
*
* This file is part of sdbus-c++.
*
* sdbus-c++ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* sdbus-c++ 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with sdbus-c++. If not, see .
*/
#include "Object.h"
#include
#include
#include
#include
#include
#include "IConnection.h"
#include "VTableUtils.h"
#include
#include
#include
namespace sdbus { namespace internal {
Object::Object(sdbus::internal::IConnection& connection, std::string objectPath)
: connection_(connection), objectPath_(std::move(objectPath))
{
}
void Object::registerMethod( const std::string& interfaceName
, const std::string& methodName
, const std::string& inputSignature
, const std::string& outputSignature
, method_callback methodCallback
, Flags flags )
{
SDBUS_THROW_ERROR_IF(!methodCallback, "Invalid method callback provided", EINVAL);
auto& interface = interfaces_[interfaceName];
InterfaceData::MethodData methodData{inputSignature, outputSignature, std::move(methodCallback), flags};
auto inserted = interface.methods_.emplace(methodName, std::move(methodData)).second;
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register method: method already exists", EINVAL);
}
void Object::registerSignal( const std::string& interfaceName
, const std::string& signalName
, const std::string& signature
, Flags flags )
{
auto& interface = interfaces_[interfaceName];
InterfaceData::SignalData signalData{signature, flags};
auto inserted = interface.signals_.emplace(signalName, std::move(signalData)).second;
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register signal: signal already exists", EINVAL);
}
void Object::registerProperty( const std::string& interfaceName
, const std::string& propertyName
, const std::string& signature
, property_get_callback getCallback
, Flags flags )
{
registerProperty( interfaceName
, propertyName
, signature
, getCallback
, property_set_callback{}
, flags );
}
void Object::registerProperty( const std::string& interfaceName
, const std::string& propertyName
, const std::string& signature
, property_get_callback getCallback
, property_set_callback setCallback
, Flags flags )
{
SDBUS_THROW_ERROR_IF(!getCallback && !setCallback, "Invalid property callbacks provided", EINVAL);
auto& interface = interfaces_[interfaceName];
InterfaceData::PropertyData propertyData{signature, std::move(getCallback), std::move(setCallback), flags};
auto inserted = interface.properties_.emplace(propertyName, std::move(propertyData)).second;
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register property: property already exists", EINVAL);
}
void Object::setInterfaceFlags(const std::string& interfaceName, Flags flags)
{
auto& interface = interfaces_[interfaceName];
interface.flags_ = flags;
}
void Object::finishRegistration()
{
for (auto& item : interfaces_)
{
const auto& interfaceName = item.first;
auto& interfaceData = item.second;
const auto& vtable = createInterfaceVTable(interfaceData);
activateInterfaceVTable(interfaceName, interfaceData, vtable);
}
}
sdbus::Signal Object::createSignal(const std::string& interfaceName, const std::string& signalName)
{
return connection_.createSignal(objectPath_, interfaceName, signalName);
}
void Object::emitSignal(const sdbus::Signal& message)
{
message.send();
}
sdbus::IConnection& Object::getConnection() const
{
return dynamic_cast(connection_);
}
const std::vector& Object::createInterfaceVTable(InterfaceData& interfaceData)
{
auto& vtable = interfaceData.vtable_;
assert(vtable.empty());
vtable.push_back(createVTableStartItem(interfaceData.flags_.toSdBusInterfaceFlags()));
registerMethodsToVTable(interfaceData, vtable);
registerSignalsToVTable(interfaceData, vtable);
registerPropertiesToVTable(interfaceData, vtable);
vtable.push_back(createVTableEndItem());
return vtable;
}
void Object::registerMethodsToVTable(const InterfaceData& interfaceData, std::vector& vtable)
{
for (const auto& item : interfaceData.methods_)
{
const auto& methodName = item.first;
const auto& methodData = item.second;
vtable.push_back(createVTableMethodItem( methodName.c_str()
, methodData.inputArgs_.c_str()
, methodData.outputArgs_.c_str()
, &Object::sdbus_method_callback
, methodData.flags_.toSdBusMethodFlags() ));
}
}
void Object::registerSignalsToVTable(const InterfaceData& interfaceData, std::vector& vtable)
{
for (const auto& item : interfaceData.signals_)
{
const auto& signalName = item.first;
const auto& signalData = item.second;
vtable.push_back(createVTableSignalItem( signalName.c_str()
, signalData.signature_.c_str()
, signalData.flags_.toSdBusSignalFlags() ));
}
}
void Object::registerPropertiesToVTable(const InterfaceData& interfaceData, std::vector& vtable)
{
for (const auto& item : interfaceData.properties_)
{
const auto& propertyName = item.first;
const auto& propertyData = item.second;
if (!propertyData.setCallback_)
vtable.push_back(createVTablePropertyItem( propertyName.c_str()
, propertyData.signature_.c_str()
, &Object::sdbus_property_get_callback
, propertyData.flags_.toSdBusPropertyFlags() ));
else
vtable.push_back(createVTableWritablePropertyItem( propertyName.c_str()
, propertyData.signature_.c_str()
, &Object::sdbus_property_get_callback
, &Object::sdbus_property_set_callback
, propertyData.flags_.toSdBusWritablePropertyFlags() ));
}
}
void Object::activateInterfaceVTable( const std::string& interfaceName
, InterfaceData& interfaceData
, const std::vector& vtable )
{
// Tell, don't ask
auto slot = connection_.addObjectVTable(objectPath_, interfaceName, &vtable[0], this);
interfaceData.slot_.reset(slot);
interfaceData.slot_.get_deleter() = [this](sd_bus_slot *slot){ connection_.removeObjectVTable(slot); };
}
int Object::sdbus_method_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError)
{
auto* object = static_cast