PyAutomationIO 1.1.3__tar.gz → 1.1.4__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.
Files changed (149) hide show
  1. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/PKG-INFO +13 -2
  2. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/PyAutomationIO.egg-info/PKG-INFO +13 -2
  3. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/PyAutomationIO.egg-info/SOURCES.txt +3 -0
  4. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/README.md +12 -1
  5. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/__init__.py +1 -0
  6. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/core.py +136 -3
  7. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/modules/__init__.py +3 -1
  8. pyautomationio-1.1.4/automation/modules/settings/__init__.py +7 -0
  9. pyautomationio-1.1.4/automation/modules/settings/resources/__init__.py +5 -0
  10. pyautomationio-1.1.4/automation/modules/settings/resources/settings.py +79 -0
  11. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/utils/decorators.py +7 -1
  12. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/workers/logger.py +1 -1
  13. pyautomationio-1.1.4/version.py +2 -0
  14. pyautomationio-1.1.3/version.py +0 -2
  15. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/LICENSE +0 -0
  16. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/MANIFEST.in +0 -0
  17. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/PyAutomationIO.egg-info/dependency_links.txt +0 -0
  18. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/PyAutomationIO.egg-info/requires.txt +0 -0
  19. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/PyAutomationIO.egg-info/top_level.txt +0 -0
  20. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/alarms/__init__.py +0 -0
  21. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/alarms/states.py +0 -0
  22. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/alarms/trigger.py +0 -0
  23. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/buffer.py +0 -0
  24. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/dbmodels/__init__.py +0 -0
  25. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/dbmodels/alarms.py +0 -0
  26. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/dbmodels/core.py +0 -0
  27. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/dbmodels/events.py +0 -0
  28. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/dbmodels/logs.py +0 -0
  29. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/dbmodels/machines.py +0 -0
  30. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/dbmodels/opcua.py +0 -0
  31. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/dbmodels/opcua_server.py +0 -0
  32. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/dbmodels/tags.py +0 -0
  33. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/dbmodels/users.py +0 -0
  34. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/extensions/__init__.py +0 -0
  35. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/extensions/api.py +0 -0
  36. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/extensions/cors.py +0 -0
  37. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/filter/__init__.py +0 -0
  38. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/iad/__init__.py +0 -0
  39. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/iad/frozen_data.py +0 -0
  40. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/iad/out_of_range.py +0 -0
  41. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/iad/outliers.py +0 -0
  42. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/logger/__init__.py +0 -0
  43. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/logger/alarms.py +0 -0
  44. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/logger/core.py +0 -0
  45. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/logger/datalogger.py +0 -0
  46. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/logger/events.py +0 -0
  47. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/logger/logdict.py +0 -0
  48. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/logger/logs.py +0 -0
  49. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/logger/machines.py +0 -0
  50. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/logger/opcua_server.py +0 -0
  51. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/logger/users.py +0 -0
  52. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/managers/__init__.py +0 -0
  53. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/managers/alarms.py +0 -0
  54. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/managers/db.py +0 -0
  55. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/managers/opcua_client.py +0 -0
  56. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/managers/state_machine.py +0 -0
  57. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/models.py +0 -0
  58. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/modules/alarms/__init__.py +0 -0
  59. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/modules/alarms/resources/__init__.py +0 -0
  60. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/modules/alarms/resources/alarms.py +0 -0
  61. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/modules/alarms/resources/summary.py +0 -0
  62. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/modules/events/__init__.py +0 -0
  63. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/modules/events/resources/__init__.py +0 -0
  64. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/modules/events/resources/events.py +0 -0
  65. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/modules/events/resources/logs.py +0 -0
  66. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/modules/tags/__init__.py +0 -0
  67. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/modules/tags/resources/__init__.py +0 -0
  68. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/modules/tags/resources/tags.py +0 -0
  69. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/modules/users/__init__.py +0 -0
  70. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/modules/users/resources/__init__.py +0 -0
  71. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/modules/users/resources/models/__init__.py +0 -0
  72. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/modules/users/resources/models/roles.py +0 -0
  73. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/modules/users/resources/models/users.py +0 -0
  74. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/modules/users/resources/roles.py +0 -0
  75. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/modules/users/resources/users.py +0 -0
  76. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/modules/users/roles.py +0 -0
  77. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/modules/users/users.py +0 -0
  78. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/opcua/__init__.py +0 -0
  79. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/opcua/models.py +0 -0
  80. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/opcua/subscription.py +0 -0
  81. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/pages/__init__.py +0 -0
  82. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/pages/alarms.py +0 -0
  83. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/pages/alarms_history.py +0 -0
  84. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/pages/assets/styles.css +0 -0
  85. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/pages/callbacks/__init__.py +0 -0
  86. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/pages/callbacks/alarms.py +0 -0
  87. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/pages/callbacks/alarms_summary.py +0 -0
  88. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/pages/callbacks/db.py +0 -0
  89. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/pages/callbacks/filter.py +0 -0
  90. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/pages/callbacks/machines.py +0 -0
  91. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/pages/callbacks/machines_detailed.py +0 -0
  92. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/pages/callbacks/opcua.py +0 -0
  93. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/pages/callbacks/opcua_server.py +0 -0
  94. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/pages/callbacks/tags.py +0 -0
  95. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/pages/callbacks/trends.py +0 -0
  96. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/pages/communications.py +0 -0
  97. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/pages/components/__init__.py +0 -0
  98. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/pages/components/alarms.py +0 -0
  99. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/pages/components/alarms_summary.py +0 -0
  100. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/pages/components/database.py +0 -0
  101. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/pages/components/gaussian_filter.py +0 -0
  102. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/pages/components/machines.py +0 -0
  103. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/pages/components/opcua.py +0 -0
  104. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/pages/components/opcua_server.py +0 -0
  105. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/pages/components/tags.py +0 -0
  106. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/pages/components/trends.py +0 -0
  107. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/pages/database.py +0 -0
  108. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/pages/filter.py +0 -0
  109. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/pages/machines.py +0 -0
  110. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/pages/machines_detailed.py +0 -0
  111. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/pages/main.py +0 -0
  112. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/pages/opcua_server.py +0 -0
  113. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/pages/tags.py +0 -0
  114. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/pages/trends.py +0 -0
  115. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/singleton.py +0 -0
  116. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/state_machine.py +0 -0
  117. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/tags/__init__.py +0 -0
  118. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/tags/cvt.py +0 -0
  119. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/tags/filter.py +0 -0
  120. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/tags/tag.py +0 -0
  121. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/tests/__init__.py +0 -0
  122. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/tests/test_alarms.py +0 -0
  123. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/tests/test_core.py +0 -0
  124. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/tests/test_unit.py +0 -0
  125. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/tests/test_user.py +0 -0
  126. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/utils/__init__.py +0 -0
  127. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/utils/observer.py +0 -0
  128. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/utils/units.py +0 -0
  129. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/variables/__init__.py +0 -0
  130. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/variables/adimentional.py +0 -0
  131. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/variables/current.py +0 -0
  132. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/variables/density.py +0 -0
  133. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/variables/eng_time.py +0 -0
  134. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/variables/force.py +0 -0
  135. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/variables/length.py +0 -0
  136. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/variables/mass.py +0 -0
  137. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/variables/mass_flow.py +0 -0
  138. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/variables/percentage.py +0 -0
  139. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/variables/power.py +0 -0
  140. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/variables/pressure.py +0 -0
  141. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/variables/temperature.py +0 -0
  142. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/variables/volume.py +0 -0
  143. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/variables/volumetric_flow.py +0 -0
  144. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/workers/__init__.py +0 -0
  145. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/workers/state_machine.py +0 -0
  146. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/automation/workers/worker.py +0 -0
  147. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/requirements.txt +0 -0
  148. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/setup.cfg +0 -0
  149. {pyautomationio-1.1.3 → pyautomationio-1.1.4}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyAutomationIO
3
- Version: 1.1.3
3
+ Version: 1.1.4
4
4
  Summary: A python library to develop automation continuous tasks using sync or async concurrent threads
5
5
  Home-page: https://github.com/know-ai/PyAutomation
6
6
  Author: KnowAI
@@ -117,10 +117,22 @@ services:
117
117
  volumes:
118
118
  - automation_db:/app/db
119
119
  - automation_logs:/app/logs
120
+ logging:
121
+ driver: "json-file"
122
+ options:
123
+ max-size: "10m" # Rota cuando llega a 10MB
124
+ max-file: "3" # Guarda máximo 3 archivos (30MB total)
120
125
  environment:
121
126
  OPCUA_SERVER_PORT: ${AUTOMATION_OPCUA_SERVER_PORT}
122
127
  LOG_MAX_BYTES: ${AUTOMATION_LOG_MAX_BYTES}
123
128
  LOG_BACKUP_COUNT: ${AUTOMATION_LOG_BACKUP_COUNT}
129
+ tmpfs:
130
+ - /tmp:size=500k
131
+ deploy:
132
+ resources:
133
+ limits:
134
+ cpus: "0.5"
135
+ memory: 256M
124
136
  healthcheck:
125
137
  test: ["CMD", "python", "/app/healthcheck.py"]
