gea-program 0.23.0__tar.gz → 0.24.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.23.0 → gea_program-0.24.0}/PKG-INFO +20 -2
- {gea_program-0.23.0 → gea_program-0.24.0}/README.md +19 -1
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/__init__.py +1 -1
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/__main__.py +125 -4
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/acceptance_tests.py +193 -0
- gea_program-0.24.0/gea/dlis.py +624 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/files.py +18 -2
- gea_program-0.24.0/gea/help/dlis.md +11 -0
- gea_program-0.24.0/gea/help/historian.md +11 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/helplib.py +2 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/patches.py +35 -5
- gea_program-0.24.0/gea/piwebapi.py +366 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/ports.py +4 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/service.py +3 -3
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/survey_cmd.py +13 -3
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/web/app.html +1 -1
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/workspace.py +1 -1
- {gea_program-0.23.0 → gea_program-0.24.0}/gea_program.egg-info/PKG-INFO +20 -2
- {gea_program-0.23.0 → gea_program-0.24.0}/gea_program.egg-info/SOURCES.txt +4 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/pyproject.toml +1 -1
- {gea_program-0.23.0 → gea_program-0.24.0}/LICENSE +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/BENCH_TEST_PROTOCOL.md +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/accuracy_statement.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/alarm_engine.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/backup.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/bench.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/blind_harness.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/acex_lomonosov_age_depth_model.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/acex_lomonosov_age_depth_model.txt +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/agassiz77_canada_temperature.csv +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/agassiz77_canada_temperature.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/barbados_110_consolidation.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/barbados_110_consolidation.txt +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/bengal_u1452_grain_size.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/bengal_u1452_grain_size.txt +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/blake_164_methane_isotopes.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/blake_164_methane_isotopes.txt +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.txt +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/collingwood_1_28_ks_complete.las +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/collingwood_1_28_ks_complete.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/costa_rica_odp_friction_envelope.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/costa_rica_odp_friction_envelope.txt +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.txt +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/dsdp_504b_physical_properties.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/dsdp_504b_physical_properties.txt +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/dsdp_504b_sound_velocity.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/dsdp_504b_sound_velocity.txt +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/elgygytgyn_5011_turbidites.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/elgygytgyn_5011_turbidites.txt +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/epica_domec_co2_800kyr.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/epica_domec_co2_800kyr.txt +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/fram_909_organic_petrography.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/fram_909_organic_petrography.txt +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/gbr_325_coral_uth_ages.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/gbr_325_coral_uth_ages.txt +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/gisp2_greenland_temperature.csv +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/gisp2_greenland_temperature.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/gom_308_t2p_insitu.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/gom_308_t2p_insitu.txt +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/guaymas_385_dom_d13c.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/guaymas_385_dom_d13c.txt +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/hikurangi_u1520_friction_insitu.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/hikurangi_u1520_friction_insitu.txt +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.txt +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/iodp_u1324_pore_pressure.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/iodp_u1324_pore_pressure.txt +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/jfast_c0019_slow_slip_events.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/jfast_c0019_slow_slip_events.txt +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/kennetcook_2_p129_excerpt.las +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/kennetcook_2_p129_excerpt.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/ktb_hb_bhgm_density.dat +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/ktb_hb_bhgm_density.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/ktb_hb_complog_6020_excerpt.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/ktb_hb_complog_6020_excerpt.txt +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/ktb_hb_hlog246_temperature.dat +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/ktb_hb_hlog246_temperature.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/ktb_hb_rockmech_compress.dat +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/ktb_hb_rockmech_compress.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.dat +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/ktb_vb_rockmech_compress.dat +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/ktb_vb_rockmech_compress.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/ktb_vb_vlog251_temperature.dat +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/ktb_vb_vlog251_temperature.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/l06_06_nl_survey.csv +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/l06_06_nl_survey.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/l07_01_nl_excerpt.las +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/l07_01_nl_excerpt.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/mariana_1200_serpentinite_geochem.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/mariana_1200_serpentinite_geochem.txt +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/med_160_sapropels.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/med_160_sapropels.txt +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/nankai_megasplay_shear_strength.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/nankai_megasplay_shear_strength.txt +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/odp_1027b_thermal_conductivity.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/odp_1027b_thermal_conductivity.txt +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/odp_1027c_cork_temperature.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/odp_1027c_cork_temperature.txt +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/odp_1165b_thermal_conductivity.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/odp_1165b_thermal_conductivity.txt +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.txt +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/odp_504b_dike_elastic_moduli.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/odp_504b_dike_elastic_moduli.txt +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/odp_504b_leg137_borehole_fluids.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/odp_504b_leg137_borehole_fluids.txt +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.txt +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/peru_201_sulfate_reduction.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/peru_201_sulfate_reduction.txt +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/scorpio_e1_sa_excerpt.las +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/scorpio_e1_sa_excerpt.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/sumatra_362_cohesion.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/sumatra_362_cohesion.txt +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/university_6_17_no1_tx_excerpt.las +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/university_6_17_no1_tx_excerpt.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/ursa_308_xrd_mineralogy.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/ursa_308_xrd_mineralogy.txt +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/volve_15_9_19_sr_excerpt.las +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/volve_15_9_19_sr_excerpt.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/volve_15_9_19a_core_excerpt.csv +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/volve_15_9_19a_core_excerpt.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/volve_f12_f14_production_excerpt.csv +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/volve_f12_f14_production_excerpt.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/walvis_208_petm_carbonate.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/walvis_208_petm_carbonate.txt +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/woodlark_1109_rock_eval.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/catalog/woodlark_1109_rock_eval.txt +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/certificates.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/cli.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/client_reports.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/config_versioning.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/conformance.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/correlation.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/dashboard.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/deviation.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/dnp3.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/doctor.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/downhole_engine.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/drift_monitor.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/earth_model.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/elevation.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/etp.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/etp12_schemas.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/example_register_map.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/fat_sat.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/follower.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/forward_model.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/gamma.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/gauge_aging.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/gauge_specs.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/geodesy.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/gravity_reference.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/hartip.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/help/TESTER_GUIDE.md +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/help/alarms.md +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/help/audit-update.md +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/help/backup.md +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/help/conformance.md +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/help/data-in.md +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/help/doctor.md +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/help/drift.md +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/help/etp.md +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/help/field-ports.md +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/help/files.md +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/help/instruments.md +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/help/interval.md +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/help/month-end.md +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/help/notifications.md +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/help/osdu.md +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/help/patches.md +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/help/ppdm.md +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/help/quakeml.md +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/help/quality.md +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/help/seedlink.md +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/help/seismic.md +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/help/site.md +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/help/sites.md +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/help/soak.md +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/help/sra.md +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/help/start.md +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/help/structure.md +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/help/transients.md +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/help/upkeep.md +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/help/vibration.md +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/help/well-tests.md +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/housekeeping.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/interval.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/inverse_engine.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/jobs.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/live_ports.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/loadtest.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/matplotlib_demo.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/modbus.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/modbus_rtu.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/model_card.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/month_end.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/mqtt_port.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/notify.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/opcua_port.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/operator_app.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/osdu.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/permits.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/ppdm.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/profile_catalog.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/project.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/qt6_downhole_app.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/quakeml.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/reconciler.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/reference/IU_ANMO_10_BHZ_response.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/reference/IU_ANMO_10_BHZ_response.xml +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/reference/libmseed_steim.provenance.json +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/reference/libmseed_steim1_4096.mseed +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/reference/libmseed_steim2_4096.mseed +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/reference/libmseed_steim2_512.mseed +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/rock_inventory.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/sample_record.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/sample_well_profile.csv +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/sbom.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/seedlink.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/segy.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/seismic.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/seismic_array.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/seismic_assoc.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/seismic_detect.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/seismic_film.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/seismic_harmonic.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/seismic_qc.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/seismic_response.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/seismic_signature.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/seismic_track.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/seismic_uncertainty.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/seismic_unlisted.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/sensor_swap.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/service_life.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/shapefile.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/shell.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/shut_in.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/sla_report.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/soak.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/sra.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/store_forward.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/strata_join.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/structure.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/supervisor.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/survey_view.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/telemetry.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/tool_library.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/transient.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/vibration.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/well_assembler.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/well_test_validation.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/wits0.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea/witsml.py +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea_program.egg-info/dependency_links.txt +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea_program.egg-info/entry_points.txt +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea_program.egg-info/requires.txt +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/gea_program.egg-info/top_level.txt +0 -0
- {gea_program-0.23.0 → gea_program-0.24.0}/setup.cfg +0 -0
- {gea_program-0.23.0 → gea_program-0.24.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.24.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
|
|
@@ -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 (359 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]")
|
|
@@ -648,6 +648,24 @@ against it; `gea seedlink --config ... --hello` prints what a server says of
|
|
|
648
648
|
itself; `gea help seedlink`. A TexNet station is served by EarthScope at
|
|
649
649
|
`rtserve.iris.washington.edu:18000`, network `TX`.
|
|
650
650
|
|
|
651
|
+
### The log as delivered, and the historian
|
|
652
|
+
|
|
653
|
+
A well log arrives as DLIS (API RP66 V1); `gea/dlis.py` reads the container
|
|
654
|
+
as the standard lays it out, with no dependency - the storage unit label,
|
|
655
|
+
visible records, logical record segments and trailers, the sets of objects
|
|
656
|
+
with every representation code, the frames and channels, the frame data -
|
|
657
|
+
and hands the strata survey the same stream the LAS port does: `gea dlis
|
|
658
|
+
--file log.dlis --info` describes it, `--to-las` exports a frame, `gea survey
|
|
659
|
+
--file log.dlis` reads it directly, and the Files page detects it by its
|
|
660
|
+
label. Encrypted records are skipped and named, never decrypted or guessed.
|
|
661
|
+
Proven on a real Schlumberger station log (104 channels, two frames, 3,252
|
|
662
|
+
records in a tenth of a second). The operator's gauge history in a PI System
|
|
663
|
+
comes out through PI Web API (`gea piwebapi`, protocol `piwebapi` on the
|
|
664
|
+
Patch panel): GET only, the archive between two times with its paging
|
|
665
|
+
followed, every value with the archive's timestamp and flags, credentials
|
|
666
|
+
from environment variables the map names. `gea help dlis`, `gea help
|
|
667
|
+
historian`.
|
|
668
|
+
|
|
651
669
|
### The field ports
|
|
652
670
|
|
|
653
671
|
The three links a gauge panel on a pad actually offers, each read-only in
|
|
@@ -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 (359 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]")
|
|
@@ -609,6 +609,24 @@ against it; `gea seedlink --config ... --hello` prints what a server says of
|
|
|
609
609
|
itself; `gea help seedlink`. A TexNet station is served by EarthScope at
|
|
610
610
|
`rtserve.iris.washington.edu:18000`, network `TX`.
|
|
611
611
|
|
|
612
|
+
### The log as delivered, and the historian
|
|
613
|
+
|
|
614
|
+
A well log arrives as DLIS (API RP66 V1); `gea/dlis.py` reads the container
|
|
615
|
+
as the standard lays it out, with no dependency - the storage unit label,
|
|
616
|
+
visible records, logical record segments and trailers, the sets of objects
|
|
617
|
+
with every representation code, the frames and channels, the frame data -
|
|
618
|
+
and hands the strata survey the same stream the LAS port does: `gea dlis
|
|
619
|
+
--file log.dlis --info` describes it, `--to-las` exports a frame, `gea survey
|
|
620
|
+
--file log.dlis` reads it directly, and the Files page detects it by its
|
|
621
|
+
label. Encrypted records are skipped and named, never decrypted or guessed.
|
|
622
|
+
Proven on a real Schlumberger station log (104 channels, two frames, 3,252
|
|
623
|
+
records in a tenth of a second). The operator's gauge history in a PI System
|
|
624
|
+
comes out through PI Web API (`gea piwebapi`, protocol `piwebapi` on the
|
|
625
|
+
Patch panel): GET only, the archive between two times with its paging
|
|
626
|
+
followed, every value with the archive's timestamp and flags, credentials
|
|
627
|
+
from environment variables the map names. `gea help dlis`, `gea help
|
|
628
|
+
historian`.
|
|
629
|
+
|
|
612
630
|
### The field ports
|
|
613
631
|
|
|
614
632
|
The three links a gauge panel on a pad actually offers, each read-only in
|
|
@@ -278,6 +278,21 @@ def main(argv=None) -> int:
|
|
|
278
278
|
p_lp.add_argument("--stream-csv", type=str, default=None, help="also write a historian-style CSV the other commands ingest")
|
|
279
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")
|
|
280
280
|
|
|
281
|
+
p_dl = sub.add_parser("dlis", help="a DLIS (RP66 V1) well log: --info describes it (origin, channels, frames, what was skipped); --frame --out writes the frame as a historian-style CSV; --to-las writes LAS 2.0")
|
|
282
|
+
p_dl.add_argument("--file", type=str, required=True)
|
|
283
|
+
p_dl.add_argument("--info", action="store_true")
|
|
284
|
+
p_dl.add_argument("--frame", type=str, default=None, help="the frame to read when the file holds several")
|
|
285
|
+
p_dl.add_argument("--out", type=str, default=None, help="write the frame's channels as CSV (index first)")
|
|
286
|
+
p_dl.add_argument("--to-las", type=str, default=None, help="write the frame as a LAS 2.0 file")
|
|
287
|
+
p_dl.add_argument("--json", action="store_true")
|
|
288
|
+
p_pi = sub.add_parser("piwebapi", help="the PI Web API historian pull, read-only: --config (base_url, auth by environment variable names, tags with paths); --selftest against the stand-in")
|
|
289
|
+
p_pi.add_argument("--config", type=str, default=None)
|
|
290
|
+
p_pi.add_argument("--write-example-config", type=str, default=None)
|
|
291
|
+
p_pi.add_argument("--out", type=str, default=None, help="write the records CSV here")
|
|
292
|
+
p_pi.add_argument("--stream-csv", type=str, default=None)
|
|
293
|
+
p_pi.add_argument("--seconds", type=float, default=None, help="ignored: a pull is one request per tag (kept so the live tap's call shape is the same)")
|
|
294
|
+
p_pi.add_argument("--record", type=str, default=None, help="write the pull's summary as one JSON line here")
|
|
295
|
+
p_pi.add_argument("--selftest", action="store_true")
|
|
281
296
|
for name, helptxt in (("modbus-rtu", "Modbus RTU port, read-only: the serial line (pyserial) or RTU over TCP; functions 03/04 only; the stand-in slave with --selftest"),
|
|
282
297
|
("dnp3", "DNP3 (IEEE 1815) master, read-only: integrity and event polls over TCP; analog inputs, binaries, counters with flags and event times; the stand-in outstation with --selftest"),
|
|
283
298
|
("hartip", "HART-IP client, read-only: session, command 0, 3 and 9; the variables with their unit codes and status; the stand-in device with --selftest")):
|
|
@@ -671,8 +686,9 @@ def main(argv=None) -> int:
|
|
|
671
686
|
p_fl.add_argument("--station-md", type=float, default=None)
|
|
672
687
|
p_fl.add_argument("--actor", type=str, default="cli")
|
|
673
688
|
|
|
674
|
-
p_sy = sub.add_parser("survey", help="a LAS file in, one strata report out (--demo: the bundled public KTB excerpt); --out writes report.txt + survey.json")
|
|
675
|
-
p_sy.add_argument("--file", type=str, default=None, help="LAS 2.0 file")
|
|
689
|
+
p_sy = sub.add_parser("survey", help="a LAS or DLIS file in, one strata report out (--demo: the bundled public KTB excerpt); --out writes report.txt + survey.json")
|
|
690
|
+
p_sy.add_argument("--file", type=str, default=None, help="LAS 2.0 or DLIS (RP66 V1) file")
|
|
691
|
+
p_sy.add_argument("--frame", type=str, default=None, help="DLIS: the frame to read when the file holds several (gea dlis --file X --info lists them)")
|
|
676
692
|
p_sy.add_argument("--demo", action="store_true")
|
|
677
693
|
p_sy.add_argument("--family", type=str, default="continental_crystalline", help="prior family for the estimator")
|
|
678
694
|
p_sy.add_argument("--lat", type=float, default=None)
|
|
@@ -808,8 +824,8 @@ def main(argv=None) -> int:
|
|
|
808
824
|
import json as _json
|
|
809
825
|
from .survey_cmd import run_survey
|
|
810
826
|
if not (a.file or a.demo):
|
|
811
|
-
raise SystemExit("survey needs --file <LAS> or --demo")
|
|
812
|
-
txt, machine = run_survey(path=a.file, demo=a.demo, family=a.family, lat=a.lat, elev=a.elev)
|
|
827
|
+
raise SystemExit("survey needs --file <LAS or DLIS> or --demo")
|
|
828
|
+
txt, machine = run_survey(path=a.file, demo=a.demo, family=a.family, lat=a.lat, elev=a.elev, frame=a.frame)
|
|
813
829
|
print(txt)
|
|
814
830
|
if a.out:
|
|
815
831
|
os.makedirs(a.out, exist_ok=True)
|
|
@@ -2282,6 +2298,111 @@ def main(argv=None) -> int:
|
|
|
2282
2298
|
stop.set()
|
|
2283
2299
|
print("etp-sim: stopped")
|
|
2284
2300
|
return 0
|
|
2301
|
+
elif a.cmd == "dlis":
|
|
2302
|
+
import json as _json
|
|
2303
|
+
from .dlis import DlisError, DlisFile, read as _read_dlis
|
|
2304
|
+
try:
|
|
2305
|
+
if a.info or not (a.out or a.to_las):
|
|
2306
|
+
with open(a.file, "rb") as f:
|
|
2307
|
+
info = DlisFile(f.read(), os.path.basename(a.file)).describe()
|
|
2308
|
+
if a.json:
|
|
2309
|
+
print(_json.dumps(info, indent=1, default=str)); return 0
|
|
2310
|
+
o = info["origin"]
|
|
2311
|
+
print(f"dlis: {a.file}")
|
|
2312
|
+
print(f" storage set: {info['storage_unit']['storage_set_id']}; file {info['file_header'].get('ID', '').strip()} seq {info['file_header'].get('SEQUENCE-NUMBER', '').strip()}")
|
|
2313
|
+
print(f" well: {o.get('WELL-NAME', '').strip() or '?'} field: {o.get('FIELD-NAME', '').strip() or '?'} company: {o.get('COMPANY', '').strip() or '?'} producer: {o.get('PRODUCER-NAME', '').strip() or '?'} created: {o.get('CREATION-TIME', '?')}")
|
|
2314
|
+
print(f" {info['n_records']} logical records; sets: " + ", ".join(f"{t}{'(' + n + ')' if n else ''} x{k}" for t, n, k in info["eflr_sets"]))
|
|
2315
|
+
if info.get("encrypted_records"):
|
|
2316
|
+
print(" encrypted records skipped: " + ", ".join(f"{k} x{n}" for k, n in info["encrypted_records"].items()))
|
|
2317
|
+
for u in info["unparsed"]:
|
|
2318
|
+
print(" not parsed: " + u)
|
|
2319
|
+
print(f" {len(info['channels'])} channels:")
|
|
2320
|
+
for c in info["channels"]:
|
|
2321
|
+
print(f" {c['name']:<12s} {c['units']:<10s} {c['code']:<7s} dim {c['dimension']} {c['long_name']}")
|
|
2322
|
+
for fr in info["frames"]:
|
|
2323
|
+
print(f" frame {fr['name']}: index {fr['index_type']} {fr['direction']}, spacing {fr['spacing']} {fr['spacing_units']}, {fr['index_min']}..{fr['index_max']}; {len(fr['channels'])} channels: {', '.join(fr['channels'][:12])}{' ...' if len(fr['channels']) > 12 else ''}")
|
|
2324
|
+
return 0
|
|
2325
|
+
st = _read_dlis(a.file, frame=a.frame)
|
|
2326
|
+
except (DlisError, OSError) as e:
|
|
2327
|
+
raise SystemExit(f"dlis: {e}")
|
|
2328
|
+
print(f"dlis: frame {st.meta['frame']}: {len(st.index)} rows, index {st.meta['index_channel']} ({st.meta['index_units']}) {float(st.index[0]):g}..{float(st.index[-1]):g}, {len(st.channels)} channels"
|
|
2329
|
+
+ (f"; {st.meta['notes']}" if st.meta.get("notes") else ""))
|
|
2330
|
+
if a.out:
|
|
2331
|
+
import csv as _csv
|
|
2332
|
+
with open(a.out, "w", newline="", encoding="utf-8") as f:
|
|
2333
|
+
w = _csv.writer(f)
|
|
2334
|
+
w.writerow([st.meta["index_channel"]] + list(st.channels))
|
|
2335
|
+
w.writerow([st.meta["index_units"]] + [st.channels[c].unit for c in st.channels])
|
|
2336
|
+
for i in range(len(st.index)):
|
|
2337
|
+
w.writerow([float(st.index[i])] + ["" if st.channels[c].values[i] != st.channels[c].values[i] else float(st.channels[c].values[i]) for c in st.channels])
|
|
2338
|
+
print(" csv:", a.out)
|
|
2339
|
+
if a.to_las:
|
|
2340
|
+
with open(a.to_las, "w", encoding="utf-8") as f:
|
|
2341
|
+
f.write("~VERSION INFORMATION\n VERS. 2.0 : CWLS LOG ASCII STANDARD - VERSION 2.0\n WRAP. NO : ONE LINE PER DEPTH STEP\n")
|
|
2342
|
+
f.write("~WELL INFORMATION\n")
|
|
2343
|
+
f.write(f" STRT.{st.meta['index_units']:<6s} {float(st.index[0]):.4f} : START\n STOP.{st.meta['index_units']:<6s} {float(st.index[-1]):.4f} : STOP\n")
|
|
2344
|
+
f.write(f" STEP.{st.meta['index_units']:<6s} {(float(st.index[1]) - float(st.index[0])) if len(st.index) > 1 else 0.0:.4f} : STEP\n NULL. -999.25 : NULL VALUE\n")
|
|
2345
|
+
f.write(f" WELL. {st.meta.get('WELL-NAME', '').strip()} : WELL\n FLD . {st.meta.get('FIELD-NAME', '').strip()} : FIELD\n COMP. {st.meta.get('COMPANY', '').strip()} : COMPANY\n SRVC. {st.meta.get('PRODUCER-NAME', '').strip()} : SERVICE COMPANY\n")
|
|
2346
|
+
f.write(f" SRC . DLIS {os.path.basename(a.file)} frame {st.meta['frame']} : SOURCE (gea dlis --to-las)\n")
|
|
2347
|
+
f.write("~CURVE INFORMATION\n")
|
|
2348
|
+
f.write(f" {st.meta['index_channel']:<8s}.{st.meta['index_units']:<8s} : INDEX\n")
|
|
2349
|
+
for c in st.channels:
|
|
2350
|
+
f.write(f" {c:<8s}.{st.channels[c].unit:<8s} : {c}\n")
|
|
2351
|
+
f.write("~A\n")
|
|
2352
|
+
for i in range(len(st.index)):
|
|
2353
|
+
f.write(f"{float(st.index[i]):.4f}" + "".join(f" {(-999.25 if st.channels[c].values[i] != st.channels[c].values[i] else float(st.channels[c].values[i])):.4f}" for c in st.channels) + "\n")
|
|
2354
|
+
print(" las:", a.to_las)
|
|
2355
|
+
return 0
|
|
2356
|
+
elif a.cmd == "piwebapi":
|
|
2357
|
+
import json as _json
|
|
2358
|
+
from .live_ports import records_to_stream, summarize, write_records_csv
|
|
2359
|
+
from . import piwebapi as _PI
|
|
2360
|
+
if a.selftest:
|
|
2361
|
+
r = _PI.selftest()
|
|
2362
|
+
print(f"piwebapi selftest: {'OK' if r['ok'] else 'FAILED'}")
|
|
2363
|
+
for k, v in r.items():
|
|
2364
|
+
if k != "ok":
|
|
2365
|
+
print(f" {'ok ' if v else 'BAD'} {k}")
|
|
2366
|
+
return 0 if r["ok"] else 1
|
|
2367
|
+
if a.write_example_config:
|
|
2368
|
+
with open(a.write_example_config, "w", encoding="utf-8") as f:
|
|
2369
|
+
_json.dump(_PI.EXAMPLE_CONFIG, f, indent=1)
|
|
2370
|
+
print("config:", a.write_example_config); return 0
|
|
2371
|
+
if not a.config:
|
|
2372
|
+
raise SystemExit("piwebapi needs --config (or --write-example-config to start one)")
|
|
2373
|
+
cfg = _PI.load_config(a.config)
|
|
2374
|
+
sim_h = None
|
|
2375
|
+
try:
|
|
2376
|
+
if cfg.get("base_url") == "sim":
|
|
2377
|
+
sim = _PI.SimServer(); sim_h, port_ = sim.serve(); cfg = {**cfg, "base_url": f"http://127.0.0.1:{port_}/piwebapi", "auth": "none"}
|
|
2378
|
+
try:
|
|
2379
|
+
port = _PI.PiPort(cfg)
|
|
2380
|
+
r = port.pull()
|
|
2381
|
+
except (_PI.PiError, ValueError) as e:
|
|
2382
|
+
raise SystemExit(f"piwebapi: {e}")
|
|
2383
|
+
finally:
|
|
2384
|
+
if sim_h is not None:
|
|
2385
|
+
sim_h.close()
|
|
2386
|
+
recs = port.records
|
|
2387
|
+
print(f"piwebapi: {_json.dumps(port.summary(), default=str)}")
|
|
2388
|
+
print(_json.dumps(summarize(recs), indent=1))
|
|
2389
|
+
if a.record:
|
|
2390
|
+
with open(a.record, "w", encoding="utf-8") as f:
|
|
2391
|
+
f.write(_json.dumps({"pull": r, "summary": port.summary()}, default=str) + "\n")
|
|
2392
|
+
if a.out:
|
|
2393
|
+
print(" records:", write_records_csv(recs, a.out))
|
|
2394
|
+
if a.stream_csv and recs:
|
|
2395
|
+
st = records_to_stream(recs, name="piwebapi")
|
|
2396
|
+
import csv as _csv
|
|
2397
|
+
from datetime import datetime as _dt, timedelta as _td
|
|
2398
|
+
t0 = _dt.fromisoformat(st.meta["start_time"])
|
|
2399
|
+
with open(a.stream_csv, "w", newline="", encoding="utf-8") as f:
|
|
2400
|
+
w = _csv.writer(f)
|
|
2401
|
+
w.writerow(["timestamp"] + list(st.channels))
|
|
2402
|
+
for i, t in enumerate(st.index):
|
|
2403
|
+
w.writerow([(t0 + _td(seconds=float(t))).isoformat()] + ["" if st.channels[c].values[i] != st.channels[c].values[i] else round(float(st.channels[c].values[i]), 4) for c in st.channels])
|
|
2404
|
+
print(" stream:", a.stream_csv)
|
|
2405
|
+
return 0 if not port.faults else 1
|
|
2285
2406
|
elif a.cmd in ("modbus-rtu", "dnp3", "hartip"):
|
|
2286
2407
|
import json as _json
|
|
2287
2408
|
from .live_ports import records_to_stream, summarize, write_records_csv
|
|
@@ -6774,6 +6774,198 @@ def section_bq_field_ports(tmp: str) -> None:
|
|
|
6774
6774
|
+ (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
|
|
6775
6775
|
|
|
6776
6776
|
|
|
6777
|
+
def section_br_logs_historian(tmp: str) -> None:
|
|
6778
|
+
"""Section BR - the DLIS reader proven on files this program wrote (every layer of RP66 V1, the representation
|
|
6779
|
+
codes, segmentation across visible records, encrypted records skipped and named, two frames, the refusals), into
|
|
6780
|
+
the files detector, the survey and the CLI; the PI Web API pull against the stand-in server (paging, flags,
|
|
6781
|
+
digital and system states, units, basic auth, plain HTTP refused), on the Patch panel and the CLI; the help."""
|
|
6782
|
+
import http.cookiejar
|
|
6783
|
+
import struct
|
|
6784
|
+
import subprocess as _sp
|
|
6785
|
+
import urllib.request
|
|
6786
|
+
import urllib.error
|
|
6787
|
+
from . import dlis as DL
|
|
6788
|
+
from . import piwebapi as PI
|
|
6789
|
+
from . import helplib as H
|
|
6790
|
+
from .files import detect, preview, read_any
|
|
6791
|
+
from .patches import PROTOCOLS, PatchSupervisor, validate_port_config
|
|
6792
|
+
from .survey_cmd import run_survey
|
|
6793
|
+
from .telemetry import TelemetryRecorder, TelemetryConfig
|
|
6794
|
+
from .downhole_engine import DownholeEngine, SimulatorConfig
|
|
6795
|
+
from .workspace import Workspace
|
|
6796
|
+
from .service import Service, Users
|
|
6797
|
+
pkg = Path(__file__).parent
|
|
6798
|
+
d = Path(tmp, "br"); d.mkdir()
|
|
6799
|
+
|
|
6800
|
+
# -- BR1: DLIS ------------------------------------------------------------------------------------------------------
|
|
6801
|
+
n = 3000
|
|
6802
|
+
depth = [1000.0 + 0.1524 * i for i in range(n)]
|
|
6803
|
+
rho = [2.3 + 0.3 * np.sin(i / 50.0) for i in range(n)]; rho[5] = -999.25
|
|
6804
|
+
dt = [80.0 + 10.0 * np.cos(i / 70.0) for i in range(n)]
|
|
6805
|
+
gr = [60.0 + 40.0 * np.sin(i / 20.0) for i in range(n)]
|
|
6806
|
+
cnt = [i % 7 for i in range(n)]
|
|
6807
|
+
p_gate = DL.write(str(d / "gate.dlis"), [("DEPT", "m", 7, depth), ("RHOB", "g/cm3", 2, rho), ("DTCO", "us/ft", 2, dt), ("GR", "gAPI", 2, gr), ("CNT", "", 15, cnt)], well="GATE 1", max_record=256)
|
|
6808
|
+
st = DL.read(p_gate)
|
|
6809
|
+
f = DL.DlisFile(Path(p_gate).read_bytes(), "gate"); info = f.describe()
|
|
6810
|
+
# the representation codes, each through a cursor
|
|
6811
|
+
c = DL._Cursor(struct.pack(">f", 1.5) + struct.pack(">d", -2.25) + struct.pack(">h", -300) + struct.pack(">i", -70000) + bytes([200]) + struct.pack(">H", 60000)
|
|
6812
|
+
+ struct.pack(">I", 4000000000) + bytes([0x7F]) + struct.pack(">H", 0x8000 | 300) + struct.pack(">I", 0xC0000000 | 70000)
|
|
6813
|
+
+ bytes([3]) + b"ABC" + bytes([4]) + b"TEXT" + bytes([126, 0x1A, 20, 22, 48, 50]) + struct.pack(">H", 250) + bytes([1, 0, 3]) + b"OBJ")
|
|
6814
|
+
vals = [c.value(2), c.value(7), c.value(13), c.value(14), c.value(15), c.value(16), c.value(17), c.uvari(), c.uvari(), c.uvari(), c.ident(), c.ascii(), c.value(21), c.obname()]
|
|
6815
|
+
fshort = DL._Cursor(struct.pack(">H", (0x7FF << 4) | 0)).value(1)
|
|
6816
|
+
# a second file: two frames, a time frame and a depth frame, a 2-D channel, an encrypted record planted
|
|
6817
|
+
raw = bytearray(Path(p_gate).read_bytes())
|
|
6818
|
+
# plant: flip the encryption bit of the first IFLR segment found (type 0, not EFLR)
|
|
6819
|
+
p = DL.SUL_LEN; planted = 0
|
|
6820
|
+
while p + 4 <= len(raw) and not planted:
|
|
6821
|
+
vlen = struct.unpack(">H", raw[p:p + 2])[0]; q = p + 4
|
|
6822
|
+
while q + 4 <= p + vlen:
|
|
6823
|
+
slen = struct.unpack(">H", raw[q:q + 2])[0]
|
|
6824
|
+
if not (raw[q + 2] & 0x80) and raw[q + 3] == 0 and not (raw[q + 2] & 0x60):
|
|
6825
|
+
raw[q + 2] |= 0x10; planted = 1; break
|
|
6826
|
+
q += slen
|
|
6827
|
+
p += vlen
|
|
6828
|
+
st_enc = DL.read(bytes(raw))
|
|
6829
|
+
not_dlis = None
|
|
6830
|
+
try:
|
|
6831
|
+
DL.read(b"~VERSION INFORMATION\n VERS. 2.0\n" + bytes(100))
|
|
6832
|
+
except DL.DlisError as e:
|
|
6833
|
+
not_dlis = str(e)
|
|
6834
|
+
two = str(d / "two.dlis")
|
|
6835
|
+
DL.write(two, [("TIME", "s", 7, [float(i) for i in range(50)]), ("P", "psi", 2, [3000.0 + i for i in range(50)])], frame_name="TFRAME", index_type="TIME")
|
|
6836
|
+
two_err = None
|
|
6837
|
+
try:
|
|
6838
|
+
DL.read(two + ".nope")
|
|
6839
|
+
except (DL.DlisError, OSError) as e:
|
|
6840
|
+
two_err = str(e)
|
|
6841
|
+
st_t = DL.read(two)
|
|
6842
|
+
c1 = {
|
|
6843
|
+
"round_trip": st.index_kind == "depth" and len(st.index) == n and list(st.channels) == ["RHOB", "DTCO", "GR", "CNT"] and st.channels["RHOB"].unit == "g/cm3"
|
|
6844
|
+
and np.isnan(st.channels["RHOB"].values[5]) and float(np.nanmax(np.abs(st.channels["RHOB"].values[6:] - np.array(rho[6:])))) < 1e-6
|
|
6845
|
+
and float(np.max(np.abs(st.index - np.array(depth)))) < 1e-9 and st.channels["CNT"].values[13] == 6.0,
|
|
6846
|
+
"layers": info["storage_unit"]["version"] == "V1.00" and info["storage_unit"]["max_record_length"] == 256 and info["origin"]["WELL-NAME"] == "GATE 1"
|
|
6847
|
+
and [t for t, _, _ in info["eflr_sets"]] == ["FILE-HEADER", "ORIGIN", "CHANNEL", "FRAME"] and info["frames"][0]["spacing"] is not None
|
|
6848
|
+
and abs(info["frames"][0]["spacing"] - 0.1524) < 1e-9 and info["n_records"] == 4 + n and st.meta["spacing"].startswith("0.1524 m"),
|
|
6849
|
+
"segmented": len(DL.segments(Path(p_gate).read_bytes())) > info["n_records"], # the CHANNEL and FRAME sets span segments at 256-byte records
|
|
6850
|
+
"codes": (abs(vals[0] - 1.5) < 1e-7 and vals[1] == -2.25 and vals[2] == -300 and vals[3] == -70000 and vals[4] == 200 and vals[5] == 60000 and vals[6] == 4000000000
|
|
6851
|
+
and vals[7:10] == [127, 300, 70000] and vals[10] == "ABC" and vals[11] == "TEXT" and vals[12].startswith("2026-10-20T22:48:50.250") and vals[13] == (1, 0, "OBJ")
|
|
6852
|
+
and abs(fshort - 2047.0) < 1e-9),
|
|
6853
|
+
"encrypted_skipped": planted == 1 and len(st_enc.index) == n - 1 and "encrypted records skipped: IFLR type 0 x1" in st_enc.meta["notes"],
|
|
6854
|
+
"refusals": not_dlis and "not a DLIS V1 storage unit label" in not_dlis and two_err and st_t.index_kind == "time_s" and st_t.meta["frame"] == "TFRAME",
|
|
6855
|
+
}
|
|
6856
|
+
failed1 = [k for k, v in c1.items() if not v]
|
|
6857
|
+
ok(not failed1,
|
|
6858
|
+
"BR1 DLIS read as specified: a file this program wrote round-trips (the index, every channel with its units, the null sentinel, an integer channel); "
|
|
6859
|
+
"the storage unit label, origin, sets and frame spacing described; a 1 KB visible record forces segmentation and the segments reassemble; the "
|
|
6860
|
+
"representation codes (FSINGL, FDOUBL, the integers, UVARI in all three lengths, IDENT, ASCII, DTIME, OBNAME, FSHORT) decode; a planted encrypted "
|
|
6861
|
+
"record is skipped and named; a LAS handed to the reader is refused by its label; a time-indexed frame is read as time"
|
|
6862
|
+
+ (f" [failed: {', '.join(failed1)}]" if failed1 else ""))
|
|
6863
|
+
|
|
6864
|
+
# -- BR2: PI Web API ------------------------------------------------------------------------------------------------
|
|
6865
|
+
sp = PI.selftest()
|
|
6866
|
+
sim = PI.SimServer(n_values=30, page=7); h, port = sim.serve()
|
|
6867
|
+
try:
|
|
6868
|
+
prt = PI.PiPort({**PI.EXAMPLE_CONFIG, "base_url": f"http://127.0.0.1:{port}/piwebapi", "auth": "none", "mode": "interpolated", "mapping": PI.EXAMPLE_CONFIG["mapping"][:1]})
|
|
6869
|
+
r = prt.pull()
|
|
6870
|
+
prt2 = PI.PiPort({**PI.EXAMPLE_CONFIG, "base_url": f"http://127.0.0.1:{port}/piwebapi", "auth": "none", "mode": "recorded", "mapping": PI.EXAMPLE_CONFIG["mapping"][:1]})
|
|
6871
|
+
r2 = prt2.pull()
|
|
6872
|
+
finally:
|
|
6873
|
+
h.close()
|
|
6874
|
+
bad_mode = None
|
|
6875
|
+
try:
|
|
6876
|
+
PI.PiPort({**PI.EXAMPLE_CONFIG, "mode": "latest"})
|
|
6877
|
+
except ValueError as e:
|
|
6878
|
+
bad_mode = str(e)
|
|
6879
|
+
c2 = {
|
|
6880
|
+
"selftest": sp["ok"],
|
|
6881
|
+
"interpolated": r["tags"]["DHP_psi"]["items"] == 6 and len(prt.records) == 6 and prt.records[1].timestamp_utc.startswith("2026-10-01T00:25:00"),
|
|
6882
|
+
"paged": r2["tags"]["DHP_psi"]["items"] == 30 and sum("startIndex=" in u for u in prt2.client.calls) == 4 and prt2.summary()["calls"] == 6,
|
|
6883
|
+
"bad_mode": bad_mode and "mode must be one of" in bad_mode,
|
|
6884
|
+
}
|
|
6885
|
+
failed2 = [k for k, v in c2.items() if not v]
|
|
6886
|
+
ok(not failed2,
|
|
6887
|
+
"BR2 PI Web API: the self-test (classification of Good, Questionable, Substituted, not Good, digital and system states; recorded values paged "
|
|
6888
|
+
"through Links.Next; a point the server lacks named with its HTTP status; a bar point recorded in psi; basic auth from the environment with a "
|
|
6889
|
+
"wrong password a 401; a credential over plain HTTP refused); an interpolated pull at the interval; a 30-value archive in 7-value pages fetched "
|
|
6890
|
+
"in 4 follow-ups; a mode the port does not offer refused"
|
|
6891
|
+
+ (f" [failed: {', '.join(failed2)}]" if failed2 else ""))
|
|
6892
|
+
|
|
6893
|
+
# -- BR3: the detector, the survey, the supervisor, the service, the CLI, the help ---------------------------------
|
|
6894
|
+
det = detect(p_gate); pv = preview(p_gate)
|
|
6895
|
+
st_any = read_any(p_gate)
|
|
6896
|
+
txt, machine = run_survey(path=p_gate)
|
|
6897
|
+
time_refused = None
|
|
6898
|
+
try:
|
|
6899
|
+
run_survey(path=two)
|
|
6900
|
+
except SystemExit as e:
|
|
6901
|
+
time_refused = str(e)
|
|
6902
|
+
hist = str(d / "hist.csv")
|
|
6903
|
+
TelemetryRecorder(engine=DownholeEngine(SimulatorConfig()), config=TelemetryConfig(duration_hours=0.5, seed=3)).run().export_csv(hist)
|
|
6904
|
+
wsp = str(d / "ws"); ws = Workspace.create(wsp, "Pad 3", actor="tester")
|
|
6905
|
+
w = ws.add_well_file(hist, display="Well H", actor="tester")
|
|
6906
|
+
w_dlis = ws.add_well_file(p_gate, display="Gate log", actor="tester")
|
|
6907
|
+
sup = PatchSupervisor(ws)
|
|
6908
|
+
sup.store.add("hist", "piwebapi", w["id"], {**PI.EXAMPLE_CONFIG, "base_url": "sim", "interval_s": 0.5, "mode": "recorded"}, "tester", priority=1, stale_after_s=30)
|
|
6909
|
+
bad_cfg = None
|
|
6910
|
+
try:
|
|
6911
|
+
validate_port_config("piwebapi", {"base_url": "sim", "mapping": [{"address": "\\\\X\\Y", "tag_id": "P"}]})
|
|
6912
|
+
except ValueError as e:
|
|
6913
|
+
bad_cfg = str(e)
|
|
6914
|
+
sup.start("hist", "tester")
|
|
6915
|
+
time.sleep(2.5)
|
|
6916
|
+
s1 = {x["name"]: x for x in sup.states()}["hist"]
|
|
6917
|
+
sup.stop("hist", "tester")
|
|
6918
|
+
users = Users(str(Path(wsp, "users.json"))); users.add("op", "operator-pass-1", "operator")
|
|
6919
|
+
svc = Service(wsp, port=0, scheduler=False).start()
|
|
6920
|
+
base_url = svc.url.rstrip("/")
|
|
6921
|
+
opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(http.cookiejar.CookieJar()))
|
|
6922
|
+
|
|
6923
|
+
def call(method, path, body=None):
|
|
6924
|
+
req = urllib.request.Request(base_url + path, method=method, data=(json.dumps(body).encode() if body is not None else None))
|
|
6925
|
+
req.add_header("Content-Type", "application/json"); req.add_header("X-GEA-Action", "1")
|
|
6926
|
+
try:
|
|
6927
|
+
with opener.open(req, timeout=120) as rr_:
|
|
6928
|
+
return rr_.status, json.loads(rr_.read())
|
|
6929
|
+
except urllib.error.HTTPError as e:
|
|
6930
|
+
return e.code, json.loads(e.read())
|
|
6931
|
+
try:
|
|
6932
|
+
call("POST", "/api/login", {"name": "op", "password": "operator-pass-1"})
|
|
6933
|
+
st_p, pl = call("GET", "/api/patches")
|
|
6934
|
+
st_x, ex = call("GET", "/api/config/defaults/piwebapi")
|
|
6935
|
+
finally:
|
|
6936
|
+
svc.stop()
|
|
6937
|
+
page_src = (pkg / "web" / "app.html").read_text(encoding="utf-8")
|
|
6938
|
+
r_info = _sp.run([sys.executable, "-m", "gea", "dlis", "--file", p_gate, "--info"], capture_output=True, text=True)
|
|
6939
|
+
r_las = _sp.run([sys.executable, "-m", "gea", "dlis", "--file", p_gate, "--to-las", str(d / "gate.las"), "--out", str(d / "gate.csv")], capture_output=True, text=True)
|
|
6940
|
+
r_sv = _sp.run([sys.executable, "-m", "gea", "survey", "--file", str(d / "gate.las")], capture_output=True, text=True)
|
|
6941
|
+
pi_cfg = str(d / "pi.json")
|
|
6942
|
+
with open(pi_cfg, "w", encoding="utf-8") as fcfg:
|
|
6943
|
+
json.dump({**PI.EXAMPLE_CONFIG, "base_url": "sim"}, fcfg)
|
|
6944
|
+
r_pi = _sp.run([sys.executable, "-m", "gea", "piwebapi", "--config", pi_cfg, "--out", str(d / "pi.csv"), "--stream-csv", str(d / "pi_stream.csv")], capture_output=True, text=True)
|
|
6945
|
+
r_pis = _sp.run([sys.executable, "-m", "gea", "piwebapi", "--selftest"], capture_output=True, text=True)
|
|
6946
|
+
c3 = {
|
|
6947
|
+
"detected": det["kind"] == "dlis" and "gea gate file" in det["detail"] and pv["lines"] and "GATE 1 / GATE by gea-program" in pv["lines"][0] and st_any.source_format == "dlis",
|
|
6948
|
+
"survey": "read by the dlis port" in txt and machine["channels_found"].get("density") == "RHOB" and machine["n_stations"] == n
|
|
6949
|
+
and time_refused and "not depth" in time_refused and "TFRAME" not in time_refused,
|
|
6950
|
+
"imported": w_dlis["kind"] == "file" and os.path.isfile(Path(wsp, "wells", w_dlis["id"], "source", "gate.dlis")),
|
|
6951
|
+
"supervised": "piwebapi" in PROTOCOLS and bad_cfg and "source" in bad_cfg and s1["status"] in ("CONNECTED", "STOPPED") and s1["samples_total"] >= 500
|
|
6952
|
+
and abs(s1["tags"]["DHP_psi"]["value"] - (3185.5 + 0.01 * 249)) < 1e-6,
|
|
6953
|
+
"service": st_p == 200 and "piwebapi" in pl["protocols"] and st_x == 200 and ex["content"]["auth"] == "basic" and 'value="piwebapi"' in page_src,
|
|
6954
|
+
"cli": r_info.returncode == 0 and "well: GATE 1" in r_info.stdout and "frame FRAME1: index BOREHOLE-DEPTH INCREASING" in r_info.stdout
|
|
6955
|
+
and r_las.returncode == 0 and os.path.isfile(d / "gate.las") and r_sv.returncode == 0 and "density -> RHOB" in r_sv.stdout
|
|
6956
|
+
and r_pi.returncode == 0 and '"records": 750' in r_pi.stdout and os.path.isfile(d / "pi_stream.csv") and r_pis.returncode == 0,
|
|
6957
|
+
"help": all(any(t["topic"] == x for t in H.topics()) and not H.check(x) for x in ("dlis", "historian"))
|
|
6958
|
+
and "does not decrypt" in (pkg / "help" / "dlis.md").read_text(encoding="utf-8").split("will not call a measurement")[1]
|
|
6959
|
+
and "no code path that writes a value" in (pkg / "help" / "historian.md").read_text(encoding="utf-8").split("will not call a measurement")[1],
|
|
6960
|
+
}
|
|
6961
|
+
failed3 = [k for k, v in c3.items() if not v]
|
|
6962
|
+
ok(not failed3,
|
|
6963
|
+
"BR3 a DLIS detected by its label, previewed and read like a LAS; the strata survey runs on it and refuses a time-indexed frame by name; "
|
|
6964
|
+
"imported as a well; a PI pull supervised on the Patch panel with the window moving (a config without its source refused); the service "
|
|
6965
|
+
"lists the protocol and serves the example; `gea dlis --info`, `--to-las` then `gea survey` on the LAS, `gea piwebapi` against the stand-in; the help pages"
|
|
6966
|
+
+ (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
|
|
6967
|
+
|
|
6968
|
+
|
|
6777
6969
|
def main() -> int:
|
|
6778
6970
|
print("GEA-Program - ACCEPTANCE SUITE (the product gate)")
|
|
6779
6971
|
# a handle a job subprocess still holds at the end must not turn a finished gate into a traceback on Windows:
|
|
@@ -6846,6 +7038,7 @@ def main() -> int:
|
|
|
6846
7038
|
section_bo_structure(tmp)
|
|
6847
7039
|
section_bp_interval(tmp)
|
|
6848
7040
|
section_bq_field_ports(tmp)
|
|
7041
|
+
section_br_logs_historian(tmp)
|
|
6849
7042
|
if _FAILS:
|
|
6850
7043
|
print(f"[ACCEPTANCE] {len(_FAILS)} FAILURES ({_PASS} passed):")
|
|
6851
7044
|
for f in _FAILS:
|