diff --git a/.devcontainer/Dockerfile.preview b/.devcontainer/Dockerfile.preview index fc9106c..16232f0 100644 --- a/.devcontainer/Dockerfile.preview +++ b/.devcontainer/Dockerfile.preview @@ -9,6 +9,9 @@ COPY --chown=docker:docker vue vue COPY --chown=docker:docker protobuf-msgs protobuf-msgs COPY --chown=docker:docker Makefile Makefile +RUN mkdir -p django/storage/logs +RUN mkdir -p django/storage/images + RUN cd vue && npm install RUN mkdir -p build/gRPC diff --git a/.devcontainer/docker-compose.preview.yml b/.devcontainer/docker-compose.preview.yml index 839aa7f..58a9fa8 100644 --- a/.devcontainer/docker-compose.preview.yml +++ b/.devcontainer/docker-compose.preview.yml @@ -15,15 +15,11 @@ services: image: rabbitmq:3.10.7 ports: - 5672:5672 - # healthcheck: - # interval: 10s - # timeout: 5s - # retries: 5 - # test: [ "CMD", "rabbitmqctl", "status" ] postgres: image: postgres:14 restart: unless-stopped + user: postgres volumes: - postgres-data:/var/lib/postgresql/data ports: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f99aa54..9f4567c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -82,7 +82,8 @@ jobs: uses: docker/build-push-action@v2 with: file: .devcontainer/Dockerfile - push: ${{ GitHub.event_name != 'pull_request'}} + # build on dev branch, only push on main branch + push: ${{ (GitHub.event_name != 'pull_request') && (github.ref == 'refs/heads/main') }} tags: ${{ needs.create-ids.outputs.image_id }}:${{ needs.create-ids.outputs.version }} cache-from: type=registry,ref=${{ needs.create-ids.outputs.image_id }}:${{ needs.create-ids.outputs.version }} cache-to: type=inline diff --git a/.github/workflows/mkdocs.yml b/.github/workflows/mkdocs.yml index 22f9066..5f87356 100644 --- a/.github/workflows/mkdocs.yml +++ b/.github/workflows/mkdocs.yml @@ -3,7 +3,6 @@ on: push: branches: - main - - dev paths: - docs/** - mkdocs.yml diff --git a/README.md b/README.md index d9f64b5..501a7d1 100644 --- a/README.md +++ b/README.md @@ -43,14 +43,21 @@ SOFTWARE.

