gea-program 0.15.0__tar.gz → 0.17.0__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 (243) hide show
  1. {gea_program-0.15.0/gea_program.egg-info → gea_program-0.17.0}/PKG-INFO +56 -4
  2. {gea_program-0.15.0 → gea_program-0.17.0}/README.md +55 -3
  3. {gea_program-0.15.0 → gea_program-0.17.0}/gea/__init__.py +2 -1
  4. {gea_program-0.15.0 → gea_program-0.17.0}/gea/__main__.py +142 -6
  5. {gea_program-0.15.0 → gea_program-0.17.0}/gea/acceptance_tests.py +268 -0
  6. {gea_program-0.15.0 → gea_program-0.17.0}/gea/doctor.py +25 -1
  7. gea_program-0.17.0/gea/etp.py +1027 -0
  8. gea_program-0.17.0/gea/etp12_schemas.json +3702 -0
  9. gea_program-0.17.0/gea/help/etp.md +11 -0
  10. gea_program-0.17.0/gea/help/seedlink.md +11 -0
  11. {gea_program-0.15.0 → gea_program-0.17.0}/gea/helplib.py +2 -0
  12. {gea_program-0.15.0 → gea_program-0.17.0}/gea/patches.py +5 -2
  13. gea_program-0.17.0/gea/seedlink.py +1106 -0
  14. {gea_program-0.15.0 → gea_program-0.17.0}/gea/seismic.py +14 -4
  15. {gea_program-0.15.0 → gea_program-0.17.0}/gea/service.py +71 -2
  16. {gea_program-0.15.0 → gea_program-0.17.0}/gea/web/app.html +35 -2
  17. {gea_program-0.15.0 → gea_program-0.17.0}/gea/workspace.py +114 -1
  18. {gea_program-0.15.0 → gea_program-0.17.0/gea_program.egg-info}/PKG-INFO +56 -4
  19. {gea_program-0.15.0 → gea_program-0.17.0}/gea_program.egg-info/SOURCES.txt +5 -0
  20. {gea_program-0.15.0 → gea_program-0.17.0}/pyproject.toml +2 -2
  21. {gea_program-0.15.0 → gea_program-0.17.0}/LICENSE +0 -0
  22. {gea_program-0.15.0 → gea_program-0.17.0}/gea/BENCH_TEST_PROTOCOL.md +0 -0
  23. {gea_program-0.15.0 → gea_program-0.17.0}/gea/accuracy_statement.py +0 -0
  24. {gea_program-0.15.0 → gea_program-0.17.0}/gea/alarm_engine.py +0 -0
  25. {gea_program-0.15.0 → gea_program-0.17.0}/gea/bench.py +0 -0
  26. {gea_program-0.15.0 → gea_program-0.17.0}/gea/blind_harness.py +0 -0
  27. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/acex_lomonosov_age_depth_model.provenance.json +0 -0
  28. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/acex_lomonosov_age_depth_model.txt +0 -0
  29. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/agassiz77_canada_temperature.csv +0 -0
  30. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/agassiz77_canada_temperature.provenance.json +0 -0
  31. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/barbados_110_consolidation.provenance.json +0 -0
  32. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/barbados_110_consolidation.txt +0 -0
  33. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/bengal_u1452_grain_size.provenance.json +0 -0
  34. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/bengal_u1452_grain_size.txt +0 -0
  35. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/blake_164_methane_isotopes.provenance.json +0 -0
  36. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/blake_164_methane_isotopes.txt +0 -0
  37. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.provenance.json +0 -0
  38. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.txt +0 -0
  39. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/collingwood_1_28_ks_complete.las +0 -0
  40. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/collingwood_1_28_ks_complete.provenance.json +0 -0
  41. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/costa_rica_odp_friction_envelope.provenance.json +0 -0
  42. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/costa_rica_odp_friction_envelope.txt +0 -0
  43. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.provenance.json +0 -0
  44. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.txt +0 -0
  45. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/dsdp_504b_physical_properties.provenance.json +0 -0
  46. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/dsdp_504b_physical_properties.txt +0 -0
  47. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/dsdp_504b_sound_velocity.provenance.json +0 -0
  48. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/dsdp_504b_sound_velocity.txt +0 -0
  49. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/elgygytgyn_5011_turbidites.provenance.json +0 -0
  50. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/elgygytgyn_5011_turbidites.txt +0 -0
  51. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/epica_domec_co2_800kyr.provenance.json +0 -0
  52. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/epica_domec_co2_800kyr.txt +0 -0
  53. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/fram_909_organic_petrography.provenance.json +0 -0
  54. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/fram_909_organic_petrography.txt +0 -0
  55. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/gbr_325_coral_uth_ages.provenance.json +0 -0
  56. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/gbr_325_coral_uth_ages.txt +0 -0
  57. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/gisp2_greenland_temperature.csv +0 -0
  58. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/gisp2_greenland_temperature.provenance.json +0 -0
  59. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/gom_308_t2p_insitu.provenance.json +0 -0
  60. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/gom_308_t2p_insitu.txt +0 -0
  61. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/guaymas_385_dom_d13c.provenance.json +0 -0
  62. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/guaymas_385_dom_d13c.txt +0 -0
  63. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/hikurangi_u1520_friction_insitu.provenance.json +0 -0
  64. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/hikurangi_u1520_friction_insitu.txt +0 -0
  65. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.provenance.json +0 -0
  66. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.txt +0 -0
  67. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/iodp_u1324_pore_pressure.provenance.json +0 -0
  68. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/iodp_u1324_pore_pressure.txt +0 -0
  69. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/jfast_c0019_slow_slip_events.provenance.json +0 -0
  70. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/jfast_c0019_slow_slip_events.txt +0 -0
  71. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/kennetcook_2_p129_excerpt.las +0 -0
  72. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/kennetcook_2_p129_excerpt.provenance.json +0 -0
  73. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/ktb_hb_bhgm_density.dat +0 -0
  74. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/ktb_hb_bhgm_density.provenance.json +0 -0
  75. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/ktb_hb_complog_6020_excerpt.provenance.json +0 -0
  76. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/ktb_hb_complog_6020_excerpt.txt +0 -0
  77. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/ktb_hb_hlog246_temperature.dat +0 -0
  78. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/ktb_hb_hlog246_temperature.provenance.json +0 -0
  79. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/ktb_hb_rockmech_compress.dat +0 -0
  80. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/ktb_hb_rockmech_compress.provenance.json +0 -0
  81. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.dat +0 -0
  82. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.provenance.json +0 -0
  83. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/ktb_vb_rockmech_compress.dat +0 -0
  84. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/ktb_vb_rockmech_compress.provenance.json +0 -0
  85. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/ktb_vb_vlog251_temperature.dat +0 -0
  86. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/ktb_vb_vlog251_temperature.provenance.json +0 -0
  87. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/l06_06_nl_survey.csv +0 -0
  88. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/l06_06_nl_survey.provenance.json +0 -0
  89. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/l07_01_nl_excerpt.las +0 -0
  90. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/l07_01_nl_excerpt.provenance.json +0 -0
  91. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/mariana_1200_serpentinite_geochem.provenance.json +0 -0
  92. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/mariana_1200_serpentinite_geochem.txt +0 -0
  93. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/med_160_sapropels.provenance.json +0 -0
  94. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/med_160_sapropels.txt +0 -0
  95. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/nankai_megasplay_shear_strength.provenance.json +0 -0
  96. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/nankai_megasplay_shear_strength.txt +0 -0
  97. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/odp_1027b_thermal_conductivity.provenance.json +0 -0
  98. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/odp_1027b_thermal_conductivity.txt +0 -0
  99. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/odp_1027c_cork_temperature.provenance.json +0 -0
  100. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/odp_1027c_cork_temperature.txt +0 -0
  101. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/odp_1165b_thermal_conductivity.provenance.json +0 -0
  102. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/odp_1165b_thermal_conductivity.txt +0 -0
  103. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.provenance.json +0 -0
  104. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.txt +0 -0
  105. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/odp_504b_dike_elastic_moduli.provenance.json +0 -0
  106. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/odp_504b_dike_elastic_moduli.txt +0 -0
  107. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/odp_504b_leg137_borehole_fluids.provenance.json +0 -0
  108. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/odp_504b_leg137_borehole_fluids.txt +0 -0
  109. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.provenance.json +0 -0
  110. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.txt +0 -0
  111. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/peru_201_sulfate_reduction.provenance.json +0 -0
  112. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/peru_201_sulfate_reduction.txt +0 -0
  113. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/scorpio_e1_sa_excerpt.las +0 -0
  114. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/scorpio_e1_sa_excerpt.provenance.json +0 -0
  115. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/sumatra_362_cohesion.provenance.json +0 -0
  116. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/sumatra_362_cohesion.txt +0 -0
  117. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/university_6_17_no1_tx_excerpt.las +0 -0
  118. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/university_6_17_no1_tx_excerpt.provenance.json +0 -0
  119. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/ursa_308_xrd_mineralogy.provenance.json +0 -0
  120. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/ursa_308_xrd_mineralogy.txt +0 -0
  121. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/volve_15_9_19_sr_excerpt.las +0 -0
  122. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/volve_15_9_19_sr_excerpt.provenance.json +0 -0
  123. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/volve_15_9_19a_core_excerpt.csv +0 -0
  124. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/volve_15_9_19a_core_excerpt.provenance.json +0 -0
  125. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/volve_f12_f14_production_excerpt.csv +0 -0
  126. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/volve_f12_f14_production_excerpt.provenance.json +0 -0
  127. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/walvis_208_petm_carbonate.provenance.json +0 -0
  128. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/walvis_208_petm_carbonate.txt +0 -0
  129. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/woodlark_1109_rock_eval.provenance.json +0 -0
  130. {gea_program-0.15.0 → gea_program-0.17.0}/gea/catalog/woodlark_1109_rock_eval.txt +0 -0
  131. {gea_program-0.15.0 → gea_program-0.17.0}/gea/certificates.py +0 -0
  132. {gea_program-0.15.0 → gea_program-0.17.0}/gea/cli.py +0 -0
  133. {gea_program-0.15.0 → gea_program-0.17.0}/gea/client_reports.py +0 -0
  134. {gea_program-0.15.0 → gea_program-0.17.0}/gea/config_versioning.py +0 -0
  135. {gea_program-0.15.0 → gea_program-0.17.0}/gea/conformance.py +0 -0
  136. {gea_program-0.15.0 → gea_program-0.17.0}/gea/correlation.py +0 -0
  137. {gea_program-0.15.0 → gea_program-0.17.0}/gea/dashboard.py +0 -0
  138. {gea_program-0.15.0 → gea_program-0.17.0}/gea/deviation.py +0 -0
  139. {gea_program-0.15.0 → gea_program-0.17.0}/gea/downhole_engine.py +0 -0
  140. {gea_program-0.15.0 → gea_program-0.17.0}/gea/drift_monitor.py +0 -0
  141. {gea_program-0.15.0 → gea_program-0.17.0}/gea/earth_model.py +0 -0
  142. {gea_program-0.15.0 → gea_program-0.17.0}/gea/example_register_map.json +0 -0
  143. {gea_program-0.15.0 → gea_program-0.17.0}/gea/fat_sat.py +0 -0
  144. {gea_program-0.15.0 → gea_program-0.17.0}/gea/files.py +0 -0
  145. {gea_program-0.15.0 → gea_program-0.17.0}/gea/follower.py +0 -0
  146. {gea_program-0.15.0 → gea_program-0.17.0}/gea/forward_model.py +0 -0
  147. {gea_program-0.15.0 → gea_program-0.17.0}/gea/gamma.py +0 -0
  148. {gea_program-0.15.0 → gea_program-0.17.0}/gea/gauge_aging.py +0 -0
  149. {gea_program-0.15.0 → gea_program-0.17.0}/gea/gauge_specs.py +0 -0
  150. {gea_program-0.15.0 → gea_program-0.17.0}/gea/geodesy.py +0 -0
  151. {gea_program-0.15.0 → gea_program-0.17.0}/gea/gravity_reference.py +0 -0
  152. {gea_program-0.15.0 → gea_program-0.17.0}/gea/help/TESTER_GUIDE.md +0 -0
  153. {gea_program-0.15.0 → gea_program-0.17.0}/gea/help/alarms.md +0 -0
  154. {gea_program-0.15.0 → gea_program-0.17.0}/gea/help/audit-update.md +0 -0
  155. {gea_program-0.15.0 → gea_program-0.17.0}/gea/help/conformance.md +0 -0
  156. {gea_program-0.15.0 → gea_program-0.17.0}/gea/help/data-in.md +0 -0
  157. {gea_program-0.15.0 → gea_program-0.17.0}/gea/help/doctor.md +0 -0
  158. {gea_program-0.15.0 → gea_program-0.17.0}/gea/help/drift.md +0 -0
  159. {gea_program-0.15.0 → gea_program-0.17.0}/gea/help/files.md +0 -0
  160. {gea_program-0.15.0 → gea_program-0.17.0}/gea/help/instruments.md +0 -0
  161. {gea_program-0.15.0 → gea_program-0.17.0}/gea/help/notifications.md +0 -0
  162. {gea_program-0.15.0 → gea_program-0.17.0}/gea/help/osdu.md +0 -0
  163. {gea_program-0.15.0 → gea_program-0.17.0}/gea/help/patches.md +0 -0
  164. {gea_program-0.15.0 → gea_program-0.17.0}/gea/help/ppdm.md +0 -0
  165. {gea_program-0.15.0 → gea_program-0.17.0}/gea/help/quakeml.md +0 -0
  166. {gea_program-0.15.0 → gea_program-0.17.0}/gea/help/quality.md +0 -0
  167. {gea_program-0.15.0 → gea_program-0.17.0}/gea/help/seismic.md +0 -0
  168. {gea_program-0.15.0 → gea_program-0.17.0}/gea/help/site.md +0 -0
  169. {gea_program-0.15.0 → gea_program-0.17.0}/gea/help/sites.md +0 -0
  170. {gea_program-0.15.0 → gea_program-0.17.0}/gea/help/sra.md +0 -0
  171. {gea_program-0.15.0 → gea_program-0.17.0}/gea/help/start.md +0 -0
  172. {gea_program-0.15.0 → gea_program-0.17.0}/gea/help/transients.md +0 -0
  173. {gea_program-0.15.0 → gea_program-0.17.0}/gea/help/upkeep.md +0 -0
  174. {gea_program-0.15.0 → gea_program-0.17.0}/gea/help/vibration.md +0 -0
  175. {gea_program-0.15.0 → gea_program-0.17.0}/gea/help/well-tests.md +0 -0
  176. {gea_program-0.15.0 → gea_program-0.17.0}/gea/housekeeping.py +0 -0
  177. {gea_program-0.15.0 → gea_program-0.17.0}/gea/inverse_engine.py +0 -0
  178. {gea_program-0.15.0 → gea_program-0.17.0}/gea/jobs.py +0 -0
  179. {gea_program-0.15.0 → gea_program-0.17.0}/gea/live_ports.py +0 -0
  180. {gea_program-0.15.0 → gea_program-0.17.0}/gea/loadtest.py +0 -0
  181. {gea_program-0.15.0 → gea_program-0.17.0}/gea/matplotlib_demo.py +0 -0
  182. {gea_program-0.15.0 → gea_program-0.17.0}/gea/modbus.py +0 -0
  183. {gea_program-0.15.0 → gea_program-0.17.0}/gea/model_card.py +0 -0
  184. {gea_program-0.15.0 → gea_program-0.17.0}/gea/mqtt_port.py +0 -0
  185. {gea_program-0.15.0 → gea_program-0.17.0}/gea/notify.py +0 -0
  186. {gea_program-0.15.0 → gea_program-0.17.0}/gea/opcua_port.py +0 -0
  187. {gea_program-0.15.0 → gea_program-0.17.0}/gea/operator_app.py +0 -0
  188. {gea_program-0.15.0 → gea_program-0.17.0}/gea/osdu.py +0 -0
  189. {gea_program-0.15.0 → gea_program-0.17.0}/gea/permits.py +0 -0
  190. {gea_program-0.15.0 → gea_program-0.17.0}/gea/ports.py +0 -0
  191. {gea_program-0.15.0 → gea_program-0.17.0}/gea/ppdm.py +0 -0
  192. {gea_program-0.15.0 → gea_program-0.17.0}/gea/profile_catalog.py +0 -0
  193. {gea_program-0.15.0 → gea_program-0.17.0}/gea/project.py +0 -0
  194. {gea_program-0.15.0 → gea_program-0.17.0}/gea/qt6_downhole_app.py +0 -0
  195. {gea_program-0.15.0 → gea_program-0.17.0}/gea/quakeml.py +0 -0
  196. {gea_program-0.15.0 → gea_program-0.17.0}/gea/reconciler.py +0 -0
  197. {gea_program-0.15.0 → gea_program-0.17.0}/gea/reference/IU_ANMO_10_BHZ_response.provenance.json +0 -0
  198. {gea_program-0.15.0 → gea_program-0.17.0}/gea/reference/IU_ANMO_10_BHZ_response.xml +0 -0
  199. {gea_program-0.15.0 → gea_program-0.17.0}/gea/reference/libmseed_steim.provenance.json +0 -0
  200. {gea_program-0.15.0 → gea_program-0.17.0}/gea/reference/libmseed_steim1_4096.mseed +0 -0
  201. {gea_program-0.15.0 → gea_program-0.17.0}/gea/reference/libmseed_steim2_4096.mseed +0 -0
  202. {gea_program-0.15.0 → gea_program-0.17.0}/gea/reference/libmseed_steim2_512.mseed +0 -0
  203. {gea_program-0.15.0 → gea_program-0.17.0}/gea/rock_inventory.py +0 -0
  204. {gea_program-0.15.0 → gea_program-0.17.0}/gea/sample_record.py +0 -0
  205. {gea_program-0.15.0 → gea_program-0.17.0}/gea/sample_well_profile.csv +0 -0
  206. {gea_program-0.15.0 → gea_program-0.17.0}/gea/sbom.py +0 -0
  207. {gea_program-0.15.0 → gea_program-0.17.0}/gea/segy.py +0 -0
  208. {gea_program-0.15.0 → gea_program-0.17.0}/gea/seismic_array.py +0 -0
  209. {gea_program-0.15.0 → gea_program-0.17.0}/gea/seismic_assoc.py +0 -0
  210. {gea_program-0.15.0 → gea_program-0.17.0}/gea/seismic_detect.py +0 -0
  211. {gea_program-0.15.0 → gea_program-0.17.0}/gea/seismic_film.py +0 -0
  212. {gea_program-0.15.0 → gea_program-0.17.0}/gea/seismic_harmonic.py +0 -0
  213. {gea_program-0.15.0 → gea_program-0.17.0}/gea/seismic_qc.py +0 -0
  214. {gea_program-0.15.0 → gea_program-0.17.0}/gea/seismic_response.py +0 -0
  215. {gea_program-0.15.0 → gea_program-0.17.0}/gea/seismic_signature.py +0 -0
  216. {gea_program-0.15.0 → gea_program-0.17.0}/gea/seismic_track.py +0 -0
  217. {gea_program-0.15.0 → gea_program-0.17.0}/gea/seismic_uncertainty.py +0 -0
  218. {gea_program-0.15.0 → gea_program-0.17.0}/gea/seismic_unlisted.py +0 -0
  219. {gea_program-0.15.0 → gea_program-0.17.0}/gea/sensor_swap.py +0 -0
  220. {gea_program-0.15.0 → gea_program-0.17.0}/gea/service_life.py +0 -0
  221. {gea_program-0.15.0 → gea_program-0.17.0}/gea/shell.py +0 -0
  222. {gea_program-0.15.0 → gea_program-0.17.0}/gea/shut_in.py +0 -0
  223. {gea_program-0.15.0 → gea_program-0.17.0}/gea/sla_report.py +0 -0
  224. {gea_program-0.15.0 → gea_program-0.17.0}/gea/sra.py +0 -0
  225. {gea_program-0.15.0 → gea_program-0.17.0}/gea/store_forward.py +0 -0
  226. {gea_program-0.15.0 → gea_program-0.17.0}/gea/strata_join.py +0 -0
  227. {gea_program-0.15.0 → gea_program-0.17.0}/gea/supervisor.py +0 -0
  228. {gea_program-0.15.0 → gea_program-0.17.0}/gea/survey_cmd.py +0 -0
  229. {gea_program-0.15.0 → gea_program-0.17.0}/gea/survey_view.py +0 -0
  230. {gea_program-0.15.0 → gea_program-0.17.0}/gea/telemetry.py +0 -0
  231. {gea_program-0.15.0 → gea_program-0.17.0}/gea/tool_library.py +0 -0
  232. {gea_program-0.15.0 → gea_program-0.17.0}/gea/transient.py +0 -0
  233. {gea_program-0.15.0 → gea_program-0.17.0}/gea/vibration.py +0 -0
  234. {gea_program-0.15.0 → gea_program-0.17.0}/gea/well_assembler.py +0 -0
  235. {gea_program-0.15.0 → gea_program-0.17.0}/gea/well_test_validation.py +0 -0
  236. {gea_program-0.15.0 → gea_program-0.17.0}/gea/wits0.py +0 -0
  237. {gea_program-0.15.0 → gea_program-0.17.0}/gea/witsml.py +0 -0
  238. {gea_program-0.15.0 → gea_program-0.17.0}/gea_program.egg-info/dependency_links.txt +0 -0
  239. {gea_program-0.15.0 → gea_program-0.17.0}/gea_program.egg-info/entry_points.txt +0 -0
  240. {gea_program-0.15.0 → gea_program-0.17.0}/gea_program.egg-info/requires.txt +0 -0
  241. {gea_program-0.15.0 → gea_program-0.17.0}/gea_program.egg-info/top_level.txt +0 -0
  242. {gea_program-0.15.0 → gea_program-0.17.0}/setup.cfg +0 -0
  243. {gea_program-0.15.0 → gea_program-0.17.0}/tests/test_acceptance.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gea-program
