blob: 6c143457736ed2b71dfc49a8b04024389d6e4555 [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 implements a derived Weave Data Management (WDM)
* publisher for the Weave Data Management profile and protocol
* used for the Weave mock device command line functional testing
* tool.
*
*/
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#include <assert.h>
#include "MockDMPublisher.h"
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;
WEAVE_ERROR MockDMPublisher::ViewIndication(ExchangeContext *aResponseCtx, ReferencedTLVData &aPathList)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
PacketBuffer* buf = PacketBuffer::New();
StatusReport report;
ReferencedTLVData dataList;
// when a failure is requested, this is the mode;
uint8_t mode = kFailureMode_Invalid;
printf("processing: <view indication>\n");
if (!buf)
{
err = WEAVE_ERROR_NO_MEMORY;
}
else
{
/*
* so we've got a path list and we want to extract the relevant
* data. just call retrieve after setting up a data list to store
* the result in.
*/
dataList.init(buf);
err = mDatabase.Retrieve(aPathList, dataList);
}
if (err == WEAVE_NO_ERROR)
{
err = report.init(kWeaveProfile_Common, kStatus_Success);
if (err == WEAVE_NO_ERROR)
err = ViewResponse(aResponseCtx, report, &dataList);
}
else
{
/*
* we're using view requests to test special failure cases,
* e.g a closed connection in the middle of a transaction. the
* cue for this is an invalid profile ID (see TestProfile.h)
* and instance IDs that select different failure tests.
*/
if (err == WEAVE_ERROR_INVALID_PROFILE_ID)
{
mode = LookupFailureMode(aPathList);
switch (mode)
{
case kFailureMode_CloseConnection:
printf("<view indication> failure requested: closing connection\n");
aResponseCtx->Con->Close();
break;
case kFailureMode_NoResponse:
printf("<view indication> failure requested: no response\n");
break;
default:
printf("<view indication> failure requested: invalid request\n");
break;
}
err = WEAVE_NO_ERROR;
}
/*
* if there's any other error here it was in the foregoing
* retrieval of the data list. in any case, we should still be
* able to send back status but we shouldn't include the data
* list.
*/
else
{
printf("<view indication> error: %s\n", ErrorStr(err));
err = report.init(err);
if (err == WEAVE_NO_ERROR)
err = ViewResponse(aResponseCtx, report, NULL);
if (err != WEAVE_NO_ERROR)
printf("could not send view response: %s\n", ErrorStr(err));
}
}
aResponseCtx->Close();
PacketBuffer::Free(buf);
return err;
}
WEAVE_ERROR MockDMPublisher::UpdateIndication(ExchangeContext *aResponseCtx, ReferencedTLVData &aDataList)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
StatusReport report;
printf("processing: <update indication>\n");
/*
* store the data list and update the version
*/
err = mDatabase.Store(aDataList);
SuccessOrExit(err);
mDatabase.mTestData.mVersion++;
exit:
if (err == WEAVE_NO_ERROR)
{
err = report.init(kWeaveProfile_Common, kStatus_Success);
}
else
{
/*
* if there's an error here it was in the foregoing storage
* of the data list. send an internal error status.
*/
printf("<update indication> error: %s\n", ErrorStr(err));
err = report.init(err);
}
if (err == WEAVE_NO_ERROR)
err = StatusResponse(aResponseCtx, report);
aResponseCtx->Close();
if (err != WEAVE_NO_ERROR)
/*
* if there's an error it can only be in formatting or
* sending the response.
*/
printf("could not send update response: %s\n", ErrorStr(err));
return err;
}
void MockDMPublisher::IncompleteIndication(const uint64_t &aPeerNodeId, StatusReport &aReport)
{
printf("processing: <incomplete indication>\n");
}
#if WEAVE_CONFIG_WDM_ALLOW_PUBLISHER_SUBSCRIPTION
WEAVE_ERROR MockDMPublisher::SubscribeIndication(ExchangeContext *aResponseCtx, const TopicIdentifier &aTopicId)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
uint8_t dataBuf[kTestBufferSize];
ReferencedTLVData dataList;
ReferencedTLVData *pDataList = NULL;
TLVWriter writer;
StatusReport report;
printf("processing: <subscribe indication>\n");
/*
* OK. if it's a topic ID we "know" then we respond with
* success and AND we get together a data list to return
* otherwise, we respond with non-success status
*/
if (aTopicId == kTestTopic)
{
// make a nice success-oriented status report
err = report.init(kWeaveProfile_Common, kStatus_Success);
SuccessOrExit(err);
// retrieve the current contents of the database
err = dataList.init(0, kTestBufferSize, dataBuf);
SuccessOrExit(err);
err = StartDataList(dataList, writer);
SuccessOrExit(err);
err = StartDataListElement(writer);
SuccessOrExit(err);
err = mDatabase.mTestData.Retrieve(writer);
SuccessOrExit(err);
err = EndDataListElement(writer);
SuccessOrExit(err);
EndList(dataList, writer);
pDataList = &dataList;
}
else
{
err = report.init(kWeaveProfile_Common, kStatus_UnknownTopic);
SuccessOrExit(err);
}
exit:
if (err == WEAVE_NO_ERROR)
{
err = SubscribeResponse(aResponseCtx, report, aTopicId, pDataList);
}
else
{
/*
* if there's an error here it was in the foregoing. send an
* internal error status.
*/
printf("<subscribe indication> error: %s\n", ErrorStr(err));
err = report.init(err);
if (err == WEAVE_NO_ERROR)
err = StatusResponse(aResponseCtx, report);
}
aResponseCtx->Close();
if (err != WEAVE_NO_ERROR)
/*
* if there's an error it can only be in formatting or
* sending the response.
*/
printf("could not send subscribe response: %s\n", ErrorStr(err));
return err;
}
WEAVE_ERROR MockDMPublisher::SubscribeIndication(ExchangeContext *aResponseCtx, ReferencedTLVData &aPathList)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
StatusReport report;
printf("processing: <subscribe indication>\n");
/*
* we're not currently supporting this form of subscription so
* just send 'em a status report saying so.
*/
printf("<subscribe indication> error: unsupported subscription type\n");
err = report.init(kWeaveProfile_WDM, kStatus_UnsupportedSubscriptionMode);
if (err == WEAVE_NO_ERROR)
err = StatusResponse(aResponseCtx, report);
aResponseCtx->Close();
if (err != WEAVE_NO_ERROR)
/*
* if there's an error it can only be in formatting or
* sending the response.
*/
printf("could not send subscribe response: %s\n", ErrorStr(err));
return err;
}
WEAVE_ERROR MockDMPublisher::UnsubscribeIndication(const uint64_t &aClientId, const TopicIdentifier &aTopicId, StatusReport &aReport)
{
printf("processing: <unsubscribe indication 0x%" PRIx64 ", 0x%" PRIx64 ">\n", aClientId, aTopicId);
return WEAVE_NO_ERROR;
}
WEAVE_ERROR MockDMPublisher::CancelSubscriptionIndication(ExchangeContext *aResponseCtx, const TopicIdentifier &aTopicId)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
printf("processing: <cancel subscription indication>\n");
err = DMPublisher::CancelSubscriptionIndication(aResponseCtx, aTopicId);
if (SubscriptionTableEmpty())
printf("--- empty subscription table ---\n");
return err;
}
WEAVE_ERROR MockDMPublisher::NotifyConfirm(const uint64_t &aResponderId, const TopicIdentifier &aTopicId, StatusReport &aStatus, uint16_t aTxnId)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
printf("processing: <notify confirm>\n");
err = DMPublisher::NotifyConfirm(aResponderId, aTopicId, aStatus, aTxnId);
if (err != WEAVE_NO_ERROR)
EndSubscription(aTopicId, aResponderId);
return err;
}
WEAVE_ERROR MockDMPublisher::Republish(void)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
uint8_t pathBuf[kTestBufferSize];
TLVWriter writer;
ReferencedTLVData pathList;
uint8_t dataBuf[kTestBufferSize];
ReferencedTLVData dataList;
if (++mRepublicationCounter > kUpdatePeriod)
{
mRepublicationCounter = 0;
if (!SubscriptionTableEmpty())
{
printf("change profile data and notify clients\n");
mDatabase.ChangeProfileData();
// extract the modified data using a path list
writer.Init(pathBuf, kTestBufferSize);
err = StartPathList(writer);
SuccessOrExit(err);
err = EncodePath(writer, AnonymousTag, kWeaveProfile_Test, kInstanceIdNotSpecified, 1, ContextTag(kTag_IntegerItem));
SuccessOrExit(err);
err = EndList(writer);
SuccessOrExit(err);
err = writer.Finalize();
SuccessOrExit(err);
err = pathList.init(writer.GetLengthWritten(), kTestBufferSize, pathBuf);
SuccessOrExit(err);
err = dataList.init(0, kTestBufferSize, dataBuf);
SuccessOrExit(err);
err = mDatabase.Retrieve(pathList, dataList);
SuccessOrExit(err);
// and request a notification
err = NotifyRequest(kTestTopic, dataList, 1, kDefaultDMResponseTimeout);
}
}
exit:
if (err != WEAVE_NO_ERROR)
{
mDatabase.RevertProfileData();
printf("error in change and notify: %s\n", ErrorStr(err));
}
return err;
}
#endif // WEAVE_CONFIG_WDM_ALLOW_PUBLISHER_SUBSCRIPTION