diff --git a/src/cli/README.md b/src/cli/README.md index 5dea63500..051b89546 100644 --- a/src/cli/README.md +++ b/src/cli/README.md @@ -14,7 +14,7 @@ OpenThread test scripts use the CLI to execute test cases. * [child](#child-list) * [childmax](#childmax) * [childtimeout](#childtimeout) -* [coap](#coap-server-phase) +* [coap](#coap-start) * [commissioner](#commissioner-start-provisioningurl) * [contextreusedelay](#contextreusedelay) * [counter](#counter) @@ -271,55 +271,57 @@ Set the Thread Child Timeout value. Done ``` -### coap server \ +### coap start -Starts and stops the simple CoAP server. - -* phase: Either "start" or "stop" the server. +Starts the application coap service. ```bash -> coap server start -Server started with resource '': Done -> coap server stop -Server stopped: Done +> coap start +Coap service started: Done ``` -### coap server name \[URI\] +### coap stop -Outputs the currently used URI String of the CoAP resource. - -* URI: If provided the URI String will be changed to the new value. +Stops the application coap service. ```bash -> coap server name -Current resource name is '': Done -> coap server name test -Changing resource name to 'test': Done +> coap start +Coap service stopped: Done ``` -### coap client \ \ \ \[payload\] \[messageType\] +### coap resource \[uri-path\] -Simple CoAP client that can send Non-/Confirmable GET/PUT/POST/DELETE messages. +Sets the URI-Path for the test resource. + +```bash +> coap resource test +Resource name is 'test': Done +> coap resource +Resource name is 'test': Done +``` + +### coap \ \ \ \[payload\] \[type\] * method: CoAP method to be used (GET/PUT/POST/DELETE). -* IPv6address: IP address of the CoAP server to query. -* URI: URI String of the resource on the CoAP server. +* address: IP address of the CoAP server to query. +* uri: URI String of the resource on the CoAP server. * payload: In case of PUT/POST/DELETE a payload can be encapsulated. -* messageType: Switch between confirmable ("con") and non-confirmable (default). +* type: Switch between confirmable ("con") and non-confirmable (default). ```bash -> coap client get fdde:ad00:beef:0:dbaa:f1d0:8afb:30dc test -Sending CoAP message: Done -Received CoAP request from [fdde:ad00:beef:0:dbaa:f1d0:8afb:30dc]: GET -CoAP response sent successfully! -Received CoAP response with payload: 30 -> coap client put fdde:ad00:beef:0:dbaa:f1d0:8afb:30dc test non-con somePayload -Sending CoAP message: Done -> coap client put fdde:ad00:beef:0:dbaa:f1d0:8afb:30dc test con 123 -Sending CoAP message: Done -Received CoAP request from [fdde:ad00:beef:0:dbaa:f1d0:8afb:30dc]: PUT with payload: ba 00 00 20 -CoAP response sent successfully! -Received CoAP response +> coap get fdde:ad00:beef:0:dbaa:f1d0:8afb:30dc test +Sending coap message: Done +Received coap request from [fdde:ad00:beef:0:dbaa:f1d0:8afb:30dc]: GET +coap response sent successfully! +Received coap response with payload: 30 +> coap put fdde:ad00:beef:0:dbaa:f1d0:8afb:30dc test non-con somePayload +Sending coap message: Done +Received coap request from [fdde:ad00:beef:0:dbaa:f1d0:8afb:30dc]: PUT with payload: 73 6f 6d 65 50 61 79 6c 6f 61 64 +> coap put fdde:ad00:beef:0:dbaa:f1d0:8afb:30dc test con 123 +Sending coap message: Done +Received coap request from [fdde:ad00:beef:0:dbaa:f1d0:8afb:30dc]: PUT with payload: 31 32 33 +coap response sent successfully! +Received coap response ``` ### commissioner start \ diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index bf4359151..e649a2716 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -223,6 +223,9 @@ typedef otPtr otBufferPtr; typedef otPtr otStringPtr; Interpreter::Interpreter(otInstance *aInstance): +#if OPENTHREAD_ENABLE_APPLICATION_COAP + mCoap(*this), +#endif mServer(NULL), #ifdef OTDLL mApiInstance(otApiInit()), @@ -313,7 +316,7 @@ int Interpreter::Hex2Bin(const char *aHex, uint8_t *aBin, uint16_t aBinLength) return static_cast(cur - aBin); } -void Interpreter::AppendResult(otError aError) +void Interpreter::AppendResult(otError aError) const { if (aError == OT_ERROR_NONE) { @@ -325,7 +328,7 @@ void Interpreter::AppendResult(otError aError) } } -void Interpreter::OutputBytes(const uint8_t *aBytes, uint8_t aLength) +void Interpreter::OutputBytes(const uint8_t *aBytes, uint8_t aLength) const { for (int i = 0; i < aLength; i++) { @@ -657,7 +660,7 @@ exit: void Interpreter::ProcessCoap(int argc, char *argv[]) { otError error; - error = Coap::Process(mInstance, argc, argv, *mServer); + error = mCoap.Process(argc, argv); AppendResult(error); } diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp index 28544b8aa..706d078f3 100644 --- a/src/cli/cli.hpp +++ b/src/cli/cli.hpp @@ -50,6 +50,7 @@ #if OPENTHREAD_ENABLE_APPLICATION_COAP #include +#include "cli/cli_coap.hpp" #endif #include "common/code_utils.hpp" @@ -94,6 +95,8 @@ struct Command */ class Interpreter { + friend class Coap; + public: /** @@ -156,8 +159,8 @@ private: kDefaultJoinerTimeout = 120, ///< Default timeout for Joiners, in seconds. }; - void AppendResult(otError error); - void OutputBytes(const uint8_t *aBytes, uint8_t aLength); + void AppendResult(otError error) const; + void OutputBytes(const uint8_t *aBytes, uint8_t aLength) const; void ProcessHelp(int argc, char *argv[]); void ProcessAutoStart(int argc, char *argv[]); @@ -321,6 +324,12 @@ private: void HandleDnsResponse(const char *aHostname, Ip6::Address &aAddress, uint32_t aTtl, otError aResult); #endif +#if OPENTHREAD_ENABLE_APPLICATION_COAP + + Coap mCoap; + +#endif // OPENTHREAD_ENABLE_APPLICATION_COAP + static const struct Command sCommands[]; Server *mServer; diff --git a/src/cli/cli_coap.cpp b/src/cli/cli_coap.cpp index 78bd11a8f..2b204c688 100644 --- a/src/cli/cli_coap.cpp +++ b/src/cli/cli_coap.cpp @@ -28,7 +28,7 @@ /** * @file - * This file implements a simple CLI coap server and client. + * This file implements a simple CLI for the CoAP service. */ #ifdef OPENTHREAD_CONFIG_FILE @@ -49,18 +49,7 @@ namespace ot { namespace Cli { -const CoapCommand Coap::sCommands[] = -{ - { "client", &ProcessClient }, - { "server", &ProcessServer } -}; - -Server *Coap::sServer; -otCoapResource Coap::sResource; -otInstance *Coap::sInstance; -char Coap::sUriPath[kMaxUriLength]; - -void Coap::PrintPayload(otMessage *aMessage) +void Coap::PrintPayload(otMessage *aMessage) const { uint8_t buf[kMaxBufferSize]; uint16_t bytesToPrint; @@ -69,115 +58,70 @@ void Coap::PrintPayload(otMessage *aMessage) if (length > 0) { - sServer->OutputFormat(" with payload: "); + mInterpreter.mServer->OutputFormat(" with payload: "); while (length > 0) { bytesToPrint = (length < sizeof(buf)) ? length : sizeof(buf); otMessageRead(aMessage, otMessageGetOffset(aMessage) + bytesPrinted, buf, bytesToPrint); - OutputBytes(buf, static_cast(bytesToPrint)); + mInterpreter.OutputBytes(buf, static_cast(bytesToPrint)); length -= bytesToPrint; bytesPrinted += bytesToPrint; } } - sServer->OutputFormat("\r\n"); + mInterpreter.mServer->OutputFormat("\r\n"); } -void Coap::ConvertToLower(char *aString) -{ - uint8_t i = 0; - - while (aString[i]) - { - aString[i] = (char)tolower(aString[i]); - i++; - } -} - -void Coap::OutputBytes(const uint8_t *aBytes, uint8_t aLength) -{ - for (int i = 0; i < aLength; i++) - { - sServer->OutputFormat("%02x ", aBytes[i]); - } -} - -otError Coap::Process(otInstance *aInstance, int argc, char *argv[], Server &aServer) -{ - otError error = OT_ERROR_NONE; - - sInstance = aInstance; - sServer = &aServer; - sResource.mUriPath = sUriPath; - sResource.mContext = aInstance; - sResource.mHandler = (otCoapRequestHandler) &Coap::s_HandleServerResponse; - - VerifyOrExit(argc > 0, error = OT_ERROR_INVALID_ARGS); - - for (unsigned int i = 0; i < sizeof(sCommands) / sizeof(sCommands[0]); i++) - { - if (strcmp(argv[0], sCommands[i].mName) == 0) - { - error = sCommands[i].mCommand(argc - 1, argv + 1); - break; - } - } - -exit: - return error; -} - -otError Coap::ProcessServer(int argc, char *argv[]) +otError Coap::Process(int argc, char *argv[]) { otError error = OT_ERROR_NONE; VerifyOrExit(argc > 0, error = OT_ERROR_INVALID_ARGS); - ConvertToLower(argv[0]); - if (strcmp(argv[0], "start") == 0) { - SuccessOrExit(error = otCoapStart(sInstance, OT_DEFAULT_COAP_PORT)); - SuccessOrExit(error = otCoapAddResource(sInstance, &sResource)); - sServer->OutputFormat("Server started with resource '%s': ", sResource.mUriPath); + SuccessOrExit(error = otCoapStart(mInterpreter.mInstance, OT_DEFAULT_COAP_PORT)); + mInterpreter.mServer->OutputFormat("Coap service started: "); } else if (strcmp(argv[0], "stop") == 0) { - otCoapRemoveResource(sInstance, &sResource); - SuccessOrExit(error = otCoapStop(sInstance)); - sServer->OutputFormat("Server stopped: "); + otCoapRemoveResource(mInterpreter.mInstance, &mResource); + SuccessOrExit(error = otCoapStop(mInterpreter.mInstance)); + mInterpreter.mServer->OutputFormat("Coap service stopped: "); } - else if (strcmp(argv[0], "name") == 0) + else if (strcmp(argv[0], "resource") == 0) { + mResource.mUriPath = mUriPath; + mResource.mContext = this; + mResource.mHandler = &Coap::HandleServerResponse; + if (argc > 1) { - strlcpy(sUriPath, argv[1], kMaxUriLength); - sServer->OutputFormat("Changing resource name to '%s': ", sResource.mUriPath); - } - else - { - sServer->OutputFormat("Current resource name is '%s': ", sResource.mUriPath); + strlcpy(mUriPath, argv[1], kMaxUriLength); + SuccessOrExit(error = otCoapAddResource(mInterpreter.mInstance, &mResource)); } + + mInterpreter.mServer->OutputFormat("Resource name is '%s': ", mResource.mUriPath); } else { - ExitNow(error = OT_ERROR_INVALID_ARGS); + error = ProcessRequest(argc, argv); } exit: return error; } -void OTCALL Coap::s_HandleServerResponse(void *aContext, otCoapHeader *aHeader, otMessage *aMessage, - otMessageInfo *aMessageInfo) +void OTCALL Coap::HandleServerResponse(void *aContext, otCoapHeader *aHeader, otMessage *aMessage, + const otMessageInfo *aMessageInfo) { static_cast(aContext)->HandleServerResponse(aHeader, aMessage, aMessageInfo); } -void Coap::HandleServerResponse(otCoapHeader *aHeader, otMessage *aMessage, otMessageInfo *aMessageInfo) +void Coap::HandleServerResponse(otCoapHeader *aHeader, otMessage *aMessage, const otMessageInfo *aMessageInfo) { otError error = OT_ERROR_NONE; otCoapHeader responseHeader; @@ -185,36 +129,36 @@ void Coap::HandleServerResponse(otCoapHeader *aHeader, otMessage *aMessage, otMe otCoapCode responseCode = OT_COAP_CODE_EMPTY; char responseContent = '0'; - sServer->OutputFormat("Received CoAP request from [%x:%x:%x:%x:%x:%x:%x:%x]: ", - HostSwap16(aMessageInfo->mSockAddr.mFields.m16[0]), - HostSwap16(aMessageInfo->mSockAddr.mFields.m16[1]), - HostSwap16(aMessageInfo->mSockAddr.mFields.m16[2]), - HostSwap16(aMessageInfo->mSockAddr.mFields.m16[3]), - HostSwap16(aMessageInfo->mSockAddr.mFields.m16[4]), - HostSwap16(aMessageInfo->mSockAddr.mFields.m16[5]), - HostSwap16(aMessageInfo->mSockAddr.mFields.m16[6]), - HostSwap16(aMessageInfo->mSockAddr.mFields.m16[7])); + mInterpreter.mServer->OutputFormat("Received coap request from [%x:%x:%x:%x:%x:%x:%x:%x]: ", + HostSwap16(aMessageInfo->mSockAddr.mFields.m16[0]), + HostSwap16(aMessageInfo->mSockAddr.mFields.m16[1]), + HostSwap16(aMessageInfo->mSockAddr.mFields.m16[2]), + HostSwap16(aMessageInfo->mSockAddr.mFields.m16[3]), + HostSwap16(aMessageInfo->mSockAddr.mFields.m16[4]), + HostSwap16(aMessageInfo->mSockAddr.mFields.m16[5]), + HostSwap16(aMessageInfo->mSockAddr.mFields.m16[6]), + HostSwap16(aMessageInfo->mSockAddr.mFields.m16[7])); switch (otCoapHeaderGetCode(aHeader)) { case OT_COAP_CODE_GET: - sServer->OutputFormat("GET"); + mInterpreter.mServer->OutputFormat("GET"); break; case OT_COAP_CODE_DELETE: - sServer->OutputFormat("DELETE"); + mInterpreter.mServer->OutputFormat("DELETE"); break; case OT_COAP_CODE_PUT: - sServer->OutputFormat("PUT"); + mInterpreter.mServer->OutputFormat("PUT"); break; case OT_COAP_CODE_POST: - sServer->OutputFormat("POST"); + mInterpreter.mServer->OutputFormat("POST"); break; default: - sServer->OutputFormat("Undefined\r\n"); + mInterpreter.mServer->OutputFormat("Undefined\r\n"); return; } @@ -240,7 +184,7 @@ void Coap::HandleServerResponse(otCoapHeader *aHeader, otMessage *aMessage, otMe otCoapHeaderSetPayloadMarker(&responseHeader); } - responseMessage = otCoapNewMessage(sInstance, &responseHeader); + responseMessage = otCoapNewMessage(mInterpreter.mInstance, &responseHeader); VerifyOrExit(responseMessage != NULL, error = OT_ERROR_NO_BUFS); if (otCoapHeaderGetCode(aHeader) == OT_COAP_CODE_GET) @@ -248,26 +192,24 @@ void Coap::HandleServerResponse(otCoapHeader *aHeader, otMessage *aMessage, otMe SuccessOrExit(error = otMessageAppend(responseMessage, &responseContent, sizeof(responseContent))); } - SuccessOrExit(error = otCoapSendResponse(sInstance, responseMessage, aMessageInfo)); + SuccessOrExit(error = otCoapSendResponse(mInterpreter.mInstance, responseMessage, aMessageInfo)); } exit: if (error != OT_ERROR_NONE && responseMessage != NULL) { - sServer->OutputFormat("Cannot send CoAP response message: Error %d\r\n", error); + mInterpreter.mServer->OutputFormat("Cannot send coap response message: Error %d: %s\r\n", + error, otThreadErrorToString(error)); otMessageFree(responseMessage); } - else + else if (responseCode >= OT_COAP_CODE_RESPONSE_MIN) { - if (responseCode >= OT_COAP_CODE_RESPONSE_MIN) - { - sServer->OutputFormat("CoAP response sent successfully!\r\n"); - } + mInterpreter.mServer->OutputFormat("coap response sent successfully!\r\n"); } } -otError Coap::ProcessClient(int argc, char *argv[]) +otError Coap::ProcessRequest(int argc, char *argv[]) { otError error = OT_ERROR_NONE; otMessage *message = NULL; @@ -284,8 +226,6 @@ otError Coap::ProcessClient(int argc, char *argv[]) VerifyOrExit(argc > 0, error = OT_ERROR_INVALID_ARGS); // CoAP-Code - ConvertToLower(argv[0]); - if (strcmp(argv[0], "get") == 0) { coapCode = OT_COAP_CODE_GET; @@ -330,8 +270,6 @@ otError Coap::ProcessClient(int argc, char *argv[]) // CoAP-Type if (argc > 3) { - ConvertToLower(argv[3]); - if (strcmp(argv[3], "con") == 0) { coapType = OT_COAP_TYPE_CONFIRMABLE; @@ -352,7 +290,7 @@ otError Coap::ProcessClient(int argc, char *argv[]) } } - message = otCoapNewMessage(sInstance, &header); + message = otCoapNewMessage(mInterpreter.mInstance, &header); VerifyOrExit(message != NULL, error = OT_ERROR_NO_BUFS); // Embed content into message if given @@ -366,17 +304,16 @@ otError Coap::ProcessClient(int argc, char *argv[]) messageInfo.mPeerPort = OT_DEFAULT_COAP_PORT; messageInfo.mInterfaceId = OT_NETIF_INTERFACE_ID_THREAD; - if ((coapType == OT_COAP_TYPE_CONFIRMABLE) || coapCode == OT_COAP_CODE_GET) + if ((coapType == OT_COAP_TYPE_CONFIRMABLE) || (coapCode == OT_COAP_CODE_GET)) { - error = otCoapSendRequest(sInstance, message, &messageInfo, - (otCoapResponseHandler) &Coap::s_HandleClientResponse, NULL); + error = otCoapSendRequest(mInterpreter.mInstance, message, &messageInfo, &Coap::HandleClientResponse, this); } else { - error = otCoapSendRequest(sInstance, message, &messageInfo, NULL, NULL); + error = otCoapSendRequest(mInterpreter.mInstance, message, &messageInfo, NULL, NULL); } - sServer->OutputFormat("Sending CoAP message: "); + mInterpreter.mServer->OutputFormat("Sending coap request: "); exit: @@ -388,22 +325,23 @@ exit: return error; } -void OTCALL Coap::s_HandleClientResponse(void *aContext, otCoapHeader *aHeader, otMessage *aMessage, - otMessageInfo *aMessageInfo, otError aResult) +void OTCALL Coap::HandleClientResponse(void *aContext, otCoapHeader *aHeader, otMessage *aMessage, + const otMessageInfo *aMessageInfo, otError aError) { - static_cast(aContext)->HandleClientResponse(aHeader, aMessage, aMessageInfo, aResult); + static_cast(aContext)->HandleClientResponse(aHeader, aMessage, aMessageInfo, aError); } -void Coap::HandleClientResponse(otCoapHeader *aHeader, otMessage *aMessage, otMessageInfo *aMessageInfo, - otError aResult) +void Coap::HandleClientResponse(otCoapHeader *aHeader, otMessage *aMessage, const otMessageInfo *aMessageInfo, + otError aError) { - if (aResult != OT_ERROR_NONE) + if (aError != OT_ERROR_NONE) { - sServer->OutputFormat("Error receiving CoAP response message: %d\r\n", aResult); + mInterpreter.mServer->OutputFormat("Error receiving coap response message: Error %d: %s\r\n", + aError, otThreadErrorToString(aError)); } else { - sServer->OutputFormat("Received CoAP response"); + mInterpreter.mServer->OutputFormat("Received coap response"); PrintPayload(aMessage); } diff --git a/src/cli/cli_coap.hpp b/src/cli/cli_coap.hpp index aa7141aaf..fa7ff70b2 100644 --- a/src/cli/cli_coap.hpp +++ b/src/cli/cli_coap.hpp @@ -36,21 +36,12 @@ #include -#include "cli/cli.hpp" #include "coap/coap_header.hpp" namespace ot { namespace Cli { -/** - * This structure represents a CLI command. - * - */ -struct CoapCommand -{ - const char *mName; ///< A pointer to the command string. - otError(*mCommand)(int argc, char *argv[]); ///< A function pointer to process the command. -}; +class Interpreter; /** * This class implements the CLI CoAP server and client. @@ -60,15 +51,21 @@ class Coap { public: /** - * This method interprets a list of CLI arguments. + * Constructor * - * @param[in] aInstance The OpenThread instance structure. - * @param[in] argc The number of elements in argv. - * @param[in] argv A pointer to an array of command line arguments. - * @param[in] aServer A reference to the CLI server. + * @param[in] aInterpreter The CLI interpreter. * */ - static otError Process(otInstance *aInstance, int argc, char *argv[], Server &aServer); + Coap(Interpreter &aInterpreter): mInterpreter(aInterpreter) { } + + /** + * This method interprets a list of CLI arguments. + * + * @param[in] argc The number of elements in argv. + * @param[in] argv A pointer to an array of command line arguments. + * + */ + otError Process(int argc, char *argv[]); private: enum @@ -76,26 +73,24 @@ private: kMaxUriLength = 32, kMaxBufferSize = 16 }; - static void PrintPayload(otMessage *aMessage); - static void ConvertToLower(char *aString); - static void OutputBytes(const uint8_t *aBytes, uint8_t aLength); - static otError ProcessClient(int argc, char *argv[]); - static otError ProcessServer(int argc, char *argv[]); + void PrintPayload(otMessage *aMessage) const; - static void OTCALL s_HandleServerResponse(void *aContext, otCoapHeader *aHeader, otMessage *aMessage, - otMessageInfo *aMessageInfo); - static void HandleServerResponse(otCoapHeader *aHeader, otMessage *aMessage, otMessageInfo *aMessageInfo); - static void OTCALL s_HandleClientResponse(void *aContext, otCoapHeader *aHeader, otMessage *aMessage, - otMessageInfo *aMessageInfo, otError aResult); - static void HandleClientResponse(otCoapHeader *aHeader, otMessage *aMessage, otMessageInfo *aMessageInfo, - otError aResult); + otError ProcessRequest(int argc, char *argv[]); - static const CoapCommand sCommands[]; - static Server *sServer; - static otCoapResource sResource; - static otInstance *sInstance; - static char sUriPath[kMaxUriLength]; + static void OTCALL HandleServerResponse(void *aContext, otCoapHeader *aHeader, otMessage *aMessage, + const otMessageInfo *aMessageInfo); + void HandleServerResponse(otCoapHeader *aHeader, otMessage *aMessage, const otMessageInfo *aMessageInfo); + + static void OTCALL HandleClientResponse(void *aContext, otCoapHeader *aHeader, otMessage *aMessage, + const otMessageInfo *aMessageInfo, otError aError); + void HandleClientResponse(otCoapHeader *aHeader, otMessage *aMessage, const otMessageInfo *aMessageInfo, + otError aError); + + otCoapResource mResource; + char mUriPath[kMaxUriLength]; + + Interpreter &mInterpreter; }; } // namespace Cli