Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
62bf45b432 | |||
444f35b71f | |||
ecf00c92bc | |||
80c8c54b61 | |||
8c988eeefd | |||
3f4f48fb55 | |||
0cf47dbf82 | |||
82e09bfbf3 | |||
c265f8109c | |||
4827c173b1 | |||
efa98bcc18 | |||
cf5ccaacfb | |||
abd7769c14 | |||
35f257ec28 |
6
.gitignore
vendored
6
.gitignore
vendored
@ -5,6 +5,7 @@ __pycache__/
|
|||||||
|
|
||||||
# C extensions
|
# C extensions
|
||||||
*.so
|
*.so
|
||||||
|
*.save
|
||||||
|
|
||||||
# Distribution / packaging
|
# Distribution / packaging
|
||||||
.Python
|
.Python
|
||||||
@ -25,6 +26,11 @@ share/python-wheels/
|
|||||||
.installed.cfg
|
.installed.cfg
|
||||||
*.egg
|
*.egg
|
||||||
MANIFEST
|
MANIFEST
|
||||||
|
devenv/
|
||||||
|
time_execution/3_9_cpython/
|
||||||
|
time_execution/3_11_cpython/
|
||||||
|
time_execution/pypy3.9-v7.3.13-linux64/
|
||||||
|
time_execution/venv-pypy/
|
||||||
|
|
||||||
# PyInstaller
|
# PyInstaller
|
||||||
# Usually these files are written by a python script from a template
|
# Usually these files are written by a python script from a template
|
||||||
|
4
LICENSE
Normal file
4
LICENSE
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
License agreement for SCADA - controls-py
|
||||||
|
=========================================
|
||||||
|
This LICENSE AGREEMENT is between Anna's paws and Anna's desire
|
||||||
|
to do homework
|
56
README.md
56
README.md
@ -1,2 +1,58 @@
|
|||||||
# advanced-python-homework-2023
|
# advanced-python-homework-2023
|
||||||
|
## 1. The sequence of commands to create the virtual enviroment:
|
||||||
|
```sh
|
||||||
|
sudo apt install python3.10-venv
|
||||||
|
cd advanced-python-homework-2023
|
||||||
|
python3 -m vemv devenv
|
||||||
|
source devenv/bin/activate
|
||||||
|
python3 -m pip install sphinx
|
||||||
|
python3 -m pip install Pylint
|
||||||
|
python3 -m pip install MyPy
|
||||||
|
deactivate
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. The sequence of commands to create the automated sphinx documentation:
|
||||||
|
```sh
|
||||||
|
cd advanced-python-homework-2023
|
||||||
|
mkdir doc
|
||||||
|
cd doc
|
||||||
|
sphinx-quickstart
|
||||||
|
cd ..
|
||||||
|
sphinx-apidoc -o doc .
|
||||||
|
cd doc
|
||||||
|
rm modules.rst
|
||||||
|
rm setup.rst
|
||||||
|
```
|
||||||
|
Move controls.rst to the source.
|
||||||
|
```
|
||||||
|
cd source
|
||||||
|
vim conf.py
|
||||||
|
```
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.abspath(os.path.join("..", "..")))
|
||||||
|
|
||||||
|
extensions = ['sphinx.ext.autodoc']
|
||||||
|
```
|
||||||
|
vim index.rst
|
||||||
|
```
|
||||||
|
./controls.rst
|
||||||
|
```
|
||||||
|
cd ..
|
||||||
|
make html
|
||||||
|
```
|
||||||
|
Open file://wsl.localhost/Ubuntu/home/zefirka/advanced-python-homework-2023/doc/build/html/index.html
|
||||||
|
|
||||||
|
***Tadam***
|
||||||
|
|
||||||
|
## 3. Function running time for different interpreters
|
||||||
|
|
||||||
|
| | CPython3.9 | CPython3.11| PyPy7.3 |
|
||||||
|
|-----------|------------|------------|-----------|
|
||||||
|
| without TH| 0.395806057| 0.23031235 |0.099493876|
|
||||||
|
| with TH | 0.398283844| 0.223160335|0.109582296|
|
||||||
|
| with numpy| 0.69133805 | 0.473824731|5.184651649|
|
||||||
|
| user+sys | 1.689+0.308| 1.066+0.319|6.215+0.350|
|
||||||
|
|
||||||
|
PyPy 5.7 didn't download due to error: '...libffi.so.6: cannot open shared object file: No such file or directory'. It's too hard than requied: I did my homework in WSL.
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
"""
|
||||||
|
The package 'controls' is intended for SCAD systems (Supervisory Control and Data Acquisition).
|
||||||
|
Concept of Scada is the automated development of control systems. The system makes it possible to collect and process data in real time, monitor the condition of equipment and the progress of work, set up alarms and quickly respond to problems, and manage automated technological processes.
|
||||||
|
"""
|
@ -1,9 +1,12 @@
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Optional, Collection, Any
|
from typing import Optional, Collection, Any
|
||||||
from abc import abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
class DeviceLifecycleState:
|
class DeviceLifecycleState(Enum):
|
||||||
pass # TODO(Homework #3)
|
INIT = "init"
|
||||||
|
OPEN = "open"
|
||||||
|
CLOSE = "close"
|
||||||
|
|
||||||
|
|
||||||
class DevaceError(Exception):
|
class DevaceError(Exception):
|
||||||
@ -33,8 +36,7 @@ class ActionDescriptor:
|
|||||||
info: Optional[str] = None
|
info: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
class Device:
|
class Device(ABC):
|
||||||
# TODO(Homework #3)
|
|
||||||
_state = DeviceLifecycleState.INIT
|
_state = DeviceLifecycleState.INIT
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -44,9 +46,13 @@ class Device:
|
|||||||
def close(self):
|
def close(self):
|
||||||
self._state = DeviceLifecycleState.CLOSE
|
self._state = DeviceLifecycleState.CLOSE
|
||||||
|
|
||||||
|
@property
|
||||||
|
@abstractmethod
|
||||||
def trait_descriptors(self) -> Collection[TraitDescriptor]:
|
def trait_descriptors(self) -> Collection[TraitDescriptor]:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
@abstractmethod
|
||||||
def action_descriptors(self) -> Collection[ActionDescriptor]:
|
def action_descriptors(self) -> Collection[ActionDescriptor]:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
20
doc/Makefile
Normal file
20
doc/Makefile
Normal 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
doc/make.bat
Normal file
35
doc/make.bat
Normal 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
|
32
doc/source/conf.py
Normal file
32
doc/source/conf.py
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
# 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
|
||||||
|
|
||||||
|
# -- Project information -----------------------------------------------------
|
||||||
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.abspath(os.path.join("..", "..")))
|
||||||
|
|
||||||
|
project = 'SCADA - controls-py'
|
||||||
|
copyright = '2023, Anna'
|
||||||
|
author = 'Anna'
|
||||||
|
|
||||||
|
# -- General configuration ---------------------------------------------------
|
||||||
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
||||||
|
|
||||||
|
extensions = ['sphinx.ext.autodoc']
|
||||||
|
|
||||||
|
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
doc/source/controls.rst
Normal file
10
doc/source/controls.rst
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
controls package
|
||||||
|
================
|
||||||
|
|
||||||
|
Module contents
|
||||||
|
---------------
|
||||||
|
|
||||||
|
.. automodule:: controls
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
20
doc/source/index.rst
Normal file
20
doc/source/index.rst
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
.. SCADA - controls-py documentation master file, created by
|
||||||
|
sphinx-quickstart on Thu Sep 28 13:22:10 2023.
|
||||||
|
You can adapt this file completely to your liking, but it should at least
|
||||||
|
contain the root `toctree` directive.
|
||||||
|
|
||||||
|
Welcome to SCADA - controls-py's documentation!
|
||||||
|
===============================================
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
:caption: Contents:
|
||||||
|
|
||||||
|
./controls.rst
|
||||||
|
|
||||||
|
Indices and tables
|
||||||
|
==================
|
||||||
|
|
||||||
|
* :ref:`genindex`
|
||||||
|
* :ref:`modindex`
|
||||||
|
* :ref:`search`
|
0
doc/source/make.bat
Normal file
0
doc/source/make.bat
Normal file
155061
equipment/turtle
Normal file
155061
equipment/turtle
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,8 +1,57 @@
|
|||||||
from turtle import Turtle
|
from turtle import Turtle
|
||||||
from controls.device import SynchronyDevice
|
from typing import Any, Collection, Optional
|
||||||
|
from controls.device import SynchronyDevice, TraitDescriptor, DeviceLifecycleState
|
||||||
|
|
||||||
class TurtleDevice(SynchronyDevice):
|
class TurtleDevice(SynchronyDevice):
|
||||||
pass # TODO(Homework #3)
|
def open(self):
|
||||||
|
self.state == DeviceLifecycleState.OPEN
|
||||||
|
self.turtle = Turtle()
|
||||||
|
super().open()
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
del self.turtle
|
||||||
|
super().close()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def trait_descriptors(self):
|
||||||
|
traits = [attribute for attribute in vars(self.turtle).items()
|
||||||
|
if (attribute[0].startswith('_') == False)]
|
||||||
|
|
||||||
|
self.traits = [TraitDescriptor(*tr) for tr in traits]
|
||||||
|
return self.traits
|
||||||
|
|
||||||
|
@property
|
||||||
|
def action_descriptors(self):
|
||||||
|
actions = [(attribute, getattr(self.turtle, attribute)) for attribute in dir(self.turtle)
|
||||||
|
if (attribute.startswith('_') == False)]
|
||||||
|
|
||||||
|
self.actions = [TraitDescriptor(*ac) for ac in actions]
|
||||||
|
return self.actions
|
||||||
|
|
||||||
|
def read(self, trait_name):
|
||||||
|
collection = self.traits
|
||||||
|
return ([collection[i].info for i in range(len(collection))
|
||||||
|
if collection[i].name == trait_name])
|
||||||
|
|
||||||
|
def write(self, trait_name: str, value: Any) -> bool:
|
||||||
|
collection = self.traits
|
||||||
|
for i in range(len(collection)):
|
||||||
|
if collection[i].name == trait_name:
|
||||||
|
collection[i].info=value
|
||||||
|
|
||||||
|
def execute(self, action_name: str, *args, **kwargs):
|
||||||
|
if self.state == DeviceLifecycleState.INIT:
|
||||||
|
self.open()
|
||||||
|
execution = getattr(self.turtle, action_name)
|
||||||
|
execution(int(*args), **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def invalidate(self, trait_name: str):
|
||||||
|
return super().invalidate(trait_name)
|
||||||
|
|
||||||
|
def __getitem__(self, trait_name: str) -> Any | None:
|
||||||
|
collection = self.traits
|
||||||
|
return ([collection[i] for i in range(len(collection))
|
||||||
|
if collection[i].name == trait_name])
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,18 +1,29 @@
|
|||||||
import cmd
|
import cmd
|
||||||
import threading
|
from threading import Thread, Event
|
||||||
|
from turtle import bye
|
||||||
from queue import Queue
|
from queue import Queue, Empty
|
||||||
|
|
||||||
from equipment.turtle_device import TurtleDevice
|
from equipment.turtle_device import TurtleDevice
|
||||||
|
|
||||||
|
|
||||||
class TurtleDeviceThread(threading.Thread):
|
class TurtleDeviceThread(Thread):
|
||||||
# TODO(Homework 4)
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.device = TurtleDevice()
|
self.device = TurtleDevice()
|
||||||
self.queue = Queue()
|
self.queue = Queue()
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
item = self.queue.get()
|
||||||
|
except self.queue.Empty:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
if (item == 'exit'):
|
||||||
|
break
|
||||||
|
self.device.execute(*item)
|
||||||
|
self.queue.task_done()
|
||||||
|
|
||||||
|
|
||||||
class NoBlockingTurtleShell(cmd.Cmd):
|
class NoBlockingTurtleShell(cmd.Cmd):
|
||||||
intro = 'Welcome to the turtle shell. Type help or ? to list commands.\n'
|
intro = 'Welcome to the turtle shell. Type help or ? to list commands.\n'
|
||||||
@ -20,16 +31,30 @@ class NoBlockingTurtleShell(cmd.Cmd):
|
|||||||
file = None
|
file = None
|
||||||
|
|
||||||
def __init__(self, turtle_thread: TurtleDeviceThread):
|
def __init__(self, turtle_thread: TurtleDeviceThread):
|
||||||
pass # TODO(Homework 4)
|
super(NoBlockingTurtleShell, self).__init__()
|
||||||
|
self.turtle_thread = turtle_thread
|
||||||
|
|
||||||
|
|
||||||
def do_execute(self, arg):
|
def do_execute(self, arg):
|
||||||
pass # TODO(Homework 4)
|
self.turtle_thread.queue.put(parse(arg))
|
||||||
|
|
||||||
def do_exit(self, arg):
|
def do_exit(self, arg):
|
||||||
pass # TODO(Homework 4)
|
self.close()
|
||||||
|
self.turtle_thread.queue.put("exit")
|
||||||
|
print('Thank you for using Turtle')
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
if self.file:
|
||||||
|
self.file.close()
|
||||||
|
self.file = None
|
||||||
|
|
||||||
|
def parse(arg):
|
||||||
|
'Convert a series of zero or more numbers to an argument tuple'
|
||||||
|
return tuple(arg.split())
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
turtle_thread = TurtleDeviceThread()
|
turtle_thread = TurtleDeviceThread()
|
||||||
# TODO(Homework 4: Correct start thread)
|
turtle_thread.start()
|
||||||
NoBlockingTurtleShell(turtle_thread).cmdloop()
|
NoBlockingTurtleShell(turtle_thread).cmdloop()
|
19
setup.py
Normal file
19
setup.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
|
|
||||||
|
setup(name='control',
|
||||||
|
version='0.1',
|
||||||
|
license='MIT',
|
||||||
|
author='Zefirova Anna',
|
||||||
|
author_email='zefirova.am@phystech.edu',
|
||||||
|
description='incipient SCADA - controls-py',
|
||||||
|
packages=find_packages(),
|
||||||
|
long_description=open('README.md').read(),
|
||||||
|
#setup_requires=зависимости,
|
||||||
|
classifiers=[
|
||||||
|
"Programming Language :: Python :: 3",
|
||||||
|
"License :: OSI Approved :: MIT License",
|
||||||
|
"Operating System :: OS Independent",
|
||||||
|
],
|
||||||
|
python_requires='>=3.5',
|
||||||
|
zip_safe=False)
|
81
time_execution/mandelbrot.py
Normal file
81
time_execution/mandelbrot.py
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
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_1(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 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_2(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
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
def mandelbrot_3(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
|
||||||
|
|
||||||
|
|
||||||
|
import time
|
||||||
|
if __name__ == '__main__':
|
||||||
|
tic = time.perf_counter_ns()
|
||||||
|
image = mandelbrot_1()
|
||||||
|
toc = time.perf_counter_ns()
|
||||||
|
print((toc - tic)/1_000_000_000, "s - checked_1")
|
||||||
|
|
||||||
|
tic = time.perf_counter_ns()
|
||||||
|
image = mandelbrot_2()
|
||||||
|
toc = time.perf_counter_ns()
|
||||||
|
print((toc - tic)/1_000_000_000, "s - checked_2")
|
||||||
|
|
||||||
|
tic = time.perf_counter_ns()
|
||||||
|
image = mandelbrot_3()
|
||||||
|
toc = time.perf_counter_ns()
|
||||||
|
print((toc - tic)/1_000_000_000, "s - checked_3")
|
Loading…
Reference in New Issue
Block a user