3
- Version: 0.15.0
3
+ Version: 0.17.0
4
4
  Summary: GEA-Program: downhole gauge monitoring - canonical measurement records, quality rules, drift evaluation with a re-fit change log, well-test validation, alarm management, model cards, store-and-forward, monthly SLA measurement, FAT/SAT protocols and a dashboard, generated from live objects in the client's reporting outline.
5
5
  Author: Daniel T. Murphy
6
6
  License: MPL-2.0
@@ -72,7 +72,7 @@ reaches a client document. Nothing unmeasured is ever reported as met.
72
72
  rate-controlled replay, duplicate suppression, per-record latency.
73
73
  - **Configuration versioning, SBOM, monthly SLA, FAT/SAT**
74
74
  (`config_versioning.py`, `sbom.py`, `sla_report.py`, `fat_sat.py`).
75
- - **Live protocol ports** (`live_ports.py`, `wits0.py`, `witsml.py`, `opcua_port.py`,
75
+ - **Live protocol ports** (`live_ports.py`, `wits0.py`, `witsml.py`, `etp.py`, `seedlink.py`, `opcua_port.py`,
76
76
  `mqtt_port.py`, `modbus.py`): WITS Level 0 from the drill floor (TCP connect,
77
77
  TCP listen, serial), WITSML 1.4.1 stores (read-only, polled), OPC UA (read and
78
78
  subscribe), MQTT (number, JSON, Sparkplug B), Modbus TCP; every message becomes
@@ -125,7 +125,7 @@ gea dashboard --catalog-well volve_f12_f14_production_excerpt:15/9-F-12:10000 --
125
125
  gea client-report --report accuracy --out client_report
126
126
  gea model-cards --out model_cards
127
127
  gea sbom --out sbom
128
- gea accept # the product gate (333 checks)
128
+ gea accept # the product gate (340 checks)
129
129
  gea help drift # the help library, by the job (16 pages; the same text is on every dashboard page)
130
130
  gea guide # the click-by-click tester guide (docs/TESTER_GUIDE.md)
131
131
  gea gui # the desktop window (pip install "gea-program[desktop]")
@@ -143,7 +143,7 @@ gea swaps / gea certificates / gea notify / gea housekeeping / gea loadtest #
143
143
 
144
144
  ## Live data
145
145
 
146
- The engine reads live data through five protocol ports. All are **read-only**,
146
+ The engine reads live data through six protocol ports. All are **read-only**,
147
147
  all map only what the site declares (a tag map the client owns; an empty map
148
148
  is declined), and all write every received message to a JSON-lines recording
149
149
  that `--replay` turns back into records without a connection - which is how a
@@ -156,6 +156,9 @@ gea wits0-sim --port 5001 # a WITS0 sender to rehear
156
156
  gea wits0 --config wits0.json --seconds 60 --record floor/session.jsonl --out floor --stream-csv floor/stream.csv # in the first window, while the sender runs
157
157
  gea witsml --write-example-config witsml.json # a WITSML 1.4.1 store: url, uids, mnemonics -> tags; credentials by env name
158
158
  gea witsml --config witsml.json --seconds 60 --out store # needs a real store: the example url is a placeholder until you edit it
159
+ gea etp --write-example-config etp.json # a WITSML 2.x store over ETP v1.2: ws:// or wss:// url, bearer token by env name, channel URIs -> tags, units
160
+ gea etp-sim --port 9800 # an ETP store with two channels to rehearse with - run it in a SECOND window and leave it running
161
+ gea etp --config etp.json --seconds 60 --record rig/session.jsonl --out rig # subscribes; the store pushes every value as it is written
159
162
  gea opcua --write-example-config opcua.json # edit: endpoint, security, credential env names, nodes
160
163
  gea opcua --config opcua.json --seconds 60 --record opc/session.jsonl --out opc --stream-csv opc/stream.csv # needs a real OPC UA server, likewise
161
164
  gea mqtt --write-example-config mqtt.json # edit: broker, TLS, credential env names, topics
@@ -167,6 +170,7 @@ gea ingest --file floor/stream.csv # a stream CSV feeds the h
167
170
  |---|---|---|---|
168
171
  | `wits0` | WITS Level 0 frames (`&&` ... `!!`, four-digit item codes) over TCP connect, TCP listen or serial; record-01 dictionary built in | numeric GOOD; sentinels (-9999, -999.25) and non-numeric -> GAP with the reason | items 0105/0106 (date, time) when present, else arrival; ingest at arrival |
169
172
  | `witsml` | WITSML 1.4.1 SOAP store: GetVersion, GetCap, GetFromStore on one log object, rows newer than the last seen at each poll | numeric GOOD; the store's nullValue and empty fields -> GAP | the index curve of a time log; arrival for a depth log |
173
+ | `etp` | Energistics ETP v1.2 over WebSocket (`etp12.energistics.org`, Avro binary to the published schemas): RequestSession/OpenSession, GetChannelMetadata for the declared URIs, SubscribeChannels, ChannelData pushed by the store (Protocol 21) | numeric GOOD; a non-numeric or null value -> BAD with the reason; a channel the map does not name is counted unmapped | the store's own index: an ETP DateTime index is microseconds since the epoch; an ElapsedTime index is seconds from its declared start; a depth index leaves the arrival time standing, named |
170
174
  | `opcua` | opc.tcp, read + subscribe; Basic256Sha256 SignAndEncrypt when a certificate and key are given | StatusCode severity bits: Good -> GOOD, Uncertain -> STALE, Bad -> GAP (value withheld) | source timestamp, else server timestamp, else arrival |
171
175
  | `mqtt` | MQTT v5 / v3.1.1, TLS optional, QoS per topic; `+`/`#` wildcards | number: GOOD or GAP; JSON: value/time/quality paths; Sparkplug B: `is_null` -> GAP, `Quality` != 192 -> STALE | JSON time path, Sparkplug metric timestamp, else arrival |
172
176
  | `modbus_g6` | Modbus TCP, user-supplied register map with a citation | read failures -> GAP | arrival |
@@ -619,6 +623,54 @@ promoted; the group and the support are declarations; a matched frequency
619
623
  is not a fault size. CSV or miniSEED/SAC; `gea vibration` runs the labelled
620
624
  pump; `gea help vibration`.
621
625
 
626
+ ### Live stations (SeedLink)
627
+
628
+ The seismic leg had run on records that were already files. The stations
629
+ serve SeedLink - the real-time protocol every data centre and most
630
+ digitisers speak - and `gea/seedlink.py` is the customer: the TCP session on
631
+ port 18000, `HELLO` read for what the server offers, SeedLink 3 (`STATION
632
+ STA NET`, `SELECT LLCCC.T`, `DATA` with a hexadecimal sequence, `END`, the
633
+ 8-byte `SL` header on each 512-byte record) or SeedLink 4.0 (`SLPROTO 4.0`,
634
+ `STATION NET_STA`, `SELECT LOC_B_S_SS`, a decimal sequence, the `SE` header
635
+ with its length, uint64 sequence and station id, `ERROR <CODE>`), whichever
636
+ the server speaks. Every record is appended byte for byte to a day file per
637
+ channel under the station's `live/` folder with the state beside it - the
638
+ last sequence number per station, so a reconnection (with backoff) resumes
639
+ where it stopped and no record is filed twice; per channel the last sample,
640
+ the latency, the records, the samples, the gaps (counted, never bridged).
641
+ On the dashboard's Seismic page a live station is added with its host, port,
642
+ network, station and selectors, started and stopped (the serving process
643
+ keeps every enabled one up), and **folded**: the finished day files move into
644
+ the station's record list, hashed, vertical first, and Refresh runs the leg
645
+ on them as on any brought file. `gea seedlink-sim` is a three-component
646
+ synthetic station speaking both versions; `gea seedlink --selftest` runs both
647
+ against it; `gea seedlink --config ... --hello` prints what a server says of
648
+ itself; `gea help seedlink`. A TexNet station is served by EarthScope at
649
+ `rtserve.iris.washington.edu:18000`, network `TX`.
650
+
651
+ ### WITSML 2.x over ETP
652
+
653
+ The newer rigs and stores do not poll: Energistics ETP v1.2 is a WebSocket
654
+ session, Avro-encoded to the published schemas, on which the customer names
655
+ the channels it wants and the store pushes every value as it is written.
656
+ `gea/etp.py` is that customer: the handshake with the subprotocol, the
657
+ binary encoding and the payload limits; RequestSession for Discovery, Store
658
+ and ChannelSubscribe and what the store answered; the channel metadata for
659
+ the URIs the mapping declares (a URI the store does not have is named as
660
+ missing); the subscription; and each ChannelData DataItem as a record under
661
+ the mapping's tag and unit, with the store's own index as the source time -
662
+ microseconds since the epoch for a DateTime index, seconds from the declared
663
+ start for ElapsedTime, the arrival time standing (and named) for a depth
664
+ index - and the arrival as the ingest time, so the latency is per record.
665
+ Client message ids even, the store's odd; a ProtocolException carried with
666
+ its code; a client or a store without the subprotocol refused at the
667
+ handshake. The patch supervisor runs it like every other port; the page
668
+ offers it; `gea etp-sim` is a store with two channels of a synthetic
669
+ disposal well; `gea etp --selftest` runs the codec and a loopback session;
670
+ `gea help etp`. The doctor now also names a panel started through the
671
+ `gea.exe` launcher, which pip cannot replace while it runs - stop, update,
672
+ start with `python -m gea serve`.
673
+
622
674
  ### Conformity, in the standards' words
623
675
 
624
676
  The drift report's "bias inside the instrument's class" is a statement of
@@ -33,7 +33,7 @@ reaches a client document. Nothing unmeasured is ever reported as met.
33
33
  rate-controlled replay, duplicate suppression, per-record latency.
34
34
  - **Configuration versioning, SBOM, monthly SLA, FAT/SAT**
35
35
  (`config_versioning.py`, `sbom.py`, `sla_report.py`, `fat_sat.py`).
36
- - **Live protocol ports** (`live_ports.py`, `wits0.py`, `witsml.py`, `opcua_port.py`,
36
+ - **Live protocol ports** (`live_ports.py`, `wits0.py`, `witsml.py`, `etp.py`, `seedlink.py`, `opcua_port.py`,
37
37
  `mqtt_port.py`, `modbus.py`): WITS Level 0 from the drill floor (TCP connect,
38
38
  TCP listen, serial), WITSML 1.4.1 stores (read-only, polled), OPC UA (read and
39
39
  subscribe), MQTT (number, JSON, Sparkplug B), Modbus TCP; every message becomes
@@ -86,7 +86,7 @@ gea dashboard --catalog-well volve_f12_f14_production_excerpt:15/9-F-12:10000 --
86
86
  gea client-report --report accuracy --out client_report
87
87
  gea model-cards --out model_cards
88
88
  gea sbom --out sbom
89
- gea accept # the product gate (333 checks)
89
+ gea accept # the product gate (340 checks)
90
90
  gea help drift # the help library, by the job (16 pages; the same text is on every dashboard page)
91
91
  gea guide # the click-by-click tester guide (docs/TESTER_GUIDE.md)
92
92
  gea gui # the desktop window (pip install "gea-program[desktop]")
@@ -104,7 +104,7 @@ gea swaps / gea certificates / gea notify / gea housekeeping / gea loadtest #
104
104
 
105
105
  ## Live data
106
106
 
107
- The engine reads live data through five protocol ports. All are **read-only**,
107
+ The engine reads live data through six protocol ports. All are **read-only**,
108
108
  all map only what the site declares (a tag map the client owns; an empty map
109
109
  is declined), and all write every received message to a JSON-lines recording
110
110
  that `--replay` turns back into records without a connection - which is how a
@@ -117,6 +117,9 @@ gea wits0-sim --port 5001 # a WITS0 sender to rehear
117
117
  gea wits0 --config wits0.json --seconds 60 --record floor/session.jsonl --out floor --stream-csv floor/stream.csv # in the first window, while the sender runs
118
118
  gea witsml --write-example-config witsml.json # a WITSML 1.4.1 store: url, uids, mnemonics -> tags; credentials by env name
119
119
  gea witsml --config witsml.json --seconds 60 --out store # needs a real store: the example url is a placeholder until you edit it
120
+ gea etp --write-example-config etp.json # a WITSML 2.x store over ETP v1.2: ws:// or wss:// url, bearer token by env name, channel URIs -> tags, units
121
+ gea etp-sim --port 9800 # an ETP store with two channels to rehearse with - run it in a SECOND window and leave it running
122
+ gea etp --config etp.json --seconds 60 --record rig/session.jsonl --out rig # subscribes; the store pushes every value as it is written
120
123
  gea opcua --write-example-config opcua.json # edit: endpoint, security, credential env names, nodes
121
124
  gea opcua --config opcua.json --seconds 60 --record opc/session.jsonl --out opc --stream-csv opc/stream.csv # needs a real OPC UA server, likewise
122
125
  gea mqtt --write-example-config mqtt.json # edit: broker, TLS, credential env names, topics
@@ -128,6 +131,7 @@ gea ingest --file floor/stream.csv # a stream CSV feeds the h
128
131
  |---|---|---|---|
129
132
  | `wits0` | WITS Level 0 frames (`&&` ... `!!`, four-digit item codes) over TCP connect, TCP listen or serial; record-01 dictionary built in | numeric GOOD; sentinels (-9999, -999.25) and non-numeric -> GAP with the reason | items 0105/0106 (date, time) when present, else arrival; ingest at arrival |
130
133
  | `witsml` | WITSML 1.4.1 SOAP store: GetVersion, GetCap, GetFromStore on one log object, rows newer than the last seen at each poll | numeric GOOD; the store's nullValue and empty fields -> GAP | the index curve of a time log; arrival for a depth log |
134
+ | `etp` | Energistics ETP v1.2 over WebSocket (`etp12.energistics.org`, Avro binary to the published schemas): RequestSession/OpenSession, GetChannelMetadata for the declared URIs, SubscribeChannels, ChannelData pushed by the store (Protocol 21) | numeric GOOD; a non-numeric or null value -> BAD with the reason; a channel the map does not name is counted unmapped | the store's own index: an ETP DateTime index is microseconds since the epoch; an ElapsedTime index is seconds from its declared start; a depth index leaves the arrival time standing, named |
131
135
  | `opcua` | opc.tcp, read + subscribe; Basic256Sha256 SignAndEncrypt when a certificate and key are given | StatusCode severity bits: Good -> GOOD, Uncertain -> STALE, Bad -> GAP (value withheld) | source timestamp, else server timestamp, else arrival |
132
136
  | `mqtt` | MQTT v5 / v3.1.1, TLS optional, QoS per topic; `+`/`#` wildcards | number: GOOD or GAP; JSON: value/time/quality paths; Sparkplug B: `is_null` -> GAP, `Quality` != 192 -> STALE | JSON time path, Sparkplug metric timestamp, else arrival |
133
137
  | `modbus_g6` | Modbus TCP, user-supplied register map with a citation | read failures -> GAP | arrival |
@@ -580,6 +584,54 @@ promoted; the group and the support are declarations; a matched frequency
580
584
  is not a fault size. CSV or miniSEED/SAC; `gea vibration` runs the labelled
581
585
  pump; `gea help vibration`.
582
586
 
587
+ ### Live stations (SeedLink)
588
+
589
+ The seismic leg had run on records that were already files. The stations
590
+ serve SeedLink - the real-time protocol every data centre and most
591
+ digitisers speak - and `gea/seedlink.py` is the customer: the TCP session on
592
+ port 18000, `HELLO` read for what the server offers, SeedLink 3 (`STATION
593
+ STA NET`, `SELECT LLCCC.T`, `DATA` with a hexadecimal sequence, `END`, the
594
+ 8-byte `SL` header on each 512-byte record) or SeedLink 4.0 (`SLPROTO 4.0`,
595
+ `STATION NET_STA`, `SELECT LOC_B_S_SS`, a decimal sequence, the `SE` header
596
+ with its length, uint64 sequence and station id, `ERROR <CODE>`), whichever
597
+ the server speaks. Every record is appended byte for byte to a day file per
598
+ channel under the station's `live/` folder with the state beside it - the
599
+ last sequence number per station, so a reconnection (with backoff) resumes
600
+ where it stopped and no record is filed twice; per channel the last sample,
601
+ the latency, the records, the samples, the gaps (counted, never bridged).
602
+ On the dashboard's Seismic page a live station is added with its host, port,
603
+ network, station and selectors, started and stopped (the serving process
604
+ keeps every enabled one up), and **folded**: the finished day files move into
605
+ the station's record list, hashed, vertical first, and Refresh runs the leg
606
+ on them as on any brought file. `gea seedlink-sim` is a three-component
607
+ synthetic station speaking both versions; `gea seedlink --selftest` runs both
608
+ against it; `gea seedlink --config ... --hello` prints what a server says of
609
+ itself; `gea help seedlink`. A TexNet station is served by EarthScope at
610
+ `rtserve.iris.washington.edu:18000`, network `TX`.
611
+
612
+ ### WITSML 2.x over ETP
613
+
614
+ The newer rigs and stores do not poll: Energistics ETP v1.2 is a WebSocket
615
+ session, Avro-encoded to the published schemas, on which the customer names
616
+ the channels it wants and the store pushes every value as it is written.
617
+ `gea/etp.py` is that customer: the handshake with the subprotocol, the
618
+ binary encoding and the payload limits; RequestSession for Discovery, Store
619
+ and ChannelSubscribe and what the store answered; the channel metadata for
620
+ the URIs the mapping declares (a URI the store does not have is named as
621
+ missing); the subscription; and each ChannelData DataItem as a record under
622
+ the mapping's tag and unit, with the store's own index as the source time -
623
+ microseconds since the epoch for a DateTime index, seconds from the declared
624
+ start for ElapsedTime, the arrival time standing (and named) for a depth
625
+ index - and the arrival as the ingest time, so the latency is per record.
626
+ Client message ids even, the store's odd; a ProtocolException carried with
627
+ its code; a client or a store without the subprotocol refused at the
628
+ handshake. The patch supervisor runs it like every other port; the page
629
+ offers it; `gea etp-sim` is a store with two channels of a synthetic
630
+ disposal well; `gea etp --selftest` runs the codec and a loopback session;
631
+ `gea help etp`. The doctor now also names a panel started through the
632
+ `gea.exe` launcher, which pip cannot replace while it runs - stop, update,
633
+ start with `python -m gea serve`.
634
+
583
635
  ### Conformity, in the standards' words
584
636
 
585
637
  The drift report's "bias inside the instrument's class" is a statement of
@@ -87,6 +87,7 @@ from .opcua_port import ASYNCUA_AVAILABLE, OpcUaTap # registers the 'op
87
87
  from .mqtt_port import PAHO_AVAILABLE, MqttTap # registers the 'mqtt' port at import
88
88
  from .wits0 import Wits0Tap, SERIAL_AVAILABLE # registers the 'wits0' port at import
89
89
  from .witsml import WitsmlTap # registers the 'witsml' port at import
90
+ from .etp import EtpTap # registers the 'etp' port at import (WITSML 2.x over ETP v1.2)
90
91
  from .well_assembler import (
91
92
  WellAssembly, WellComponent, assemble, assemble_ktb_hb, assemble_odp_504b,
92
93
  assemble_site_1027, assemble_u1324, BUILTIN_ASSEMBLIES,
@@ -122,7 +123,7 @@ from .profile_catalog import (
122
123
  read_pangaea_txt,
123
124
  )
124
125
 
125
- __version__ = "0.15.0"
126
+ __version__ = "0.17.0"
126
127
  __all__ = [
127
128
  "aging_rate", "rate_psi_yr", "accuracy_psi",
128
129
  "Sensor", "SimulatorConfig", "DownholeEngine",
@@ -265,7 +265,8 @@ def main(argv=None) -> int:
265
265
  for name, helptxt in (("opcua", "OPC UA client port: read once or subscribe; record; replay a recording offline"),
266
266
  ("mqtt", "MQTT subscriber port (3.1.1/5.0; number | json | Sparkplug B): run; record; replay offline"),
267
267
  ("wits0", "WITS Level 0 port (drill floor: mud logger / EDR) over TCP connect, TCP listen or serial: run; record; replay offline"),
268
- ("witsml", "WITSML 1.4.1 read-only client: poll a log object from the store; record; replay offline")):
268
+ ("witsml", "WITSML 1.4.1 read-only client: poll a log object from the store; record; replay offline"),
269
+ ("etp", "WITSML 2.x over Energistics ETP v1.2 (WebSocket, Avro): subscribe to channels and the store pushes; record; replay the session's shape")):
269
270
  p_lp = sub.add_parser(name, help=helptxt)
270
271
  p_lp.add_argument("--config", type=str, default=None, help="site map JSON (client-owned)")
271
272
  p_lp.add_argument("--write-example-config", type=str, default=None, help="write an example map here and exit")
@@ -275,6 +276,7 @@ def main(argv=None) -> int:
275
276
  p_lp.add_argument("--read-once", action="store_true", help="opcua: one Read of all nodes instead of a subscription")
276
277
  p_lp.add_argument("--out", type=str, default=None, help="write the records CSV here")
277
278
  p_lp.add_argument("--stream-csv", type=str, default=None, help="also write a historian-style CSV the other commands ingest")
279
+ p_lp.add_argument("--selftest", action="store_true", help="etp: the codec round-trip and a session against the simulated store on the loopback")
278
280
 
279
281
  p_ws = sub.add_parser("workspace", help="the client site folder: init, add wells (file | catalogue | live), list, migrate a --out folder, refresh the dashboard, audit log")
280
282
  p_ws.add_argument("--path", type=str, required=True, help="the workspace folder")
@@ -282,7 +284,8 @@ def main(argv=None) -> int:
282
284
  choices=["init", "add-file", "add-catalog", "add-live", "remove", "list", "migrate", "refresh", "audit",
283
285
  "add-seismic", "refresh-seismic", "remove-seismic", "sar-film", "refresh-all", "add-track", "refresh-track", "remove-track",
284
286
  "seismic-dataset", "seismic-field", "add-site", "sites", "site-report", "remove-site", "rename",
285
- "set-well", "sra-define", "sra-report", "associate", "osdu-export", "quakeml-export", "well-identity", "vibration-report"])
287
+ "set-well", "sra-define", "sra-report", "associate", "osdu-export", "quakeml-export", "well-identity", "vibration-report",
288
+ "add-live-station", "live-fold", "live-run"])
286
289
  p_ws.add_argument("--stations", type=str, nargs="+", default=None, help="add-track: two or more array station ids of this workspace")
