Skip to content

Commit

Permalink
Refactoring OTA Requestor. Code compiles and links with this commit
Browse files Browse the repository at this point in the history
  • Loading branch information
selissia committed Nov 22, 2021
1 parent 21b7aeb commit 28f991b
Show file tree
Hide file tree
Showing 3 changed files with 237 additions and 5 deletions.
4 changes: 2 additions & 2 deletions examples/ota-requestor-app/linux/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -219,15 +219,15 @@ int main(int argc, char * argv[])
LinuxOTARequestorDriver *requestorUser = new LinuxOTARequestorDriver;

// Connect the two objects
requestorCore->setOtaRequestorDriver(requestorUser);
requestorCore->SetOtaRequestorDriver(requestorUser);

OTADownloader *downloaderCore = new OTADownloader;
// TODO: enable SetDownloaderInstance(downloaderCore);

LinuxOTAImageProcessor *downloaderUser = new LinuxOTAImageProcessor;

// Connect the two objects
downloaderCore->setDelegate(downloaderUser);
downloaderCore->SetImageProcessorDelegate(downloaderUser);


// If a delay is provided, QueryImage after the timer expires REFACTOR DELETE THIS ??
Expand Down
221 changes: 219 additions & 2 deletions src/app/clusters/ota-requestor/OTARequestor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,53 @@
#include <app/util/util.h>
#include <app/server/Server.h>
#include <platform/CHIPDeviceLayer.h>
#include "BDXDownloader.h"

#include <controller/CHIPDeviceControllerFactory.h>
#include <controller/CommissioneeDeviceProxy.h>
#include <controller/ExampleOperationalCredentialsIssuer.h>

#include <zap-generated/CHIPClientCallbacks.h>
#include <zap-generated/CHIPClusters.h>

using chip::ByteSpan;
using chip::CASESessionManager;
using chip::CASESessionManagerConfig;
using chip::CharSpan;
using chip::DeviceProxy;
using chip::EndpointId;
using chip::FabricIndex;
using chip::FabricInfo;
using chip::NodeId;
using chip::OnDeviceConnected;
using chip::OnDeviceConnectionFailure;
using chip::PeerId;
using chip::Server;
using chip::VendorId;
using chip::bdx::TransferSession;
using chip::Callback::Callback;
using chip::System::Layer;
using chip::Transport::PeerAddress;
//using namespace chip::ArgParser;
using namespace chip::Messaging;
using namespace chip::app::Clusters::OtaSoftwareUpdateProvider::Commands;

// Global instance of the OTARequestorInterface.
OTARequestorInterface *globalOTARequestorInstance = nullptr;

constexpr uint32_t kImmediateStartDelayMs = 1; // Start the timer with this value when starting OTA "immediately"

constexpr uint8_t kNodeIdHexStringMaxLen = 16;
// Callbacks for connection management
void OnConnected(void * context, chip::DeviceProxy * deviceProxy);
Callback<OnDeviceConnected> mOnConnectedCallback(OnConnected, nullptr);

void OnConnectionFailure(void * context, NodeId deviceId, CHIP_ERROR error);
Callback<OnDeviceConnectionFailure> mOnConnectionFailureCallback(OnConnectionFailure, nullptr);

void OnQueryImageResponse(void * context, const QueryImageResponse::DecodableType & response);
void OnQueryImageFailure(void * context, EmberAfStatus status);

void SetRequestorInstance(OTARequestorInterface *instance)
{
globalOTARequestorInstance = instance;
Expand All @@ -37,13 +78,98 @@ OTARequestorInterface *GetRequestorInstance()
return globalOTARequestorInstance;
}


void StartDelayTimerHandler(chip::System::Layer * systemLayer, void * appState)
{
VerifyOrReturn(appState != nullptr);
static_cast<OTARequestor *>(appState)->ConnectToProvider();
}

void OnQueryImageFailure(void * context, EmberAfStatus status)
{
ChipLogDetail(SoftwareUpdate, "QueryImage failure response %" PRIu8, status);
}

void OnConnectionFailure(void * context, NodeId deviceId, CHIP_ERROR error)
{
ChipLogError(SoftwareUpdate, "failed to connect to 0x%" PRIX64 ": %" CHIP_ERROR_FORMAT, deviceId, error.Format());
}
// Finds the Requestor instance and calls the corresponding OTARequestor member function
void OnQueryImageResponse(void * context, const QueryImageResponse::DecodableType & response)
{
OTARequestor *requestorCore = static_cast<OTARequestor *>(GetRequestorInstance());

assert(requestorCore != nullptr);

requestorCore->mOnQueryImageResponse(context, response);
}

