Compare commits

...

30 Commits
main ... main

Author SHA1 Message Date
ilia
e35ac53eda fix after review 2023-12-02 10:48:19 +03:00
ilia
c524ddeb1a done turtle thread 2023-11-16 21:21:10 +03:00
ilia
6204c8e721 noblocking progress 2023-11-10 15:05:58 +03:00
ilia
4675fa33a6 structure 2023-11-08 19:11:39 +03:00
ilia
0c8fb14366 edit readme 2023-11-07 22:20:51 +03:00
ilia
b1e76ba618 edit readme 2023-11-07 22:19:27 +03:00
ilia
c6a153b683 start hw4 2023-11-07 09:22:13 +03:00
e5d0db75c2 Merge remote-tracking branch 'upstream/main' into main 2023-11-07 09:20:34 +03:00
ilia
e6a2f6eac7 fill methods 2023-10-26 17:28:48 +03:00
ilia
b0e095e2e7 trait and action descriptors 2023-10-24 10:35:32 +03:00
ilia
2ed1f00421 fix get_doc 2023-10-24 10:34:02 +03:00
ilia
593ad6b45c rewrite get_descriptors to use __dict__ 2023-10-24 10:28:38 +03:00
ilia
08cbcec767 add get_descriptors with using "return" and "set" 2023-10-24 10:13:31 +03:00
ilia
9cf90c7f18 add execute 2023-10-24 10:11:30 +03:00
ilia
48b18048c8 start doing turtle task 2023-10-23 13:44:07 +03:00
ilia
5c5d61ec03 add course materials 2023-10-23 08:34:23 +03:00
ilia
4c9c697135 add lectures 2023-10-23 08:32:55 +03:00
a6607b4526 Merge remote-tracking branch 'upstream/main' into main 2023-10-22 15:25:44 +03:00
ilia
1ab5673232 resize image in readme 2023-10-10 18:49:55 +03:00
ilia
3bfda92204 remove old files 2023-10-10 18:43:49 +03:00
ilia
e4f2bee355 hw2 on interpreters done 2023-10-10 18:42:30 +03:00
ilia
f26a16dc8c fix interpreters readme 2023-10-06 14:54:25 +03:00
ilia
805711e614 fix interpreters readme 2023-10-06 14:18:24 +03:00
ilia
d2991376d2 fix interpreters readme 2023-10-06 13:21:31 +03:00
ilia
6f9170a891 edit interpreters readme 2023-10-06 13:19:11 +03:00
ilia
f3cf3b975e hw2 interpreters 2023-10-06 13:14:14 +03:00
ilia
1fbb754bde fix readme and pyproject 2023-09-29 17:46:34 +03:00
ilia
a03f5c41d9 fix structure 2023-09-29 07:18:26 +03:00
ilia
0c48a5fc16 edit readme 2023-09-28 22:04:57 +03:00
ilia
9cbb7ba909 homework done 2023-09-28 21:47:21 +03:00
47 changed files with 3533 additions and 52 deletions

2
.gitignore vendored
View File

@ -1,3 +1,5 @@
*.code-workspace
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]

9
Licence.txt Normal file
View File

@ -0,0 +1,9 @@
I am providing code and resources in this repository to you under an open source
license. Because this is my personal repository, the license you receive to my
code and resources is from me and not my employer.
Copyright 2023 Ilia Tambovtsev
Creative Commons Attribution 4.0 International License (CC BY 4.0)
http://creativecommons.org/licenses/by/4.0/

View File

