PyAutomationIO 0.0.0__py3-none-any.whl

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.
Files changed (138) hide show
  1. automation/__init__.py +46 -0
  2. automation/alarms/__init__.py +563 -0
  3. automation/alarms/states.py +192 -0
  4. automation/alarms/trigger.py +64 -0
  5. automation/buffer.py +132 -0
  6. automation/core.py +1775 -0
  7. automation/dbmodels/__init__.py +23 -0
  8. automation/dbmodels/alarms.py +524 -0
  9. automation/dbmodels/core.py +86 -0
  10. automation/dbmodels/events.py +153 -0
  11. automation/dbmodels/logs.py +155 -0
  12. automation/dbmodels/machines.py +181 -0
  13. automation/dbmodels/opcua.py +81 -0
  14. automation/dbmodels/opcua_server.py +174 -0
  15. automation/dbmodels/tags.py +921 -0
  16. automation/dbmodels/users.py +259 -0
  17. automation/extensions/__init__.py +15 -0
  18. automation/extensions/api.py +149 -0
  19. automation/extensions/cors.py +18 -0
  20. automation/filter/__init__.py +19 -0
  21. automation/iad/__init__.py +3 -0
  22. automation/iad/frozen_data.py +54 -0
  23. automation/iad/out_of_range.py +51 -0
  24. automation/iad/outliers.py +51 -0
  25. automation/logger/__init__.py +0 -0
  26. automation/logger/alarms.py +426 -0
  27. automation/logger/core.py +265 -0
  28. automation/logger/datalogger.py +646 -0
  29. automation/logger/events.py +194 -0
  30. automation/logger/logdict.py +53 -0
  31. automation/logger/logs.py +203 -0
  32. automation/logger/machines.py +248 -0
  33. automation/logger/opcua_server.py +130 -0
  34. automation/logger/users.py +96 -0
  35. automation/managers/__init__.py +4 -0
  36. automation/managers/alarms.py +455 -0
  37. automation/managers/db.py +328 -0
  38. automation/managers/opcua_client.py +186 -0
  39. automation/managers/state_machine.py +183 -0
  40. automation/models.py +174 -0
  41. automation/modules/__init__.py +14 -0
  42. automation/modules/alarms/__init__.py +0 -0
  43. automation/modules/alarms/resources/__init__.py +10 -0
  44. automation/modules/alarms/resources/alarms.py +280 -0
  45. automation/modules/alarms/resources/summary.py +79 -0
  46. automation/modules/events/__init__.py +0 -0
  47. automation/modules/events/resources/__init__.py +10 -0
  48. automation/modules/events/resources/events.py +83 -0
  49. automation/modules/events/resources/logs.py +109 -0
  50. automation/modules/tags/__init__.py +0 -0
  51. automation/modules/tags/resources/__init__.py +8 -0
  52. automation/modules/tags/resources/tags.py +201 -0
  53. automation/modules/users/__init__.py +2 -0
  54. automation/modules/users/resources/__init__.py +10 -0
  55. automation/modules/users/resources/models/__init__.py +2 -0
  56. automation/modules/users/resources/models/roles.py +5 -0
  57. automation/modules/users/resources/models/users.py +14 -0
  58. automation/modules/users/resources/roles.py +38 -0
  59. automation/modules/users/resources/users.py +113 -0
  60. automation/modules/users/roles.py +121 -0
  61. automation/modules/users/users.py +335 -0
  62. automation/opcua/__init__.py +1 -0
  63. automation/opcua/models.py +541 -0
  64. automation/opcua/subscription.py +259 -0
  65. automation/pages/__init__.py +0 -0
  66. automation/pages/alarms.py +34 -0
  67. automation/pages/alarms_history.py +21 -0
  68. automation/pages/assets/styles.css +7 -0
  69. automation/pages/callbacks/__init__.py +28 -0
  70. automation/pages/callbacks/alarms.py +218 -0
  71. automation/pages/callbacks/alarms_summary.py +20 -0
  72. automation/pages/callbacks/db.py +222 -0
  73. automation/pages/callbacks/filter.py +238 -0
  74. automation/pages/callbacks/machines.py +29 -0
  75. automation/pages/callbacks/machines_detailed.py +581 -0
  76. automation/pages/callbacks/opcua.py +266 -0
  77. automation/pages/callbacks/opcua_server.py +244 -0
  78. automation/pages/callbacks/tags.py +495 -0
  79. automation/pages/callbacks/trends.py +119 -0
  80. automation/pages/communications.py +129 -0
  81. automation/pages/components/__init__.py +123 -0
  82. automation/pages/components/alarms.py +151 -0
  83. automation/pages/components/alarms_summary.py +45 -0
  84. automation/pages/components/database.py +128 -0
  85. automation/pages/components/gaussian_filter.py +69 -0
  86. automation/pages/components/machines.py +396 -0
  87. automation/pages/components/opcua.py +384 -0
  88. automation/pages/components/opcua_server.py +53 -0
  89. automation/pages/components/tags.py +253 -0
  90. automation/pages/components/trends.py +66 -0
  91. automation/pages/database.py +26 -0
  92. automation/pages/filter.py +55 -0
  93. automation/pages/machines.py +20 -0
  94. automation/pages/machines_detailed.py +41 -0
  95. automation/pages/main.py +63 -0
  96. automation/pages/opcua_server.py +28 -0
  97. automation/pages/tags.py +40 -0
  98. automation/pages/trends.py +35 -0
  99. automation/singleton.py +30 -0
  100. automation/state_machine.py +1672 -0
  101. automation/tags/__init__.py +2 -0
  102. automation/tags/cvt.py +1198 -0
  103. automation/tags/filter.py +55 -0
  104. automation/tags/tag.py +418 -0
  105. automation/tests/__init__.py +10 -0
  106. automation/tests/test_alarms.py +110 -0
  107. automation/tests/test_core.py +257 -0
  108. automation/tests/test_unit.py +21 -0
  109. automation/tests/test_user.py +155 -0
  110. automation/utils/__init__.py +164 -0
  111. automation/utils/decorators.py +222 -0
  112. automation/utils/npw.py +294 -0
  113. automation/utils/observer.py +21 -0
  114. automation/utils/units.py +118 -0
  115. automation/variables/__init__.py +55 -0
  116. automation/variables/adimentional.py +30 -0
  117. automation/variables/current.py +71 -0
  118. automation/variables/density.py +115 -0
  119. automation/variables/eng_time.py +68 -0
  120. automation/variables/force.py +90 -0
  121. automation/variables/length.py +104 -0
  122. automation/variables/mass.py +80 -0
  123. automation/variables/mass_flow.py +101 -0
  124. automation/variables/percentage.py +30 -0
  125. automation/variables/power.py +113 -0
  126. automation/variables/pressure.py +93 -0
  127. automation/variables/temperature.py +168 -0
  128. automation/variables/volume.py +70 -0
  129. automation/variables/volumetric_flow.py +100 -0
  130. automation/workers/__init__.py +2 -0
  131. automation/workers/logger.py +164 -0
  132. automation/workers/state_machine.py +207 -0
  133. automation/workers/worker.py +36 -0
  134. pyautomationio-0.0.0.dist-info/METADATA +198 -0
  135. pyautomationio-0.0.0.dist-info/RECORD +138 -0
  136. pyautomationio-0.0.0.dist-info/WHEEL +5 -0
  137. pyautomationio-0.0.0.dist-info/licenses/LICENSE +21 -0
  138. pyautomationio-0.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,164 @@
