diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 00000000..41167050 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,36 @@ +name: Upload Python Package + +on: push + +# on: +# workflow_dispatch: +# push: +# tags: +# - v** + +jobs: + docs: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: "3.11" + - name: Install dependencies + run: | + python3 -m pip install -r requirements.txt + python3 -m pip install -r mkdocs_requirements.txt + python3 -m pip install -e . + - name: Build docs + run: mkdocs build -v + - name: Analyze + run: | + pwd + ls -lha + - name: Deploy gh-pages + uses: JamesIves/github-pages-deploy-action@v4.2.5 + with: + branch: gh-pages + folder: site diff --git a/.gitignore b/.gitignore index 5d8885ee..c90ab088 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ detector/ *.env *.swp site/ +docs/reference/ .nicegui/ diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..c0b2af1e --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +nicegui@zauberzeug.com. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/README.md b/README.md index cccd0756..a0cea9a5 100644 --- a/README.md +++ b/README.md @@ -2,65 +2,8 @@ # Zauberzeug Field Friend -This is the full source code of the [Field Friend](http://feldfreund.de) (aka Feldfreund) weeding robot. +This is the full source code of the [Field Friend](http://feldfreund.de) (aka Feldfreund) agricultural robot. The software is based on [RoSys](https://rosys.io) and fully Open Source. The hardware is build by [Zauberzeug](http://zauberzeug.com) and intended as a platform to advance organic and regenerative agriculture. -## Features - -- full control via web interface -- manual steering with touch-joystick and keyboard -- camera/motor calibration for real world coordinate system (unit: meters) -- ... - -## Getting Started - -Run these commands on your local machine to start the Field Friend simulation: - -```bash -git clone git@github.com:zauberzeug/field_friend.git -cd field_friend -python3 -m pip install -r requirements.txt -./main.py -``` - -This will open the user interface with a simulated robot in your browser. - -NOTE: The software is intended to run on Linux and Unix systems. -If you are using windows please consider running in a Docker container or virtual machine. - -## On Real Hardware - -### Development - -The following instructions will only work if you have a real "Zauberzeug Field Friend" at your disposal. -Contact [sales@zauberzeug.com](mailto:sales@zauberzeug.com) if you are interested in a non-profit purchase of this development hardware. - -#### Setup - -1. ensure you can login via ssh without providing a password (via `ssh-copy-id` command) -2. ensure you have [LiveSync](https://github.com/zauberzeug/livesync) installed with
`python3 -m pip install livesync` -3. ensure the latest version of the docker image is installed on the Field Friend by syncing the code as described below and then running
`./docker.sh uppull` -4. Optional: ensure the correct docker containers are loaded on startup by running
`./docker.sh stopall && ./docker.sh uppull && ./docker.sh install` -5. Optional: update the [Lizard](https://lizard.dev) microcontroller firmware on your Robot Brain by accessing the Field Friend web interface and navigating to the "Developer" options - -#### Deploy and Change Code - -1. go to your local `field_friend` folder and start the [LiveSync](https://github.com/zauberzeug/livesync) script:
- `./sync.py ` -2. this will deploy your local code to the Field Friend -3. as long as [LiveSync](https://github.com/zauberzeug/livesync) is active, all code change are automatically pushed to the machine -4. any code changes will automatically trigger a reload on the Field Friend - -### Update RoSys and NiceGUI - -To utilize personal versions of RoSys and NiceGUI instead of the default ones provided in the docker image, -modify the `sync.py` file by uncommenting the specific folders. - -### Debugging - -You can see the current log with - -```bash -./docker.sh l rosys -``` +Please see the [documentation](https://docs.feldfreund.de) for details on installation, setup and usage. diff --git a/docs/contributing.md b/docs/contributing.md new file mode 100644 index 00000000..b9c007c3 --- /dev/null +++ b/docs/contributing.md @@ -0,0 +1,85 @@ +# Contributing + +We're thrilled that you're interested in contributing to the Field Friend source code! +Here are some guidelines that will help you get started. + +## Reporting issues + +If you encounter a bug or other issue, the best way to report it is by opening a new issue on our [GitHub repository](https://github.com/zauberzeug/field_friend). +When creating the issue, please provide a clear and concise description of the problem, including any relevant error messages and code snippets. +If possible, include steps to reproduce the issue. + +## Code of Conduct + +We follow a [Code of Conduct](https://github.com/zauberzeug/field_friend/blob/main/CODE_OF_CONDUCT.md) to ensure that everyone who participates in the NiceGUI community feels welcome and safe. +By participating, you agree to abide by its terms. + +## Contributing code + +We are excited that you want to contribute code to the Field Friend source code. +We're always looking for bug fixes, performance improvements, and new features. + +## Coding Style Guide + +### Formatting + +We use [autopep8](https://github.com/hhatto/autopep8) with a 120 character line length to format our code. +Before submitting a pull request, please run + +```bash +autopep8 --max-line-length=120 --in-place --recursive . +``` + +on your code to ensure that it meets our formatting guidelines. +Alternatively you can use VSCode, open the field_friend.code-workspace file and install the recommended extensions. +Then the formatting rules are applied whenever you save a file. + +In our point of view, the Black formatter is sometimes a bit too strict. +There are cases where one or the other arrangement of, e.g., function arguments is more readable than the other. +Then we like the flexibility to either put all arguments on separate lines or only put the lengthy event handler +on a second line and leave the other arguments as they are. + +### Imports + +We use [ruff](https://docs.astral.sh/ruff/) to automatically sort imports: + +```bash +ruff check . --fix +``` + +### Single vs Double Quotes + +Regarding single or double quotes: [PEP 8](https://peps.python.org/pep-0008/) doesn't give any recommendation, so we simply chose single quotes and sticked with it. +On qwerty keyboards it's a bit easier to type, is visually less cluttered, and it works well for strings containing double quotes from the English language. + +### F-Strings + +We use f-strings where ever possible because they are generally more readable - once you get used to them. +There are only a few places in the code base where performance really matters and f-strings might not be the best choice. +These places should be marked with a `# NOTE: ...` comment when diverging from f-string usage. + +## Documentation + +### Formatting + +Because it has [numerous benefits](https://nick.groenen.me/notes/one-sentence-per-line/) we write each sentence in a new line. + +### Examples + +Each example should be about one concept. +Please try to make them as minimal as possible to show what is needed to get some kind of functionality. +We are happy to merge pull requests with new examples which show new concepts, ideas or interesting use cases. + +## Pull requests + +To get started, fork the repository on GitHub, clone it somewhere on your filesystem, commit and push your changes, +and then open a pull request (PR) with a detailed description of the changes you've made +(the PR button is shown on the GitHub website of your forked repository). + +When submitting a PR, please make sure that the code follows the existing coding style and that all tests are passing. +If you're adding a new feature, please include tests that cover the new functionality. + +## Thank you! + +Thank you for your interest in contributing. +We're looking forward to work with you! diff --git a/docs/features/field_planner.md b/docs/features/field_planner.md new file mode 100644 index 00000000..2c3f3992 --- /dev/null +++ b/docs/features/field_planner.md @@ -0,0 +1,3 @@ +# Field Planner + +The Field Planner is a tool to create and manage fields and routes for the Field Friend. diff --git a/docs/features/user_interface.md b/docs/features/user_interface.md new file mode 100644 index 00000000..7237eba3 --- /dev/null +++ b/docs/features/user_interface.md @@ -0,0 +1,4 @@ +# User Interface + +The Field Friend user interface is a web application build with [NiceGUI](https://nicegui.io). +This allows you to control the robot via the local "Feldfreund" WiFi hotspot or remotely via NiceGUI On Air. diff --git a/docs/generate_reference.py b/docs/generate_reference.py new file mode 100644 index 00000000..f9a681df --- /dev/null +++ b/docs/generate_reference.py @@ -0,0 +1,73 @@ +import dataclasses +import importlib +import inspect +import logging +import sys +from pathlib import Path +from types import ModuleType + +import mkdocs_gen_files + +nav = mkdocs_gen_files.Nav() + + +def extract_events(filepath: str) -> dict[str, str]: + with open(filepath, 'r') as f: + lines = f.read().splitlines() + events = {} + for l, line in enumerate(lines): + if line.endswith('= Event()'): + event_name = line.strip().split()[0].removeprefix('self.') + event_doc = lines[l+1].split('"""')[1] + events[event_name] = event_doc + return events + + +for path in sorted(Path('.').rglob('__init__.py')): + identifier = str(path.parent).replace('/', '.') + if identifier in ['field_friend',]: + continue + + try: + module = importlib.import_module(identifier) + except Exception: + logging.warning(f'Failed to import {identifier}') + continue + + doc_path = path.parent.with_suffix('.md') + found_something = False + for name in getattr(module, '__all__', dir(module)): + if name.startswith('_'): + continue # skip private fields + cls = getattr(module, name) + if isinstance(cls, ModuleType): + continue # skip sub-modules + if dataclasses.is_dataclass(cls): + continue # skip dataclasses + if not cls.__doc__: + continue # skip classes without docstring + try: + events = extract_events(inspect.getfile(cls)) + except Exception: + logging.warning(f'skipping {identifier}.{name}') + continue + with mkdocs_gen_files.open(Path('reference', doc_path), 'a') as fd: + print(f'::: {identifier}.{name}', file=fd) + if events: + print(' options:', file=fd) + print(' filters:', file=fd) + for event_name in events: + print(f' - "!{event_name}"', file=fd) + print('### Events', file=fd) + print('Name | Description', file=fd) + print('- | -', file=fd) + for event_name, event_doc in events.items(): + print(f'{event_name} | {event_doc}', file=fd) + print('', file=fd) + found_something = True + + if found_something: + nav[path.parent.parts[1:]] = doc_path.as_posix() + +with mkdocs_gen_files.open('reference/SUMMARY.md', 'w') as nav_file: + nav_file.writelines(nav.build_literate_nav()) diff --git a/docs/getting_started.md b/docs/getting_started.md new file mode 100644 index 00000000..65b58b2a --- /dev/null +++ b/docs/getting_started.md @@ -0,0 +1,60 @@ +# Getting Started + +## Run in Simulation + +We suggest you begin with simulating the Field Friend on your local development machine. +The software is meant to run on Linux and Unix systems so if you are using Windows, consider running in a Docker container or virtual machine. + +Just execute the following commands: + +```bash +git clone git@github.com:zauberzeug/field_friend.git +cd field_friend +python3 -m pip install -r requirements.txt +./main.py +``` + +This will open the user interface of a simulated robot in your browser. +If you change some code, the simulation will automatically reload. +The Field Friend code is based on [RoSys](https://rosys.io) which itself uses [NiceGUI](https://nicegui.io), +both having a very gentle learning curve and are designed to boost your rapid development and testing. + +## Run on Real Hardware + +The following instructions will only work if you have a real Zauberzeug Field Friend at your disposal. +Contact [sales@zauberzeug.com](mailto:sales@zauberzeug.com) if you are interested in purchasing this robot. + +### Setup + +1. ensure you can login via ssh without providing a password (via `ssh-copy-id` command) +2. ensure you have [LiveSync](https://github.com/zauberzeug/livesync) installed with
`python3 -m pip install livesync` +3. ensure the latest version of the docker image is installed on the Field Friend by syncing the code as described below and then running
`./docker.sh uppull` +4. Optional: ensure the correct docker containers are loaded on startup by running
`./docker.sh stopall && ./docker.sh uppull && ./docker.sh install` +5. Optional: update the [Lizard](https://lizard.dev) microcontroller firmware on your Robot Brain by accessing the Field Friend web interface and navigating to the "Developer" options + +### Deploy and Change Code + +1. go to your local `field_friend` folder and start the [LiveSync](https://github.com/zauberzeug/livesync) script:
+ `./sync.py ` +2. this will deploy your local code to the Field Friend +3. as long as [LiveSync](https://github.com/zauberzeug/livesync) is active, all code change are automatically pushed to the machine +4. any code changes will automatically trigger a reload on the Field Friend + +### Update RoSys and NiceGUI + +To utilize personal versions of RoSys and NiceGUI instead of the default ones provided in the docker image, +modify the `sync.py` file by uncommenting the specific folders. + +### Logs + +You can see the current log with + +```bash +./docker.sh l rosys +``` + +The history of logs can be seen with + +```bash +less -r ~/.rosys/debug.log +``` diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..2c5a1783 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,18 @@ +# About + +**Zauberzeug Field Friend** is an autonomous, mobile, and AI-driven agricultural robot developed by [Zauberzeug GmbH](https://zauberzeug.com). +The robot is specifically designed for autonomous actions, combining lightness, flexibility, and robustness +to efficiently handle a variety of outdoor tasks. +Equipped with advanced sensor technologies and camera systems, +the Field Friend can precisely determine its position, follow crop lines and detect various kinds of plants. +With it's modular design, the Field Friend can be extended with various tools and sensors to fit the specific needs of the use case. + +## Features + +- The Open Source software encourages you to modify and enhance the behavior and adapt it to your specific needs. +- The Modular Design allows equipping with tools from Zauberzeug as well as third-party solutions or your own developments. +- Advanced Sensing and Autonomy-Algorithms allows autonomous navigation and obstacle avoidance. +- Full control via web interface remote and locally via WiFi. +- Manual steering with touch-joystick and keyboard or App. +- A combined camera/motor calibration for real world coordinate system (unit: meters) +- ... diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css new file mode 100644 index 00000000..6d595d73 --- /dev/null +++ b/docs/stylesheets/extra.css @@ -0,0 +1,22 @@ +:root { + --md-primary-fg-color: #6e93d6; + --md-default-fg-color: #3a3e42; + --md-accent-fg-color: #53b689; +} + +:root > * { + --md-code-hl-color: #d8e5fa; +} + +h2.doc-heading { + border-bottom: 1pt solid lightgray; +} + +img { + width: 70%; + display: block; + margin-left: auto; + margin-right: auto; + border-radius: 5px; + box-shadow: rgba(0, 0, 0, 0.25) 0 5px 10px; +} diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md new file mode 100644 index 00000000..8169f779 --- /dev/null +++ b/docs/troubleshooting.md @@ -0,0 +1,18 @@ +# Troubleshooting + +## Asyncio Warning + +While running RoSys you may see warnings similar to this one: + +``` +2021-10-31 15:08:04.040 [WARNING] asyncio: Executing wait_for=<_GatheringFuture pending cb=[()] created at /usr/local/lib/python3.9/asyncio/tasks.py:705> created at /usr/local/lib/python3.9/site-packages/justpy/justpy.py:261> took 0.238 seconds +``` + +This means some coroutine is clogging the event loop for too long. +In the above example it is a whopping 238 ms in which no other actor can do anything. +This is an eternity when machine communication is expected to happen about every 10 ms. +The warning also provides a (not so readable) hint where the time is consumed. + +The example above is one of the more frequent scenarios. +It means some code inside a user interaction event handler (e.g. `handle_event()` in `justpy.py`) is blocking. +Try to figure out which UI event code is responsible by commenting out parts of your logic and try to reproduce the warning systematically. diff --git a/field_friend/automations/__init__.py b/field_friend/automations/__init__.py index 0c7e5ccf..e1b92156 100644 --- a/field_friend/automations/__init__.py +++ b/field_friend/automations/__init__.py @@ -29,6 +29,7 @@ 'Plant', 'Puncher', 'Row', + 'KpiProvider', 'find_sequence', 'Weeding', 'BatteryWatcher', diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 00000000..2a6bd029 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,56 @@ +site_name: Field Friend Software Documentation +site_url: https://docs.feldfreund.de/ +nav: + - index.md + - getting_started.md + - Features: + - features/user_interface.md # NOTE: shown when clicking on "Examples" folder + - features/user_interface.md + - features/field_planner.md + - Module Reference: reference/ + - contributing.md + - troubleshooting.md +repo_url: https://github.com/zauberzeug/field_friend +edit_uri: edit/main/docs/ +theme: + name: material + font: + text: Source Sans Pro + features: + - content.code.annotate +extra_css: + - stylesheets/extra.css +markdown_extensions: + - toc: + permalink: True + - admonition + - def_list + - mdx_include: + base_path: docs + - pymdownx.highlight + - pymdownx.inlinehilite + - pymdownx.superfences + - pymdownx.snippets + - attr_list + - footnotes +plugins: + - search + - gen-files: + scripts: + - docs/generate_reference.py + - literate-nav: + nav_file: SUMMARY.md + - section-index + - mkdocstrings: + default_handler: python + handlers: + python: + options: + show_root_heading: true + show_root_full_path: false + show_source: false + show_signature_annotations: true + merge_init_into_class: true + separate_signature: true +watch: + - field_friend diff --git a/mkdocs_requirements.txt b/mkdocs_requirements.txt new file mode 100644 index 00000000..935e46c9 --- /dev/null +++ b/mkdocs_requirements.txt @@ -0,0 +1,10 @@ +mkdocs==1.5.3 +mkdocs-material +mdx-include +mkdocs-pymdownx-material-extras +mdx-footnotes2 +mkdocstrings-python +mkdocs-gen-files +mkdocs-literate-nav +mkdocs-section-index +black \ No newline at end of file