sciveo 0.1.72__tar.gz → 0.1.73__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 (175) hide show
  1. {sciveo-0.1.72 → sciveo-0.1.73}/PKG-INFO +3 -1
  2. sciveo-0.1.73/sciveo/db/read_table.py +204 -0
  3. sciveo-0.1.73/sciveo/monitoring/power/ems300.py +108 -0
  4. sciveo-0.1.73/sciveo/monitoring/power/sim.py +71 -0
  5. sciveo-0.1.73/sciveo/monitoring/power/tools.py +378 -0
  6. sciveo-0.1.73/sciveo/tools/draw/__init__.py +0 -0
  7. sciveo-0.1.73/sciveo/version.py +2 -0
  8. sciveo-0.1.73/sciveo/web/__init__.py +0 -0
  9. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo.egg-info/PKG-INFO +3 -1
  10. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo.egg-info/SOURCES.txt +6 -0
  11. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo.egg-info/requires.txt +8 -0
  12. {sciveo-0.1.72 → sciveo-0.1.73}/setup.py +8 -0
  13. sciveo-0.1.72/sciveo/version.py +0 -2
  14. {sciveo-0.1.72 → sciveo-0.1.73}/README.md +0 -0
  15. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/__init__.py +0 -0
  16. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/api/__init__.py +0 -0
  17. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/api/base.py +0 -0
  18. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/api/predictors.py +0 -0
  19. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/api/server.py +0 -0
  20. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/api/upload.py +0 -0
  21. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/cli.py +0 -0
  22. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/common/__init__.py +0 -0
  23. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/common/configuration.py +0 -0
  24. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/common/model.py +0 -0
  25. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/common/optimizers.py +0 -0
  26. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/common/sampling.py +0 -0
  27. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/content/__init__.py +0 -0
  28. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/content/dataset.py +0 -0
  29. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/content/experiment.py +0 -0
  30. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/content/project.py +0 -0
  31. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/content/runner.py +0 -0
  32. {sciveo-0.1.72/sciveo/media → sciveo-0.1.73/sciveo/db}/__init__.py +0 -0
  33. {sciveo-0.1.72/sciveo/media/capture → sciveo-0.1.73/sciveo/media}/__init__.py +0 -0
  34. {sciveo-0.1.72/sciveo/media/ml → sciveo-0.1.73/sciveo/media/capture}/__init__.py +0 -0
  35. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/capture/cam.py +0 -0
  36. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/capture/motion_detection.py +0 -0
  37. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/capture/nvr.py +0 -0
  38. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/capture/readers.py +0 -0
  39. {sciveo-0.1.72/sciveo/media/ml/encoders → sciveo-0.1.73/sciveo/media/ml}/__init__.py +0 -0
  40. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/ml/base.py +0 -0
  41. {sciveo-0.1.72/sciveo/media/ml/nlp → sciveo-0.1.73/sciveo/media/ml/encoders}/__init__.py +0 -0
  42. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/ml/encoders/base.py +0 -0
  43. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/ml/encoders/normalizer.py +0 -0
  44. {sciveo-0.1.72/sciveo/media/ml/time_series → sciveo-0.1.73/sciveo/media/ml/nlp}/__init__.py +0 -0
  45. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/ml/nlp/search.py +0 -0
  46. {sciveo-0.1.72/sciveo/media/pipelines → sciveo-0.1.73/sciveo/media/ml/time_series}/__init__.py +0 -0
  47. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/ml/time_series/dataset.py +0 -0
  48. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/ml/time_series/predictor.py +0 -0
  49. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/ml/time_series/trainer.py +0 -0
  50. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/ml/time_series/window_generator.py +0 -0
  51. {sciveo-0.1.72/sciveo/media/pipelines/layouts → sciveo-0.1.73/sciveo/media/pipelines}/__init__.py +0 -0
  52. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/base.py +0 -0
  53. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/job_daemon.py +0 -0
  54. {sciveo-0.1.72/sciveo/media/pipelines/postprocessors → sciveo-0.1.73/sciveo/media/pipelines/layouts}/__init__.py +0 -0
  55. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/layouts/base.py +0 -0
  56. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/pipeline.py +0 -0
  57. {sciveo-0.1.72/sciveo/media/pipelines/processors → sciveo-0.1.73/sciveo/media/pipelines/postprocessors}/__init__.py +0 -0
  58. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/postprocessors/base.py +0 -0
  59. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/postprocessors/default.py +0 -0
  60. {sciveo-0.1.72/sciveo/media/pipelines/processors/audio → sciveo-0.1.73/sciveo/media/pipelines/processors}/__init__.py +0 -0
  61. {sciveo-0.1.72/sciveo/media/pipelines/processors/file → sciveo-0.1.73/sciveo/media/pipelines/processors/audio}/__init__.py +0 -0
  62. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/processors/audio/audio.py +0 -0
  63. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/processors/audio/audio_extractor_process.py +0 -0
  64. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/processors/aws.py +0 -0
  65. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/processors/base.py +0 -0
  66. {sciveo-0.1.72/sciveo/media/pipelines/processors/image → sciveo-0.1.73/sciveo/media/pipelines/processors/file}/__init__.py +0 -0
  67. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/processors/file/archive.py +0 -0
  68. {sciveo-0.1.72/sciveo/media/pipelines/processors/nlp → sciveo-0.1.73/sciveo/media/pipelines/processors/image}/__init__.py +0 -0
  69. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/processors/image/album.py +0 -0
  70. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/processors/image/album_in_image.py +0 -0
  71. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/processors/image/depth_esimation.py +0 -0
  72. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/processors/image/embeddings.py +0 -0
  73. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/processors/image/filters.py +0 -0
  74. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/processors/image/generators.py +0 -0
  75. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/processors/image/histogram.py +0 -0
  76. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/processors/image/mask.py +0 -0
  77. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/processors/image/object_detection.py +0 -0
  78. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/processors/image/resize.py +0 -0
  79. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/processors/image/segmentation.py +0 -0
  80. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/processors/image/watermark.py +0 -0
  81. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/processors/media_info.py +0 -0
  82. {sciveo-0.1.72/sciveo/media/pipelines/processors/sci → sciveo-0.1.73/sciveo/media/pipelines/processors/nlp}/__init__.py +0 -0
  83. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/processors/nlp/address.py +0 -0
  84. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/processors/qr.py +0 -0
  85. {sciveo-0.1.72/sciveo/media/pipelines/processors/sci/time_series → sciveo-0.1.73/sciveo/media/pipelines/processors/sci}/__init__.py +0 -0
  86. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/processors/sci/base.py +0 -0
  87. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/processors/sci/dataset.py +0 -0
  88. {sciveo-0.1.72/sciveo/media/pipelines/processors/video → sciveo-0.1.73/sciveo/media/pipelines/processors/sci/time_series}/__init__.py +0 -0
  89. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/processors/sci/time_series/predictor.py +0 -0
  90. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/processors/sci/time_series/trainer.py +0 -0
  91. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/processors/tpu_base.py +0 -0
  92. {sciveo-0.1.72/sciveo/media/pipelines/web → sciveo-0.1.73/sciveo/media/pipelines/processors/video}/__init__.py +0 -0
  93. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/processors/video/generators.py +0 -0
  94. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/processors/video/motion_detection.py +0 -0
  95. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/processors/video/resize.py +0 -0
  96. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/processors/video/video_album.py +0 -0
  97. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/processors/video/video_frames.py +0 -0
  98. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/processors/video/video_resample.py +0 -0
  99. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/queues.py +0 -0
  100. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/server.py +0 -0
  101. {sciveo-0.1.72/sciveo/media/tools → sciveo-0.1.73/sciveo/media/pipelines/web}/__init__.py +0 -0
  102. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/pipelines/web/server.py +0 -0
  103. {sciveo-0.1.72/sciveo/ml → sciveo-0.1.73/sciveo/media/tools}/__init__.py +0 -0
  104. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/media/tools/video_interactive.py +0 -0
  105. {sciveo-0.1.72/sciveo/ml/dataset → sciveo-0.1.73/sciveo/ml}/__init__.py +0 -0
  106. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/ml/base.py +0 -0
  107. {sciveo-0.1.72/sciveo/ml/evaluation → sciveo-0.1.73/sciveo/ml/dataset}/__init__.py +0 -0
  108. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/ml/dataset/object_detection.py +0 -0
  109. {sciveo-0.1.72/sciveo/ml/images → sciveo-0.1.73/sciveo/ml/evaluation}/__init__.py +0 -0
  110. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/ml/evaluation/markdown.py +0 -0
  111. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/ml/evaluation/object_detection.py +0 -0
  112. {sciveo-0.1.72/sciveo/ml/nlp → sciveo-0.1.73/sciveo/ml/images}/__init__.py +0 -0
  113. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/ml/images/base.py +0 -0
  114. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/ml/images/description.py +0 -0
  115. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/ml/images/embeddings.py +0 -0
  116. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/ml/images/object_detection.py +0 -0
  117. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/ml/images/tools.py +0 -0
  118. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/ml/images/transformers.py +0 -0
  119. {sciveo-0.1.72/sciveo/ml/nlp/tokenizers → sciveo-0.1.73/sciveo/ml/nlp}/__init__.py +0 -0
  120. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/ml/nlp/embeddings.py +0 -0
  121. {sciveo-0.1.72/sciveo/ml/video → sciveo-0.1.73/sciveo/ml/nlp/tokenizers}/__init__.py +0 -0
  122. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/ml/nlp/tokenizers/bpe.py +0 -0
  123. {sciveo-0.1.72/sciveo/monitoring → sciveo-0.1.73/sciveo/ml/video}/__init__.py +0 -0
  124. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/ml/video/description.py +0 -0
  125. {sciveo-0.1.72/sciveo/monitoring/watchdog → sciveo-0.1.73/sciveo/monitoring}/__init__.py +0 -0
  126. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/monitoring/monitor.py +0 -0
  127. {sciveo-0.1.72/sciveo/network → sciveo-0.1.73/sciveo/monitoring/power}/__init__.py +0 -0
  128. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/monitoring/start.py +0 -0
  129. {sciveo-0.1.72/sciveo/tools → sciveo-0.1.73/sciveo/monitoring/watchdog}/__init__.py +0 -0
  130. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/monitoring/watchdog/base.py +0 -0
  131. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/monitoring/watchdog/process.py +0 -0
  132. {sciveo-0.1.72/sciveo/tools/aws → sciveo-0.1.73/sciveo/network}/__init__.py +0 -0
  133. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/network/camera.py +0 -0
  134. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/network/sniffer.py +0 -0
  135. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/network/tools.py +0 -0
  136. {sciveo-0.1.72/sciveo/tools/draw → sciveo-0.1.73/sciveo/tools}/__init__.py +0 -0
  137. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/tools/array.py +0 -0
  138. {sciveo-0.1.72/sciveo/web → sciveo-0.1.73/sciveo/tools/aws}/__init__.py +0 -0
  139. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/tools/aws/priority_queue.py +0 -0
  140. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/tools/aws/s3.py +0 -0
  141. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/tools/common.py +0 -0
  142. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/tools/complexity.py +0 -0
  143. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/tools/compress.py +0 -0
  144. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/tools/configuration.py +0 -0
  145. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/tools/crypto.py +0 -0
  146. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/tools/daemon.py +0 -0
  147. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/tools/draw/contours.py +0 -0
  148. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/tools/formating.py +0 -0
  149. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/tools/hardware.py +0 -0
  150. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/tools/http.py +0 -0
  151. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/tools/logger.py +0 -0
  152. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/tools/os.py +0 -0
  153. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/tools/queue.py +0 -0
  154. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/tools/random.py +0 -0
  155. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/tools/remote.py +0 -0
  156. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/tools/simple_counter.py +0 -0
  157. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/tools/synchronized.py +0 -0
  158. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/tools/timers.py +0 -0
  159. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/tools/totp.py +0 -0
  160. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo/web/common.py +0 -0
  161. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo.egg-info/dependency_links.txt +0 -0
  162. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo.egg-info/entry_points.txt +0 -0
  163. {sciveo-0.1.72 → sciveo-0.1.73}/sciveo.egg-info/top_level.txt +0 -0
  164. {sciveo-0.1.72 → sciveo-0.1.73}/setup.cfg +0 -0
  165. {sciveo-0.1.72 → sciveo-0.1.73}/test/test_complexity.py +0 -0
  166. {sciveo-0.1.72 → sciveo-0.1.73}/test/test_compress.py +0 -0
  167. {sciveo-0.1.72 → sciveo-0.1.73}/test/test_configuration.py +0 -0
  168. {sciveo-0.1.72 → sciveo-0.1.73}/test/test_crypto.py +0 -0
  169. {sciveo-0.1.72 → sciveo-0.1.73}/test/test_eval_markdown.py +0 -0
  170. {sciveo-0.1.72 → sciveo-0.1.73}/test/test_ml_datasets.py +0 -0
  171. {sciveo-0.1.72 → sciveo-0.1.73}/test/test_monitoring.py +0 -0
  172. {sciveo-0.1.72 → sciveo-0.1.73}/test/test_runner.py +0 -0
  173. {sciveo-0.1.72 → sciveo-0.1.73}/test/test_sampling.py +0 -0
  174. {sciveo-0.1.72 → sciveo-0.1.73}/test/test_tokenizers.py +0 -0
  175. {sciveo-0.1.72 → sciveo-0.1.73}/test/test_totp.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sciveo