287
290
  p_ws.add_argument("--site", type=str, default=None, help="site-report/remove-site: the site id")
288
291
  p_ws.add_argument("--api", type=str, default=None, help="set-well: the API number")
@@ -352,7 +355,10 @@ def main(argv=None) -> int:
352
355
  p_ws.add_argument("--unit", type=str, default="m", help="add-seismic with --permits and --zone: the grid unit (m, usft, ft)")
353
356
  p_ws.add_argument("--band", type=float, nargs=2, default=None, metavar=("LO", "HI"), help="add-seismic: the band, Hz (default 1 50)")
354
357
  p_ws.add_argument("--permits", type=str, default=None, help="add-seismic: a permit export to turn into the rigs CSV (instead of --sources)")
355
- p_ws.add_argument("--station", type=str, default=None, help="refresh-seismic / remove-seismic: the station id")
358
+ p_ws.add_argument("--station", type=str, default=None, help="refresh-seismic / remove-seismic / live-fold / live-run: the station id")
359
+ p_ws.add_argument("--seedlink-config", type=str, default=None, help="add-live-station: the SeedLink config JSON (gea seedlink --write-example-config)")
360
+ p_ws.add_argument("--include-open", action="store_true", help="live-fold: fold the current day's files too (they are still being written)")
361
+ p_ws.add_argument("--seconds", type=float, default=None, help="live-run: how long to run (default: until Ctrl+C)")
356
362
  p_ws.add_argument("--name", type=str, default=None, help="init: site name; add-*: display name")