void OTARequestor::mOnQueryImageResponse(void * context, const QueryImageResponse::DecodableType & response)
{
ChipLogDetail(SoftwareUpdate, "QueryImageResponse responded with action %" PRIu8, response.status);
ChipLogDetail(SoftwareUpdate, " status: %" PRIu8 "", response.status);
if (response.delayedActionTime.HasValue())
{
ChipLogDetail(SoftwareUpdate, " delayedActionTime: %" PRIu32 "", response.delayedActionTime.Value());
}
if (response.imageURI.HasValue())
{
ChipLogDetail(SoftwareUpdate, " imageURI: %.*s", static_cast<int>(response.imageURI.Value().size()),
response.imageURI.Value().data());
}
if (response.softwareVersion.HasValue())
{
ChipLogDetail(SoftwareUpdate, " softwareVersion: %" PRIu32 "", response.softwareVersion.Value());
}
if (response.softwareVersionString.HasValue())
{
ChipLogDetail(SoftwareUpdate, " softwareVersionString: %.*s",
static_cast<int>(response.softwareVersionString.Value().size()),
response.softwareVersionString.Value().data());
}
if (response.updateToken.HasValue())
{
ChipLogDetail(SoftwareUpdate, " updateToken: %zu", response.updateToken.Value().size());
}
if (response.userConsentNeeded.HasValue())
{
ChipLogDetail(SoftwareUpdate, " userConsentNeeded: %d", response.userConsentNeeded.Value());
}
if (response.metadataForRequestor.HasValue())
{
ChipLogDetail(SoftwareUpdate, " metadataForRequestor: %zu", response.metadataForRequestor.Value().size());
}

// Parse out the provider node ID from the ImageURI
NodeId nodeId = chip::kUndefinedNodeId;
if (response.imageURI.HasValue())
{
CharSpan nodeIdString = response.imageURI.Value().SubSpan(6, kNodeIdHexStringMaxLen);

uint8_t buffer[kNodeIdHexStringMaxLen];
if (chip::Encoding::HexToBytes(nodeIdString.data(), nodeIdString.size(), buffer, kNodeIdHexStringMaxLen) == 0)
{
ChipLogError(SoftwareUpdate, "Cannot convert provider Node ID: %.*s", static_cast<int>(nodeIdString.size()),
nodeIdString.data());
return;
}

nodeId = chip::Encoding::BigEndian::Get64(buffer);
}

// Use the same CSM from sending of QueryImage based on the assumption that the provider node ID that will supply the OTA image
// is on the same fabric as the sender of the QueryImageResponse
if (mCASESessionManager != nullptr)
{
// Establish a CASE session with the parsed node ID to obtain exchange context and session handle for BDX transfer
onConnectedState = kStartBDX;
CHIP_ERROR err = mCASESessionManager->FindOrEstablishSession(nodeId, &mOnConnectedCallback, &mOnConnectionFailureCallback);
if (err != CHIP_NO_ERROR)
{
ChipLogError(SoftwareUpdate, "Cannot establish connection to peer device: %" CHIP_ERROR_FORMAT, err.Format());
}
}
}

EmberAfStatus OTARequestor::HandleAnnounceOTAProvider(
chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath,
const chip::app::Clusters::OtaSoftwareUpdateRequestor::Commands::AnnounceOtaProvider::DecodableType & commandData)
Expand Down Expand Up @@ -154,7 +280,6 @@ void OTARequestor::ConnectToProvider()
{
chip::NodeId peerNodeId = mProviderNodeId;
chip::FabricIndex peerFabricIndex = mProviderFabricIndex;
chip::Server * server = &(chip::Server::GetInstance());

CHIP_ERROR err = SetupCASESessionManager(peerFabricIndex);
if (err != CHIP_NO_ERROR)
Expand Down Expand Up @@ -187,6 +312,98 @@ void OTARequestor::ConnectToProvider()
}



// Called whenever FindOrEstablishSession is successful. Finds the Requestor instance
// and calls the corresponding OTARequestor member function
void OnConnected(void * context, chip::DeviceProxy * deviceProxy)
{
OTARequestor *requestorCore = static_cast<OTARequestor *>(GetRequestorInstance());

assert(requestorCore != nullptr);

requestorCore->mOnConnected(context, deviceProxy);
}

