Description
Update the AsyncCoapServerAdapter with the remaining functionality necessary to support the following helper methods and any other necessary functionality:
def _initServer(self)
async def _runServer(self)
async def _runServerTask(self)
async def _keepServerRunning(self)
async def _shutdownServer(self)
- 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: Implement _initServer() in AsyncCoapServerAdapter
- This method will initialize the server component, set the necessary configuration properties, and initialize the request handlers created earlier.
- Here's a sample implementation:
def _initServer(self):
try:
# Resource tree creation - lib-specific (this next line of code assumes use of aiocoap)
self.rootResource = resource.Site()
# Necessary to ensure discovery will function
self.rootResource.add_resource( \
['.well-known', 'core'], \
resource.WKCResource(self.rootResource.get_resources_as_linkheader))
self.addResource( \
resourcePath = ResourceNameEnum.CDA_ACTUATOR_CMD_RESOURCE, \
endName = ConfigConst.HUMIDIFIER_ACTUATOR_NAME, \
resource = AsyncUpdateActuatorResourceHandler(dataMsgListener = self.dataMsgListener))
# TODO: add other actuator resource handlers (for HVAC, etc.)
sysPerfDataListener = AsyncGetSystemPerformanceResourceHandler()
self.addResource( \
resourcePath = ResourceNameEnum.CDA_SYSTEM_PERF_MSG_RESOURCE, \
resource = sysPerfDataListener)
sensorDataListener = AsyncGetTelemetryResourceHandler()
self.addResource( \
resourcePath = ResourceNameEnum.CDA_SENSOR_MSG_RESOURCE, \
resource = sensorDataListener)
# TODO: register the callbacks with the data message listener instance
self.dataMsgListener.setTelemetryDataListener(listener = sensorDataListener)
self.dataMsgListener.setSystemPerformanceDataListener(listener = sysPerfDataListener)
logging.info("Created CoAP server with default resources.")
except Exception as e:
traceback.print_exception(type(e), e, e.__traceback__)
logging.warning(f"Failed to create Async CoAP Server at {self.serverUri}")
Step 2: Implement _runServer() in AsyncCoapServerAdapter
- This method will attempt to gracefully start the async server. It will be called from within the
_runServerTask() method.
- Here's a sample implementation:
async def _runServer(self):
if self.rootResource:
logging.info("Creating Async CoAP Server context...")
try:
bindTuple = (self.host, self.port)
self.coapServer = \
await aiocoap.Context.create_server_context( \
site = self.rootResource, \
bind = bindTuple)
logging.info("Initializing Async CoAP Server Task...")
self._serverTask = asyncio.current_task()
await self._keepServerRunning()
except asyncio.CancelledError:
logging.info("Server coroutine cancelled.")
except Exception as e:
logging.exception(f"Failed to spin up Async CoAP Server task: {e}")
else:
logging.warning("Root resource not yet created. Can't start server.")
Step 3: Implement _runServerTask() in AsyncCoapServerAdapter
- This method will initialize the async server's start sequence, using the newly created event loop "thread" instantiated in the constructor.
- Here's a sample implementation:
def _runServerTask(self):
logging.info("Starting Async CoAP Server task...")
asyncio.set_event_loop(self._eventLoopThread)
try:
self._eventLoopThread.run_until_complete(self._runServer())
except asyncio.CancelledError:
logging.info("Server task cancelled.")
except Exception as e:
logging.exception(f"Error starting Async CoAP Server task: {e}")
traceback.print_exception(type(e), e, e.__traceback__)
finally:
pendingTasks = asyncio.all_tasks(self._eventLoopThread)
try:
for task in pendingTasks:
task.cancel()
if pendingTasks:
self._eventLoopThread.run_until_complete(
asyncio.gather(*pendingTasks, return_exceptions = True))
self._eventLoopThread.close()
except Exception as e:
logging.exception(f"Async CoAP Server event loop thread exception: {e}")
traceback.print_exception(type(e), e, e.__traceback__)
logging.info("Successfully closed Async CoAP Server event loop.")
Step 4: Implement _keepServerRunning() in AsyncCoapServerAdapter
- This method will initialize an asyncio
Future component for use when cleanly shutting down the server.
- Here's a sample implementation:
async def _keepServerRunning(self):
try:
logging.info("Initializing Async CoAP Server shutdown future...")
self._shutdownFuture = asyncio.get_running_loop().create_future()
await self._shutdownFuture
except asyncio.CancelledError:
logging.info("Server keep-alive cancelled.")
except Exception as e:
logging.info("Async CoAP Server future cancelled [OK]")
Step 5: Implement _shutdownServer() in AsyncCoapServerAdapter
- This method will attempt to cleanly shutdown the async server.
- Here's a sample implementation:
async def _shutdownServer(self):
try:
if self.coapServer:
logging.info(f"Shutting down Async CoAP Server at {self.serverUri}")
await self.coapServer.shutdown()
self.coapServer = None
if hasattr(self, "_shutdownFuture") and not self._shutdownFuture.done():
self._shutdownFuture.set_result(None)
if self._serverTask and not self._serverTask.done():
self._serverTask.cancel()
with suppress(asyncio.exceptions.CancelledError):
await self._serverTask
logging.info("Async CoAP Server shutdown completed.")
except Exception as e:
logging.warning("Failed to shutdown Async CoAP server.")
traceback.print_exception(type(e), e, e.__traceback__)
Estimate (Small = < 2 hrs; Medium = 4 hrs; Large = 8 hrs)
Tests
- You can use the Californium client described in PIOT-CFG-08-001 to test your CoAP server running within the CDA.
Configure the CDA to run with CoAP server enabled
- Make sure your CDA's configuration file is updated to enable the CoAP server
- Update the config file and start the CDA in a separate terminal (or within your IDE)
- NOTE: The config info below is JUST AN EXAMPLE
#
# CoAP client configuration information
#
[Coap.GatewayService]
credFile = ./cred/PiotCoapCred.props
certFile = ./cert/PiotCoapLocalCertFile.pem
host = localhost
port = 5683
securePort = 5684
enableAuth = False
enableCrypt = False
#
# CDA specific configuration information
#
[ConstrainedDevice]
deviceLocationID = constraineddevice001
enableSimulator = True
enableEmulator = False
enableSenseHAT = False
enableMqttClient = False
enableCoapServer = True
enableCoapClient = False
enableSystemPerformance = True
enableSensing = True
enableLogging = True
pollCycleSecs = 5
testGdaDataPath = /tmp/gda-data
testCdaDataPath = /tmp/cda-data
testEmptyApp = False
runForever = True
Configure and run the Californium Client
- Open a separate terminal window on your system to run the Californium client.
- Change directory to the Californium Tools path (INSTALL_PATH/californium.tools)
- Run
cf-client with a simple discover or get request
CoAP GET Example
cd cf-client/target
java -jar cf-client-3.10.0-SNAPSHOT.jar -m GET coap://localhost:5683
CoAP DISCOVER Example
cd cf-client/target
java -jar cf-client-3.10.0-SNAPSHOT.jar -m GET coap://localhost:5683/.well-known/core
Californium client help
- For a list of supported client parameters, use the following:
java -jar cf-client-3.10.0-SNAPSHOT.jar --help
Results (Initial)
- You should see something similar to the following, depending on how the server is configured:
- NOTE: The following is an example response from a DISCOVER request
Results (Upon Full Lab Module Completion)
- Once you've completed all the exercises in this lab module, you should see an output that looks similar to the following, depending on how the server is configured:
- NOTE: The following is an example response from a DISCOVER request when using the
aiocoap library.
Description
Update the
AsyncCoapServerAdapterwith the remaining functionality necessary to support the following helper methods and any other necessary functionality:def _initServer(self)async def _runServer(self)async def _runServerTask(self)async def _keepServerRunning(self)async def _shutdownServer(self)Review the README
Estimated effort may vary greatly
Actions
Step 1: Implement
_initServer()inAsyncCoapServerAdapterStep 2: Implement
_runServer()inAsyncCoapServerAdapter_runServerTask()method.Step 3: Implement
_runServerTask()inAsyncCoapServerAdapterStep 4: Implement
_keepServerRunning()inAsyncCoapServerAdapterFuturecomponent for use when cleanly shutting down the server.Step 5: Implement
_shutdownServer()inAsyncCoapServerAdapterEstimate (Small = < 2 hrs; Medium = 4 hrs; Large = 8 hrs)
Tests
Configure the CDA to run with CoAP server enabled
Configure and run the Californium Client
cf-clientwith a simple discover or get requestCoAP GET Example
CoAP DISCOVER Example
Californium client help
Results (Initial)
Results (Upon Full Lab Module Completion)
aiocoaplibrary.