126
138
  interval: 15s
@@ -130,7 +142,6 @@ services:
130
142
  volumes:
131
143
  automation_db:
132
144
  automation_logs:
133
-
134
145
  ```
135
146
 
136
147
  Start the docker compose file
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyAutomationIO
3
- Version: 1.1.3
3
+ Version: 1.1.4
4
4
  Summary: A python library to develop automation continuous tasks using sync or async concurrent threads
5
5
  Home-page: https://github.com/know-ai/PyAutomation
6
6
  Author: KnowAI
@@ -117,10 +117,22 @@ services:
117
117
  volumes:
118
118
  - automation_db:/app/db
119
119
  - automation_logs:/app/logs
120
+ logging:
121
+ driver: "json-file"
122
+ options:
123
+ max-size: "10m" # Rota cuando llega a 10MB
124
+ max-file: "3" # Guarda máximo 3 archivos (30MB total)
120
125
  environment:
121
126
  OPCUA_SERVER_PORT: ${AUTOMATION_OPCUA_SERVER_PORT}
122
127
  LOG_MAX_BYTES: ${AUTOMATION_LOG_MAX_BYTES}
123
128
  LOG_BACKUP_COUNT: ${AUTOMATION_LOG_BACKUP_COUNT}
129
+ tmpfs:
130
+ - /tmp:size=500k
131
+ deploy:
132
+ resources:
133
+ limits:
134
+ cpus: "0.5"
135
+ memory: 256M
124
136
  healthcheck:
125
137
  test: ["CMD", "python", "/app/healthcheck.py"]
126
138
  interval: 15s
@@ -130,7 +142,6 @@ services:
130
142
  volumes:
131
143
  automation_db:
132
144
  automation_logs:
133
-
134
145
  ```
135
146
 
136
147
  Start the docker compose file
@@ -60,6 +60,9 @@ automation/modules/events/__init__.py
60
60
  automation/modules/events/resources/__init__.py
61
61
  automation/modules/events/resources/events.py
62
62
  automation/modules/events/resources/logs.py
63
+ automation/modules/settings/__init__.py
64
+ automation/modules/settings/resources/__init__.py
65
+ automation/modules/settings/resources/settings.py
63
66
  automation/modules/tags/__init__.py
64
67
  automation/modules/tags/resources/__init__.py
65
68
  automation/modules/tags/resources/tags.py
@@ -64,10 +64,22 @@ services:
64
64
  volumes:
65
65
  - automation_db:/app/db
66
66
  - automation_logs:/app/logs
67
+ logging:
68
+ driver: "json-file"
69
+ options:
70
+ max-size: "10m" # Rota cuando llega a 10MB
71
+ max-file: "3" # Guarda máximo 3 archivos (30MB total)
67
72
  environment:
68
73
  OPCUA_SERVER_PORT: ${AUTOMATION_OPCUA_SERVER_PORT}
69
74
  LOG_MAX_BYTES: ${AUTOMATION_LOG_MAX_BYTES}
70
75
  LOG_BACKUP_COUNT: ${AUTOMATION_LOG_BACKUP_COUNT}