357
363
  p_ws.add_argument("--file", type=str, default=None, help="add-file: the client's data file; add-live: the port configuration JSON")
358
364
  p_ws.add_argument("--entry", type=str, default=None, help="add-catalog: catalogue entry")
@@ -400,6 +406,26 @@ def main(argv=None) -> int:
400
406
  p_us.add_argument("--password-env", type=str, default="GEA_PASSWORD", help="environment variable holding the password (never a flag, never on the command line)")
401
407
  p_us.add_argument("--actor", type=str, default="cli")
402
408
 
409
+ p_es = sub.add_parser("etp-sim", help="an ETP v1.2 store for testing a patch without a rig: two channels of a synthetic disposal well, pushed as a customer subscribes")
410
+ p_es.add_argument("--port", type=int, default=9800)
411
+ p_es.add_argument("--frames", type=int, default=600)
412
+ p_es.add_argument("--interval", type=float, default=1.0, help="seconds between samples")
413
+ p_es.add_argument("--seed", type=int, default=1)
414
+ p_sl = sub.add_parser("seedlink", help="the seismic leg's live port: a SeedLink (3 / 4.0) station's records as it writes them, byte for byte into day files; --selftest against the simulator")
415
+ p_sl.add_argument("--config", type=str, default=None, help="the SeedLink config JSON (host, port, streams)")
416
+ p_sl.add_argument("--write-example-config", type=str, default=None, help="write an example config here and exit")
417
+ p_sl.add_argument("--out", type=str, default="seedlink", help="the folder for the day files and the state (default ./seedlink)")
418
+ p_sl.add_argument("--seconds", type=float, default=60.0, help="how long to run (the state resumes the next run from the last sequence number)")
419
+ p_sl.add_argument("--max-records", type=int, default=None, help="stop after this many records")
420
+ p_sl.add_argument("--selftest", action="store_true", help="both protocols against the simulator on the loopback")
421
+ p_sl.add_argument("--hello", action="store_true", help="connect, print what the server says of itself (HELLO) and its INFO ID, and exit")
422
+ p_ss = sub.add_parser("seedlink-sim", help="a SeedLink server (3 and 4.0) for rehearsing the live station without a data centre: one synthetic three-component station")
423
+ p_ss.add_argument("--port", type=int, default=18000)
424
+ p_ss.add_argument("--network", type=str, default="XX")
425
+ p_ss.add_argument("--station", type=str, default="PAD3")
426
+ p_ss.add_argument("--channels", type=str, default="HHZ,HHN,HHE")
427
+ p_ss.add_argument("--rate", type=float, default=100.0, help="samples per second")
428
+ p_ss.add_argument("--seed", type=int, default=7)
403
429
  p_w0 = sub.add_parser("wits0-sim", help="a WITS0 sender for testing a patch without a rig: listens once, then streams a deterministic drilling sequence")
