PyAutomationIO 1.1.4__tar.gz → 1.1.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyautomationio-1.1.6/PKG-INFO +188 -0
- pyautomationio-1.1.6/PyAutomationIO.egg-info/PKG-INFO +188 -0
- pyautomationio-1.1.6/PyAutomationIO.egg-info/requires.txt +44 -0
- pyautomationio-1.1.6/README.md +131 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/alarms/__init__.py +73 -38
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/alarms/states.py +37 -12
- pyautomationio-1.1.6/automation/alarms/trigger.py +99 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/buffer.py +62 -11
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/core.py +1253 -151
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/dbmodels/alarms.py +93 -158
- pyautomationio-1.1.6/automation/dbmodels/core.py +86 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/dbmodels/events.py +49 -5
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/dbmodels/logs.py +48 -2
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/dbmodels/machines.py +49 -22
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/dbmodels/opcua.py +14 -31
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/dbmodels/opcua_server.py +42 -14
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/dbmodels/tags.py +97 -172
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/dbmodels/users.py +92 -6
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/logger/alarms.py +131 -32
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/logger/core.py +73 -45
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/logger/datalogger.py +166 -172
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/logger/events.py +64 -12
- pyautomationio-1.1.6/automation/logger/logdict.py +105 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/logger/logs.py +49 -12
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/logger/machines.py +75 -12
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/logger/opcua_server.py +44 -9
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/logger/users.py +40 -10
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/managers/alarms.py +135 -67
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/managers/db.py +60 -58
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/managers/opcua_client.py +109 -16
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/managers/state_machine.py +50 -69
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/models.py +30 -14
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/modules/alarms/resources/alarms.py +101 -49
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/modules/alarms/resources/summary.py +27 -15
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/modules/events/resources/events.py +29 -17
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/modules/events/resources/logs.py +32 -20
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/modules/settings/resources/settings.py +16 -9
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/modules/tags/resources/tags.py +65 -33
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/modules/users/resources/models/users.py +1 -1
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/modules/users/resources/roles.py +16 -5
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/modules/users/resources/users.py +48 -12
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/state_machine.py +246 -195
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/tags/cvt.py +242 -347
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/tags/filter.py +54 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/tags/tag.py +194 -39
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/utils/decorators.py +3 -3
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/workers/logger.py +53 -4
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/workers/state_machine.py +95 -14
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/workers/worker.py +16 -3
- pyautomationio-1.1.6/requirements.txt +28 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/setup.py +4 -4
- pyautomationio-1.1.6/version.py +2 -0
- pyautomationio-1.1.4/PKG-INFO +0 -153
- pyautomationio-1.1.4/PyAutomationIO.egg-info/PKG-INFO +0 -153
- pyautomationio-1.1.4/PyAutomationIO.egg-info/requires.txt +0 -26
- pyautomationio-1.1.4/README.md +0 -100
- pyautomationio-1.1.4/automation/alarms/trigger.py +0 -64
- pyautomationio-1.1.4/automation/dbmodels/core.py +0 -86
- pyautomationio-1.1.4/automation/logger/logdict.py +0 -53
- pyautomationio-1.1.4/requirements.txt +0 -23
- pyautomationio-1.1.4/version.py +0 -2
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/LICENSE +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/MANIFEST.in +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/PyAutomationIO.egg-info/SOURCES.txt +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/PyAutomationIO.egg-info/dependency_links.txt +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/PyAutomationIO.egg-info/top_level.txt +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/__init__.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/dbmodels/__init__.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/extensions/__init__.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/extensions/api.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/extensions/cors.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/filter/__init__.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/iad/__init__.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/iad/frozen_data.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/iad/out_of_range.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/iad/outliers.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/logger/__init__.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/managers/__init__.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/modules/__init__.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/modules/alarms/__init__.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/modules/alarms/resources/__init__.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/modules/events/__init__.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/modules/events/resources/__init__.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/modules/settings/__init__.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/modules/settings/resources/__init__.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/modules/tags/__init__.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/modules/tags/resources/__init__.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/modules/users/__init__.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/modules/users/resources/__init__.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/modules/users/resources/models/__init__.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/modules/users/resources/models/roles.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/modules/users/roles.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/modules/users/users.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/opcua/__init__.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/opcua/models.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/opcua/subscription.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/pages/__init__.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/pages/alarms.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/pages/alarms_history.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/pages/assets/styles.css +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/pages/callbacks/__init__.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/pages/callbacks/alarms.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/pages/callbacks/alarms_summary.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/pages/callbacks/db.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/pages/callbacks/filter.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/pages/callbacks/machines.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/pages/callbacks/machines_detailed.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/pages/callbacks/opcua.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/pages/callbacks/opcua_server.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/pages/callbacks/tags.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/pages/callbacks/trends.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/pages/communications.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/pages/components/__init__.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/pages/components/alarms.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/pages/components/alarms_summary.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/pages/components/database.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/pages/components/gaussian_filter.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/pages/components/machines.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/pages/components/opcua.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/pages/components/opcua_server.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/pages/components/tags.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/pages/components/trends.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/pages/database.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/pages/filter.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/pages/machines.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/pages/machines_detailed.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/pages/main.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/pages/opcua_server.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/pages/tags.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/pages/trends.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/singleton.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/tags/__init__.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/tests/__init__.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/tests/test_alarms.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/tests/test_core.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/tests/test_unit.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/tests/test_user.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/utils/__init__.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/utils/observer.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/utils/units.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/variables/__init__.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/variables/adimentional.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/variables/current.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/variables/density.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/variables/eng_time.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/variables/force.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/variables/length.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/variables/mass.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/variables/mass_flow.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/variables/percentage.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/variables/power.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/variables/pressure.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/variables/temperature.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/variables/volume.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/variables/volumetric_flow.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/automation/workers/__init__.py +0 -0
- {pyautomationio-1.1.4 → pyautomationio-1.1.6}/setup.cfg +0 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: PyAutomationIO
|
|
3
|
+
Version: 1.1.6
|
|
4
|
+
Summary: A python framework to develop automation industrial processes applications and Artificial Intelligence applications for the industrial field
|
|
5
|
+
Home-page: https://github.com/know-ai/PyAutomation
|
|
6
|
+
Author: KnowAI
|
|
7
|
+
Author-email: dev.know.ai@gmail.com
|
|
8
|
+
License: GNU AFFERO GENERAL PUBLIC LICENSE
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
14
|
+
Classifier: License :: OSI Approved :: GNU Affero General Public License v3
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
17
|
+
Classifier: Topic :: System :: Logging
|
|
18
|
+
Classifier: Topic :: System :: Monitoring
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: python-statemachine==2.4.0
|
|
22
|
+
Requires-Dist: python-statemachine[diagrams]
|
|
23
|
+
Requires-Dist: peewee==3.18.3
|
|
24
|
+
Requires-Dist: psycopg2-binary==2.9.9; sys_platform != "win32"
|
|
25
|
+
Requires-Dist: psycopg2==2.9.9; sys_platform == "win32"
|
|
26
|
+
Requires-Dist: numpy==1.25.0; python_version <= "3.11"
|
|
27
|
+
Requires-Dist: numpy==1.26.0; python_version == "3.12"
|
|
28
|
+
Requires-Dist: numpy==2.0.0; python_version == "3.13"
|
|
29
|
+
Requires-Dist: numpy==2.1.0; python_version >= "3.14"
|
|
30
|
+
Requires-Dist: PyWavelets==1.6.0; python_version == "3.9"
|
|
31
|
+
Requires-Dist: PyWavelets==1.7.0; python_version >= "3.10" and python_version <= "3.11"
|
|
32
|
+
Requires-Dist: PyWavelets==1.9.0; python_version >= "3.12" and python_version <= "3.14"
|
|
33
|
+
Requires-Dist: dash==3.3.0
|
|
34
|
+
Requires-Dist: dash-bootstrap-components==2.0.4
|
|
35
|
+
Requires-Dist: dash-mantine-components==2.4.0
|
|
36
|
+
Requires-Dist: dash-iconify==0.1.2
|
|
37
|
+
Requires-Dist: opcua==0.98.13
|
|
38
|
+
Requires-Dist: python-dotenv==1.2.1
|
|
39
|
+
Requires-Dist: cryptography==43.0.3
|
|
40
|
+
Requires-Dist: Flask-Cors==6.0.1
|
|
41
|
+
Requires-Dist: flask-restx==1.3.2
|
|
42
|
+
Requires-Dist: Flask-SocketIO==5.5.1
|
|
43
|
+
Requires-Dist: gunicorn==23.0.0
|
|
44
|
+
Requires-Dist: websocket-client==1.9.0
|
|
45
|
+
Requires-Dist: gevent-websocket==0.10.1
|
|
46
|
+
Requires-Dist: PyJWT==2.10.1
|
|
47
|
+
Dynamic: author
|
|
48
|
+
Dynamic: author-email
|
|
49
|
+
Dynamic: classifier
|
|
50
|
+
Dynamic: description
|
|
51
|
+
Dynamic: description-content-type
|
|
52
|
+
Dynamic: home-page
|
|
53
|
+
Dynamic: license
|
|
54
|
+
Dynamic: license-file
|
|
55
|
+
Dynamic: requires-dist
|
|
56
|
+
Dynamic: summary
|
|
57
|
+
|
|
58
|
+
# PyAutomation
|
|
59
|
+
|
|
60
|
+
[](https://pyautomation.readthedocs.io/en/latest/?badge=latest)
|
|
61
|
+
[](https://opensource.org/licenses/MIT)
|
|
62
|
+
[](https://www.python.org/downloads/release/python-3100/)
|
|
63
|
+
|
|
64
|
+
**PyAutomation** is a robust, concurrent framework designed for developing industrial automation applications using Python. It bridges the gap between industrial protocols (like OPC UA) and modern software development capabilities, enabling Data Science, AI, and web-based SCADA applications.
|
|
65
|
+
|
|
66
|
+

|
|
67
|
+
|
|
68
|
+
## Features
|
|
69
|
+
|
|
70
|
+
- **Concurrency & State Machines**: Built-in support for running multiple state machines concurrently for complex process control and monitoring.
|
|
71
|
+
- **Industrial Connectivity**: Native support for OPC UA (Client & Server) to communicate with PLCs, RTUs, and other industrial devices.
|
|
72
|
+
- **Data Acquisition**:
|
|
73
|
+
- **DAQ (Data Acquisition)**: Polling-based data collection.
|
|
74
|
+
- **DAS (Data Acquisition by Subscription)**: Event-driven data collection for efficiency.
|
|
75
|
+
- **CVT (Current Value Table)**: In-memory real-time database for fast access to process variables.
|
|
76
|
+
- **Data Persistence**:
|
|
77
|
+
- **Historian**: Log historical data to SQL databases (SQLite, PostgreSQL, MySQL).
|
|
78
|
+
- **Alarms & Events**: Comprehensive alarm management and event logging system compliant with ISA 18.2 concepts.
|
|
79
|
+
- **Web Integration**: Integrated Dash/Flask web server with Socket.IO for real-time HMI/SCADA interfaces.
|
|
80
|
+
- **Extensible**: Easy to extend with custom state machines and logic.
|
|
81
|
+
|
|
82
|
+
## Installation
|
|
83
|
+
|
|
84
|
+
### Prerequisites
|
|
85
|
+
|
|
86
|
+
- Python 3.10 or higher
|
|
87
|
+
- pip
|
|
88
|
+
- virtualenv (recommended)
|
|
89
|
+
|
|
90
|
+
### Local Setup
|
|
91
|
+
|
|
92
|
+
1. **Clone the repository:**
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
git clone https://github.com/know-ai/PyAutomation.git
|
|
96
|
+
cd PyAutomation
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
2. **Create a virtual environment:**
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
python3 -m venv venv
|
|
103
|
+
source venv/bin/activate # On Windows use `venv\Scripts\activate`
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
3. **Install dependencies:**
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
pip install --upgrade pip
|
|
110
|
+
pip install -r requirements.txt
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
4. **Run the application:**
|
|
114
|
+
```bash
|
|
115
|
+
./docker-entrypoint.sh
|
|
116
|
+
# Or directly with python:
|
|
117
|
+
# python wsgi.py
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Docker Deployment
|
|
121
|
+
|
|
122
|
+
PyAutomation is container-ready. You can deploy it easily using Docker Compose.
|
|
123
|
+
|
|
124
|
+
1. **Create an `.env` file:**
|
|
125
|
+
|
|
126
|
+
```ini
|
|
127
|
+
AUTOMATION_PORT=8050
|
|
128
|
+
AUTOMATION_VERSION=1.1.3
|
|
129
|
+
AUTOMATION_OPCUA_SERVER_PORT=53530
|
|
130
|
+
AUTOMATION_LOG_MAX_BYTES=5242880 # 5MB
|
|
131
|
+
AUTOMATION_LOG_BACKUP_COUNT=3
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
2. **Create a `docker-compose.yml`:**
|
|
135
|
+
|
|
136
|
+
```yaml
|
|
137
|
+
services:
|
|
138
|
+
automation:
|
|
139
|
+
container_name: "Automation"
|
|
140
|
+
image: "knowai/automation:${AUTOMATION_VERSION}"
|
|
141
|
+
restart: always
|
|
142
|
+
ports:
|
|
143
|
+
- "${AUTOMATION_PORT}:${AUTOMATION_PORT}"
|
|
144
|
+
- "${AUTOMATION_OPCUA_SERVER_PORT}:${AUTOMATION_OPCUA_SERVER_PORT}"
|
|
145
|
+
volumes:
|
|
146
|
+
- automation_db:/app/db
|
|
147
|
+
- automation_logs:/app/logs
|
|
148
|
+
environment:
|
|
149
|
+
OPCUA_SERVER_PORT: ${AUTOMATION_OPCUA_SERVER_PORT}
|
|
150
|
+
LOG_MAX_BYTES: ${AUTOMATION_LOG_MAX_BYTES}
|
|
151
|
+
LOG_BACKUP_COUNT: ${AUTOMATION_LOG_BACKUP_COUNT}
|
|
152
|
+
healthcheck:
|
|
153
|
+
test: ["CMD", "python", "/app/healthcheck.py"]
|
|
154
|
+
interval: 15s
|
|
155
|
+
timeout: 10s
|
|
156
|
+
retries: 3
|
|
157
|
+
|
|
158
|
+
volumes:
|
|
159
|
+
automation_db:
|
|
160
|
+
automation_logs:
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
3. **Start the service:**
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
docker-compose --env-file .env up -d
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
4. **Access the Dashboard:**
|
|
170
|
+
Go to `http://localhost:8050` (or your configured port).
|
|
171
|
+
|
|
172
|
+
## Documentation
|
|
173
|
+
|
|
174
|
+
Full documentation is available at [Read the Docs](https://pyautomation.readthedocs.io/).
|
|
175
|
+
|
|
176
|
+
It covers:
|
|
177
|
+
|
|
178
|
+
- **Architecture**: detailed system design.
|
|
179
|
+
- **User Guide**: how to use the alarms, tags, and database features.
|
|
180
|
+
- **Developer Guide**: API reference and how to build custom state machines.
|
|
181
|
+
|
|
182
|
+
## Contributing
|
|
183
|
+
|
|
184
|
+
We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to submit pull requests, report issues, and the code of conduct.
|
|
185
|
+
|
|
186
|
+
## License
|
|
187
|
+
|
|
188
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: PyAutomationIO
|
|
3
|
+
Version: 1.1.6
|
|
4
|
+
Summary: A python framework to develop automation industrial processes applications and Artificial Intelligence applications for the industrial field
|
|
5
|
+
Home-page: https://github.com/know-ai/PyAutomation
|
|
6
|
+
Author: KnowAI
|
|
7
|
+
Author-email: dev.know.ai@gmail.com
|
|
8
|
+
License: GNU AFFERO GENERAL PUBLIC LICENSE
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
14
|
+
Classifier: License :: OSI Approved :: GNU Affero General Public License v3
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
17
|
+
Classifier: Topic :: System :: Logging
|
|
18
|
+
Classifier: Topic :: System :: Monitoring
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: python-statemachine==2.4.0
|
|
22
|
+
Requires-Dist: python-statemachine[diagrams]
|
|
23
|
+
Requires-Dist: peewee==3.18.3
|
|
24
|
+
Requires-Dist: psycopg2-binary==2.9.9; sys_platform != "win32"
|
|
25
|
+
Requires-Dist: psycopg2==2.9.9; sys_platform == "win32"
|
|
26
|
+
Requires-Dist: numpy==1.25.0; python_version <= "3.11"
|
|
27
|
+
Requires-Dist: numpy==1.26.0; python_version == "3.12"
|
|
28
|
+
Requires-Dist: numpy==2.0.0; python_version == "3.13"
|
|
29
|
+
Requires-Dist: numpy==2.1.0; python_version >= "3.14"
|
|
30
|
+
Requires-Dist: PyWavelets==1.6.0; python_version == "3.9"
|
|
31
|
+
Requires-Dist: PyWavelets==1.7.0; python_version >= "3.10" and python_version <= "3.11"
|
|
32
|
+
Requires-Dist: PyWavelets==1.9.0; python_version >= "3.12" and python_version <= "3.14"
|
|
33
|
+
Requires-Dist: dash==3.3.0
|
|
34
|
+
Requires-Dist: dash-bootstrap-components==2.0.4
|
|
35
|
+
Requires-Dist: dash-mantine-components==2.4.0
|
|
36
|
+
Requires-Dist: dash-iconify==0.1.2
|
|
37
|
+
Requires-Dist: opcua==0.98.13
|
|
38
|
+
Requires-Dist: python-dotenv==1.2.1
|
|
39
|
+
Requires-Dist: cryptography==43.0.3
|
|
40
|
+
Requires-Dist: Flask-Cors==6.0.1
|
|
41
|
+
Requires-Dist: flask-restx==1.3.2
|
|
42
|
+
Requires-Dist: Flask-SocketIO==5.5.1
|
|
43
|
+
Requires-Dist: gunicorn==23.0.0
|
|
44
|
+
Requires-Dist: websocket-client==1.9.0
|
|
45
|
+
Requires-Dist: gevent-websocket==0.10.1
|
|
46
|
+
Requires-Dist: PyJWT==2.10.1
|
|
47
|
+
Dynamic: author
|
|
48
|
+
Dynamic: author-email
|
|
49
|
+
Dynamic: classifier
|
|
50
|
+
Dynamic: description
|
|
51
|
+
Dynamic: description-content-type
|
|
52
|
+
Dynamic: home-page
|
|
53
|
+
Dynamic: license
|
|
54
|
+
Dynamic: license-file
|
|
55
|
+
Dynamic: requires-dist
|
|
56
|
+
Dynamic: summary
|
|
57
|
+
|
|
58
|
+
# PyAutomation
|
|
59
|
+
|
|
60
|
+
[](https://pyautomation.readthedocs.io/en/latest/?badge=latest)
|
|
61
|
+
[](https://opensource.org/licenses/MIT)
|
|
62
|
+
[](https://www.python.org/downloads/release/python-3100/)
|
|
63
|
+
|
|
64
|
+
**PyAutomation** is a robust, concurrent framework designed for developing industrial automation applications using Python. It bridges the gap between industrial protocols (like OPC UA) and modern software development capabilities, enabling Data Science, AI, and web-based SCADA applications.
|
|
65
|
+
|
|
66
|
+

|
|
67
|
+
|
|
68
|
+
## Features
|
|
69
|
+
|
|
70
|
+
- **Concurrency & State Machines**: Built-in support for running multiple state machines concurrently for complex process control and monitoring.
|
|
71
|
+
- **Industrial Connectivity**: Native support for OPC UA (Client & Server) to communicate with PLCs, RTUs, and other industrial devices.
|
|
72
|
+
- **Data Acquisition**:
|
|
73
|
+
- **DAQ (Data Acquisition)**: Polling-based data collection.
|
|
74
|
+
- **DAS (Data Acquisition by Subscription)**: Event-driven data collection for efficiency.
|
|
75
|
+
- **CVT (Current Value Table)**: In-memory real-time database for fast access to process variables.
|
|
76
|
+
- **Data Persistence**:
|
|
77
|
+
- **Historian**: Log historical data to SQL databases (SQLite, PostgreSQL, MySQL).
|
|
78
|
+
- **Alarms & Events**: Comprehensive alarm management and event logging system compliant with ISA 18.2 concepts.
|
|
79
|
+
- **Web Integration**: Integrated Dash/Flask web server with Socket.IO for real-time HMI/SCADA interfaces.
|
|
80
|
+
- **Extensible**: Easy to extend with custom state machines and logic.
|
|
81
|
+
|
|
82
|
+
## Installation
|
|
83
|
+
|
|
84
|
+
### Prerequisites
|
|
85
|
+
|
|
86
|
+
- Python 3.10 or higher
|
|
87
|
+
- pip
|
|
88
|
+
- virtualenv (recommended)
|
|
89
|
+
|
|
90
|
+
### Local Setup
|
|
91
|
+
|
|
92
|
+
1. **Clone the repository:**
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
git clone https://github.com/know-ai/PyAutomation.git
|
|
96
|
+
cd PyAutomation
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
2. **Create a virtual environment:**
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
python3 -m venv venv
|
|
103
|
+
source venv/bin/activate # On Windows use `venv\Scripts\activate`
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
3. **Install dependencies:**
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
pip install --upgrade pip
|
|
110
|
+
pip install -r requirements.txt
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
4. **Run the application:**
|
|
114
|
+
```bash
|
|
115
|
+
./docker-entrypoint.sh
|
|
116
|
+
# Or directly with python:
|
|
117
|
+
# python wsgi.py
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Docker Deployment
|
|
121
|
+
|
|
122
|
+
PyAutomation is container-ready. You can deploy it easily using Docker Compose.
|
|
123
|
+
|
|
124
|
+
1. **Create an `.env` file:**
|
|
125
|
+
|
|
126
|
+
```ini
|
|
127
|
+
AUTOMATION_PORT=8050
|
|
128
|
+
AUTOMATION_VERSION=1.1.3
|
|
129
|
+
AUTOMATION_OPCUA_SERVER_PORT=53530
|
|
130
|
+
AUTOMATION_LOG_MAX_BYTES=5242880 # 5MB
|
|
131
|
+
AUTOMATION_LOG_BACKUP_COUNT=3
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
2. **Create a `docker-compose.yml`:**
|
|
135
|
+
|
|
136
|
+
```yaml
|
|
137
|
+
services:
|
|
138
|
+
automation:
|
|
139
|
+
container_name: "Automation"
|
|
140
|
+
image: "knowai/automation:${AUTOMATION_VERSION}"
|
|
141
|
+
restart: always
|
|
142
|
+
ports:
|
|
143
|
+
- "${AUTOMATION_PORT}:${AUTOMATION_PORT}"
|
|
144
|
+
- "${AUTOMATION_OPCUA_SERVER_PORT}:${AUTOMATION_OPCUA_SERVER_PORT}"
|
|
145
|
+
volumes:
|
|
146
|
+
- automation_db:/app/db
|
|
147
|
+
- automation_logs:/app/logs
|
|
148
|
+
environment:
|
|
149
|
+
OPCUA_SERVER_PORT: ${AUTOMATION_OPCUA_SERVER_PORT}
|
|
150
|
+
LOG_MAX_BYTES: ${AUTOMATION_LOG_MAX_BYTES}
|
|
151
|
+
LOG_BACKUP_COUNT: ${AUTOMATION_LOG_BACKUP_COUNT}
|
|
152
|
+
healthcheck:
|
|
153
|
+
test: ["CMD", "python", "/app/healthcheck.py"]
|
|
154
|
+
interval: 15s
|
|
155
|
+
timeout: 10s
|
|
156
|
+
retries: 3
|
|
157
|
+
|
|
158
|
+
volumes:
|
|
159
|
+
automation_db:
|
|
160
|
+
automation_logs:
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
3. **Start the service:**
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
docker-compose --env-file .env up -d
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
4. **Access the Dashboard:**
|
|
170
|
+
Go to `http://localhost:8050` (or your configured port).
|
|
171
|
+
|
|
172
|
+
## Documentation
|
|
173
|
+
|
|
174
|
+
Full documentation is available at [Read the Docs](https://pyautomation.readthedocs.io/).
|
|
175
|
+
|
|
176
|
+
It covers:
|
|
177
|
+
|
|
178
|
+
- **Architecture**: detailed system design.
|
|
179
|
+
- **User Guide**: how to use the alarms, tags, and database features.
|
|
180
|
+
- **Developer Guide**: API reference and how to build custom state machines.
|
|
181
|
+
|
|
182
|
+
## Contributing
|
|
183
|
+
|
|
184
|
+
We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to submit pull requests, report issues, and the code of conduct.
|
|
185
|
+
|
|
186
|
+
## License
|
|
187
|
+
|
|
188
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
python-statemachine==2.4.0
|
|
2
|
+
python-statemachine[diagrams]
|
|
3
|
+
peewee==3.18.3
|
|
4
|
+
dash==3.3.0
|
|
5
|
+
dash-bootstrap-components==2.0.4
|
|
6
|
+
dash-mantine-components==2.4.0
|
|
7
|
+
dash-iconify==0.1.2
|
|
8
|
+
opcua==0.98.13
|
|
9
|
+
python-dotenv==1.2.1
|
|
10
|
+
cryptography==43.0.3
|
|
11
|
+
Flask-Cors==6.0.1
|
|
12
|
+
flask-restx==1.3.2
|
|
13
|
+
Flask-SocketIO==5.5.1
|
|
14
|
+
gunicorn==23.0.0
|
|
15
|
+
websocket-client==1.9.0
|
|
16
|
+
gevent-websocket==0.10.1
|
|
17
|
+
PyJWT==2.10.1
|
|
18
|
+
|
|
19
|
+
[:python_version <= "3.11"]
|
|
20
|
+
numpy==1.25.0
|
|
21
|
+
|
|
22
|
+
[:python_version == "3.12"]
|
|
23
|
+
numpy==1.26.0
|
|
24
|
+
|
|
25
|
+
[:python_version == "3.13"]
|
|
26
|
+
numpy==2.0.0
|
|
27
|
+
|
|
28
|
+
[:python_version == "3.9"]
|
|
29
|
+
PyWavelets==1.6.0
|
|
30
|
+
|
|
31
|
+
[:python_version >= "3.10" and python_version <= "3.11"]
|
|
32
|
+
PyWavelets==1.7.0
|
|
33
|
+
|
|
34
|
+
[:python_version >= "3.12" and python_version <= "3.14"]
|
|
35
|
+
PyWavelets==1.9.0
|
|
36
|
+
|
|
37
|
+
[:python_version >= "3.14"]
|
|
38
|
+
numpy==2.1.0
|
|
39
|
+
|
|
40
|
+
[:sys_platform != "win32"]
|
|
41
|
+
psycopg2-binary==2.9.9
|
|
42
|
+
|
|
43
|
+
[:sys_platform == "win32"]
|
|
44
|
+
psycopg2==2.9.9
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# PyAutomation
|
|
2
|
+
|
|
3
|
+
[](https://pyautomation.readthedocs.io/en/latest/?badge=latest)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://www.python.org/downloads/release/python-3100/)
|
|
6
|
+
|
|
7
|
+
**PyAutomation** is a robust, concurrent framework designed for developing industrial automation applications using Python. It bridges the gap between industrial protocols (like OPC UA) and modern software development capabilities, enabling Data Science, AI, and web-based SCADA applications.
|
|
8
|
+
|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- **Concurrency & State Machines**: Built-in support for running multiple state machines concurrently for complex process control and monitoring.
|
|
14
|
+
- **Industrial Connectivity**: Native support for OPC UA (Client & Server) to communicate with PLCs, RTUs, and other industrial devices.
|
|
15
|
+
- **Data Acquisition**:
|
|
16
|
+
- **DAQ (Data Acquisition)**: Polling-based data collection.
|
|
17
|
+
- **DAS (Data Acquisition by Subscription)**: Event-driven data collection for efficiency.
|
|
18
|
+
- **CVT (Current Value Table)**: In-memory real-time database for fast access to process variables.
|
|
19
|
+
- **Data Persistence**:
|
|
20
|
+
- **Historian**: Log historical data to SQL databases (SQLite, PostgreSQL, MySQL).
|
|
21
|
+
- **Alarms & Events**: Comprehensive alarm management and event logging system compliant with ISA 18.2 concepts.
|
|
22
|
+
- **Web Integration**: Integrated Dash/Flask web server with Socket.IO for real-time HMI/SCADA interfaces.
|
|
23
|
+
- **Extensible**: Easy to extend with custom state machines and logic.
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
### Prerequisites
|
|
28
|
+
|
|
29
|
+
- Python 3.10 or higher
|
|
30
|
+
- pip
|
|
31
|
+
- virtualenv (recommended)
|
|
32
|
+
|
|
33
|
+
### Local Setup
|
|
34
|
+
|
|
35
|
+
1. **Clone the repository:**
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
git clone https://github.com/know-ai/PyAutomation.git
|
|
39
|
+
cd PyAutomation
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
2. **Create a virtual environment:**
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
python3 -m venv venv
|
|
46
|
+
source venv/bin/activate # On Windows use `venv\Scripts\activate`
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
3. **Install dependencies:**
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pip install --upgrade pip
|
|
53
|
+
pip install -r requirements.txt
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
4. **Run the application:**
|
|
57
|
+
```bash
|
|
58
|
+
./docker-entrypoint.sh
|
|
59
|
+
# Or directly with python:
|
|
60
|
+
# python wsgi.py
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Docker Deployment
|
|
64
|
+
|
|
65
|
+
PyAutomation is container-ready. You can deploy it easily using Docker Compose.
|
|
66
|
+
|
|
67
|
+
1. **Create an `.env` file:**
|
|
68
|
+
|
|
69
|
+
```ini
|
|
70
|
+
AUTOMATION_PORT=8050
|
|
71
|
+
AUTOMATION_VERSION=1.1.3
|
|
72
|
+
AUTOMATION_OPCUA_SERVER_PORT=53530
|
|
73
|
+
AUTOMATION_LOG_MAX_BYTES=5242880 # 5MB
|
|
74
|
+
AUTOMATION_LOG_BACKUP_COUNT=3
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
2. **Create a `docker-compose.yml`:**
|
|
78
|
+
|
|
79
|
+
```yaml
|
|
80
|
+
services:
|
|
81
|
+
automation:
|
|
82
|
+
container_name: "Automation"
|
|
83
|
+
image: "knowai/automation:${AUTOMATION_VERSION}"
|
|
84
|
+
restart: always
|
|
85
|
+
ports:
|
|
86
|
+
- "${AUTOMATION_PORT}:${AUTOMATION_PORT}"
|
|
87
|
+
- "${AUTOMATION_OPCUA_SERVER_PORT}:${AUTOMATION_OPCUA_SERVER_PORT}"
|
|
88
|
+
volumes:
|
|
89
|
+
- automation_db:/app/db
|
|
90
|
+
- automation_logs:/app/logs
|
|
91
|
+
environment:
|
|
92
|
+
OPCUA_SERVER_PORT: ${AUTOMATION_OPCUA_SERVER_PORT}
|
|
93
|
+
LOG_MAX_BYTES: ${AUTOMATION_LOG_MAX_BYTES}
|
|
94
|
+
LOG_BACKUP_COUNT: ${AUTOMATION_LOG_BACKUP_COUNT}
|
|
95
|
+
healthcheck:
|
|
96
|
+
test: ["CMD", "python", "/app/healthcheck.py"]
|
|
97
|
+
interval: 15s
|
|
98
|
+
timeout: 10s
|
|
99
|
+
retries: 3
|
|
100
|
+
|
|
101
|
+
volumes:
|
|
102
|
+
automation_db:
|
|
103
|
+
automation_logs:
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
3. **Start the service:**
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
docker-compose --env-file .env up -d
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
4. **Access the Dashboard:**
|
|
113
|
+
Go to `http://localhost:8050` (or your configured port).
|
|
114
|
+
|
|
115
|
+
## Documentation
|
|
116
|
+
|
|
117
|
+
Full documentation is available at [Read the Docs](https://pyautomation.readthedocs.io/).
|
|
118
|
+
|
|
119
|
+
It covers:
|
|
120
|
+
|
|
121
|
+
- **Architecture**: detailed system design.
|
|
122
|
+
- **User Guide**: how to use the alarms, tags, and database features.
|
|
123
|
+
- **Developer Guide**: API reference and how to build custom state machines.
|
|
124
|
+
|
|
125
|
+
## Contributing
|
|
126
|
+
|
|
127
|
+
We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to submit pull requests, report issues, and the code of conduct.
|
|
128
|
+
|
|
129
|
+
## License
|
|
130
|
+
|
|
131
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|