blob: b840994a65b366dbb389ce9530b16032e617f119 [file] [log] [blame]
/*
*
* Copyright (c) 2016-2017 Nest Labs, Inc.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
*
* @brief
* Implementations for the abstract DMClient base class.
*
* This file contains implementations for the abstract DMClient base
* class, which serves as the basis for application-specific clients
* based on WDM. See the document, "Nest Weave-Data Management
* Protocol" document for a complete(ish) description.
*/
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif // __STDC_FORMAT_MACROS
#include <Weave/Profiles/data-management/Legacy/WdmManagedNamespace.h>
#include <SystemLayer/SystemStats.h>
#include <Weave/Support/CodeUtils.h>
#include <Weave/Profiles/data-management/DataManagement.h>
using namespace ::nl;
using namespace ::nl::Inet;
using namespace ::nl::Weave;
using namespace ::nl::Weave::TLV;
using namespace ::nl::Weave::Profiles;
using namespace ::nl::Weave::Profiles::Common;
using namespace ::nl::Weave::Profiles::DataManagement;
using namespace ::nl::Weave::Profiles::StatusReporting;
#if WEAVE_CONFIG_WDM_ALLOW_CLIENT_SUBSCRIPTION
// C++ requires this be declared here.
ClientNotifier DMClient::sNotifier;
#endif
/**
* @brief
* The default constructor for DMClient objects.
*
* Clears all internal state. A DMClient requires further
* initialization with Init() before use.
*/
DMClient::DMClient(void)
{
Clear();
}
/**
* @brief
* The destructor for DMClient objects.
*
* Clears all internal state and, if necessary cancels pending
* subscriptions.
*/
DMClient::~DMClient(void)
{
Finalize();
}
/**
* @brief
* Clear the internal state associated with a DMClient object.
*
* In particular, this method clears all the client transaction
* pools. For clients that have been in use the Finalize() method is
* preferable since it also cancels subscriptions and cleans up the
* transaction and binding tables.
*
* @sa Finalize()
*/
void DMClient::Clear(void)
{
int i;
for (i = 0; i<kViewPoolSize; i++)
mViewPool[i].Free();
SYSTEM_STATS_RESET(nl::Weave::System::Stats::kWDMClient_NumViews);
for (i = 0; i<kUpdatePoolSize; i++)
mUpdatePool[i].Free();
SYSTEM_STATS_RESET(nl::Weave::System::Stats::kWDMClient_NumUpdates);
#if WEAVE_CONFIG_WDM_ALLOW_CLIENT_SUBSCRIPTION
for (i = 0; i<kSubscribePoolSize; i++)
mSubscribePool[i].Free();
SYSTEM_STATS_RESET(nl::Weave::System::Stats::kWDMClient_NumSubscribes);
for (i = 0; i<kCancelSubscriptionPoolSize; i++)
mCancelSubscriptionPool[i].Free();
SYSTEM_STATS_RESET(nl::Weave::System::Stats::kWDMClient_NumCancels);
#endif // WEAVE_CONFIG_WDM_ALLOW_CLIENT_SUBSCRIPTION
}
/**
* @brief
* Shut down an operating DMClient.
*
* Clears all the operating state associated with the client and
* removes all related subscriptions from the notifier. After a call
* to Finalize() a DMClient may be reinitialized simply by calling
* Init(). Finalize() is invoked by the DMClient destructor but may
* be called in the case where a DMClient requires cleanup, e.g. in
* case of failure or temporary shutdown, but may need to be
* reconstituted at some later time.
*/
void DMClient::Finalize(void)
{
#if WEAVE_CONFIG_WDM_ALLOW_CLIENT_SUBSCRIPTION
sNotifier.RemoveSubscription(kTopicIdNotSpecified, kNodeIdNotSpecified, this);
#endif
Clear();
ProtocolEngine::Finalize();
}
/**
* @brief
* Handle the "incompletion" of a binding in use by the client.
*
* When a binding fails unexpectedly, e.g. if the connection involved
* in the binding is closed, then this method is called.
*
* @param [in] aBinding A pointer to the Binding that has
* become incomplete.
*
* @param [in] aReport A reference to a status report
* giving a reason for the failure.
*/
void DMClient::IncompleteIndication(Binding *aBinding, StatusReport &aReport)
{
ProtocolEngine::IncompleteIndication(aBinding, aReport);
#if WEAVE_CONFIG_WDM_ALLOW_CLIENT_SUBSCRIPTION
sNotifier.FailSubscription(kTopicIdNotSpecified, aBinding->mPeerNodeId, this, aReport);
#endif
}
/**
* @name
* View methods.
*
* @brief
* View, under WDM, is used by a client to request a snapshot of
* specified data managed by a publisher.
*
* See Detailed Description for discussion of WDM request method
* signatures.
*
* @{
*/
/**
* @brief
* Request a view of published data.
*
* Request a view of data residing on and managed by a specified
* remote publisher.
*
* @param [in] aDestinationId A reference to the 64-bit
* node ID of the remote
* publisher.
*
* @param [in] aPathList A reference to a
* ReferencedTLVData object
* containing a TLV-encoded path
* list indicating the requested
* data.
*
* @param [in] aTxnId, An identifier for the WDM
* transaction set up to manage
* this view operation.
*
* @param [in] aTimeout A maximum time in milliseconds
* to wait for the view response.
*
* @retval #WEAVE_NO_ERROR On success.
*
* @retval #WEAVE_ERROR_NO_MEMORY If a transaction couldn't be allocated.
*
* @return Otherwise, a #WEAVE_ERROR reflecting the failure to
* initialize or start the transaction.
*/
WEAVE_ERROR DMClient::ViewRequest(const uint64_t &aDestinationId, ReferencedTLVData &aPathList, uint16_t aTxnId, uint32_t aTimeout)
{
WEAVE_ERROR err = WEAVE_ERROR_INCORRECT_STATE;
Binding *binding = GetBinding(aDestinationId);
View *view;
if (binding)
{
view = NewView();
if (view)
{
err = view->Init(this, aPathList, aTxnId, aTimeout);
SuccessOrExit(err);
err = StartTransaction(view, binding);
}
else
{
err = WEAVE_ERROR_NO_MEMORY;
}
}
exit:
return err;
}
/**
* @brief
* Request a view of data on default publisher.
*
* Request a view of data residing on and managed by the "default"
* publisher, i.e. the first (or only) publisher in the client's
* binding table.
*
* @param [in] aPathList A reference to a
* ReferencedTLVData object
* containing a TLV-encoded path
* list indicating the requested
* data.
*
* @param [in] aTxnId An identifier for the WDM
* transaction set up to manage
* this view operation.
*
* @param [in] aTimeout A maximum time in milliseconds
* to wait for the view response.
*
* @retval #WEAVE_NO_ERROR On success.
*
* @retval #WEAVE_ERROR_NO_MEMORY If a transaction couldn't be
* allocated.
*
* @return Otherwise, a #WEAVE_ERROR reflecting the failure to
* initialize or start the transaction.
*/
WEAVE_ERROR DMClient::ViewRequest(ReferencedTLVData &aPathList, uint16_t aTxnId, uint32_t aTimeout)
{
WEAVE_ERROR err = WEAVE_ERROR_NO_MEMORY;
View *view = NewView();
if (view)
{
err = view->Init(this, aPathList, aTxnId, aTimeout);
SuccessOrExit(err);
err = StartTransaction(view);
}
exit:
return err;
}
/**
* @}
*/
/**
* @name
* Subscribe methods.
*
* @brief
* Subscription, under WDM, is used by a client to request a
* snapshot of specified data managed by a publisher as with a
* view, but in addition requests notification when the data of
* interest changes,
*
* See Detailed Description for discussion of WDM request method
* signatures.
*
* @{
*/
#if WEAVE_CONFIG_WDM_ALLOW_CLIENT_SUBSCRIPTION
/**
* @brief
* Check if this client has a particular subscription with a
* specified publisher.
*
* @param [in] aTopicId A reference to a topic ID to
* search or. This may be a
* well-known ID or the "working"
* publisher-assigned ID and it
* may be the value kAnyTopicId
* in the case where the caller
* doesn't care.
*
* @param [in] aPublisherId A reference to the 64-bit node
* ID of a publisher to search
* for. This may have a value of
* kAnyNodeId in the case where
* the caller doesn't care.
*
* @return true if a match is found, false otherwise.
*/
bool DMClient::HasSubscription(const TopicIdentifier &aTopicId, const uint64_t &aPublisherId)
{
bool retVal = false;
retVal = sNotifier.HasSubscription(aTopicId, aPublisherId, this);
return retVal;
}
/**
* @brief
* Start a subscription.
*
* This method installs a subscription to a particular assigned
* ID/requested ID pair in the notifier's subscription table with the
* current client as the client requiring notification.
*
* @param [in] aAssignedId A reference to the "working" topic ID
* assigned by the publisher. In
* the case where the
* subscription is unilateral
* this parameter shall have a
* value of
* kTopicIdNotSpecified. In the
* case where the a subscribe
* request has been sent to the
* publisher and no well-known ID
* has been supplied, the
* "working" topic ID is
* delivered as a parameter to
* the SubscribeConfirm() method
* that results form the receipt
* of a successful response.
*
* @param [in] aRequestedId A reference to a well-known
* topic ID given in the initial
* request. This parameter may
* take a value of
* kTopicIdNotSpecified in the
* case where there was no
* requested topic.
*
* @param [in] aPublisherId A reference to the 64-bit node
* ID of the publisher.
*
* @return #WEAVE_NO_ERROR On success. Otherwise return a
* #WEAVE_ERROR reflecting a failure to install the subscription.
*/
WEAVE_ERROR DMClient::BeginSubscription(const TopicIdentifier &aAssignedId, const TopicIdentifier &aRequestedId, const uint64_t &aPublisherId)
{
WEAVE_ERROR err;
err = sNotifier.InstallSubscription(aAssignedId, aRequestedId, aPublisherId, this);
return err;
}
/**
* @brief
* Stop, and remove, a subscription.
*
* Remove a subscription from the notifier's subscription table
* thereby stopping any future notifications from being delivered to
* this client.
*
* @note
* This method simply removes the subscription locally. To cancel a
* subscription that has been established using SubscribeRequest(),
* use CancelSubscriptionRequest().
*
* @sa CancelSubscriptionRequest(const uint64_t &aDestinationId, const TopicIdentifier &aTopicId, uint16_t aTxnId, uint32_t aTimeout)
*
* @param [in] aTopicId A reference to a topic ID
* associated with the
* subscription. This may be the
* "working" ID or the requested
* ID.
*
* @param [in] aPublisherId A reference to the 64-bit node
* ID of the publisher.
*
* @return #WEAVE_NO_ERROR On success. Otherwise return a
* #WEAVE_ERROR reflecting a failure to remove the subscription.
*/
WEAVE_ERROR DMClient::EndSubscription(const TopicIdentifier &aTopicId, const uint64_t &aPublisherId)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
sNotifier.RemoveSubscription(aTopicId, aPublisherId, this);
return err;
}
/**
* @brief
* Request a subscription to a published topic from a specified
* publisher.
*
* Request a view of data residing on and managed by a specified
* remote publisher and request subsequent updates, at the discretion
* of the publisher in the case that the data changes. This version
* uses a known topic ID.
*
* @param [in] aDestinationId A reference to the 64-bit node
* ID of the remote publisher.
*
* @param [in] aTopicId A reference to a well-known
* topic identifier representing
* the requested data.
*
* @param [in] aTxnId An identifier for the
* transaction set up to manage
* this subscribe operation.
*
* @param [in] aTimeout A maximum time in milliseconds
* to wait for the subscribe
* response.
*
* @retval #WEAVE_NO_ERROR On success.
*
* @retval #WEAVE_ERROR_INCORRECT_STATE If a binding to the
* destination could not be found.
*
* @return #WEAVE_ERROR_NO_MEMORY If a transaction couldn't be allocated.
*
* @return Otherwise, a #WEAVE_ERROR reflecting the failure to
* initialize or start the transaction.
*/
WEAVE_ERROR DMClient::SubscribeRequest(const uint64_t &aDestinationId, const TopicIdentifier &aTopicId, uint16_t aTxnId, uint32_t aTimeout)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
Binding *binding = GetBinding(aDestinationId);
Subscribe *subscribe;
if (binding)
{
subscribe = NewSubscribe();
if (subscribe)
{
err = subscribe->Init(this, aTopicId, aTxnId, aTimeout);
SuccessOrExit(err);
err = StartTransaction(subscribe, binding);
}
else
{
err = WEAVE_ERROR_NO_MEMORY;
}
}
else
{
err = WEAVE_ERROR_INCORRECT_STATE;
}
exit:
return err;
}
/**
* @brief
* Request a subscription to a published topic on the default
* publisher.
*
* Request a view of data residing on and managed by the "default"
* publisher, i.e. the first (or only) publisher in the client's
* binding table request subsequent updates, at the discretion of the
* publisher in the case that the data changes. This version uses a
* known topic ID.
*
* @param [in] aTopicId A reference to a well-known
* topic identifier representing
* the requested data.
*
* @param [in] aTxnId An identifier for the
* transaction set up to manage
* this subscribe operation.
*
* @param [in] aTimeout A maximum time in milliseconds
* to wait for the subscribe
* response.
*
* @retval #WEAVE_NO_ERROR On success.
*
* @retval #WEAVE_ERROR_NO_MEMORY If a transaction couldn't be allocated.
*
* @return Otherwise, a #WEAVE_ERROR reflecting the failure to
* initialize or start the transaction.
*/
WEAVE_ERROR DMClient::SubscribeRequest(const TopicIdentifier &aTopicId, uint16_t aTxnId, uint32_t aTimeout)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
Subscribe *subscribe = NewSubscribe();
if (subscribe)
{
err = subscribe->Init(this, aTopicId, aTxnId, aTimeout);
SuccessOrExit(err);
err = StartTransaction(subscribe);
}
else
{
err = WEAVE_ERROR_NO_MEMORY;
}
exit:
return err;
}
/**
* @brief
* Request a subscrption to published data from a specified
* publisher.
*
* Request a view of data residing on and managed by a specified
* remote publisher and request subsequent updates, at the discretion
* of the publisher in the case that the data changes. This version
* uses a path list to specify the data of interest.
*
* @param [in] aDestinationId A reference to the 64-bit node ID
* of the remote publisher.
*
* @param [in] aPathList A reference to a
* ReferencedTLVData object
* containing a TLV-encoded path
* list representing the
* requested data.
*
* @param [in] aTxnId An identifier for the
* transaction set up to manage
* this subscribe operation.
*
* @param [in] aTimeout A maximum time in milliseconds
* to wait for the subscribe
* response.
*
* @retval #WEAVE_NO_ERROR On success.
*
* @retval #WEAVE_ERROR_INCORRECT_STATE If a binding to the
* destination could not be found.
*
* @retval #WEAVE_ERROR_NO_MEMORY If a transaction couldn't be allocated.
*
* @return Otherwise, a #WEAVE_ERROR reflecting the failure to
* initialize or start the transaction.
*/
WEAVE_ERROR DMClient::SubscribeRequest(const uint64_t &aDestinationId, ReferencedTLVData &aPathList, uint16_t aTxnId, uint32_t aTimeout)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
Binding *binding = GetBinding(aDestinationId);
Subscribe *subscribe;
if (binding)
{
subscribe = NewSubscribe();
if (subscribe)
{
err = subscribe->Init(this, aPathList, aTxnId, aTimeout);
SuccessOrExit(err);
err = StartTransaction(subscribe, binding);
}
else
{
err = WEAVE_ERROR_NO_MEMORY;
}
}
else
{
err = WEAVE_ERROR_INCORRECT_STATE;
}
exit:
return err;
}
/**
* @brief
* Request a subscription to data on the default publisher.
*
* Request a view of data residing on and managed by the "default"
* publisher, i.e. the first (or only) publisher in the client's
* binding table and request subsequent updates, at the discretion of
* the publisher in the case that the data changes. This version uses
* a path list to specify the data of interest.
*
* @param [in] aPathList A reference to a
* ReferencedTLVData object
* containing a TLV-encoded path
* list representing the
* requested data.
*
* @param [in] aTxnId An identifier for the
* transaction set up to manage
* this subscribe operation.
*
* @param [in] aTimeout A maximum time in milliseconds
* to wait for the subscribe
* response.
*
* @retval #WEAVE_NO_ERROR on success,
*
* @retval #WEAVE_ERROR_NO_MEMORY if a transaction couldn't be
* allocated.
*
* @return Otherwise, a #WEAVE_ERROR reflecting the failure to
* initialize or start the transaction.
*/
WEAVE_ERROR DMClient::SubscribeRequest(ReferencedTLVData &aPathList, uint16_t aTxnId, uint32_t aTimeout)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
Subscribe *subscribe = NewSubscribe();
if (subscribe)
{
err = subscribe->Init(this, aPathList, aTxnId, aTimeout);
SuccessOrExit(err);
err = StartTransaction(subscribe);
}
else
{
err = WEAVE_ERROR_NO_MEMORY;
}
exit:
return err;
}
/**
* @brief
* Cancel a subscription.
*
* Request the cancellation of a subscription from a given publisher,
* and remove the corresponding subscription from the local notifier
* table.
*
* @note
* This method should be used to cancel a subscription that has
* been establish using SubscribeRequest(). To simply remove a
* subscription locally, use EndSubscription().
*
* @sa EndSubscription(const TopicIdentifier &aTopicId, const uint64_t &aPublisherId)
*
* @param [in] aDestinationId A reference to the 64-bit node
* ID of the remote publisher to
* which the request is being
* sent.
*
* @param [in] aTopicId A reference to a topic ID for
* this subscription. Note this
* this may be either the
* publisher-assigned "working"
* ID or the well-known topic ID
* associated with the original
* request.
*
* @param [in] aTxnId An identifier for the
* transaction set up to manage
* this subscribe operation.
*
* @param [in] aTimeout A maximum time in milliseconds
* to wait for the subscribe
* response.
*
* @return #WEAVE_NO_ERROR on success or a #WEAVE_ERROR reflecting
* a failure to cancel the subscription.
*/
WEAVE_ERROR DMClient::CancelSubscriptionRequest(const uint64_t &aDestinationId, const TopicIdentifier &aTopicId, uint16_t aTxnId, uint32_t aTimeout)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
Binding *binding;
if (HasSubscription(aTopicId, aDestinationId))
{
/*
* we should get rid of the subscription here and not rely
* on the publisher to respond before we get rid of it. this
* may result in a status message with "unknown topic" also
* being sent to the publisher, but it saves us from keeping
* the subscription around indefinitely if the publisher
* fails to respond or the packet gets lost.
*/
sNotifier.RemoveSubscription(aTopicId, aDestinationId, this);
binding = GetBinding(aDestinationId);
if (binding)
{
CancelSubscription *cancel = NewCancelSubscription();
if (cancel)
{
err = cancel->Init(this, aTopicId, aTxnId, aTimeout);
if (err == WEAVE_NO_ERROR)
err = StartTransaction(cancel, binding);
}
else
{
err = WEAVE_ERROR_NO_MEMORY;
}
}
else
{
err = WEAVE_ERROR_INCORRECT_STATE;
}
}
/*
* if the subscription didn't exist, the NHL may
* still want a confirmation.
*/
else
{
StatusReport s;
s.init(kWeaveProfile_Common, kStatus_Success);
err = CancelSubscriptionConfirm(aDestinationId, aTopicId, s, aTxnId);
}
return err;
}
/**
* @brief
* Cancel a subscription on the default publisher.
*
* Request the cancellation of a subscription on the "default"
* publisher, i.e. the first (or only) publisher in the client's
* binding table and remove the corresponding subscription from the
* local notifier table.
*
* @note
* This method should be used to cancel a subscription that has
* been establish using SubscribeRequest(). To simply remove a
* subscription locally, use EndSubscription().
*
* @sa EndSubscription(const TopicIdentifier &aTopicId, const uint64_t &aPublisherId)
*
* @param [in] aTopicId A reference to a topic ID for
* this subscription. Note this
* this may be either the
* publisher-assigned "working"
* ID or the well-known topic ID
* associated with the original
* request.
*
* @param [in] aTxnId An identifier for the
* transaction set up to manage
* this subscribe operation.
*
* @param [in] aTimeout A maximum time in milliseconds
* to wait for the subscribe
* response.
*
* @return #WEAVE_NO_ERROR on success or a #WEAVE_ERROR reflecting
* a failure to cancel the subscription.
*/
WEAVE_ERROR DMClient::CancelSubscriptionRequest(const TopicIdentifier &aTopicId, uint16_t aTxnId, uint32_t aTimeout)
{
WEAVE_ERROR err = WEAVE_ERROR_INCORRECT_STATE;
uint64_t &defaultPeer = mBindingTable[kDefaultBindingTableIndex].mPeerNodeId;
if (defaultPeer != kNodeIdNotSpecified)
err = CancelSubscriptionRequest(defaultPeer, aTopicId, aTxnId, aTimeout);
return err;
}
/**
* @}
*/
#endif // WEAVE_CONFIG_WDM_ALLOW_CLIENT_SUBSCRIPTION
/**
* @name
* Update methods.
*
* @brief
* Update, under WDM, is used by a client to request a
* change to specified data managed by a publisher.
*
* See Detailed Description for discussion of WDM request method
* signatures.
*
* @{
*/
/**
* @brief
* Request an update to published data.
*
* Request that a remote publisher update data under management.
*
* @param [in] aDestinationId A reference to the 64-bit node
* ID of the remote publisher to
* which the request is being
* sent.
*
* @param [in] aDataList A reference to a
* ReferencedTLVData object
* containing a TLV-encoded data
* list containing a
* representation of the update
* including the paths to which
* the update is to be applied.
*
* @param [in] aTxnId An identifier for the
* transaction set up to manage
* the update request.
*
* @param [in] aTimeout A maximum time in milliseconds
* to wait for the corresponding
* status report.
*
* @return #WEAVE_NO_ERROR on success or #WEAVE_ERROR_NO_MEMORY if
* an update transaction could not be allocated. Otherwise, return a
* #WEAVE_ERROR reflecting an update failure.
*/
WEAVE_ERROR DMClient::UpdateRequest(const uint64_t &aDestinationId, ReferencedTLVData &aDataList, uint16_t aTxnId, uint32_t aTimeout)
{
WEAVE_ERROR err = WEAVE_ERROR_INCORRECT_STATE;
Binding *binding = GetBinding(aDestinationId);
Update *update;
if (binding)
{
update = NewUpdate();
if (update)
{
err = update->Init(this, aDataList, aTxnId, aTimeout);
SuccessOrExit(err);
err = StartTransaction(update, binding);
}
else
{
err = WEAVE_ERROR_NO_MEMORY;
}
}
else
{
err = WEAVE_ERROR_INCORRECT_STATE;
}
exit:
return err;
}
/*
* if we're allowing old-style updates with the legacy message types,
* e.g. for Amber, then this is how it works. the "real" method just
* calls the compatibility method with the "use legacy msg type" flag
* set to false. in order to use the compatibility mode, use the call
* with the flag set to true.
*/
#if WEAVE_CONFIG_WDM_ALLOW_CLIENT_LEGACY_MESSAGE_TYPES
/**
* @brief
* Request an update to data on the default publisher.
*
* Request that a remote publisher update data under management. This
* version directs the request to the publisher that is the target of
* the client's default binding.
*
* @param [in] aDataList A reference to a
* ReferencedTLVData object
* containing a TLV-encoded data
* list containing a
* representation of the update
* including the paths to which
* the update is to be applied.
*
* @param [in] aTxnId An identifier for the
* transaction set up to manage
* the update request.
*
* @param [in] aTimeout A maximum time in milliseconds
* to wait for the corresponding
* status report.
*
* @return #WEAVE_NO_ERROR on success or #WEAVE_ERROR_NO_MEMORY if
* an update transaction could not be allocated. Otherwise, return a
* #WEAVE_ERROR reflecting an update failure.
*/
WEAVE_ERROR DMClient::UpdateRequest(ReferencedTLVData &aDataList, uint16_t aTxnId, uint32_t aTimeout)
{
return UpdateRequest(aDataList, aTxnId, aTimeout, false);
}
/**
* @brief
* Request an update to published data.
*
* Request that a remote publisher update data under management. This
* version takes a boolean selector for legacy message types - see
* DMConstants.h
*
* @param [in] aDataList A reference to a
* ReferencedTLVData object
* containing a TLV-encoded data
* list containing a
* representation of the update
* including the paths to which
* the update is to be applied.
*
* @param [in] aTxnId An identifier for the
* transaction set up to manage
* the update request.
*
* @param [in] aTimeout A maximum time in milliseconds
* to wait for the corresponding
* status report.
*
* @param [in] aUseLegacyMsgType A control flag that should be
* true if the message is to be
* sent with the message type
* defined in the WDM spec. up
* through v1.2. Otherwise the
* message should be sent using
* the type defined in the WDM2.0
* spec and beyond.
*
* @return #WEAVE_NO_ERROR on success or #WEAVE_ERROR_NO_MEMORY if
* an update transaction could not be allocated. Otherwise, return a
* #WEAVE_ERROR reflecting an update failure.
*/
WEAVE_ERROR DMClient::UpdateRequest(ReferencedTLVData &aDataList, uint16_t aTxnId, uint32_t aTimeout, bool aUseLegacyMsgType)
{
WEAVE_ERROR err = WEAVE_ERROR_NO_MEMORY;
Update *update = NewUpdate();
if (update)
{
err = update->Init(this, aDataList, aTxnId, aTimeout);
SuccessOrExit(err);
update->mUseLegacyMsgType = aUseLegacyMsgType;
err = StartTransaction(update);
}
exit:
return err;
}
/*
* otherwise, there's no compatiblity method defined.
*/
#else // WEAVE_CONFIG_WDM_ALLOW_CLIENT_LEGACY_MESSAGE_TYPES
/**
* @brief
* Request an update to data on the default publisher.
*
* Request that a remote publisher update data under management. This
* version directs the request to the publisher that is the target of
* the client's default binding.
*
* @param [in] aDataList A reference to a
* ReferencedTLVData object
* containing a TLV-encoded data
* list containing a
* representation of the update
* including the paths to which
* the update is to be applied.
*
* @param [in] aTxnId An identifier for the
* transaction set up to manage
* the update request.
*
* @param [in] aTimeout A maximum time in milliseconds
* to wait for the corresponding
* status report.
*
* @return #WEAVE_NO_ERROR on success or #WEAVE_ERROR_NO_MEMORY if
* an update transaction could not be allocated. Otherwise, return a
* #WEAVE_ERROR reflecting an update failure.
*/
WEAVE_ERROR DMClient::UpdateRequest(ReferencedTLVData &aDataList, uint16_t aTxnId, uint32_t aTimeout)
{
WEAVE_ERROR err = WEAVE_ERROR_NO_MEMORY;
Update *update = NewUpdate();
if (update)
{
err = update->Init(this, aDataList, aTxnId, aTimeout);
SuccessOrExit(err);
err = StartTransaction(update);
}
else
{
err = WEAVE_ERROR_NO_MEMORY;
}
exit:
return err;
}
#endif // WEAVE_CONFIG_WDM_ALLOW_CLIENT_LEGACY_MESSAGE_TYPES
/**
* @}
*/
/**
* @brief
* Request that an executing transaction be canceled.
* This method doesn't generate network traffic, but just release resources allocated for
* the specified transaction(s)
*
* @param [in] aTxnId The number of the transaction
* to be canceled. If kTransactionIdNotSpecified is provided,
* all transactions would be canceled.
*
* @return #WEAVE_NO_ERROR on success or a #WEAVE_ERROR reflecting
* a failure to cancel the transaction.
*/
WEAVE_ERROR DMClient::CancelTransactionRequest(uint16_t aTxnId, WEAVE_ERROR aError)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
int i;
if (aTxnId == kTransactionIdNotSpecified)
{
for (i = 0; i<kViewPoolSize; i++)
mViewPool[i].Finalize();
for (i = 0; i<kUpdatePoolSize; i++)
mUpdatePool[i].Finalize();
#if WEAVE_CONFIG_WDM_ALLOW_CLIENT_SUBSCRIPTION
for (i = 0; i<kSubscribePoolSize; i++)
mSubscribePool[i].Finalize();
for (i = 0; i<kCancelSubscriptionPoolSize; i++)
mCancelSubscriptionPool[i].Finalize();
#endif // WEAVE_CONFIG_WDM_ALLOW_CLIENT_SUBSCRIPTION
}
else
{
for (i = 0; i<kViewPoolSize; i++)
{
View &v = mViewPool[i];
if (!v.IsFree())
VerifyOrExit(v.mTxnId != aTxnId, err = v.Finalize());
}
for (i = 0; i<kUpdatePoolSize; i++)
{
Update &u = mUpdatePool[i];
if (!u.IsFree())
VerifyOrExit(u.mTxnId != aTxnId, err = u.Finalize());
}
#if WEAVE_CONFIG_WDM_ALLOW_CLIENT_SUBSCRIPTION
for (i = 0; i<kSubscribePoolSize; i++)
{
Subscribe &s = mSubscribePool[i];
if (!s.IsFree())
VerifyOrExit(s.mTxnId != aTxnId, err = s.Finalize());
}
for (i = 0; i<kCancelSubscriptionPoolSize; i++)
{
CancelSubscription &c = mCancelSubscriptionPool[i];
if (!c.IsFree())
VerifyOrExit(c.mTxnId != aTxnId, err = c.Finalize());
}
#endif // WEAVE_CONFIG_WDM_ALLOW_CLIENT_SUBSCRIPTION
}
exit:
return err;
}
/*
* the following transaction-related methods are not a part of the
* public interface to WDM and so are declared protected in DMClient.h
*/
WEAVE_ERROR DMClient::View::Init(DMClient *aClient, ReferencedTLVData &aPathList, uint16_t aTxnId, uint32_t aTimeout)
{
DMTransaction::Init(aClient, aTxnId, aTimeout);
mPathList = aPathList;
return WEAVE_NO_ERROR;
}
void DMClient::View::Free(void)
{
DMTransaction::Free();
mPathList.free();
SYSTEM_STATS_DECREMENT(nl::Weave::System::Stats::kWDMClient_NumViews);
}
WEAVE_ERROR DMClient::View::SendRequest(PacketBuffer *aBuffer, uint16_t aSendFlags)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
VerifyOrExit(mExchangeCtx, err = WEAVE_ERROR_INCORRECT_STATE);
err = mPathList.pack(aBuffer);
SuccessOrExit(err);
err = mExchangeCtx->SendMessage(kWeaveProfile_WDM, kMsgType_ViewRequest, aBuffer, aSendFlags);
aBuffer = NULL;
exit:
if (aBuffer != NULL)
{
PacketBuffer::Free(aBuffer);
}
/*
* and free the path list since we're done with it. note
* that this ONLY does something substantive if the path
* list has an PacketBuffer associated with it.
*/
mPathList.free();
return err;
}
WEAVE_ERROR DMClient::View::OnStatusReceived(const uint64_t &aResponderId, StatusReport &aStatus)
{
DMClient *client = static_cast<DMClient *>(mEngine);
uint16_t txnId = mTxnId;
Finalize();
return client->ViewConfirm(aResponderId, aStatus, txnId);
}
WEAVE_ERROR DMClient::View::OnResponseReceived(const uint64_t &aResponderId, uint8_t aMsgType, PacketBuffer *aMsg)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
WEAVE_ERROR appErr = WEAVE_NO_ERROR;
DMClient *client = static_cast<DMClient *>(mEngine);
uint16_t txnId = mTxnId;
ReferencedTLVData dataList;
VerifyOrExit(aMsgType == kMsgType_ViewResponse, err = WEAVE_ERROR_INVALID_MESSAGE_TYPE);
err = ReferencedTLVData::parse(aMsg, dataList);
SuccessOrExit(err);
Finalize();
appErr = client->ViewConfirm(aResponderId, dataList, txnId);
if (appErr != WEAVE_NO_ERROR)
WeaveLogError(DataManagement, "DMClient::ViewConfirm => %s", ErrorStr(appErr));
exit:
if (err != WEAVE_NO_ERROR)
OnError(aResponderId, err);
return err;
}
DMClient::View *DMClient::NewView(void)
{
int i;
View *result = NULL;
for (i = 0; i < kViewPoolSize; i++)
{
if (mViewPool[i].IsFree())
{
result = &mViewPool[i];
SYSTEM_STATS_INCREMENT(nl::Weave::System::Stats::kWDMClient_NumViews);
break;
}
}
return result;
}
#if WEAVE_CONFIG_WDM_ALLOW_CLIENT_SUBSCRIPTION
WEAVE_ERROR DMClient::Subscribe::Init(DMClient *aClient, const TopicIdentifier &aTopicId, uint16_t aTxnId, uint32_t aTimeout)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
if (aTopicId == kTopicIdNotSpecified)
{
err = WEAVE_ERROR_INVALID_ARGUMENT;
}
else
{
DMTransaction::Init(aClient, aTxnId, aTimeout);
mPathList.free();
mTopicId = aTopicId;
}
return err;
}
WEAVE_ERROR DMClient::Subscribe::Init(DMClient *aClient, ReferencedTLVData &aPathList, uint16_t aTxnId, uint32_t aTimeout)
{
DMTransaction::Init(aClient, aTxnId, aTimeout);
mTopicId = kTopicIdNotSpecified;
mPathList = aPathList;
return WEAVE_NO_ERROR;
}
void DMClient::Subscribe::Free(void)
{
DMTransaction::Free();
mTopicId = kTopicIdNotSpecified;
mPathList.free();
SYSTEM_STATS_DECREMENT(nl::Weave::System::Stats::kWDMClient_NumSubscribes);
}
WEAVE_ERROR DMClient::Subscribe::SendRequest(PacketBuffer *aBuffer, uint16_t aSendFlags)
{
WEAVE_ERROR err;
VerifyOrExit(mExchangeCtx, err = WEAVE_ERROR_INCORRECT_STATE);
{
MessageIterator i(aBuffer);
i.append();
err = i.write64(mTopicId);
SuccessOrExit(err);
if (!mPathList.isEmpty())
{
err = mPathList.pack(i);
SuccessOrExit(err);
}
}
err = mExchangeCtx->SendMessage(kWeaveProfile_WDM, kMsgType_SubscribeRequest, aBuffer, aSendFlags);
aBuffer = NULL;
exit:
if (aBuffer != NULL)
{
PacketBuffer::Free(aBuffer);
}
/*
* and free the path list since we're done with it. note that this
* ONLY does something substantive if the path list has an
* PacketBuffer associated with it.
*/
mPathList.free();
return err;
}
WEAVE_ERROR DMClient::Subscribe::OnStatusReceived(const uint64_t &aResponderId, StatusReport &aStatus)
{
DMClient *client = static_cast<DMClient *>(mEngine);
uint16_t txnId = mTxnId;
Finalize();
return client->SubscribeConfirm(aResponderId, aStatus, txnId);
}
WEAVE_ERROR DMClient::Subscribe::OnResponseReceived(const uint64_t &aResponderId, uint8_t aMsgType, PacketBuffer *aMsg)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
WEAVE_ERROR appErr = WEAVE_NO_ERROR;
ReferencedTLVData dataList;
TopicIdentifier topicId;
DMClient *client = static_cast<DMClient *>(mEngine);
uint16_t txnId = mTxnId;
{
MessageIterator i(aMsg);
VerifyOrExit(aMsgType == kMsgType_SubscribeResponse, err = WEAVE_ERROR_INVALID_MESSAGE_TYPE);
err = i.read64(&topicId);
SuccessOrExit(err);
err = ReferencedTLVData::parse(i, dataList);
SuccessOrExit(err);
}
/*
* install the subscription here rather than burdening higher layers with it.
*/
err = client->BeginSubscription(topicId, mTopicId, aResponderId);
SuccessOrExit(err);
/*
* if the subscription was requested under a well-known topic
* ID then pass that up to the client code rather than the
* publisher-assigned one.
*/
if (mTopicId != kTopicIdNotSpecified)
topicId = mTopicId;
Finalize();
if (dataList.isEmpty())
appErr = client->SubscribeConfirm(aResponderId, topicId, txnId);
else
appErr = client->SubscribeConfirm(aResponderId, topicId, dataList, txnId);
if (appErr != WEAVE_NO_ERROR)
WeaveLogError(DataManagement, "DMClient::SubscribeConfirm => %s", ErrorStr(appErr));
exit:
if (err != WEAVE_NO_ERROR)
OnError(aResponderId, err);
return err;
}
DMClient::Subscribe *DMClient::NewSubscribe(void)
{
int i;
Subscribe *result = NULL;
for (i = 0; i < kSubscribePoolSize; i++)
{
if (mSubscribePool[i].IsFree())
{
result = &mSubscribePool[i];
SYSTEM_STATS_INCREMENT(nl::Weave::System::Stats::kWDMClient_NumSubscribes);
break;
}
}
return result;
}
WEAVE_ERROR DMClient::CancelSubscription::Init(DMClient *aClient,
const TopicIdentifier &aTopicId,
uint16_t aTxnId,
uint32_t aTimeout)
{
WEAVE_ERROR err;
err = DMTransaction::Init(aClient, aTxnId, aTimeout);
SuccessOrExit(err);
mTopicId = aTopicId;
exit:
return err;
}
void DMClient::CancelSubscription::Free(void)
{
DMTransaction::Free();
mTopicId = kTopicIdNotSpecified;
SYSTEM_STATS_DECREMENT(nl::Weave::System::Stats::kWDMClient_NumCancels);
}
WEAVE_ERROR DMClient::CancelSubscription::SendRequest(PacketBuffer *aBuffer, uint16_t aSendFlags)
{
WEAVE_ERROR err;
VerifyOrExit(mExchangeCtx, err = WEAVE_ERROR_INCORRECT_STATE);
{
MessageIterator i(aBuffer);
i.append();
err = i.write64(mTopicId);
SuccessOrExit(err);
}
err = mExchangeCtx->SendMessage(kWeaveProfile_WDM, kMsgType_CancelSubscriptionRequest, aBuffer, aSendFlags);
aBuffer = NULL;
exit:
if (aBuffer != NULL)
{
PacketBuffer::Free(aBuffer);
}
return err;
}
WEAVE_ERROR DMClient::CancelSubscription::OnStatusReceived(const uint64_t &aResponderId, StatusReport &aStatus)
{
DMClient *client = static_cast<DMClient *>(mEngine);
uint16_t txnId = mTxnId;
Finalize();
return client->CancelSubscriptionConfirm(aResponderId, mTopicId, aStatus, txnId);
}
DMClient::CancelSubscription *DMClient::NewCancelSubscription(void)
{
int i;
CancelSubscription *result = NULL;
for (i = 0; i < kCancelSubscriptionPoolSize; i++)
{
if (mCancelSubscriptionPool[i].IsFree())
{
result = &mCancelSubscriptionPool[i];
SYSTEM_STATS_INCREMENT(nl::Weave::System::Stats::kWDMClient_NumCancels);
break;
}
}
return result;
}
#endif // WEAVE_CONFIG_WDM_ALLOW_CLIENT_SUBSCRIPTION
WEAVE_ERROR DMClient::Update::Init(DMClient *aClient,
ReferencedTLVData &aDataList,
uint16_t aTxnId,
uint32_t aTimeout)
{
DMTransaction::Init(aClient, aTxnId, aTimeout);
mDataList = aDataList;
return WEAVE_NO_ERROR;
}
void DMClient::Update::Free(void)
{
DMTransaction::Free();
mDataList.free();
SYSTEM_STATS_DECREMENT(nl::Weave::System::Stats::kWDMClient_NumUpdates);
}
WEAVE_ERROR DMClient::Update::SendRequest(PacketBuffer *aBuffer, uint16_t aSendFlags)
{
WEAVE_ERROR err;
uint8_t msgType = kMsgType_UpdateRequest;
VerifyOrExit(mExchangeCtx, err = WEAVE_ERROR_INCORRECT_STATE);
err = mDataList.pack(aBuffer);
SuccessOrExit(err);
#if WEAVE_CONFIG_WDM_ALLOW_CLIENT_LEGACY_MESSAGE_TYPES
if (mUseLegacyMsgType)
msgType = kMsgType_UpdateRequest_Deprecated;
#endif // WEAVE_CONFIG_WDM_ALLOW_CLIENT_LEGACY_MESSAGE_TYPES
err = mExchangeCtx->SendMessage(kWeaveProfile_WDM, msgType, aBuffer, aSendFlags);
aBuffer = NULL;
exit:
if (aBuffer != NULL)
{
PacketBuffer::Free(aBuffer);
}
/*
* and free the data list since we're done with it. note
* that this ONLY does something substantive if the data
* list has an PacketBuffer associated with it.
*/
mDataList.free();
return err;
}
WEAVE_ERROR DMClient::Update::OnStatusReceived(const uint64_t &aResponderId, StatusReport &aStatus)
{
DMClient *client = static_cast<DMClient *>(mEngine);
uint16_t txnId = mTxnId;
Finalize();
return client->UpdateConfirm(aResponderId, aStatus, txnId);
}
DMClient::Update *DMClient::NewUpdate(void)
{
int i;
Update *result = NULL;
for (i = 0; i < kUpdatePoolSize; i++)
{
if (mUpdatePool[i].IsFree())
{
result = &mUpdatePool[i];
SYSTEM_STATS_INCREMENT(nl::Weave::System::Stats::kWDMClient_NumUpdates);
break;
}
}
return result;
}