blob: 94ff930ded176ab1a2188d3cdff86a9769a7bfe1 [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
* This file defines command handle for Weave
* Data Management (WDM) profile.
*
*/
#ifndef _WEAVE_DATA_MANAGEMENT_COMMAND_SENDER_CURRENT_H
#define _WEAVE_DATA_MANAGEMENT_COMMAND_SENDER_CURRENT_H
#include <Weave/Profiles/data-management/Current/WdmManagedNamespace.h>
#include <Weave/Core/WeaveCore.h>
#include <Weave/Profiles/data-management/Current/TraitCatalog.h>
#include <Weave/Profiles/data-management/Current/Command.h>
namespace nl {
namespace Weave {
namespace Profiles {
namespace WeaveMakeManagedNamespaceIdentifier(DataManagement, kWeaveManagedNamespaceDesignation_Current) {
/**
* @class CommandSender
*
* @note This class encapsulates the protocol details of sending commands, simplifying the mechanics involved for
* applications. The application provides a PacketBuffer containing the payload of the command as well as
* an optional set of arguments that alter the contents of the command header as well as the behavior of the command.
*
* The utility of this wrapper around command handling is indeed limited, mainly due to the complexity/flexibility involved
* in security validation and data serialization/de-serialization.
*
* The details for command validation is still TBD
*
* This class also helps applications infer if the data within an associated TraitDataSink has caught up to the side-effects
* of the commmand (based on the version provided in the command response). The application is responsible for managing the
* storage of that object.
*
* ## Weave Binding
*
* An object of this class can be intialized with a Weave Binding, which will serve as the default
* Binding to use to send Commands. The user may also provide a Binding to each call of
* SendCommand(), which will override the default Binding. It is not necessary to provide a default
* Binding, however any binding provided to CommandSender must already be initialized.
*
* ## EventCallback
*
* The user must define a function of this type if they wish to be updated about
* events that happen after the sending of the command (see "API Events" below).
* This can be NULL if the user does not care what happens after the command is
* sent.
*
* ## API Events
*
* The following events are the possible outcomes after sending a Command:
*
* ### CommunicationError
*
* An error occurred while forming or sending the Command, or while waiting for a response.
* Examples of errors that can occur while waiting for a response are key errors or unexpected close
* of a connection. The error reason will be contained in the InEventParam argument to the
* EventCallback handler.
*
* ### InProgressReceived
*
* The recipient can send an 'in progress' message which signifies that the Command has been
* receieved, but not yet completed. Once completed, the recipient will send a Response or
* StatusReport. Sending an 'in progress' message is not required.
*
* ### StatusReportReceived
*
* Receipt of a StatusReport implies that there was an error in processing the Command. The
* StatusReport can be accessed through the InEventParam.
*
* ### ResponseReceived
*
* Receiving a Response implies that the Command recipient handled the Command successfully. The
* Response may contain a payload or it may not. If the applcation desires to keep the packet
* buffer, it may call ExchangeContext::AddRef() to increment the ref count.
*
*/
class CommandSender
{
public:
enum EventType
{
kEvent_CommunicationError = 1, // All errors during communication (transmission error, failure to get a response timeout) will be communicated through this event
kEvent_InProgressReceived = 2, // Receipt of an 'in progress' message
kEvent_StatusReportReceived = 3, // On receipt of a status report
kEvent_ResponseReceived = 4, // On receipt of a command response
kEvent_DefaultCheck = 100, //< Used to verify correct default event handling in the application.
};
/**
* Data returned to the sender by the recipient of the custom command.
*/
struct InEventParam
{
void Clear(void) { memset(this, 0, sizeof(*this)); }
union {
struct
{
Profiles::StatusReporting::StatusReport *statusReport;
} StatusReportReceived;
/*
* All communication errors, including transport errors on transmission
* as well as failure to receive a response (WEAVE_ERROR_TIMEOUT).
*/
struct
{
WEAVE_ERROR error;
} CommunicationError;
/* On receipt of a command response, both a reader positioned at the
* payload as well as a pointer to the packet buffer is provided. If the applicaton
* desires to hold on to the buffer, it can do so by incrementing the ref count on the buffer
*/
struct
{
uint64_t traitDataVersion;
nl::Weave::TLV::TLVReader *reader;
nl::Weave::PacketBuffer *packetBuf;
} ResponseReceived;
};
};
/**
* Data returned back to a CommandSender object from an EventCallback
* function.
*/
struct OutEventParam
{
void Clear(void) { memset(this, 0, sizeof(*this)); }
bool defaultHandlerCalled; /**< should be set if DefaultEventHandler is called */
};
typedef void (*EventCallback)(void * const aAppState, EventType aEvent, const InEventParam &aInParam, OutEventParam &aOutEventParam);
/**
* Encapsulates arguments to be passed into SendCommand().
*
* At minimum, ResourceId, ProfileId, and CommandType should be set before
* the struct is passed to SendCommand(). InstanceId, Flags, and
* VersionRange will default to 0 if not set.
*/
struct SendParams {
WEAVE_ERROR PopulateTraitPath(TraitCatalogBase<TraitDataSink> *aCatalog, TraitDataSink *aSink, uint32_t aCommandType);
TraitDataSink *Sink;
ResourceIdentifier ResourceId;
uint32_t ProfileId;
SchemaVersionRange VersionRange;
uint64_t InstanceId;
uint32_t CommandType;
uint8_t Flags;
uint64_t MustBeVersion;
uint64_t InitiationTimeMicroSecond;
uint64_t ActionTimeMicroSecond;
uint64_t ExpiryTimeMicroSecond;
/*
* Set to non-zero number to specify command timeout that's different than the one specified in the binding
*/
uint32_t ResponseTimeoutMsOverride;
};
public:
struct SynchronizedTraitState;
static void DefaultEventHandler(void *aAppState, EventType aEvent, const InEventParam& aInParam, OutEventParam& aOutParam);
WEAVE_ERROR Init(nl::Weave::Binding *aBinding, const EventCallback aEventCallback, void * const aAppState);
WEAVE_ERROR SendCommand(nl::Weave::PacketBuffer *aPayload, nl::Weave::Binding *aBinding, ResourceIdentifier &aResourceId, uint32_t aProfileId, uint32_t aCommandType);
WEAVE_ERROR SendCommand(nl::Weave::PacketBuffer *aPayload, nl::Weave::Binding *aBinding, SendParams &aSendParams);
void Close(bool aAbortNow = false);
void SetSynchronizedTraitState(SynchronizedTraitState *aTraitState);
SynchronizedTraitState *mSyncronizedTraitState = NULL;
void *mAppState = NULL;
private:
static void OnMessageReceived(nl::Weave::ExchangeContext *aEC, const nl::Weave::IPPacketInfo *aPktInfo, const nl::Weave::WeaveMessageInfo *aMsgInfo, uint32_t aProfileId, uint8_t aMsgType, nl::Weave::PacketBuffer *aPayload);
static void OnResponseTimeout(nl::Weave::ExchangeContext *aEC);
static void OnSendError(nl::Weave::ExchangeContext *aEC, WEAVE_ERROR sendError, void *aMsgCtxt);
EventCallback mEventCallback;
nl::Weave::Binding *mBinding = NULL;
nl::Weave::PacketBuffer *mPacketBuf = NULL;
nl::Weave::ExchangeContext *mEC = NULL;
uint8_t mFlags = 0;
};
/**
* @class CommandSender::SynchronizedTraitState
*
* @note This class helps inform if an associated TraitDataSink has caught up to all the side-effects of a command.
* The CommandSender class is responsible for filling in the requisite information necessary at the time of
* request transmission and response reception.
*
* The application can use this in one of two modalities:
* a) Have a valid data version in the data sink before starting to send commands
* b) Never having a valid data version before starting to send commands.
*
* In the former case, the version of the sink prior to sending the command is known, allowing for an accurate
* later inference of whether the sink has caught up.
*
* In the latter case, the absence of a prior version results in the logic to infer synchronization reverting
* to a window-based heuristic. This is due to the presence of randomized data versions that can result in the
* received data version from the publisher jumping to a lower number post command reception.
*
*/
struct CommandSender::SynchronizedTraitState
{
WEAVE_ERROR Init();
bool HasDataCaughtUp(void);
private:
// If we dont' have a pre version, it means we're still in the act of subscribing after having received the command response.
// We have to make take a guess at what the pre version could be relative to what the received command response version is.
// We'll need to define a 'stale window' backward of the received command response version that defines a region of versions that don't contain
// the side-effects of the command.
//
// The case that drives the sizing of this window is if we receive a notify from the responder containing data prior to the command being received on the responder,
// but the notify arriving after the command has been transmitted to the responder. Later when we get the response (with the notify arriving either before or after the response),
// we need to ensure we still infer correctly that the data sink has not caught up.
//
// The worst case is if the notify was retransmitted a couple of times (but not failing). We have to figure out the effective number of data changes (i.e version up-ticks)
// during that window of time.
//
// Scenario:
//
// 1. Responder sends out notify to receiver that doesn't contain the side-effects of the command. Data version = 10 on the trait source.
// 2. It gets re-transmitted a couple of times (up to say 10s) before the last one gets through.
// 3. In this time, a 100Hz temperature sensor has been ticking the version away, the version being = 1010 by now.
// 4. The command request is received and a response is sent back (v = 1011).
// 5. When the stale notify finally makes it to the receiver, we need to infer that 10 is still stale (i.e not caught up).
//
static const uint32_t kCommandSideEffectWindowSize = 1000;
uint64_t mPreCommandVersion;
uint64_t mPostCommandVersion;
TraitDataSink *mDataSink;
friend class CommandSender;
friend class TestTdm;
};
} // namespace WeaveMakeManagedNamespaceIdentifier(DataManagement, kWeaveManagedNamespaceDesignation_Current)
} // namespace Profiles
} // namespace Weave
} // namespace nl
#endif // _WEAVE_DATA_MANAGEMENT_COMMAND_CURRENT_H