404
430
  p_w0.add_argument("--port", type=int, default=5001)
405
431
  p_w0.add_argument("--frames", type=int, default=600)
@@ -1908,6 +1934,31 @@ def main(argv=None) -> int:
1908
1934
  raise SystemExit("well-identity needs --well <id>")
1909
1935
  r = ws.well_identity(a.well)
1910
1936
  print(_ppdm_text(r)); return 0
1937
+ elif a.action == "add-live-station":
1938
+ if not a.seedlink_config or not a.name:
1939
+ raise SystemExit("add-live-station needs --name <display> and --seedlink-config <json>")
1940
+ st = ws.add_live_station(a.name, a.seedlink_config, a.lat, a.lon, actor=a.actor, band=a.band, stationxml=a.stationxml, sources_csv=a.sources)
1941
+ print(f"live station {st['id']}: {st['live']['host']}:{st['live']['port']} {st['live']['stations']} - run `gea workspace --path ... --action live-run --station {st['id']}` or Start it on the page")
1942
+ return 0
1943
+ elif a.action == "live-fold":
1944
+ if not a.station:
1945
+ raise SystemExit("live-fold needs --station <id>")
1946
+ r = ws.fold_live_station(a.station, actor=a.actor, include_open=a.include_open)
1947
+ print(f"folded {len(r['folded'])} file(s) into {a.station}: {r['folded']}; still open: {r['kept_open']}")
1948
+ return 0
1949
+ elif a.action == "live-run":
1950
+ from . import seedlink as _L
1951
+ if not a.station:
1952
+ raise SystemExit("live-run needs --station <id>")
1953
+ cfg = ws.live_station_config(a.station)
1954
+ d = ws.live_dir(a.station)
1955
+ tap = _L.SeedLinkTap(cfg, d, os.path.join(d, "seedlink_state.json"))
1956
+ try:
1957
+ st = tap.run(a.seconds if a.seconds else None)
1958
+ except KeyboardInterrupt:
1959
+ tap.stop(); st = tap.state
1960
+ print(_L.report_text(st))
1961
+ return 0 if st["records"] else 1
1911
1962
  elif a.action == "quakeml-export":
