diff --git a/Dockerfile b/Dockerfile index d19aad64..9778d801 100644 --- a/Dockerfile +++ b/Dockerfile @@ -41,8 +41,6 @@ RUN chown -R user:user /app RUN chmod -R 755 /var/log/django RUN chown -R user:user /var/log/django -RUN chown -R user:user /app/transmission/processing/temp -RUN chmod -R 755 /app/transmission/processing/temp RUN chown -R user:user /app/home/temp RUN chmod -R 755 /app/home/temp diff --git a/README.md b/README.md index 7d2aa95a..1ffc30a6 100644 --- a/README.md +++ b/README.md @@ -29,22 +29,25 @@ The dependencies are generated by running `./create_python_requirements`. This a 3. Install the requirements (one time instruction): `pip install -r requirements.txt -r requirements_dev.txt` -4. Set up the database via docker or connect your own Postgres instance +4. Set up the database via docker `docker compose up db` 5. Run the migrations from the root folder: `python src/manage.py migrate` -6. Create the InfluxDB buckets: +6. Set up InfluxDB via docker +`docker compose up influxdb` + +7. Create the InfluxDB buckets: `python src/manage.py initbuckets` -7. Run the server from the root folder: +8. Run the server from the root folder: `python src/manage.py runserver` The server runs on http://127.0.0.1:8000/ -8. To run the tests: +9. To run the tests: `python src/manage.py test` -9. To run pylint: +10. To run pylint: `find src -name "*.py" | xargs pylint` ## Setup (Run via docker) @@ -115,7 +118,7 @@ CROWDSEC_LAPI= 8. Generate a django keys with `python manage.py djecrety` and copy it to the .env file. -9. Create the InfluxDB buckets: `python src/manage.py initbuckets` +9. Create the InfluxDB buckets: `python manage.py initbuckets` Note: remove `--build` to skip building the container, will use the cached one (last build) diff --git a/docker-compose-deploy.yml b/docker-compose-deploy.yml index 1e1e83b3..13660fea 100644 --- a/docker-compose-deploy.yml +++ b/docker-compose-deploy.yml @@ -22,7 +22,7 @@ services: driver: "json-file" options: max-file: "5" # file count - max-size: "10m" # file size + max-size: "1m" # file size proxy: @@ -51,7 +51,11 @@ services: pgadmin: container_name: pgadmin4_container +<<<<<<< HEAD + image: dpage/pgadmin4:8.6 +======= image: dpage/pgadmin4:8.9 +>>>>>>> 4c892495399d42d7e674640b738a495bfe09dc8b restart: always environment: - PGADMIN_DEFAULT_EMAIL=${PGADMIN_DEFAULT_EMAIL:-admin@admin.com} @@ -68,7 +72,7 @@ services: crowdsec: - image: crowdsecurity/crowdsec:v1.6.0-1 + image: crowdsecurity/crowdsec:v1.6.2 restart: always ports: - "8080:8080" diff --git a/docker-compose.yml b/docker-compose.yml index 667bd22f..16c02e9a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -23,7 +23,6 @@ services: build: context: . volumes: - - ./src/transmission/processing/temp/:/app/transmission/processing/temp/ - ./src/home/temp/:/app/home/temp/ environment: - ALLOWED_HOSTS=${ALLOWED_HOSTS:-localhost,127.0.0.1} @@ -32,7 +31,6 @@ services: - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-postgres} - POSTGRES_HOST=${POSTGRES_HOST:-db} - POSTGRES_PORT=${POSTGRES_PORT:-5432} - - INFLUXDB_V2_ORG=${INFLUXDB_V2_ORG:-DelfiSpace} - INFLUXDB_V2_TOKEN=${INFLUXDB_V2_TOKEN:-adminpwd} restart: always depends_on: @@ -56,8 +54,8 @@ services: - DOCKER_INFLUXDB_INIT_MODE=setup - DOCKER_INFLUXDB_INIT_USERNAME=${INFLUX_USERNAME:-admin} - DOCKER_INFLUXDB_INIT_PASSWORD=${INFLUX_PASSWORD:-adminpwd} - - DOCKER_INFLUXDB_INIT_ORG=${INFLUXDB_V2_ORG:-DelfiSpace} - - DOCKER_INFLUXDB_INIT_BUCKET=${INFLUX_BUCKET:-default} + - DOCKER_INFLUXDB_INIT_ORG=default + - DOCKER_INFLUXDB_INIT_BUCKET=default - DOCKER_INFLUXDB_INIT_ADMIN_TOKEN=${INFLUXDB_V2_TOKEN:-adminpwd} restart: always logging: @@ -80,6 +78,7 @@ services: environment: - GF_SERVER_DOMAIN=${GF_SERVER_DOMAIN:-localhost} - GF_INFLUXDB_V2_TOKEN=${INFLUXDB_V2_TOKEN:-adminpwd} + - INFLUXDB_V2_ORG=${INFLUXDB_V2_ORG:-DelfiSpace} restart: always logging: driver: "json-file" diff --git a/docs/deployment_intructions.md b/docs/deployment_intructions.md index c3a4e2bf..94e2d94b 100644 --- a/docs/deployment_intructions.md +++ b/docs/deployment_intructions.md @@ -32,22 +32,25 @@ The dependencies are generated by running `./create_python_requirements`. This a 3. Install the requirements (one time instruction): `pip install -r requirements.txt -r requirements_dev.txt` -4. Set up the database via docker or connect your own Postgres instance +4. Set up the database via docker `docker compose up db` 5. Run the migrations from the root folder: `python src/manage.py migrate` -6. Create the InfluxDB buckets: +6. Set up InfluxDB via docker +`docker compose up influxdb` + +7. Create the InfluxDB buckets: `python src/manage.py initbuckets` -7. Run the server from the root folder: +8. Run the server from the root folder: `python src/manage.py runserver` The server runs on http://127.0.0.1:8000/ -8. To run the tests: +9. To run the tests: `python src/manage.py test` -9. To run pylint: +10. To run pylint: `find src -name "*.py" | xargs pylint` ## Setup (Run via docker) @@ -118,7 +121,7 @@ CROWDSEC_LAPI= 8. Generate a django keys with `python manage.py djecrety` and copy it to the .env file. -9. Create the InfluxDB buckets: `python src/manage.py initbuckets` +9. Create the InfluxDB buckets: `python manage.py initbuckets` Note: remove `--build` to skip building the container, will use the cached one (last build) diff --git a/grafana/dashboards/Delfi-C3/overview.json b/grafana/dashboards/Delfi-C3/overview.json new file mode 100644 index 00000000..cd32ac61 --- /dev/null +++ b/grafana/dashboards/Delfi-C3/overview.json @@ -0,0 +1,482 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 2, + "links": [ + { + "asDropdown": false, + "icon": "dashboard", + "includeVars": false, + "keepTime": true, + "tags": [], + "targetBlank": false, + "title": "Telemetry", + "tooltip": "Delfi-C3 Telemetry", + "type": "link", + "url": "https://delfispace.tudelft.nl/grafana/d/Delfi-C3_Telemetry" + }, + { + "asDropdown": false, + "icon": "dashboard", + "includeVars": false, + "keepTime": true, + "tags": [], + "targetBlank": false, + "title": "Payload", + "tooltip": "Delfi-C3 Payload", + "type": "link", + "url": "https://delfispace.tudelft.nl/grafana/d/Delfi-C3_Payload" + } + ], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "gridPos": { + "h": 9, + "w": 8, + "x": 0, + "y": 0 + }, + "id": 1, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "", + "mode": "markdown" + }, + "pluginVersion": "10.3.1", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "", + "refId": "A" + } + ], + "type": "text" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + } + }, + "displayName": "${__field.labels.application}", + "mappings": [], + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 8, + "y": 0 + }, + "id": 2, + "options": { + "displayLabels": [ + "percent", + "name" + ], + "legend": { + "displayMode": "list", + "placement": "bottom", + "showLegend": false + }, + "pieType": "pie", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": false + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": " from(bucket: \"delfi_c3\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"_raw\")\n |> filter(fn: (r) => r[\"_field\"] == \"processed\")\n |> group(columns: [\"application\"], mode:\"by\")\n |> count()\n ", + "refId": "A" + } + ], + "title": "Data Source", + "type": "piechart" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "dark-green", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 10, + "x": 14, + "y": 0 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "10.3.1", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_c3\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"_raw\")\n |> filter(fn: (r) => r[\"_field\"] == \"processed\")\n |> pivot(rowKey:[\"_time\"], columnKey: [\"_field\"], valueColumn: \"_value\") \n |> filter(fn: (r) => \"${username}\" == \"All\" or r[\"user\"] == \"${username}\") \n |> filter(fn: (r) => \"${application}\" == \"All\" or r[\"application\"] == \"${application}\") \n |> group()\n |> aggregateWindow(every: v.windowPeriod, fn: count, column: \"processed\")\n |> cumulativeSum(columns: [\"processed\"]) ", + "refId": "A" + } + ], + "title": "Frame Count", + "type": "stat" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "dark-green", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 8, + "x": 0, + "y": 9 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/^user \\{_start=\"2008\\-04\\-28 00:00:00 \\+0000 UTC\", _stop=\"2023\\-11\\-14 23:59:59\\.999 \\+0000 UTC\"\\}$/", + "limit": 1, + "values": true + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "10.3.1", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_c3\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"_raw\")\n |> filter(fn: (r) => r[\"_field\"] == \"processed\")\n |> group()\n |> last()", + "refId": "A" + } + ], + "title": "Last frame received by", + "type": "stat" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "dark-green", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "dateTimeAsIsoNoDateIfToday", + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 9, + "x": 8, + "y": 9 + }, + "id": 5, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/^_time$/", + "limit": 1, + "values": true + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "10.3.1", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_c3\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"_raw\")\n |> filter(fn: (r) => r[\"_field\"] == \"processed\")\n |> group()\n |> last()", + "refId": "A" + } + ], + "title": "Last frame received on", + "type": "stat" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "dark-green", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 7, + "x": 17, + "y": 9 + }, + "id": 6, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [], + "fields": "/^application \\{_start=\"2008\\-04\\-28 00:00:00 \\+0000 UTC\", _stop=\"2023\\-11\\-14 23:59:59\\.999 \\+0000 UTC\"\\}$/", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "10.3.1", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_c3\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"_raw\")\n |> filter(fn: (r) => r[\"_field\"] == \"processed\")\n |> group()\n |> last()", + "refId": "A" + } + ], + "title": "Last frame received using", + "type": "stat" + } + ], + "refresh": false, + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [ + { + "allValue": "All", + "current": { + "selected": true, + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "definition": "from(bucket: \"delfi_c3\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"_raw\")\n |> distinct(column: \"user\")\n |> group()\n |> unique(column: \"user\")\n |> keep(columns: [\"user\"])", + "hide": 0, + "includeAll": true, + "label": "Username", + "multi": false, + "name": "username", + "options": [], + "query": "from(bucket: \"delfi_c3\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"_raw\")\n |> distinct(column: \"user\")\n |> group()\n |> unique(column: \"user\")\n |> keep(columns: [\"user\"])", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 1, + "type": "query" + }, + { + "allValue": "All", + "current": { + "selected": true, + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "definition": "from(bucket: \"delfi_c3\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"_raw\")\n\n|> distinct(column: \"application\")\n//|> keep(columns: [\"_value\"])\n|> group()\n|> unique(column: \"application\")\n|> keep(columns: [\"application\"])", + "description": "", + "hide": 0, + "includeAll": true, + "label": "Application", + "multi": false, + "name": "application", + "options": [], + "query": "from(bucket: \"delfi_c3\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"_raw\")\n\n|> distinct(column: \"application\")\n//|> keep(columns: [\"_value\"])\n|> group()\n|> unique(column: \"application\")\n|> keep(columns: [\"application\"])", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 1, + "type": "query" + } + ] + }, + "time": { + "from": "2008-04-28T00:00:00.000Z", + "to": "2023-11-14T23:59:59.999Z" + }, + "timepicker": {}, + "timezone": "utc", + "title": "Overview", + "uid": "Delfi-C3_Overview", + "version": 7, + "weekStart": "" +} diff --git a/grafana/dashboards/Delfi-C3/payload.json b/grafana/dashboards/Delfi-C3/payload.json new file mode 100644 index 00000000..3801bda8 --- /dev/null +++ b/grafana/dashboards/Delfi-C3/payload.json @@ -0,0 +1,804 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 3, + "links": [ + { + "asDropdown": false, + "icon": "dashboard", + "includeVars": false, + "keepTime": true, + "tags": [], + "targetBlank": false, + "title": "Overview", + "tooltip": "Delfi-C3 Overview", + "type": "link", + "url": "https://delfispace.tudelft.nl/grafana/d/Delfi-C3_Overview" + }, + { + "asDropdown": false, + "icon": "dashboard", + "includeVars": false, + "keepTime": true, + "tags": [], + "targetBlank": false, + "title": "Telemetry", + "tooltip": "Delfi-C3 Telemetry", + "type": "link", + "url": "https://delfispace.tudelft.nl/grafana/d/Delfi-C3_Telemetry" + } + ], + "liveNow": false, + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 5, + "panels": [], + "title": "Reference Diodes", + "type": "row" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "smooth", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "mvolt", + "unitScale": true + }, + "overrides": [ + { + "__systemRef": "hideSeriesFrom", + "matcher": { + "id": "byNames", + "options": { + "mode": "exclude", + "names": [ + "RD.V_ZpXp", + "RD.V_ZpXm", + "RD.V_ZmYm", + "RD.V_ZmYp" + ], + "prefix": "All except:", + "readOnly": true + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": false, + "tooltip": false, + "viz": true + } + } + ] + } + ] + }, + "gridPos": { + "h": 11, + "w": 24, + "x": 0, + "y": 1 + }, + "id": 3, + "options": { + "legend": { + "calcs": [ + "min", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_c3\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"Payload\")\n |> filter(fn: (r) => r[\"_field\"] == \"RD.V_ZpXp\" or r[\"_field\"] == \"RD.V_ZpXm\" or r[\"_field\"] == \"RD.V_ZmYp\" or r[\"_field\"] == \"RD.V_ZmYm\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\")\n |> group(columns: [\"_field\"], mode:\"by\") \n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "Voltage", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "smooth", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "amp", + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 12 + }, + "id": 11, + "options": { + "legend": { + "calcs": [ + "min", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_c3\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"Payload\")\n |> filter(fn: (r) => r[\"_field\"] == \"RD.I_ZpXp\" or r[\"_field\"] == \"RD.I_ZpXm\" or r[\"_field\"] == \"RD.I_ZmYp\" or r[\"_field\"] == \"RD.I_ZmYm\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\")\n |> group(columns: [\"_field\"], mode:\"by\") \n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "Current", + "type": "timeseries" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 22 + }, + "id": 6, + "panels": [], + "title": "Thin-Film Solar Cells", + "type": "row" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "smooth", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 23 + }, + "id": 2, + "options": { + "legend": { + "calcs": [ + "min", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_c3\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"Payload\")\n |> filter(fn: (r) => r[\"_field\"] == \"TFSC_T_ZmYm\" or r[\"_field\"] == \"TFSC_T_ZmYp\" or r[\"_field\"] == \"TFSC_T_ZpXm\" or r[\"_field\"] == \"TFSC_T_ZpXp\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\")\n |> group(columns:[\"_field\"])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "Thin-Film Solar Cells Temperature", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "smooth", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "volt", + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 33 + }, + "id": 10, + "options": { + "legend": { + "calcs": [ + "min", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_c3\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"Payload\")\n |> filter(fn: (r) => r[\"_field\"] == \"TFSC_IV_ZmYm.V0\" or r[\"_field\"] == \"TFSC_IV_ZmYm.V1\" or r[\"_field\"] == \"TFSC_IV_ZmYm.V2\" or r[\"_field\"] == \"TFSC_IV_ZmYm.V3\" or r[\"_field\"] == \"TFSC_IV_ZmYm.V4\" or r[\"_field\"] == \"TFSC_IV_ZmYm.V5\" or r[\"_field\"] == \"TFSC_IV_ZmYm.V6\" or r[\"_field\"] == \"TFSC_IV_ZmYm.V7\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\")\n |> group(columns: [\"_field\"], mode:\"by\") \n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "ZmYm Voltage", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "amp", + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 41 + }, + "id": 9, + "options": { + "legend": { + "calcs": [ + "min", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_c3\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"Payload\")\n |> filter(fn: (r) => r[\"_field\"] == \"TFSC_IV_ZmYm.I0\" or r[\"_field\"] == \"TFSC_IV_ZmYm.I1\" or r[\"_field\"] == \"TFSC_IV_ZmYm.I2\" or r[\"_field\"] == \"TFSC_IV_ZmYm.I3\" or r[\"_field\"] == \"TFSC_IV_ZmYm.I4\" or r[\"_field\"] == \"TFSC_IV_ZmYm.I5\" or r[\"_field\"] == \"TFSC_IV_ZmYm.I6\" or r[\"_field\"] == \"TFSC_IV_ZmYm.I7\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\")\n |> group(columns: [\"_field\"], mode:\"by\") \n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "ZmYm Current", + "type": "timeseries" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 49 + }, + "id": 7, + "panels": [], + "title": "Autonomous Wireless Sun Sensor", + "type": "row" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "smooth", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "volt", + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 50 + }, + "id": 8, + "options": { + "legend": { + "calcs": [ + "min", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_c3\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"Payload\")\n |> filter(fn: (r) => r[\"_field\"] == \"AWSS.sensor_supply_voltage_Zm\" or r[\"_field\"] == \"AWSS.sensor_supply_voltage_Zp\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\")\n |> group(columns: [\"_field\"], mode:\"by\") \n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "Supply Voltage", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "smooth", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 60 + }, + "id": 4, + "options": { + "legend": { + "calcs": [ + "min", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_c3\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"Payload\")\n |> filter(fn: (r) => r[\"_field\"] == \"AWSS.sensor_temperature_Zm\" or r[\"_field\"] == \"AWSS.sensor_temperature_Zp\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\")\n |> group(columns: [\"_field\"], mode:\"by\") \n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "Temperature", + "type": "timeseries" + } + ], + "refresh": false, + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "2008-04-28T00:00:00.000Z", + "to": "2023-11-14T23:59:59.000Z" + }, + "timepicker": {}, + "timezone": "utc", + "title": "Payload", + "uid": "Delfi-C3_Payload", + "version": 5, + "weekStart": "" +} diff --git a/grafana/dashboards/Delfi-C3/telemetry.json b/grafana/dashboards/Delfi-C3/telemetry.json new file mode 100644 index 00000000..fc491b06 --- /dev/null +++ b/grafana/dashboards/Delfi-C3/telemetry.json @@ -0,0 +1,633 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 6, + "links": [ + { + "asDropdown": false, + "icon": "dashboard", + "includeVars": false, + "keepTime": true, + "tags": [], + "targetBlank": false, + "title": "Overview", + "tooltip": "Delfi-C3 Overview", + "type": "link", + "url": "https://delfispace.tudelft.nl/grafana/d/Delfi-C3_Overview" + }, + { + "asDropdown": false, + "icon": "dashboard", + "includeVars": false, + "keepTime": true, + "tags": [], + "targetBlank": false, + "title": "Payload", + "tooltip": "Delfi-C3 Payload", + "type": "link", + "url": "https://delfispace.tudelft.nl/grafana/d/Delfi-C3_Payload" + } + ], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "mamp", + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 18, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 3, + "options": { + "legend": { + "calcs": [ + "min", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_c3\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"Housekeeping\")\n |> filter(fn: (r) => r[\"_field\"] == \"RAP1_Rx_I\" or r[\"_field\"] == \"RAP1_Tx_I\" or r[\"_field\"] == \"RAP2_Rx_I\" or r[\"_field\"] == \"RAP2_Tx_I\" or r[\"_field\"] == \"ComBo_I\" or r[\"_field\"] == \"FM430_I\" or r[\"_field\"] == \"MeBo_Zm_I\" or r[\"_field\"] == \"MeBo_Zp_I\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\")\n |> group(columns: [\"_field\"], mode:\"by\") \n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "Currents", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 24, + "x": 0, + "y": 18 + }, + "id": 6, + "options": { + "legend": { + "calcs": [ + "min", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_c3\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"Housekeeping\")\n |> filter(fn: (r) => r[\"_field\"] == \"bus_V_dep\" or r[\"_field\"] == \"bus_V_sys\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\")\n |> group(columns: [\"_field\"], mode:\"by\") \n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")\n", + "refId": "A" + } + ], + "title": "Voltages", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "smooth", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "celsius", + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 29 + }, + "id": 2, + "options": { + "legend": { + "calcs": [ + "min", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_c3\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"Housekeeping\")\n |> filter(fn: (r) => r[\"_field\"] == \"OBC_T\" or r[\"_field\"] == \"RAP1_T\" or r[\"_field\"] == \"RAP2_T\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\")\n |> group(columns: [\"_field\"], mode:\"by\")\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "Temperatures", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 24, + "x": 0, + "y": 39 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": false + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_c3\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"Housekeeping\")\n |> filter(fn: (r) => r[\"_field\"] == \"BootCounter\")\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "Boot Counter", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 48 + }, + "id": 4, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": false + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_c3\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"Housekeeping\")\n |> filter(fn: (r) => r[\"_field\"] == \"BootNumber\")\n |> group(columns: [\"_field\"], mode:\"by\") \n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "Boot Number", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 24, + "x": 0, + "y": 56 + }, + "id": 5, + "options": { + "legend": { + "calcs": [ + "min", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_c3\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"Housekeeping\")\n |> filter(fn: (r) => r[\"_field\"] == \"FrameNumber\")\n |> group(columns: [\"_field\"], mode:\"by\") \n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "Frame Number", + "type": "timeseries" + } + ], + "refresh": false, + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "2008-04-28T00:00:00.000Z", + "to": "2023-11-14T23:59:59.999Z" + }, + "timepicker": {}, + "timezone": "utc", + "title": "Telemetry", + "uid": "Delfi-C3_Telemetry", + "version": 6, + "weekStart": "" +} diff --git a/grafana/dashboards/Delfi-PQ/comms.json b/grafana/dashboards/Delfi-PQ/comms.json new file mode 100644 index 00000000..877d6e16 --- /dev/null +++ b/grafana/dashboards/Delfi-PQ/comms.json @@ -0,0 +1,648 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 11, + "links": [ + { + "asDropdown": false, + "icon": "dashboard", + "includeVars": false, + "keepTime": false, + "tags": [], + "targetBlank": false, + "title": "Overview", + "tooltip": "Delfi-PQ Overview", + "type": "link", + "url": "https://delfispace.tudelft.nl/grafana/d/Delfi-PQ_Overview" + }, + { + "asDropdown": false, + "icon": "dashboard", + "includeVars": false, + "keepTime": false, + "tags": [], + "targetBlank": false, + "title": "EPS", + "tooltip": "EPS Telemetry", + "type": "link", + "url": "https://delfispace.tudelft.nl/grafana/d/Delfi-PQ_EPS" + }, + { + "asDropdown": false, + "icon": "dashboard", + "includeVars": false, + "keepTime": false, + "tags": [], + "targetBlank": false, + "title": "OBC", + "tooltip": "OBC Telemetry", + "type": "link", + "url": "https://delfispace.tudelft.nl/grafana/d/Delfi-PQ_OBC" + } + ], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "amp", + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 12, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 1, + "options": { + "legend": { + "calcs": [ + "logmin", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_pq\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"RadioCOMMSTelemetry\")\n |> filter(fn: (r) => r[\"_field\"] == \"Current\" or r[\"_field\"] == \"AmplifierCurrent\" or r[\"_field\"] == \"TransmitCurrent\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\")\n |> group(columns: [\"_field\"])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "Current", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "volt", + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 24, + "x": 0, + "y": 12 + }, + "id": 2, + "options": { + "legend": { + "calcs": [ + "logmin", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_pq\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"RadioCOMMSTelemetry\")\n |> filter(fn: (r) => r[\"_field\"] == \"Voltage\" or r[\"_field\"] == \"AmplifierVoltage\" or r[\"_field\"] == \"TransmitVoltage\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\")\n |> group(columns: [\"_field\"])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "Voltage", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "celsius", + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 12, + "w": 24, + "x": 0, + "y": 23 + }, + "id": 3, + "options": { + "legend": { + "calcs": [ + "min", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_pq\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"RadioCOMMSTelemetry\")\n |> filter(fn: (r) => r[\"_field\"] == \"MCUTemp\" or r[\"_field\"] == \"Temperature\" or r[\"_field\"] == \"AmplifierTemperature\" or r[\"_field\"] == \"PhasingTemperature\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\")\n |> group(columns: [\"_field\"])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "Temperature", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "dBm", + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 35 + }, + "id": 5, + "options": { + "legend": { + "calcs": [ + "min", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_pq\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"RadioCOMMSTelemetry\")\n |> filter(fn: (r) => r[\"_field\"] == \"ReceiverRSSI\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\")\n |> group(columns: [\"_field\"])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "Receiver Signal Strength Indicator", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "log": 2, + "type": "log" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s", + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 24, + "x": 0, + "y": 45 + }, + "id": 4, + "options": { + "legend": { + "calcs": [ + "logmin", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_pq\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"RadioCOMMSTelemetry\")\n |> filter(fn: (r) => r[\"_field\"] == \"Uptime\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\")\n |> group(columns: [\"_field\"])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "Uptime", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 13, + "w": 24, + "x": 0, + "y": 56 + }, + "id": 6, + "options": { + "legend": { + "calcs": [], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_pq\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"RadioCOMMSTelemetry\")\n |> filter(fn: (r) => r[\"_field\"] == \"BootCounter\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\")\n |> group(columns: [\"_field\"])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "Boot Counter", + "type": "timeseries" + } + ], + "refresh": "", + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "2022-01-13T00:00:00.000Z", + "to": "2024-01-08T23:59:59.999Z" + }, + "timepicker": {}, + "timezone": "utc", + "title": "COMMS", + "uid": "Delfi-PQ_COMMS", + "version": 2, + "weekStart": "" +} diff --git a/grafana/dashboards/Delfi-PQ/eps.json b/grafana/dashboards/Delfi-PQ/eps.json new file mode 100644 index 00000000..748511eb --- /dev/null +++ b/grafana/dashboards/Delfi-PQ/eps.json @@ -0,0 +1,1616 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 8, + "links": [ + { + "asDropdown": false, + "icon": "dashboard", + "includeVars": false, + "keepTime": false, + "tags": [], + "targetBlank": false, + "title": "Overview", + "tooltip": "Delfi-PQ Overview", + "type": "link", + "url": "https://delfispace.tudelft.nl/grafana/d/Delfi-PQ_Overview" + }, + { + "asDropdown": false, + "icon": "dashboard", + "includeVars": false, + "keepTime": false, + "tags": [], + "targetBlank": false, + "title": "COMMS", + "tooltip": "COMMS Telemetry", + "type": "link", + "url": "https://delfispace.tudelft.nl/grafana/d/Delfi-PQ_COMMS" + }, + { + "asDropdown": false, + "icon": "dashboard", + "includeVars": false, + "keepTime": false, + "tags": [], + "targetBlank": false, + "title": "OBC", + "tooltip": "OBC Telemetry", + "type": "link", + "url": "https://delfispace.tudelft.nl/grafana/d/Delfi-PQ_OBC" + } + ], + "liveNow": false, + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 16, + "panels": [], + "title": "Solar Panels", + "type": "row" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "amp", + "unitScale": true + }, + "overrides": [ + { + "__systemRef": "hideSeriesFrom", + "matcher": { + "id": "byNames", + "options": { + "mode": "exclude", + "names": [ + "PanelYpCurrent Valid", + "PanelXmCurrent Valid", + "PanelXpCurrent Valid", + "PanelYmCurrent Valid" + ], + "prefix": "All except:", + "readOnly": true + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": false, + "tooltip": false, + "viz": true + } + } + ] + } + ] + }, + "gridPos": { + "h": 9, + "w": 24, + "x": 0, + "y": 1 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_pq\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"RadioEPSTelemetry\")\n |> filter(fn: (r) => r[\"_field\"] == \"PanelYpCurrent\" or r[\"_field\"] == \"PanelYmCurrent\" or r[\"_field\"] == \"PanelXpCurrent\" or r[\"_field\"] == \"PanelXmCurrent\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\")\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "Solar Panels Current", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "volt", + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 10 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_pq\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"RadioEPSTelemetry\")\n |> filter(fn: (r) => r[\"_field\"] == \"PanelYpVoltage\" or r[\"_field\"] == \"PanelYmVoltage\" or r[\"_field\"] == \"PanelXpVoltage\" or r[\"_field\"] == \"PanelXmVoltage\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\")\n |> group(columns:[\"_field\"])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "Solar Panels Voltage", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "celsius", + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 18 + }, + "id": 10, + "options": { + "legend": { + "calcs": [ + "min", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_pq\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"RadioEPSTelemetry\")\n |> filter(fn: (r) => r[\"_field\"] == \"PanelYpTemperature\" or r[\"_field\"] == \"PanelXmTemperature\" or r[\"_field\"] == \"PanelXpTemperature\" or r[\"_field\"] == \"PanelYmTemperature\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\")\n |> group(columns: [\"_field\"])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "Solar Panels Temperature", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "volt", + "unitScale": true + }, + "overrides": [ + { + "__systemRef": "hideSeriesFrom", + "matcher": { + "id": "byNames", + "options": { + "mode": "exclude", + "names": [ + "CellYpVoltage" + ], + "prefix": "All except:", + "readOnly": true + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": false, + "tooltip": false, + "viz": true + } + } + ] + } + ] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 28 + }, + "id": 6, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_pq\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"RadioEPSTelemetry\")\n |> filter(fn: (r) => r[\"_field\"] == \"CellXpVoltage\" or r[\"_field\"] == \"CellXmVoltage\" or r[\"_field\"] == \"CellYpVoltage\" or r[\"_field\"] == \"CellYmVoltage\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\")\n |> group(columns: [\"_field\"])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "Solar Cells Voltage", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "amp", + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 28 + }, + "id": 7, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_pq\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"RadioEPSTelemetry\")\n |> filter(fn: (r) => r[\"_field\"] == \"CellXpCurrent\" or r[\"_field\"] == \"CellXmCurrent\" or r[\"_field\"] == \"CellYpCurrent\" or r[\"_field\"] == \"CellYmCurrent\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\")\n |> group(columns: [\"_field\"])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "Solar Cells Current", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "volt", + "unitScale": true + }, + "overrides": [ + { + "__systemRef": "hideSeriesFrom", + "matcher": { + "id": "byNames", + "options": { + "mode": "exclude", + "names": [ + "MpptXmVoltage" + ], + "prefix": "All except:", + "readOnly": true + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": false, + "tooltip": false, + "viz": true + } + } + ] + } + ] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 37 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_pq\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"RadioEPSTelemetry\")\n |> filter(fn: (r) => r[\"_field\"] == \"MpptXpVoltage\" or r[\"_field\"] == \"MpptXmVoltage\" or r[\"_field\"] == \"MpptYpVoltage\" or r[\"_field\"] == \"MpptYmVoltage\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\")\n |> group(columns: [\"_field\"])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "MPPT Voltage", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "amp", + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 37 + }, + "id": 3, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_pq\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"RadioEPSTelemetry\")\n |> filter(fn: (r) => r[\"_field\"] == \"MpptXpCurrent\" or r[\"_field\"] == \"MpptXmCurrent\" or r[\"_field\"] == \"MpptYpCurrent\" or r[\"_field\"] == \"MpptYmCurrent\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\")\n |> group(columns: [\"_field\"])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "MPPT Current", + "type": "timeseries" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 46 + }, + "id": 18, + "panels": [], + "title": "Main Board", + "type": "row" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "description": "Positive currents indicate a charging battery, negative currents a discharging one.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "amp", + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 16, + "w": 24, + "x": 0, + "y": 47 + }, + "id": 24, + "options": { + "legend": { + "calcs": [ + "min", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_pq\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"RadioEPSTelemetry\")\n |> filter(fn: (r) => r[\"_field\"] == \"UnregulatedINACurrent\" or r[\"_field\"] == \"InternalINACurrent\" or r[\"_field\"] == \"Bus1Current\" or r[\"_field\"] == \"Bus2Current\" or r[\"_field\"] == \"Bus3Current\" or r[\"_field\"] == \"Bus4Current\" or r[\"_field\"] == \"BatteryINACurrent\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\")\n |> group(columns: [\"_field\"])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "Current", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "volt", + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 14, + "w": 24, + "x": 0, + "y": 63 + }, + "id": 25, + "options": { + "legend": { + "calcs": [ + "min", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_pq\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"RadioEPSTelemetry\")\n |> filter(fn: (r) => r[\"_field\"] == \"Bus1Voltage\" or r[\"_field\"] == \"Bus2Voltage\" or r[\"_field\"] == \"Bus3Voltage\" or r[\"_field\"] == \"Bus4Voltage\" or r[\"_field\"] == \"InternalINAVoltage\" or r[\"_field\"] == \"UnregulatedINAVoltage\" or r[\"_field\"] == \"BatteryINAVoltage\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\")\n |> group(columns: [\"_field\"])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "Voltage", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "celsius", + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 24, + "x": 0, + "y": 77 + }, + "id": 26, + "options": { + "legend": { + "calcs": [ + "min", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_pq\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"RadioEPSTelemetry\")\n |> filter(fn: (r) => r[\"_field\"] == \"MCUTemp\" or r[\"_field\"] == \"BatteryGGTemperature\" or r[\"_field\"] == \"BatteryTMP20Temperature\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\")\n |> group(columns: [\"_field\"])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "Temperature", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "mamph", + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 88 + }, + "id": 23, + "options": { + "legend": { + "calcs": [ + "min", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_pq\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"RadioEPSTelemetry\")\n |> filter(fn: (r) => r[\"_field\"] == \"BatteryGGCapacity\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\")\n |> group(columns: [\"_field\"])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "Battery Capacity", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "log": 2, + "type": "log" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s", + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 24, + "x": 0, + "y": 98 + }, + "id": 27, + "options": { + "legend": { + "calcs": [ + "logmin", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_pq\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"RadioEPSTelemetry\")\n |> filter(fn: (r) => r[\"_field\"] == \"Uptime\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\")\n |> group(columns: [\"_field\"])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "EPS Uptime", + "type": "timeseries" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 107 + }, + "id": 19, + "panels": [], + "title": "Anomalies", + "type": "row" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "amp", + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 24, + "x": 0, + "y": 108 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_pq\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"RadioEPSTelemetry\")\n |> filter(fn: (r) => r[\"_field\"] == \"PanelYpCurrent\" or r[\"_field\"] == \"PanelYmCurrent\" or r[\"_field\"] == \"PanelXpCurrent\" or r[\"_field\"] == \"PanelXmCurrent\")\n |> filter(fn: (r) => r[\"status\"] != \"Valid\")\n |> group(columns: [\"_field\"])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "Solar Panels Current Anomalies", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "amp", + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 117 + }, + "id": 12, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_pq\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"RadioEPSTelemetry\")\n |> filter(fn: (r) => r[\"_field\"] == \"BatteryINACurrent\")\n |> filter(fn: (r) => r[\"status\"] != \"Valid\")\n |> group(columns: [\"_field\"])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "Battery Current Anomaly", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 125 + }, + "id": 11, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_pq\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"RadioEPSTelemetry\")\n |> filter(fn: (r) => r[\"_field\"] == \"Bus1Voltage\" or r[\"_field\"] == \"Bus2Voltage\" or r[\"_field\"] == \"Bus3Voltage\" or r[\"_field\"] == \"Bus4Voltage\" or r[\"_field\"] == \"BatteryGGVoltage\" or r[\"_field\"] == \"UnregulatedINAVoltage\" or r[\"_field\"] == \"InternalINAVoltage\")\n |> filter(fn: (r) => r[\"status\"] != \"Valid\")\n |> group(columns: [\"_field\"])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "EPS Voltage Anomalies ", + "type": "timeseries" + } + ], + "refresh": "", + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "2022-01-13T00:00:00.000Z", + "to": "2024-01-08T23:59:59.999Z" + }, + "timepicker": {}, + "timezone": "utc", + "title": "EPS", + "uid": "Delfi-PQ_EPS", + "version": 2, + "weekStart": "" +} diff --git a/grafana/dashboards/Delfi-PQ/obc.json b/grafana/dashboards/Delfi-PQ/obc.json new file mode 100644 index 00000000..e896d205 --- /dev/null +++ b/grafana/dashboards/Delfi-PQ/obc.json @@ -0,0 +1,556 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 8, + "links": [ + { + "asDropdown": false, + "icon": "dashboard", + "includeVars": false, + "keepTime": false, + "tags": [], + "targetBlank": false, + "title": "Overview", + "tooltip": "Delfi-PQ Overview", + "type": "link", + "url": "https://delfispace.tudelft.nl/grafana/d/Delfi-PQ_Overview" + }, + { + "asDropdown": false, + "icon": "dashboard", + "includeVars": false, + "keepTime": false, + "tags": [], + "targetBlank": false, + "title": "EPS", + "tooltip": "EPS Telemetry", + "type": "link", + "url": "https://delfispace.tudelft.nl/grafana/d/Delfi-PQ_EPS" + }, + { + "asDropdown": false, + "icon": "dashboard", + "includeVars": false, + "keepTime": false, + "tags": [], + "targetBlank": false, + "title": "COMMS", + "tooltip": "COMMS Telemetry", + "type": "link", + "url": "https://delfispace.tudelft.nl/grafana/d/Delfi-PQ_COMMS" + } + ], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "amp", + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 1, + "options": { + "legend": { + "calcs": [ + "min", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": false + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_pq\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"RadioOBCTelemetry\")\n |> filter(fn: (r) => r[\"_field\"] == \"Current\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\")\n |> group(columns: [\"_field\"])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "Current", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "volt", + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 24, + "x": 0, + "y": 10 + }, + "id": 2, + "options": { + "legend": { + "calcs": [ + "logmin", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": false + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_pq\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"RadioOBCTelemetry\")\n |> filter(fn: (r) => r[\"_field\"] == \"Voltage\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\")\n |> group(columns: [\"_field\"])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "Voltage", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "celsius", + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 14, + "w": 24, + "x": 0, + "y": 21 + }, + "id": 3, + "options": { + "legend": { + "calcs": [ + "min", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_pq\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"RadioOBCTelemetry\")\n |> filter(fn: (r) => r[\"_field\"] == \"MCUTemp\" or r[\"_field\"] == \"Temperature\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\")\n |> group(columns: [\"_field\"])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "Temperature", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "log": 2, + "type": "log" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s", + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 24, + "x": 0, + "y": 35 + }, + "id": 4, + "options": { + "legend": { + "calcs": [ + "logmin", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": false + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_pq\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"RadioOBCTelemetry\")\n |> filter(fn: (r) => r[\"_field\"] == \"Uptime\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\")\n |> group(columns: [\"_field\"])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "Uptime", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 24, + "x": 0, + "y": 46 + }, + "id": 5, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": false + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_pq\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"RadioOBCTelemetry\")\n |> filter(fn: (r) => r[\"_field\"] == \"BootCounter\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\")\n |> group(columns: [\"_field\"])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", + "refId": "A" + } + ], + "title": "Boot Counter", + "type": "timeseries" + } + ], + "refresh": "", + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "2022-01-13T00:00:00.000Z", + "to": "2024-01-08T23:59:59.999Z" + }, + "timepicker": {}, + "timezone": "utc", + "title": "OBC", + "uid": "Delfi-PQ_OBC", + "version": 5, + "weekStart": "" +} diff --git a/grafana/dashboards/Delfi-PQ/overview.json b/grafana/dashboards/Delfi-PQ/overview.json new file mode 100644 index 00000000..769548ca --- /dev/null +++ b/grafana/dashboards/Delfi-PQ/overview.json @@ -0,0 +1,469 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 2, + "links": [ + { + "asDropdown": false, + "icon": "dashboard", + "includeVars": false, + "keepTime": true, + "tags": [], + "targetBlank": false, + "title": "EPS", + "tooltip": "EPS Telemetry", + "type": "link", + "url": "https://delfispace.tudelft.nl/grafana/d/Delfi-PQ_EPS" + }, + { + "asDropdown": false, + "icon": "dashboard", + "includeVars": false, + "keepTime": true, + "tags": [], + "targetBlank": false, + "title": "COMMS", + "tooltip": "COMMS Telemetry", + "type": "link", + "url": "https://delfispace.tudelft.nl/grafana/d/Delfi-PQ_COMMS" + }, + { + "asDropdown": false, + "icon": "dashboard", + "includeVars": false, + "keepTime": false, + "tags": [], + "targetBlank": false, + "title": "OBC", + "tooltip": "OBC Telemetry", + "type": "link", + "url": "https://delfispace.tudelft.nl/grafana/d/Delfi-PQ_OBC" + } + ], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "gridPos": { + "h": 9, + "w": 8, + "x": 0, + "y": 0 + }, + "id": 1, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "", + "mode": "markdown" + }, + "pluginVersion": "10.3.1", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "", + "refId": "A" + } + ], + "type": "text" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + } + }, + "displayName": "${__field.labels.__values}", + "mappings": [], + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 8, + "y": 0 + }, + "id": 2, + "options": { + "displayLabels": [ + "percent", + "name" + ], + "legend": { + "displayMode": "list", + "placement": "bottom", + "showLegend": false + }, + "pieType": "pie", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": false + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": " from(bucket: \"delfi_pq\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"_raw\" and r[\"_field\"] == \"application\")\n |> duplicate(column: \"_value\", as: \"new_column\")\n |> group(columns: [\"new_column\"])\n |> count()\n ", + "refId": "A" + } + ], + "title": "Data Source", + "type": "piechart" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "dark-green", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 10, + "x": 14, + "y": 0 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "10.3.1", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_pq\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"_raw\")\n |> filter(fn: (r) => r[\"_field\"] == \"user\")\n |> filter(fn: (r) => \"${username}\" == \"All\" or r[\"_value\"] == \"${username}\") \n |> aggregateWindow(every: v.windowPeriod, fn: count)\n |> cumulativeSum(columns: [\"_value\"]) ", + "refId": "A" + } + ], + "title": "Frame Count", + "type": "stat" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "dark-green", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 8, + "x": 0, + "y": 9 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/^user$/", + "limit": 1, + "values": true + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "10.3.1", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_pq\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"_raw\")\n |> filter(fn: (r) => r[\"_field\"] == \"user\")\n |> last()", + "refId": "A" + } + ], + "title": "Last frame received by", + "type": "stat" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "dark-green", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "dateTimeAsIsoNoDateIfToday", + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 9, + "x": 8, + "y": 9 + }, + "id": 5, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/^Time$/", + "limit": 1, + "values": true + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "10.3.1", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_pq\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"_raw\")\n |> filter(fn: (r) => r[\"_field\"] == \"user\")\n |> last()", + "refId": "A" + } + ], + "title": "Last frame received on", + "type": "stat" + }, + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "dark-green", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 7, + "x": 17, + "y": 9 + }, + "id": 6, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [], + "fields": "/^application$/", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "10.3.1", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "query": "from(bucket: \"delfi_pq\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"_raw\")\n |> filter(fn: (r) => r[\"_field\"] == \"application\")\n |> last()", + "refId": "A" + } + ], + "title": "Last frame received using", + "type": "stat" + } + ], + "refresh": "", + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [ + { + "allValue": "All", + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "influxdb", + "uid": "3ap6QYRVz" + }, + "definition": "from(bucket: \"delfi_pq\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"_raw\")\n |> filter(fn: (r) => r[\"_field\"] == \"user\")\n |> distinct()", + "description": "", + "hide": 0, + "includeAll": true, + "label": "Username", + "multi": false, + "name": "username", + "options": [], + "query": "from(bucket: \"delfi_pq\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"_raw\")\n |> filter(fn: (r) => r[\"_field\"] == \"user\")\n |> distinct()", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 1, + "type": "query" + } + ] + }, + "time": { + "from": "2022-01-13T00:00:00.000Z", + "to": "2024-01-08T23:59:59.999Z" + }, + "timepicker": {}, + "timezone": "utc", + "title": "Overview", + "uid": "Delfi-PQ_Overview", + "version": 3, + "weekStart": "" +} diff --git a/grafana/dashboards/delfi_c3.json b/grafana/dashboards/delfi_c3.json deleted file mode 100644 index 5d9f7923..00000000 --- a/grafana/dashboards/delfi_c3.json +++ /dev/null @@ -1,133 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "grafana", - "uid": "-- Grafana --" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "id": 3, - "links": [], - "liveNow": false, - "panels": [ - { - "datasource": { - "type": "influxdb", - "uid": "3ap6QYRVz" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 0 - }, - "id": 1, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "influxdb", - "uid": "3ap6QYRVz" - }, - "query": "from(bucket: \"delfi_c3_downlink\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"Housekeeping\")\n |> filter(fn: (r) => r[\"_field\"] == \"BootCounter\")\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", - "refId": "A" - } - ], - "title": "Boot Counter", - "type": "timeseries" - } - ], - "refresh": "", - "schemaVersion": 39, - "tags": [], - "templating": { - "list": [] - }, - "time": { - "from": "now-6h", - "to": "now" - }, - "timepicker": {}, - "timezone": "", - "title": "Delfi-C3", - "uid": "ec0b906e-c704-41d9-a063-645b803db188", - "version": 1, - "weekStart": "" - } \ No newline at end of file diff --git a/grafana/dashboards/delfi_pq.json b/grafana/dashboards/delfi_pq.json deleted file mode 100644 index 5870f1bd..00000000 --- a/grafana/dashboards/delfi_pq.json +++ /dev/null @@ -1,975 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "grafana", - "uid": "-- Grafana --" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "target": { - "limit": 100, - "matchAny": false, - "tags": [], - "type": "dashboard" - }, - "type": "dashboard" - } - ] - }, - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "links": [], - "liveNow": false, - "panels": [ - { - "datasource": { - "type": "influxdb", - "uid": "3ap6QYRVz" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "__systemRef": "hideSeriesFrom", - "matcher": { - "id": "byNames", - "options": { - "mode": "exclude", - "names": [ - "PanelXmCurrent Valid" - ], - "prefix": "All except:", - "readOnly": true - } - }, - "properties": [ - { - "id": "custom.hideFrom", - "value": { - "legend": false, - "tooltip": false, - "viz": true - } - } - ] - } - ] - }, - "gridPos": { - "h": 9, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 14, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "influxdb", - "uid": "3ap6QYRVz" - }, - "query": "from(bucket: \"delfi_pq_downlink\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"DelfiPqRadioEPSTelemetry\")\n |> filter(fn: (r) => r[\"_field\"] == \"PanelYpCurrent\" or r[\"_field\"] == \"PanelYmCurrent\" or r[\"_field\"] == \"PanelXpCurrent\" or r[\"_field\"] == \"PanelXmCurrent\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\" or r[\"status\"] == \"Too Low\" or r[\"status\"] == \"Too High\")\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", - "refId": "A" - } - ], - "title": "Solar Panels Currents + Status", - "type": "timeseries" - }, - { - "datasource": { - "type": "influxdb", - "uid": "3ap6QYRVz" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 9, - "w": 24, - "x": 0, - "y": 9 - }, - "id": 15, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "influxdb", - "uid": "3ap6QYRVz" - }, - "query": "from(bucket: \"delfi_pq_downlink\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"DelfiPqRadioEPSTelemetry\")\n |> filter(fn: (r) => r[\"_field\"] == \"PanelYpCurrent\" or r[\"_field\"] == \"PanelYmCurrent\" or r[\"_field\"] == \"PanelXpCurrent\" or r[\"_field\"] == \"PanelXmCurrent\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\" or r[\"status\"] == \"Too Low\" or r[\"status\"] == \"Too High\")\n |> group(columns: [\"_field\"])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", - "refId": "A" - } - ], - "title": "Solar Panels Currents", - "type": "timeseries" - }, - { - "datasource": { - "type": "influxdb", - "uid": "3ap6QYRVz" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 24, - "x": 0, - "y": 18 - }, - "id": 12, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "influxdb", - "uid": "3ap6QYRVz" - }, - "query": "from(bucket: \"delfi_pq_downlink\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"DelfiPqRadioEPSTelemetry\")\n |> filter(fn: (r) => r[\"_field\"] == \"PanelYpVoltage\" or r[\"_field\"] == \"PanelYmVoltage\" or r[\"_field\"] == \"PanelXpVoltage\" or r[\"_field\"] == \"PanelXmVoltage\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\" or r[\"status\"] == \"Too Low\" or r[\"status\"] == \"Too High\")\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", - "refId": "A" - } - ], - "title": "Solar Panels Voltages + Status", - "type": "timeseries" - }, - { - "datasource": { - "type": "influxdb", - "uid": "3ap6QYRVz" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 24, - "x": 0, - "y": 26 - }, - "id": 9, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "influxdb", - "uid": "3ap6QYRVz" - }, - "query": "from(bucket: \"delfi_pq_downlink\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"DelfiPqRadioEPSTelemetry\")\n |> filter(fn: (r) => r[\"_field\"] == \"PanelYpVoltage\" or r[\"_field\"] == \"PanelYmVoltage\" or r[\"_field\"] == \"PanelXpVoltage\" or r[\"_field\"] == \"PanelXmVoltage\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\" or r[\"status\"] == \"Too Low\" or r[\"status\"] == \"Too High\")\n |> group(columns:[\"_field\"])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", - "refId": "A" - } - ], - "title": "Solar Panels Voltages ", - "type": "timeseries" - }, - { - "datasource": { - "type": "influxdb", - "uid": "3ap6QYRVz" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "__systemRef": "hideSeriesFrom", - "matcher": { - "id": "byNames", - "options": { - "mode": "exclude", - "names": [ - "PanelYpTemperature Valid" - ], - "prefix": "All except:", - "readOnly": true - } - }, - "properties": [ - { - "id": "custom.hideFrom", - "value": { - "legend": false, - "tooltip": false, - "viz": true - } - } - ] - } - ] - }, - "gridPos": { - "h": 8, - "w": 24, - "x": 0, - "y": 34 - }, - "id": 10, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "influxdb", - "uid": "3ap6QYRVz" - }, - "query": "from(bucket: \"delfi_pq_downlink\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"DelfiPqRadioEPSTelemetry\")\n |> filter(fn: (r) => r[\"_field\"] == \"PanelYpTemperature\" or r[\"_field\"] == \"PanelXmTemperature\" or r[\"_field\"] == \"PanelXpTemperature\" or r[\"_field\"] == \"PanelYmTemperature\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\" or r[\"status\"] == \"Too Low\" or r[\"status\"] == \"Too High\")\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", - "refId": "A" - } - ], - "title": "Solar Panels Temp + Status", - "type": "timeseries" - }, - { - "datasource": { - "type": "influxdb", - "uid": "3ap6QYRVz" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "__systemRef": "hideSeriesFrom", - "matcher": { - "id": "byNames", - "options": { - "mode": "exclude", - "names": [ - "PanelXmTemperature" - ], - "prefix": "All except:", - "readOnly": true - } - }, - "properties": [ - { - "id": "custom.hideFrom", - "value": { - "legend": false, - "tooltip": false, - "viz": true - } - } - ] - } - ] - }, - "gridPos": { - "h": 8, - "w": 24, - "x": 0, - "y": 42 - }, - "id": 11, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "influxdb", - "uid": "3ap6QYRVz" - }, - "query": "from(bucket: \"delfi_pq_downlink\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"DelfiPqRadioEPSTelemetry\")\n |> filter(fn: (r) => r[\"_field\"] == \"PanelYpTemperature\" or r[\"_field\"] == \"PanelXmTemperature\" or r[\"_field\"] == \"PanelXpTemperature\" or r[\"_field\"] == \"PanelYmTemperature\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\" or r[\"status\"] == \"Too Low\" or r[\"status\"] == \"Too High\")\n |> group(columns: [\"_field\"])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", - "refId": "A" - } - ], - "title": "Solar Panels Temp", - "type": "timeseries" - }, - { - "datasource": { - "type": "influxdb", - "uid": "3ap6QYRVz" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 9, - "w": 12, - "x": 0, - "y": 50 - }, - "id": 6, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "influxdb", - "uid": "3ap6QYRVz" - }, - "query": "from(bucket: \"delfi_pq_downlink\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"DelfiPqRadioEPSTelemetry\")\n |> filter(fn: (r) => r[\"_field\"] == \"PanelYpTemperature\" or r[\"_field\"] == \"PanelYpVoltage\" or r[\"_field\"] == \"PanelYpCurrent\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\" or r[\"status\"] == \"Too Low\" or r[\"status\"] == \"Too High\")\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", - "refId": "A" - } - ], - "title": "PanelYp + Status", - "type": "timeseries" - }, - { - "datasource": { - "type": "influxdb", - "uid": "3ap6QYRVz" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 9, - "w": 12, - "x": 12, - "y": 50 - }, - "id": 7, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "influxdb", - "uid": "3ap6QYRVz" - }, - "query": "from(bucket: \"delfi_pq_downlink\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"DelfiPqRadioEPSTelemetry\")\n |> filter(fn: (r) => r[\"_field\"] == \"PanelYpTemperature\" or r[\"_field\"] == \"PanelYpVoltage\" or r[\"_field\"] == \"PanelYpCurrent\")\n |> filter(fn: (r) => r[\"status\"] == \"Valid\" or r[\"status\"] == \"Too Low\" or r[\"status\"] == \"Too High\")\n |> group(columns: [\"_field\"])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", - "refId": "A" - } - ], - "title": "PanelYp", - "type": "timeseries" - }, - { - "datasource": { - "type": "influxdb", - "uid": "3ap6QYRVz" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 9, - "w": 12, - "x": 0, - "y": 59 - }, - "id": 2, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "influxdb", - "uid": "3ap6QYRVz" - }, - "query": "from(bucket: \"delfi_pq_downlink\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"DelfiPqRadioEPSTelemetry\")\n |> filter(fn: (r) => r[\"_field\"] == \"PanelYmCurrent\")\n |> filter(fn: (r) => r[\"status\"] == \"Too Low\" or r[\"status\"] == \"Valid\")\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", - "refId": "A" - } - ], - "title": "PanelYmCurrent + status", - "type": "timeseries" - }, - { - "datasource": { - "type": "influxdb", - "uid": "3ap6QYRVz" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 9, - "w": 12, - "x": 12, - "y": 59 - }, - "id": 3, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "influxdb", - "uid": "3ap6QYRVz" - }, - "query": "from(bucket: \"delfi_pq_downlink\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"DelfiPqRadioEPSTelemetry\")\n |> filter(fn: (r) => r[\"_field\"] == \"PanelYmCurrent\")\n |> filter(fn: (r) => r[\"status\"] == \"Too Low\" or r[\"status\"] == \"Valid\")\n |> group()\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", - "refId": "A" - } - ], - "title": "PanelYmCurrent", - "type": "timeseries" - } - ], - "refresh": "5s", - "schemaVersion": 36, - "style": "dark", - "tags": [], - "templating": { - "list": [] - }, - "time": { - "from": "2022-01-14T15:00:00.000Z", - "to": "2022-02-06T14:59:59.000Z" - }, - "timepicker": {}, - "timezone": "utc", - "title": "DelfiPQ", - "uid": "_j9ikmi4z", - "version": 1, - "weekStart": "" - } \ No newline at end of file diff --git a/grafana/dashboards/grafana-dashboard.yml b/grafana/dashboards/grafana-dashboard.yml index e020c284..6099532d 100644 --- a/grafana/dashboards/grafana-dashboard.yml +++ b/grafana/dashboards/grafana-dashboard.yml @@ -6,8 +6,9 @@ providers: folder: '' type: 'file' editable: true + allowUiUpdates: false options: # path to dashboard files on disk. Required when using the 'file' type path: /etc/grafana/provisioning/dashboards/ # use folder names from filesystem to create folders in Grafana - foldersFromFilesStructure: true \ No newline at end of file + foldersFromFilesStructure: true diff --git a/grafana/dashboards/reception_times.json b/grafana/dashboards/reception_times.json deleted file mode 100644 index 9e56f7a7..00000000 --- a/grafana/dashboards/reception_times.json +++ /dev/null @@ -1,309 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "grafana", - "uid": "-- Grafana --" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "target": { - "limit": 100, - "matchAny": false, - "tags": [], - "type": "dashboard" - }, - "type": "dashboard" - } - ] - }, - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "id": 2, - "links": [], - "liveNow": false, - "panels": [ - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 7, - "panels": [], - "title": "Receptions", - "type": "row" - }, - { - "datasource": { - "type": "influxdb", - "uid": "3ap6QYRVz" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 1 - }, - "id": 2, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "/^timestamp$/", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "9.1.2", - "targets": [ - { - "datasource": { - "type": "influxdb", - "uid": "3ap6QYRVz" - }, - "query": "from(bucket: \"delfi_pq_raw_data\")\n |> range(start: 0, stop: now())\n |> filter(fn: (r) => r._measurement == \"delfi_pq_downlink_raw_data\")\n |> filter(fn: (r) => r[\"_field\"] == \"timestamp\") \n |> last()\n", - "refId": "A" - } - ], - "title": "Delfi-PQ Last Reception", - "type": "stat" - }, - { - "datasource": { - "type": "influxdb", - "uid": "3ap6QYRVz" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 1 - }, - "id": 5, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "/^timestamp$/", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "9.1.2", - "targets": [ - { - "datasource": { - "type": "influxdb", - "uid": "3ap6QYRVz" - }, - "query": "from(bucket: \"delfi_c3_raw_data\")\n |> range(start: 0, stop: now())\n |> filter(fn: (r) => r._measurement == \"delfi_c3_downlink_raw_data\")\n |> filter(fn: (r) => r[\"_field\"] == \"timestamp\") \n |> last()\n", - "refId": "A" - } - ], - "title": "Delfi-C3 Last Reception", - "type": "stat" - }, - { - "datasource": { - "type": "influxdb", - "uid": "3ap6QYRVz" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 9 - }, - "id": 3, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "/^timestamp$/", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "9.1.2", - "targets": [ - { - "datasource": { - "type": "influxdb", - "uid": "3ap6QYRVz" - }, - "query": "from(bucket: \"delfi_next_raw_data\")\n |> range(start: 0, stop: now())\n |> filter(fn: (r) => r._measurement == \"delfi_next_downlink_raw_data\")\n |> filter(fn: (r) => r[\"_field\"] == \"timestamp\") \n |> last()\n", - "refId": "A" - } - ], - "title": "Delfi-Next Last Reception", - "type": "stat" - }, - { - "datasource": { - "type": "influxdb", - "uid": "3ap6QYRVz" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 9 - }, - "id": 4, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "/^timestamp$/", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "9.1.2", - "targets": [ - { - "datasource": { - "type": "influxdb", - "uid": "3ap6QYRVz" - }, - "query": "from(bucket: \"da_vinci_raw_data\")\n |> range(start: 0, stop: now())\n |> filter(fn: (r) => r._measurement == \"da_vinci_downlink_raw_data\")\n |> filter(fn: (r) => r[\"_field\"] == \"timestamp\") \n |> last()\n", - "refId": "A" - } - ], - "title": "Da-Vinci-Satellite Last Reception", - "type": "stat" - } - ], - "schemaVersion": 37, - "style": "dark", - "tags": [], - "templating": { - "list": [] - }, - "time": { - "from": "now-6h", - "to": "now" - }, - "timepicker": {}, - "timezone": "", - "title": "Latest Reception Times", - "uid": "sknvMScVz", - "version": 1, - "weekStart": "" -} \ No newline at end of file diff --git a/grafana/provisioning/grafana-datasources.yml b/grafana/provisioning/grafana-datasources.yml old mode 100644 new mode 100755 index 683c8be9..41c1e4df --- a/grafana/provisioning/grafana-datasources.yml +++ b/grafana/provisioning/grafana-datasources.yml @@ -10,5 +10,4 @@ datasources: token: $GF_INFLUXDB_V2_TOKEN jsonData: version: Flux - organization: Delfi Space - defaultBucket: delfi_pq_downlink \ No newline at end of file + organization: downlink diff --git a/src/delfitlm/settings.py b/src/delfitlm/settings.py index 793443fb..6735dfdf 100644 --- a/src/delfitlm/settings.py +++ b/src/delfitlm/settings.py @@ -80,6 +80,7 @@ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'django.contrib.humanize', 'django_filters', 'axes', # Django Axes 'channels', @@ -192,6 +193,12 @@ if 'test' in sys.argv or 'test_coverage' in sys.argv: # Covers regular testing and django-coverage DATABASES['default']['ENGINE'] = 'django.db.backends.sqlite3' +INFLUXDB = { + 'HOST' : "http://influxdb", + 'PORT' : 8086, + 'TOKEN' : os.environ.get('INFLUXDB_V2_TOKEN'), +} + # Password validation # https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators @@ -230,8 +237,6 @@ USE_I18N = True -USE_L10N = True - USE_TZ = True # Django Axes settings diff --git a/src/home/templates/home/index.html b/src/home/templates/home/index.html index 9135b3e9..c7b36b73 100644 --- a/src/home/templates/home/index.html +++ b/src/home/templates/home/index.html @@ -1,4 +1,5 @@ {% load static %} +{% load humanize %} {% block title %}DelfiSpace - Home{% endblock %} @@ -44,14 +45,19 @@ } +