+Environmental friendly micro framing requires a lot of knowledge for the +cultivation of a garden. As a consequence, a lot of newcomers and people +switching from convention agriculture have trouble managing their garden. To +resolve this problem, the **PlantMap Digital Logbook** was created, a digital +representation of a garden, which monitors the plants and helps with +instructions if problems like water deficiency or diseases are detected. + +![crops-view](docs/imgs/crops-tab.png) ## Table of Contents -- [README](#readme) - - [General](#general) - - [Quick Start](#quick-start) - - [Documentation](#documentation) - - [Contributing](#contributing) - - [License](#license) +- [General](#general) +- [Quick Start](#quick-start) +- [Documentation](#documentation) +- [Contributing](#contributing) +- [License](#license) ## General @@ -82,38 +89,28 @@ semantical environment representation For running the application, we assume that you have a current version of [Visual Studio Code](https://code.visualstudio.com/) and -[Docker](https://www.docker.com/). Further, Visual Studio Code **needs** to have +[Docker](https://www.docker.com/) installed. Further, Visual Studio Code **needs** to have the **docker and the remote container extensions installed**. If you didn't understand the requirements, please visit the [installation guide](https://naturerobots.github.io/HSOS-SEP-PlantMap-2022/getting-started/installation/) in the documentation for more detailed steps. -To get started, clone the repository and open it in Visual Studio Code: +If you just want to get the application running, clone the repository and run the docker compose command. ```sh - git clone https://github.com/naturerobots/HSOS-SEP-PlantMap-2022.git - cd HSOS-SEP-PlantMap-2022 - code . +git clone https://github.com/naturerobots/HSOS-SEP-PlantMap-2022.git +cd HSOS-SEP-PlantMap-2022 +docker compose -f .devcontainer/docker-compose.preview.yml up -d ``` -Next, we need to mount the current workspace into the projects docker container -to use it as a preconfigured development environment. For that, you can press -`F1` or `CTRL+SHIFT+P` and enter `Remote-Containers: Reopen Folder in Container` -and confirm with `ENTER`. Visual Studio Code additionally shows a notification -after opening the project in the bottom right corner with the same option. Now -the [docker -image](https://github.com/naturerobots/HSOS-SEP-PlantMap-2022/pkgs/container/plant-map-digital-logbook) -of the project along with the images for the - [PostgreSQL](https://www.postgresql.org/) database and the - [RabbitMQ](https://www.rabbitmq.com/) message broker are downloaded. - Additionally, the set-up of all required packages and extensions is handled. - -The default user for the container is: - -```shell -user: docker -password: docker - ``` +Now the application should be available under +[localhost:5000](http://localhost:5050), the REST API is runs on port 8000. + +To stop the application use : + +```sh +docker compose -f .devcontainer/docker-compose.preview.yml stop +``` ## Documentation @@ -129,8 +126,7 @@ learn and inspire. Any contributions are **greatly appreciated**. This repository uses [pre-commit](https://pre-commit.com/) checks to verify the code style before committing. Since the pre-commit checks are automatically -installed in the development container, they are run before each commit you -make. The checks can also be run manually with: +installed, they are run before each commit. The checks can also be run manually with: ```bash pre-commit run -a diff --git a/docs/getting-started/improvements.md b/docs/getting-started/improvements.md index 32d35ba..944db8f 100644 --- a/docs/getting-started/improvements.md +++ b/docs/getting-started/improvements.md @@ -1 +1,18 @@ # Improvements + +The following section contains suggestions for improving the application and may be extended much further. + +## Tiling of garden images to increase loading speed + +Administrators of companies or gardens have the ability to upload a (self-taken) picture of a garden using +the web interface. The image can be displayed in the dashboard-, beds- or plants-view in the web interface, +replacing the satellite image at the exact location of the garden, providing a much more detailed +view of the beds and plants. + +High resolution images can have a large file size, this can lead to long loading times +of the web interface, especially for users which have a slow internet connection. + +To solve this problem, a functionality could be implemented, which splits the uploaded image into many little +square tiles. A single tile can be loaded much faster, because of the smaller file size. The tiles will be loaded +incrementally, building the hole image. Users will have the advantage of seeing parts of the image earlier +and having better visual feedback of the loading process. diff --git a/docs/imgs/logo_small_white.svg b/docs/imgs/logo_small_white.svg new file mode 100644 index 0000000..f0aa7d0 --- /dev/null +++ b/docs/imgs/logo_small_white.svg @@ -0,0 +1,9 @@ + diff --git a/docs/reference/3d-view.md b/docs/reference/3d-view.md new file mode 100644 index 0000000..f382e5c --- /dev/null +++ b/docs/reference/3d-view.md @@ -0,0 +1,72 @@ +# 3D-View + +## Overview + +| Name | HTTP | URL | | | +|---------------------------------------------------------------------|------|-----------------------------------------------------------------------------------|---|---| +| [Get list of 3d meta plant information](#3d-meta-plant-information-list) | GET | `/companies//gardens//beds//3d-scene/` | | | + +## 3d meta plant information list + +Send a GET request to this endpoint to get the Plant information. +The response contains two elements. +Firstly an array called plants, which contains informations about every plant in the bed. +Secondly an global element which describes center position of the bed. + +The plants array only contains plants for which the pointclouds are in the `django/storage/media/pointclouds/ply` folder. +The pointcloud of a plant can be loaded by the url given in the plants array. + +for more information on how to access the pointclouds take a look at the [storage system](/reference/storage-system). + +**Request**: `/companies//gardens//beds//3d-scene/` +**Response** `200 Ok`, `404 Not Found`, `400 Bad Request` + +Example response: + +```json +{ +"plants": [ + { + "geometryUUID": "a671dab73f9844b280ac4d36f0314ad5", + "measurementUUID": "8374c5058da64b73a7520955fc0c65fd", + "url": "http://localhost:8000/media/pointclouds/ply/e1ef73b1258b475a996d2b72924c27ac/a671dab73f9844b280ac4d36f0314ad5.ply", + "name": "crop 16", + "locationDescription": 1, + "type": "mangold", + "position": { + "x": "-2.0091417", + "y": "0.0038527877", + "z": "-0.817174" + }, + "health": [ + { + "type": "water", + "loglevel": 2, + "shortcut": "w" + }, + { + "type": "nutrients", + "loglevel": 1, + "shortcut": "n" + }, + { + "type": "diseases", + "loglevel": 3, + "shortcut": "d" + } + ], + "yield": 705.1650603082642, + "status": "vegetating", + "harvest": "4 Weeks", + "progress": "0.9351895742166416" + } + ], + "global": { + "position": { + "x": "-1.9149369", + "y": "-0.05081649", + "z": "-0.8281588" + } + } +} +``` diff --git a/docs/reference/external-apis.md b/docs/reference/external-apis.md new file mode 100644 index 0000000..1e7ee75 --- /dev/null +++ b/docs/reference/external-apis.md @@ -0,0 +1,26 @@ +# External APIs + +## OpenWeatherMap + +The following [APIs](https://openweathermap.org/api) from [OpenWeatherMap](https://openweathermap.org/) +were used for the `weather widget`. +To use the APIs, an [OpenWeatherMap account](https://home.openweathermap.org/users/sign_up) +and a [token](https://home.openweathermap.org/api_keys) must be created. +The token must then be added as environment variable `VITE_TOKEN_OWM`. + +| API | Description | [Pricing](https://openweathermap.org/price) | +|-----------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------| +| [Current weather data](https://openweathermap.org/current) | Used to get the current temperature, weather and city name based on the coordinates of the garden.
**The geocoder used in the API will soon be deprecated.** | 60 free calls/minute
1,000,000 free calls/month | +| [One Call API 3.0](https://openweathermap.org/api/one-call-3) | Used to get the current day's temperature low and high, as well as the hourly forecast for temperature, rain probability, and wind speed and direction. | 1,000 free calls/day | +| [Geocoding API](https://openweathermap.org/api/geocoding-api) | **Not used yet**, but the service has already been implemented for it, so it can be used when the `Current weater data API` geocoder API is deprecated. | 60 free calls/minute
1,000,000 free calls/month | + +## Mapbox + +The following [APIs](https://www.mapbox.com/product-apis) from [Mapbox](https://www.mapbox.com/) +were used for the `garden map`. +To use the APIs, an [Mapbox account](https://account.mapbox.com/auth/signup/) and a `token` must be created. +The token must then be added as environment variable `VITE_TOKEN_MAPBOX`. + +| API | Description | [Pricing](https://www.mapbox.com/pricing) | +|----------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------|----------------------------------| +| [Static Tiles](https://docs.mapbox.com/api/maps/static-tiles/) | Used to add tiles to the map for the garden, so that the garden image can be better placed in the environment. | 200,000 free tile requests/month | diff --git a/docs/reference/permissions.md b/docs/reference/permissions.md index 14a1e93..34b9a4c 100644 --- a/docs/reference/permissions.md +++ b/docs/reference/permissions.md @@ -42,22 +42,22 @@ The following roles exist: ### Create company permission Send a POST request to this endpoint to give a user permissions on the company specified in the URL. -The request body needs to contain a `user_id` and a `permission` field as json. +The request body needs to contain a `username` and a `permission` field as json. Only admins of the corresponding company can access this endpoint. -The following request with URL: ../companies/2/createPermission -gives the user with id '42' admin permissions on the company with id '2': +The following request with URL: `../companies/2/createPermission` +gives the user with username `testuser` admin permissions on the company with id `2`: ```json { - "user_id": "42", + "username": "testuser", "permission": "a" } ``` -If a permission for this user and company already existed before, it will be deleted and a new one is created. -Own permissions cannot be overwritten. +If a permission for this user and company already exists, it will be overwritten. Own permissions cannot be overwritten, +in order to prevent locking yourself out. **Request**: `POST /companies/{company_id:int}/createPermission` **Responses**: @@ -70,17 +70,17 @@ Own permissions cannot be overwritten. ### Remove company permission Send a POST request to this endpoint to remove the permissions of a user to the company specified in the URL. -The request body needs to contain a `user_id` field as json. +The request body needs to contain a `username` field as json. Only admins of the corresponding company can access this endpoint. Own permissions or those of the last admin cannot be removed. -The following request with URL: ../companies/2/createPermission -removes the permissions for the user with id '42' on the company with id '2': +The following request with URL: `../companies/2/createPermission` +removes the permissions for the user with username `testuser` on the company with id `2`: ```json { - "user_id": "42" + "username": "testuser" } ``` diff --git a/docs/reference/storage-system.md b/docs/reference/storage-system.md new file mode 100644 index 0000000..7ddf73b --- /dev/null +++ b/docs/reference/storage-system.md @@ -0,0 +1,35 @@ +# Storage System + +## Basics + +To store files like the garden images, logs or plant pointclouds we use the storage folder. +Images is a common folder, which allows to store files that are not accessible through an url. +The logs folder uses the [django logging system](https://docs.djangoproject.com/en/4.1/topics/logging/). +It is configured to contain two logs. `debug.log`, which shows all logs but can get kind of messy. +For a cleaner logging experience we added the second log `info.log`, which shows everything except the logs defined as debug. +As a third option for storage we added the media folder. +everything inside this folder can be accessed through an url. +For this we implemented the [django storage system](https://docs.djangoproject.com/en/4.1/ref/files/storage/). +There is currently no system implemented to restrict the access to the data inside of the media folder. + +## Folder Structure + +Files inside the media folder can be accessed by `http://localhost:8000/media/`. +An example GET request to the pointcloud of plant1 would look like this `http://localhost:8000/media/pointclouds/ply/05717c979b0d4bd790d31ce218cd58ee/06694a57e7cf4ee1acce970ab9d9d67a.ply`. + +```text +storage +├── images +│   └── garden1.png +├── logs +│   ├── debug.log +│   └── info.log +└── media + └── pointclouds + └── ply + ├── 05717c979b0d4bd790d31ce218cd58ee + │   ├── 06694a57e7cf4ee1acce970ab9d9d67a.ply + │   ├── 0bf37a0851b7402d88674e153f58e6f8.ply + │   ├── 0d927fa6b3534f9580d1db73d483b254.ply + │   ├── ... +``` diff --git a/docs/reference/terminology.md b/docs/reference/terminology.md index 2c1fe78..2b2826a 100644 --- a/docs/reference/terminology.md +++ b/docs/reference/terminology.md @@ -4,8 +4,15 @@ ![Definitions Bed - Garden - Company](../imgs/plant-map-companie-definitons-low.jpg) -```text -A: Bed -B: Garden -C: Company -``` +### A: Bed + +A single row of soil, where crops/plants of the same variety are growing. + +### B: Garden + +Beds with different varieties of plants in a specified area together are forming a unit called garden. + +### C: Company + +All gardens and their corresponding beds belong to a company. +The company has information about users/members supervising them. diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css index 24a9b18..ed839c5 100644 --- a/docs/stylesheets/extra.css +++ b/docs/stylesheets/extra.css @@ -1,5 +1,5 @@ [data-md-color-scheme="naturerobots"] { - --md-primary-fg-color: #b6c1a7; + --md-primary-fg-color: #79b729; --md-accent-fg-color: #9c9b9b; --md-typeset-a-color: #26561d; } diff --git a/mkdocs.yml b/mkdocs.yml index 22ff956..4fe1cb1 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -17,13 +17,16 @@ nav: - Beds & Plants: "reference/bed-plant-endpoints.md" - Company & Gardens: "reference/company-garden-endpoints.md" - Permission System: "reference/permissions.md" - - Env Variables: "reference/env-vars.md" + - 3D-View: "reference/3d-view.md" + - Storage System: "reference/storage-system.md" - Architecture: "reference/architecture.md" + - External APIs: "reference/external-apis.md" - Terminology: "reference/terminology.md" + - Env Variables: "reference/env-vars.md" theme: name: material language: en - logo: imgs/logo_small_color.svg + logo: imgs/logo_small_white.svg favicon: imgs/logo_small_color.svg palette: - scheme: naturerobots diff --git a/vue/src/components/sensor/SensorComp.vue b/vue/src/components/sensor/SensorComp.vue index 84d88d4..cd854f4 100644 --- a/vue/src/components/sensor/SensorComp.vue +++ b/vue/src/components/sensor/SensorComp.vue @@ -73,7 +73,7 @@ function getRowBySensorId(sensorId: number): HTMLTableRowElement | undefined { if (sensorId == table.value.rows[i].id) { return document .getElementsByClassName("q-table")[0] - .getElementsByTagName("tr")[i + 1]; + .getElementsByTagName("tr")[i + 2]; } } } diff --git a/vue/src/components/three-scenes/CropScene.vue b/vue/src/components/three-scenes/CropScene.vue index cbf0c1a..83c7cb2 100644 --- a/vue/src/components/three-scenes/CropScene.vue +++ b/vue/src/components/three-scenes/CropScene.vue @@ -243,7 +243,7 @@ onMounted(() => { "/beds/" + bedId.value + "/3d-scene/"; - let payload = {}; + //let payload = {}; let header = { headers: { Authorization: "Token " + storeToRefs(userStore()).getToken.value.token, @@ -251,9 +251,9 @@ onMounted(() => { }; axios - .post(url, payload, header) + .get(url, header) .then(function (response): void { - //console.log("then", response); + console.log("then", response); crop3dArray = response["data"]["plants"]; globalPosition = response["data"]["global"]["position"]; diff --git a/vue/src/services/bedApi.ts b/vue/src/services/bedApi.ts index 2cf7a89..97deb9c 100644 --- a/vue/src/services/bedApi.ts +++ b/vue/src/services/bedApi.ts @@ -8,6 +8,8 @@ import type { Ref } from "vue"; const baseURL = "http://127.0.0.1:8000"; +let lastChunkFailed; + export async function loadBeds(): Promise { const companyId: Ref = storeToRefs( companyStore() @@ -29,16 +31,27 @@ export async function loadBeds(): Promise { }, } ).then(async (response) => { - // response.body is a ReadableStream const reader = response.body?.getReader(); const beds: Beds = { bedList: [], }; for await (const chunk of readChunks(reader)) { if (chunk) { - const json = JSON.parse(Utf8ArrayToStr(chunk)); - beds.bedList.push(json); - bedStore().setBed(json); + let json; + try { + if (lastChunkFailed) { + lastChunkFailed += Utf8ArrayToStr(chunk); + json = JSON.parse(lastChunkFailed); + } else { + json = JSON.parse(Utf8ArrayToStr(chunk)); + } + beds.bedList.push(json); + bedStore().setBed(json); + lastChunkFailed = null; + } catch (error) { + console.log(error); + lastChunkFailed = Utf8ArrayToStr(chunk); + } } } return false; diff --git a/vue/src/stores/bedStore.ts b/vue/src/stores/bedStore.ts index b846fca..0f95f4f 100644 --- a/vue/src/stores/bedStore.ts +++ b/vue/src/stores/bedStore.ts @@ -41,6 +41,7 @@ export const bedStore = defineStore({ this.selectedBed = bedId; }, setBed(bed: Bed) { + console.log(bed); this.beds.bedList.push(bed); }, },