1912
1963
  from .quakeml import report_text as _qml_text
1913
1964
  if not a.site:
@@ -1966,10 +2017,84 @@ def main(argv=None) -> int:
1966
2017
  stop.set()
1967
2018
  print("wits0-sim: stopped")
1968
2019
  return 0
1969
- elif a.cmd in ("opcua", "mqtt", "wits0", "witsml"):
2020
+ elif a.cmd == "seedlink":
2021
+ import json as _json
2022
+ from . import seedlink as _L
2023
+ if a.selftest:
2024
+ r = _L.selftest()
2025
+ print(_json.dumps({k: r[k] for k in ("label", "status", "checks")}, indent=1))
2026
+ print(_json.dumps(r["detail"], indent=1, default=str))
2027
+ return 0 if r["status"] == "OK" else 1
2028
+ if a.write_example_config:
2029
+ print("config:", _L.write_example_config(a.write_example_config))
2030
+ return 0
2031
+ if not a.config:
2032
+ raise SystemExit("seedlink needs --config (or --write-example-config to start one, or --selftest)")
2033
+ try:
2034
+ cfg = _L.load_config(a.config)
2035
+ except (ValueError, OSError) as e:
2036
+ raise SystemExit(f"seedlink: {e}")
2037
+ if a.hello:
2038
+ try:
2039
+ cl = _L.SeedLinkClient(cfg["host"], cfg["port"], cfg["timeout_s"]).connect()
2040
+ h = cl.say_hello()
2041
+ print(f"seedlink: {cfg['host']}:{cfg['port']} says {h['software']!r}; organization {cl.organization!r}; protocols {h['protocols']}; capabilities {h['capabilities']}")
2042
+ cl.info("ID")
2043
+ cl.sock.settimeout(cfg["timeout_s"])
2044
+ pkt = cl.read_packet()
2045
+ print(f" INFO ID: {pkt['kind']} packet, SeedLink {pkt.get('protocol')}, {len(pkt.get('payload') or b'')} bytes")
2046
+ cl.close()
2047
+ except (OSError, ValueError, _L.SeedLinkError) as e:
2048
+ raise SystemExit(f"seedlink: {e}\n no SeedLink server answers at that host:port - start `gea seedlink-sim --port {cfg['port']}` in a SECOND window first, or point the config at the data centre (rtserve.iris.washington.edu:18000 for TexNet)")
2049
+ return 0
2050
+ tap = _L.SeedLinkTap(cfg, a.out)
2051
+ try:
2052
+ st = tap.run(a.seconds, max_records=a.max_records)
2053
+ except KeyboardInterrupt:
2054
+ tap.stop(); st = tap.state
2055
+ print(_L.report_text(st))
2056
+ if st["status"] == "REFUSED" or (st["records"] == 0 and st.get("last_error")):
2057
+ print(f" no records: start `gea seedlink-sim --port {cfg['port']}` in a SECOND window first, or point the config at the data centre (rtserve.iris.washington.edu:18000, network TX for TexNet)")
2058
+ return 1
2059
+ return 0
2060
+ elif a.cmd == "seedlink-sim":
2061
+ from . import seedlink as _L
2062
+ srv = _L.simulate_server(port=a.port, network=a.network, station=a.station, channels=[c.strip().upper() for c in a.channels.split(",") if c.strip()], rate=a.rate, seed=a.seed)
2063
+ print(f"seedlink-sim: a SeedLink server (3.1 and 4.0) listening on 127.0.0.1:{srv.port} - this window is the STATION and stays here; it writes {a.network}.{a.station} "
2064
+ f"({a.channels}) at {a.rate:g} Hz in 512-byte records and serves them as customers ask.\n"
2065
+ f" Open a SECOND window and run: gea seedlink --write-example-config seedlink.json; gea seedlink --config seedlink.json --seconds 60 --out live\n"
2066
+ f" or on the dashboard: Seismic -> Live stations -> Add (host 127.0.0.1, port {srv.port}, network {a.network}, station {a.station}) -> Start. Ctrl+C stops it.", flush=True)
2067
+ try:
2068
+ while srv.is_alive():
2069
+ srv.join(0.5)
2070
+ except KeyboardInterrupt:
2071
+ srv.stop()
2072
+ print("seedlink-sim: stopped")
2073
+ return 0
2074
+ elif a.cmd == "etp-sim":
2075
+ from . import etp as _E
2076
+ th, port, stop = _E.simulate_store(port=a.port, frames=a.frames, interval_s=a.interval, seed=a.seed, verbose=True)
2077
+ print(f"etp-sim: an ETP v1.2 store listening on ws://127.0.0.1:{port}/ - this window is the STORE and stays here; nothing happens until a customer connects.\n"
2078
+ f" Open a SECOND window and run: gea etp --config etp.json --seconds 60 --out floor (with etp.json: url ws://127.0.0.1:{port}/ and the two example channel URIs)\n"
2079
+ f" or on the dashboard: Patch panel -> Add a patch -> etp, url ws://127.0.0.1:{port}/. Then it pushes {a.frames} samples per channel {a.interval:g} s apart. Ctrl+C stops it.", flush=True)
2080
+ try:
2081
+ while th.is_alive():
2082
+ th.join(0.5)
2083
+ except KeyboardInterrupt:
2084
+ stop.set()
2085
+ print("etp-sim: stopped")
2086
+ return 0
2087
+ elif a.cmd in ("opcua", "mqtt", "wits0", "witsml", "etp"):
1970
2088
  import json as _json
