Description
- Update the Python module named
AsyncCoapClientConnector with support for POST requests, using the existing method definitions within IRequestResponseHandler to support this request type.
- NOTE: These instructions make use of the following CoAP library:
- The aiocoap open source CoAP library, located at: aiocoap. Reference: Amsüss, Christian and Wasilak, Maciej. aiocoap: Python CoAP Library. Energy Harvesting Solutions, 2013–. http://github.com/chrysn/aiocoap/.
Review the README
- Please see README.md for further information on, and use of, this content.
- License for embedded documentation and source codes: PIOT-DOC-LIC
Estimated effort may vary greatly
- The estimated level of effort for this exercise shown in the 'Estimate' section below is a very rough approximation. The actual level of effort may vary greatly depending on your development and test environment, experience with the requisite technologies, and many other factors.
Actions
Step 1: Create and implement a callable POST request method
- Implement the public
sendPostRequest() method
- This method will take the following arguments:
- resource (
ResourceNameEnum): Used to create the general request URL
- name (
str): Used to extend the general request URL with further detail (if warranted)
- enableCON (
bool): If True, use a CONFIRMABLE request; else, use NONCONFIRMABLE
- payload (
str): The payload to send to the server
- timeout (
int): The timeout value to pass to the CoAP client infrastructure
- A general implementation may look like the following:
def sendPostRequest(self, resource: ResourceNameEnum = None, name: str = None, enableCON: bool = False, payload: str = None, timeout: int = IRequestResponseClient.DEFAULT_TIMEOUT) -> bool:
if resource or name:
resourcePath = self._createResourcePath(resource, name)
logging.info(f"Issuing Async POST to path: {resourcePath}")
future = asyncio.run_coroutine_threadsafe(
self._handlePostRequest(resourcePath, payload, enableCON),
self._eventLoopThread
)
return future.result()
else:
logging.warning("Can't issue Async POST - no path provided.")
Step 2: Create and implement the internal POST request callback method and response handler
- Implement the internal
_handlePostRequest() and _onPostResponse() methods
- These methods will process the incoming response from the CoAP server, parse the payload, determine which data type is part of the payload, and react accordingly.
_handlePostRequest() will take the following arguments:
- resourcePath (
str): This will be the full resource path used for the request
- payload (
str): The payload to send to the CoAP server
- enableCON (
bool): If True, use a CONFIRMABLE request; else, use NONCONFIRMABLE
- A general implementation may look like the following:
async def _handlePostRequest(self, resourcePath: str = None, payload: str = None, enableCON: bool = False):
try:
uriAndResourcePath = self.uriPath + resourcePath
msgType = NON
if enableCON:
msgType = CON
msg = Message(mtype = msgType, payload = payload.encode("utf-8"), code = Code.POST, uri = uriAndResourcePath)
responseData = await self.clientContext.request(request_message = msg).response
self._onPostResponse(responseData)
except Exception as e:
# NOTE: for debugging, you may want to optionally include the stack trace, as shown
logging.warning(f"Failed to process Async GET request for path: {uriAndResourcePath}")
traceback.print_exception(type(e), e, e.__traceback__)
- The
_onPostResponse() method is rather simple - it decodes the incoming payload and logs it to the console.
def _onPostResponse(self, response):
if not response:
logging.warning("Async POST response invalid. Ignoring.")
return
logging.info("Async POST response received.")
responseData = response.payload.decode("utf-8")
logging.info(f"Response data received. Payload: {responseData}")
Estimate
Tests
- Edit / add test cases
- Update the
test_CoapAsyncClientConnectorTest.py module (containing the CoapAsyncClientConnectorTest class) in CDA_HOME/tests/integration/connection as indicated below.
- If not already done for you within the code, disable the existing tests by UNCOMMENTING the skip test annotation before each test case (change
#@unittest.skip("Ignore for now.") to @unittest.skip("Ignore for now.")).
- If not already done for you within the code, add two tests to store a test
SensorData instance on the CoAP server using both CON and NON requests.
- The code for each should look similar to the following:
#@unittest.skip("Ignore for now.")
def testPostSensorMessageCon(self):
data = SensorData()
jsonData = DataUtil().sensorDataToJson(data = data)
self.coapClient.sendPutRequest( \
resource = ResourceNameEnum.CDA_SENSOR_MSG_RESOURCE, enableCON = True, payload = jsonData, timeout = 5)
#@unittest.skip("Ignore for now.")
def testPostSensorMessageNon(self):
data = SensorData()
jsonData = DataUtil().sensorDataToJson(data = data)
self.coapClient.sendPutRequest( \
resource = ResourceNameEnum.CDA_SENSOR_MSG_RESOURCE, enableCON = False, payload = jsonData, timeout = 5)
- Setup
- Start Wireshark, and ensure it's watching the loopback adapter.
- You can filter on 'coap' to track only CoAP messages, which is recommended for this test.
- Start your GDA application with the CoAP server enabled. Make sure it runs for a couple of minutes - long enough for you to run the integration tests listed below.
- To run your GDA from the command line, you just need to do the following from a shell (assuming you're starting from the parent directory containing your java-components, and that the path is named 'piot-java-components':
- NOTE: Be sure to run your GDA Java app for a few minutes so you have time to run the tests!!
cd piot-java-components
mvn clean install -DskipTests
java -jar target/gateway-device-app-0.0.1-jar-with-dependencies.jar
- Run the POST tests
- Run both
testPostSensorMessage...() test cases and watch the output in the console for the client as well as within Wireshark.
- NOTE: If you're a student in the Connected Devices course, be sure to follow the instructions in PIOT-INF-09-003.
- Your output will contain log content similar to the following:
Description
AsyncCoapClientConnectorwith support for POST requests, using the existing method definitions withinIRequestResponseHandlerto support this request type.Review the README
Estimated effort may vary greatly
Actions
Step 1: Create and implement a callable POST request method
sendPostRequest()methodResourceNameEnum): Used to create the general request URLstr): Used to extend the general request URL with further detail (if warranted)bool): If True, use a CONFIRMABLE request; else, use NONCONFIRMABLEstr): The payload to send to the serverint): The timeout value to pass to the CoAP client infrastructureStep 2: Create and implement the internal POST request callback method and response handler
_handlePostRequest()and_onPostResponse()methods_handlePostRequest()will take the following arguments:str): This will be the full resource path used for the requeststr): The payload to send to the CoAP serverbool): If True, use a CONFIRMABLE request; else, use NONCONFIRMABLE_onPostResponse()method is rather simple - it decodes the incoming payload and logs it to the console.Estimate
Tests
test_CoapAsyncClientConnectorTest.pymodule (containing theCoapAsyncClientConnectorTestclass) in CDA_HOME/tests/integration/connection as indicated below.#@unittest.skip("Ignore for now.")to@unittest.skip("Ignore for now.")).SensorDatainstance on the CoAP server using both CON and NON requests.testPostSensorMessage...()test cases and watch the output in the console for the client as well as within Wireshark.