Commit f84c816a authored by Federico Sismondi's avatar Federico Sismondi

Merge branch 'fountain_fixes' into 'master'

Fountain fixes

See merge request !15
parents 2735a24b 86b1ccb3
......@@ -27,28 +27,14 @@ from flask import request
import requests as client_request
app = Flask(__name__, )
print("Starting to collect..")
# get config from environment
COLLECTION_DIR = os.getenv('COLLECTION_DIR', default='.')
URL_BASE = 'http://{}:1026'.format(os.getenv('ORION_HOST', default='localhost'))
LORA_PLATFORM_URL = 'https://{}'.format(os.getenv('LORA_PLATFORM_SERVER', default='lora-ns.sig-ge.ch:443'))
LORA_PLATFORM_URL = os.getenv('LORA_PLATFORM_URL')
LORA_PLATFORM_SECRET_FILE = os.getenv('LORA_PLATFORM_SECRET_FILE', 'secret_carouge_lora')
with open(LORA_PLATFORM_SECRET_FILE, 'r') as file:
secret = file.read().replace('\n', '')
assert secret, 'No auth token defined for LoRa platform API'
# AUTH Header for lora platform
h = {'Authorization': secret}
# build header for GET
http_header_get = {
'Fiware-Service': 'carouge',
'Accept': 'application/json',
}
def get_ngsiv2_typed_description(val):
if isinstance(val, Point):
......@@ -126,28 +112,82 @@ def _dump_raw_data_to_filesystem():
return 'ok'
def _dummy_post_handler():
if request.data:
app.logger.info("Request data: %s" % request.data)
if request.form:
app.logger.info("Request form: %s" % request.form)
def _get_reading(message_data):
"""
>>> base64.standard_b64decode("+AACoQEDAAAAAANY").hex()
'f8 00 02 a1 01 03 00 00 00 00 03 58'
decode:
f8 -> reg00-04 OK, reg05-07 NOK/not-used
00 -> reg08-15 NOK/not-used
_
02 |-> 673
a1 _|
01 |-> 259
03 _|
00 |-> 0
00 _|
00 |-> 0
00 _|
03 |-> 856
58 _|
units:
ph *100 ,
T celcius * 10,
DIS1 *100
DIS2 *100
REDOX mV
:param message_data: modbus base64 encoded message
:return: (ph, T , DIS1, DIS2, redox)
"""
resp = []
reading_hex = base64.standard_b64decode(message_data).hex()
status_registry = reading_hex[0:4]
values_registry = reading_hex[4:]
for i in reversed(range(0, 16)): # iterates i from 15 to 0
reg_i_status = int(status_registry, 16) >> i & 1 # checks reg bit
if reg_i_status:
rev_i = 15 - i # reverse index
resp.append(int(values_registry[4 * rev_i: 4 * rev_i + 4], 16))
else:
resp.append(None)
return 'Got POST with json %s' % request.json
app.logger.info("modbus / LoRa registers status : {}".format("{0:b}".format(int(status_registry, 16))))
app.logger.info("modbus / LoRa registers values : {}".format(values_registry))
print("modbus / LoRa registers status : {}".format("{0:b}".format(int(status_registry, 16))))
print("modbus / LoRa registers values : {}".format(values_registry))
return resp
# = = = = = = = = FLASK ROUTES = = = = = = = =
@app.route('/dca-carouge-fountain-sensed-data/rest/callback/payloads/ul', methods=['POST'])
def post_dca_carouge_fountain_sensed_data_payload():
# get values
#soil_humidity, sensor_eui = _get_reading(request.json['dataFrame']), request.json['deveui']
# soil_humidity, sensor_eui = _get_reading(request.json['dataFrame']), request.json['deveui']
# log reading
#_log_level_of_moisture(soil_humidity, sensor_eui)
# _log_level_of_moisture(soil_humidity, sensor_eui)
if request.json['deveui'] == '70b3d5078000066e':
app.logger.info('Got message from probe LoRa device, hence sensors have LoRa coverage')
return {'status': 'ok'}
if not request.json['dataFrame'] or request.json['dataFrame'] == '' :
app.logger.error('Message with empty <data frame> field from sensor {}'.format(request.json['deveui'] ))
raise InvalidUsage(message='Empty data frame field received', payload=request.json)
app.logger.info("Got message from FOUNTAIN devices: {}".format(request.json))
# push data to context broker
resp = {}
resp.update({"fs_dump": _dump_raw_data_to_filesystem()})
#resp.update({"update_flowerbed_entities": _update_flowerbed_entity(soil_humidity, sensor_eui)})
# resp.update({"update_flowerbed_entities": _update_flowerbed_entity(soil_humidity, sensor_eui)})
# resp.update({"update_device_entities": _update_device_entity(
# deveui=request.json['deveui'],
# comment=None,
......@@ -164,7 +204,13 @@ def post_dca_carouge_fountain_sensed_data_payload():
@app.route('/dca-carouge-fountain-sensed-data/rest/callback/nodeinfo', methods=['PUT'])
def put_dca_carouge_fountain_sensed_node_info():
_dummy_post_handler()
app.logger.info("Got FOUNTAINS message: {}".format(request.json))
if request.data:
app.logger.info("\t Request data: %s" % request.data)
if request.form:
app.logger.info("\t Request form: %s" % request.form)
raise InvalidUsage(message='This is still not implemented', payload=request.json)
......@@ -183,5 +229,21 @@ def main():
if __name__ == "__main__":
#init()
with open(LORA_PLATFORM_SECRET_FILE, 'r') as file:
secret = file.read().replace('\n', '')
assert secret, 'No auth token defined for LoRa platform API'
# AUTH Header for lora platform
h = {'Authorization': secret}
# build header for GET
http_header_get = {
'Fiware-Service': 'carouge',
'Accept': 'application/json',
}
print("Starting to collect..")
# init()
main()
......@@ -29,7 +29,7 @@ print("Starting to collect..")
COLLECTION_DIR = os.getenv('COLLECTION_DIR', default='.')
URL_BASE = 'http://{}:1026'.format(os.getenv('ORION_HOST', default='localhost'))
LORA_PLATFORM_URL = 'https://{}'.format(os.getenv('LORA_PLATFORM_SERVER', default='lora-ns.sig-ge.ch:443'))
LORA_PLATFORM_URL = os.getenv('LORA_PLATFORM_URL')
LORA_PLATFORM_SECRET_FILE = os.getenv('LORA_PLATFORM_SECRET_FILE', 'secret_carouge_lora')
with open(LORA_PLATFORM_SECRET_FILE, 'r') as file:
......
......@@ -2,17 +2,66 @@
# NAIADES IoT Platform and services #
# - + - + - + - + - + - + - + - + - + - + - + - + - +
# Authored by UDGA
# PRODUCTION ToDOs
# include let's encrypt in nginx
# platform's FQDM ?
# all services requests must pass through nginx
# - + - + - + - + - + - + - + - + - + - + - + - + - +
# Notes #
# - + - + - + - + - + - + - + - + - + - + - + - + - +
# ToDoS for PROD
# ==============
# - include let's encrypt in nginx
# - platform's FQDM ?
# - all services requests must pass through nginx
# - change secrets
# inspired by
# see: https://smartsdk.github.io/smartsdk-recipes/
#
# Authored by UDGA in the context of NAIADES E.U. PROJECT
#
# Received contributions from
# ---------------------------
# - ?
#
# inspired by https://smartsdk.github.io/smartsdk-recipes/
#
# PRODUCTION ToDOs
# ----------------
# - include let's encrypt in nginx
# - platform's FQDM ?
# - all services requests must pass through nginx
#
#
#
# DEPLOYMENT NOTES:
# -----------------
#
# About Docker Volumes:
# ---------------------
#
# A data volume is a specially-designated directory within one or more containers that bypasses the Union File System.
# Data volumes provide several useful features for persistent or shared data:
#
# - Volumes are initialized when a container is created.
# If the container’s base image contains data at the specified mount point,
# that existing data is copied into the new volume upon volume
# initialization.
# (Note that this does not apply when mounting a host
# directory.)
#
# - Data volumes can be shared and reused among containers.
#
# - Changes to a data volume are made directly.
#
# - Changes to a data volume will not be included when you update an image.
# Data volumes persist even if the container itself is deleted.
#
# Into Dockerfile you can specify only destination of volume inside container. e.g. /usr/src/app.
# You may but -not necessary needed- specify mounting point (/opt) in host machine.
# Using the CLI that is `docker run --volume=/opt:/usr/src/app my_image`
# If you not specify --volume argument then mount point will be chosen automatically
#
version: "3.5"
services:
......@@ -322,6 +371,7 @@ services:
- COLLECTION_DIR=/code/raw_data
- ORION_HOST=orion
- LORA_PLATFORM_SECRET_FILE=/run/secrets/lora_platform_auth_header
- LORA_PLATFORM_URL=https://lora-ns.sig-ge.ch:443
secrets:
- lora_platform_auth_header
expose:
......@@ -354,6 +404,7 @@ services:
- COLLECTION_DIR=/code/raw_data
- ORION_HOST=orion
- LORA_PLATFORM_SECRET_FILE=/run/secrets/lora_platform_auth_header
- LORA_PLATFORM_URL=https://eu.saas.orbiwise.com:443
secrets:
- lora_platform_auth_header
expose:
......@@ -369,6 +420,7 @@ services:
interval: 5m00s
timeout: 10s
retries: 3
# - + - + - + - + - + - + - + - + - + - + - + - + - +
# Docker configs #
# - + - + - + - + - + - + - + - + - + - + - + - + - +
......
......@@ -60,8 +60,8 @@ http {
}
location ^~ /dca-carouge-fountain-sensed-data {
auth_basic "Restricted Area!";
auth_basic_user_file htpasswd/.htpasswd;
#auth_basic "Restricted Area!";
#auth_basic_user_file htpasswd/.htpasswd;
include conf.d/proxy_params.conf;
proxy_pass http://DCA-CAROUGE-FOUNTAIN/dca-carouge-fountain-sensed-data; # see cond.d/upstreams
proxy_redirect default;
......
......@@ -37,7 +37,7 @@ def get_ngsiv2_typed_description(val):
else:
new_val = re.sub(pat, '', val)
return {'type': 'Text', 'value': new_val}
elif isinstance(val, datetime.datetime):
elif isinstance(val, datetime):
# replace +00:00 is the same as Z but Orion doesnt like it :/
return {'type': 'DateTime', 'value': str(val.isoformat()).replace("+00:00", "Z")}
elif isinstance(val, dict):
......@@ -63,6 +63,9 @@ attrs = {
'windSpeed': 0,
'pressureTendency': 0.0,
'relativeHumidity': 0,
"dewPoint": 0,
"windDirection": 0,
},
'WeatherForecast': {
'dateIssued': datetime.utcnow(), # mandatory
......@@ -97,33 +100,33 @@ entities = [
('urn:ngsi-ld:WeatherObserved:WeatherObserved-1', ['alicante']),
('urn:ngsi-ld:WeatherObserved:WeatherObserved-2', ['alicante']),
# prediction use case - forecast today
('urn:ngsi-ld:WeatherForecast:WeatherForecast-Day0-0', ['carouge', 'alicante', 'braila']),
('urn:ngsi-ld:WeatherForecast:WeatherForecast-Day0-1', ['carouge', 'alicante', 'braila']),
('urn:ngsi-ld:WeatherForecast:WeatherForecast-Day0-2', ['carouge', 'alicante', 'braila']),
('urn:ngsi-ld:WeatherForecast:WeatherForecast-Day0-3', ['carouge', 'alicante', 'braila']),
# prediction use case - forecast tomorrow
('urn:ngsi-ld:WeatherForecast:WeatherForecast-Day1-0', ['carouge', 'alicante', 'braila']),
('urn:ngsi-ld:WeatherForecast:WeatherForecast-Day1-1', ['carouge', 'alicante', 'braila']),
('urn:ngsi-ld:WeatherForecast:WeatherForecast-Day1-2', ['carouge', 'alicante', 'braila']),
('urn:ngsi-ld:WeatherForecast:WeatherForecast-Day1-3', ['carouge', 'alicante', 'braila']),
# prediction use case - forecast tomorrow
('urn:ngsi-ld:WeatherForecast:WeatherForecast-Day2-0', ['carouge', 'alicante', 'braila']),
('urn:ngsi-ld:WeatherForecast:WeatherForecast-Day2-1', ['carouge', 'alicante', 'braila']),
('urn:ngsi-ld:WeatherForecast:WeatherForecast-Day2-2', ['carouge', 'alicante', 'braila']),
('urn:ngsi-ld:WeatherForecast:WeatherForecast-Day2-3', ['carouge', 'alicante', 'braila']),
# watering use case - flower beds
('urn:ngsi-ld:FlowerBed:FlowerBed-1', ['carouge']),
('urn:ngsi-ld:FlowerBed:FlowerBed-2', ['carouge']),
('urn:ngsi-ld:FlowerBed:FlowerBed-3', ['carouge']),
('urn:ngsi-ld:FlowerBed:FlowerBed-4', ['carouge']),
('urn:ngsi-ld:FlowerBed:FlowerBed-5', ['carouge']),
('urn:ngsi-ld:FlowerBed:FlowerBed-6', ['carouge']),
('urn:ngsi-ld:FlowerBed:FlowerBed-7', ['carouge']),
('urn:ngsi-ld:FlowerBed:FlowerBed-8', ['carouge']),
# # prediction use case - forecast today
# ('urn:ngsi-ld:WeatherForecast:WeatherForecast-Day0-0', ['carouge', 'alicante', 'braila']),
# ('urn:ngsi-ld:WeatherForecast:WeatherForecast-Day0-1', ['carouge', 'alicante', 'braila']),
# ('urn:ngsi-ld:WeatherForecast:WeatherForecast-Day0-2', ['carouge', 'alicante', 'braila']),
# ('urn:ngsi-ld:WeatherForecast:WeatherForecast-Day0-3', ['carouge', 'alicante', 'braila']),
#
# # prediction use case - forecast tomorrow
# ('urn:ngsi-ld:WeatherForecast:WeatherForecast-Day1-0', ['carouge', 'alicante', 'braila']),
# ('urn:ngsi-ld:WeatherForecast:WeatherForecast-Day1-1', ['carouge', 'alicante', 'braila']),
# ('urn:ngsi-ld:WeatherForecast:WeatherForecast-Day1-2', ['carouge', 'alicante', 'braila']),
# ('urn:ngsi-ld:WeatherForecast:WeatherForecast-Day1-3', ['carouge', 'alicante', 'braila']),
#
# # prediction use case - forecast tomorrow
# ('urn:ngsi-ld:WeatherForecast:WeatherForecast-Day2-0', ['carouge', 'alicante', 'braila']),
# ('urn:ngsi-ld:WeatherForecast:WeatherForecast-Day2-1', ['carouge', 'alicante', 'braila']),
# ('urn:ngsi-ld:WeatherForecast:WeatherForecast-Day2-2', ['carouge', 'alicante', 'braila']),
# ('urn:ngsi-ld:WeatherForecast:WeatherForecast-Day2-3', ['carouge', 'alicante', 'braila']),
#
# # watering use case - flower beds
# ('urn:ngsi-ld:FlowerBed:FlowerBed-1', ['carouge']),
# ('urn:ngsi-ld:FlowerBed:FlowerBed-2', ['carouge']),
# ('urn:ngsi-ld:FlowerBed:FlowerBed-3', ['carouge']),
# ('urn:ngsi-ld:FlowerBed:FlowerBed-4', ['carouge']),
# ('urn:ngsi-ld:FlowerBed:FlowerBed-5', ['carouge']),
# ('urn:ngsi-ld:FlowerBed:FlowerBed-6', ['carouge']),
# ('urn:ngsi-ld:FlowerBed:FlowerBed-7', ['carouge']),
# ('urn:ngsi-ld:FlowerBed:FlowerBed-8', ['carouge']),
]
# http headers
......
#!/usr/bin/env bash
[[ -z "$ORION_HOST" ]] && echo "Please set ORION_HOST env var. E.g. export ORION_HOST=127.0.0.1" && exit
echo "Querying context broker at: ${ORION_HOST}"
# API NOTES:
# "condition": { "attrs": [] } -> means monitor all the attributes in the entity
curl --location --request POST \
"http://$ORION_HOST:1026/v2/subscriptions/" \
--header "Fiware-Service: alicante" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data '{
"description": "Notify QuantumLeap, the historic API, of all WeatherObserved changes",
"subject": {
"entities": [
{
"idPattern": ".*",
"type": "WeatherObserved"
}
],
"condition": {
"attrs": []
}
},
"notification": {
"http": {
"url": "http://172.18.1.7:8668/v2/notify"
},
"attrs": [],
"attrsFormat" : "normalized",
"metadata": ["dateCreated", "dateModified"]
}
}'
#!/usr/bin/env bash
[[ -z "$ORION_HOST" ]] && echo "Please set ORION_HOST env var. E.g. export ORION_HOST=127.0.0.1" && exit
echo "Querying context broker at: ${ORION_HOST}"
# API NOTES:
# "condition": { "attrs": [] } -> means monitor all the attributes in the entity
curl --location --request POST \
"http://$ORION_HOST:1026/v2/subscriptions/" \
--header "Fiware-Service: braila" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data '{
"description": "Notify QuantumLeap, the historic API, of all WeatherObserved changes",
"subject": {
"entities": [
{
"idPattern": ".*",
"type": "WeatherObserved"
}
],
"condition": {
"attrs": []
}
},
"notification": {
"http": {
"url": "http://172.18.1.7:8668/v2/notify"
},
"attrs": [],
"attrsFormat" : "normalized",
"metadata": ["dateCreated", "dateModified"]
}
}'
......@@ -12,12 +12,12 @@ curl --location --request POST \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data '{
"description": "Notify QuantumLeap, the historic API, of all WeatherObservedRaw changes",
"description": "Notify QuantumLeap, the historic API, of all WeatherObserved changes",
"subject": {
"entities": [
{
"idPattern": ".*",
"type": "WeatherObservedRaw"
"type": "WeatherObserved"
}
],
"condition": {
......@@ -29,7 +29,7 @@ curl --location --request POST \
"url": "http://172.18.1.7:8668/v2/notify"
},
"attrs": [],
"attrsFormat" : "keyValues",
"attrsFormat" : "normalized",
"metadata": ["dateCreated", "dateModified"]
}
}'
curl --request DELETE 'localhost:1026/v2/entities/urn:ngsi-ld:FlowerBed:FlowerBed-345'
#!/usr/bin/env bash
[[ -z "$ORION_HOST" ]] && echo "Please set ORION_HOST env var. E.g. export ORION_HOST=127.0.0.1" && exit
echo "Querying context broker at: ${ORION_HOST}"
curl --location --request DELETE "http://$ORION_HOST:1026/v2/subscriptions/5ec4fbfe91d551c2b4612f42"\
--header "Fiware-Service: carouge"
TODO list:
- PATCH /entity value of moisture & updated date of reading
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment