Skip to content

Commit

Permalink
Build CLI application (#3)
Browse files Browse the repository at this point in the history
* NEW: Added CLI on top of application.
* NEW: Updated docs.

---------

Co-authored-by: Lewis Chambers <[email protected]>
  • Loading branch information
lewis-chambers and lewis-chambers authored Jun 3, 2024
1 parent 617ad60 commit 634fb5f
Show file tree
Hide file tree
Showing 18 changed files with 561 additions and 101 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/static.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ on:
# Runs on pushes targeting the default branch
workflow_run:
workflows: ["Python Tests"]
branches: ["main"]
types:
- completed

Expand Down Expand Up @@ -54,13 +53,14 @@ jobs:
run: |
python -c "import iotdevicesimulator; print(iotdevicesimulator.__version__)"
pushd docs
. ./make.sh apidoc
. ./make.sh build
- name: Upload artifact
if: ${{ github.head_ref == 'main' || github.ref_name == 'main' }}
uses: actions/upload-pages-artifact@v3
with:
# Upload entire repository
path: 'docs/_build/html'
- name: Deploy to GitHub Pages
if: ${{ github.head_ref == 'main' || github.ref_name == 'main' }}
id: deployment
uses: actions/deploy-pages@v4
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ aws-auth
*.log
*.certs
!.github/*
docs/source/*
docs/_*
conf.sh
9 changes: 8 additions & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,19 @@

# -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information

import os, sys

path = os.path.abspath("../src")
sys.path.append(path)
print(path)
import iotdevicesimulator

project = "IoT Thing Swarm"
copyright = "2024, Lewis Chambers"
author = "Lewis Chambers"
release = iotdevicesimulator.__version__

version = release
# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration

Expand All @@ -21,6 +27,7 @@
"sphinx.ext.intersphinx",
"sphinx_rtd_theme",
"sphinx_copybutton",
"sphinx_click",
]


Expand Down
51 changes: 46 additions & 5 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
.. role:: python(code)
:language: python

.. role:: shell(code)
:language: shell

.. toctree::
:maxdepth: 2
:caption: Contents:
Expand Down Expand Up @@ -43,10 +46,49 @@ Install this package via pip:
This installs the package :python:`iotdevicesimulator` into your python environment.

-------------
Using The CLI
-------------

-----
Usage
-----
Installing this package will add a Command Line Interface (CLI) tool to your environment.
It can be invoked by typing :shell:`iot-swarm` into your terminal. The CLI can be called to
intialise a swarm with data sent every 30 minutes like so:

.. code-block:: shell
iot-swarm cosmos --dsn="xxxxxx" --user="xxxxx" --password="*****" \
mqtt "aws" LEVEL_1_SOILMET_30MIN "client_id" \
--endpoint="xxxxxxx" \
--cert-path="C:\path\..." \
--key-path="C:\path\..." \
--ca-cert-path="C:\path\..." \
--sleep-time=1800 \
--dry
Parameters such as the certificates, credentials, endpoints take up a lot of volume, and can be provided by environment variables instead:

.. code-block:: shell
# COSMOS Credentials
export IOT_SWARM_COSMOS_DSN="xxxxxxxx"
export IOT_SWARM_COSMOS_USER="xxxxxxxx"
export IOT_SWARM_COSMOS_PASSWORD="xxxxxxx"
# AWS MQTT Configuration
export IOT_SWARM_MQTT_ENDPOINT="xxxxxxxx"
export IOT_SWARM_MQTT_CERT_PATH=="C:\path\..."
export IOT_SWARM_MQTT_KEY_PATH="C:\path\..."
export IOT_SWARM_MQTT_CA_CERT_PATH="C:\path\..."
Then the CLI can be called more cleanly:

.. code-block:: shell
iot-swarm cosmos mqtt "aws" LEVEL_1_SOILMET_30MIN "client_id" --sleep-time=1800 --swarm-name="my-swarm"
------------------------
Using the Python Modules
------------------------

To create an IoT Swarm you must write a script such as the following:

Expand Down Expand Up @@ -105,8 +147,7 @@ To create an IoT Swarm you must write a script such as the following:
This instantiates and runs a swarm of 5 sites from the COSMOS database that
each run for 5 cycles of queries and wait for 30 seconds between queries.

The system requires a config file containing database credentials and endpoint
details for data transmission.
The system expects config credentials for the MQTT endpoint and the COSMOS Oracle database.

.. include:: example-config.cfg

Expand Down
2 changes: 1 addition & 1 deletion docs/make.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ function apidoc() {
args="--module-first --force"
fi

sphinx-apidoc $(echo $args) -o source ../src/iotdevicesimulator
sphinx-apidoc $(echo $args) -o source ../src/iotdevicesimulator '../src/iotdevicesimulator/scripts/*'
}

function build() {
Expand Down
26 changes: 26 additions & 0 deletions docs/source/iotdevicesimulator.messaging.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
iotdevicesimulator.messaging package
====================================

.. automodule:: iotdevicesimulator.messaging
:members:
:undoc-members:
:show-inheritance:

Submodules
----------

iotdevicesimulator.messaging.aws module
---------------------------------------

.. automodule:: iotdevicesimulator.messaging.aws
:members:
:undoc-members:
:show-inheritance:

iotdevicesimulator.messaging.core module
----------------------------------------

.. automodule:: iotdevicesimulator.messaging.core
:members:
:undoc-members:
:show-inheritance:
67 changes: 67 additions & 0 deletions docs/source/iotdevicesimulator.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
iotdevicesimulator package
==========================

.. automodule:: iotdevicesimulator
:members:
:undoc-members:
:show-inheritance:

Subpackages
-----------

.. toctree::
:maxdepth: 4

iotdevicesimulator.messaging
iotdevicesimulator.scripts

Submodules
----------

iotdevicesimulator.db module
----------------------------

.. automodule:: iotdevicesimulator.db
:members:
:undoc-members:
:show-inheritance:

iotdevicesimulator.devices module
---------------------------------

.. automodule:: iotdevicesimulator.devices
:members:
:undoc-members:
:show-inheritance:

iotdevicesimulator.example module
---------------------------------

.. automodule:: iotdevicesimulator.example
:members:
:undoc-members:
:show-inheritance:

iotdevicesimulator.loggers module
---------------------------------

.. automodule:: iotdevicesimulator.loggers
:members:
:undoc-members:
:show-inheritance:

iotdevicesimulator.queries module
---------------------------------

.. automodule:: iotdevicesimulator.queries
:members:
:undoc-members:
:show-inheritance:

iotdevicesimulator.swarm module
-------------------------------

.. automodule:: iotdevicesimulator.swarm
:members:
:undoc-members:
:show-inheritance:
20 changes: 20 additions & 0 deletions docs/source/iotdevicesimulator.scripts.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
iotdevicesimulator.scripts package
==================================

Submodules
----------

iotdevicesimulator.scripts.cli module
-------------------------------------

.. click:: iotdevicesimulator.scripts.cli:main
:prog: iot-swarm
:nested: full

Module contents
---------------

.. automodule:: iotdevicesimulator.scripts
:members:
:undoc-members:
:show-inheritance:
7 changes: 7 additions & 0 deletions docs/source/modules.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
iotdevicesimulator
==================

.. toctree::
:maxdepth: 4

iotdevicesimulator
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ description = "Package for simulating a net of IoT devices for stress testing."
test = ["pytest", "pytest-cov", "pytest-asyncio", "parameterized"]
docs = ["sphinx", "sphinx-copybutton", "sphinx-rtd-theme"]

[project.scripts]
iot-swarm = "iotdevicesimulator.scripts.cli:main"
[tool.setuptools.dynamic]
version = { attr = "iotdevicesimulator.__version__" }

Expand Down
37 changes: 31 additions & 6 deletions src/iotdevicesimulator/devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class SensorSite:
max_cycles: Maximum number of cycles before shutdown.
inherit_logger: Override for the module logger.
delay_first_cycle: Adds a random delay to first invocation from 0 - `sleep_time`.
topic_prefix: Prefixes the sensor topic.
"""

cycle: int | None = 0
Expand All @@ -37,8 +38,27 @@ class SensorSite:
_instance_logger: logging.Logger
"""Logger used by the instance."""

topic_prefix: str = None
"""Added as prefix to topic string."""

@property
def topic(self):
"""MQTT message topic."""
return self._topic

@topic.setter
def topic(self, query):
"""Gets the topic"""
_topic = f"fdri/cosmos_site/{self.site_id}/{query}"

if self.topic_prefix is not None:
_topic = f"{self.topic_prefix}/{_topic}"

self._topic = _topic

def __init__(self, site_id: str,*, sleep_time: int|None=None, max_cycles: int|None=None, inherit_logger:logging.Logger|None=None, delay_first_cycle:bool|None=None) -> None:
def __init__(self, site_id: str,*, sleep_time: int|None=None,
max_cycles: int|None=None, inherit_logger:logging.Logger|None=None,
delay_first_cycle:bool|None=None, topic_prefix: str|None=None) -> None:

self.site_id = str(site_id)

Expand All @@ -49,8 +69,8 @@ def __init__(self, site_id: str,*, sleep_time: int|None=None, max_cycles: int|No

if max_cycles is not None:
max_cycles = int(max_cycles)
if max_cycles <= 0 and max_cycles != -1:
raise ValueError(f"`max_cycles` must be 1 or more, or -1 for no maximum. Received: {max_cycles}")
if max_cycles < 0:
raise ValueError(f"`max_cycles` must be 1 or more, or 0 for no maximum. Received: {max_cycles}")

self.max_cycles = max_cycles

Expand All @@ -69,6 +89,9 @@ def __init__(self, site_id: str,*, sleep_time: int|None=None, max_cycles: int|No

self.delay_first_cycle = delay_first_cycle

if topic_prefix is not None:
self.topic_prefix = str(topic_prefix)

self._instance_logger.info(f"Initialised Site: {repr(self)}")

def __repr__(self):
Expand All @@ -86,6 +109,8 @@ async def run(self, oracle: Oracle, query: CosmosQuery,
oracle: The oracle database.
query: Query to process by database.
"""

self.topic = query.name
while True:

if self.delay_first_cycle and self.cycle == 0:
Expand All @@ -99,11 +124,11 @@ async def run(self, oracle: Oracle, query: CosmosQuery,
self._instance_logger.warn(f"No data found.")
else:
self._instance_logger.debug(f"Cycle {self.cycle+1}/{self.max_cycles} Read data from: {row["DATE_TIME"]}")
mqtt_topic = f"fdri/cosmos_site/{self.site_id}/{query.name}"
message_connection.send_message(str(row), mqtt_topic)
message_connection.send_message(str(row), self.topic)
self._instance_logger.info(f"Sent message to: {self.topic}")

self.cycle += 1
if self.cycle >= self.max_cycles:
if self.max_cycles > 0 and self.cycle >= self.max_cycles:
break

await asyncio.sleep(self.sleep_time)
2 changes: 1 addition & 1 deletion src/iotdevicesimulator/example.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@ async def main(config_path: str):
swarm = await CosmosSwarm.create(
CosmosQuery.LEVEL_1_SOILMET_30MIN,
mqtt_connection,
oracle_config,
swarm_name="soilmet",
delay_first_cycle=True,
max_cycles=5,
max_sites=5,
sleep_time=30,
credentials=oracle_config,
)
await swarm.run()

Expand Down
Loading

0 comments on commit 634fb5f

Please sign in to comment.