76
+ tmpfs:
77
+ - /tmp:size=500k
78
+ deploy:
79
+ resources:
80
+ limits:
81
+ cpus: "0.5"
82
+ memory: 256M
71
83
  healthcheck:
72
84
  test: ["CMD", "python", "/app/healthcheck.py"]
73
85
  interval: 15s
@@ -77,7 +89,6 @@ services:
77
89
  volumes:
78
90
  automation_db:
79
91
  automation_logs:
80
-
81
92
  ```
82
93
 
83
94
  Start the docker compose file
@@ -17,6 +17,7 @@ if not os.path.isfile(CERT_FILE):
17
17
  if not os.path.isfile(KEY_FILE):
18
18
  KEY_FILE = None
19
19
  OPCUA_SERVER_PORT = os.environ.get('OPCUA_SERVER_PORT') or "53530"
20
+ LOGGER_PERIOD = os.environ.get('LOGGER_PERIOD') or 10.0
20
21
 
21
22
 
22
23
  class CreateApp():
@@ -1066,6 +1066,50 @@ class PyAutomation(Singleton):
1066
1066
  logging.warning(message)
1067
1067
  return None
1068
1068
 
1069
+ @logging_error_handler
1070
+ def set_app_config(self, **kwargs):
1071
+ r"""
1072
+ Update app configuration into app_config.json
1073
+ """
1074
+ try:
1075
+ config_path = os.path.join(".", "db", "app_config.json")
1076
+ config = {}
1077
+
1078
+ if os.path.exists(config_path):
1079
+ with open(config_path, 'r') as f:
1080
+ config = json.load(f)
1081
+
1082
+ config.update(kwargs)
1083
+
1084
+ with open(config_path, 'w') as f:
1085
+ json.dump(config, f, indent=4)
1086
+
1087
+ except Exception as e:
1088
+ logging.error(f"Failed to persist app config: {e}")
1089
+
1090
+ @logging_error_handler
1091
+ def get_app_config(self) -> dict:
1092
+ r"""
1093
+ Get app configuration from app_config.json
1094
+ """
1095
+ try:
1096
+ config_path = os.path.join(".", "db", "app_config.json")
1097
+ if os.path.exists(config_path):
1098
+ with open(config_path, 'r') as f:
1099
+ return json.load(f)
1100
+ else:
1101
+ # Create default config if not exists
1102
+ default_config = {
1103
+ "logger_period": 10.0,
1104
+ "log_level": 20
1105
+ }
1106
+ self.set_app_config(**default_config)
1107
+ return default_config
1108
+
1109
+ except Exception as e:
1110
+ logging.error(f"Failed to read app config: {e}")
1111
+ return {"logger_period": 10.0, "log_level": 20}
1112
+
1069
1113
  @logging_error_handler
1070
1114
  @validate_types(output=bool)
1071
1115
  def is_db_connected(self):
@@ -1655,6 +1699,18 @@ class PyAutomation(Singleton):
1655
1699
 
1656
1700
  **Returns:** `None`
1657
1701
  """
1702
+ str_date = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
1703
+ logging.critical(f"{str_date} Starting app with configuration:")
1704
+ logging.critical(f"{str_date} Logger period: {self.get_app_config().get('logger_period', 10.0)} seconds")
1705
+ logging.critical(f"{str_date} Log max bytes: {self.get_app_config().get('log_max_bytes', 10 * 1024 * 1024)} bytes")
1706
+ logging.critical(f"{str_date} Log backup count: {self.get_app_config().get('log_backup_count', 3)} backups")
1707
+ logging.critical(f"{str_date} Log level: {self.get_app_config().get('log_level', 20)}")
1708
+ print(f"[INFO] {str_date} Starting app with configuration:")
1709
+ print(f"[INFO] {str_date} Logger period: {self.get_app_config().get('logger_period', 10.0)} seconds")
1710
+ print(f"[INFO] {str_date} Log max bytes: {self.get_app_config().get('log_max_bytes', 10 * 1024 * 1024)} bytes")
1711
+ print(f"[INFO] {str_date} Log backup count: {self.get_app_config().get('log_backup_count', 3)} backups")
1712
+ print(f"[INFO] {str_date} Log level: {self.get_app_config().get('log_level', 20)}")
1713
+
1658
1714
  self.safe_start(test=test, create_tables=create_tables, machines=machines)
1659
1715
  self.create_system_user()
1660
1716
 
@@ -1694,6 +1750,23 @@ class PyAutomation(Singleton):
1694
1750
  self._create_tables = create_tables
1695
1751
  self.__start_logger()
1696
1752
  self.__start_workers(test=test, machines=machines)
1753
+ str_date = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
1754
+ logging.critical(f"{str_date} App started successfully")
1755
+ print(f"[INFO] {str_date} App started successfully")
1756
+
1757
+ @logging_error_handler
1758
+ @validate_types(period=float, output=None)
1759
+ def update_logger_period(self, period:float):
1760
+ r"""
1761
+ Update logger worker period
1762
+ """
1763
+ if hasattr(self, 'db_worker'):
1764
+ self.db_worker._period = period
1765
+ str_date = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
1766
+ logging.info(f"{str_date} Logger period updated to {period} seconds")
1767
+ print(f"[INFO] {str_date} Logger period updated to {period} seconds")
1768
+
1769
+ self.set_app_config(logger_period=period)
1697
1770
 
1698
1771
  @logging_error_handler
1699
1772
  @validate_types(output=None)
@@ -1723,8 +1796,11 @@ class PyAutomation(Singleton):
1723
1796
  * DASWorker
1724
1797
  """
1725
1798
  if self._create_tables:
1799
+
1800
+ app_config = self.get_app_config()
1801
+ logger_period = float(app_config.get("logger_period", 10.0))
1726
1802
 
1727
- self.db_worker = LoggerWorker(self.db_manager)
1803
+ self.db_worker = LoggerWorker(self.db_manager, period=logger_period)
1728
1804
  self.connect_to_db(test=test)
1729
1805
  self.db_worker.start()
1730
1806
 
@@ -1753,6 +1829,50 @@ class PyAutomation(Singleton):
1753
1829
  if hasattr(self, 'subscription_monitor'):
1754
1830
  self.subscription_monitor.stop()
1755
1831
 
1832
+ @logging_error_handler
1833
+ @validate_types(level=int, output=None)
1834
+ def update_log_level(self, level:int):
1835
+ r"""
1836
+ Update logger level dynamically and persist it.
1837
+ """
1838
+ self._logging_level = level
1839
+
1840
+ # Update root logger
1841
+ root_logger = logging.getLogger()
1842
+ root_logger.setLevel(level)
1843
+
1844
+ # Update app logger
1845
+ app_logger = logging.getLogger("pyautomation")
1846
+ app_logger.setLevel(level)
1847
+
1848
+ logging.log(level, f"Log level updated to {level}")
1849
+
1850
+ # Persist to config
1851
+ self.set_app_config(log_level=level)
1852
+
1853
+ @logging_error_handler
1854
+ @validate_types(max_bytes=int, backup_count=int, output=None)
1855
+ def update_log_config(self, max_bytes:int, backup_count:int):
1856
+ r"""
1857
+ Update logger configuration (maxBytes and backupCount) dynamically and persist it.
1858
+ """
1859
+ # 1. Update runtime logger
1860
+ root_logger = logging.getLogger()
1861
+ for handler in root_logger.handlers:
1862
+ if isinstance(handler, RotatingFileHandler):
1863
+ # Update handler properties
1864
+ handler.maxBytes = max_bytes
1865
+ handler.backupCount = backup_count
1866
+ # Trigger rollover if needed (optional, or wait for next write)
1867
+ # handler.doRollover()
1868
+ str_date = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
1869
+ logging.info(f"{str_date} Log configuration updated: maxBytes={max_bytes}, backupCount={backup_count}")
1870
+ print(f"[INFO] {str_date} Log configuration updated: maxBytes={max_bytes}, backupCount={backup_count}")
1871
+ break
1872
+
1873
+ # 2. Persist to config
1874
+ self.set_app_config(log_max_bytes=max_bytes, log_backup_count=backup_count)
1875
+
1756
1876
  @logging_error_handler
1757
1877
  @validate_types(output=None)
1758
1878
  def __start_logger(self)->None:
@@ -1768,13 +1888,26 @@ class PyAutomation(Singleton):
1768
1888
  logging.getLogger('opcua').setLevel(logging.CRITICAL)
1769
1889
  # Configure root logger with rotating file handler (size-based)
1770
1890
  root_logger = logging.getLogger()
1891
+
1892
+ # Load level from config
1893
+ app_config = self.get_app_config()
1894
+ # Default logging.INFO (20) if not set in config, overriding __init__ warning default
1895
+ persisted_level = int(app_config.get('log_level', logging.INFO))
1896
+ self._logging_level = persisted_level
1897
+
1771
1898
  root_logger.setLevel(self._logging_level)
1772
1899
  # Clear existing handlers to avoid duplicates
1773
1900
  for _h in list(root_logger.handlers):
1774
1901
  root_logger.removeHandler(_h)
1775
1902
 
1776
- max_bytes = int(os.environ.get('LOG_MAX_BYTES', 10 * 1024 * 1024)) # 10MB
1777
- backup_count = int(os.environ.get('LOG_BACKUP_COUNT', 3)) # 3 backups
1903
+ app_config = self.get_app_config()
1904
+ # Default fallback to env or hardcoded
1905
+ env_max_bytes = int(os.environ.get('LOG_MAX_BYTES', 10 * 1024 * 1024))
1906
+ env_backup_count = int(os.environ.get('LOG_BACKUP_COUNT', 3))
1907
+
1908
+ max_bytes = int(app_config.get('log_max_bytes', env_max_bytes))
1909
+ backup_count = int(app_config.get('log_backup_count', env_backup_count))
1910
+
1778
1911
  handler = RotatingFileHandler(
1779
1912
  filename=self._log_file,
1780
1913
  maxBytes=max_bytes,
@@ -7,8 +7,10 @@ def init_app(app):
7
7
  from ..modules.alarms.resources import init_app as init_alarms
8
8
  from ..modules.users.resources import init_app as init_users
9
9
  from ..modules.events.resources import init_app as init_events
10
+ from ..modules.settings.resources import init_app as init_settings
10
11
 
11
12
  init_tags()
12
13
  init_alarms()
13
14
  init_users()
14
- init_events()
15
+ init_events()
16
+ init_settings()
@@ -0,0 +1,7 @@
1
+ # -*- coding: utf-8 -*-
2
+ """automation/modules/settings/__init__.py
3
+
4
+ This module implements settings resources.
5
+ """
6
+ from .resources.settings import ns
7
+
@@ -0,0 +1,5 @@
1
+ from .settings import ns
2
+
3
+ def init_app():
4
+ from ....extensions.api import api
5
+ api.add_namespace(ns, path="/settings")
@@ -0,0 +1,79 @@
1
+ from flask_restx import Namespace, Resource, fields
2
+ from .... import PyAutomation
3
+ from ....extensions.api import api
4
+ from ....extensions import _api as Api
5
+
6
+ ns = Namespace('Settings', description='Settings')
7
+ app = PyAutomation()
8
+
9
+ settings_model = api.model("settings_model", {
10
+ 'logger_period': fields.Float(required=False, min=1.0, description='Logger worker period in seconds'),
11
+ 'log_max_bytes': fields.Integer(required=False, min=1024, description='Max bytes for log file rotation'),
12
+ 'log_backup_count': fields.Integer(required=False, min=1, description='Number of backup log files to keep'),
13
+ 'log_level': fields.Integer(required=False, min=0, max=50, description='Logging level (0=NOTSET, 10=DEBUG, 20=INFO, 30=WARNING, 40=ERROR, 50=CRITICAL)')
14
+ })
15
+
16
+ @ns.route('/update')
17
+ class SettingsUpdateResource(Resource):
18
+
19
+ @api.doc(security='apikey')
20
+ @Api.token_required(auth=True)
21
+ @ns.expect(settings_model)
22
+ def put(self):
23
+ """
24
+ Update application settings (Logger period, Log rotation config, Log level)
25
+ """
26
+ data = api.payload
27
+
28
+ # 1. Update Logger Worker Period
29
+ if 'logger_period' in data:
30
+ logger_period = data['logger_period']
31
+ if logger_period < 1.0:
32
+ return "Logger period must be >= 1.0", 400
33
+ app.update_logger_period(logger_period)
34
+
35
+ # 2. Update Log Rotation Config
36
+ if 'log_max_bytes' in data and 'log_backup_count' in data:
37
+ max_bytes = data['log_max_bytes']
38
+ backup_count = data['log_backup_count']
39
+
40
+ if max_bytes < 1024:
41
+ return "log_max_bytes must be >= 1024", 400
42
+ if backup_count < 1:
43
+ return "log_backup_count must be >= 1", 400
44
+
45
+ app.update_log_config(max_bytes, backup_count)
46
+
47
+ elif 'log_max_bytes' in data or 'log_backup_count' in data:
48
+ return "Both log_max_bytes and log_backup_count must be provided together", 400
49
+
50
+ # 3. Update Log Level
51
+ if 'log_level' in data:
52
+ log_level = data['log_level']
53
+ # Basic validation for standard levels
54
+ if log_level not in [0, 10, 20, 30, 40, 50]:
55
+ return "Invalid log_level. Use standard Python logging levels (10, 20, 30, 40, 50)", 400
56
+
57
+ app.update_log_level(log_level)
58
+
59
+ return "Settings updated", 200
60
+
61
+ @ns.route('/logger_period')
62
+ class LoggerPeriodResource(Resource):
63
+
64
+ @api.doc(security='apikey')
65
+ @Api.token_required(auth=True)
66
+ @ns.expect(settings_model)
67
+ def put(self):
68
+ """
69
+ Update logger worker period
70
+ """
71
+ logger_period = api.payload.get('logger_period')
72
+
73
+ if logger_period < 1.0:
74
+ return "Logger period must be >= 1.0", 400
75
+
76
+ app.update_logger_period(logger_period)
77
+
78
+ return "Logger period updated", 200
79
+
@@ -1,4 +1,4 @@
1
- import functools, logging, sys
1
+ import functools, logging, sys, datetime
2
2
  from ..modules.users.users import User, Users
3
3
  from ..logger.events import EventsLoggerEngine
4
4
 
@@ -159,6 +159,8 @@ def validate_types(**validations):
159
159
  message = f"Expected output type ({counter}) {expected}, but got {type(result[counter])} in func {func}"
160
160
  logger = logging.getLogger("pyautomation")
161
161
  logger.error(message)
162
+ str_date = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
163
+ print(f"[ERROR] {str_date} {message}")
162
164
  raise TypeError(message)
163
165
 
164
166
  else:
@@ -167,6 +169,8 @@ def validate_types(**validations):
167
169
  message = f"Expected output type {_output}, but got {type(result)} in func {func}"
168
170
  logger = logging.getLogger("pyautomation")
169
171
  logger.error(message)
172
+ str_date = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
173
+ print(f"[ERROR] {str_date} {message}")
170
174
  raise TypeError(message)
171
175
 
172
176
  return result
@@ -201,6 +205,8 @@ def logging_error_handler(func, args, kwargs):
201
205
  })
202
206
  logger = logging.getLogger("pyautomation")
203
207
  logger.error(msg=msg)
208
+ str_date = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
209
+ print(f"[ERROR] {str_date} {msg}")
204
210
 
205
211
  @decorator
206
212
  def db_rollback(func, args, kwargs):
@@ -19,7 +19,7 @@ from ..dbmodels.logs import Logs
19
19
 
20
20
  class LoggerWorker(BaseWorker):
21
21
 
22
- def __init__(self, manager:DBManager, period:int=5.0):
22
+ def __init__(self, manager:DBManager, period:float=10.0):
23
23
 
24
24
  super(LoggerWorker, self).__init__()
25
25
 
@@ -0,0 +1,2 @@
1
+ __version__ = '1.1.4'
2
+
@@ -1,2 +0,0 @@
1
- __version__ = '1.1.3'
2
-
File without changes
File without changes
File without changes