@ -1,2 +1,54 @@
# advanced-python-homework-2023
## Student:
[Ilia Tambovtsev](https://t.me/hyperpopa)
## Materials
- [Homework](https://sciprogcentre.github.io/green-courses/advanced_python.html)
## Controls (1st homework)
### Venv creation:
```
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
```
### Documentation
#### Build
```
cd docs
make.bat
```
#### From scratch
1. run sphinx initialization: `sphinx-quickstart docs`
2. add project packages: `sphinx-apidoc -o docs/source .`
3. edit `conf.py`
```py
import sys
import os
sys.path.insert(0, os.path.abspath("../../"))
extensions = [
'sphinx.ext.duration',
'sphinx.ext.doctest',
'sphinx.ext.autodoc',
'sphinx.ext.autosummary',
]
```
4. add `modules` after `Contents:` in `index.rst`
5. run `make.bat`
## Interpreters (2nd homework)
- [assignment](https://sciprogcentre.github.io/green-courses/pages/advanced_python/2023/hw/02.html)
- [solution](./interpreters)
## Turtle Device (3rd homework)
- [assignment](https://sciprogcentre.github.io/green-courses/pages/advanced_python/2023/hw/03.html)
- [solution](./turtle)
## Turtle Thread (4th homework)
- [assignment](https://sciprogcentre.github.io/green-courses/pages/advanced_python/2023/hw/04.html)
- [solution](./turtle)

File diff suppressed because one or more lines are too long

20
docs/Makefile Normal file
View File

@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = source
BUILDDIR = build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

35
docs/make.bat Normal file
View File

@ -0,0 +1,35 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=source
set BUILDDIR=build
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.https://www.sphinx-doc.org/
exit /b 1
)
if "%1" == "" goto help
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
:end
popd

36
docs/source/conf.py Normal file
View File

@ -0,0 +1,36 @@
# Configuration file for the Sphinx documentation builder.
#
# For the full list of built-in configuration values, see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
import sys
import os
sys.path.insert(0, os.path.abspath("../../"))
# -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
project = 'Advanced python homework'
copyright = '2023, Ilia'
author = 'Ilia'
# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
extensions = [
'sphinx.ext.duration',
'sphinx.ext.doctest',
'sphinx.ext.autodoc',
'sphinx.ext.autosummary',
]
templates_path = ['_templates']
exclude_patterns = []
# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
html_theme = 'alabaster'
html_static_path = ['_static']

10
docs/source/controls.rst Normal file
View File

@ -0,0 +1,10 @@
controls package
================
Module contents
---------------
.. automodule:: controls
:members:
:undoc-members:
:show-inheritance:

20
docs/source/index.rst Normal file
View File

@ -0,0 +1,20 @@
.. Advanced python homework documentation master file, created by
sphinx-quickstart on Thu Sep 28 21:33:05 2023.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to Advanced python homework's documentation!
====================================================
.. toctree::
:maxdepth: 2
:caption: Contents:
modules
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

7
docs/source/modules.rst Normal file
View File

@ -0,0 +1,7 @@
advanced-python-homework-2023
=============================
.. toctree::
:maxdepth: 4
controls

View File

@ -1,8 +0,0 @@
from turtle import Turtle
from controls.device import SynchronyDevice
class TurtleDevice(SynchronyDevice):
pass # TODO(Homework #3)

162
interpreters/.gitignore vendored Normal file
View File

@ -0,0 +1,162 @@
builds
# Byte-compiled / optimized / DLL files
__pycache__/
.jython_cache
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

113
interpreters/README.md Normal file
View File

@ -0,0 +1,113 @@
# Interpreters
## Results
<img src="result.png" alt="drawing" width="650"/>
<!-- ![result.png](result.png) -->
### Observations
- Functions w/o Numpy in PyPy run **much faster**. JIT compilation optimizes the function's code and runs compiled versions of them $\Rightarrow$ improvement in time
- Entire file evaluation for PyPy takes **a bit longer**. This may be a drawback of JIT optimization
- [PyPy with Numpy is slow](https://stackoverflow.com/questions/42536308/why-is-pypy-slower-for-adding-numpy-arrays). Reason:
- Numpy is written in C
- PyPy's JIT compilation is not compatible with C
- Extra conversion is required (takes time)
- Numpy functions don't give a good improvement in perfomance. Possibly because the code uses classic Python loops instead of numpy vectorisation.
## Tested interpreters
- CPython 3.9
- CPython 3.11
- PyPy 3.9 latest
- Pyodide (did not test full file because it only runs in a browser)
- Xeus (again no full file)
## Failed to test
- PyPy 3.9 v5.7
- couldn't install numpy `Ignoring ensurepip failure: pip 9.0.1 requires SSL/TLS`
- error when testing with types:
```python
invalid syntax --> image: list[list[int]] = [[0 for i in range(qpoints)] for j in range(ppoints)]
```
- Jython (dependencies issues)
## Testing Code
- Tested functions code is in `./test_funcs` package
- File for testing function times: `test_func_only.py`
- Files for entire evaluation testing: `test_full_{func}.py`
- Bash script: `test_full.sh`
- Analysis file: `analyse.ipynb`
### Realisations of mandelbrot functions tested:
```python
def linspace(start, stop, n):
if n == 1:
yield stop
return
h = (stop - start) / (n - 1)
for i in range(n):
yield start + h * i
def mandelbrot_with_types(
pmin: float = -2.5,
pmax: float = 1.5,
qmin: float = -2,
qmax: float = 2,
ppoints: int = 200,
qpoints: int = 200,
max_iterations: int = 300,
infinity_border: float = 100) -> list[list[int]]:
image: list[list[int]] = [[0 for i in range(qpoints)] for j in range(ppoints)]
for ip, p in enumerate(linspace(pmin, pmax, ppoints)):
for iq, q in enumerate(linspace(qmin, qmax, qpoints)):
c: complex = p + 1j * q
z: complex = 0
for k in range(max_iterations):
z = z ** 2 + c
if abs(z) > infinity_border:
image[ip][iq] = 1
break
return image
def mandelbrot_no_types(
pmin=-2.5,
pmax=1.5,
qmin=-2,
qmax=2,
ppoints=200,
qpoints=200,
max_iterations=300,
infinity_border=100):
image = [[0 for i in range(qpoints)] for j in range(ppoints)]
for ip, p in enumerate(linspace(pmin, pmax, ppoints)):
for iq, q in enumerate(linspace(qmin, qmax, qpoints)):
c = p + 1j * q
z = 0
for k in range(max_iterations):
z = z ** 2 + c
if abs(z) > infinity_border:
image[ip][iq] = 1
break
return image
def mandelbrot_np(
pmin=-2.5,
pmax=1.5,
qmin=-2,
qmax=2,
ppoints=200,
qpoints=200,
max_iterations=300,
infinity_border=100):
image = np.zeros((ppoints, qpoints))
for ip, p in enumerate(np.linspace(pmin, pmax, ppoints)):
for iq, q in enumerate(np.linspace(qmin, qmax, qpoints)):
c = p + 1j * q
z = 0
for k in range(max_iterations):
z = z ** 2 + c
if abs(z) > infinity_border:
image[ip, iq] = 1
break
return image
```

1620
interpreters/analyse.ipynb Normal file

File diff suppressed because it is too large Load Diff

BIN
interpreters/result.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 KiB

View File

@ -0,0 +1,6 @@
{
"interpreter": "CPython-3.11",
"time_with_types": 0.8206779,
"time_no_types": 0.8562159,
"time_np": 1.2020039
}

View File

@ -0,0 +1,3 @@
1.630
1.166
1.675

View File

@ -0,0 +1,6 @@
{
"interpreter": "CPython-3.9",
"time_with_types": 1.445288,
"time_no_types": 1.4769202,
"time_np": 1.4072699
}

View File

@ -0,0 +1,3 @@
2.242
1.502
1.986

View File

@ -0,0 +1,6 @@
{
"interpreter": "PyPy-3.9-v7.3.13",
"time_with_types": 0.099363702,
"time_no_types": 0.109923603,
"time_np": 3.676712179
}

View File

@ -0,0 +1,3 @@
5.319
3.638
14.639

View File

@ -0,0 +1,6 @@
{
"interpreter": "piodide",
"time_with_types": 1.785,
"time_no_types": 1.681,
"time_np": 2.833
}

View File

@ -0,0 +1,6 @@
{
"interpreter": "Xeus",
"time_with_types": 1.1555191,
"time_no_types": 1.0386694,
"time_np": 1.2727932
}

12
interpreters/test_full.sh Normal file
View File

@ -0,0 +1,12 @@
#!/bin/bash
interpreterCmd=$1;
interpreterTitle=$2;
TIMEFORMAT=%R
{
time $interpreterCmd test_full_with_types.py &&
time $interpreterCmd test_full_no_types.py &&
time $interpreterCmd test_full_np.py;
} 2> results/${interpreterTitle}_full.txt;
$interpreterCmd test_func_only.py $interpreterTitle;

View File

@ -0,0 +1,3 @@
from test_funcs.pure_no_types import mandelbrot_no_types
mandelbrot_no_types()

View File

@ -0,0 +1,3 @@
from test_funcs.np_extended import mandelbrot_np
mandelbrot_np()

View File

@ -0,0 +1,3 @@
from test_funcs.pure_with_types import mandelbrot_with_types
mandelbrot_with_types()

View File

@ -0,0 +1,49 @@
import time
import json
import sys, os
# sys.path.append(os.path.abspath("./test_files"))
from test_funcs.pure_with_types import mandelbrot_with_types
from test_funcs.pure_no_types import mandelbrot_no_types
# from test_funcs.np_extended import mandelbrot_np
def test(function):
tic = time.perf_counter_ns()
image = function()
toc = time.perf_counter_ns()
return (toc - tic) / 1e9
def try_func(function):
try:
time = test(function)
except Exception as e:
raise e
return time
def main(arg):
interpreter = arg[1]
time_with_types = "err"
time_no_types = "err"
time_np = "err"
time_with_types = try_func(mandelbrot_with_types)
time_no_types = try_func(mandelbrot_no_types)
# time_np = try_func(mandelbrot_np)
response = dict(
interpreter=interpreter,
time_with_types=time_with_types,
time_no_types=time_no_types,
# time_np=time_np
)
result_string = json.dumps(response, indent=4, ensure_ascii=False)
# f = open("results/{}.txt".format(interpreter), "w", encoding="utf8")
# print(result_string, file=f)
# f.close()
print(result_string)
if __name__ == "__main__":
main(sys.argv)

View File

@ -0,0 +1,7 @@
def linspace(start, stop, n):
if n == 1:
yield stop
return
h = (stop - start) / (n - 1)
for i in range(n):
yield start + h * i

View File

@ -0,0 +1,24 @@
import numpy as np
def mandelbrot_np(
pmin=-2.5,
pmax=1.5,
qmin=-2,
qmax=2,
ppoints=200,
qpoints=200,
max_iterations=300,
infinity_border=100):
image = np.zeros((ppoints, qpoints))
for ip, p in enumerate(np.linspace(pmin, pmax, ppoints)):
for iq, q in enumerate(np.linspace(qmin, qmax, qpoints)):
c = p + 1j * q
z = 0
for k in range(max_iterations):
z = z ** 2 + c
if abs(z) > infinity_border:
image[ip, iq] = 1
break
return image

View File

@ -0,0 +1,22 @@
from .linspace import linspace
def mandelbrot_no_types(
pmin=-2.5,
pmax=1.5,
qmin=-2,
qmax=2,
ppoints=200,
qpoints=200,
max_iterations=300,
infinity_border=100):
image = [[0 for i in range(qpoints)] for j in range(ppoints)]
for ip, p in enumerate(linspace(pmin, pmax, ppoints)):
for iq, q in enumerate(linspace(qmin, qmax, qpoints)):
c = p + 1j * q
z = 0
for k in range(max_iterations):
z = z ** 2 + c
if abs(z) > infinity_border:
image[ip][iq] = 1
break
return image

View File

@ -0,0 +1,23 @@
from .linspace import linspace
def mandelbrot_with_types(
pmin: float = -2.5,
pmax: float = 1.5,
qmin: float = -2,
qmax: float = 2,
ppoints: int = 200,
qpoints: int = 200,
max_iterations: int = 300,
infinity_border: float = 100) -> list[list[int]]:
image: list[list[int]] = [[0 for i in range(qpoints)] for j in range(ppoints)]
for ip, p in enumerate(linspace(pmin, pmax, ppoints)):
for iq, q in enumerate(linspace(qmin, qmax, qpoints)):
c: complex = p + 1j * q
z: complex = 0
for k in range(max_iterations):
z = z ** 2 + c
if abs(z) > infinity_border:
image[ip][iq] = 1
break
return image

View File

@ -1,35 +0,0 @@
import cmd
import threading
from queue import Queue
from equipment.turtle_device import TurtleDevice
class TurtleDeviceThread(threading.Thread):
# TODO(Homework 4)
def __init__(self):
super().__init__()
self.device = TurtleDevice()
self.queue = Queue()
class NoBlockingTurtleShell(cmd.Cmd):
intro = 'Welcome to the turtle shell. Type help or ? to list commands.\n'
prompt = '(turtle) '
file = None
def __init__(self, turtle_thread: TurtleDeviceThread):
pass # TODO(Homework 4)
def do_execute(self, arg):
pass # TODO(Homework 4)
def do_exit(self, arg):
pass # TODO(Homework 4)
if __name__ == '__main__':
turtle_thread = TurtleDeviceThread()
# TODO(Homework 4: Correct start thread)
NoBlockingTurtleShell(turtle_thread).cmdloop()

29
pyproject.toml Normal file
View File

@ -0,0 +1,29 @@
[project]
name = "spam"
version = "2023.0.0"
description = "Lovely Spam! Wonderful Spam!"
readme = "README.md"
requires-python = ">=3.7"
license = {file = "LICENSE.txt"}
keywords = ["SCADA", "mipt"]
authors = [
{email = "tambovtsev.io@phystech.edu"},
{name = "Ilia Tambovtsev"}
]
classifiers = [
"Programming Language :: Python"
]
dependencies = [
"numpy",
"scipy"
]
[project.optional-dependencies]
test = [
"pytest < 5.0.0",
]
[project.urls]
repository = "https://git.sciprog.center/tambovtsev.io/advanced-python-homework-2023"

3
requirements.txt Normal file
View File

@ -0,0 +1,3 @@
pylint
sphinx
mypy

1
turtle/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
test.ipynb

11
turtle/README.md Normal file
View File

@ -0,0 +1,11 @@
# NonBlocking Turtle Shell
## Problem statement
Create a [non blocking shell](https://sciprogcentre.github.io/green-courses/pages/advanced_python/2023/hw/04.html) for Python Turtle.
## Test
I added two shortcut methods for testing:
- `le` = `execute left 1000` - spinning by 1000$^\circ$
- `fw` = `execute forward 100` - go forward by 100px
`le` takes a lot of time. The shell is not locked while turtle is spinning and you
can enter other commands (e.g. `fw`, `le`). The turtle will do them all
step-by-step.

160
turtle/controls/.gitignore vendored Normal file
View File

@ -0,0 +1,160 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

View File

@ -0,0 +1,38 @@
"""SCADA (Supervisory Control and Data Acquisition) is a system used to
monitor and control industrial processes and operations. It is a combination of
hardware and software components that gather and analyze real-time data from
various sources, such as sensors, meters, and other devices, in order to
provide supervision, control, and visualization of the entire process.
Key features of a SCADA system include:
1. Data Acquisition: SCADA systems collect data in real-time from different
sources, such as sens
2. Graphical User Interface (GUI): SCADA systems provide a user-friendly
interface, typically in the form of a graphical display, allowing operators
to visualize the process, alarms, and data in a clear and intuitive manner.
3. Control and Automation: SCADA systems enable operators to remotely control
and automate various aspects of the industrial process, such as
opening/closing valves or adjusting setpoints, to optimize performance and
ensure safety.
4. Alarming and Event Notification: SCADA systems generate alarms and
notifications in response to abnormal conditions or events, such as
equipment failure or deviations from set parameters, allowing operators to
take appropriate actions.
5. Historical Data Logging and Reporting: SCADA systems store and archive
historical data, enabling long-term analysis, trend identification, and
reporting for performance evaluation, regulatory compliance, and
decision-making purposes.
6. Remote Access and Monitoring: SCADA systems often support remote access,
allowing authorized users to monitor and control the industrial process from
anywhere, enhancing flexibility, efficiency, and troubleshooting capability.
7. Security and Authentication: SCADA systems implement security measures, such
as authentication and user access controls, to protect against unauthorized
access, data breaches, and cyber threats.
"""

View File

@ -1,12 +1,16 @@
from dataclasses import dataclass
from typing import Optional, Collection, Any
from abc import abstractmethod
class DeviceLifecycleState:
pass # TODO(Homework #3)
from abc import ABC, abstractmethod, abstractproperty
from enum import Enum
class DevaceError(Exception):
class DeviceLifecycleState(Enum):
INIT = 0
OPEN = 1
CLOSE = 2
class DeviceError(Exception):
pass
@ -33,8 +37,7 @@ class ActionDescriptor:
info: Optional[str] = None
class Device:
# TODO(Homework #3)
class Device(ABC):
_state = DeviceLifecycleState.INIT
@property
@ -44,9 +47,11 @@ class Device:
def close(self):
self._state = DeviceLifecycleState.CLOSE
@abstractproperty
def trait_descriptors(self) -> Collection[TraitDescriptor]:
pass
@abstractproperty
def action_descriptors(self) -> Collection[ActionDescriptor]:
pass
@ -64,7 +69,7 @@ class SynchronyDevice(Device):
@abstractmethod
def execute(self, action_name: str, *args, **kwargs):
"""Execute action `action_name`, using `args` and `kwargs` as action argument."""
pass
raise DeviceError
@abstractmethod
def read(self, trait_name: str) -> Any:
@ -79,4 +84,4 @@ class SynchronyDevice(Device):
@abstractmethod
def invalidate(self, trait_name: str):
"""Invalidate logical state of trait `trait_name`"""
pass
pass

View File

@ -0,0 +1,80 @@
from turtle import Turtle
from typing import Optional, Collection, Any
from controls.device import *
import inspect
class TurtleDevice(SynchronyDevice):
def open(self):
self.turtle = Turtle()
self.descriptors = dict(traits=dict(), actions=dict())
super().open()
def close(self):
self.turtle.reset()
super().close()
def trait_descriptors(self) -> Collection[TraitDescriptor]:
return self.get_descriptors()["traits"]
def action_descriptors(self) -> Collection[ActionDescriptor]:
return self.get_descriptors()["actions"]
def __getitem__(self, trait_name: str) -> Optional[Any]:
"""Return logical state of trait `trait_name`."""
return self.descriptors[trait_name]
def read_descriptors(self, arg):
self.descriptors = self.get_descriptors()
return self.descriptors
def read(self, trait_name: str) -> Any:
"""Read physical state of trait `trait_name` from device."""
self.read_descriptors()
return self.descriptors[trait_name]
def write(self, trait_name: str, value: Any) -> bool:
"""Turtle traits are not writable"""
# trait_desc = self.trait_descriptors.get(trait_name)
# if trait_desc.writable:
# return getattr(self.turtle, trait_name)(value)
super().write()
def execute(self, action_name: str, *args, **kwargs):
"""Execute action `action_name`, using `args` and `kwargs` as action argument."""
action = self.get_descriptors()["actions"].get(action_name)
if action:
getattr(self.turtle, action_name)(*args, **kwargs)
return
else:
raise DeviceError("No such action: `{}`".format(action_name))
def invalidate(self, trait_name: str):
"""Invalidate logical state of trait `trait_name`"""
if self.trait_descriptors.get(trait_name):
self.trait_descriptors[trait_name] = None
raise DeviceError()
def get_descriptors(self):
descriptors = dict(actions=dict(), traits=dict())
for m_name, member in inspect.getmembers(Turtle):
if m_name.startswith("_"):
continue
if m_name.lower() != m_name:
continue
doc = inspect.getdoc(member)
if doc is None:
continue
if not inspect.isfunction(member):
descriptors["traits"][m_name] = TraitDescriptor(
m_name, doc, readable=True, writable=False
)
else:
sig = inspect.signature(member)
params_dict = dict(sig.parameters)
descriptors["actions"][m_name] = ActionDescriptor(
m_name, arguments=params_dict, info=doc
)
return descriptors

View File

@ -0,0 +1,73 @@
import cmd
import threading
from queue import Queue
from equipment.turtle_device import TurtleDevice
class TurtleDeviceThread(threading.Thread):
# TODO(Homework 4)
def __init__(self):
super().__init__()
self.device = TurtleDevice()
self.queue = Queue[tuple]()
self.device.open()
self.device.execute("speed", 1)
def run(self):
while True:
action, args, kwargs = self.queue.get()
if action == "exit":
self.queue.task_done()
break
self.device.execute(action, *args, **kwargs)
self.queue.task_done()
def add_task(self, action, *args, **kwargs):
self.queue.put((action, args, kwargs))
class NoBlockingTurtleShell(cmd.Cmd):
intro = "Welcome to the turtle shell. Type help or ? to list commands.\n"
prompt = "(turtle) "
file = None
def __init__(self, turtle_thread: TurtleDeviceThread):
super().__init__()
self.turtle_thread = turtle_thread
@property
def turtle_device(self):
return self.turtle_thread.device
def do_execute(self, arg):
command_and_args = arg.split()
if len(command_and_args) == 1:
self.turtle_thread.add_task(command)
return
command = command_and_args[0]
args = tuple(int(c) if c.isdecimal() else c for c in command_and_args[1:])
self.turtle_thread.add_task(command, *args)
def do_fw(self, arg):
self.do_execute("forward 100")
def do_le(self, arg):
self.do_execute("left 1000")
def do_exit(self, arg):
self.turtle_thread.add_task("exit")
return True
if __name__ == "__main__":
turtle_thread = TurtleDeviceThread()
# TODO(Homework 4: Correct start thread)
turtle_thread.daemon = True
thread_shell = threading.Thread(target=NoBlockingTurtleShell(turtle_thread).cmdloop)
thread_shell.start()
turtle_thread.run()
thread_shell.join()