DelfiSpace

-

Delfi Space - TU Delft SpaceOps

+ + +

Space Operations at the Delft University of Technology

+

Our Satellites

-
+
Delfi Space
Delfi-C3

Status: {{ delfi_c3_status }}

+

Launch:
   {% if delfi_c3_launch is not None %}{{ delfi_c3_launch|date:"M dS Y" }}{% else %}-{% endif %}

+

Last Reception:
   {% if delfi_c3_last_data is not None %}{{ delfi_c3_last_data|date:"M dS Y H:i:s" }} UTC{% else %}-{% endif %}

About - Dashboard + Dashboard
@@ -80,7 +88,9 @@
Delfi-C3
Delfi-n3Xt
-

Status: {{delfi_next_status}}

+

Status: {{delfi_next_status}}

+

Launch:
   {% if delfi_next_launch is not None %}{{ delfi_next_launch|date:"M dS Y" }}{% else %}-{% endif %}

+

Last Reception:
   {% if delfi_next_last_data is not None %}{{ delfi_next_last_data|date:"M dS Y H:i:s" }} UTC{% else %}-{% endif %}

About @@ -93,9 +103,11 @@
Delfi-n3Xt
Delfi-PQ
-

Status: {{delfi_pq_status}}

+

Status: {{delfi_pq_status}}

+

Launch:
   {% if delfi_pq_launch is not None %}{{ delfi_pq_launch|date:"M dS Y" }}{% else %}-{% endif %}

+

Last Reception:
   {% if delfi_pq_last_data is not None %}{{ delfi_pq_last_data|date:"M dS Y H:i:s" }} UTC{% else %}-{% endif %}

About - Dashboard + Dashboard
@@ -107,20 +119,26 @@
Delfi-PQ
Da Vinci Satellite
-

Status: {{da_vinci_status}}

+

Status: {{da_vinci_status}}

+

Launch:
   {% if da_vinci_launch is not None %}{{ da_vinci_launch|date:"M dS Y" }}{% else %}-{% endif %}

+

Last Reception:
   {% if da_vinci_last_data is not None %}{{ da_vinci_last_data|date:"M dS Y H:i:s" }} UTC{% else %}-{% endif %}

About
-
+ + +

Where are our satellites

+ {% include 'home/map.html' %} +

Only currently orbiting satellites are shown on the map. Decayed and not launched satellites ones are not visible.

{% endblock %} diff --git a/src/home/templates/home/map.html b/src/home/templates/home/map.html index c53937e1..a06f7b42 100644 --- a/src/home/templates/home/map.html +++ b/src/home/templates/home/map.html @@ -9,5 +9,5 @@ - + diff --git a/src/home/views.py b/src/home/views.py index 72cae4b2..e20e001d 100644 --- a/src/home/views.py +++ b/src/home/views.py @@ -2,6 +2,8 @@ from datetime import datetime, timedelta import json import os +import sys +import traceback from django.core.exceptions import PermissionDenied from django.core.serializers.json import DjangoJSONEncoder from django.http.response import JsonResponse @@ -10,6 +12,8 @@ from skyfield.api import load, EarthSatellite, wgs84, Topos from satellite_tle import fetch_tle_from_celestrak from transmission.processing.satellites import SATELLITES, TIME_FORMAT +from transmission.processing.influxdb_api import influxdb_api +from django_logger import logger #pylint: disable=W0718 def get_tle(norad_id: str): @@ -40,7 +44,10 @@ def get_tle(norad_id: str): return tles[norad_id]['tle'] except Exception as _: - pass + # something went wrong retrieving TLEs + # use the old ones, even if outdated + logger.error("Error retrieving TLEs\n%s", traceback.format_exc()) + return tles[norad_id]['tle'] return None @@ -50,7 +57,7 @@ def get_satellite_location_now(norad_id: str) -> dict: tle = get_tle(norad_id) if tle is None: - return {"satellite": None, "latitude": None, "longitude": None, "sunlit": None} + return {"satellite": None, "norad_id": None, "latitude": None, "longitude": None, "sunlit": None} time_scale = load.timescale() time = time_scale.now() @@ -67,7 +74,8 @@ def get_satellite_location_now(norad_id: str) -> dict: eph = load('de421.bsp') sunlit = satellite.at(time).is_sunlit(eph) - return {"satellite": str(tle[0]), "latitude": lat_deg, "longitude": lon_deg, "sunlit": int(sunlit)} + return {"satellite": str(tle[0]), "norad_id": norad_id, "latitude": lat_deg, "longitude": lon_deg, + "sunlit": int(sunlit)} def get_next_pass_over_delft(request, norad_id: str): @@ -111,10 +119,30 @@ def get_satellite_location_now_api(request, norad_id): def _get_satellites_status(): """Method to find satellite status.""" + try: + db = influxdb_api() + except : + db = None sats_status = {} for sat, info in SATELLITES.items(): sats_status[str(sat + "_status")] = info["status"] - + if db is None: + last_rx_time = None + else: + try: + last_rx_time = db.get_last_received_frame(sat) + except: + last_rx_time = None + if last_rx_time is not None and isinstance(last_rx_time, datetime): + sats_status[str(sat + "_last_data")] = last_rx_time + else: + sats_status[str(sat + "_last_data")] = None + + if info["launch"] is not None: + launch_time = datetime.strptime(info["launch"], '%Y-%m-%dT%H:%M:%S.%fZ') + sats_status[str(sat + "_launch")] = launch_time + else: + sats_status[str(sat + "_launch")] = None return sats_status @@ -126,9 +154,15 @@ def get_satellites_status(request): def home(request): """Render index.html page""" - context = _get_satellites_status() - return render(request, "home/index.html", context) + #context = _get_satellites_status() + context = [] + try: + context = _get_satellites_status() + return render(request, "home/index.html", context) + except Exception as _: + logger.error(traceback.format_exc()) + return render(request, "home/index.html", context) def ban_view(request): """Ban request""" diff --git a/src/members/migrations/0002_alter_apikey_hashed_key_alter_apikey_id.py b/src/members/migrations/0002_alter_apikey_hashed_key_alter_apikey_id.py new file mode 100644 index 00000000..8a380f0a --- /dev/null +++ b/src/members/migrations/0002_alter_apikey_hashed_key_alter_apikey_id.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2.10 on 2024-03-04 16:03 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('members', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='apikey', + name='hashed_key', + field=models.CharField(editable=False, max_length=150), + ), + migrations.AlterField( + model_name='apikey', + name='id', + field=models.CharField(editable=False, max_length=150, primary_key=True, serialize=False, unique=True), + ), + ] diff --git a/src/members/test/test_views_templates.py b/src/members/test/test_views_templates.py index 53cda73c..b4832825 100644 --- a/src/members/test/test_views_templates.py +++ b/src/members/test/test_views_templates.py @@ -759,7 +759,7 @@ def test_telemetry_persistence_after_account_deletion(self): self.assertEqual(len(Uplink.objects.all()), 3) for frame in Downlink.objects.all(): - self.assertEqual(frame.observer, str(self.user.UUID)) + self.assertEqual(frame.observer, str(self.user.username)) for frame in Uplink.objects.all(): self.assertEqual(frame.operator, 'user') @@ -781,7 +781,7 @@ def test_telemetry_persistence_after_account_deletion(self): self.assertEqual(len(Uplink.objects.all()), 3) for frame in Downlink.objects.all(): - self.assertEqual(frame.observer, str(self.user.UUID)) + self.assertEqual(frame.observer, str(self.user.username)) for frame in Uplink.objects.all(): self.assertEqual(frame.operator, 'user') diff --git a/src/static/map.js b/src/static/map.js index 446300dc..bf117039 100644 --- a/src/static/map.js +++ b/src/static/map.js @@ -1,57 +1,77 @@ let zoomLevel = 3; let latitude = 52.0116; let longitude = 4.3571; -let SATELLITES = { - "DELFI-N3XT": '39428', - -} // refresh rate in seconds const refreshRate = 2; -let map = L.map('map').setView([latitude, longitude], zoomLevel); +let map = L.map('map',{ + noWrap: true, + autoPan: false, + zoomSnap: 0.1, + maxBounds: [ + [-90.0, -180.0], + [90.0, 180.0]], + maxBoundsViscosity: 1.0 + }).setView([0, 0], 0); L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { - attribution: '© OpenStreetMap contributors' + attribution: '© OpenStreetMap contributors', + noWrap: true, + autoPan: false }).addTo(map); -let sunlightOverlay = L.terminator(); +// use an icon to show the satellite position +var satellite = L.icon({ + iconUrl: 'static/satellite.webp', + iconSize: [116, 87], // size of the icon + iconAnchor: [58, 43], // point of the icon which will correspond to marker's location + popupAnchor: [0, -21] // point from which the popup should open relative to the iconAnchor +}); + +// add the solar terminator +let sunlightOverlay = L.terminator({resolution: 5, longitudeRange:360}); sunlightOverlay.addTo(map); +// update the solar terminator periodically +function updateTerminator(t) +{ + t.setTime(); +} setInterval(function(){updateTerminator(sunlightOverlay)}, refreshRate * 1000); -function updateTerminator(t) { - t.setTime(); +// limit the zoom value to display the full Earth only once +function setMinimumZoom(map) +{ + let minZoom = 1.0 / 10.0 * Math.ceil(10 * Math.log2(Math.min(map.getSize().x, map.getSize().y) / 256)) + map.setMinZoom(minZoom); } +setMinimumZoom(map); let markers = {} +// display the satellites on the map function findSat() { fetch("/location/all/") .then(response => response.json()) .then(data => { let satellite_list = data.satellites; - console.log(satellite_list); for (const element of satellite_list){ - console.log(element); - let sat = element.satellite + let sat = element.satellite; let lat = element.latitude.toFixed(2); let long = element.longitude.toFixed(2); + let norad_id = element.norad_id; // let sunlit = element.sunlit; if (sat != null){ - updateSatMarker(sat, lat, long); + updateSatMarker(sat, norad_id, lat, long); } } }).catch(e => console.log(e)); } - - -function updateSatMarker(sat, lat, long,) { - - - fetch("/next-pass/"+ SATELLITES[sat]+"/") +function updateSatMarker(sat, norad_id, lat, long,) { + fetch("/next-pass/"+ norad_id + "/") .then(response => response.json()) .then(data => { let pass_events = data.passes; @@ -67,18 +87,27 @@ function updateSatMarker(sat, lat, long,) { if (markers.hasOwnProperty(sat)){ markers[sat].setLatLng([lat, long]); - markers[sat].bindPopup(sat + " Lat: " + lat + " Long: " + long +"
"+ next_pass); + //markers[sat].bindPopup(sat + " Lat: " + lat + " Long: " + long +"
"+ next_pass); + //markers[sat].bindPopup(sat, {autoPan: false}); } else{ - let marker = L.marker([lat, long]).addTo(map).bindPopup(sat + " Lat: " + lat + " Long: " + long +"
"+ next_pass).openPopup(); + //let marker = L.marker([lat, long], {icon: satellite}).addTo(map).bindPopup(sat + " Lat: " + lat + " Long: " + long +"
"+ next_pass); + let marker = L.marker([lat, long], {icon: satellite, autoPan: false}).addTo(map).bindPopup(sat).openPopup(); markers[sat] = marker } }).catch(e => console.log(e)); - -// updates map view according to Marker's new position -// map.setView([lat, long]); } +findSat(); + +// automatically update the satellite positions setInterval(findSat, refreshRate * 1000); + +// automatically update the minimum zoom if the map is resized +map.on('resize', function () + { + setMinimumZoom(map); + }); + diff --git a/src/static/satellite.webp b/src/static/satellite.webp new file mode 100644 index 00000000..4119632d Binary files /dev/null and b/src/static/satellite.webp differ diff --git a/src/static/terminator.js b/src/static/terminator.js new file mode 100644 index 00000000..e7a610f2 --- /dev/null +++ b/src/static/terminator.js @@ -0,0 +1,149 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('leaflet')) : + typeof define === 'function' && define.amd ? define(['leaflet'], factory) : + (global.L = global.L || {}, global.L.terminator = factory(global.L)); +}(this, (function (L) { 'use strict'; + + L = L && L.hasOwnProperty('default') ? L['default'] : L; + + /* Terminator.js -- Overlay day/night region on a Leaflet map */ + + function julian(date) { + /* Calculate the present UTC Julian Date. Function is valid after + * the beginning of the UNIX epoch 1970-01-01 and ignores leap + * seconds. */ + return (date / 86400000) + 2440587.5; + } + + function GMST(julianDay) { + /* Calculate Greenwich Mean Sidereal Time according to + http://aa.usno.navy.mil/faq/docs/GAST.php */ + var d = julianDay - 2451545.0; + // Low precision equation is good enough for our purposes. + return (18.697374558 + 24.06570982441908 * d) % 24; + } + + var Terminator = L.Polygon.extend({ + options: { + color: '#00', + opacity: 0.5, + fillColor: '#00', + fillOpacity: 0.5, + resolution: 2, + longitudeRange: 720 + }, + + initialize: function (options) { + this.version = '0.1.0'; + this._R2D = 180 / Math.PI; + this._D2R = Math.PI / 180; + L.Util.setOptions(this, options); + var latLng = this._compute(this.options.time); + this.setLatLngs(latLng); + }, + + setTime: function (date) { + this.options.time = date; + var latLng = this._compute(date); + this.setLatLngs(latLng); + }, + + _sunEclipticPosition: function (julianDay) { + /* Compute the position of the Sun in ecliptic coordinates at + julianDay. Following + http://en.wikipedia.org/wiki/Position_of_the_Sun */ + // Days since start of J2000.0 + var n = julianDay - 2451545.0; + // mean longitude of the Sun + var L$$1 = 280.460 + 0.9856474 * n; + L$$1 %= 360; + // mean anomaly of the Sun + var g = 357.528 + 0.9856003 * n; + g %= 360; + // ecliptic longitude of Sun + var lambda = L$$1 + 1.915 * Math.sin(g * this._D2R) + + 0.02 * Math.sin(2 * g * this._D2R); + // distance from Sun in AU + var R = 1.00014 - 0.01671 * Math.cos(g * this._D2R) - + 0.0014 * Math.cos(2 * g * this._D2R); + return {lambda: lambda, R: R}; + }, + + _eclipticObliquity: function (julianDay) { + // Following the short term expression in + // http://en.wikipedia.org/wiki/Axial_tilt#Obliquity_of_the_ecliptic_.28Earth.27s_axial_tilt.29 + var n = julianDay - 2451545.0; + // Julian centuries since J2000.0 + var T = n / 36525; + var epsilon = 23.43929111 - + T * (46.836769 / 3600 + - T * (0.0001831 / 3600 + + T * (0.00200340 / 3600 + - T * (0.576e-6 / 3600 + - T * 4.34e-8 / 3600)))); + return epsilon; + }, + + _sunEquatorialPosition: function (sunEclLng, eclObliq) { + /* Compute the Sun's equatorial position from its ecliptic + * position. Inputs are expected in degrees. Outputs are in + * degrees as well. */ + var alpha = Math.atan(Math.cos(eclObliq * this._D2R) + * Math.tan(sunEclLng * this._D2R)) * this._R2D; + var delta = Math.asin(Math.sin(eclObliq * this._D2R) + * Math.sin(sunEclLng * this._D2R)) * this._R2D; + + var lQuadrant = Math.floor(sunEclLng / 90) * 90; + var raQuadrant = Math.floor(alpha / 90) * 90; + alpha = alpha + (lQuadrant - raQuadrant); + + return {alpha: alpha, delta: delta}; + }, + + _hourAngle: function (lng, sunPos, gst) { + /* Compute the hour angle of the sun for a longitude on + * Earth. Return the hour angle in degrees. */ + var lst = gst + lng / 15; + return lst * 15 - sunPos.alpha; + }, + + _latitude: function (ha, sunPos) { + /* For a given hour angle and sun position, compute the + * latitude of the terminator in degrees. */ + var lat = Math.atan(-Math.cos(ha * this._D2R) / + Math.tan(sunPos.delta * this._D2R)) * this._R2D; + return lat; + }, + + _compute: function (time) { + var today = time ? new Date(time) : new Date(); + var julianDay = julian(today); + var gst = GMST(julianDay); + var latLng = []; + + var sunEclPos = this._sunEclipticPosition(julianDay); + var eclObliq = this._eclipticObliquity(julianDay); + var sunEqPos = this._sunEquatorialPosition(sunEclPos.lambda, eclObliq); + for (var i = 0; i <= this.options.longitudeRange * this.options.resolution; i++) { + var lng = -this.options.longitudeRange/2 + i / this.options.resolution; + var ha = this._hourAngle(lng, sunEqPos, gst); + latLng[i + 1] = [this._latitude(ha, sunEqPos), lng]; + } + if (sunEqPos.delta < 0) { + latLng[0] = [90, -this.options.longitudeRange/2]; + latLng[latLng.length] = [90, this.options.longitudeRange/2]; + } else { + latLng[0] = [-90, -this.options.longitudeRange/2]; + latLng[latLng.length] = [-90, this.options.longitudeRange/2]; + } + return latLng; + } + }); + + function terminator(options) { + return new Terminator(options); + } + + return terminator; + +}))); diff --git a/src/static/tud-lr.webp b/src/static/tud-lr.webp index 1ba7fad6..810d73ac 100644 Binary files a/src/static/tud-lr.webp and b/src/static/tud-lr.webp differ diff --git a/src/transmission/forms/forms.py b/src/transmission/forms/forms.py index 809f75eb..bb8a599f 100644 --- a/src/transmission/forms/forms.py +++ b/src/transmission/forms/forms.py @@ -12,6 +12,7 @@ jobs = [ (None, '-'), ('buffer_processing', 'Frame Buffer Processing'), + ('buffer_reprocessing', 'Frame Buffer Reprocessing'), ('scraper', 'Scrape'), ('raw_bucket_processing', 'Bucket Processing (new frames)'), ('reprocess_failed_raw_bucket', 'Bucket Reprocessing (failed frames)'), diff --git a/src/transmission/management/commands/initbuckets.py b/src/transmission/management/commands/initbuckets.py index 3e603ef7..5a981646 100644 --- a/src/transmission/management/commands/initbuckets.py +++ b/src/transmission/management/commands/initbuckets.py @@ -3,26 +3,18 @@ from django.core.management.base import BaseCommand from influxdb_client import BucketRetentionRules from transmission.processing.satellites import SATELLITES -from transmission.processing.influxdb_api import get_influxdb_bucket_api +from transmission.processing.influxdb_api import influxdb_api class Command(BaseCommand): """Django command class""" def handle(self, *args, **options): - """Create influxdb buckets for each satellite: - - 1 raw data bucket - - 1 bucket for uplink data - - 1 bucket for downlink data""" + """Initialize the database for each satellite, uplink and downlink""" - buckets_api = get_influxdb_bucket_api() - buckets = [] + db = influxdb_api() + + satellites = [] for sat in SATELLITES: - buckets.append(sat + "_raw_data") - buckets.append(sat + "_downlink") - buckets.append(sat + "_uplink") + satellites.append(sat) - for bucket in buckets: - retention_rules = BucketRetentionRules(type="expire", every_seconds=0) - if buckets_api.find_bucket_by_name(bucket) is None: - buckets_api.create_bucket(bucket_name=bucket, retention_rules=retention_rules) - print(f"Bucket: {bucket} created") + db.init_database(satellites) diff --git a/src/transmission/models.py b/src/transmission/models.py index c18bee61..a4f8d010 100644 --- a/src/transmission/models.py +++ b/src/transmission/models.py @@ -2,7 +2,6 @@ from django.db import models from django.db.models.deletion import DO_NOTHING from django.utils import timezone -from transmission.processing.telemetry_scraper import TIME_FORMAT class Satellite(models.Model): @@ -36,10 +35,10 @@ class Downlink(models.Model): def to_dictionary(self) -> dict: """Convert Downlink object to dict""" frame_dict = {} - frame_dict["timestamp"] = self.timestamp.strftime(TIME_FORMAT) - frame_dict["observer"] = self.observer + frame_dict["user"] = self.observer frame_dict["application"] = self.application frame_dict["processed"] = self.processed + frame_dict["invalid"] = self.invalid frame_dict["frequency"] = self.frequency frame_dict["frame"] = self.frame frame_dict["metadata"] = self.metadata @@ -61,12 +60,19 @@ class Uplink(models.Model): def to_dictionary(self) -> dict: """Convert Uplink object to dict""" frame_dict = {} - frame_dict["timestamp"] = self.timestamp.strftime(TIME_FORMAT) - frame_dict["operator"] = self.operator + frame_dict["user"] = self.operator frame_dict["application"] = self.application frame_dict["processed"] = self.processed + frame_dict["invalid"] = self.invalid frame_dict["frequency"] = self.frequency frame_dict["frame"] = self.frame frame_dict["metadata"] = self.metadata return frame_dict + +#class DateTimeMicrosecondsField(models.DateTimeField): +# def db_type(self, connection): +# return "timestamp(6) with time zone" +# +# #def rel_db_type(self, connection): +# # return "integer UNSIGNED" diff --git a/src/transmission/processing/XTCEParser.py b/src/transmission/processing/XTCEParser.py index 4b401b17..58795a47 100644 --- a/src/transmission/processing/XTCEParser.py +++ b/src/transmission/processing/XTCEParser.py @@ -9,13 +9,15 @@ class XTCEParser: gateway_started = 0 def __init__(self, XTCEfile, stream): - - if XTCEParser.gateway_started == 0: - launch_gateway(classpath='transmission/processing/xtcetools-1.1.5.jar', - port=25333, - die_on_exit=True) - XTCEParser.gateway_started = 1 - + #logger.info("XTCETools " + str(XTCEfile) + " " + str(XTCEParser.gateway_started)) + #if XTCEParser.gateway_started == 0: + # XTCEParser.gateway_started = 1 + # logger.info("Loading XTCETools") + # launch_gateway(classpath='transmission/processing/xtcetools-1.1.5.jar', + # port=25333, + # die_on_exit=True) + + logger.info("Loading XTCEParser") gateway = JavaGateway() XTCEContainerContentModel = gateway.jvm.org.xtce.toolkit.XTCEContainerContentModel @@ -29,27 +31,33 @@ def __init__(self, XTCEfile, stream): XTCEEngineeringType = gateway.jvm.org.xtce.toolkit.XTCETypedObject.EngineeringType File = gateway.jvm.java.io.File + self.XTCEfile_ = XTCEfile self.db_ = XTCEDatabase(File(XTCEfile), True, False, True) self.stream_ = self.db_.getStream(stream) + def loadGateway(): + logger.info("Loading py4j and XTCETools") + launch_gateway(classpath='transmission/processing/xtcetools-1.1.5.jar', + port=25333, + die_on_exit=True) + + def getFile(self): + return self.XTCEfile + def processTMFrame(self, data): try: model = self.stream_.processStream(data) entries = model.getContentList() telemetry = {"frame": model.getName()} - logger.debug("TMFrame entries: %s", entries) - + # throw an error if the frame is found but empty for entry in entries: val = entry.getValue() name = entry.getName() - logger.debug("TMFrame val: %s", val) - logger.debug("TMFrame name: %s", name) - if val: + if val and entry.getParameter().getAncillaryData("Ignore").isEmpty(): telemetry[name] = {"value": val.getCalibratedValue(), "status": self.isFieldValid(entry)} - logger.debug("TMFrame TLM: %s", telemetry) return telemetry except Py4JJavaError as ex: raise XTCEException(ex.java_exception) @@ -111,7 +119,7 @@ class SatParsers: def __init__(self): self.parsers = { "delfi_pq": XTCEParser("delfipq/Delfi-PQ.xml", "Radio"), - "delfi_next": None, + #"delfi_next": None, "delfi_c3": XTCEParser("delfic3/Delfi-C3.xml", "TLM"), - "da_vinci": None + #"da_vinci": None } diff --git a/src/transmission/processing/bookkeep_new_data_time_range.py b/src/transmission/processing/bookkeep_new_data_time_range.py deleted file mode 100644 index 514070f4..00000000 --- a/src/transmission/processing/bookkeep_new_data_time_range.py +++ /dev/null @@ -1,133 +0,0 @@ -"""Methods recording timestamps of newly added data, used for more targeted processing.""" -import os -from datetime import datetime, timedelta -import json -from transmission.processing.satellites import TIME_FORMAT - -TIME_RANGE_FILES_DIR = "transmission/processing/temp/" - - -def get_new_data_file_path(satellite: str, link: str) -> str: - """Return filepath of the new data time range file.""" - return TIME_RANGE_FILES_DIR + satellite + "/" + satellite + "_" + link + ".json" - - -def get_failed_data_file_path(satellite: str, link: str) -> str: - """Return filepath of the time range file storing the interval .""" - return TIME_RANGE_FILES_DIR + satellite + "/" + "failed" + "_" + link + ".json" - - -def get_new_data_scraper_temp_folder(satellite: str) -> str: - """Return filepath of the scraper process temp time range files.""" - return TIME_RANGE_FILES_DIR + satellite + "/scraper/" - - -def get_new_data_buffer_temp_folder(satellite: str) -> str: - """Return filepath of the buffer process temp time range files.""" - return TIME_RANGE_FILES_DIR + satellite + "/buffer/" - - -def read_time_range_file(input_file: str) -> dict: - """Read time range file and return contents as dictionary.""" - new_data_time_range = {} - with open(input_file, "r", encoding="utf-8") as file: - new_data_time_range = json.load(file) - - return new_data_time_range - - -def save_timestamps_to_file(timestamps: dict, input_file: str) -> None: - """Dump timestamps to the input file in json format.""" - with open(input_file, "w", encoding="utf-8") as file: - file.write(json.dumps(timestamps, indent=4)) - - -def reset_new_data_timestamps(satellite: str, link: str, input_file: str) -> None: - """Replace timestamps form the json given by the input file with [].""" - new_data_time_range = read_time_range_file(input_file) - new_data_time_range[satellite][link] = [] - - with open(input_file, "w", encoding="utf-8") as file: - file.write(json.dumps(new_data_time_range, indent=4)) - - -def include_timestamp_in_time_range(satellite: str, link: str, timestamp, - input_file: str = None, existing_range: dict = None) -> dict: - """This function ensures that a given timestamp will be included in the - time range such that it can then be processed and parsed from raw form. - The range can be maintained in memory given an existing_range or in file given an input_file.""" - - if isinstance(timestamp, str): - time = datetime.strptime(timestamp, TIME_FORMAT) - else: - time = timestamp - - start_time = (time - timedelta(seconds=1)).strftime(TIME_FORMAT) - end_time = (time + timedelta(seconds=1)).strftime(TIME_FORMAT) - - time_range = (start_time, end_time) - - return update_new_data_timestamps(satellite, link, time_range, input_file, existing_range) - - -def update_new_data_timestamps(satellite: str, link: str, new_time_range: tuple, - input_file: str = None, existing_range: dict = None) -> dict: - """Bookkeep time range of unprocessed telemetry. - If an input_file is specified, the timestamps from the file, will be updated and dumped. - If the existing_range is specified, it will be updated and returned as a dictionary. - If both input_file and existing_range are specified, an exception is raised.""" - - if input_file is not None and existing_range is not None: - raise RuntimeError("Specify either input_file or existing_range, not both.") - - start_time = new_time_range[0] - end_time = new_time_range[1] - - if input_file is not None: - new_data_time_range = read_time_range_file(input_file) - - elif existing_range in [{}, None]: - new_data_time_range = {} - new_data_time_range[satellite] = {} - new_data_time_range[satellite][link] = [] - else: - new_data_time_range = existing_range - - # No time range saved - if new_data_time_range[satellite][link] == []: - new_data_time_range[satellite][link] = [start_time, end_time] - # Update time range - else: - new_data_time_range[satellite][link][0] = min( - new_data_time_range[satellite][link][0], - start_time - ) - new_data_time_range[satellite][link][1] = max( - new_data_time_range[satellite][link][1], - end_time - ) - if input_file is not None: - with open(input_file, "w", encoding="utf-8") as file: - json.dump(new_data_time_range, file, indent=4) - - return new_data_time_range - - -def combine_time_ranges(satellite: str, link: str) -> None: - """Combine time ranges of new data from all processes (buffer processing and scraper).""" - scraper_folder = get_new_data_scraper_temp_folder(satellite) - buffer_folder = get_new_data_buffer_temp_folder(satellite) - - for folder in [scraper_folder, buffer_folder]: - - for temp_file in os.listdir(folder): - if link in temp_file: - new_data_time_range = read_time_range_file(folder + temp_file) - new_data_overview_file = get_new_data_file_path(satellite, link) - include_timestamp_in_time_range(satellite, link, - new_data_time_range[satellite][link][0], - input_file=new_data_overview_file) - include_timestamp_in_time_range(satellite, link, - new_data_time_range[satellite][link][1], - input_file=new_data_overview_file) - os.remove(folder + temp_file) diff --git a/src/transmission/processing/influxdb_api.py b/src/transmission/processing/influxdb_api.py index 154fd37f..0b6ea125 100644 --- a/src/transmission/processing/influxdb_api.py +++ b/src/transmission/processing/influxdb_api.py @@ -1,104 +1,212 @@ """Methods for saving raw data frames and retrieving the influxdb API.""" from datetime import datetime, timedelta -import os from influxdb_client import InfluxDBClient from influxdb_client.client.write_api import SYNCHRONOUS - -from transmission.processing.satellites import TIME_FORMAT +from influxdb_client.rest import ApiException +from influxdb_client.domain.bucket import Bucket +from django.conf import settings from django_logger import logger -INFLUXDB_URL = "http://influxdb:8086" -INFLUX_ORG = os.environ.get('INFLUXDB_V2_ORG', 'Delfi Space') - - -def get_influxdb_client(): - """Connect to influxdb and return the influxdb client.""" +RAW_MEASUREMENT = "_raw" +DATETIME_FORMAT_STRING = "%Y-%m-%dT%H:%M:%S.%fZ" - client = InfluxDBClient(url=INFLUXDB_URL, token=os.environ.get('INFLUXDB_V2_TOKEN', 'adminpwd'), org=INFLUX_ORG) +class influxdb_api: + """Class interfcing with InfluxDB""" - return client + def __init__(self): + self.url = settings.INFLUXDB['HOST'] + ":" + str(settings.INFLUXDB['PORT']) + self.token = settings.INFLUXDB['TOKEN'] + self.client = InfluxDBClient(url=self.url, token=self.token) + self.buckets_api = None + self.organizations_api = None + self.write_api = None + self.query_api = None -def get_influxdb_bucket_api(): - """Connect to influxdb and return bucket_api.""" + def _get_buckets_api(self): + if self.buckets_api is None: + self.buckets_api = self.client.buckets_api() + return self.buckets_api - client = get_influxdb_client() - buckets_api = client.buckets_api() + def _get_organizations_api(self): + if self.organizations_api is None: + self.organizations_api = self.client.organizations_api() + return self.organizations_api - return buckets_api + def _get_query_api(self): + if self.query_api is None: + self.query_api = self.client.query_api() + return self.query_api -def get_influx_db_read_and_query_api() -> tuple: - """Connect to influxdb and return write_api and query_api.""" - client = get_influxdb_client() + def _get_write_api(self): + if self.write_api is None: + self.write_api = self.client.write_api(write_options=SYNCHRONOUS) + return self.write_api - write_api = client.write_api(write_options=SYNCHRONOUS) - query_api = client.query_api() - return (write_api, query_api) + def init_database(self, satellites): + """Initialize the database.""" + try: + orgs = self._get_organizations_api().find_organizations(org="downlink") + except ApiException: + self._get_organizations_api().create_organization('downlink') -def write_frame_to_raw_bucket(write_api, satellite, link, timestamp, frame_fields) -> None: - """Save frame given its fields. Note: to update/overwrite a field write the frame again - with the changed fields and the same timestamp as before.""" + try: + orgs = self._get_organizations_api().find_organizations(org="uplink") + except ApiException: + self._get_organizations_api().create_organization('uplink') - tags = {} + for sat in satellites: + try: + self._get_buckets_api().find_buckets(name=sat, org="downlink") + except ApiException: + self._get_buckets_api().create_bucket(bucket_name=sat, org="downlink") - bucket = satellite + "_raw_data" + try: + self._get_buckets_api().find_buckets(name=sat, org="uplink") + except ApiException: + self._get_buckets_api().create_bucket(bucket_name=sat, org="uplink") - db_fields = { - "measurement": satellite + "_" + link + "_raw_data", - "time": timestamp, - "tags": tags, - "fields": frame_fields - } + def get_last_received_frame(self, satellite: str): + """Retrieve the last received frame for the specified satellite from the raw + data bucket.""" + + query = f'''from(bucket: "{satellite}") + |> range(start: 0) + |> filter(fn: (r) => r["_measurement"] == "{RAW_MEASUREMENT}") + |> filter(fn: (r) => r["_field"] == "processed") + |> group() + |> last() + ''' - logger.info("%s: raw frame stored or updated. Frame timestamp: %s, link: %s, bucket: %s", - satellite, timestamp, link, bucket) + ret = self._get_query_api().query(query=query, org="downlink") - write_api.write(bucket, INFLUX_ORG, db_fields) + if len(ret) > 0: + # data received found in bucket + return ret[0].records[0]["_time"] + + # no result found + return None -def commit_frame(write_api, query_api, satellite: str, link: str, tlm: dict) -> bool: - """Write frame to corresponding satellite table (if not already stored). - Returns True if the frame was stored and False otherwise (if the frame is already stored). - Also store the frame with processed = False.""" + def get_raw_frames_to_process(self, satellite: str, link: str, frames: int = 100): + """Retrieve the first raw frames to process""" + + get_frames_to_process = f''' + from(bucket: "{satellite}") + |> range(start: 0, stop: now()) + |> filter(fn: (r) => r._measurement == "{RAW_MEASUREMENT}") + |> filter(fn: (r) => r["_field"] == "processed" or + r["_field"] == "frame" or + r["_field"] == "user") + |> pivot(rowKey:["_time"], columnKey: ["_field"], valueColumn: "_value") + |> filter(fn: (r) => r[\"processed\"] == false) + |> sort(columns: ["_time"], desc: false) + |> limit(n:{frames}, offset: 0)''' + + # query result as dataframe + dataframe = self._get_query_api().query_data_frame(query=get_frames_to_process, org=link) + return dataframe + + + def save_raw_frame(self, satellite: str, link: str, timestamp: datetime, tlm: dict) -> bool: + """Write frame to corresponding satellite table (if not already stored). + Returns True if the frame was stored and False otherwise (if the frame is already stored). + Also store the frame with processed = False.""" + + time_range_start = (timestamp - timedelta(seconds=0.1)).strftime(DATETIME_FORMAT_STRING) + time_range_stop = (timestamp + timedelta(seconds=0.1)).strftime(DATETIME_FORMAT_STRING) + + # check if frame already exists + query = f'''from(bucket: "{satellite}") + |> range(start: {time_range_start}, stop: {time_range_stop}) + |> filter(fn: (r) => r._measurement == "{RAW_MEASUREMENT}") + |> filter(fn: (r) => r["_field"] == "frame" and r["_value"] == "{tlm["frame"]}") + ''' + + # store frame only if not stored already + if len(self._get_query_api().query(query=query, org=link)) != 0: + return False + + tlm["processed"] = False + tlm["invalid"] = False + + db_fields = { + "measurement": RAW_MEASUREMENT, + "time": timestamp, + "tags": {"user": tlm.pop("user"), "application": tlm.pop("application")}, + "fields": tlm + } + + self._get_write_api().write(satellite, link, db_fields) + + return True + + + def save_processed_frame(self, satellite: str, link: str, measurement: str, timestamp, tags, fields): + """Write frame to corresponding satellite table (if not already stored). + Returns True if the frame was stored and False otherwise (if the frame is already stored). + Also store the frame with processed = False.""" + + #time_range_lower_bound = (timestamp - timedelta(seconds=1)).strftime(DATETIME_FORMAT_STRING) + #time_range_upper_bound = (timestamp + timedelta(seconds=1)).strftime(DATETIME_FORMAT_STRING) + + # check if frame already exists + #query = f'''from(bucket: "{satellite}") + # |> range(start: {time_range_lower_bound}, stop: {time_range_upper_bound}) + # |> filter(fn: (r) => r._measurement == "raw") + # |> filter(fn: (r) => r["_field"] == "frame" and r["_value"] == "{tlm["frame"]}") + # ''' + # store frame only if not stored already + #if len(self._get_query_api().query(query=query)) != 0: + # logger.info("Found!") + # return False + + #tlm["processed"] = False + #tlm["invalid"] = False + + db_fields = { + "measurement": measurement, + "time": timestamp, + "tags": tags, + "fields": fields + } + + self._get_write_api().write(satellite, link, db_fields) + + + def update_raw_frame(self, satellite: str, link: str, timestamp: datetime, tlm: dict) -> bool: + """Updates frame in corresponding satellite table (if not already stored). + Returns True if the frame was updated and False otherwise (if the frame was not found). + """ + time_range_start = (timestamp - timedelta(seconds=0.1)).strftime(DATETIME_FORMAT_STRING) + time_range_stop = (timestamp + timedelta(seconds=0.1)).strftime(DATETIME_FORMAT_STRING) + + # check if frame already exists + query = f'''from(bucket: "{satellite}") + |> range(start: {time_range_start}, stop: {time_range_stop}) + |> filter(fn: (r) => r._measurement == "{RAW_MEASUREMENT}") + |> pivot(rowKey:["_time"], columnKey: ["_field"], valueColumn: "_value") + ''' + + # update frame only if already present + frames = self._get_query_api().query(query=query, org=link) + if len(frames) != 0: + frame = frames[0].records[0] + bucket = satellite + + db_fields = { + "measurement": RAW_MEASUREMENT, + "time": timestamp, + "tags": {"user": frame["user"], "application": frame["application"]}, + "fields": tlm + } + + self._get_write_api().write(bucket, link, db_fields) + return True - bucket = satellite + "_raw_data" - tlm_time = datetime.strptime(tlm['timestamp'], TIME_FORMAT) - - time_range_lower_bound = (tlm_time - timedelta(seconds=1)).strftime(TIME_FORMAT) - time_range_upper_bound = (tlm_time + timedelta(seconds=1)).strftime(TIME_FORMAT) - - # check if frame already exists - query = f'''from(bucket: "{bucket}") - |> range(start: {time_range_lower_bound}, stop: {time_range_upper_bound}) - |> filter(fn: (r) => r._measurement == "{satellite + "_" + link + "_raw_data"}") - |> filter(fn: (r) => r["_field"] == "frame" and r["_value"] == "{tlm["frame"]}") - ''' - # store frame only if not stored already - if len(query_api.query(query=query)) != 0: return False - - tlm["processed"] = False - write_frame_to_raw_bucket(write_api, satellite, link, tlm["timestamp"], tlm) - return True - - -def save_raw_frame_to_influxdb(satellite: str, link: str, telemetry) -> bool: - """Connect to influxdb and save raw telemetry. - Return True if telemetry was stored, False otherwise.""" - - write_api, query_api = get_influx_db_read_and_query_api() - - stored = False - - if isinstance(telemetry, list): - for tlm in telemetry: - stored = stored or commit_frame(write_api, query_api, satellite, link, tlm) - elif isinstance(telemetry, dict): - stored = stored or commit_frame(write_api, query_api, satellite, link, telemetry) - - return stored diff --git a/src/transmission/processing/process_raw_bucket.py b/src/transmission/processing/process_raw_bucket.py index b2b263ae..1f78c1ed 100644 --- a/src/transmission/processing/process_raw_bucket.py +++ b/src/transmission/processing/process_raw_bucket.py @@ -1,52 +1,22 @@ """Script to store satellite telemetry frames""" import string -from transmission.processing import XTCEParser as xtce_parser +import time +import traceback +from transmission.processing.XTCEParser import SatParsers, XTCEException +from transmission.processing.influxdb_api import influxdb_api from django_logger import logger -import transmission.processing.bookkeep_new_data_time_range as time_range -from transmission.processing.influxdb_api import INFLUX_ORG, commit_frame, \ - get_influx_db_read_and_query_api, write_frame_to_raw_bucket -write_api, query_api = get_influx_db_read_and_query_api() - -def store_raw_frame(satellite: str, timestamp: str, frame: str, observer: str, link: str) -> bool: - """Store raw unprocessed frame in influxdb""" - frame_fields = { - "frame": frame, - "observer": observer, - "timestamp": timestamp, - "processed": False - } - - stored = commit_frame(write_api, query_api, satellite, link, frame_fields) - if stored: - file = time_range.get_new_data_file_path(satellite, link) - time_range.include_timestamp_in_time_range(satellite, link, timestamp, file) - return stored - - -def parse_and_store_frame(satellite: str, timestamp: str, frame: str, observer: str, link: str) -> None: +def parse_and_store_frame(parsers: SatParsers, db: influxdb_api, satellite: str, timestamp: str, frame: str, + link: str) -> None: """Store parsed frame in influxdb""" - parser = xtce_parser.SatParsers().parsers[satellite] - logger.debug("%s: frame: %s", satellite, frame) + parser = parsers.parsers[satellite] + telemetry = parser.processTMFrame(bytes.fromhex(frame)) bucket = satellite + "_" + link if "frame" in telemetry: - sat_name_pascal_case = string.capwords(satellite.replace("_", " ")).replace(" ", "") - tags = {} - - db_fields = { - - "measurement": sat_name_pascal_case + telemetry["frame"], - "time": timestamp, - "tags": tags, - "fields": { - "observer": observer, - } - } - for field, value_and_status in telemetry.items(): # skip frame field if field == "frame": @@ -60,137 +30,79 @@ def parse_and_store_frame(satellite: str, timestamp: str, frame: str, observer: except ValueError: pass - # print(field + " " + str(value) + " " + status) - logger.debug("%s: field: %s, val: %s, status: %s", satellite, field, str(value), status) - - db_fields["fields"][field] = value - db_fields["tags"]["status"] = status - - write_api.write(bucket, INFLUX_ORG, db_fields) - # print(db_fields) - db_fields["fields"] = {} - db_fields["tags"] = {} - - logger.info("%s: processed frame stored. Frame timestamp: %s, link: %s, bucket: %s", - satellite, timestamp, link, bucket) + db.save_processed_frame(satellite, link, telemetry["frame"], timestamp, {"status": status}, {field: value}) -def mark_processed_flag(satellite: str, link: str, timestamp: str, value: bool) -> None: - """Write the processed flag to either True or False.""" - write_frame_to_raw_bucket(write_api, satellite, link, timestamp, {'processed': value}) - - -# pylint: disable=R0914 -def process_retrieved_frames(satellite: str, link: str, start_time: str, end_time: str, - skip_processed: bool = True) -> tuple: +def process_retrieved_frames(parsers: SatParsers, db: influxdb_api, satellite: str, link: str) -> int: """Parse frames, store the parsed form and mark the raw entry as processed. Return the total number of frames attempting to process and how many frames were successfully processed. Skip_processed=True will skip over the already processed frames.""" - radio_amateur = 'observer' - if link == 'uplink': - radio_amateur = 'operator' - - get_unprocessed_frames_query = f''' - from(bucket: "{satellite + "_raw_data"}") - |> range(start: {start_time}, stop: {end_time}) - |> filter(fn: (r) => r._measurement == "{satellite + "_" + link + "_raw_data"}") - |> filter(fn: (r) => r["_field"] == "processed" or - r["_field"] == "frame" or - r["_field"] == "{radio_amateur}") - |> pivot(rowKey:["_time"], columnKey: ["_field"], valueColumn: "_value") - ''' - # query result as dataframe - dataframe = query_api.query_data_frame(query=get_unprocessed_frames_query) - dataframe = dataframe.reset_index() - - failed_processing_count = 0 + frames_list = db.get_raw_frames_to_process(satellite, link) + processed_frames_count = 0 - total_frames_count = 0 + # process each frame - for _, row in dataframe.iterrows(): - total_frames_count += 1 + for _, row in frames_list.iterrows(): try: - if row["processed"] and skip_processed: # skip frame if it's processed - logger.info("%s: frame skipped (already processed): %s ", satellite, row["frame"]) - continue # store processed frame - parse_and_store_frame(satellite, row["_time"], row["frame"], row[radio_amateur], link) - # mark raw frame as processed - mark_processed_flag(satellite, link, row["_time"], True) + parse_and_store_frame(parsers, db, satellite, row["_time"], row["frame"], link) + # mark raw frame as processed and valid + db.update_raw_frame(satellite, link, row["_time"], {'processed': True, 'invalid': False}) processed_frames_count += 1 - except xtce_parser.XTCEException as ex: + except XTCEException as ex: logger.error("%s: frame processing error: %s (%s)", satellite, ex, row["frame"]) - time_range.include_timestamp_in_time_range(satellite, - link, - row["_time"], - time_range.get_failed_data_file_path(satellite, link) - ) - # mark frame as unprocessed - mark_processed_flag(satellite, link, row["_time"], False) - failed_processing_count += 1 - - skipped_frames_count = total_frames_count - processed_frames_count - failed_processing_count + logger.error(traceback.format_exc()) + # mark raw frame as processed and invalid + db.update_raw_frame(satellite, link, row["_time"], {'processed': True, 'invalid': True}) - frames_status = f"out of {total_frames_count} frames: " + \ - f"{processed_frames_count} were successfully parsed, " + \ - f"{skipped_frames_count} were skipped, and " + \ - f"{failed_processing_count} failed." + # indeed a very broad exception, but it keeps the processor running in case of rogue frames + except Exception as ex: # pylint: disable=broad-except + logger.error("%s: frame storage error: %s (%s)", satellite, ex, row["frame"]) + logger.error(traceback.format_exc()) + logger.info("Problematic frame: " + str(row)) + logger.info(row) + # mark raw frame as processed and invalid + db.update_raw_frame(satellite, link, row["_time"], {'processed': True, 'invalid': True}) - logger.info("%s: %s frames processed from %s - %s; %s", satellite, link, start_time, end_time, frames_status) - - if total_frames_count == 0: - logger.info("%s: no frames to process", satellite) - - return processed_frames_count, total_frames_count + return processed_frames_count def process_raw_bucket(satellite: str, link: str = None, all_frames: bool = False, failed: bool = False): - """Trigger bucket processing or reprocessing given satellite.""" + """Bucket processing or reprocessing task.""" # if link is None process both uplink and downlink, otherwise process only specified link - if link in ["uplink", "downlink"]: - _process_raw_bucket(satellite, link, all_frames, failed) - else: - _process_raw_bucket(satellite, "uplink", all_frames, failed) - _process_raw_bucket(satellite, "downlink", all_frames, failed) - + total_processed_frames = 0 + iterations = 0 + parsers = SatParsers() + db = influxdb_api() -def _process_raw_bucket(satellite: str, link: str, all_frames: bool, failed: bool) -> tuple: - """Trigger bucket processing given satellite and link. - all_frames=True will process the entire bucket and failed=True will process only failed frames. - When both flags are True all frames will be processed.""" + # TODO handle reprocessing failed frames - time_range.combine_time_ranges(satellite, link) + # once the last frame has been processed, maintain the task active for + # at least 10 seconds while looking for more frames to process + while iterations < 50: + total_processed_frames = 0 - # process the entire bucket - if all_frames: - return process_retrieved_frames(satellite, link, "0", "now()", skip_processed=False) - # process frames in the failed frames time range - if failed: - file = time_range.get_failed_data_file_path(satellite, link) - new_data_time_range = time_range.read_time_range_file(file) - # process frames in the new data time range (newly ingested data) - else: - file = time_range.get_new_data_file_path(satellite, link) - new_data_time_range = time_range.read_time_range_file(file) - - processed_frames_count = 0 - total_frames_count = 0 + if link in ["uplink", "downlink"]: + processed_frames_count = process_retrieved_frames(parsers, db, satellite, link) + total_processed_frames += processed_frames_count + else: + processed_frames_count = process_retrieved_frames(parsers, db, satellite, "uplink") + total_processed_frames += processed_frames_count + processed_frames_count = process_retrieved_frames(parsers, db, satellite, "downlink") + total_processed_frames += processed_frames_count - # if the time range is empty there are no frames to process - if new_data_time_range[satellite][link] != []: + # one more iteration + iterations += 1 - start_time = new_data_time_range[satellite][link][0] - end_time = new_data_time_range[satellite][link][1] - processed_frames_count, total_frames_count = process_retrieved_frames(satellite, link, start_time, end_time) + if total_processed_frames != 0: + # frames were processed in this iteration, reset the iteration counter + iterations = 0 + logger.info("Processed " + str(total_processed_frames) + " frames") - # don't reset the interval of failed frames, unless reprocessing was successful - if failed is False or processed_frames_count == total_frames_count: - time_range.reset_new_data_timestamps(satellite, link, file) - else: - logger.info("%s: no frames to process", satellite) - return processed_frames_count, total_frames_count + # maintain the thread alive and re-check if new frames have been received + time.sleep(0.2) diff --git a/src/transmission/processing/satellites.py b/src/transmission/processing/satellites.py index 9cd2046c..89e5606d 100644 --- a/src/transmission/processing/satellites.py +++ b/src/transmission/processing/satellites.py @@ -6,17 +6,21 @@ "delfi_pq": { "norad_id": '51074', "status": "Decayed", + "launch": "2022-01-13T18:00:00.000Z", }, "delfi_next": { "norad_id": '39428', "status": "Non Operational", + "launch": "2013-11-21T18:00:00.000Z", }, "delfi_c3": { "norad_id": '32789', "status": "Decayed", + "launch": "2008-04-28T18:00:00.000Z", }, "da_vinci": { "norad_id": None, #update id - "status": "Under Development" + "status": "Under Development", + "launch": None, } } diff --git a/src/transmission/processing/save_raw_data.py b/src/transmission/processing/save_raw_data.py index d2067878..3bd93568 100644 --- a/src/transmission/processing/save_raw_data.py +++ b/src/transmission/processing/save_raw_data.py @@ -1,26 +1,20 @@ """Scripts for saving the frames into the database""" -import os import re import copy import json from typing import Union - +import time +from datetime import datetime, timezone from django.forms import ValidationError from django.core.exceptions import PermissionDenied from django.db import models from django.db.models.query import QuerySet from django.utils.dateparse import parse_datetime from skyfield.api import load, EarthSatellite -import pytz -from django_logger import logger from members.models import Member from transmission.models import Uplink, Downlink, TLE, Satellite from transmission.processing.XTCEParser import SatParsers, XTCEException -from transmission.processing.bookkeep_new_data_time_range import get_new_data_buffer_temp_folder, \ - include_timestamp_in_time_range, save_timestamps_to_file -from transmission.processing.influxdb_api import save_raw_frame_to_influxdb -from transmission.processing.telemetry_scraper import strip_tlm - +from transmission.processing.influxdb_api import influxdb_api def store_frames(frames, username: str, application: str = None) -> int: """Store frames in batches if the input is a list. @@ -64,13 +58,25 @@ def build_frame_model_object(frame: dict, username: str, application: str = None if not user.has_perm("transmission.add_uplink"): raise PermissionDenied() frame_entry = Uplink() - frame_entry.operator = user + if "username" not in frame: + frame_entry.operator = user + else: + if user.is_staff: + frame_entry.operator = frame["username"] + else: + raise PermissionDenied() elif frame["link"] == "downlink": if not user.has_perm("transmission.add_downlink"): raise PermissionDenied() frame_entry = Downlink() - frame_entry.observer = user.UUID + if "username" not in frame: + frame_entry.observer = user + else: + if user.is_staff: + frame_entry.observer = frame["username"] + else: + raise PermissionDenied() else: raise ValidationError("Invalid frame link.") @@ -102,7 +108,7 @@ def parse_submitted_frame(frame: dict, frame_entry: models.Model) -> models.Mode # assign the frame HEX values frame_entry.frame = frame['frame'] # assign the timestamp - frame_entry.timestamp = parse_datetime(frame["timestamp"]).astimezone(pytz.utc) + frame_entry.timestamp = parse_datetime(frame["timestamp"]).astimezone(timezone.utc) # assign frequency, if present if "frequency" in frame and frame["frequency"] is not None: frame_entry.frequency = frame["frequency"] @@ -110,7 +116,7 @@ def parse_submitted_frame(frame: dict, frame_entry: models.Model) -> models.Mode # add metadata metadata = copy.deepcopy(frame) # remove previously parsed fields - for field in ["frame", "timestamp", "frequency"]: + for field in ["frame", "timestamp", "frequency", "link", "username"]: if field in metadata: del metadata[field] @@ -119,85 +125,104 @@ def parse_submitted_frame(frame: dict, frame_entry: models.Model) -> models.Mode return frame_entry -def process_uplink_and_downlink() -> tuple: - """Process all unprocessed uplink and downlink frames, - i.e. move them to the influxdb raw satellite data bucket.""" +def process_uplink_and_downlink(invalid_frames: bool = False, callback = None): + """Process all processed uplink and downlink frames, + i.e. move them to the influxdb raw satellite data bucket. + if invalid_frames is false, only new frames are processed. If + invalid_frames is true, only frames already marked as invalid + will be processed.""" + + iterations = 0 + + parsers = SatParsers() + db = influxdb_api() + + # list the satellites whose frames have been processed: + # ths allows to later start their processing task + satellites_list = [] + + # once the last frame has been processed, maintain the task active for + # at least 10 seconds while looking for more frames to process + while iterations < 50: + total_processed_frames = 0 + + if not invalid_frames: + # set a maximum length to the results to ensure responsive data processing + downlink_frames = Downlink.objects.all().filter(processed=False).order_by("timestamp")[:100] + uplink_frames = Uplink.objects.all().filter(processed=False).order_by("timestamp")[:100] + else: + # set a maximum length to the results to ensure responsive data processing + downlink_frames = Downlink.objects.all().filter(processed=True).filter(invalid=True).order_by("timestamp")[:100] + uplink_frames = Uplink.objects.all().filter(processed=True).filter(invalid=True).order_by("timestamp")[:100] - downlink_frames = Downlink.objects.all().filter(processed=False) - process_frames(downlink_frames, "downlink") + downlink_frames_count = process_frames(parsers, db, downlink_frames, "downlink", satellites_list) + uplink_frames_count = process_frames(parsers, db, uplink_frames, "uplink", satellites_list) + total_processed_frames = downlink_frames_count + uplink_frames_count - uplink_frames = Uplink.objects.all().filter(processed=False) - process_frames(uplink_frames, "uplink") + # if the satellites list is not empty and the scheduler callback is not None + if satellites_list and callback is not None: + # report the satellites that have been sending frames + callback(satellites_list) + # empty the list + satellites_list = [] - return len(downlink_frames), len(uplink_frames) + # one more iteration + iterations += 1 + if total_processed_frames != 0: + # frames were processed in this iteration, reset the iteration counter + iterations = 0 -def process_frames(frames: QuerySet, link: str) -> int: + # maintain the thread alive and re-check if new frames have been received + time.sleep(0.2) + + +def process_frames(parsers: SatParsers, db: influxdb_api, frames: QuerySet, link: str, satellites_list: list) -> int: """Try to store frame to influxdb and set the processed flag to True if a frame was successfully stored in influxdb. Returns the count of successfully processed_frames.""" processed_frames = 0 - processed_frames_timestamps = {} + for frame_obj in frames: frame_dict = frame_obj.to_dictionary() - stored, satellite = store_frame_to_influxdb(frame_dict, link) + stored, satellite = store_frame_to_influxdb(parsers, db, frame_obj.timestamp, frame_dict, link) + frame_obj.processed = True + if stored: - frame_obj.processed = True + # valid frame frame_obj.invalid = False - frame_obj.save() processed_frames += 1 - if satellite not in processed_frames_timestamps: - processed_frames_timestamps[satellite] = include_timestamp_in_time_range( - satellite, link, - frame_obj.timestamp, - existing_range={} - ) - else: - processed_frames_timestamps[satellite] = include_timestamp_in_time_range( - satellite, link, - frame_obj.timestamp, - existing_range=processed_frames_timestamps[satellite] - ) - for satellite, time_range in processed_frames_timestamps.items(): - path = get_new_data_buffer_temp_folder(satellite) - path += satellite + "_" + link + "_" + str(len(os.listdir(path))) + ".json" - save_timestamps_to_file(time_range, path) - return processed_frames + # satellite found, add it to the processing list + if satellite not in satellites_list: + satellites_list.append(satellite) + else: + frame_obj.invalid = True + frame_obj.save() -def mark_frame_as_invalid(frame: str, link: str) -> None: - """Flag an invalid frame that doesn't correspond to any satellite.""" - if link == "downlink": - Downlink.objects.filter(frame=frame).update(invalid=True) - elif link == "uplink": - Uplink.objects.filter(frame=frame).update(invalid=True) + return processed_frames -def store_frame_to_influxdb(frame: dict, link: str) -> tuple: +def store_frame_to_influxdb(parsers: SatParsers, db: influxdb_api, timestamp: datetime, frame: dict, link: str) -> tuple: """Try to store frame to influxdb. Returns True if the frame was successfully stored, False otherwise.""" - satellite = get_satellite_from_frame(frame["frame"]) + satellite = get_satellite_from_frame(parsers, frame["frame"]) if satellite is None: - mark_frame_as_invalid(frame["frame"], link) - logger.warning("invalid %s frame, cannot match satellite: %s", link, frame["frame"]) return False, satellite - fields_to_save = ["frame", "timestamp", "observer", "frequency", "application", "metadata"] - - frame = strip_tlm(frame, fields_to_save) - stored = save_raw_frame_to_influxdb(satellite, link, frame) + stored = db.save_raw_frame(satellite, link, timestamp, frame) return stored, satellite -def get_satellite_from_frame(frame: str) -> Union[str, None]: +def get_satellite_from_frame(parsers: SatParsers, frame: str) -> Union[str, None]: """Find the corresponding satellite by attempting to parse the frame. If the parsing is successful, return the satellite name, else None.""" - for sat, parser in SatParsers().parsers.items(): + for sat, parser in parsers.parsers.items(): if parser is not None: try: parser.processTMFrame(bytes.fromhex(frame)) diff --git a/src/transmission/processing/telemetry_scraper.py b/src/transmission/processing/telemetry_scraper.py index a921cfb1..44cb19ae 100644 --- a/src/transmission/processing/telemetry_scraper.py +++ b/src/transmission/processing/telemetry_scraper.py @@ -8,9 +8,7 @@ from django_logger import logger from transmission.processing.satellites import SATELLITES, TIME_FORMAT -from transmission.processing.bookkeep_new_data_time_range import get_new_data_scraper_temp_folder, \ - include_timestamp_in_time_range, save_timestamps_to_file -from transmission.processing.influxdb_api import save_raw_frame_to_influxdb +#from transmission.processing.influxdb_api import save_raw_frame_to_influxdb SATNOGS_PATH = "https://db.satnogs.org/api/telemetry/" SATNOGS_TOKEN_PATH = "tokens/satnogs_token.txt" @@ -34,7 +32,7 @@ def get_satnogs_params(satellite: str) -> dict: now = datetime.utcnow().strftime(TIME_FORMAT) logger.debug("Now: %s", now) # params = {'app_source':'network', 'end': now, 'format': 'json', 'satellite': '51074'} - params = {'end': now, 'format': 'json', 'satellite': SATELLITES[satellite]} + params = {'end': now, 'format': 'json', 'satellite': SATELLITES[satellite]['norad_id']} return params @@ -68,55 +66,53 @@ def scrape(satellite: str, save_to_db=True, save_to_file=False) -> None: telemetry = [] telemetry_tmp = [] logger.info("SatNOGS scraper started. Scraping %s telemetry.", satellite) - time_range = {} while True: + logger.info(get_satnogs_params(satellite)) response = requests.get( SATNOGS_PATH, params=get_satnogs_params(satellite), headers=get_satnogs_headers(), - timeout=10 # seconds + timeout=100 # seconds ) + logger.info(response) telemetry_tmp = response.json() + logger.info(telemetry_tmp) try: + logger.info("1") last = telemetry_tmp[-1] + logger.info(last) first = telemetry_tmp[0] + logger.info(first) # concatenate telemetry telemetry = telemetry + telemetry_tmp last_time = datetime.strptime(last['timestamp'], TIME_FORMAT) next_time = last_time - timedelta(seconds=1) - logger.debug("Next: %s", next_time.strftime(TIME_FORMAT)) + logger.info("Next: %s", next_time.strftime(TIME_FORMAT)) - if save_to_db: - fields_to_save = ["frame", "timestamp", "observer"] - stripped_tlm = strip_tlm_list(telemetry_tmp, fields_to_save) - if save_raw_frame_to_influxdb(satellite, "downlink", stripped_tlm): - time_range = include_timestamp_in_time_range(satellite, 'downlink', - first["timestamp"], existing_range=time_range) - time_range = include_timestamp_in_time_range(satellite, 'downlink', - last["timestamp"], existing_range=time_range) + #if save_to_db: + #fields_to_save = ["frame", "timestamp", "observer"] + #stripped_tlm = strip_tlm_list(telemetry_tmp, fields_to_save) + #save_raw_frame_to_influxdb(satellite, "downlink", stripped_tlm) # if the frame is not stored (due to it being stored in a past scrape) and # the next request retrieves data older than a week -> stop - elif (datetime.now() - next_time).days > 7: - logger.info("SatNOGS scraper stopped. Done scraping %s telemetry.", satellite) - break # stop scraping + #elif (datetime.now() - next_time).days > 7: + # logger.info("SatNOGS scraper stopped. Done scraping %s telemetry.", satellite) + # break # stop scraping except IndexError: logger.info("SatNOGS scraper stopped. Done scraping %s telemetry.", satellite) break except KeyError: - logger.debug(telemetry_tmp) + logger.info(telemetry_tmp) if 'detail' in telemetry_tmp: if "throttled" in telemetry_tmp["detail"]: delay = re.findall('[0-9]+', telemetry_tmp["detail"])[0] - logger.debug("Sleeping %s s (request throttled)", delay) + logger.info("Sleeping %s s (request throttled)", delay) time.sleep(int(delay)) - path = get_new_data_scraper_temp_folder(satellite) - path += satellite + "_downlink_" + str(len(os.listdir(path))) + ".json" - save_timestamps_to_file(time_range, path) else: break else: @@ -124,3 +120,4 @@ def scrape(satellite: str, save_to_db=True, save_to_file=False) -> None: if save_to_file: dump_telemetry_to_file(satellite, telemetry) + logger.info("Scraper done") diff --git a/src/transmission/processing/temp/da_vinci/buffer/.gitkeep b/src/transmission/processing/temp/da_vinci/buffer/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/transmission/processing/temp/da_vinci/da_vinci_downlink.json b/src/transmission/processing/temp/da_vinci/da_vinci_downlink.json deleted file mode 100644 index 8afbb6e5..00000000 --- a/src/transmission/processing/temp/da_vinci/da_vinci_downlink.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "da_vinci": { - "downlink": [] - } -} \ No newline at end of file diff --git a/src/transmission/processing/temp/da_vinci/da_vinci_uplink.json b/src/transmission/processing/temp/da_vinci/da_vinci_uplink.json deleted file mode 100644 index e2fae0b6..00000000 --- a/src/transmission/processing/temp/da_vinci/da_vinci_uplink.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "da_vinci": { - "uplink": [] - } -} \ No newline at end of file diff --git a/src/transmission/processing/temp/da_vinci/failed_downlink.json b/src/transmission/processing/temp/da_vinci/failed_downlink.json deleted file mode 100644 index 8afbb6e5..00000000 --- a/src/transmission/processing/temp/da_vinci/failed_downlink.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "da_vinci": { - "downlink": [] - } -} \ No newline at end of file diff --git a/src/transmission/processing/temp/da_vinci/failed_uplink.json b/src/transmission/processing/temp/da_vinci/failed_uplink.json deleted file mode 100644 index e2fae0b6..00000000 --- a/src/transmission/processing/temp/da_vinci/failed_uplink.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "da_vinci": { - "uplink": [] - } -} \ No newline at end of file diff --git a/src/transmission/processing/temp/da_vinci/scraper/.gitkeep b/src/transmission/processing/temp/da_vinci/scraper/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/transmission/processing/temp/delfi_c3/buffer/.gitkeep b/src/transmission/processing/temp/delfi_c3/buffer/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/transmission/processing/temp/delfi_c3/delfi_c3_downlink.json b/src/transmission/processing/temp/delfi_c3/delfi_c3_downlink.json deleted file mode 100644 index b8d7085b..00000000 --- a/src/transmission/processing/temp/delfi_c3/delfi_c3_downlink.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "delfi_c3": { - "downlink": [] - } -} \ No newline at end of file diff --git a/src/transmission/processing/temp/delfi_c3/delfi_c3_uplink.json b/src/transmission/processing/temp/delfi_c3/delfi_c3_uplink.json deleted file mode 100644 index 46f9c4d6..00000000 --- a/src/transmission/processing/temp/delfi_c3/delfi_c3_uplink.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "delfi_c3": { - "uplink": [] - } -} \ No newline at end of file diff --git a/src/transmission/processing/temp/delfi_c3/failed_downlink.json b/src/transmission/processing/temp/delfi_c3/failed_downlink.json deleted file mode 100644 index b8d7085b..00000000 --- a/src/transmission/processing/temp/delfi_c3/failed_downlink.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "delfi_c3": { - "downlink": [] - } -} \ No newline at end of file diff --git a/src/transmission/processing/temp/delfi_c3/failed_uplink.json b/src/transmission/processing/temp/delfi_c3/failed_uplink.json deleted file mode 100644 index 46f9c4d6..00000000 --- a/src/transmission/processing/temp/delfi_c3/failed_uplink.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "delfi_c3": { - "uplink": [] - } -} \ No newline at end of file diff --git a/src/transmission/processing/temp/delfi_c3/scraper/.gitkeep b/src/transmission/processing/temp/delfi_c3/scraper/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/transmission/processing/temp/delfi_next/buffer/.gitkeep b/src/transmission/processing/temp/delfi_next/buffer/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/transmission/processing/temp/delfi_next/delfi_next_downlink.json b/src/transmission/processing/temp/delfi_next/delfi_next_downlink.json deleted file mode 100644 index 1c2ff850..00000000 --- a/src/transmission/processing/temp/delfi_next/delfi_next_downlink.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "delfi_next": { - "downlink": [] - } -} \ No newline at end of file diff --git a/src/transmission/processing/temp/delfi_next/delfi_next_uplink.json b/src/transmission/processing/temp/delfi_next/delfi_next_uplink.json deleted file mode 100644 index 1b082f83..00000000 --- a/src/transmission/processing/temp/delfi_next/delfi_next_uplink.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "delfi_next": { - "uplink": [] - } -} \ No newline at end of file diff --git a/src/transmission/processing/temp/delfi_next/failed_downlink.json b/src/transmission/processing/temp/delfi_next/failed_downlink.json deleted file mode 100644 index 1c2ff850..00000000 --- a/src/transmission/processing/temp/delfi_next/failed_downlink.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "delfi_next": { - "downlink": [] - } -} \ No newline at end of file diff --git a/src/transmission/processing/temp/delfi_next/failed_uplink.json b/src/transmission/processing/temp/delfi_next/failed_uplink.json deleted file mode 100644 index 1b082f83..00000000 --- a/src/transmission/processing/temp/delfi_next/failed_uplink.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "delfi_next": { - "uplink": [] - } -} \ No newline at end of file diff --git a/src/transmission/processing/temp/delfi_next/scraper/.gitkeep b/src/transmission/processing/temp/delfi_next/scraper/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/transmission/processing/temp/delfi_pq/buffer/.gitkeep b/src/transmission/processing/temp/delfi_pq/buffer/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/transmission/processing/temp/delfi_pq/delfi_pq_downlink.json b/src/transmission/processing/temp/delfi_pq/delfi_pq_downlink.json deleted file mode 100644 index 50133fd9..00000000 --- a/src/transmission/processing/temp/delfi_pq/delfi_pq_downlink.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "delfi_pq": { - "downlink": [] - } -} \ No newline at end of file diff --git a/src/transmission/processing/temp/delfi_pq/delfi_pq_uplink.json b/src/transmission/processing/temp/delfi_pq/delfi_pq_uplink.json deleted file mode 100644 index 006a3d31..00000000 --- a/src/transmission/processing/temp/delfi_pq/delfi_pq_uplink.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "delfi_pq": { - "uplink": [] - } -} \ No newline at end of file diff --git a/src/transmission/processing/temp/delfi_pq/failed_downlink.json b/src/transmission/processing/temp/delfi_pq/failed_downlink.json deleted file mode 100644 index 50133fd9..00000000 --- a/src/transmission/processing/temp/delfi_pq/failed_downlink.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "delfi_pq": { - "downlink": [] - } -} \ No newline at end of file diff --git a/src/transmission/processing/temp/delfi_pq/failed_uplink.json b/src/transmission/processing/temp/delfi_pq/failed_uplink.json deleted file mode 100644 index 006a3d31..00000000 --- a/src/transmission/processing/temp/delfi_pq/failed_uplink.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "delfi_pq": { - "uplink": [] - } -} \ No newline at end of file diff --git a/src/transmission/processing/temp/delfi_pq/scraper/.gitkeep b/src/transmission/processing/temp/delfi_pq/scraper/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/transmission/processing/time_range_files/delfi_pq_uplink.json b/src/transmission/processing/time_range_files/delfi_pq_uplink.json deleted file mode 100644 index 0084296f..00000000 --- a/src/transmission/processing/time_range_files/delfi_pq_uplink.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "delfi_pq": { - "uplink": [ - "2021-12-19T02:20:13Z", - "2021-12-19T02:20:15Z" - ] - } -} \ No newline at end of file diff --git a/src/transmission/scheduler.py b/src/transmission/scheduler.py index 198998e5..130dd073 100644 --- a/src/transmission/scheduler.py +++ b/src/transmission/scheduler.py @@ -15,14 +15,15 @@ from apscheduler.triggers.interval import IntervalTrigger from apscheduler.triggers.date import DateTrigger from apscheduler.executors.pool import ThreadPoolExecutor -from apscheduler.events import EVENT_JOB_ADDED, EVENT_JOB_REMOVED, EVENT_JOB_EXECUTED, EVENT_JOB_SUBMITTED +from apscheduler.events import EVENT_JOB_ADDED, EVENT_JOB_REMOVED, EVENT_JOB_EXECUTED, \ + EVENT_JOB_SUBMITTED, EVENT_JOB_ERROR from django.forms import ValidationError from django_logger import logger from transmission.processing.satellites import SATELLITES from transmission.processing.process_raw_bucket import process_raw_bucket from transmission.processing.telemetry_scraper import scrape from transmission.processing.save_raw_data import process_uplink_and_downlink - +from transmission.processing.XTCEParser import XTCEParser def get_job_id(satellite: str, job_description: str) -> str: """Create an id, job description""" @@ -32,6 +33,21 @@ def get_job_id(satellite: str, job_description: str) -> str: return satellite + "_" + job_description + +def raw_bucket_processing_trigger_callback(satellites_list): + """Trigger raw bucket processing tasks. This callback is called when the buffer + processor finishes processing one block of frames.""" + + # retrieve the current scheduler instance + scheduler = Scheduler() + + for satellite in satellites_list: + args = [satellite] + job_id = get_job_id(satellite, "raw_bucket_processing") + # add a new job to the execution list + scheduler.add_job_to_schedule(process_raw_bucket, args, job_id) + + def schedule_job(job_type: str, satellite: str = None, link: str = None, date: datetime = None, interval: int = None) -> None: """Schedule job for a specified satellite and/or link. @@ -46,7 +62,14 @@ def schedule_job(job_type: str, satellite: str = None, link: str = None, scheduler.add_job_to_schedule(scrape, args, job_id, date, interval) elif job_type == "buffer_processing": - args = [] + # process new frames. Add the trigger callback for start the raw bucket processing tasks + args = [False, raw_bucket_processing_trigger_callback] + job_id = job_type + scheduler.add_job_to_schedule(process_uplink_and_downlink, args, job_id, date, interval) + + elif job_type == "buffer_reprocessing": + # re-process failed frames. Add the trigger callback for start the raw bucket processing tasks + args = [True, raw_bucket_processing_trigger_callback] job_id = job_type scheduler.add_job_to_schedule(process_uplink_and_downlink, args, job_id, date, interval) @@ -106,8 +129,7 @@ def __init__(self) -> None: logger.info("Scheduler already instantiated") else: executors = { - 'default': ThreadPoolExecutor(1), - # 'processpool': ProcessPoolExecutor(0) + 'default': ThreadPoolExecutor(10), } job_defaults = { 'coalesce': True, @@ -123,9 +145,14 @@ def __init__(self) -> None: self.scheduler.add_listener(self.executed_job_listener, EVENT_JOB_EXECUTED) self.scheduler.add_listener(self.add_job_listener, EVENT_JOB_ADDED) self.scheduler.add_listener(self.remove_job_listener, EVENT_JOB_REMOVED) + self.scheduler.add_listener(self.exception_listener, EVENT_JOB_ERROR) Scheduler.__instance = self + # TODO not very nice to start it here, but this class is a singleton and + # in ensures the gateway is loaded only once + XTCEParser.loadGateway() + def get_state(self) -> str: """Returns the state of the scheduler: running, paused, shutdown.""" if self.scheduler.state == STATE_STOPPED: @@ -137,37 +164,32 @@ def get_state(self) -> str: return "" + def exception_listener(self, event) -> None: + """Listens to newly added jobs""" + self.running_jobs.remove(event.job_id) + logger.info("Terminated job: %s", event.job_id) + logger.error(event.traceback) + def add_job_listener(self, event) -> None: """Listens to newly added jobs""" - logger.info("Scheduler added job: %s", event.job_id) self.pending_jobs.add(event.job_id) def remove_job_listener(self, event) -> None: """Listens to removed jobs""" - logger.info("Scheduler removed job: %s", event.job_id) self.pending_jobs.remove(event.job_id) def executed_job_listener(self, event) -> None: """Listens to executed jobs""" - logger.info("Scheduler executed job: %s", event.job_id) + logger.info("Executed job: %s", event.job_id) self.running_jobs.remove(event.job_id) - # automated processing pipeline: - # - when a buffer processing task completes that will trigger the raw bucket processing - # - when a scraper task completes that will trigger the raw bucket processing - if "buffer_processing" in event.job_id: - for sat in SATELLITES: - schedule_job("raw_bucket_processing", sat) - - elif "scraper" in event.job_id: - for sat in SATELLITES: - if sat in event.job_id: - schedule_job("raw_bucket_processing", sat, "downlink") - def submitted_job_listener(self, event) -> None: """Listens to submitted jobs""" + logger.info("Started job: %s", event.job_id) self.running_jobs.add(event.job_id) - logger.info("Scheduler submitted job: %s", event.job_id) + + if "scraper" in event.job_id: + schedule_job("buffer_processing") def get_pending_jobs(self) -> set: """Get the ids of the currently scheduled jobs.""" diff --git a/src/transmission/templates/transmission/table.html b/src/transmission/templates/transmission/table.html index 26801bb7..f8ed6001 100644 --- a/src/transmission/templates/transmission/table.html +++ b/src/transmission/templates/transmission/table.html @@ -1,4 +1,5 @@ {% load static %} +{% load humanize %} {% block title %}DelfiSpace - Frames{% endblock %} @@ -58,7 +59,7 @@

{{ table_name }} Frames

{% for frame in page_obj %} {{ frame.id }} - {{ frame.timestamp }} + {{ frame.timestamp|date:"Y-m-d H:i:s.u" }} {% if table_name == "Downlink" %} {{ frame.observer }} {% endif %} diff --git a/src/transmission/test/test_frame_processing.py b/src/transmission/test/test_frame_processing.py index 130a34df..ef1d79d3 100644 --- a/src/transmission/test/test_frame_processing.py +++ b/src/transmission/test/test_frame_processing.py @@ -4,7 +4,6 @@ from django.contrib.messages.storage.fallback import FallbackStorage from transmission.models import Downlink, Satellite, Uplink -from transmission.processing.bookkeep_new_data_time_range import combine_time_ranges from transmission.processing.satellites import SATELLITES from transmission.processing.save_raw_data import process_uplink_and_downlink, store_frames from transmission.views import delete_processed_frames, process @@ -25,11 +24,6 @@ def setUp(self): self.user.save() Satellite.objects.create(sat='delfipq', norad_id=1).save() - def tearDown(self) -> None: - for sat in SATELLITES: - combine_time_ranges(sat, 'uplink') - combine_time_ranges(sat, 'downlink') - def testSubmitFramesBatch(self): # add 3 valid frames from delfi_pq f1 = { "qos": 98.6, "sat": "delfipq", "timestamp": "2021-12-19T02:20:14.959630Z", "frequency": 2455.66, @@ -91,10 +85,6 @@ def setUp(self): store_frames(f, "user") def tearDown(self): - for sat in SATELLITES: - combine_time_ranges(sat, 'uplink') - combine_time_ranges(sat, 'downlink') - self.client.logout() diff --git a/src/transmission/test/test_insert_tle.py b/src/transmission/test/test_insert_tle.py index 01bb099b..cd36bac0 100644 --- a/src/transmission/test/test_insert_tle.py +++ b/src/transmission/test/test_insert_tle.py @@ -2,7 +2,7 @@ from transmission.models import TLE, Satellite from transmission.processing.save_raw_data import save_tle import datetime as dt -import pytz +from datetime import timezone # pylint: disable=all @@ -21,5 +21,5 @@ def test_insert_tle(self): tle_instance = TLE.objects.all()[0] self.assertEqual(tle_instance.sat, new_sat) self.assertEqual(tle_instance.tle, tle) - self.assertEqual(tle_instance.valid_from, dt.datetime(2008, 9, 20, 12, 25, 40, 104187, tzinfo=pytz.UTC)) - self.assertEqual(len(TLE.objects.all()), 1) \ No newline at end of file + self.assertEqual(tle_instance.valid_from, dt.datetime(2008, 9, 20, 12, 25, 40, 104187, tzinfo=timezone.utc)) + self.assertEqual(len(TLE.objects.all()), 1) diff --git a/src/transmission/test/test_views_templates.py b/src/transmission/test/test_views_templates.py index 0a4aed10..155f8936 100644 --- a/src/transmission/test/test_views_templates.py +++ b/src/transmission/test/test_views_templates.py @@ -7,7 +7,6 @@ from django.http import HttpRequest, SimpleCookie from django.urls import reverse from transmission.models import Downlink, Satellite, Uplink -from transmission.processing.bookkeep_new_data_time_range import combine_time_ranges from transmission.processing.satellites import SATELLITES from transmission.processing.save_raw_data import store_frames from transmission.views import submit_frame, submit_job, modify_scheduler @@ -338,9 +337,6 @@ def setUp(self): Satellite.objects.create(sat='delfic3', norad_id=1).save() def tearDown(self): - for sat in SATELLITES: - combine_time_ranges(sat, 'uplink') - combine_time_ranges(sat, 'downlink') self.client.logout() def test_requested_tables(self): @@ -398,10 +394,6 @@ def setUp(self): Satellite.objects.create(sat='delfic3', norad_id=1).save() def tearDown(self): - for sat in SATELLITES: - combine_time_ranges(sat, 'uplink') - combine_time_ranges(sat, 'downlink') - self.client.logout() def test_submit_get_request_not_allowed(self): diff --git a/src/transmission/urls.py b/src/transmission/urls.py index 13dbdac5..12f9c21e 100644 --- a/src/transmission/urls.py +++ b/src/transmission/urls.py @@ -1,23 +1,13 @@ """API urls""" -import os from django.urls import path from django.views.decorators.csrf import csrf_exempt from . import views urlpatterns = [ path('transmission//', views.get_frames_table, name='get_frames_table'), - path('transmission/process-frames//', views.process, name='process'), path('transmission/delete-processed-frames//', views.delete_processed_frames, name='delete_processed_frames'), # path('TLEs/', views.get_tle_table, name='get_tle_table'), path('submit/', csrf_exempt(views.submit_frame), name='submit_frame'), path('schedule-job/', views.submit_job, name='submit_job'), path('modify-scheduler//', views.modify_scheduler, name='modify_scheduler'), - ] - -# add path only in debug mode -dummy_data_path = path('add/', views.add_dummy_downlink_frames, name='add_dummy_downlink_frames') -DEBUG = bool(int(os.environ.get('DEBUG', 1))) - -if DEBUG: - urlpatterns.append(dummy_data_path) diff --git a/src/transmission/views.py b/src/transmission/views.py index 9853fc38..a6a07b10 100644 --- a/src/transmission/views.py +++ b/src/transmission/views.py @@ -1,5 +1,5 @@ """API request handling. Map requests to the corresponding HTMLs.""" -from datetime import timedelta, datetime +from datetime import datetime from http import HTTPStatus import json from json.decoder import JSONDecodeError @@ -16,11 +16,11 @@ from django_logger import logger from members.models import APIKey from transmission.forms.forms import SubmitJob -from transmission.processing.add_dummy_data import add_dummy_downlink_frames from transmission.scheduler import Scheduler, schedule_job from .models import Uplink, Downlink, TLE from .filters import TelemetryDownlinkFilter, TelemetryUplinkFilter, TLEFilter -from .processing.save_raw_data import process_frames, store_frames +#from .processing.save_raw_data import process_frames, store_frames +from .processing.save_raw_data import store_frames QUERY_ROW_LIMIT = 100 @@ -50,7 +50,7 @@ def submit_frame(request): # pylint:disable=R0911 api_key_name, number_of_saved_frames) try: - schedule_job("buffer_processing", date=datetime.now() + timedelta(seconds=30)) + schedule_job("buffer_processing", date=datetime.now()) except ValidationError as _: pass @@ -102,14 +102,6 @@ def submit_frame(request): # pylint:disable=R0911 return JsonResponse({"result": "failure", "message": "Method not allowed"}, status=HTTPStatus.METHOD_NOT_ALLOWED) -def add_dummy_downlink(request): - """Add dummy frames to Downlink table as admin user.""" - - add_dummy_downlink_frames() - - return JsonResponse({"len": len(Downlink.objects.all())}) - - @login_required(login_url='/login') def delete_processed_frames(request, link): """Remove the processed frames that are already stored in influxdb""" @@ -137,38 +129,38 @@ def delete_processed_frames(request, link): return redirect('get_frames_table', link) -@login_required(login_url='/login') -def process(request, link): - """Process frames that are not already stored in influxdb""" - - user = request.user - - if link not in ['uplink', 'downlink']: - return HttpResponseBadRequest() - - if link == "uplink" and user.has_perm("transmission.view_uplink"): - frames = Uplink.objects.all().filter(processed=False) - logger.info("%s frames processing triggered: %s frames to process", link, len(frames)) - - processed_frame_count = process_frames(frames, link) - logger.info("%s %s frames were successfully processed", processed_frame_count, link) - - messages.info(request, f"{processed_frame_count} {link} frames were processed.") - - elif link == "downlink" and user.has_perm("transmission.view_downlink"): - frames = Downlink.objects.all().filter(processed=False) - logger.info("%s frames processing triggered: %s frames to process", link, len(frames)) - - processed_frame_count = process_frames(frames, link) - logger.info("%s %s frames were successfully processed", processed_frame_count, link) - - messages.info(request, f"{processed_frame_count} {link} frames were processed.") - - else: - logger.warning("%s was denied permission to access uplink or downlink tables", request.user) - return HttpResponseForbidden() - - return redirect('get_frames_table', link) +#@login_required(login_url='/login') +#def process(request, link): +# """Process frames that are not already stored in influxdb""" +# +# user = request.user +# +# if link not in ['uplink', 'downlink']: +# return HttpResponseBadRequest() +# +# if link == "uplink" and user.has_perm("transmission.view_uplink"): +# frames = Uplink.objects.all().filter(processed=False) +# logger.info("%s frames processing triggered: %s frames to process", link, len(frames)) +# +# processed_frame_count = process_frames(frames, link) +# logger.info("%s %s frames were successfully processed", processed_frame_count, link) +# +# messages.info(request, f"{processed_frame_count} {link} frames were processed.") +# +# elif link == "downlink" and user.has_perm("transmission.view_downlink"): +# frames = Downlink.objects.all().filter(processed=False) +# logger.info("%s frames processing triggered: %s frames to process", link, len(frames)) +# +# processed_frame_count = process_frames(frames, link) +# logger.info("%s %s frames were successfully processed", processed_frame_count, link) +# +# messages.info(request, f"{processed_frame_count} {link} frames were processed.") +# +# else: +# logger.warning("%s was denied permission to access uplink or downlink tables", request.user) +# return HttpResponseForbidden() +# +# return redirect('get_frames_table', link) def paginate_telemetry_table(request, telemetry_filter, table_name):