// Member function called whenever FindOrEstablishSession is successful
void OTARequestor::mOnConnected(void * context, chip::DeviceProxy * deviceProxy)
{
switch (onConnectedState)
{
case kQueryImage: {
CHIP_ERROR err = CHIP_NO_ERROR;
chip::Controller::OtaSoftwareUpdateProviderCluster cluster;
constexpr EndpointId kOtaProviderEndpoint = 0;

// These QueryImage params have been chosen arbitrarily
constexpr VendorId kExampleVendorId = VendorId::Common;
constexpr uint16_t kExampleProductId = 77;
constexpr uint16_t kExampleHWVersion = 3;
constexpr uint16_t kExampleSoftwareVersion = 0;
constexpr EmberAfOTADownloadProtocol kExampleProtocolsSupported[] = { EMBER_ZCL_OTA_DOWNLOAD_PROTOCOL_BDX_SYNCHRONOUS };
const char locationBuf[] = { 'U', 'S' };
CharSpan exampleLocation(locationBuf);
constexpr bool kExampleClientCanConsent = false;
ByteSpan metadata;

err = cluster.Associate(deviceProxy, kOtaProviderEndpoint);
if (err != CHIP_NO_ERROR)
{
ChipLogError(SoftwareUpdate, "Associate() failed: %" CHIP_ERROR_FORMAT, err.Format());
return;
}
QueryImage::Type args;
args.vendorId = kExampleVendorId;
args.productId = kExampleProductId;
args.softwareVersion = kExampleSoftwareVersion;
args.protocolsSupported = kExampleProtocolsSupported;
args.hardwareVersion.Emplace(kExampleHWVersion);
args.location.Emplace(exampleLocation);
args.requestorCanConsent.Emplace(kExampleClientCanConsent);
args.metadataForProvider.Emplace(metadata);
err = cluster.InvokeCommand(args, /* context = */ nullptr, OnQueryImageResponse, OnQueryImageFailure);
if (err != CHIP_NO_ERROR)
{
ChipLogError(SoftwareUpdate, "QueryImage() failed: %" CHIP_ERROR_FORMAT, err.Format());
}

break;
}
case kStartBDX: {
TransferSession::TransferInitData initOptions;
initOptions.TransferCtlFlags = chip::bdx::TransferControlFlags::kReceiverDrive;
initOptions.MaxBlockSize = 1024;
char testFileDes[9] = { "test.txt" };
initOptions.FileDesLength = static_cast<uint16_t>(strlen(testFileDes));
initOptions.FileDesignator = reinterpret_cast<uint8_t *>(testFileDes);

if (deviceProxy != nullptr)
{
chip::Messaging::ExchangeManager * exchangeMgr = deviceProxy->GetExchangeManager();
chip::Optional<chip::SessionHandle> session = deviceProxy->GetSecureSession();
if (exchangeMgr != nullptr && session.HasValue())
{
exchangeCtx = exchangeMgr->NewContext(session.Value(), &bdxDownloader);
}

if (exchangeCtx == nullptr)
{
ChipLogError(BDX, "unable to allocate ec: exchangeMgr=%p sessionExists? %u", exchangeMgr, session.HasValue());
return;
}
}

bdxDownloader.SetInitialExchange(exchangeCtx);

// This will kick of a timer which will regularly check for updates to the bdx::TransferSession state machine.
bdxDownloader.InitiateTransfer(&chip::DeviceLayer::SystemLayer(), chip::bdx::TransferRole::kReceiver, initOptions,
chip::System::Clock::Seconds16(20));
break;
}
default:
break;
}
}

#if 0 // LISS for now !!!!!!!!!!!!!!!!!!!!!!!


Expand Down
17 changes: 16 additions & 1 deletion src/app/clusters/ota-requestor/ota-requestor.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "ota-requestor-driver.h"
#include "ota-requestor-interface.h"
#include <app/CASESessionManager.h>
#include "BDXDownloader.h"
#pragma once

// This class implements all of the core logic of the OTA Requestor
Expand Down Expand Up @@ -53,15 +54,29 @@ class OTARequestor : public OTARequestorInterface
// Virtual functions from OTARequestorInterface -- end
void ConnectToProvider();

void mOnConnected(void * context, chip::DeviceProxy * deviceProxy);
void mOnQueryImageResponse(void * context,
const chip::app::Clusters::OtaSoftwareUpdateProvider::Commands::QueryImageResponse::DecodableType & response);

private:

// Enums
// Various cases for when OnConnected callback could be called
enum OnConnectedState
{
kQueryImage = 0,
kStartBDX,
};

// Variables
OTARequestorDriver * mOtaRequestorDriver;
chip::NodeId mProviderNodeId;
chip::FabricIndex mProviderFabricIndex;
uint32_t mOtaStartDelayMs;
chip::CASESessionManager * mCASESessionManager = nullptr;

OnConnectedState onConnectedState = kQueryImage;
chip::Messaging::ExchangeContext * exchangeCtx = nullptr;
BdxDownloader bdxDownloader;
// Functions
CHIP_ERROR SetupCASESessionManager(chip::FabricIndex fabricIndex);
};

0 comments on commit 28f991b

Please sign in to comment.