1971
2089
  from .live_ports import records_to_stream, summarize, write_records_csv
1972
- mod = __import__({"opcua": "gea.opcua_port", "mqtt": "gea.mqtt_port", "wits0": "gea.wits0", "witsml": "gea.witsml"}[a.cmd], fromlist=["x"])
2090
+ mod = __import__({"opcua": "gea.opcua_port", "mqtt": "gea.mqtt_port", "wits0": "gea.wits0", "witsml": "gea.witsml", "etp": "gea.etp"}[a.cmd], fromlist=["x"])
2091
+ if a.cmd == "etp" and getattr(a, "selftest", False):
2092
+ r = mod.selftest()
2093
+ print(f"etp selftest [{r['label']}]: {r['status']}")
2094
+ for k, v in r["checks"].items():
2095
+ print(f" {'ok ' if v else 'BAD'} {k}")
2096
+ print(f" {r['n_records']} record(s) on {r['tags']}; store {r['store'].get('application')} protocols {r['store'].get('protocols')}; counts {r['counts']}")
2097
+ return 0 if r["status"] == "OK" else 1
1973
2098
  if a.write_example_config:
1974
2099
  print("config:", mod.write_example_config(a.write_example_config))
1975
2100
  return 0
@@ -1994,6 +2119,11 @@ def main(argv=None) -> int:
1994
2119
  tap = mod.Wits0Tap(a.config, recording_path=a.record)