1
+ # -*- coding: utf-8 -*-
2
+ """rackio/workers/logger.py
3
+
4
+ This module implements Logger Worker.
5
+ """
6
+ import logging, time, datetime, os, shutil
7
+ from .worker import BaseWorker
8
+ from ..managers import DBManager
9
+ from ..opcua.models import Client
10
+ from ..logger.datalogger import DataLoggerEngine
11
+ from ..tags.cvt import CVTEngine
12
+ import sqlite3
13
+ from peewee import SqliteDatabase
14
+ from ..dbmodels.tags import TagValue
15
+ from ..dbmodels.alarms import AlarmSummary
16
+ from ..dbmodels.events import Events
17
+ from ..dbmodels.logs import Logs
18
+
19
+
20
+ class LoggerWorker(BaseWorker):
21
+
22
+ def __init__(self, manager:DBManager, period:int=5.0):
23
+
24
+ super(LoggerWorker, self).__init__()
25
+
26
+ self._manager = manager
27
+ self._period = period
28
+ self.logger = DataLoggerEngine()
29
+ self.cvt = CVTEngine()
30
+ self.sqlite_db = None
31
+ self.sqlite_db_name = None
32
+
33
+ def sqlite_db_backup(self):
34
+ if self.sqlite_db:
35
+ file_size_mb = os.path.getsize(self.sqlite_db_name) / 1024 / 1024
36
+ if file_size_mb > 1 * 1024: # 1 Gb:
37
+ timestamp = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
38
+ name = self.sqlite_db_name.split(".db")[0]
39
+ name = name.split(os.path.sep)[-1]
40
+ backup_file = os.path.join(".", "db", "backups", f"{name}_{timestamp}.db")
41
+ shutil.copy2(os.path.join(".", "db", "app.db"), backup_file)
42
+ logger = logging.getLogger("pyautomation")
43
+ logger.info(f"Backup creado: {backup_file}")
44
+ # Empty TagValue
45
+ query = TagValue.delete()
46
+ query.execute()
47
+ # Empty Alarm Summary
48
+ query = AlarmSummary.delete()
49
+ query.execute()
50
+ # Empty Events
51
+ query = Events.delete()
52
+ query.execute()
53
+ # Empty Logs
54
+ query = Logs.delete()
55
+ query.execute()
56
+ # Execute Vacuum to compact DB
57
+ self.sqlite_db.close()
58
+ conn = sqlite3.connect(self.sqlite_db_name)
59
+ cur = conn.cursor()
60
+ cur.execute("VACUUM;")
61
+ conn.commit()
62
+ conn.close()
63
+ # Reopen DB connection
64
+ from ..dbmodels import proxy
65
+ self._db = self._manager.get_db()
66
+ proxy.initialize(self._db)
67
+
68
+ else:
69
+ db = self.logger.logger.get_db()
70
+ if db:
71
+ if isinstance(db, SqliteDatabase):
72
+ self.sqlite_db = db
73
+ self.sqlite_db_name = db.database
74
+
75
+
76
+ def check_opcua_connection(self):
77
+ from automation import PyAutomation
78
+ app = PyAutomation()
79
+ if app.opcua_client_manager._clients:
80
+ for _, opcua_client in app.opcua_client_manager._clients.items():
81
+
82
+ if isinstance(opcua_client, Client):
83
+
84
+ opcua_client.reconnect()
85
+ else:
86
+
87
+ app.load_opcua_clients_from_db()
88
+
89
+ def get_tags_from_queue(self, _queue):
90
+ from .. import SEGMENT, MANUFACTURER
91
+ tags = list()
92
+ while not _queue.empty():
93
+
94
+ item = _queue.get(block=False)
95
+ tag_name = item["tag"]
96
+ tag = self.cvt.get_tag_by_name(name=tag_name)
97
+ if tag:
98
+
99
+ if tag.manufacturer==MANUFACTURER and tag.segment==SEGMENT:
100
+
101
+ value = item['value']
102
+ timestamp = item["timestamp"]
103
+ tags.append({"tag":tag_name, "value":value, "timestamp":timestamp})
104
+
105
+ elif not MANUFACTURER and not SEGMENT:
106
+
107
+ value = item['value']
108
+ timestamp = item["timestamp"]
109
+ tags.append({"tag":tag_name, "value":value, "timestamp":timestamp})
110
+
111
+ return tags
112
+
113
+ def reconnect_to_db(self):
114
+ from automation import PyAutomation
115
+ app = PyAutomation()
116
+
117
+ if self.db_reconnection:
118
+
119
+ logging.critical("Trying reconnect to DB...")
120
+
121
+ self.db_reconnection = False
122
+ db_connected = app.reconnect_to_db()
123
+
124
+ if db_connected:
125
+
126
+ logging.critical("Reconnection successfully")
127
+ self.db_reconnection = True
128
+
129
+ def run(self):
130
+ r"""
131
+ Documentation here
132
+ """
133
+ _queue = self._manager.get_queue()
134
+ self.db_reconnection = True
135
+
136
+ while True:
137
+
138
+ if self.db_reconnection:
139
+
140
+ db_connection = self.logger.logger.check_connectivity()
141
+
142
+ if db_connection:
143
+ self.sqlite_db_backup()
144
+ tags = self.get_tags_from_queue(_queue=_queue)
145
+
146
+ if tags:
147
+
148
+ self.logger.write_tags(tags=tags)
149
+
150
+ else:
151
+
152
+ self.reconnect_to_db()
153
+
154
+ else:
155
+
156
+ self.reconnect_to_db()
157
+
158
+ self.check_opcua_connection()
159
+
160
+ if self.stop_event.is_set():
161
+ logging.critical("Alarm worker shutdown successfully!")
162
+ break
163
+
164
+ time.sleep(self._period)
@@ -0,0 +1,207 @@
1
+ # -*- coding: utf-8 -*-
2
+ """automation/workers/state.py
3
+
4
+ This module implements State Machine Worker.
5
+ """
6
+ import heapq
7
+ import logging
8
+ import time
9
+ from collections import deque
10
+ from threading import Thread
11
+ from .worker import BaseWorker
12
+
13
+
14
+ class MachineScheduler():
15
+
16
+ def __init__(self):
17
+
18
+ self._ready = deque()
19
+ self._sleeping = list()
20
+ self._sequence = 0
21
+ self.last = None
22
+ self._stop = False
23
+
24
+ def call_soon(self, func):
25
+
26
+ self._ready.append(func)
27
+
28
+ def call_later(self, delay, func, machine):
29
+ self._sequence += 1
30
+ deadline = time.time() + delay
31
+ heapq.heappush(self._sleeping, (deadline, self._sequence, func, machine))
32
+
33
+ def stop(self):
34
+
35
+ self._stop = True
36
+
37
+ def run(self):
38
+
39
+ self.set_last()
40
+
41
+ while self._ready or self._sleeping:
42
+
43
+ if self._stop:
44
+ break
45
+
46
+ if not self._ready and self._sleeping:
47
+ deadline, _, func, machine = heapq.heappop(self._sleeping)
48
+ self.sleep_elapsed(machine)
49
+
50
+ self._ready.append(func)
51
+
52
+ while self._ready:
53
+ func = self._ready.popleft()
54
+ func()
55
+
56
+ def set_last(self):
57
+
58
+ self.last = time.time()
59
+
60
+ return self.last
61
+
62
+ def sleep_elapsed(self, machine):
63
+ elapsed = time.time() - self.last
64
+ interval = machine.get_interval()
65
+
66
+ try:
67
+ time.sleep(interval - elapsed)
68
+ self.set_last()
69
+ except ValueError:
70
+ self.set_last()
71
+ logger = logging.getLogger("pyautomation")
72
+ logger.warning(f"State Machine: {machine.name.value} NOT executed on time - Execution Interval: {interval} - Elapsed: {elapsed}")
73
+
74
+
75
+ class SchedThread(Thread):
76
+
77
+ def __init__(self, machine):
78
+
79
+ super(SchedThread, self).__init__()
80
+
81
+ self.machine = machine
82
+
83
+ def stop(self):
84
+
85
+ self.scheduler.stop()
86
+
87
+ def loop_closure(self, machine, scheduler:MachineScheduler):
88
+
89
+ def loop():
90
+ machine.loop()
91
+ interval = machine.get_interval()
92
+ scheduler.call_later(interval, loop, machine)
93
+
94
+ return loop
95
+
96
+ def target(self, machine):
97
+ scheduler = MachineScheduler()
98
+ self.scheduler = scheduler
99
+ func = self.loop_closure(machine, scheduler)
100
+ scheduler.call_soon(func)
101
+ scheduler.run()
102
+
103
+ def run(self):
104
+
105
+ self.target(self.machine)
106
+
107
+
108
+ class AsyncStateMachineWorker(BaseWorker):
109
+
110
+ def __init__(self):
111
+
112
+ super(AsyncStateMachineWorker, self).__init__()
113
+ self._machines = list()
114
+ self._schedulers = list()
115
+ self.jobs = list()
116
+
117
+ def add_machine(self, machine):
118
+
119
+ self._machines.append(machine)
120
+
121
+ def run(self):
122
+
123
+ for machine in self._machines:
124
+
125
+ sched = SchedThread(machine)
126
+ self._schedulers.append(sched)
127
+
128
+ for sched in self._schedulers:
129
+
130
+ sched.daemon = True
131
+ sched.start()
132
+
133
+ def join(self, machine):
134
+
135
+ sched = SchedThread(machine)
136
+ self._schedulers.append(sched)
137
+ sched.daemon = True
138
+ sched.start()
139
+
140
+ def drop(self, machine):
141
+ r"""
142
+ Documentation here
143
+ """
144
+ sched_to_drop = None
145
+ for index, sched in enumerate(self._schedulers):
146
+ if machine==sched.machine:
147
+
148
+ sched_to_drop = self._schedulers.pop(index)
149
+ break
150
+
151
+ if sched_to_drop:
152
+ sched.stop()
153
+
154
+ def stop(self):
155
+
156
+ for sched in self._schedulers:
157
+ try:
158
+ sched.stop()
159
+ except Exception as e:
160
+ message = "Error on async scheduler stop"
161
+ logger = logging.getLogger("pyautomation")
162
+ logger.error(f"{message} - {e}")
163
+
164
+
165
+ class StateMachineWorker(BaseWorker):
166
+
167
+ def __init__(self, manager):
168
+
169
+ super(StateMachineWorker, self).__init__()
170
+
171
+ self._manager = manager
172
+ self._sync_scheduler = MachineScheduler()
173
+ self._async_scheduler = AsyncStateMachineWorker()
174
+ self.jobs = list()
175
+
176
+ def loop_closure(self, machine):
177
+
178
+ self._machine = machine
179
+
180
+ def loop():
181
+ machine.loop()
182
+ interval = machine.get_interval()
183
+ self._sync_scheduler.call_later(interval, loop, machine)
184
+
185
+ return loop
186
+
187
+ def run(self):
188
+
189
+ for machine, interval, mode in self._manager.get_machines():
190
+
191
+ if mode == "async":
192
+
193
+ self._async_scheduler.add_machine(machine)
194
+
195
+ else:
196
+
197
+ func = self.loop_closure(machine)
198
+ self._sync_scheduler.call_soon(func)
199
+
200
+ self._async_scheduler.run()
201
+ self._sync_scheduler.run()
202
+
203
+ def stop(self):
204
+
205
+ self._async_scheduler.stop()
206
+ self._sync_scheduler.stop()
207
+
@@ -0,0 +1,36 @@
1
+ # -*- coding: utf-8 -*-
2
+ """workers/worker.py
3
+
4
+ This module implements all thread classes for workers.
5
+ """
6
+ from threading import Thread
7
+ from threading import Event as ThreadEvent
8
+
9
+
10
+ class BaseWorker(Thread):
11
+
12
+ def __init__(self):
13
+
14
+ super(BaseWorker, self).__init__()
15
+
16
+ self.stop_event = ThreadEvent()
17
+
18
+ def get_stop_event(self):
19
+
20
+ return self.stop_event
21
+
22
+ def stop(self):
23
+
24
+ self.stop_event.set()
25
+
26
+ def __getstate__(self):
27
+
28
+ state = self.__dict__.copy()
29
+ del state['stop_event']
30
+ return state
31
+
32
+ def __setstate__(self, state):
33
+
34
+ self.__dict__.update(state)
35
+ self.stop_event = ThreadEvent()
36
+
@@ -0,0 +1,198 @@
1
+ Metadata-Version: 2.4
2
+ Name: PyAutomationIO
3
+ Version: 0.0.0
4
+ Summary: A python library to develop automation continuous tasks using sync or async concurrent threads
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.7
10
+ Classifier: Programming Language :: Python :: 3.8
11
+ Classifier: Programming Language :: Python :: 3.9
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
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.17.8
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
27
+ Requires-Dist: PyWavelets==1.7.0
28
+ Requires-Dist: dash==2.18.2
29
+ Requires-Dist: dash-bootstrap-components==1.6.0
30
+ Requires-Dist: dash-mantine-components==0.14.7
31
+ Requires-Dist: dash-iconify==0.1.2
32
+ Requires-Dist: opcua==0.98.13
33
+ Requires-Dist: python-dotenv==1.0.1
34
+ Requires-Dist: cryptography==43.0.3
35
+ Requires-Dist: Flask-Cors==5.0.0
36
+ Requires-Dist: flask-restx==1.3.0
37
+ Requires-Dist: Flask-SocketIO==5.4.1
38
+ Requires-Dist: gunicorn==23.0.0
39
+ Requires-Dist: websocket-client==1.8.0
40
+ Requires-Dist: gevent-websocket==0.10.1
41
+ Requires-Dist: PyJWT==2.9.0
42
+ Requires-Dist: pydot==3.0.2
43
+ Dynamic: author
44
+ Dynamic: author-email
45
+ Dynamic: classifier
46
+ Dynamic: description
47
+ Dynamic: description-content-type
48
+ Dynamic: home-page
49
+ Dynamic: license
50
+ Dynamic: license-file
51
+ Dynamic: requires-dist
52
+ Dynamic: summary
53
+
54
+ # PyAutomation
55
+
56
+ The development intention of this framework is to provide the ability to develop industrial applications where processes need to be executed concurrently and field data need to be managed for monitoring, control, and supervision applications.
57
+
58
+
59
+
60
+ ![Core](docs/img/PyAutomationCore.svg)
61
+
62
+ In the image above, you can generally see the architecture and interaction of the different modules that make up the framework.
63
+
64
+ For this, state machines are available that run in the background and concurrently to acquire data by query (DAQ), Acquire Data by Exception (DAS) and any other general purpose state machine.
65
+
66
+ It has a memory persistence module for real-time variables that we call (CVT, Current Value Table).
67
+
68
+ There is also an alarm management system
69
+
70
+ And finally, the disk persistence of the variables to provide functionalities for historical trends of the field variables.
71
+
72
+
73
+ # Run Config Page
74
+
75
+ ## Crearte Virtual Environment
76
+
77
+ ```python
78
+ python3 -m venv venv
79
+ . venv/bin/activate
80
+ ```
81
+ ## Install Dependencies
82
+
83
+ ```python
84
+ pip install --upgrade pip
85
+ pip install -r requirements.txt
86
+ ```
87
+
88
+ ## Run Config page
89
+
90
+ ```python
91
+ python run.py
92
+ ```
93
+
94
+ or
95
+
96
+ ```python
97
+
98
+ ./docker-entrypoint.sh
99
+ ```
100
+
101
+ ## Deploy documentation on Development mode With mkautodoc
102
+
103
+ ### Install Wheel
104
+
105
+ ```python
106
+ pip install wheel
107
+ ```
108
+
109
+ ### Create PyAutomation's package
110
+
111
+ Execute this code where is setup.py file
112
+
113
+ ```python
114
+ python3 setup.py bdist_wheel
115
+ ```
116
+
117
+ This create some folders
118
+
119
+ - build
120
+ - dist
121
+ - PyAutomation.egg-info
122
+
123
+ ### Install PyAutomation Folder
124
+
125
+ Located into "dist" folder
126
+
127
+ ```python
128
+ pip install dist/PyAutomation-1.0.0-py3-none-any.whl
129
+ ```
130
+
131
+ After that, you can run mkdocs serve
132
+
133
+
134
+ # Deploy
135
+
136
+ Make the following `.env` file:
137
+
138
+ ```
139
+ PORT=5000
140
+ ```
141
+
142
+ ## Docker
143
+
144
+ Export environment variables
145
+
146
+ ```
147
+ export $(grep -v '^#' .env | xargs)
148
+ ```
149
+
150
+ Start the app
151
+
152
+ ```
153
+ sudo docker run -d \
154
+ --name PyAutomation \
155
+ -p ${PORT}:${PORT}\
156
+ -v $(pwd)/temp/db:/app/db \
157
+ -v $(pwd)/temp/logs:/app/logs \
158
+ -e PORT=${PORT} \
159
+ knowai/automation:1.0.0
160
+ ```
161
+
162
+ ## Docker Compose
163
+
164
+ If you want to deploy it using docker compose, make the following `docker-compose.yml` file:
165
+
166
+ ```YaMl
167
+ version: '3.3'
168
+
169
+ services:
170
+
171
+ automation:
172
+ container_name: "PyAutomation"
173
+ image: "knowai/automation:1.0.0"
174
+ restart: always
175
+ ports:
176
+ - ${PORT}:${PORT}
177
+ volumes:
178
+ - ./temp/db:/app/db
179
+ - ./temp/logs:/app/logs
180
+ environment:
181
+ PORT: ${PORT}
182
+ OPCUA_SERVER_PORT: ${OPCUA_SERVER_PORT}
183
+ healthcheck:
184
+ test: ["CMD-SHELL", "curl --fail -s -k http://0.0.0.0:${PORT}/api/healthcheck/ || curl --fail -s -k https://0.0.0.0:${PORT}/api/healthcheck/ || exit 1"]
185
+ interval: 15s
186
+ timeout: 10s
187
+ retries: 3
188
+
189
+ ```
190
+
191
+ Start the docker compose file
192
+
193
+ ```
194
+ sudo docker-compose --env-file .env up -d
195
+ ```
196
+
197
+
198
+ Go to http://host:${PORT} to view the config page