3
- Version: 0.1.72
3
+ Version: 0.1.73
4
4
  Description-Content-Type: text/markdown
5
5
  Provides-Extra: mon
6
6
  Provides-Extra: net
@@ -8,6 +8,8 @@ Provides-Extra: server
8
8
  Provides-Extra: media
9
9
  Provides-Extra: media-ml
10
10
  Provides-Extra: web
11
+ Provides-Extra: db
12
+ Provides-Extra: power
11
13
  Provides-Extra: all
12
14
  Provides-Extra: ml
13
15
 
@@ -0,0 +1,204 @@
1
+ #
2
+ # Pavlin Georgiev, Softel Labs
3
+ #
4
+ # This is a proprietary file and may not be copied,
5
+ # distributed, or modified without express permission
6
+ # from the owner. For licensing inquiries, please
7
+ # contact pavlin@softel.bg.
8
+ #
9
+ # 2025
10
+ #
11
+
12
+ import os
13
+ import json
14
+ import psycopg2
15
+ import numpy as np
16
+ import pandas as pd
17
+ from sciveo.tools.logger import *
18
+
19
+
20
+ class BaseTable:
21
+ def __init__(self, table_name, config=None, batch_size=10_000, save_path=None, id_col="id"):
22
+ self.table_name = table_name
23
+ self.batch_size = batch_size
24
+ self.save_path = save_path
25
+ self.id_col = id_col
26
+ self.last_id = None
27
+
28
+ if config is None:
29
+ self.config = {
30
+ 'dbname': os.environ.get('DB_NAME'),
31
+ 'user': os.environ.get('DB_USERNAME'),
32
+ 'password': os.environ.get('DB_PASSWORD'),
33
+ 'host': os.environ.get('DB_HOST', 'localhost'),
34
+ 'port': int(os.environ.get('DB_PORT', 5432)),
35
+ }
36
+ else:
37
+ self.config = config
38
+
39
+ self.conn = psycopg2.connect(**self.config)
40
+ self.cursor = self.conn.cursor()
41
+ self.df = pd.DataFrame()
42
+
43
+ def load_file(self, file_path=None):
44
+ if not os.path.exists(file_path):
45
+ warning(f"File not found: {file_path}")
46
+ self.latest_df = pd.DataFrame()
47
+ return self.latest_df
48
+
49
+ if file_path.endswith(".parquet"):
50
+ self.latest_df = pd.read_parquet(file_path)
51
+ else:
52
+ self.latest_df = pd.read_csv(file_path)
53
+ info(f"Loaded {len(self.latest_df)} records from {file_path}")
54
+ return self.latest_df
55
+
56
+ def load(self, base_path=None):
57
+ if base_path is None and self.save_path is not None:
58
+ base_path = self.save_path
59
+
60
+ if base_path is None:
61
+ self.df = pd.DataFrame()
62
+ return self.df
63
+
64
+ files = [os.path.join(base_path, f) for f in os.listdir(base_path) if f.startswith(self.table_name) and f.endswith(".parquet")]
65
+
66
+ if not files:
67
+ info(f"There are no files in {base_path} to read")
68
+ self.df = pd.DataFrame()
69
+ return self.df
70
+
71
+ dfs = []
72
+ for file_path in sorted(files):
73
+ try:
74
+ df_part = self.load_file(file_path)
75
+ from_id = int(df_part[self.id_col].min())
76
+ to_id = int(df_part[self.id_col].max())
77
+ dfs.append(df_part)
78
+ debug(f"Loaded {len(df_part)} records from {file_path} id: [{from_id} - {to_id}]")
79
+ except Exception as e:
80
+ warning(f"Failed to load {file_path}: {e}")
81
+
82
+ if dfs:
83
+ self.df = pd.concat(dfs, ignore_index=True)
84
+ else:
85
+ self.df = pd.DataFrame()
86
+
87
+ self.last_id = int(self.df[self.id_col].max()) if not self.df.empty else None
88
+
89
+ info(f"Total loaded records: {len(self.df)} last_id: {self.last_id}")
90
+ return self.df
91
+
92
+ def _sanitize_df(self, df):
93
+ PANDAS_MIN_YEAR = 1677
94
+ PANDAS_MAX_YEAR = 2262
95
+ datetime_cols = []
96
+
97
+ # datetime cleanup
98
+ for col in df.columns:
99
+ if pd.api.types.is_datetime64_any_dtype(df[col]) or 'date' in col.lower() or 'time' in col.lower():
100
+ datetime_cols.append(col)
101
+
102
+ for col in datetime_cols:
103
+ def safe_dt(x):
104
+ if pd.isna(x):
105
+ return pd.NaT
106
+ if isinstance(x, str):
107
+ return pd.to_datetime(x, errors='coerce')
108
+ if hasattr(x, 'year'):
109
+ year = x.year
110
+ else:
111
+ return pd.NaT
112
+ if PANDAS_MIN_YEAR <= year <= PANDAS_MAX_YEAR:
113
+ return pd.Timestamp(x)
114
+ return pd.NaT
115
+ df[col] = df[col].apply(safe_dt)
116
+
117
+ # drop rows with invalid datetime
118
+ df = df.dropna(subset=datetime_cols)
119
+
120
+ # object columns: convert dict/list to JSON string
121
+ for col in df.columns:
122
+ if df[col].dtype == object:
123
+ df[col] = df[col].apply(lambda x: json.dumps(x) if isinstance(x, (dict, list)) else str(x) if x is not None else None)
124
+
125
+ df = df.reset_index(drop=True)
126
+ return df
127
+
128
+ def fetch_next_batch(self, last_id=None):
129
+ query = f"SELECT * FROM {self.table_name}"
130
+ if last_id is not None:
131
+ query += f" WHERE id > {last_id}"
132
+ query += f" ORDER BY id ASC LIMIT {self.batch_size};"
133
+
134
+ self.cursor.execute(query)
135
+ rows = self.cursor.fetchall()
136
+ if not rows:
137
+ return pd.DataFrame()
138
+
139
+ colnames = [desc[0] for desc in self.cursor.description]
140
+ df_new = pd.DataFrame(rows, columns=colnames)
141
+ return self._sanitize_df(df_new)
142
+
143
+ def append(self, df_new):
144
+ if not df_new.empty:
145
+ self.df = pd.concat([self.df, df_new], ignore_index=True)
146
+ debug(f"Added {len(df_new)} new rows (total {len(self.df)})")
147
+ self.last_id = int(self.df[self.id_col].max()) if not self.df.empty else None
148
+ debug(f"last {self.id_col} [{self.last_id}]")
149
+
150
+ def sync_incremental(self, last_id=None):
151
+ if last_id is None:
152
+ last_id = int(self.df[self.id_col].max()) if not self.df.empty else None
153
+ debug("reading from", last_id)
154
+ df_new = self.fetch_next_batch(last_id)
155
+ self.append(df_new)
156
+ if df_new.empty:
157
+ info("No new rows found")
158
+ return df_new
159
+
160
+ def save(self, file_path=None):
161
+ if self.save_path is not None and self.last_id is not None:
162
+ from_id = int(self.latest_df[self.id_col].min())
163
+ to_id = int(self.latest_df[self.id_col].max())
164
+ file_path = os.path.join(self.save_path, f"{self.table_name}--{from_id}-{to_id}.parquet")
165
+
166
+ if file_path.endswith(".parquet"):
167
+ self.latest_df.to_parquet(file_path, index=False)
168
+ else:
169
+ self.latest_df.to_csv(file_path, index=False)
170
+ info(f"Saved {len(self.latest_df)} rows to {file_path}")
171
+
172
+ def update(self, last_id=None):
173
+ if self.df.empty:
174
+ self.load()
175
+ self.latest_df = self.sync_incremental(last_id=last_id)
176
+ self.save()
177
+ return self.latest_df
178
+
179
+ def close(self):
180
+ self.cursor.close()
181
+ self.conn.close()
182
+
183
+ def __enter__(self):
184
+ return self
185
+
186
+ def __exit__(self, exc_type, exc_val, exc_tb):
187
+ self.close()
188
+
189
+
190
+ if __name__ == "__main__":
191
+ with BaseTable("video_record_predictions", batch_size=10_000, save_path="/home/ml/video_predictions/data") as T:
192
+
193
+ T.update(last_id=1_000_000)
194
+ T.load()
195
+ if not T.df.empty:
196
+ debug(T.df.columns)
197
+ debug(T.df[["key", "people", "is_valid", "updated_at", "data"]].head())
198
+
199
+ while(True):
200
+ df = T.update()
201
+ if df.empty:
202
+ info("Finished", T.df.shape)
203
+ break
204
+ info("read", df.shape, df[["key", "people"]].head())
@@ -0,0 +1,108 @@
1
+ #
2
+ # Stanislav Georgiev, Softel Labs
3
+ #
4
+ # This is a proprietary file and may not be copied,
5
+ # distributed, or modified without express permission
6
+ # from the owner. For licensing inquiries, please
7
+ # contact s.georgiev@softel.bg.
8
+ #
9
+ # 2025
10
+ #
11
+
12
+ import time
13
+ from pymodbus.client import ModbusTcpClient
14
+ from sciveo.tools.logger import *
15
+ from sciveo.tools.daemon import DaemonBase
16
+ from sciveo.monitoring.power.tools import *
17
+
18
+
19
+ class PowerEMS300(DaemonBase):
20
+ def __init__(self, host, port=502, device_id=247, delay=0.01, period=30):
21
+ super().__init__(period=period)
22
+ self.host = host
23
+ self.port = port
24
+ self.device_id = device_id
25
+ self.delay = delay
26
+ self.client = None
27
+
28
+ self.client = ModbusTcpClient(host=self.host, port=self.port)
29
+ self.connected = False
30
+
31
+ def connect(self):
32
+ if not (self.connected or self.client.connect()):
33
+ error("Connect FAIL", (self.host, self.port))
34
+ self.connected = False
35
+ else:
36
+ self.connected = True
37
+ return self.connected
38
+
39
+ def loop(self):
40
+ self.connect()
41
+ if not self.connected:
42
+ warning("Not connected", (self.host, self.port))
43
+ return
44
+
45
+ try:
46
+ # iterate known addresses in map (sorted)
47
+ for addr in sorted(REG_MAP.keys()):
48
+ dtype, factor, name = REG_MAP[addr]
49
+ count = count_for_type(dtype)
50
+ protocol_addr = addr - 1 # doc addresses start at 1; pymodbus expects zero-based
51
+ try:
52
+ rr = self.client.read_input_registers(address=protocol_addr, count=count, device_id=self.device_id)
53
+ if rr is None:
54
+ warning(f"No response for {addr} ({name})")
55
+ continue
56
+ if hasattr(rr, "isError") and rr.isError():
57
+ warning(f"Modbus exception at {addr} ({name}): {rr}")
58
+ continue
59
+ regs = getattr(rr, "registers", None)
60
+ if not regs or len(regs) < count:
61
+ warning(f"Incomplete registers at {addr} ({name}): {regs}")
62
+ continue
63
+
64
+ val_raw = decode_registers(regs, dtype)
65
+ if val_raw is None:
66
+ warning(f"Unable to decode {addr} ({name}) regs={regs}")
67
+ continue
68
+ # apply factor
69
+ value = val_raw * factor
70
+ # format integer-looking floats without trailing .0
71
+ if isinstance(value, float) and value.is_integer():
72
+ value = int(value)
73
+ info(f"{addr} {name}: raw={regs} -> {value} (factor={factor}, type={dtype})")
74
+ except Exception as e:
75
+ error(f"Error reading {addr} ({name}): {e}")
76
+ time.sleep(self.delay)
77
+
78
+ # Optionally scan remaining addresses 8000..8200 for any non-zero single-register values
79
+ info("\nQuick scan for single-register non-zero values (8000..8200):")
80
+ for addr in range(8000, 8201):
81
+ if addr in REG_MAP:
82
+ continue
83
+ try:
84
+ rr = self.client.read_input_registers(address=addr - 1, count=1, device_id=self.device_id)
85
+ if rr is None:
86
+ continue
87
+ if hasattr(rr, "isError") and rr.isError():
88
+ continue
89
+ regs = getattr(rr, "registers", None)
90
+ if regs and regs[0] != 0:
91
+ info(f"{addr}: {regs[0]} (0x{regs[0]:04X})")
92
+ except Exception:
93
+ pass
94
+ time.sleep(0.005)
95
+ except Exception as e:
96
+ exception(e)
97
+
98
+ def close(self):
99
+ self.client.close()
100
+
101
+
102
+ if __name__ == "__main__":
103
+
104
+ mon = PowerEMS300(host="localhost", port=1502, period=10)
105
+ mon.start()
106
+
107
+ while(True):
108
+ time.sleep(30)
@@ -0,0 +1,71 @@
1
+ #
2
+ # Stanislav Georgiev, Softel Labs
3
+ #
4
+ # This is a proprietary file and may not be copied,
5
+ # distributed, or modified without express permission
6
+ # from the owner. For licensing inquiries, please
7
+ # contact s.georgiev@softel.bg.
8
+ #
9
+ # 2025
10
+ #
11
+
12
+ import time
13
+ import threading
14
+ import time
15
+ from pymodbus.server import StartTcpServer
16
+ from pymodbus.datastore import ModbusServerContext, ModbusSparseDataBlock
17
+ from threading import Thread
18
+ from sciveo.tools.logger import *
19
+ from sciveo.tools.daemon import DaemonBase
20
+
21
+
22
+ # Example read-only registers
23
+ # INITIAL_REGISTERS = {
24
+ # 8044: 12500, # Plant Active Power
25
+ # 8060: 8200, # PV Active Power
26
+ # 8038: -1500, # Storage Power
27
+ # 8048: 600, # Reactive Power
28
+ # 9082: 3000, # Dynamic Allowable Charge Power
29
+ # 9084: 3500, # Dynamic Allowable Discharge Power
30
+ # }
31
+ INITIAL_REGISTERS = {
32
+ 8060: 8200, # PV Active Power
33
+ }
34
+
35
+ class SimulatorEMS300(DaemonBase):
36
+ def __init__(self, host="127.0.0.1", port=5020, period=1):
37
+ super().__init__(num_threads=1, period=period)
38
+ self.host = host
39
+ self.port = port
40
+
41
+ # Only input registers
42
+ self.ir_block = ModbusSparseDataBlock(INITIAL_REGISTERS.copy())
43
+
44
+ self.context = ModbusServerContext(
45
+ { 247: { # unit_id -> datastore dict
46
+ 'di': ModbusSparseDataBlock({}), # discrete inputs
47
+ 'co': ModbusSparseDataBlock({}), # coils
48
+ 'hr': ModbusSparseDataBlock({}), # holding registers
49
+ 'ir': self.ir_block # input registers
50
+ }
51
+ },
52
+ single=False # multiple units not used here, keep False
53
+ )
54
+
55
+ def loop(self):
56
+ # Slowly vary PV Active Power
57
+ pv = self.ir_block.getValues(8060, count=1)[0]
58
+ pv = (pv + 50) % 15000
59
+ self.ir_block.setValues(8060, [pv])
60
+ info(f"Simulated PV Active Power: {pv}")
61
+
62
+ def start_server(self):
63
+ StartTcpServer(self.context, address=(self.host, self.port))
64
+
65
+
66
+ if __name__ == "__main__":
67
+ sim = SimulatorEMS300(host="127.0.0.1", port=1502)
68
+ sim.start()
69
+ sim.start_server()
70
+ while True:
71
+ time.sleep(10)