1995
2120
  recs = tap.run(a.seconds)
1996
2121
  src = f"{tap.cfg['transport']} {tap.cfg.get('host') or tap.cfg.get('device') or ('listen:' + str(tap.cfg.get('listen_port')))}"
2122
+ elif a.cmd == "etp":
2123
+ tap = mod.EtpTap(a.config, recording_path=a.record)
2124
+ recs = tap.run(a.seconds)
2125
+ src = tap.cfg["url"] + (f" (store: {tap.state.get('store', {}).get('application')}, {tap.state.get('channels')} channel(s) subscribed"
2126
+ + (f", missing {tap.state.get('missing_uris')}" if tap.state.get('missing_uris') else '') + ')')
1997
2127
  else:
1998
2128
  tap = mod.WitsmlTap(a.config, recording_path=a.record)
1999
2129
  recs = tap.run(a.seconds)
@@ -2001,11 +2131,17 @@ def main(argv=None) -> int:
2001
2131
  except (NotImplementedError, ConnectionError, ValueError) as e:
2002
2132
  hint = {"wits0": "nothing is sending on that host:port - start `gea wits0-sim --port 5001` in a SECOND window first (it must be running while this reads), or point the config at the rig's real WITS0 feed",
2003
2133
  "witsml": "no WITSML store answers at that url - the example config points at 127.0.0.1:8000, a placeholder; put the real store's url, uids and credential env names in it",
2134
+ "etp": "no ETP store answers at that url - start `gea etp-sim --port 9800` in a SECOND window first, or point the config at the rig's ETP endpoint (wss://...) with the channel URIs it publishes",
2004
2135
  "opcua": "no OPC UA server answers at that endpoint - the example config is a placeholder; put the real server's endpoint, security and node ids in it",
2005
2136
  "mqtt": "no MQTT broker answers at that host:port - the example config is a placeholder; put the real broker in it"}.get(a.cmd, "")
2006
2137
  raise SystemExit(f"{a.cmd}: {e}" + (f"\n {hint}" if hint else "")) # the reason and what to do, without a traceback
2007
2138
  print(f"{a.cmd}: {src}")
2008
- print(_json.dumps(summarize(recs), indent=1))
2139
+ if a.cmd == "etp" and a.replay:
2140
+ shape = getattr(mod.replay, "summary", {})
2141
+ print(_json.dumps({"session_shape": shape, "records": 0,
2142
+ "note": "an ETP recording holds the messages as received with ChannelData as counts (the samples went to --out); the replay reproduces the session's shape, not the samples"}, indent=1))
2143
+ else:
2144
+ print(_json.dumps(summarize(recs), indent=1))
2009
2145
  if a.out:
2010
2146
  print(" records:", write_records_csv(recs, a.out))
2011
2147
  if a.stream_csv and recs: