gea-program 0.16.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.
- {gea_program-0.16.0/gea_program.egg-info → gea_program-0.17.0}/PKG-INFO +28 -3
- {gea_program-0.16.0 → gea_program-0.17.0}/README.md +27 -2
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/__init__.py +1 -1
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/__main__.py +100 -2
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/acceptance_tests.py +139 -0
- gea_program-0.17.0/gea/help/seedlink.md +11 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/helplib.py +1 -0
- gea_program-0.17.0/gea/seedlink.py +1106 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/seismic.py +14 -4
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/service.py +61 -1
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/web/app.html +34 -1
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/workspace.py +113 -0
- {gea_program-0.16.0 → gea_program-0.17.0/gea_program.egg-info}/PKG-INFO +28 -3
- {gea_program-0.16.0 → gea_program-0.17.0}/gea_program.egg-info/SOURCES.txt +2 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/pyproject.toml +1 -1
- {gea_program-0.16.0 → gea_program-0.17.0}/LICENSE +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/BENCH_TEST_PROTOCOL.md +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/accuracy_statement.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/alarm_engine.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/bench.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/blind_harness.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/acex_lomonosov_age_depth_model.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/acex_lomonosov_age_depth_model.txt +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/agassiz77_canada_temperature.csv +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/agassiz77_canada_temperature.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/barbados_110_consolidation.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/barbados_110_consolidation.txt +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/bengal_u1452_grain_size.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/bengal_u1452_grain_size.txt +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/blake_164_methane_isotopes.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/blake_164_methane_isotopes.txt +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.txt +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/collingwood_1_28_ks_complete.las +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/collingwood_1_28_ks_complete.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/costa_rica_odp_friction_envelope.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/costa_rica_odp_friction_envelope.txt +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.txt +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/dsdp_504b_physical_properties.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/dsdp_504b_physical_properties.txt +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/dsdp_504b_sound_velocity.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/dsdp_504b_sound_velocity.txt +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/elgygytgyn_5011_turbidites.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/elgygytgyn_5011_turbidites.txt +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/epica_domec_co2_800kyr.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/epica_domec_co2_800kyr.txt +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/fram_909_organic_petrography.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/fram_909_organic_petrography.txt +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/gbr_325_coral_uth_ages.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/gbr_325_coral_uth_ages.txt +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/gisp2_greenland_temperature.csv +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/gisp2_greenland_temperature.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/gom_308_t2p_insitu.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/gom_308_t2p_insitu.txt +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/guaymas_385_dom_d13c.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/guaymas_385_dom_d13c.txt +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/hikurangi_u1520_friction_insitu.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/hikurangi_u1520_friction_insitu.txt +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.txt +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/iodp_u1324_pore_pressure.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/iodp_u1324_pore_pressure.txt +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/jfast_c0019_slow_slip_events.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/jfast_c0019_slow_slip_events.txt +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/kennetcook_2_p129_excerpt.las +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/kennetcook_2_p129_excerpt.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/ktb_hb_bhgm_density.dat +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/ktb_hb_bhgm_density.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/ktb_hb_complog_6020_excerpt.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/ktb_hb_complog_6020_excerpt.txt +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/ktb_hb_hlog246_temperature.dat +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/ktb_hb_hlog246_temperature.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/ktb_hb_rockmech_compress.dat +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/ktb_hb_rockmech_compress.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.dat +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/ktb_vb_rockmech_compress.dat +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/ktb_vb_rockmech_compress.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/ktb_vb_vlog251_temperature.dat +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/ktb_vb_vlog251_temperature.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/l06_06_nl_survey.csv +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/l06_06_nl_survey.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/l07_01_nl_excerpt.las +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/l07_01_nl_excerpt.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/mariana_1200_serpentinite_geochem.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/mariana_1200_serpentinite_geochem.txt +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/med_160_sapropels.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/med_160_sapropels.txt +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/nankai_megasplay_shear_strength.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/nankai_megasplay_shear_strength.txt +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/odp_1027b_thermal_conductivity.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/odp_1027b_thermal_conductivity.txt +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/odp_1027c_cork_temperature.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/odp_1027c_cork_temperature.txt +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/odp_1165b_thermal_conductivity.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/odp_1165b_thermal_conductivity.txt +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.txt +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/odp_504b_dike_elastic_moduli.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/odp_504b_dike_elastic_moduli.txt +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/odp_504b_leg137_borehole_fluids.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/odp_504b_leg137_borehole_fluids.txt +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.txt +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/peru_201_sulfate_reduction.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/peru_201_sulfate_reduction.txt +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/scorpio_e1_sa_excerpt.las +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/scorpio_e1_sa_excerpt.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/sumatra_362_cohesion.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/sumatra_362_cohesion.txt +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/university_6_17_no1_tx_excerpt.las +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/university_6_17_no1_tx_excerpt.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/ursa_308_xrd_mineralogy.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/ursa_308_xrd_mineralogy.txt +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/volve_15_9_19_sr_excerpt.las +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/volve_15_9_19_sr_excerpt.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/volve_15_9_19a_core_excerpt.csv +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/volve_15_9_19a_core_excerpt.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/volve_f12_f14_production_excerpt.csv +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/volve_f12_f14_production_excerpt.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/walvis_208_petm_carbonate.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/walvis_208_petm_carbonate.txt +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/woodlark_1109_rock_eval.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/catalog/woodlark_1109_rock_eval.txt +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/certificates.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/cli.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/client_reports.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/config_versioning.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/conformance.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/correlation.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/dashboard.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/deviation.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/doctor.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/downhole_engine.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/drift_monitor.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/earth_model.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/etp.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/etp12_schemas.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/example_register_map.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/fat_sat.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/files.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/follower.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/forward_model.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/gamma.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/gauge_aging.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/gauge_specs.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/geodesy.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/gravity_reference.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/help/TESTER_GUIDE.md +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/help/alarms.md +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/help/audit-update.md +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/help/conformance.md +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/help/data-in.md +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/help/doctor.md +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/help/drift.md +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/help/etp.md +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/help/files.md +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/help/instruments.md +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/help/notifications.md +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/help/osdu.md +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/help/patches.md +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/help/ppdm.md +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/help/quakeml.md +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/help/quality.md +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/help/seismic.md +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/help/site.md +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/help/sites.md +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/help/sra.md +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/help/start.md +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/help/transients.md +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/help/upkeep.md +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/help/vibration.md +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/help/well-tests.md +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/housekeeping.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/inverse_engine.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/jobs.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/live_ports.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/loadtest.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/matplotlib_demo.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/modbus.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/model_card.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/mqtt_port.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/notify.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/opcua_port.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/operator_app.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/osdu.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/patches.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/permits.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/ports.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/ppdm.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/profile_catalog.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/project.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/qt6_downhole_app.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/quakeml.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/reconciler.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/reference/IU_ANMO_10_BHZ_response.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/reference/IU_ANMO_10_BHZ_response.xml +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/reference/libmseed_steim.provenance.json +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/reference/libmseed_steim1_4096.mseed +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/reference/libmseed_steim2_4096.mseed +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/reference/libmseed_steim2_512.mseed +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/rock_inventory.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/sample_record.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/sample_well_profile.csv +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/sbom.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/segy.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/seismic_array.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/seismic_assoc.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/seismic_detect.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/seismic_film.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/seismic_harmonic.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/seismic_qc.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/seismic_response.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/seismic_signature.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/seismic_track.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/seismic_uncertainty.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/seismic_unlisted.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/sensor_swap.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/service_life.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/shell.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/shut_in.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/sla_report.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/sra.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/store_forward.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/strata_join.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/supervisor.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/survey_cmd.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/survey_view.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/telemetry.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/tool_library.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/transient.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/vibration.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/well_assembler.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/well_test_validation.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/wits0.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea/witsml.py +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea_program.egg-info/dependency_links.txt +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea_program.egg-info/entry_points.txt +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea_program.egg-info/requires.txt +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/gea_program.egg-info/top_level.txt +0 -0
- {gea_program-0.16.0 → gea_program-0.17.0}/setup.cfg +0 -0
- {gea_program-0.16.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.
|
|
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`, `etp.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 (
|
|
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]")
|
|
@@ -623,6 +623,31 @@ promoted; the group and the support are declarations; a matched frequency
|
|
|
623
623
|
is not a fault size. CSV or miniSEED/SAC; `gea vibration` runs the labelled
|
|
624
624
|
pump; `gea help vibration`.
|
|
625
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
|
+
|
|
626
651
|
### WITSML 2.x over ETP
|
|
627
652
|
|
|
628
653
|
The newer rigs and stores do not poll: Energistics ETP v1.2 is a WebSocket
|
|
@@ -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`, `etp.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 (
|
|
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]")
|
|
@@ -584,6 +584,31 @@ promoted; the group and the support are declarations; a matched frequency
|
|
|
584
584
|
is not a fault size. CSV or miniSEED/SAC; `gea vibration` runs the labelled
|
|
585
585
|
pump; `gea help vibration`.
|
|
586
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
|
+
|
|
587
612
|
### WITSML 2.x over ETP
|
|
588
613
|
|
|
589
614
|
The newer rigs and stores do not poll: Energistics ETP v1.2 is a WebSocket
|
|
@@ -284,7 +284,8 @@ def main(argv=None) -> int:
|
|
|
284
284
|
choices=["init", "add-file", "add-catalog", "add-live", "remove", "list", "migrate", "refresh", "audit",
|
|
285
285
|
"add-seismic", "refresh-seismic", "remove-seismic", "sar-film", "refresh-all", "add-track", "refresh-track", "remove-track",
|
|
286
286
|
"seismic-dataset", "seismic-field", "add-site", "sites", "site-report", "remove-site", "rename",
|
|
287
|
-
"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"])
|
|
288
289
|
p_ws.add_argument("--stations", type=str, nargs="+", default=None, help="add-track: two or more array station ids of this workspace")
|
|
289
290
|
p_ws.add_argument("--site", type=str, default=None, help="site-report/remove-site: the site id")
|
|
290
291
|
p_ws.add_argument("--api", type=str, default=None, help="set-well: the API number")
|
|
@@ -354,7 +355,10 @@ def main(argv=None) -> int:
|
|
|
354
355
|
p_ws.add_argument("--unit", type=str, default="m", help="add-seismic with --permits and --zone: the grid unit (m, usft, ft)")
|
|
355
356
|
p_ws.add_argument("--band", type=float, nargs=2, default=None, metavar=("LO", "HI"), help="add-seismic: the band, Hz (default 1 50)")
|
|
356
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)")
|
|
357
|
-
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)")
|
|
358
362
|
p_ws.add_argument("--name", type=str, default=None, help="init: site name; add-*: display name")
|
|
359
363
|
p_ws.add_argument("--file", type=str, default=None, help="add-file: the client's data file; add-live: the port configuration JSON")
|
|
360
364
|
p_ws.add_argument("--entry", type=str, default=None, help="add-catalog: catalogue entry")
|
|
@@ -407,6 +411,21 @@ def main(argv=None) -> int:
|
|
|
407
411
|
p_es.add_argument("--frames", type=int, default=600)
|
|
408
412
|
p_es.add_argument("--interval", type=float, default=1.0, help="seconds between samples")
|
|
409
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)
|
|
410
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")
|
|
411
430
|
p_w0.add_argument("--port", type=int, default=5001)
|
|
412
431
|
p_w0.add_argument("--frames", type=int, default=600)
|
|
@@ -1915,6 +1934,31 @@ def main(argv=None) -> int:
|
|
|
1915
1934
|
raise SystemExit("well-identity needs --well <id>")
|
|
1916
1935
|
r = ws.well_identity(a.well)
|
|
1917
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
|
|
1918
1962
|
elif a.action == "quakeml-export":
|
|
1919
1963
|
from .quakeml import report_text as _qml_text
|
|
1920
1964
|
if not a.site:
|
|
@@ -1973,6 +2017,60 @@ def main(argv=None) -> int:
|
|
|
1973
2017
|
stop.set()
|
|
1974
2018
|
print("wits0-sim: stopped")
|
|
1975
2019
|
return 0
|
|
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
|
|
1976
2074
|
elif a.cmd == "etp-sim":
|
|
1977
2075
|
from . import etp as _E
|
|
1978
2076
|
th, port, stop = _E.simulate_store(port=a.port, frames=a.frames, interval_s=a.interval, seed=a.seed, verbose=True)
|
|
@@ -50,6 +50,7 @@ import re
|
|
|
50
50
|
import subprocess
|
|
51
51
|
import sys
|
|
52
52
|
import tempfile
|
|
53
|
+
import threading
|
|
53
54
|
import time
|
|
54
55
|
from pathlib import Path
|
|
55
56
|
|
|
@@ -5581,6 +5582,143 @@ def section_bj_etp(tmp: str) -> None:
|
|
|
5581
5582
|
"launcher while it runs, so the program cannot be updated in place - stop, update, start with `python -m gea serve`; a panel started "
|
|
5582
5583
|
"with `python -m gea` draws no such finding, and `gea update` from a launcher-started panel on Windows refuses to run pip and says why")
|
|
5583
5584
|
|
|
5585
|
+
|
|
5586
|
+
def section_bk_seedlink(tmp: str) -> None:
|
|
5587
|
+
"""Section BK - the seismic leg's live port: SeedLink 3 and 4.0 against the simulator on the loopback, the records
|
|
5588
|
+
byte for byte into day files, the resume by sequence number across a dropped link with no record twice, the live
|
|
5589
|
+
station in the workspace, the service and the page, folded into the record list and refreshed."""
|
|
5590
|
+
import http.cookiejar
|
|
5591
|
+
import subprocess as _sp
|
|
5592
|
+
import urllib.request
|
|
5593
|
+
import urllib.error
|
|
5594
|
+
from . import seedlink as L
|
|
5595
|
+
from . import seismic as S
|
|
5596
|
+
from . import helplib as H
|
|
5597
|
+
from .workspace import Workspace
|
|
5598
|
+
from .service import Service, Users
|
|
5599
|
+
pkg = Path(__file__).parent
|
|
5600
|
+
d = Path(tmp, "bk"); d.mkdir()
|
|
5601
|
+
|
|
5602
|
+
# -- the protocol, both versions, on the loopback ----------------------------------------------------------------
|
|
5603
|
+
r = L.selftest(tmp=str(d / "self"))
|
|
5604
|
+
h4 = L.parse_hello("SeedLink v4.0 (RingServer/2022.075) :: SLPROTO:3.1 SLPROTO:4.0 CAP EXTREPLY NSWILDCARD BATCH WS:13")
|
|
5605
|
+
h3 = L.parse_hello("SeedLink v3.3 (2020.122)")
|
|
5606
|
+
import numpy as _np
|
|
5607
|
+
tr = S.Trace("XX", "PAD3", "", "HHZ", 1.7e9, 100.0, _np.arange(500, dtype=_np.int64))
|
|
5608
|
+
recs = S.build_records([tr], "INT32", 512)
|
|
5609
|
+
back = S.read_mseed(b"".join(recs))
|
|
5610
|
+
sel_err = None
|
|
5611
|
+
try:
|
|
5612
|
+
L.parse_selector("!HHZ")
|
|
5613
|
+
except ValueError as e:
|
|
5614
|
+
sel_err = str(e)
|
|
5615
|
+
ok(r["status"] == "OK" and all(r["checks"].values()) and r["label"] == "SIMULATION_SELF_TEST"
|
|
5616
|
+
and h4["protocols"] == ["3.1", "4.0"] and "NSWILDCARD" in h4["capabilities"] and h3["protocols"] == ["3.0"]
|
|
5617
|
+
and L.selector_v4("HHZ.D") == "*_H_H_Z.2D" and L.selector_v4("00HHZ") == "00_H_H_Z" and L.parse_selector("HHZ.D") == ("", "HHZ", "D")
|
|
5618
|
+
and sel_err and "negative" in sel_err
|
|
5619
|
+
and len(recs) == 5 and all(len(x) == 512 for x in recs) and recs[0][:6] == b"000001" and recs[4][:6] == b"000005"
|
|
5620
|
+
and len(back) == 1 and back[0].npts == 500 and not back[0].gaps,
|
|
5621
|
+
"BK1 SeedLink 3 and 4.0 as the specifications have them, proven against the simulator on the loopback: HELLO parsed for the protocols and "
|
|
5622
|
+
"capabilities offered (a 3 server lists none), `STATION STA NET` / `SELECT LLCCC.T` / `DATA hhhhhh` / `END` answered OK and the 8-byte "
|
|
5623
|
+
"`SL` header with its hexadecimal sequence; `SLPROTO 4.0` / `STATION NET_STA` / `SELECT LOC_B_S_SS` / `DATA n` and the `SE` header with "
|
|
5624
|
+
"its little-endian length and uint64 sequence and the station id; INFO answered in a log record (3) and in JSON (4.0); a selector honoured; "
|
|
5625
|
+
"the records appended byte for byte to one day file per channel that reads back whole; a second run resuming from the saved sequence "
|
|
5626
|
+
"number asks DATA for the next and adds no record twice; a station the server does not have is REFUSED and named, with no reconnect loop; "
|
|
5627
|
+
"the 512-byte INT32 records the simulator serves are the writer's own and read back as one trace")
|
|
5628
|
+
|
|
5629
|
+
# -- a dropped link: the tap reconnects and resumes, nothing twice ------------------------------------------------
|
|
5630
|
+
srv = L.simulate_server(interval_s=0.1, rate=250.0)
|
|
5631
|
+
time.sleep(1.2)
|
|
5632
|
+
cfg = dict(L.EXAMPLE_CONFIG, host="127.0.0.1", port=srv.port, protocol="3", streams=[{"network": "XX", "station": "PAD3", "selectors": ["HHZ.D"]}],
|
|
5633
|
+
reconnect_s=[0.3, 0.3])
|
|
5634
|
+
tap = L.SeedLinkTap(cfg, str(d / "drop"))
|
|
5635
|
+
th = threading.Thread(target=tap.run, kwargs={"duration_s": 9.0}, daemon=True); th.start()
|
|
5636
|
+
time.sleep(2.5)
|
|
5637
|
+
n_before = tap.state["records"]
|
|
5638
|
+
dropped = srv.drop_clients()
|
|
5639
|
+
time.sleep(3.0)
|
|
5640
|
+
tap.stop(); th.join(timeout=10)
|
|
5641
|
+
st = tap.state
|
|
5642
|
+
day = [f for f in os.listdir(d / "drop") if f.endswith(".mseed")]
|
|
5643
|
+
trs = S.read_mseed(str(d / "drop" / day[0])) if day else []
|
|
5644
|
+
ok(dropped >= 1 and n_before >= 2 and st["reconnects"] >= 1 and st["records"] > n_before and st["status"] == "STOPPED"
|
|
5645
|
+
and len(day) == 1 and len(trs) == 1 and not trs[0].gaps and trs[0].npts == st["channels"]["XX.PAD3..HHZ"]["samples"] == st["records"] * 112
|
|
5646
|
+
and any(l.startswith("> DATA ") and len(l.split()) == 3 for l in st["handshake"]) and st["channels"]["XX.PAD3..HHZ"]["gaps"] == 0,
|
|
5647
|
+
"BK2 the link drops under a running session: the tap counts the reconnection, comes back with backoff, asks DATA for the sequence after "
|
|
5648
|
+
"the last record it filed, and the day file reads back as one trace with no gap and no record twice - the records on disk equal the "
|
|
5649
|
+
"records counted equal the samples over 112")
|
|
5650
|
+
|
|
5651
|
+
# -- the live station in the workspace, through the service and the page ---------------------------------------
|
|
5652
|
+
wsp = str(d / "ws"); ws = Workspace.create(wsp, "Pad 3", actor="tester")
|
|
5653
|
+
users = Users(str(Path(wsp, "users.json"))); users.add("op", "operator-pass-1", "operator")
|
|
5654
|
+
svc = Service(wsp, port=0, scheduler=False).start()
|
|
5655
|
+
base = svc.url.rstrip("/")
|
|
5656
|
+
opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(http.cookiejar.CookieJar()))
|
|
5657
|
+
|
|
5658
|
+
def call(method, path, body=None):
|
|
5659
|
+
req = urllib.request.Request(base + path, method=method, data=(json.dumps(body).encode() if body is not None else None))
|
|
5660
|
+
req.add_header("Content-Type", "application/json"); req.add_header("X-GEA-Action", "1")
|
|
5661
|
+
try:
|
|
5662
|
+
with opener.open(req, timeout=120) as rr:
|
|
5663
|
+
return rr.status, json.loads(rr.read())
|
|
5664
|
+
except urllib.error.HTTPError as e:
|
|
5665
|
+
return e.code, json.loads(e.read())
|
|
5666
|
+
try:
|
|
5667
|
+
call("POST", "/api/login", {"name": "op", "password": "operator-pass-1"})
|
|
5668
|
+
st_bad = call("POST", "/api/seismic/live/add", {"display": "bad", "config": {"host": "127.0.0.1", "port": srv.port, "streams": []}})[0]
|
|
5669
|
+
cfg_all = dict(L.EXAMPLE_CONFIG, host="127.0.0.1", port=srv.port, protocol="auto")
|
|
5670
|
+
st_add, added = call("POST", "/api/seismic/live/add", {"display": "Pad 3 live", "config": cfg_all, "lat": 31.5, "lon": -103.2, "band": [1, 50]})
|
|
5671
|
+
sid = added.get("id")
|
|
5672
|
+
st_start, started = call("POST", f"/api/seismic/live/{sid}/start")
|
|
5673
|
+
t0 = time.time(); row = {}
|
|
5674
|
+
while time.time() - t0 < 20: # until the card shows six records and a latency, not a fixed sleep (Windows was slower)
|
|
5675
|
+
st_list, lst = call("GET", "/api/seismic/live")
|
|
5676
|
+
row = next((x for x in lst["stations"] if x["id"] == sid), {})
|
|
5677
|
+
if row.get("records", 0) >= 6 and row.get("latency_p95_s") is not None and len(row.get("channels", {})) == 3:
|
|
5678
|
+
break
|
|
5679
|
+
time.sleep(0.5)
|
|
5680
|
+
st_seis, seis = call("GET", "/api/seismic")
|
|
5681
|
+
st_stop, stopped = call("POST", f"/api/seismic/live/{sid}/stop")
|
|
5682
|
+
st_fold, folded = call("POST", f"/api/seismic/live/{sid}/fold", {"include_open": True})
|
|
5683
|
+
st_fold2, folded2 = call("POST", f"/api/seismic/live/{sid}/fold", {"include_open": True})
|
|
5684
|
+
st_404 = call("POST", f"/api/seismic/live/{sid}/dance")[0]
|
|
5685
|
+
finally:
|
|
5686
|
+
svc.stop()
|
|
5687
|
+
srv.stop()
|
|
5688
|
+
station = ws.seismic_station(sid)
|
|
5689
|
+
ws.refresh_seismic(sid, actor="tester")
|
|
5690
|
+
res = ws.seismic_results(sid)
|
|
5691
|
+
audit = [a["action"] for a in ws.audit_log()]
|
|
5692
|
+
page_src = (pkg / "web" / "app.html").read_text(encoding="utf-8")
|
|
5693
|
+
r1 = _sp.run([sys.executable, "-m", "gea", "seedlink", "--write-example-config", str(d / "sl.json")], capture_output=True, text=True)
|
|
5694
|
+
r2 = _sp.run([sys.executable, "-m", "gea", "seedlink", "--config", str(d / "sl.json"), "--seconds", "1", "--out", str(d / "none")], capture_output=True, text=True)
|
|
5695
|
+
help_md = (pkg / "help" / "seedlink.md").read_text(encoding="utf-8")
|
|
5696
|
+
c = {
|
|
5697
|
+
"add_refused_and_added": st_bad == 409 and st_add == 200 and added["live"]["stations"] == ["XX_PAD3"] and added["files"] == [] and added["kind"] == "single",
|
|
5698
|
+
"started": st_start == 200 and started["running"] is True,
|
|
5699
|
+
"connected_on_card": st_list == 200 and row.get("status") == "CONNECTED" and row.get("protocol") == "4" and row.get("records", 0) >= 6 and row.get("running") is True,
|
|
5700
|
+
"three_channels_latency": set(row.get("channels", {})) == {"XX.PAD3..HHZ", "XX.PAD3..HHN", "XX.PAD3..HHE"} and row.get("latency_p95_s") is not None,
|
|
5701
|
+
"station_list_carries_live": st_seis == 200 and any(s_.get("id") == sid and s_.get("live_state", {}).get("status") == "CONNECTED" for s_ in seis["stations"]),
|
|
5702
|
+
"stopped": st_stop == 200 and stopped["running"] is False and stopped["status"] == "STOPPED",
|
|
5703
|
+
"folded_z_first": st_fold == 200 and len(folded["folded"]) == 3 and folded["folded"][0].split(".")[3] == "HHZ" and st_fold2 == 200 and folded2["folded"] == [],
|
|
5704
|
+
"unknown_action_404": st_404 == 404,
|
|
5705
|
+
"station_files_hashed": len(station["files"]) == 3 and all(n in station["sha256"] for n in station["files"]) and station["live"]["folds"] == 1 and station["live"]["enabled"] is False,
|
|
5706
|
+
"refreshed_on_folded": res.get("summary", {}).get("records") == 3 and res.get("summary", {}).get("unit") == "counts",
|
|
5707
|
+
"audited": all(x in audit for x in ("seismic.live.add", "seismic.live.start", "seismic.live.stop", "seismic.live.fold", "seismic.refresh")),
|
|
5708
|
+
"page_card": "Live stations (SeedLink)" in page_src and "/api/seismic/live/add" in page_src,
|
|
5709
|
+
"cli": r1.returncode == 0 and r2.returncode != 0 and "seedlink-sim" in (r2.stdout + r2.stderr),
|
|
5710
|
+
"help": any(t["topic"] == "seedlink" for t in H.topics()) and not H.check("seedlink") and "sequence number" in help_md
|
|
5711
|
+
and "across a gap" in help_md.split("will not call a measurement")[1],
|
|
5712
|
+
}
|
|
5713
|
+
failed = [k for k, v in c.items() if not v]
|
|
5714
|
+
ok(not failed,
|
|
5715
|
+
"BK3 a live station in the workspace: added through the service with its SeedLink config (an empty stream list refused), started - "
|
|
5716
|
+
"CONNECTED on 4.0 with the three channels, the latency and the records on the live-station card and on the station list - stopped, "
|
|
5717
|
+
"folded (the day files hashed into the record list, the vertical first; a second fold finds nothing), and refreshed by the leg on what "
|
|
5718
|
+
"the station sent; every step in the audit log; the page carries the card; `gea seedlink` writes its example and, with no server, "
|
|
5719
|
+
"names the simulator to start; the help page says a gap is counted, never bridged"
|
|
5720
|
+
+ (f" [failed: {', '.join(failed)}; row={json.dumps({k: row.get(k) for k in ('status', 'protocol', 'records', 'latency_p95_s', 'last_error')})}]" if failed else ""))
|
|
5721
|
+
|
|
5584
5722
|
def main() -> int:
|
|
5585
5723
|
print("GEA-Program - ACCEPTANCE SUITE (the product gate)")
|
|
5586
5724
|
# a handle a job subprocess still holds at the end must not turn a finished gate into a traceback on Windows:
|
|
@@ -5646,6 +5784,7 @@ def main() -> int:
|
|
|
5646
5784
|
section_bh_vibration(tmp)
|
|
5647
5785
|
section_bi_conformance(tmp)
|
|
5648
5786
|
section_bj_etp(tmp)
|
|
5787
|
+
section_bk_seedlink(tmp)
|
|
5649
5788
|
if _FAILS:
|
|
5650
5789
|
print(f"[ACCEPTANCE] {len(_FAILS)} FAILURES ({_PASS} passed):")
|
|
5651
5790
|
for f in _FAILS:
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Live stations (SeedLink)
|
|
2
|
+
|
|
3
|
+
> The seismic leg has run on records that were already files. The stations do not hand out files; they serve SeedLink - the real-time protocol every data centre and most digitisers speak - and this is the port that takes it: the customer names its stations and channels, says where it left off, and the server pushes each miniSEED record as it is written. SeedLink 3 (the one every SeisComP and ringserver serves) and SeedLink 4.0 (FDSN, 2022) are both spoken; the server's HELLO decides which.
|
|
4
|
+
|
|
5
|
+
**The command**: `gea seedlink --write-example-config seedlink.json`, then put the server's `host` and `port` (a TexNet station is served by EarthScope at `rtserve.iris.washington.edu:18000`, network `TX`), the stations as `{"network", "station", "selectors"}` with selectors in SeedLink 3 form `LLCCC.T` (`HHZ.D`; `?` for one character; converted for a 4.0 server), and run `gea seedlink --config seedlink.json --seconds 3600 --out live`; the next run resumes from the last sequence number the state file holds. In a workspace: `gea workspace --path C:\site --action add-live-station --name "<display>" --seedlink-config seedlink.json --lat --lon`, then Start on the dashboard's Seismic page (the serving process keeps every enabled live station up and reconnects with backoff) or `--action live-run --station <id>`; `--action live-fold --station <id>` moves the finished day files into the station's record list, and Refresh runs the leg on them. `gea seedlink-sim --port 18000` is a three-component synthetic station to rehearse against; `gea seedlink --selftest` runs both protocols against it on the loopback; `gea seedlink --config ... --hello` prints what a server says of itself.
|
|
6
|
+
|
|
7
|
+
**What it writes**: every record the server sends, byte for byte, appended to a day file per channel - `NET.STA.LOC.CHA.YYYY.DDD.mseed` - under the station's `live/` folder, and the state beside it: per station the last sequence number (hexadecimal and per station in SeedLink 3, decimal in 4.0; the resume asks `DATA` for the next), per channel the last sample time, the latency (arrival minus the record's last sample), the records, the samples, and the gaps and overlaps between consecutive records (a gap is counted and left, never bridged). A fold moves the day files that are no longer being written (every day but the current UTC day, unless asked) into `source/`, hashes each and appends it to the station's files, vertical channels first, with the audit line naming what was folded; what the leg then computes is computed from what the station sent. The handshake is kept on the state (`HELLO`, the protocol, each `STATION`/`SELECT`/`DATA`, `END`) and the server's own description with it.
|
|
8
|
+
|
|
9
|
+
**The number to check**: on the live-station card, `CONNECTED` with the latency p95 and the records climbing; after a fold, the station's file count; `REFUSED` with the `STATION` line names a station the server does not have (no reconnection loop); `ENDED` is the server's own `END`.
|
|
10
|
+
|
|
11
|
+
**What this page will not call a measurement**: a sample across a gap - it is counted and the file keeps the gap. A time the record's header does not carry - the record's own time is the time. A record in a payload format it does not know - counted with the format byte and left undecoded; miniSEED 2 is what is read, miniSEED 3 is named when a 4.0 server sends it. A session the server has ended - the reason is on the state and the port stops. A location or a response - the leg's refresh on the folded files, with a StationXML, is where counts become ground motion.
|
|
@@ -45,6 +45,7 @@ INDEX: List[tuple] = [
|
|
|
45
45
|
('vibration', 'Machine vibration', 'the pump\'s record through the ISO 20816-3 zones and bearing envelope analysis'),
|
|
46
46
|
('conformance', 'Conformity, in the standards\' words', 'certificates against ISO/IEC 17025 7.8 and the bias against its class under a named ILAC-G8 decision rule'),
|
|
47
47
|
('etp', 'WITSML 2.x over ETP', 'the live port on Energistics ETP v1.2: subscribe to the store\'s channels and it pushes'),
|
|
48
|
+
('seedlink', 'Live stations (SeedLink)', 'the seismic leg\'s live port: a station\'s records as it writes them, resumed by sequence number, folded into its record list'),
|
|
48
49
|
('doctor', 'Which code is running', 'gea doctor and the serve start-up gate'),
|
|
49
50
|
('audit-update', 'Audit / Update', 'the whole audit log with filters, the program against PyPI, every report against its source'),
|
|
50
51
|
('upkeep', 'Upkeep', 'housekeeping, sessions, the TLS proxy, the load test'),
|