3 Commits
v2 ... v3

Author SHA1 Message Date
44609d367f Update to version 3.2 2026-03-18 15:18:05 +01:00
f593487c77 [cmake] Use CAPS logging by default 2026-03-18 14:56:01 +01:00
ed6649e947 [cmake] Force the usage of C++17
- Set minimum CMake version to 3.10
2026-03-18 14:55:20 +01:00
50 changed files with 12661 additions and 3671 deletions

View File

@@ -1,8 +1,8 @@
PROJECT(LIBCAPS)
CMAKE_MINIMUM_REQUIRED(VERSION 2.4)
CMAKE_MINIMUM_REQUIRED(VERSION 3.10 FATAL_ERROR)
IF(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "4.7")
ADD_DEFINITIONS(-std=c++11 -DBOOST_NO_CXX11_SCOPED_ENUM -DBOOST_NO_SCOPED_ENUMS)
IF(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "7.0")
ADD_DEFINITIONS(-std=c++17 -DBOOST_NO_CXX11_SCOPED_ENUM -DBOOST_NO_SCOPED_ENUMS)
ELSE()
MESSAGE(ERROR "The CAPS client library requires gcc version 4.7 or higher")
ENDIF()
@@ -21,6 +21,7 @@ INCLUDE_DIRECTORIES(libs)
#ADD_DEFINITIONS("-DCAPS_FEATURES_RTCM2=0")
#ADD_DEFINITIONS("-DCAPS_FEATURES_BACKFILLING=0")
#ADD_DEFINITIONS("-DCAPS_FEATURES_JOURNAL=0")
ADD_DEFINITIONS("-DCAPS_SC_LOGGING=0")
OPTION(LIBCAPS_PYTHON_WRAPPER "Create Python wrappers" ON)
OPTION(LIBCAPS_EXAMPLES "Build and install example applications" OFF)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,661 @@
/***************************************************************************
* Documentation and helpers for miniSEED structures.
*
* This file is part of the miniSEED Library.
*
* Copyright (c) 2024 Chad Trabant, EarthScope Data Services
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
***************************************************************************/
#ifndef MSEEDFORMAT_H
#define MSEEDFORMAT_H 1
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include "libmseed.h"
/* Length of Fixed Section of Data Header for miniSEED v3 */
#define MS3FSDH_LENGTH 40
/***************************************************************************
* miniSEED 3.0 Fixed Section of Data Header
* 40 bytes, plus length of identifier, plus length of extra headers
*
* # FIELD TYPE OFFSET
* 1 record indicator char[2] 0
* 2 format version uint8_t 2
* 3 flags uint8_t 3
* 4a nanosecond uint32_t 4
* 4b year uint16_t 8
* 4c day uint16_t 10
* 4d hour uint8_t 12
* 4e min uint8_t 13
* 4f sec uint8_t 14
* 5 data encoding uint8_t 15
* 6 sample rate/period float64 16
* 7 number of samples uint32_t 24
* 8 CRC of record uint32_t 28
* 9 publication version uint8_t 32
* 10 length of identifer uint8_t 33
* 11 length of extra headers uint16_t 34
* 12 length of data payload uint32_t 36
* 13 source identifier char 40
* 14 extra headers char 40 + field 10
* 15 data payload encoded 40 + field 10 + field 11
*
* Convenience macros for accessing the fields via typed pointers follow:
***************************************************************************/
#define pMS3FSDH_INDICATOR(record) ((char*)record)
#define pMS3FSDH_FORMATVERSION(record) ((uint8_t*)((uint8_t*)record+2))
#define pMS3FSDH_FLAGS(record) ((uint8_t*)((uint8_t*)record+3))
#define pMS3FSDH_NSEC(record) ((uint32_t*)((uint8_t*)record+4))
#define pMS3FSDH_YEAR(record) ((uint16_t*)((uint8_t*)record+8))
#define pMS3FSDH_DAY(record) ((uint16_t*)((uint8_t*)record+10))
#define pMS3FSDH_HOUR(record) ((uint8_t*)((uint8_t*)record+12))
#define pMS3FSDH_MIN(record) ((uint8_t*)((uint8_t*)record+13))
#define pMS3FSDH_SEC(record) ((uint8_t*)((uint8_t*)record+14))
#define pMS3FSDH_ENCODING(record) ((uint8_t*)((uint8_t*)record+15))
#define pMS3FSDH_SAMPLERATE(record) ((double*)((uint8_t*)record+16))
#define pMS3FSDH_NUMSAMPLES(record) ((uint32_t*)((uint8_t*)record+24))
#define pMS3FSDH_CRC(record) ((uint32_t*)((uint8_t*)record+28))
#define pMS3FSDH_PUBVERSION(record) ((uint8_t*)((uint8_t*)record+32))
#define pMS3FSDH_SIDLENGTH(record) ((uint8_t*)((uint8_t*)record+33))
#define pMS3FSDH_EXTRALENGTH(record) ((uint16_t*)((uint8_t*)record+34))
#define pMS3FSDH_DATALENGTH(record) ((uint32_t*)((uint8_t*)record+36))
#define pMS3FSDH_SID(record) ((char*)((uint8_t*)record+40))
/* Length of Fixed Section of Data Header for miniSEED v2 */
#define MS2FSDH_LENGTH 48
/***************************************************************************
* miniSEED 2.4 Fixed Section of Data Header
* 48 bytes total
*
* FIELD TYPE OFFSET
* sequence_number char[6] 0
* dataquality char 6
* reserved char 7
* station char[5] 8
* location char[2] 13
* channel char[3] 15
* network char[2] 18
* year uint16_t 20
* day uint16_t 22
* hour uint8_t 24
* min uint8_t 25
* sec uint8_t 26
* unused uint8_t 27
* fract uint16_t 28
* numsamples uint16_t 30
* samprate_fact int16_t 32
* samprate_mult int16_t 34
* act_flags uint8_t 36
* io_flags uint8_t 37
* dq_flags uint8_t 38
* numblockettes uint8_t 39
* time_correct int32_t 40
* data_offset uint16_t 44
* blockette_offset uint16_t 46
*
* Convenience macros for accessing the fields via typed pointers follow:
***************************************************************************/
#define pMS2FSDH_SEQNUM(record) ((char*)record)
#define pMS2FSDH_DATAQUALITY(record) ((char*)((uint8_t*)record+6))
#define pMS2FSDH_RESERVED(record) ((char*)((uint8_t*)record+7))
#define pMS2FSDH_STATION(record) ((char*)((uint8_t*)record+8))
#define pMS2FSDH_LOCATION(record) ((char*)((uint8_t*)record+13))
#define pMS2FSDH_CHANNEL(record) ((char*)((uint8_t*)record+15))
#define pMS2FSDH_NETWORK(record) ((char*)((uint8_t*)record+18))
#define pMS2FSDH_YEAR(record) ((uint16_t*)((uint8_t*)record+20))
#define pMS2FSDH_DAY(record) ((uint16_t*)((uint8_t*)record+22))
#define pMS2FSDH_HOUR(record) ((uint8_t*)((uint8_t*)record+24))
#define pMS2FSDH_MIN(record) ((uint8_t*)((uint8_t*)record+25))
#define pMS2FSDH_SEC(record) ((uint8_t*)((uint8_t*)record+26))
#define pMS2FSDH_UNUSED(record) ((uint8_t*)((uint8_t*)record+27))
#define pMS2FSDH_FSEC(record) ((uint16_t*)((uint8_t*)record+28))
#define pMS2FSDH_NUMSAMPLES(record) ((uint16_t*)((uint8_t*)record+30))
#define pMS2FSDH_SAMPLERATEFACT(record) ((int16_t*)((uint8_t*)record+32))
#define pMS2FSDH_SAMPLERATEMULT(record) ((int16_t*)((uint8_t*)record+34))
#define pMS2FSDH_ACTFLAGS(record) ((uint8_t*)((uint8_t*)record+36))
#define pMS2FSDH_IOFLAGS(record) ((uint8_t*)((uint8_t*)record+37))
#define pMS2FSDH_DQFLAGS(record) ((uint8_t*)((uint8_t*)record+38))
#define pMS2FSDH_NUMBLOCKETTES(record) ((uint8_t*)((uint8_t*)record+39))
#define pMS2FSDH_TIMECORRECT(record) ((int32_t*)((uint8_t*)record+40))
#define pMS2FSDH_DATAOFFSET(record) ((uint16_t*)((uint8_t*)record+44))
#define pMS2FSDH_BLOCKETTEOFFSET(record) ((uint16_t*)((uint8_t*)record+46))
/***************************************************************************
* miniSEED 2.4 Blockette 100 - sample rate
*
* FIELD TYPE OFFSET
* type uint16_t 0
* next offset uint16_t 2
* samprate float 4
* flags uint8_t 8
* reserved uint8_t[3] 9
*
* Convenience macros for accessing the fields via typed pointers follow:
***************************************************************************/
#define pMS2B100_TYPE(blockette) ((uint16_t*)(blockette))
#define pMS2B100_NEXT(blockette) ((uint16_t*)((uint8_t*)blockette+2))
#define pMS2B100_SAMPRATE(blockette) ((float*)((uint8_t*)blockette+4))
#define pMS2B100_FLAGS(blockette) ((uint8_t*)((uint8_t*)blockette+8))
#define pMS2B100_RESERVED(blockette) ((uint8_t*)((uint8_t*)blockette+9))
/***************************************************************************
* miniSEED 2.4 Blockette 200 - generic event detection
*
* FIELD TYPE OFFSET
* type uint16_t 0
* next offset uint16_t 2
* amplitude float 4
* period float 8
* background_est float 12
* flags uint8_t 16
* reserved uint8_t 17
* year uint16_t 18
* day uint16_t 20
* hour uint8_t 22
* min uint8_t 23
* sec uint8_t 24
* unused uint8_t 25
* fract uint16_t 26
* detector char[24] 28
*
* Convenience macros for accessing the fields via typed pointers follow:
***************************************************************************/
#define pMS2B200_TYPE(blockette) ((uint16_t*)(blockette))
#define pMS2B200_NEXT(blockette) ((uint16_t*)((uint8_t*)blockette+2))
#define pMS2B200_AMPLITUDE(blockette) ((float*)((uint8_t*)blockette+4))
#define pMS2B200_PERIOD(blockette) ((float*)((uint8_t*)blockette+8))
#define pMS2B200_BACKGROUNDEST(blockette) ((float*)((uint8_t*)blockette+12))
#define pMS2B200_FLAGS(blockette) ((uint8_t*)((uint8_t*)blockette+16))
#define pMS2B200_RESERVED(blockette) ((uint8_t*)((uint8_t*)blockette+17))
#define pMS2B200_YEAR(blockette) ((uint16_t*)((uint8_t*)blockette+18))
#define pMS2B200_DAY(blockette) ((uint16_t*)((uint8_t*)blockette+20))
#define pMS2B200_HOUR(blockette) ((uint8_t*)((uint8_t*)blockette+22))
#define pMS2B200_MIN(blockette) ((uint8_t*)((uint8_t*)blockette+23))
#define pMS2B200_SEC(blockette) ((uint8_t*)((uint8_t*)blockette+24))
#define pMS2B200_UNUSED(blockette) ((uint8_t*)((uint8_t*)blockette+25))
#define pMS2B200_FSEC(blockette) ((uint16_t*)((uint8_t*)blockette+26))
#define pMS2B200_DETECTOR(blockette) ((char*)((uint8_t*)blockette+28))
/***************************************************************************
* miniSEED 2.4 Blockette 201 - Murdock event detection
*
* FIELD TYPE OFFSET
* type uint16_t 0
* next offset uint16_t 2
* amplitude float 4
* period float 8
* background_est float 12
* flags uint8_t 16
* reserved uint8_t 17
* year uint16_t 18
* day uint16_t 20
* hour uint8_t 22
* min uint8_t 23
* sec uint8_t 24
* unused uint8_t 25
* fract uint16_t 26
* snr_values uint8_t[6] 28
* loopback uint8_t 34
* pick_algorithm uint8_t 35
* detector char[24] 36
*
* Convenience macros for accessing the fields via typed pointers follow:
***************************************************************************/
#define pMS2B201_TYPE(blockette) ((uint16_t*)(blockette))
#define pMS2B201_NEXT(blockette) ((uint16_t*)((uint8_t*)blockette+2))
#define pMS2B201_AMPLITUDE(blockette) ((float*)((uint8_t*)blockette+4))
#define pMS2B201_PERIOD(blockette) ((float*)((uint8_t*)blockette+8))
#define pMS2B201_BACKGROUNDEST(blockette) ((float*)((uint8_t*)blockette+12))
#define pMS2B201_FLAGS(blockette) ((uint8_t*)((uint8_t*)blockette+16))
#define pMS2B201_RESERVED(blockette) ((uint8_t*)((uint8_t*)blockette+17))
#define pMS2B201_YEAR(blockette) ((uint16_t*)((uint8_t*)blockette+18))
#define pMS2B201_DAY(blockette) ((uint16_t*)((uint8_t*)blockette+20))
#define pMS2B201_HOUR(blockette) ((uint8_t*)((uint8_t*)blockette+22))
#define pMS2B201_MIN(blockette) ((uint8_t*)((uint8_t*)blockette+23))
#define pMS2B201_SEC(blockette) ((uint8_t*)((uint8_t*)blockette+24))
#define pMS2B201_UNUSED(blockette) ((uint8_t*)((uint8_t*)blockette+25))
#define pMS2B201_FSEC(blockette) ((uint16_t*)((uint8_t*)blockette+26))
#define pMS2B201_MEDSNR(blockette) ((uint8_t*)((uint8_t*)blockette+28))
#define pMS2B201_LOOPBACK(blockette) ((uint8_t*)((uint8_t*)blockette+34))
#define pMS2B201_PICKALGORITHM(blockette) ((uint8_t*)((uint8_t*)blockette+35))
#define pMS2B201_DETECTOR(blockette) ((char*)((uint8_t*)blockette+36))
/***************************************************************************
* miniSEED 2.4 Blockette 300 - step calibration
*
* FIELD TYPE OFFSET
* type uint16_t 0
* next offset uint16_t 2
* year uint16_t 4
* day uint16_t 6
* hour uint8_t 8
* min uint8_t 9
* sec uint8_t 10
* unused uint8_t 11
* fract uint16_t 12
* num calibrations uint8_t 14
* flags uint8_t 15
* step duration uint32_t 16
* interval duration uint32_t 20
* amplitude float 24
* input channel char[3] 28
* reserved uint8_t 31
* reference amplitude uint32_t 32
* coupling char[12] 36
* rolloff char[12] 48
*
* Convenience macros for accessing the fields via typed pointers follow:
***************************************************************************/
#define pMS2B300_TYPE(blockette) ((uint16_t*)(blockette))
#define pMS2B300_NEXT(blockette) ((uint16_t*)((uint8_t*)blockette+2))
#define pMS2B300_YEAR(blockette) ((uint16_t*)((uint8_t*)blockette+4))
#define pMS2B300_DAY(blockette) ((uint16_t*)((uint8_t*)blockette+6))
#define pMS2B300_HOUR(blockette) ((uint8_t*)((uint8_t*)blockette+8))
#define pMS2B300_MIN(blockette) ((uint8_t*)((uint8_t*)blockette+9))
#define pMS2B300_SEC(blockette) ((uint8_t*)((uint8_t*)blockette+10))
#define pMS2B300_UNUSED(blockette) ((uint8_t*)((uint8_t*)blockette+11))
#define pMS2B300_FSEC(blockette) ((uint16_t*)((uint8_t*)blockette+12))
#define pMS2B300_NUMCALIBRATIONS(blockette) ((uint8_t*)((uint8_t*)blockette+14))
#define pMS2B300_FLAGS(blockette) ((uint8_t*)((uint8_t*)blockette+15))
#define pMS2B300_STEPDURATION(blockette) ((uint32_t*)((uint8_t*)blockette+16))
#define pMS2B300_INTERVALDURATION(blockette) ((uint32_t*)((uint8_t*)blockette+20))
#define pMS2B300_AMPLITUDE(blockette) ((float*)((uint8_t*)blockette+24))
#define pMS2B300_INPUTCHANNEL(blockette) ((char *)((uint8_t*)blockette+28))
#define pMS2B300_RESERVED(blockette) ((uint8_t*)((uint8_t*)blockette+31))
#define pMS2B300_REFERENCEAMPLITUDE(blockette) ((uint32_t*)((uint8_t*)blockette+32))
#define pMS2B300_COUPLING(blockette) ((char*)((uint8_t*)blockette+36))
#define pMS2B300_ROLLOFF(blockette) ((char*)((uint8_t*)blockette+48))
/***************************************************************************
* miniSEED 2.4 Blockette 310 - sine calibration
*
* FIELD TYPE OFFSET
* type uint16_t 0
* next offset uint16_t 2
* year uint16_t 4
* day uint16_t 6
* hour uint8_t 8
* min uint8_t 9
* sec uint8_t 10
* unused uint8_t 11
* fract uint16_t 12
* reserved1 uint8_t 14
* flags uint8_t 15
* duration uint32_t 16
* period float 20
* amplitude float 24
* input channel char[3] 28
* reserved2 uint8_t 31
* reference amplitude uint32_t 32
* coupling char[12] 36
* rolloff char[12] 48
*
* Convenience macros for accessing the fields via typed pointers follow:
***************************************************************************/
#define pMS2B310_TYPE(blockette) ((uint16_t*)(blockette))
#define pMS2B310_NEXT(blockette) ((uint16_t*)((uint8_t*)blockette+2))
#define pMS2B310_YEAR(blockette) ((uint16_t*)((uint8_t*)blockette+4))
#define pMS2B310_DAY(blockette) ((uint16_t*)((uint8_t*)blockette+6))
#define pMS2B310_HOUR(blockette) ((uint8_t*)((uint8_t*)blockette+8))
#define pMS2B310_MIN(blockette) ((uint8_t*)((uint8_t*)blockette+9))
#define pMS2B310_SEC(blockette) ((uint8_t*)((uint8_t*)blockette+10))
#define pMS2B310_UNUSED(blockette) ((uint8_t*)((uint8_t*)blockette+11))
#define pMS2B310_FSEC(blockette) ((uint16_t*)((uint8_t*)blockette+12))
#define pMS2B310_RESERVED1(blockette) ((uint8_t*)((uint8_t*)blockette+14))
#define pMS2B310_FLAGS(blockette) ((uint8_t*)((uint8_t*)blockette+15))
#define pMS2B310_DURATION(blockette) ((uint32_t*)((uint8_t*)blockette+16))
#define pMS2B310_PERIOD(blockette) ((float*)((uint8_t*)blockette+20))
#define pMS2B310_AMPLITUDE(blockette) ((float*)((uint8_t*)blockette+24))
#define pMS2B310_INPUTCHANNEL(blockette) ((char *)((uint8_t*)blockette+28))
#define pMS2B310_RESERVED2(blockette) ((uint8_t*)((uint8_t*)blockette+31))
#define pMS2B310_REFERENCEAMPLITUDE(blockette) ((uint32_t*)((uint8_t*)blockette+32))
#define pMS2B310_COUPLING(blockette) ((char*)((uint8_t*)blockette+36))
#define pMS2B310_ROLLOFF(blockette) ((char*)((uint8_t*)blockette+48))
/***************************************************************************
* miniSEED 2.4 Blockette 320 - pseudo-random calibration
*
* FIELD TYPE OFFSET
* type uint16_t 0
* next offset uint16_t 2
* year uint16_t 4
* day uint16_t 6
* hour uint8_t 8
* min uint8_t 9
* sec uint8_t 10
* unused uint8_t 11
* fract uint16_t 12
* reserved1 uint8_t 14
* flags uint8_t 15
* duration uint32_t 16
* PtP amplitude float 20
* input channel char[3] 24
* reserved2 uint8_t 27
* reference amplitude uint32_t 28
* coupling char[12] 32
* rolloff char[12] 44
* noise type char[8] 56
*
* Convenience macros for accessing the fields via typed pointers follow:
***************************************************************************/
#define pMS2B320_TYPE(blockette) ((uint16_t*)(blockette))
#define pMS2B320_NEXT(blockette) ((uint16_t*)((uint8_t*)blockette+2))
#define pMS2B320_YEAR(blockette) ((uint16_t*)((uint8_t*)blockette+4))
#define pMS2B320_DAY(blockette) ((uint16_t*)((uint8_t*)blockette+6))
#define pMS2B320_HOUR(blockette) ((uint8_t*)((uint8_t*)blockette+8))
#define pMS2B320_MIN(blockette) ((uint8_t*)((uint8_t*)blockette+9))
#define pMS2B320_SEC(blockette) ((uint8_t*)((uint8_t*)blockette+10))
#define pMS2B320_UNUSED(blockette) ((uint8_t*)((uint8_t*)blockette+11))
#define pMS2B320_FSEC(blockette) ((uint16_t*)((uint8_t*)blockette+12))
#define pMS2B320_RESERVED1(blockette) ((uint8_t*)((uint8_t*)blockette+14))
#define pMS2B320_FLAGS(blockette) ((uint8_t*)((uint8_t*)blockette+15))
#define pMS2B320_DURATION(blockette) ((uint32_t*)((uint8_t*)blockette+16))
#define pMS2B320_PTPAMPLITUDE(blockette) ((float*)((uint8_t*)blockette+20))
#define pMS2B320_INPUTCHANNEL(blockette) ((char *)((uint8_t*)blockette+24))
#define pMS2B320_RESERVED2(blockette) ((uint8_t*)((uint8_t*)blockette+27))
#define pMS2B320_REFERENCEAMPLITUDE(blockette) ((uint32_t*)((uint8_t*)blockette+28))
#define pMS2B320_COUPLING(blockette) ((char*)((uint8_t*)blockette+32))
#define pMS2B320_ROLLOFF(blockette) ((char*)((uint8_t*)blockette+44))
#define pMS2B320_NOISETYPE(blockette) ((char*)((uint8_t*)blockette+56))
/***************************************************************************
* miniSEED 2.4 Blockette 390 - generic calibration
*
* FIELD TYPE OFFSET
* type uint16_t 0
* next offset uint16_t 2
* year uint16_t 4
* day uint16_t 6
* hour uint8_t 8
* min uint8_t 9
* sec uint8_t 10
* unused uint8_t 11
* fract uint16_t 12
* reserved1 uint8_t 14
* flags uint8_t 15
* duration uint32_t 16
* amplitude float 20
* input channel char[3] 24
* reserved2 uint8_t 27
*
* Convenience macros for accessing the fields via typed pointers follow:
***************************************************************************/
#define pMS2B390_TYPE(blockette) ((uint16_t*)(blockette))
#define pMS2B390_NEXT(blockette) ((uint16_t*)((uint8_t*)blockette+2))
#define pMS2B390_YEAR(blockette) ((uint16_t*)((uint8_t*)blockette+4))
#define pMS2B390_DAY(blockette) ((uint16_t*)((uint8_t*)blockette+6))
#define pMS2B390_HOUR(blockette) ((uint8_t*)((uint8_t*)blockette+8))
#define pMS2B390_MIN(blockette) ((uint8_t*)((uint8_t*)blockette+9))
#define pMS2B390_SEC(blockette) ((uint8_t*)((uint8_t*)blockette+10))
#define pMS2B390_UNUSED(blockette) ((uint8_t*)((uint8_t*)blockette+11))
#define pMS2B390_FSEC(blockette) ((uint16_t*)((uint8_t*)blockette+12))
#define pMS2B390_RESERVED1(blockette) ((uint8_t*)((uint8_t*)blockette+14))
#define pMS2B390_FLAGS(blockette) ((uint8_t*)((uint8_t*)blockette+15))
#define pMS2B390_DURATION(blockette) ((uint32_t*)((uint8_t*)blockette+16))
#define pMS2B390_AMPLITUDE(blockette) ((float*)((uint8_t*)blockette+20))
#define pMS2B390_INPUTCHANNEL(blockette) ((char *)((uint8_t*)blockette+24))
#define pMS2B390_RESERVED2(blockette) ((uint8_t*)((uint8_t*)blockette+27))
/***************************************************************************
* miniSEED 2.4 Blockette 395 - calibration abort
*
* FIELD TYPE OFFSET
* type uint16_t 0
* next offset uint16_t 2
* year uint16_t 4
* day uint16_t 6
* hour uint8_t 8
* min uint8_t 9
* sec uint8_t 10
* unused uint8_t 11
* fract uint16_t 12
* reserved uint8_t[2] 14
*
* Convenience macros for accessing the fields via typed pointers follow:
***************************************************************************/
#define pMS2B395_TYPE(blockette) ((uint16_t*)(blockette))
#define pMS2B395_NEXT(blockette) ((uint16_t*)((uint8_t*)blockette+2))
#define pMS2B395_YEAR(blockette) ((uint16_t*)((uint8_t*)blockette+4))
#define pMS2B395_DAY(blockette) ((uint16_t*)((uint8_t*)blockette+6))
#define pMS2B395_HOUR(blockette) ((uint8_t*)((uint8_t*)blockette+8))
#define pMS2B395_MIN(blockette) ((uint8_t*)((uint8_t*)blockette+9))
#define pMS2B395_SEC(blockette) ((uint8_t*)((uint8_t*)blockette+10))
#define pMS2B395_UNUSED(blockette) ((uint8_t*)((uint8_t*)blockette+11))
#define pMS2B395_FSEC(blockette) ((uint16_t*)((uint8_t*)blockette+12))
#define pMS2B395_RESERVED(blockette) ((uint8_t*)((uint8_t*)blockette+14))
/***************************************************************************
* miniSEED 2.4 Blockette 400 - beam
*
* FIELD TYPE OFFSET
* type uint16_t 0
* next offset uint16_t 2
* azimuth float 4
* slowness float 8
* configuration uint16_t 12
* reserved uint8_t[2] 14
*
* Convenience macros for accessing the fields via typed pointers follow:
***************************************************************************/
#define pMS2B400_TYPE(blockette) ((uint16_t*)(blockette))
#define pMS2B400_NEXT(blockette) ((uint16_t*)((uint8_t*)blockette+2))
#define pMS2B400_AZIMUTH(blockette) ((float*)((uint8_t*)blockette+4))
#define pMS2B400_SLOWNESS(blockette) ((float*)((uint8_t*)blockette+8))
#define pMS2B400_CONFIGURATION(blockette) ((uint16_t*)((uint8_t*)blockette+12))
#define pMS2B400_RESERVED(blockette) ((uint8_t*)((uint8_t*)blockette+14))
/***************************************************************************
* miniSEED 2.4 Blockette 405 - beam delay
*
* FIELD TYPE OFFSET
* type uint16_t 0
* next offset uint16_t 2
* delay values uint16_t[1] 4
*
* Convenience macros for accessing the fields via typed pointers follow:
***************************************************************************/
#define pMS2B405_TYPE(blockette) ((uint16_t*)(blockette))
#define pMS2B405_NEXT(blockette) ((uint16_t*)((uint8_t*)blockette+2))
#define pMS2B405_DELAYVALUES(blockette) ((uint16_t*)((uint8_t*)blockette+4))
/***************************************************************************
* miniSEED 2.4 Blockette 500 - timing
*
* FIELD TYPE OFFSET
* type uint16_t 0
* next offset uint16_t 2
* VCO correction float 4
* year uint16_t 8
* day uint16_t 10
* hour uint8_t 12
* min uint8_t 13
* sec uint8_t 14
* unused uint8_t 15
* fract uint16_t 16
* microsecond int8_t 18
* reception quality uint8_t 19
* exception count uint32_t 20
* exception type char[16] 24
* clock model char[32] 40
* clock status char[128] 72
*
* Convenience macros for accessing the fields via typed pointers follow:
***************************************************************************/
#define pMS2B500_TYPE(blockette) ((uint16_t*)(blockette))
#define pMS2B500_NEXT(blockette) ((uint16_t*)((uint8_t*)blockette+2))
#define pMS2B500_VCOCORRECTION(blockette) ((float*)((uint8_t*)blockette+4))
#define pMS2B500_YEAR(blockette) ((uint16_t*)((uint8_t*)blockette+8))
#define pMS2B500_DAY(blockette) ((uint16_t*)((uint8_t*)blockette+10))
#define pMS2B500_HOUR(blockette) ((uint8_t*)((uint8_t*)blockette+12))
#define pMS2B500_MIN(blockette) ((uint8_t*)((uint8_t*)blockette+13))
#define pMS2B500_SEC(blockette) ((uint8_t*)((uint8_t*)blockette+14))
#define pMS2B500_UNUSED(blockette) ((uint8_t*)((uint8_t*)blockette+15))
#define pMS2B500_FSEC(blockette) ((uint16_t*)((uint8_t*)blockette+16))
#define pMS2B500_MICROSECOND(blockette) ((int8_t*)((uint8_t*)blockette+18))
#define pMS2B500_RECEPTIONQUALITY(blockette) ((uint8_t*)((uint8_t*)blockette+19))
#define pMS2B500_EXCEPTIONCOUNT(blockette) ((uint32_t*)((uint8_t*)blockette+20))
#define pMS2B500_EXCEPTIONTYPE(blockette) ((char*)((uint8_t*)blockette+24))
#define pMS2B500_CLOCKMODEL(blockette) ((char*)((uint8_t*)blockette+40))
#define pMS2B500_CLOCKSTATUS(blockette) ((char*)((uint8_t*)blockette+72))
/***************************************************************************
* miniSEED 2.4 Blockette 1000 - data only SEED (miniSEED)
*
* FIELD TYPE OFFSET
* type uint16_t 0
* next offset uint16_t 2
* encoding uint8_t 4
* byteorder uint8_t 5
* reclen uint8_t 6
* reserved uint8_t 7
*
* Convenience macros for accessing the fields via typed pointers follow:
***************************************************************************/
#define pMS2B1000_TYPE(blockette) ((uint16_t*)(blockette))
#define pMS2B1000_NEXT(blockette) ((uint16_t*)((uint8_t*)blockette+2))
#define pMS2B1000_ENCODING(blockette) ((uint8_t*)((uint8_t*)blockette+4))
#define pMS2B1000_BYTEORDER(blockette) ((uint8_t*)((uint8_t*)blockette+5))
#define pMS2B1000_RECLEN(blockette) ((uint8_t*)((uint8_t*)blockette+6))
#define pMS2B1000_RESERVED(blockette) ((uint8_t*)((uint8_t*)blockette+7))
/***************************************************************************
* miniSEED 2.4 Blockette 1001 - data extension
*
* FIELD TYPE OFFSET
* type uint16_t 0
* next offset uint16_t 2
* timing quality uint8_t 4
* microsecond int8_t 5
* reserved uint8_t 6
* frame count uint8_t 7
*
* Convenience macros for accessing the fields via typed pointers follow:
***************************************************************************/
#define pMS2B1001_TYPE(blockette) ((uint16_t*)(blockette))
#define pMS2B1001_NEXT(blockette) ((uint16_t*)((uint8_t*)blockette+2))
#define pMS2B1001_TIMINGQUALITY(blockette) ((uint8_t*)((uint8_t*)blockette+4))
#define pMS2B1001_MICROSECOND(blockette) ((int8_t*)((uint8_t*)blockette+5))
#define pMS2B1001_RESERVED(blockette) ((uint8_t*)((uint8_t*)blockette+6))
#define pMS2B1001_FRAMECOUNT(blockette) ((uint8_t*)((uint8_t*)blockette+7))
/***************************************************************************
* miniSEED 2.4 Blockette 2000 - opaque data
*
* FIELD TYPE OFFSET
* type uint16_t 0
* next offset uint16_t 2
* length uint16_t 4
* data offset uint16_t 6
* recnum uint32_t 8
* byteorder uint8_t 12
* flags uint8_t 13
* numheaders uint8_t 14
* payload char[1] 15
*
* Convenience macros for accessing the fields via typed pointers follow:
***************************************************************************/
#define pMS2B2000_TYPE(blockette) ((uint16_t*)(blockette))
#define pMS2B2000_NEXT(blockette) ((uint16_t*)((uint8_t*)blockette+2))
#define pMS2B2000_LENGTH(blockette) ((uint16_t*)((uint8_t*)blockette+4))
#define pMS2B2000_DATAOFFSET(blockette) ((uint16_t*)((uint8_t*)blockette+6))
#define pMS2B2000_RECNUM(blockette) ((uint32_t*)((uint8_t*)blockette+8))
#define pMS2B2000_BYTEORDER(blockette) ((uint8_t*)((uint8_t*)blockette+12))
#define pMS2B2000_FLAGS(blockette) ((uint8_t*)((uint8_t*)blockette+13))
#define pMS2B2000_NUMHEADERS(blockette) ((uint8_t*)((uint8_t*)blockette+14))
#define pMS2B2000_PAYLOAD(blockette) ((char*)((uint8_t*)blockette+15))
/***************************************************************************
* Simple static inline convenience functions to swap bytes to "host
* order", as determined by the swap flag.
***************************************************************************/
static inline int16_t
HO2d (int16_t value, int swapflag)
{
if (swapflag)
{
ms_gswap2 (&value);
}
return value;
}
static inline uint16_t
HO2u (uint16_t value, int swapflag)
{
if (swapflag)
{
ms_gswap2 (&value);
}
return value;
}
static inline int32_t
HO4d (int32_t value, int swapflag)
{
if (swapflag)
{
ms_gswap4 (&value);
}
return value;
}
static inline uint32_t
HO4u (uint32_t value, int swapflag)
{
if (swapflag)
{
ms_gswap4 (&value);
}
return value;
}
static inline float
HO4f (float value, int swapflag)
{
if (swapflag)
{
ms_gswap4 (&value);
}
return value;
}
static inline double
HO8f (double value, int swapflag)
{
if (swapflag)
{
ms_gswap8 (&value);
}
return value;
}
/* Macro to test for sane year and day values, used primarily to
* determine if byte order swapping is needed for miniSEED 2.x.
*
* Year : between 1900 and 2100
* Day : between 1 and 366
*
* This test is non-unique (non-deterministic) for days 1, 256 and 257
* in the year 2056 because the swapped values are also within range.
* If you are using this in 2056 to determine the byte order of miniSEED 2
* you have my deepest sympathies.
*/
#define MS_ISVALIDYEARDAY(Y,D) (Y >= 1900 && Y <= 2100 && D >= 1 && D <= 366)
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -2,25 +2,185 @@
All notable changes to the CAPS client library will be documented in this file.
## 2021-04-21 1.0.0
## 2026.069 3.2.0
### Added
- Allow to override host operating system information
## 2026.068 3.1.1
### Added
- Support system load reporting
## 2026.057 3.1.0
### Added
- Support for CAPS API 7 features
## 2025.206 3.0.1
### Added
- Stream ID check. Before sending data, the stream ID is checked to ensure
that it meets the server requirements. Specical characters like '?' are
not allowed.
## 2025.163 3.0.0
### Changed
- Make session table item start and end time optional
## 2024.352 2.7.4
### Changed
- Optimize package encoding
## 2024.226 2.7.3
### Fixed
- Parse server output address correctly
## 2024.162 2.7.2
### Fixed
- Do not crash when the journal file does not follow the format
## 2024.162 2.7.1
### Changed
- Redirect log messages to SeisComP log by default
## 2024.151 2.7.0
### Added
- Add plugin instance pointer to log messages
## 2023.184 2.6.0
### Added
- More convenience push functions
## 2023.184 2.5.0
### Added
- Direct API access to last acknowledged packet end time
## 2023.107 2.4.0
### Added
- PluginApplication class logs CAPS connection settings
### Changed
- Reworked connection handling to perfom better in poor network environments
- Removed unnecessary debug output
## 2022.284 2.3.1
### Changed
- Minor changes
## 2022-04-20 2.3.0
### Added
- CAPS agent support
## 2022-04-20 2.2.0
### Added
- API function to access internal packet buffer that keeps packets until
they have been acknowledged by CAPS
## 2021-09-01 2.1.0
### Added
- Extended plugin class by the possibility to dump **outgoing** RAW and MSEED
packets to stdout. RAW data ouput is in SLIST format and MSEED packets are
dumped as binary MSEED. Applications derived from the plugin application class
can enable this feature with the command line option ``--dump-packets``.
## 2021-06-21 2.0.3
### Fixed
- CAPS Python encoder factory ownership
## 2021-06-15 2.0.2
### Fixed
- Broken uncompressed MSEED support
## 2021-05-10 2.0.1
### Fixed
- Unit tests that work on 64-bit systems only
## 2021-05-10 2.0.0
### Added
- Log status responses from server in plugin application
- SSL and authentication support for plugin application.
With this version the data output URL can be set with the
config option ``output.address``. The formal definition of
the field is: [[caps|capss]://][user:pass@]host[:port] e.g.
```script
output.address = capss://caps:caps@localhost:18003
```
The new output.address parameter superseds the output.host and
output.port parameter of previous versions and takes precedence.
The old parameters are kept for compatibility reasons but are
marked as deprecated.
## 2021-04-21 1.0.0
### Changed
- Set library version to 1.0.0
## 2021-02-16
### Added
- Allow to set maximum allowed future end time via plugin API. In additon
add commandline and config support to the plugin application class e.g.
```
```script
output.maxFutureEndTime = 120
```
By default the option is set to 120 seconds.
## 2020-12-14
### Added
- Support to set miniSEED record length via API
## 2020-09-22
### Changed
- Use last sample time as reference time for maximum future time check. Before
we used the packet end time as reference time but this makes no sense in case
of low sampled data.
@@ -30,15 +190,21 @@ All notable changes to the CAPS client library will be documented in this file.
one day in the future.
## 2020-06-22
### Added
- Python3 byte array support to any record push
## 2020-02-21
### Changed
- Increase default timeout for acknowledgement messages from 5s to 60s
## 2019-09-24
### Fixed
- Fix high load if packets could not be sent to CAPS. In that case the plugin
automatically reconnects after some amount of time. If triggered under certain
circumstances this delay was not in effect and caused unnecessarily high
@@ -49,102 +215,144 @@ All notable changes to the CAPS client library will be documented in this file.
attempting a reconnect.
## 2019-09-20
### Fixed
- Fix error string if not all data could be sent to the server
## 2019-08-19
### Changed
- Discard packets whose end time is more than 120 seconds before the system time.
## 2019-08-06
### Added
- new config option ``output.addr``
### Fixed
- ambiguous command line option ``-h``. With this version of the library the
host and port can be set via the command line option ``--addr``.
host and port can be set via the command line option ``--addr``.
- wrong config option parsing
## 2019-08-05
### Fixed
- seg fault in date time parser
## 2019-08-02
### Fixed
- Hanging TCP connections. In case of the remote side does not shutdown cleanly
the plugin did not notice that the connection is no longer available. With this
version the plugin reconnects to the server when the TCP send buffer is full and
tries to send all not acknowledged packets again.
the plugin did not notice that the connection is no longer available. With this
version the plugin reconnects to the server when the TCP send buffer is full
and tries to send all not acknowledged packets again.
- Do not discard packets if the packet buffer is full. Instead we block until
the server acknowledges some packets.
the server acknowledges some packets.
### Changed
- The plugin application class checks whether the configured buffer size is
below the minimum.
below the minimum.
## 2019-07-05
### Changed
- Ignore journal entries where the timestamp is more than 10 seconds
before the system time.
## 2018-12-19
### Fixed
- Read journal from file in plugin application.
## 2018-12-18
### Fixed
- Do not reconnect if the plugin buffer is full. Instead of we try to read
acknowledgements from the CAPS server until the plugin buffer is below the
threshold.
acknowledgements from the CAPS server until the plugin buffer is below the
threshold.
## 2018-12-17
### Added
- Support to retrieve status information e.g. the number of buffered bytes from
plugin.
## 2018-09-06
### Changed
- Enable more verbose logging for MSEED packets
## 2018-07-25
### Fixed
- unset variable of the raw data record
- trim function will return false in case of an unknown datatype
## 2018-05-30
### Fixed
- Fixed unexpected closed SSL connections
## 2018-06-05
### Fixed
- Fix RawDataRecord::setHeader
## 2018-05-16
### Fixed
- RAW data end time calculation
## 2018-03-19
### Added
### Fixed
- SSL support
## 2017-11-20
### Added
- float and double support for Steim encoders. All values will be converted implicitly
to int 32 values
- Float and double support for Steim encoders. All values will be converted
implicitly to int 32 values.
## 2017-11-08
### Added
- timing quality parameter to push call. By default the timing quality is set to -1.
- Timing quality parameter to push call. By default the timing quality is set
to -1.
## 2017-11-07
### Fixed
- do not flush encoders after reconnect
- Do not flush encoders after reconnect
## 2017-10-26
### Added
- SSL support
## 2017-10-24
### Fixed
- packet synchronization error after reconnect
- Packet synchronization error after reconnect

View File

@@ -84,6 +84,11 @@ void AnyDataRecord::setSamplingFrequency(uint16_t numerator, uint16_t denominato
}
DataRecord *AnyDataRecord::clone() const {
return new AnyDataRecord(*this);
}
const char *AnyDataRecord::formatName() const {
return "ANY";
}

View File

@@ -20,11 +20,11 @@
#include <gempa/caps/packet.h>
#include <gempa/caps/endianess.h>
#include <vector>
namespace Gempa {
namespace CAPS {
class AnyDataRecord : public DataRecord {
public:
typedef std::vector<char> Buffer;
@@ -74,37 +74,37 @@ class AnyDataRecord : public DataRecord {
bool setType(const char *type);
const char *type() const;
virtual const char *formatName() const;
DataRecord *clone() const override;
const char *formatName() const override;
virtual bool readMetaData(std::streambuf &buf, int size,
bool readMetaData(std::streambuf &buf, int size,
Header &header,
Time &startTime,
Time &endTime);
Time &endTime) override;
virtual const Header *header() const;
virtual Time startTime() const;
virtual Time endTime() const;
const Header *header() const override;
Time startTime() const override;
Time endTime() const override;
virtual bool canTrim() const;
virtual bool canMerge() const;
bool canTrim() const override;
bool canMerge() const override;
virtual bool trim(const Time &start,
const Time &end) const;
bool trim(const Time &start, const Time &end) const override;
virtual size_t dataSize(bool withHeader) const;
size_t dataSize(bool withHeader) const override;
virtual ReadStatus get(std::streambuf &buf, int size,
ReadStatus get(std::streambuf &buf, int size,
const Time &start = Time(),
const Time &end = Time(),
int maxSize = -1);
int maxSize = -1) override;
virtual bool put(std::streambuf &buf, bool withHeader) const;
bool put(std::streambuf &buf, bool withHeader) const override;
/**
* @brief Returns the packet type
* @return The packet type
*/
PacketType packetType() const { return ANYPacket; }
PacketType packetType() const override { return ANYPacket; }
/**
* @brief Sets the start time of the record
@@ -129,14 +129,15 @@ class AnyDataRecord : public DataRecord {
* @brief Returns the data vector to be filled by the caller
* @return The pointer to the internal buffer
*/
Buffer *data() { return &_data; }
Buffer *data() override { return &_data; }
/**
* @brief Initializes the internal data vector from the given buffer
* @param The buffer to read the data from
* @param The buffer size
*/
virtual void setData(char *data, size_t size);
void setData(char *data, size_t size);
protected:
AnyHeader _header;

View File

@@ -16,11 +16,14 @@
#ifndef GEMPA_CAPS_APPLICATION_H
#define GEMPA_CAPS_APPLICATION_H
#include <gempa/caps/api.h>
namespace Gempa {
namespace CAPS {
class SC_GEMPA_CAPS_API Application {
public:
Application(int argc, char **argv);
@@ -99,6 +102,7 @@ class SC_GEMPA_CAPS_API Application {
static Application *_app;
};
}
}

View File

@@ -16,6 +16,7 @@
#ifndef GEMPA_CAPS_CONNECTION_H
#define GEMPA_CAPS_CONNECTION_H
#include <gempa/caps/datetime.h>
#include <gempa/caps/sessiontable.h>
#include <gempa/caps/socket.h>
@@ -27,12 +28,15 @@
//#include <iostream>
#include <map>
namespace Gempa {
namespace CAPS {
class SessionTableItem;
struct SessionTableItem;
class Time;
class Connection {
public:
//! ConnectionStates:
@@ -171,10 +175,12 @@ class Connection {
bool _ssl;
};
typedef boost::shared_ptr<Connection> ConnectionPtr;
using ConnectionPtr = boost::shared_ptr<Connection>;
}
}
#endif

View File

@@ -18,9 +18,10 @@
#include <sstream>
#include <cmath>
#include <stdexcept>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <chrono>
#ifdef WIN32
#include <time.h>
@@ -168,6 +169,26 @@ inline void normalize(T &sec, U &usec) {
}
}
const char *timeFormats[] = {
"%FT%T.%fZ", // YYYY-MM-DDThh:mm:ss.ssssssZ
"%FT%T.%f", // YYYY-MM-DDThh:mm:ss.ssssss
"%FT%TZ", // YYYY-MM-DDThh:mm:ssZ
"%FT%T", // YYYY-MM-DDThh:mm:ss
"%FT%R", // YYYY-MM-DDThh:mm
"%FT%H", // YYYY-MM-DDThh
"%Y-%jT%T.%f", // YYYY-DDDThh:mm:ss.ssssss
"%Y-%jT%T", // YYYY-DDDThh:mm:ss
"%Y-%jT%R", // YYYY-DDDThh:mm
"%Y-%jT%H", // YYYY-DDDThh
"%F %T.%f", // YYYY-MM-DD hh:mm:ss.ssssss
"%F %T", // YYYY-MM-DD hh:mm:ss
"%F %R", // YYYY-MM-DD hh:mm
"%F %H", // YYYY-MM-DD hh
"%F", // YYYY-MM-DD
"%Y-%j", // YYYY-DDD
"%Y", // YYYY
};
}
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
@@ -342,6 +363,17 @@ TimeSpan& TimeSpan::operator=(long t) {
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
TimeSpan& TimeSpan::operator=(const TimeSpan& t) {
_timeval = t._timeval;
return *this;
}
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
TimeSpan& TimeSpan::operator=(double t) {
if( t > (double)0x7fffffff || t < -(double)0x80000000 )
@@ -801,17 +833,10 @@ Time& Time::localtime() {
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Time& Time::gmt() {
gettimeofday(&_timeval, NULL);
time_t secs = (time_t)_timeval.tv_sec;
struct tm _tm;
#ifndef WIN32
_timeval.tv_sec = (long)mktime(::localtime_r(&secs, &_tm));
#else
// We use the native localtime function of windows, which is thread safe. (But it's not reentrant)
_timeval.tv_sec = (long)timegm(::localtime(&secs));
#endif
Time &Time::gmt() {
auto us = chrono::duration_cast<chrono::microseconds>(chrono::high_resolution_clock::now().time_since_epoch()).count();
_timeval.tv_sec = us / 1000000;
_timeval.tv_usec = us % 1000000;
return *this;
}
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
@@ -1055,9 +1080,50 @@ bool Time::fromString(const char* str, const char* fmt) {
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
bool Time::fromString(const char* str) {
for ( size_t i = 0; i < sizeof(timeFormats) / sizeof(const char*); ++i ) {
if ( fromString(str, timeFormats[i]) ) {
return true;
}
}
return false;
}
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
bool Time::fromString(const std::string& str) {
return fromString(str.c_str());
}
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Time Time::FromString(const char* str, const char* fmt) {
Time t;
t.fromString(str, fmt);
return t;
}
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Time Time::FromString(const char* str) {
Time t;
t.fromString(str);
return t;
}
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Time Time::FromString(const std::string& str) {
return FromString(str.c_str());
}
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

View File

@@ -16,6 +16,7 @@
#ifndef GEMPA_CAPS_DATETIME_H
#define GEMPA_CAPS_DATETIME_H
#ifdef WIN32
#include <winsock2.h>
#else
@@ -24,8 +25,10 @@
#include <string>
struct tm;
namespace Gempa {
namespace CAPS {
@@ -64,6 +67,7 @@ class TimeSpan {
//! Assignment
TimeSpan& operator=(long t);
TimeSpan& operator=(double t);
TimeSpan& operator=(const TimeSpan& t);
//! Arithmetic
TimeSpan operator+(const TimeSpan&) const;
@@ -205,24 +209,25 @@ class Time : public TimeSpan {
//! Returns whether the date is valid or not
bool valid() const;
/** Converts the time to string using format fmt.
@param fmt The format string can contain any specifiers
as allowed for strftime. Additional the '%f'
specifier is replaced by the fraction of the seconds.
Example:
toString("%FT%T.%fZ") = "1970-01-01T00:00:00.0000Z"
@return A formatted string
/**
* @brief Converts the time to string using format fmt.
* @param fmt The format string can contain any specifiers
* as allowed for strftime. Additional the '%f'
* specifier is replaced by the fraction of the seconds.
* Example:
* toString("%FT%T.%fZ") = "1970-01-01T00:00:00.0000Z"
* @return A formatted string
*/
std::string toString(const char* fmt) const;
/**
* Converts the time to a string using the ISO time description
* @brief Converts the time to a string using the ISO time description
* @return A formatted string
*/
std::string iso() const;
/**
* Converts a string into a time representation.
* @brief Converts a string into a time representation.
* @param str The string representation of the time
* @param fmt The format string containing the conversion
* specification (-> toString)
@@ -230,11 +235,37 @@ class Time : public TimeSpan {
*/
bool fromString(const char* str, const char* fmt);
/**
* @brief Converts a string into a time representation trying a common
* set of date formats.
* @param str The string representation of the time.
* @return The conversion result
*/
bool fromString(const char* str);
/**
* @brief Convenience method for fromString(const char*).
*/
bool fromString(const std::string& str);
/**
* Static method to create a time value from a string.
* The parameters are the same as in Time::fromString.
*/
static Time FromString(const char* str, const char* fmt);
/**
* @brief Static methods that converts a string into a time
* representation trying a common set of date formats.
* @param str The string representation of the time.
* @return The conversion result
*/
static Time FromString(const char* str);
/**
* @brief Convenience method for FromString(const char*).
*/
static Time FromString(const std::string& str);
};

View File

@@ -12,16 +12,19 @@
* from gempa GmbH. *
***************************************************************************/
#ifndef GEMPA_CAPS_ENCODERFACTORY_H
#define GEMPA_CAPS_ENCODERFACTORY_H
#include "mseed/encoder.h"
#include "mseed/encoder.h"
#include <gempa/caps/packet.h>
namespace Gempa {
namespace CAPS {
/**
* @brief Abstract base class of the encoder factory. Each
* derived class must implement the create and the
@@ -158,7 +161,9 @@ class Steim2EncoderFactory : public SteimEncoderFactory {
int samplingFrequencyDenominator);
};
}
}
#endif

View File

@@ -16,6 +16,7 @@
#ifndef GEMPA_CAPS_ENDIANESS_H
#define GEMPA_CAPS_ENDIANESS_H
#include <iostream>
#include <stdint.h>
#include <streambuf>
@@ -25,6 +26,7 @@ namespace Gempa {
namespace CAPS {
namespace Endianess {
template <typename T1, typename T2> inline const T1* lvalue(const T2 &value) {
return reinterpret_cast<const T1*>(&value);
}
@@ -203,8 +205,10 @@ struct Writer {
bool good;
};
}
}
}
#endif

View File

@@ -42,6 +42,10 @@ MetaDataRecord::Buffer *MetaDataRecord::data() {
return NULL;
}
DataRecord *MetaDataRecord::clone() const {
return new MetaDataRecord(*this);
}
const char *MetaDataRecord::formatName() const {
return "META";
}

View File

@@ -16,19 +16,18 @@
#ifndef GEMPA_CAPS_METAPACKET_H
#define GEMPA_CAPS_METAPACKET_H
#include <gempa/caps/api.h>
#include <gempa/caps/packet.h>
#include <stdint.h>
#include <string>
#include <vector>
#include <cstdint>
namespace Gempa {
namespace CAPS {
struct SC_GEMPA_CAPS_API MetaResponseHeader {
struct SC_GEMPA_CAPS_API MetaResponseHeader {
struct Time {
int64_t seconds;
int32_t microSeconds;
@@ -95,15 +94,16 @@ class MetaDataRecord : public DataRecord {
//! Returns the data vector to be filled by the caller
Buffer *data();
virtual const char *formatName() const;
DataRecord *clone() const override;
const char *formatName() const override;
virtual bool readMetaData(std::streambuf &buf, int size,
bool readMetaData(std::streambuf &buf, int size,
Header &header,
Time &startTime, Time &endTime) {
Time &startTime, Time &endTime) override {
return false;
}
virtual const Header *header() const;
const Header *header() const override;
//! Sets a custom header and updates all internal structures
//! based on the current data. If the data has changed, make
@@ -111,23 +111,24 @@ class MetaDataRecord : public DataRecord {
//! is set get will not read the header information from stream
void setHeader(const MetaHeader &header);
virtual Time startTime() const;
virtual Time endTime() const;
Time startTime() const override;
Time endTime() const override;
virtual bool canTrim() const { return false; }
virtual bool canMerge() const { return false; }
bool canTrim() const override { return false; }
bool canMerge() const override { return false; }
virtual bool trim(const Time &start, const Time &end) const { return false; }
bool trim(const Time &start, const Time &end) const override { return false; }
virtual size_t dataSize(bool withHeader) const;
size_t dataSize(bool withHeader) const override;
virtual ReadStatus get(std::streambuf &buf, int size,
ReadStatus get(std::streambuf &buf, int size,
const Time &start, const Time &end,
int maxBytes);
int maxBytes) override;
virtual bool put(std::streambuf &buf, bool withHeader) const { return false; }
bool put(std::streambuf &buf, bool withHeader) const override { return false; }
PacketType packetType() const override { return MetaDataPacket; }
PacketType packetType() const { return MetaDataPacket; }
private:
MetaHeader _header;
@@ -136,6 +137,7 @@ class MetaDataRecord : public DataRecord {
mutable Time _endTime;
};
}
}

View File

@@ -35,8 +35,11 @@ namespace CAPS {
class Encoder {
public:
Encoder(int freqn, int freqd) : _clk(freqn, freqd),
_sampleCount(0), _timingQuality(-1) {}
Encoder(int freqn, int freqd)
: _clk(freqn, freqd)
, _sampleCount(0), _timingQuality(-1)
, _context{nullptr} {}
virtual ~Encoder() {}
virtual void push(void *sample) = 0;
@@ -49,13 +52,18 @@ class Encoder {
void setStartTime(const Time &time) { _clk.syncTime(time); }
const Time currentTime() const { return _clk.getTime(0); }
int timingQuality() { return _timingQuality; }
void setTimingQuality(int quality) { _timingQuality = quality; }
int timingQuality() { return _timingQuality; }
void setContext(void *context) { _context = context; }
PacketPtr pop() {
if ( _packetQueue.empty() ) return PacketPtr();
if ( _packetQueue.empty() ) {
return PacketPtr();
}
PacketPtr rec = _packetQueue.front();
rec->context = _context;
_packetQueue.pop_front();
return rec;
}
@@ -66,6 +74,7 @@ class Encoder {
int _sampleCount;
PacketQueue _packetQueue;
int _timingQuality;
void *_context;
};
}

View File

@@ -143,10 +143,9 @@ MSEEDDataRecord *MSEEDFormat::getBuffer(const Time &it, int usec_correction,
void MSEEDFormat::updateBuffer(MSEEDDataRecord *rec, int samples, int frames) {
sl_fsdh_s* fsdh = (sl_fsdh_s *)rec->data()->data();
char temp[7];
char temp[6] = { 0, 0, 0, 0, 0, 0 };
sprintf(temp, "%06d", (int)0);
memcpy(fsdh->sequence_number,temp,6);
memcpy(fsdh->sequence_number, temp, 6);
fsdh->dhq_indicator = 'D';
fsdh->num_samples = htons(samples);
@@ -157,6 +156,8 @@ void MSEEDFormat::updateBuffer(MSEEDDataRecord *rec, int samples, int frames) {
sizeof(sl_fsdh_s) + sizeof(sl_blkt_1000_s));
blkt_1001->frame_cnt = frames;
}
rec->unpackHeader();
}

View File

@@ -36,12 +36,12 @@ Steim1Encoder<T>::~Steim1Encoder() {
template<typename T>
void Steim1Encoder<T>::updateSpw(int bp) {
int spw1 = 4;
int spw = 4;
assert(bp < 4);
if ( _buf[bp] < -32768 || _buf[bp] > 32767 ) spw1 = 1;
else if ( _buf[bp] < -128 || _buf[bp] > 127 ) spw1 = 2;
if ( spw1 < _spw ) _spw = spw1;
if ( _buf[bp] < -32768 || _buf[bp] > 32767 ) spw = 1;
else if ( _buf[bp] < -128 || _buf[bp] > 127 ) spw = 2;
if ( spw < _spw ) _spw = spw;
}
template<typename T>

View File

@@ -38,36 +38,36 @@ template<typename T> Steim2Encoder<T>::~Steim2Encoder() {
}
template<typename T> void Steim2Encoder<T>::updateSpw(int bp) {
assert(_bp < 7);
assert(bp < 7);
if ( _buf[_bp] < -536870912 ) {
if ( _buf[bp] < -536870912 ) {
CAPS_WARNING("%s.%s.%s.%s: value %d is too large for Steim2 encoding",
_format->networkCode.c_str(), _format->stationCode.c_str(),
_format->locationCode.c_str(), _format->channelCode.c_str(),
_buf[_bp]);
_buf[_bp] = -536870912;
_buf[bp]);
_buf[bp] = -536870912;
_spw = 1;
return;
}
if ( _buf[_bp] > 536870911 ) {
if ( _buf[bp] > 536870911 ) {
CAPS_WARNING("%s.%s.%s.%s: value %d is too large for Steim2 encoding",
_format->networkCode.c_str(), _format->stationCode.c_str(),
_format->locationCode.c_str(), _format->channelCode.c_str(),
_buf[_bp]);
_buf[_bp] = 536870911;
_buf[bp]);
_buf[bp] = 536870911;
_spw = 1;
return;
}
int spw1 = 7;
if ( _buf[_bp] < -16384 || _buf[_bp] > 16383 ) spw1 = 1;
else if ( _buf[_bp] < -512 || _buf[_bp] > 511 ) spw1 = 2;
else if ( _buf[_bp] < -128 || _buf[_bp] > 127 ) spw1 = 3;
else if ( _buf[_bp] < -32 || _buf[_bp] > 31 ) spw1 = 4;
else if ( _buf[_bp] < -16 || _buf[_bp] > 15 ) spw1 = 5;
else if ( _buf[_bp] < -8 || _buf[_bp] > 7 ) spw1 = 6;
if ( spw1 < _spw ) _spw = spw1;
int spw = 7;
if ( _buf[bp] < -16384 || _buf[bp] > 16383 ) spw = 1;
else if ( _buf[bp] < -512 || _buf[bp] > 511 ) spw = 2;
else if ( _buf[bp] < -128 || _buf[bp] > 127 ) spw = 3;
else if ( _buf[bp] < -32 || _buf[bp] > 31 ) spw = 4;
else if ( _buf[bp] < -16 || _buf[bp] > 15 ) spw = 5;
else if ( _buf[bp] < -8 || _buf[bp] > 7 ) spw = 6;
if ( spw < _spw ) _spw = spw;
}
template<typename T> void Steim2Encoder<T>::store(int32_t value) {

View File

@@ -13,16 +13,24 @@
***************************************************************************/
#include <gempa/caps/endianess.h>
#include <gempa/caps/mseedpacket.h>
#include <gempa/caps/log.h>
#include <gempa/caps/utils.h>
#include <cmath>
#include <climits>
#include <cstdio>
#include <numeric>
#include <streambuf>
#include <iostream>
#include <libmseed.h>
namespace {
// Force local include
#include "./3rd-party/libmseed/libmseed.h"
#include "./3rd-party/libmseed/mseedformat.h"
#define MS_ISVALIDYEARDAY(Y,D) (Y >= 1900 && Y <= 2100 && D >= 1 && D <= 366)
#define LOG_SID(FUNC, format,...)\
do {\
@@ -30,43 +38,151 @@ do {\
char s[6];\
char c[6];\
char l[6];\
ms_strncpclean(n, head.network, 2);\
ms_strncpclean(s, head.station, 5);\
ms_strncpclean(l, head.location, 2);\
ms_strncpclean(c, head.channel, 3);\
ms_strncpclean(n, pMS2FSDH_NETWORK(head), 2);\
ms_strncpclean(s, pMS2FSDH_STATION(head), 5);\
ms_strncpclean(l, pMS2FSDH_LOCATION(head), 2);\
ms_strncpclean(c, pMS2FSDH_CHANNEL(head), 3);\
FUNC("[%s.%s.%s.%s] " format, n, s, l, c, ##__VA_ARGS__);\
} while(0)
}
namespace Gempa {
namespace CAPS {
namespace {
uint16_t ms2_blktlen(uint16_t blkttype, const char *blkt, int8_t swapflag) {
uint16_t blktlen = 0;
switch ( blkttype ) {
case 100: // Sample Rate
blktlen = 12;
break;
case 200: // Generic Event Detection
blktlen = 28;
break;
case 201: // Murdock Event Detection
blktlen = 36;
break;
case 300: // Step Calibration
blktlen = 32;
break;
case 310: // Sine Calibration
blktlen = 32;
break;
case 320: // Pseudo-random Calibration
blktlen = 28;
break;
case 390: // Generic Calibration
blktlen = 28;
break;
case 395: // Calibration Abort
blktlen = 16;
break;
case 400: // Beam
blktlen = 16;
break;
case 500: // Timing
blktlen = 8;
break;
case 1000: // Data Only SEED
blktlen = 8;
break;
case 1001: // Data Extension
blktlen = 8;
break;
case 2000: // Opaque Data
// First 2-byte field after the blockette header is the length
if ( blkt ) {
blktlen = *reinterpret_cast<const uint16_t*>(blkt + 4);
if ( swapflag ) {
ms_gswap2(&blktlen);
}
}
break;
}
return blktlen;
}
bool double2frac(uint16_t &numerator, uint16_t &denominator, double value) {
using namespace std;
// Check numeric limits
if ( value > numeric_limits<uint16_t>::max() ) {
numerator = numeric_limits<uint16_t>::max();
denominator = 1;
return false;
}
if ( value < numeric_limits<uint16_t>::min() ) {
numerator = numeric_limits<uint16_t>::min();
denominator = 1;
return false;
}
// Operate on positive numbers
int sign = 1;
if ( value < 0 ) {
sign = -1;
value = -value;
}
// Calculatate the largest possible power of 10 giving numeric integer
// limits and the current input number
static auto max_exp = floor(log10(numeric_limits<uint16_t>::max())) - 1.0;
auto exp = max_exp;
if ( value >= 10 ) {
exp -= floor(log10(value));
}
// Expand input number with power of 10
denominator = static_cast<int>(pow(10, exp));
numerator = static_cast<int>(round(value * denominator));
// Simplify the fraction by calculating the greatest common divisor
int gcd_ = gcd(numerator, denominator);
numerator = sign * numerator / gcd_;
denominator = denominator / gcd_;
return true;
}
}
MSEEDDataRecord::MSEEDDataRecord() {}
DataRecord *MSEEDDataRecord::clone() const {
return new MSEEDDataRecord(*this);
}
const char *MSEEDDataRecord::formatName() const {
return "MSEED";
}
bool MSEEDDataRecord::readMetaData(std::streambuf &buf, int size,
Header &header,
Time &startTime,
Time &endTime) {
fsdh_s head;
Header &header, Time &startTime, Time &endTime) {
char head[48];
if ( size <= 0 ) {
CAPS_WARNING("read metadata: invalid size of record: %d", size);
return false;
}
if ( size < MINRECLEN || size > MAXRECLEN ) {
if ( (size < MINRECLEN) || (size > MAXRECLEN) ) {
CAPS_WARNING("read metadata: invalid MSEED record size: %d", size);
return false;
}
// Read first 32 byte
// Read first 40 byte
size_t read = buf.sgetn((char*)&head, sizeof(head));
if ( read < sizeof(head) ) {
CAPS_WARNING("read metadata: input buffer underflow: only %d/%d bytes read",
@@ -74,48 +190,52 @@ bool MSEEDDataRecord::readMetaData(std::streambuf &buf, int size,
return false;
}
if ( !MS_ISVALIDHEADER(((char*)&head)) ) {
CAPS_WARNING("read metadata: invalid MSEED header");
return false;
if ( MS2_ISVALIDHEADER(((char*)&head)) ) {
int headerswapflag = 0;
// Swap byte order?
if ( !MS_ISVALIDYEARDAY(*pMS2FSDH_YEAR(head), *pMS2FSDH_DAY(head)) ) {
headerswapflag = 1;
ms_gswap2(pMS2FSDH_YEAR(head));
ms_gswap2(pMS2FSDH_DAY(head));
ms_gswap2(pMS2FSDH_FSEC(head));
ms_gswap2(pMS2FSDH_NUMSAMPLES(head));
ms_gswap2(pMS2FSDH_SAMPLERATEFACT(head));
ms_gswap2(pMS2FSDH_SAMPLERATEMULT(head));
ms_gswap4(pMS2FSDH_TIMECORRECT(head));
ms_gswap2(pMS2FSDH_DATAOFFSET(head));
ms_gswap2(pMS2FSDH_BLOCKETTEOFFSET(head));
}
bool headerswapflag = false;
if ( !MS_ISVALIDYEARDAY(head.start_time.year, head.start_time.day) )
headerswapflag = true;
/* Swap byte order? */
if ( headerswapflag ) {
MS_SWAPBTIME(&head.start_time);
ms_gswap2a(&head.numsamples);
ms_gswap2a(&head.samprate_fact);
ms_gswap2a(&head.samprate_mult);
ms_gswap4a(&head.time_correct);
ms_gswap2a(&head.data_offset);
ms_gswap2a(&head.blockette_offset);
}
auto year = *pMS2FSDH_YEAR(head);
auto yday = *pMS2FSDH_DAY(head);
auto hours = *pMS2FSDH_HOUR(head);
auto minutes = *pMS2FSDH_MIN(head);
auto seconds = *pMS2FSDH_SEC(head);
auto fsec = *pMS2FSDH_FSEC(head) * 100;
header.dataType = DT_Unknown;
hptime_t hptime = ms_btime2hptime(&head.start_time);
if ( hptime == HPTERROR ) {
LOG_SID(CAPS_DEBUG, "read metadata: invalid start time");
return false;
}
startTime = Time::FromYearDay(year, yday);
startTime += TimeSpan(hours * 3600 + minutes * 60 + seconds, fsec);
header.quality.ID = 0;
header.quality.str[0] = head.dataquality;
header.quality.str[0] = *pMS2FSDH_DATAQUALITY(head);
if ( head.time_correct != 0 && !(head.act_flags & 0x02) )
hptime += (hptime_t)head.time_correct * (HPTMODULUS / 10000);
if ( *pMS2FSDH_TIMECORRECT(head) != 0 && !(*pMS2FSDH_ACTFLAGS(head) & 0x02) ) {
startTime += TimeSpan(0, *pMS2FSDH_TIMECORRECT(head) * 100);
}
// Parse blockettes
uint32_t blkt_offset = head.blockette_offset;
uint32_t blkt_offset = *pMS2FSDH_BLOCKETTEOFFSET(head);
uint32_t blkt_length;
uint16_t blkt_type;
uint16_t next_blkt;
if ( blkt_offset < sizeof(head) ) {
LOG_SID(CAPS_DEBUG, "read metadata: blockette "
LOG_SID(CAPS_DEBUG, "%s", "read metadata: blockette "
"offset points into header");
return false;
}
@@ -123,13 +243,13 @@ bool MSEEDDataRecord::readMetaData(std::streambuf &buf, int size,
uint32_t coffs = 0;
while ( (blkt_offset != 0) && ((int)blkt_offset < size) &&
(blkt_offset < MAXRECLEN) ) {
(blkt_offset < static_cast<uint32_t>(size)) ) {
char bhead[6];
int seek_ofs = blkt_offset-sizeof(head)-coffs;
int seek_ofs = blkt_offset - sizeof(head) - coffs;
buf.pubseekoff(seek_ofs, std::ios_base::cur, std::ios_base::in);
coffs += seek_ofs;
if ( buf.sgetn(bhead, 6) != 6 ) {
LOG_SID(CAPS_DEBUG, "read metadata: "
LOG_SID(CAPS_DEBUG, "%s", "read metadata: "
"failed to read blockette header");
break;
}
@@ -137,14 +257,14 @@ bool MSEEDDataRecord::readMetaData(std::streambuf &buf, int size,
coffs += 6;
memcpy(&blkt_type, bhead, 2);
memcpy(&next_blkt, bhead+2, 2);
memcpy(&next_blkt, bhead + 2, 2);
if ( headerswapflag ) {
ms_gswap2(&blkt_type);
ms_gswap2(&next_blkt);
}
blkt_length = ms_blktlen(blkt_type, bhead, headerswapflag);
blkt_length = ms2_blktlen(blkt_type, bhead, headerswapflag);
if ( blkt_length == 0 ) {
LOG_SID(CAPS_DEBUG, "read metadata: "
@@ -194,7 +314,7 @@ bool MSEEDDataRecord::readMetaData(std::streambuf &buf, int size,
}
else if ( blkt_type == 1001 ) {
// Add usec correction
hptime += ((hptime_t)bhead[5]) * (HPTMODULUS / 1000000);
startTime += TimeSpan(0, *reinterpret_cast<int8_t*>(bhead + 5));
}
/* Check that the next blockette offset is beyond the current blockette */
@@ -216,29 +336,102 @@ bool MSEEDDataRecord::readMetaData(std::streambuf &buf, int size,
blkt_offset = next_blkt;
}
startTime = Time((hptime_t)hptime/HPTMODULUS,(hptime_t)hptime%HPTMODULUS);
endTime = startTime;
if ( head.samprate_fact > 0 ) {
header.samplingFrequencyNumerator = head.samprate_fact;
if ( *pMS2FSDH_SAMPLERATEFACT(head) > 0 ) {
header.samplingFrequencyNumerator = *pMS2FSDH_SAMPLERATEFACT(head);
header.samplingFrequencyDenominator = 1;
}
else {
header.samplingFrequencyNumerator = 1;
header.samplingFrequencyDenominator = -head.samprate_fact;
header.samplingFrequencyDenominator = -*pMS2FSDH_SAMPLERATEFACT(head);
}
if ( head.samprate_mult > 0 )
header.samplingFrequencyNumerator *= head.samprate_mult;
else
header.samplingFrequencyDenominator *= -head.samprate_mult;
if ( *pMS2FSDH_SAMPLERATEMULT(head) > 0 ) {
header.samplingFrequencyNumerator *= *pMS2FSDH_SAMPLERATEMULT(head);
}
else {
header.samplingFrequencyDenominator *= -*pMS2FSDH_SAMPLERATEMULT(head);
}
if ( header.samplingFrequencyNumerator > 0.0 && head.numsamples > 0 ) {
hptime = (hptime_t)head.numsamples * HPTMODULUS * header.samplingFrequencyDenominator / header.samplingFrequencyNumerator;
endTime += TimeSpan((hptime_t)hptime/HPTMODULUS,(hptime_t)hptime%HPTMODULUS);
if ( header.samplingFrequencyNumerator > 0.0 && *pMS2FSDH_NUMSAMPLES(head) > 0 ) {
int64_t dt = static_cast<int64_t>(*pMS2FSDH_NUMSAMPLES(head)) * 1000000 * header.samplingFrequencyDenominator / header.samplingFrequencyNumerator;
endTime += TimeSpan(0, dt);
}
timeToTimestamp(header.samplingTime, startTime);
}
else if ( MS3_ISVALIDHEADER(((char*)&head)) ) {
startTime = Time::FromYearDay(
Endianess::Converter::ToLittleEndian(*pMS3FSDH_YEAR(head)),
Endianess::Converter::ToLittleEndian(*pMS3FSDH_DAY(head))
);
startTime += TimeSpan(
Endianess::Converter::ToLittleEndian(*pMS3FSDH_HOUR(head)) * 3600 +
Endianess::Converter::ToLittleEndian(*pMS3FSDH_MIN(head)) * 60 +
Endianess::Converter::ToLittleEndian(*pMS3FSDH_SEC(head)),
Endianess::Converter::ToLittleEndian(*pMS3FSDH_NSEC(head)) / 1000
);
auto nsamp = Endianess::Converter::ToLittleEndian(*pMS3FSDH_NUMSAMPLES(head));
auto fsamp = Endianess::Converter::ToLittleEndian(*pMS3FSDH_SAMPLERATE(head));
if ( fsamp < 0 ) {
fsamp = -1.0 / fsamp;
}
auto encoding = Endianess::Converter::ToLittleEndian(*pMS3FSDH_ENCODING(head));
header.dataType = DT_Unknown;
switch ( encoding ) {
case DE_ASCII:
header.dataType = DT_INT8;
break;
case DE_INT16:
header.dataType = DT_INT16;
break;
case DE_INT32:
case DE_STEIM1:
case DE_STEIM2:
case DE_CDSN:
case DE_DWWSSN:
case DE_SRO:
header.dataType = DT_INT32;
break;
case DE_FLOAT32:
header.dataType = DT_FLOAT;
break;
case DE_FLOAT64:
header.dataType = DT_DOUBLE;
break;
case DE_GEOSCOPE24:
case DE_GEOSCOPE163:
case DE_GEOSCOPE164:
header.dataType = DT_FLOAT;
break;
default:
break;
}
header.quality.ID = 0;
if ( !double2frac(header.samplingFrequencyNumerator, header.samplingFrequencyDenominator, fsamp) ) {
CAPS_WARNING("read metadata: invalid sampling rate: %f", fsamp);
return false;
}
endTime = startTime;
if ( header.samplingFrequencyNumerator > 0.0 && *pMS3FSDH_NUMSAMPLES(head) > 0 ) {
int64_t dt = static_cast<int64_t>(nsamp) * 1000000 * header.samplingFrequencyDenominator / header.samplingFrequencyNumerator;
endTime += TimeSpan(0, dt);
}
timeToTimestamp(header.samplingTime, startTime);
return false;
}
else {
CAPS_WARNING("read metadata: invalid MSEED header");
return false;
}
return true;
}
@@ -319,68 +512,19 @@ bool MSEEDDataRecord::put(std::streambuf &buf, bool /*withHeader*/) const {
return buf.sputn(&_data[0], _data.size()) == (int)_data.size();
}
void MSEEDDataRecord::setData(const void *data, size_t size) {
_data.resize(size);
memcpy(_data.data(), data, size);
unpackHeader();
}
void MSEEDDataRecord::unpackHeader(char *data, size_t size) {
// Only unpack the header structure
MSRecord *ms_rec = NULL;
int state = msr_unpack(data, size, &ms_rec, 0, 0);
if ( state != MS_NOERROR ) {
CAPS_WARNING("read metadata: read error: %d", state);
if ( ms_rec != NULL )
msr_free(&ms_rec);
return;
void MSEEDDataRecord::unpackHeader() {
arraybuf tmp(_data.data(), _data.size());
if ( !readMetaData(tmp, _data.size(), _header, _startTime, _endTime) ) {
CAPS_WARNING("invalid MSEED header");
}
hptime_t hptime = msr_starttime(ms_rec);
_startTime = Time((hptime_t)hptime/HPTMODULUS,(hptime_t)hptime%HPTMODULUS);
_endTime = _startTime;
if ( ms_rec->samprate > 0.0 && ms_rec->samplecnt > 0 ) {
hptime = (hptime_t)(((double)(ms_rec->samplecnt) / ms_rec->samprate * HPTMODULUS) + 0.5);
_endTime += TimeSpan((hptime_t)hptime/HPTMODULUS,(hptime_t)hptime%HPTMODULUS);
}
_header.dataType = DT_Unknown;
timeToTimestamp(_header.samplingTime, _startTime);
if ( ms_rec->fsdh->samprate_fact > 0 ) {
_header.samplingFrequencyNumerator = ms_rec->fsdh->samprate_fact;
_header.samplingFrequencyDenominator = 1;
}
else {
_header.samplingFrequencyNumerator = 1;
_header.samplingFrequencyDenominator = -ms_rec->fsdh->samprate_fact;
}
if ( ms_rec->fsdh->samprate_mult > 0 )
_header.samplingFrequencyNumerator *= ms_rec->fsdh->samprate_mult;
else
_header.samplingFrequencyDenominator *= -ms_rec->fsdh->samprate_mult;
switch ( ms_rec->sampletype ) {
case 'a':
_header.dataType = DT_INT8;
break;
case 'i':
_header.dataType = DT_INT32;
break;
case 'f':
_header.dataType = DT_FLOAT;
break;
case 'd':
_header.dataType = DT_DOUBLE;
break;
default:
_header.dataType = DT_Unknown;
break;
}
msr_free(&ms_rec);
}

View File

@@ -18,7 +18,6 @@
#include <gempa/caps/packet.h>
#include <vector>
namespace Gempa {
@@ -29,46 +28,47 @@ class MSEEDDataRecord : public DataRecord {
public:
MSEEDDataRecord();
virtual const char *formatName() const;
DataRecord *clone() const override;
const char *formatName() const override;
virtual bool readMetaData(std::streambuf &buf, int size,
bool readMetaData(std::streambuf &buf, int size,
Header &header,
Time &startTime,
Time &endTime);
Time &endTime) override;
virtual const Header *header() const;
virtual Time startTime() const;
virtual Time endTime() const;
const Header *header() const override;
Time startTime() const override;
Time endTime() const override;
virtual bool canTrim() const;
virtual bool canMerge() const;
bool canTrim() const override;
bool canMerge() const override;
virtual bool trim(const Time &start,
const Time &end) const;
bool trim(const Time &start,
const Time &end) const override;
virtual size_t dataSize(bool withHeader) const;
size_t dataSize(bool withHeader) const override;
virtual ReadStatus get(std::streambuf &buf, int size,
ReadStatus get(std::streambuf &buf, int size,
const Time &start = Time(),
const Time &end = Time(),
int maxSize = -1);
int maxSize = -1) override;
virtual bool put(std::streambuf &buf, bool withHeader) const;
bool put(std::streambuf &buf, bool withHeader) const override;
/**
* @brief Returns the packet type
* @return The packet type
*/
PacketType packetType() const { return MSEEDPacket; }
PacketType packetType() const override { return MSEEDPacket; }
/**
* @brief Initializes the internal data vector from the given buffer
* @param The buffer to read the data from
* @param The buffer size
*/
virtual void setData(const void *data, size_t size);
void setData(const void *data, size_t size);
void unpackHeader() { unpackHeader(_data.data(), _data.size()); }
void unpackHeader();
protected:
@@ -78,10 +78,6 @@ class MSEEDDataRecord : public DataRecord {
Time _endTime;
int _dataType;
private:
void unpackHeader(char *data, size_t size);
};

View File

@@ -16,6 +16,7 @@
#ifndef GEMPA_CAPS_PACKET_H
#define GEMPA_CAPS_PACKET_H
#include <gempa/caps/api.h>
#include <gempa/caps/endianess.h>
@@ -24,12 +25,11 @@
#include <boost/shared_ptr.hpp>
#include <stdint.h>
#include <cstdint>
#include <string>
#include <vector>
namespace Gempa {
namespace CAPS {
@@ -320,6 +320,7 @@ class SC_GEMPA_CAPS_API DataRecord {
public:
virtual ~DataRecord();
virtual DataRecord *clone() const = 0;
virtual const char *formatName() const = 0;
virtual bool readMetaData(std::streambuf &buf, int size,
@@ -393,7 +394,8 @@ class SC_GEMPA_CAPS_API DataRecord {
Buffer _data;
};
typedef boost::shared_ptr<DataRecord> DataRecordPtr;
using DataRecordPtr = boost::shared_ptr<DataRecord>;
struct RawPacket {
@@ -403,6 +405,7 @@ struct RawPacket {
DataRecord *record;
};
struct MetaPacket {
std::string SID[4];
PacketDataHeader packetDataHeader;
@@ -413,6 +416,7 @@ struct MetaPacket {
Time timestamp;
};
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -16,6 +16,7 @@
#ifndef GEMPA_CAPS_PLUGIN_H
#define GEMPA_CAPS_PLUGIN_H
/*
// Enable/disable journal
#define CAPS_FEATURES_JOURNAL 1
@@ -36,7 +37,6 @@
#include <gempa/caps/socket.h>
#include <gempa/caps/url.h>
#include <gempa/caps/version.h>
#include <gempa/caps/pluginpacket.h>
#include <boost/function.hpp>
@@ -50,10 +50,10 @@
#include <list>
namespace Gempa {
namespace CAPS {
class SC_GEMPA_CAPS_API Plugin {
public:
enum Status {
@@ -71,6 +71,22 @@ class SC_GEMPA_CAPS_API Plugin {
size_t maxBytesBuffered;
};
struct HostInfo {
std::string agent;
std::string agentVersion;
std::string os;
int64_t totalMem{-1};
int64_t totalDisc{-1};
};
struct RuntimeInfo {
int64_t availableMem{-1};
int64_t procUsedMem{-1};
int64_t availableDisc{-1};
int cpuUsage{-1};
double systemLoad{-1};
};
typedef std::vector<char> Buffer;
typedef boost::shared_ptr<Buffer> BufferPtr;
typedef std::deque<PacketPtr> PacketBuffer;
@@ -90,13 +106,15 @@ class SC_GEMPA_CAPS_API Plugin {
typedef std::map<std::string, StreamState> StreamStates;
typedef boost::function<void (const std::string &, const CAPS::Time &,
const CAPS::Time &)> PacketAckFunc;
const CAPS::Time &, void *context)> PacketAckFunc;
typedef boost::function<void ()> ConnectedCallback;
typedef boost::function<void ()> DisconnectedCallback;
public:
Plugin(const std::string &name, const std::string &options = "",
const std::string &description = "");
~Plugin();
virtual ~Plugin();
void close();
void quit();
@@ -186,15 +204,31 @@ class SC_GEMPA_CAPS_API Plugin {
void setPacketAckFunc(const PacketAckFunc &func) { _packetAckFunc = func; }
void setSendTimeout(int timeout) {
_sendTimeout = timeout;
/**
* @brief Sets the connection timout used when the plugin
* tries to establish a connection to CAPS
* @param sec The seconds
*/
void setConnectionTimeout(int sec) {
_connectionTimeout = sec;
}
void setTimeouts(int ack, int lastAck) {
_ackTimeout = ack;
_lastAckTimeout = lastAck;
/**
* @brief Sets the maximum number of seconds to wait until
* the file descriptor is ready for writing.
* @param sec The seconds
*/
void setSendTimeout(int sec) {
_sendTimeout = sec;
}
/**
* @brief Sets timeout values
* @param ack The acknowledgement message value
* @param lastAck The last acknowledgement message value
* @param send See setSendTimeout
* @
*/
void setTimeouts(int ack, int lastAck, int send) {
_ackTimeout = ack;
_lastAckTimeout = lastAck;
@@ -209,11 +243,18 @@ class SC_GEMPA_CAPS_API Plugin {
const StreamStates &streamStates() const { return _states; }
bool writeJournal();
bool writeJournal(std::ostream &ostream);
/**
* @brief Gets end time of the last acknowledged packet
* @param id The stream ID, e.g., GE.APE..BHZ
* @return The last end time or an invalid time
*/
Gempa::CAPS::Time lastEndTime(const std::string &id);
#endif
Status push(const std::string &net, const std::string &sta,
const std::string &loc, const std::string &cha,
DataRecordPtr rec, const std::string &uom,
int timingQuality = -1);
int timingQuality = -1, void *context = nullptr);
Status push(const std::string &net, const std::string &sta,
const std::string &loc, const std::string &cha,
@@ -221,7 +262,53 @@ class SC_GEMPA_CAPS_API Plugin {
uint16_t numerator, uint16_t denominator,
const std::string &uom,
void *data, size_t count,
DataType dt, int timingQuality = -1);
DataType dt, int timingQuality = -1,
void *context = nullptr);
/**
* @brief Sends CAPS data record to CAPS
* @param net Network code
* @param sta Station code
* @param loc Location code
* @param cha Channel code
* @param rec CAPS data record
* @param uom Unit of measurement
* @param timingQuality Data timing quality. Ususally between 0 and 100 or -1
* if unset
* @param context Pointer to user-defined context data
* @return Status Packet status
*/
Status pushRecord(const std::string &net, const std::string &sta,
const std::string &loc, const std::string &cha,
DataRecordPtr rec, const std::string &uom,
int timingQuality = -1, void *context = nullptr);
/**
* @brief Sends C array data as RAW record to CAPS
* @param net Network code
* @param sta Station code
* @param loc Location code
* @param cha Channel code
* @param stime Packet start time
* @param numerator Sampling frequency numerator
* @param denominator Sampling frequency denominator
* @param uom Unit of measurement
* @param data Pointer to data array
* @param count Number of samples
* @param dt Data type, e.g., DT_DOUBLE
* @param timingQuality Data timing quality. Ususally between 0 and 100 or -1
* if unset
* @param context Pointer to user-defined context data
* @return Status Packet status
*/
Status pushRaw(const std::string &net, const std::string &sta,
const std::string &loc, const std::string &cha,
const Time &stime,
uint16_t numerator, uint16_t denominator,
const std::string &uom,
void *data, size_t count,
DataType dt, int timingQuality = -1,
void *context = nullptr);
/*
* Sends given data as any packet. Before sending the data will be
@@ -233,13 +320,62 @@ class SC_GEMPA_CAPS_API Plugin {
const std::string &loc, const std::string &cha,
const Time &stime, uint16_t numerator,
uint16_t denominator, const std::string &format,
char *data, size_t count);
char *data, size_t count, void *context = nullptr);
Status push(const std::string &net, const std::string &sta,
const std::string &loc, const std::string &cha,
const Time &stime, uint16_t numerator,
uint16_t denominator, const std::string &format,
const std::string &data);
const std::string &data, void *context = nullptr);
/**
* @brief Sends C array data as any record to CAPS
* @param net Network code
* @param sta Station code
* @param loc Location code
* @param cha Channel code
* @param stime Packet start time
* @param etime Packet end time
* @param numerator Sampling frequency numerator. Use 1 / 0
* for packets without sampling frequency.
* @param denominator Sampling frequency denominator
* @param format Data format of the provided data
* @param uom Unit of measurement, e.g., px
* @param data Pointer to data array
* @param count Number of data elements
* @param context Pointer to user-defined context data
* @return Status Packet status
*/
Status pushAny(const std::string &net, const std::string &sta,
const std::string &loc, const std::string &cha,
const Time &stime, const Time &etime,
uint16_t numerator, uint16_t denominator,
const std::string &format, const std::string &uom,
char *data, size_t count, void *context = nullptr);
/**
* @brief Sends STL string as any record to CAPS
* @param net Network code
* @param sta Station code
* @param loc Location code
* @param cha Channel code
* @param stime Packet start time
* @param etime Packet end time
* @param numerator Sampling frequency numerator. Use 1 / 0
* for packets without sampling frequency.
* @param denominator Sampling frequency denominator
* @param format Data format of the provided data
* @param uom Unit of measurement, e.g., px
* @param data Data as string
* @param context Pointer to user-defined context data
* @return Status Packet status
*/
Status pushAny(const std::string &net, const std::string &sta,
const std::string &loc, const std::string &cha,
const Time &stime, const Time &etime,
uint16_t numerator, uint16_t denominator,
const std::string &format, const std::string &uom,
const std::string &data, void *context = nullptr);
#endif
void flushEncoders();
@@ -256,12 +392,51 @@ class SC_GEMPA_CAPS_API Plugin {
return _packetBuffer;
}
/**
* @brief Sets the agent string. Allows the server to
* identify the application that sends data.
* @param agent The agent string
* @brief Set host information like agent or total mem
* @param info The host info struct
*/
void setAgent(const std::string &agent);
void setHostInfo(const HostInfo &info);
/**
* @brief Set runtime information like CPU load or process memory usage.
* This methods blocks until sent.
* @param info The runtime info struct
*/
void setRuntimeInfo(const RuntimeInfo &info);
/**
* @brief Sets the connection ID. If not set, the server generates an ID
* during the first connection attempt.
* @param id The connection ID string
*/
void setConnectionID(const std::string &id);
/**
* @brief Set the connection ID file to read the connection ID from
* @param filename The filename
* @return True if the connection could be read from the given file
*/
bool setConnectionIDFile(const std::string &filename);
/**
* @brief Returns the connection ID
* @return Returns the connection ID
*/
const std::string &connectionID() const;
/**
* @brief Set function that gets called if the connection to the server
* has been established.
*/
void setConnectedCallback(ConnectedCallback f);
/**
* @brief Set function that gets called if the connection to the server
* was disconnected.
*/
void setDisconnectedCallback(DisconnectedCallback f);
const char *version() {
return LIB_CAPS_VERSION_NAME;
@@ -277,6 +452,18 @@ class SC_GEMPA_CAPS_API Plugin {
*/
bool getAPIVersion(int &version);
/**
* @brief Get connection ID from server. If the plugin sends no
* connection ID the server generates one.
* @return True on success
*/
bool getConnectionID();
/**
* brief Send runtime information
*/
void sendRuntimeInfo();
private:
typedef boost::shared_ptr<Encoder> EncoderPtr;
@@ -324,6 +511,8 @@ class SC_GEMPA_CAPS_API Plugin {
fd_set _readFDs;
fd_set _writeFDs;
int _sendTimeout;
int _readTimeout;
int _connectionTimeout;
#if !defined(CAPS_FEATURES_JOURNAL) || CAPS_FEATURES_JOURNAL
std::string _journalFile;
bool _journalDirty;
@@ -352,10 +541,17 @@ class SC_GEMPA_CAPS_API Plugin {
TimeSpan _maxFutureEndTime;
bool _dumpPackets;
std::string _agent;
HostInfo _hostInfo;
RuntimeInfo _runtimeInfo;
std::string _connectionID;
std::string _connectionIDFile;
ConnectedCallback _connectedCallback;
DisconnectedCallback _disconnectedCallback;
};
typedef boost::shared_ptr<Plugin> PluginPtr;
using PluginPtr = boost::shared_ptr<Plugin>;
}
}

View File

@@ -20,19 +20,17 @@
#include <gempa/caps/log.h>
#include <gempa/caps/utils.h>
#include <seiscomp3/core/system.h>
#include <seiscomp3/logging/log.h>
#include <seiscomp/core/system.h>
#include <seiscomp/logging/log.h>
#include <boost/algorithm/string.hpp>
#ifdef SC_GEMPA_SEATTLE
#include <seiscomp3/system/environment.h>
#else
#include <seiscomp3/config/environment.h>
#endif
#include <seiscomp/system/environment.h>
using namespace std;
#include <cstdlib>
#include <filesystem>
namespace fs = std::filesystem;
namespace sc = Seiscomp::Core;
namespace {
@@ -73,52 +71,91 @@ void LogDebug(const char *fmt, ...) {
}
const size_t MIN_BUFFER_SIZE = 1024*16;
const uint16_t DEFAULT_PORT = 18003;
bool readKeyValueFile(std::map<std::string, std::string> &data,
const std::string &filename, const char *sep) {
std::ifstream ifs(filename.c_str());
if ( !ifs.is_open() ) {
return false;
}
std::string line;
while ( getline(ifs, line) ) {
const char* str = line.c_str();
int len = strlen(str);
const char *tok = nullptr;
int tok_len = 0;
int tok_count = 0;
std::string key;
for ( ; (tok = Gempa::CAPS::tokenize(str, sep, len, tok_len)) != nullptr;
++tok_count ) {
Gempa::CAPS::trim(tok, tok_len);
if ( tok_count == 0 ) {
if ( len == 0) {
break;
}
key.assign(tok, tok_len);
}
else if ( tok_count == 1 ) {
data.insert({key, std::string(tok, tok_len)});
}
}
}
return true;
}
int64_t getAvailableMemory() {
std::map<std::string, std::string> data;
if ( !readKeyValueFile(data, "/proc/meminfo", ":") ) {
return -1;
}
auto it = data.find("MemAvailable");
if ( it == data.end() ) {
return -1;
}
std::vector<std::string> tokens;
sc::split(tokens, it->second.c_str(), "kB");
if ( tokens.empty() ) {
return -1;
}
sc::trim(tokens[0]);
int64_t availableMemory = 0;
sc::fromString(availableMemory, tokens[0]);
return availableMemory;
}
}
namespace Gempa {
namespace CAPS {
namespace Gempa::CAPS {
PluginApplication::PluginApplication(int argc, char **argv, const string &desc)
PluginApplication::PluginApplication(int argc, char **argv, const std::string &name)
: Seiscomp::Client::StreamApplication(argc, argv)
, _plugin(Plugin(desc)) {
_bufferSize = 1 << 20;
_backfillingBufferSize = 180;
_flushInterval = 10;
_ackTimeout = 60;
_lastAckTimeout = 5;
_sendTimeout = 60;
_logStatus = false;
_statusFlushInterval = 10;
_strAddr = "localhost:" + sc::toString(DEFAULT_PORT);
, _plugin(Plugin(name)) {
SC_FS_DECLARE_PATH(path, "@ROOTDIR@/var/run/" + SCCoreApp->name() + "/journal")
_journalFile = path.string();
_mseedEnabled = false;
_mseedEncoding = Steim2;
_mseedRecordLength = 9;
_strMseedEncoding = "Steim2";
_maxFutureEndTime = 120;
_dumpPackets = false;
fs::path path("@ROOTDIR@/var/run/" + SCCoreApp->name());
_journalFile = (path / "journal").string();
// By default we disable the acquisition autostart because not all plugins
// require this feature. It must be enabled explicitly if required.
setAutoAcquisitionStart(false);
setRecordStreamEnabled(true);
}
void PluginApplication::createCommandLineDescription() {
Seiscomp::Client::StreamApplication::createCommandLineDescription();
commandline().addGroup("Output");
commandline().addOption("Output", "addr,a",
"Data output address\n"
commandline().addOption("Output", "output,O",
"Data output address. Format:\n"
"[[caps|capss]://][user:pass@]host[:port]", &_strAddr);
commandline().addOption("Output", "agent",
"Sets the agent string. Allows "
"the server to identify the "
"application that sends data.",
&_agent);
commandline().addOption("Output", "buffer-size,b",
"Size (bytes) of the packet buffer", &_bufferSize);
commandline().addOption("Output", "backfilling",
@@ -133,9 +170,11 @@ void PluginApplication::createCommandLineDescription() {
commandline().addOption("Output", "rec-len", "MiniSEED record length expressed as a power of 2."
"A 512 byte record would be 9.",
&_mseedRecordLength);
commandline().addOption("Output", "max-future-endtime", "Maximum allowed relative end time for packets. If "
commandline().addOption("Output", "max-future-endtime",
"Maximum allowed relative end time for packets. If "
"the packet end time is greater than the current time plus this "
"value the packet will be discarded. By default this value is set to 120 seconds.",
"value the packet will be discarded. By default this value is set to 120 seconds. "
"A negative value disables the check.",
&_maxFutureEndTime);
commandline().addOption("Output", "dump-packets", "Dump packets to stdout");
commandline().addGroup("Journal");
@@ -154,6 +193,14 @@ void PluginApplication::createCommandLineDescription() {
commandline().addOption("Status", "status-flush", "Flush status every n "
"seconds to disk",
&_statusFlushInterval);
commandline().addGroup("Host");
commandline().addOption("Host", "host-storage",
"Determine disc capacity and available space from this path",
&_host.storage);
commandline().addOption("Host", "host-os",
"Set host operating system information",
&_host.os);
}
void PluginApplication::done() {
@@ -169,6 +216,8 @@ void PluginApplication::done() {
_stats.endTime.valid()?_stats.endTime.iso().c_str():"",
_stats.files);
_plugin.close();
Seiscomp::Client::StreamApplication::done();
}
@@ -179,17 +228,29 @@ void PluginApplication::exit(int returnCode) {
}
void PluginApplication::handleTimeout() {
sc::Time time = sc::Time::GMT().toLocalTime();
auto time = Time::GMT();
auto seconds = time.seconds();
if ( _logStatus && (seconds % _statusFlushInterval == 0) ) {
Plugin::Stats stats = _plugin.stats();
_statusFile.stream() << time.toString("%Y/%m/%d %T") << " " << stats.maxBytesBuffered << endl;
_statusFile.stream() << time.toLocalTime().toString("%Y/%m/%d %T") << " "
<< stats.maxBytesBuffered << std::endl;
_plugin.resetMaxBytesBuffered();
}
if ( seconds % 3 == 0 ) {
updateRuntimeInfo();
}
if ( seconds % 10 == 0 ) {
sendRuntimeInfo();
}
}
bool PluginApplication::init() {
if ( !Seiscomp::Client::StreamApplication::init() ) return false;
if ( !Seiscomp::Client::StreamApplication::init() ) {
return false;
}
// Setup log handlers
Gempa::CAPS::SetLogHandler(Gempa::CAPS::LL_ERROR, LogError);
@@ -198,12 +259,31 @@ bool PluginApplication::init() {
Gempa::CAPS::SetLogHandler(Gempa::CAPS::LL_INFO, LogInfo);
Gempa::CAPS::SetLogHandler(Gempa::CAPS::LL_DEBUG, LogDebug);
Plugin::HostInfo hostInfo;
if ( !getHostInfo(hostInfo) ) {
return false;
}
_plugin.setBufferSize(_bufferSize);
_plugin.setFlushInterval(_flushInterval);
_plugin.setConnectionTimeout(_connectionTimeout);
_plugin.setTimeouts(_ackTimeout, _lastAckTimeout, _sendTimeout);
_plugin.setMaxFutureEndTime(_maxFutureEndTime);
_plugin.dumpPackets(_dumpPackets);
_plugin.setAgent(_agent);
_plugin.setHostInfo(hostInfo);
LogInfo("CAPS connection settings\n"
" Output CAPS server : %s\n"
" Buffer size : %zu bytes\n"
" Backfilling buffer size : %zu s\n"
" Max future end time : %d s\n"
" Connection timeout : %d s\n"
" Timeouts Ack/LastAck/Send : %d s/%d s/%d s\n"
" Agent : %s\n"
" Agent version : %s",
_strAddr.c_str(), _bufferSize, _backfillingBufferSize, _maxFutureEndTime,
_connectionTimeout, _ackTimeout, _lastAckTimeout, _sendTimeout,
hostInfo.agent.data(), hostInfo.agentVersion.data());
if ( _mseedEnabled ) {
MSEEDEncoderFactory *factory = nullptr;
@@ -238,7 +318,13 @@ bool PluginApplication::init() {
if ( _backfillingBufferSize > 0 ) {
_plugin.setBackfillingBufferSize(_backfillingBufferSize);
LogInfo("Backfilling buffer size set to %d", _backfillingBufferSize);
}
std::string connectionIDFile = "@ROOTDIR@/var/run/" + name() + "/id";
connectionIDFile = Seiscomp::Environment::Instance()->absolutePath(connectionIDFile);
LogInfo("Reading connection ID from %s", connectionIDFile.c_str());
if ( !_plugin.setConnectionIDFile(connectionIDFile) ) {
return false;
}
if ( !_journalFile.empty() ) {
@@ -251,20 +337,25 @@ bool PluginApplication::init() {
}
if ( _logStatus ) {
string filename = Seiscomp::Environment::Instance()->logDir() + "/" + SCCoreApp->name() + "-stats.log";
std::string filename = Seiscomp::Environment::Instance()->logDir() + "/" + SCCoreApp->name() + "-stats.log";
if ( !_statusFile.open(filename.c_str()) ) {
LogError("Could not open status file %s.", filename.c_str());
return false;
}
enableTimer(_statusFlushInterval);
}
// This causes a connect
updateRuntimeInfo();
_plugin.setRuntimeInfo(_runtimeInfo);
enableTimer(1);
return true;
}
bool PluginApplication::initConfiguration() {
if ( !Seiscomp::Client::StreamApplication::initConfiguration() ) return false;
if ( !Seiscomp::Client::StreamApplication::initConfiguration() ) {
return false;
}
try {
_plugin.setHost(configGetString("output.host"));
@@ -280,8 +371,11 @@ bool PluginApplication::initConfiguration() {
try { _sendTimeout = configGetInt("output.timeout"); }
catch ( ... ) { }
try { _connectionTimeout = configGetInt("output.connectionTimeout"); }
catch ( ... ) { }
try {
string addr = configGetString("output.address");
std::string addr = configGetString("output.address");
if ( !_plugin.setAddress(addr) ) {
return false;
}
@@ -305,8 +399,10 @@ bool PluginApplication::initConfiguration() {
catch ( ... ) {}
try {
string str = configGetString("output.mseed.encoding");
if ( !fromString(_mseedEncoding, str)) return false;
std::string str = configGetString("output.mseed.encoding");
if ( !fromString(_mseedEncoding, str)) {
return false;
}
}
catch ( ... ) {}
@@ -319,16 +415,6 @@ bool PluginApplication::initConfiguration() {
try { _maxFutureEndTime = configGetInt("output.maxFutureEndTime"); }
catch ( ... ) { }
try { _agent = configGetString("output.agent"); }
catch ( ... ) {
if ( version() ) {
_agent = name() + string(" ") + version();
}
else {
_agent = name();
}
}
try { _journalFile = configGetString("journal.file"); }
catch ( ... ) {}
@@ -341,6 +427,14 @@ bool PluginApplication::initConfiguration() {
try { _lastAckTimeout = configGetInt("journal.waitForLastAck"); }
catch ( ... ) { }
_host.agent = name();
try { _host.storage = configGetString("host.storage"); }
catch ( ... ) { }
try { _host.os = configGetString("host.os"); }
catch ( ... ) { }
try { _logStatus = configGetBool("statusLog.enable"); }
catch ( ... ) { }
@@ -351,7 +445,9 @@ bool PluginApplication::initConfiguration() {
}
bool PluginApplication::validateParameters() {
if ( !Seiscomp::Client::StreamApplication::validateParameters() ) return false;
if ( !Seiscomp::Client::StreamApplication::validateParameters() ) {
return false;
}
if ( commandline().hasOption("mseed") ) {
_mseedEnabled = true;
@@ -366,7 +462,9 @@ bool PluginApplication::validateParameters() {
}
if ( commandline().hasOption("encoding") ) {
if ( !fromString(_mseedEncoding, _strMseedEncoding)) return false;
if ( !fromString(_mseedEncoding, _strMseedEncoding)) {
return false;
}
}
if ( _bufferSize < MIN_BUFFER_SIZE ) {
@@ -375,23 +473,28 @@ bool PluginApplication::validateParameters() {
return false;
}
if ( commandline().hasOption("addr") ) {
if ( !_plugin.setAddress(_strAddr, DEFAULT_PORT) ) {
if ( commandline().hasOption("output") ) {
if ( !_plugin.setAddress(_strAddr, 18003) ) {
return false;
}
}
_host.storage = Seiscomp::Environment::Instance()->absolutePath(_host.storage);
return true;
}
bool PluginApplication::fromString(MseedEncoding &enc, string str) {
bool PluginApplication::fromString(MseedEncoding &enc, std::string str) {
boost::to_lower(str);
if( str == "uncompressed" )
if( str == "uncompressed" ) {
enc = Uncompressed;
else if ( str == "steim1" )
}
else if ( str == "steim1" ) {
enc = Steim1;
else if ( str == "steim2" )
}
else if ( str == "steim2" ) {
enc = Steim2;
}
else {
SEISCOMP_ERROR("Unsupported encoding %s", str.c_str());
return false;
@@ -400,5 +503,60 @@ bool PluginApplication::fromString(MseedEncoding &enc, string str) {
return true;
}
bool PluginApplication::getHostInfo(Plugin::HostInfo &hostInfo) {
hostInfo.agent = _host.agent;
hostInfo.agentVersion = version() ? version() : "";
hostInfo.totalMem = _hostInfo.totalMemory();
try {
auto spaceInfo = fs::space(_host.storage);
hostInfo.totalDisc = spaceInfo.capacity / 1024;
}
catch ( const fs::filesystem_error& e ) {
SEISCOMP_ERROR("Failed to determine filesystem information: %s", e.what());
return false;
}
hostInfo.os = _host.os;
if ( hostInfo.os.empty() ) {
std::map<std::string, std::string> osRelease;
if ( !readKeyValueFile(osRelease, "/etc/os-release", "=") ) {
SEISCOMP_WARNING("Unable to read file /etc/os-release");
}
auto it = osRelease.find("PRETTY_NAME");
if ( it != osRelease.end() ) {
auto unquote = [](const std::string& str) {
if ( str.length() >= 2 && str.front() == '"' && str.back() == '"' ) {
return str.substr(1, str.length() - 2);
}
return str;
};
hostInfo.os = unquote(it->second);
}
}
return true;
}
void PluginApplication::updateRuntimeInfo() {
_runtimeInfo.cpuUsage = std::max(0, static_cast<int>(_hostInfo.getCurrentCpuUsage() * 1000));
_runtimeInfo.procUsedMem = _hostInfo.getCurrentMemoryUsage();
_runtimeInfo.availableMem = getAvailableMemory();
getloadavg(&_runtimeInfo.systemLoad, 1);
try {
auto spaceInfo = fs::space(_host.storage);
_runtimeInfo.availableDisc = spaceInfo.available / 1024;
}
catch ( const fs::filesystem_error& e ) {
SEISCOMP_WARNING("Failed to determine filesystem information: %s", e.what());
}
}
void PluginApplication::sendRuntimeInfo() {
_plugin.setRuntimeInfo(_runtimeInfo);
}
}

View File

@@ -16,98 +16,153 @@
#ifndef GEMPA_CAPS_PLUGINAPPLICATION_H
#define GEMPA_CAPS_PLUGINAPPLICATION_H
#include <gempa/caps/api.h>
#include <gempa/caps/plugin.h>
#include <seiscomp3/client/streamapplication.h>
#include <seiscomp3/logging/filerotator.h>
#include <seiscomp3/utils/timer.h>
#include <seiscomp/core/version.h>
#include <seiscomp/client/streamapplication.h>
#if SC_API_VERSION < SC_API_VERSION_CHECK(17, 0, 0)
#include <seiscomp/logging/filerotator.h>
#else
#include <seiscomp/logging/output/filerotator.h>
#endif
#include <seiscomp/system/hostinfo.h>
#include <seiscomp/utils/timer.h>
namespace Gempa::CAPS {
namespace Gempa {
namespace CAPS {
class SC_GEMPA_CAPS_API PluginApplication : public Seiscomp::Client::StreamApplication {
public:
PluginApplication(int argc, char **argv,
const std::string &desc = "");
const std::string &name = "");
protected:
struct Statistics {
Statistics() = default;
uint32_t records{0};
uint32_t samples{0};
Time startTime;
Time endTime;
uint32_t gaps{0};
uint32_t files{0};
};
/**
* @brief The MseedEncoding enum defines supported MSEED encodings.
*/
enum MseedEncoding {Uncompressed, Steim1, Steim2};
/**
* @brief Adds common plugin commandline options.
*/
virtual void createCommandLineDescription();
void createCommandLineDescription() override;
virtual void done();
/**
* @brief Cleanup method called before exec() returns.
*/
void done() override;
virtual void exit(int returnCode);
/**
* @brief This method is called before the applications exits.
* @param returnCode The exit code
*/
void exit(int returnCode) override;
virtual void handleRecord(Seiscomp::Record *record) {}
virtual void handleTimeout();
/**
* @brief Handles records. This is dummy implemenation as the base
* class requires to implement the method.
* @param record Pointer to record
*/
void handleRecord(Seiscomp::Record *record) override {}
/**
* @brief Handles timer events. For instance writes the plugin states
* information to file.
*/
void handleTimeout() override;
/**
* @brief Initialization method.
*/
virtual bool init();
bool init() override;
/**
* @brief Reads common plugin configuration options.
*/
virtual bool initConfiguration();
bool initConfiguration() override;
/**
* @brief Validates command line parameters.
*/
virtual bool validateParameters();
bool validateParameters() override;
protected:
struct Statistics {
uint32_t records;
uint32_t samples;
Time startTime;
Time endTime;
uint32_t gaps;
uint32_t files;
Statistics() : records(0), samples(0), gaps(0), files(0) {}
};
enum MseedEncoding {Uncompressed, Steim1, Steim2};
bool fromString(MseedEncoding &enc, std::string str);
protected:
class FileRotator : public Seiscomp::Logging::FileRotatorOutput {
public:
std::ofstream& stream() { return _stream; }
};
/**
* @brief Get MSEED encoding value from string
* @param enc The encoding
* @param str The encoding as string
* @return True on success
*/
static bool fromString(MseedEncoding &enc, std::string str);
/**
* @brief Get host information like total mem or OS
* @param hostInfo Host info struct
* @return True on success
*/
bool getHostInfo(Plugin::HostInfo &hostInfo);
/**
* @brief Get runtime information
*/
void updateRuntimeInfo();
virtual void sendRuntimeInfo();
protected:
struct Host {
std::string agent;
std::string storage{"@LOGDIR@"};
std::string os;
};
Plugin _plugin;
std::string _strAddr;
size_t _bufferSize;
size_t _backfillingBufferSize;
std::string _strAddr{"localhost:18003"};
size_t _bufferSize{1 << 20};
size_t _backfillingBufferSize{180};
bool _mseed;
std::string _journalFile;
int _flushInterval;
int _ackTimeout;
int _lastAckTimeout;
int _sendTimeout;
int _maxFutureEndTime;
int _flushInterval{10};
int _ackTimeout{60};
int _lastAckTimeout{5};
int _sendTimeout{60};
int _connectionTimeout{30};
int _maxFutureEndTime{120};
Statistics _stats;
bool _mseedEnabled;
MseedEncoding _mseedEncoding;
uint _mseedRecordLength;
std::string _strMseedEncoding;
bool _mseedEnabled{false};
MseedEncoding _mseedEncoding{Steim2};
uint _mseedRecordLength{9};
std::string _strMseedEncoding{"Steim2"};
std::string _strPacketType;
Seiscomp::Util::Timer _timer;
FileRotator _statusFile;
bool _logStatus;
uint _statusFlushInterval;
bool _dumpPackets;
std::string _agent;
bool _logStatus{false};
uint _statusFlushInterval{10};
bool _dumpPackets{false};
Seiscomp::System::HostInfo _hostInfo;
Host _host;
Plugin::RuntimeInfo _runtimeInfo;
};
}
}

View File

@@ -16,18 +16,28 @@
#ifndef GEMPA_CAPS_PLUGINPACKET_H
#define GEMPA_CAPS_PLUGINPACKET_H
#include "mseed/encoder.h"
#include "packet.h"
namespace Gempa {
namespace CAPS {
struct Packet {
Packet() : timingQuality(-1) {}
Packet(DataRecordPtr rec,
const std::string &networkCode, const std::string &stationCode,
const std::string &locationCode, const std::string &channelCode)
: record(rec), networkCode(networkCode), stationCode(stationCode),
locationCode(locationCode), channelCode(channelCode), timingQuality(-1) {
const std::string &locationCode, const std::string &channelCode,
void *context = nullptr)
: record(rec)
, networkCode(networkCode)
, stationCode(stationCode)
, locationCode(locationCode)
, channelCode(channelCode)
, timingQuality(-1)
, context(context)
{
streamID = networkCode + "." + stationCode + "." +
locationCode + "." + channelCode;
}
@@ -42,8 +52,8 @@ struct Packet {
return packet;
}
typedef std::vector<char> Buffer;
typedef boost::shared_ptr<Buffer> BufferPtr;
using Buffer = std::vector<char>;
using BufferPtr = boost::shared_ptr<Buffer>;
BufferPtr buffer; // PacketDataHeader, PacketHeader[V1|V2], Data
DataRecordPtr record;
@@ -58,12 +68,17 @@ struct Packet {
#endif
std::string uom;
int timingQuality;
void *context;
size_t size() const { return buffer->size(); }
};
typedef boost::shared_ptr<Packet> PacketPtr;
using PacketPtr = boost::shared_ptr<Packet>;
}
}
#endif

View File

@@ -83,6 +83,10 @@ inline Time getEndTime(const Time &stime, size_t count, const DataRecord::Header
RawDataRecord::RawDataRecord() : _dataOfs(0), _dataSize(0), _dirty(false) {}
DataRecord *RawDataRecord::clone() const {
return new RawDataRecord(*this);
}
const char *RawDataRecord::formatName() const {
DT2STR("RAW/", _currentHeader.dataType)
}
@@ -258,11 +262,13 @@ DataRecord::ReadStatus RawDataRecord::getData(streambuf &buf, int size,
if ( (start.valid() || end.valid()) && _endTime.valid() ) {
// Out of bounds?
if ( end.valid() && (end <= _startTime) )
if ( end.valid() && (end <= _startTime) ) {
return RS_AfterTimeWindow;
}
if ( start.valid() && (start >= _endTime) )
if ( start.valid() && (start >= _endTime) ) {
return RS_BeforeTimeWindow;
}
// Trim packet front
if ( _startTime < start ) {
@@ -284,7 +290,7 @@ DataRecord::ReadStatus RawDataRecord::getData(streambuf &buf, int size,
// return a partial record
if ( maxSamples < sampleCount ) {
CAPS_DEBUG("Clip %d available samples to %d", sampleCount, maxSamples);
_endTime -= samplesToTimeSpan(_header, sampleCount-maxSamples);
_endTime -= samplesToTimeSpan(_header, sampleCount - maxSamples);
sampleCount = maxSamples;
partial = true;
}
@@ -314,7 +320,9 @@ DataRecord::ReadStatus RawDataRecord::getData(streambuf &buf, int size,
{
// Stay with little endian data
RIFF::VectorChunk<1,false> dataChunk(_data, sampleOfs, sampleCount);
if ( !dataChunk.get(buf, size) ) return RS_Error;
if ( !dataChunk.get(buf, size) ) {
return RS_Error;
}
}
break;
@@ -322,7 +330,9 @@ DataRecord::ReadStatus RawDataRecord::getData(streambuf &buf, int size,
{
// Stay with little endian data
RIFF::VectorChunk<2,false> dataChunk(_data, sampleOfs, sampleCount);
if ( !dataChunk.get(buf, size) ) return RS_Error;
if ( !dataChunk.get(buf, size) ) {
return RS_Error;
}
}
break;
@@ -331,7 +341,9 @@ DataRecord::ReadStatus RawDataRecord::getData(streambuf &buf, int size,
{
// Stay with little endian data
RIFF::VectorChunk<4,false> dataChunk(_data, sampleOfs, sampleCount);
if ( !dataChunk.get(buf, size) ) return RS_Error;
if ( !dataChunk.get(buf, size) ) {
return RS_Error;
}
}
break;
@@ -340,7 +352,9 @@ DataRecord::ReadStatus RawDataRecord::getData(streambuf &buf, int size,
{
// Stay with little endian data
RIFF::VectorChunk<8,false> dataChunk(_data, sampleOfs, sampleCount);
if ( !dataChunk.get(buf, size) ) return RS_Error;
if ( !dataChunk.get(buf, size) ) {
return RS_Error;
}
}
break;
@@ -354,7 +368,7 @@ DataRecord::ReadStatus RawDataRecord::getData(streambuf &buf, int size,
_dataOfs = 0;
_dataSize = _data.size();
return partial?RS_Partial:RS_Complete;
return partial ? RS_Partial : RS_Complete;
}
bool RawDataRecord::put(std::streambuf &buf, bool withHeader) const {

View File

@@ -18,12 +18,12 @@
#include <gempa/caps/packet.h>
#include <vector>
namespace Gempa {
namespace CAPS {
struct SC_GEMPA_CAPS_API RawResponseHeader {
int64_t timeSeconds;
int32_t timeMicroSeconds;
@@ -47,7 +47,8 @@ class RawDataRecord : public DataRecord {
public:
RawDataRecord();
const char *formatName() const;
DataRecord *clone() const override;
const char *formatName() const override;
/**
* @brief Reads metadata from data record header
@@ -59,7 +60,7 @@ class RawDataRecord : public DataRecord {
*/
bool readMetaData(std::streambuf &buf, int size,
Header &header,
Time &startTime, Time &endTime);
Time &startTime, Time &endTime) override;
void setHeader(const Header &header);
@@ -67,33 +68,33 @@ class RawDataRecord : public DataRecord {
* @brief Returns the meta information of the data if supported
* @return The data record header
*/
const Header *header() const;
const Header *header() const override;
/**
* @brief Returns the start time of the record
* @return The start time
*/
Time startTime() const;
Time startTime() const override;
/**
* @brief Returns the end time of the record
* @return The end time
*/
Time endTime() const;
Time endTime() const override;
/**
* @brief canTrim checks if data trimming is possible
* without modifying preceding data
* @return True, if data trimming is possible
*/
bool canTrim() const;
bool canTrim() const override;
/**
* @brief canMerge checks if data merging is possible
* without modifying preceding data
* @return True, if data merging is possible
*/
bool canMerge() const;
bool canMerge() const override;
/**
* @brief Trims a record to start and end time. Trimming
@@ -109,7 +110,7 @@ class RawDataRecord : public DataRecord {
* @return It returns true if trimming has been done or false
* if an error occured or trimming is not supported.
*/
bool trim(const Time &start, const Time &end) const;
bool trim(const Time &start, const Time &end) const override;
/**
* @brief Returns the data size in bytes if the current state would
@@ -118,7 +119,7 @@ class RawDataRecord : public DataRecord {
* @param withHeader Take header into account
* @return Returns the data size in bytes
*/
size_t dataSize(bool withHeader) const;
size_t dataSize(bool withHeader) const override;
/**
* @brief Reads the packet data including header from a streambuf
@@ -137,7 +138,7 @@ class RawDataRecord : public DataRecord {
ReadStatus get(std::streambuf &buf, int size,
const Time &start = Time(),
const Time &end = Time(),
int maxBytes = -1);
int maxBytes = -1) override;
/**
* @brief Reads the packet data without header from a streambuf
@@ -167,13 +168,13 @@ class RawDataRecord : public DataRecord {
* @param Take header into account
* @return True, if the data has been written
*/
bool put(std::streambuf &buf, bool withHeader) const;
bool put(std::streambuf &buf, bool withHeader) const override;
/**
* @brief Returns the packet type
* @return The packet type
*/
PacketType packetType() const { return RawDataPacket; }
PacketType packetType() const override { return RawDataPacket; }
/**
* @brief Sets the start time of the record
@@ -218,18 +219,19 @@ class RawDataRecord : public DataRecord {
class FixedRawDataRecord : public RawDataRecord {
public:
virtual bool canTrim() const { return false; }
virtual bool canMerge() const { return false; }
virtual bool trim(const Time &start, const Time &end) const { return false; }
virtual const char *formatName() const;
virtual ReadStatus get(std::streambuf &buf, int size,
bool canTrim() const override { return false; }
bool canMerge() const override { return false; }
bool trim(const Time &start, const Time &end) const override { return false; }
const char *formatName() const override;
ReadStatus get(std::streambuf &buf, int size,
const Time &/*start*/, const Time &/*end*/,
int maxBytes) {
int maxBytes) override {
return RawDataRecord::get(buf, size, Time(), Time(), maxBytes);
}
PacketType packetType() const { return FixedRawDataPacket; }
PacketType packetType() const override { return FixedRawDataPacket; }
};
}
}

View File

@@ -16,15 +16,18 @@
#ifndef GEMPA_CAPS_RECORD_SAMPLER_H
#define GEMPA_CAPS_RECORD_SAMPLER_H
#include <gempa/caps/datetime.h>
#include <gempa/caps/datetime.h>
#include <boost/shared_ptr.hpp>
#include <functional>
#include <vector>
namespace Gempa {
namespace CAPS {
template<typename T> class RecordBuilder {
public:
struct Record {
@@ -40,7 +43,8 @@ template<typename T> class RecordBuilder {
void *userData;
};
typedef boost::shared_ptr<Record> RecordPtr;
typedef boost::function<void (Record *rec, void*)> FlushCallback;
typedef std::function<void (Record *rec, void*)> FlushCallback;
public:
RecordBuilder() : _bufSize(64) {}
@@ -130,6 +134,7 @@ template<typename T> class RecordBuilder {
void *_userData;
};
}
}

View File

@@ -61,6 +61,11 @@ void RTCM2DataRecord::setSamplingFrequency(uint16_t numerator, uint16_t denomina
}
DataRecord *RTCM2DataRecord::clone() const {
return new RTCM2DataRecord(*this);
}
const char *RTCM2DataRecord::formatName() const {
return "RTC2";
}

View File

@@ -20,11 +20,11 @@
#include <gempa/caps/endianess.h>
#include <gempa/caps/packet.h>
#include <vector>
namespace Gempa {
namespace CAPS {
class RTCM2DataRecord : public DataRecord {
public:
struct RTCM2Header : Header {
@@ -61,37 +61,38 @@ class RTCM2DataRecord : public DataRecord {
RTCM2DataRecord();
const char* formatName() const;
DataRecord *clone() const override;
const char* formatName() const override;
void setTimeStamp(const Time &ts);
void setSamplingFrequency(uint16_t numerator, uint16_t denominator);
virtual bool readMetaData(std::streambuf &buf, int size,
bool readMetaData(std::streambuf &buf, int size,
Header &header,
Time &startTime,
Time &endTime);
Time &endTime) override;
virtual const Header *header() const;
virtual Time startTime() const;
virtual Time endTime() const;
const Header *header() const override;
Time startTime() const override;
Time endTime() const override;
virtual bool canTrim() const;
virtual bool canMerge() const;
bool canTrim() const override;
bool canMerge() const override;
virtual bool trim(const Time &start,
const Time &end) const;
bool trim(const Time &start,
const Time &end) const override;
virtual size_t dataSize(bool withHeader) const;
size_t dataSize(bool withHeader) const override;
virtual ReadStatus get(std::streambuf &buf, int size,
ReadStatus get(std::streambuf &buf, int size,
const Time &start,
const Time &end,
int maxSize = -1);
int maxSize = -1) override;
virtual bool put(std::streambuf &buf, bool withHeader) const;
bool put(std::streambuf &buf, bool withHeader) const override;
PacketType packetType() const { return RTCM2Packet; }
PacketType packetType() const override { return RTCM2Packet; }
private:

View File

@@ -16,9 +16,11 @@
#ifndef GEMPA_CAPS_SESSIONTABLE_H
#define GEMPA_CAPS_SESSIONTABLE_H
#include "packet.h"
#include <map>
#include <optional>
#include <string>
#include <functional>
@@ -26,6 +28,7 @@
namespace Gempa {
namespace CAPS {
struct SessionTableItem {
SessionTableItem() : samplingFrequency(0), samplingFrequencyDivider(0),
fSamplingFrequency(0.0), dataType(DT_Unknown),
@@ -42,8 +45,8 @@ struct SessionTableItem {
DataType dataType;
int dataSize;
UOM uom;
Time startTime;
Time endTime;
std::optional<Time> startTime;
std::optional<Time> endTime;
void *userData;
bool splitStreamID();
@@ -54,7 +57,7 @@ class SC_GEMPA_CAPS_API SessionTable : public std::map<int, SessionTableItem> {
public:
enum Status {Success, Error, EOD};
typedef std::function<void (SessionTableItem*)> CallbackFunc;
using CallbackFunc = std::function<void (SessionTableItem*)>;
public:
//! Default constructor
@@ -97,6 +100,7 @@ class SC_GEMPA_CAPS_API SessionTable : public std::map<int, SessionTableItem> {
CallbackFunc _itemAboutToBeRemovedFunc;
};
}
}

View File

@@ -230,7 +230,7 @@ Socket::Device::Status Socket::setNonBlocking(bool nb) {
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Socket::Status Socket::connect(const std::string &hostname, uint16_t port) {
Socket::Status Socket::connect(const std::string &hostname, uint16_t port, int timeout) {
if ( _fd != -1 ) {
//CAPS_WARNING("closing stale socket");
close();
@@ -250,7 +250,7 @@ Socket::Status Socket::connect(const std::string &hostname, uint16_t port) {
int ret = getaddrinfo(hostname.c_str(), strPort.c_str(), &hints, &res);
if ( ret ) {
CAPS_DEBUG("Test3 Socket::connect(%s:%d): %s",
CAPS_DEBUG("Socket::connect(%s:%d): %s",
hostname.c_str(), port,
#ifndef WIN32
strerror(errno));
@@ -271,14 +271,46 @@ Socket::Status Socket::connect(const std::string &hostname, uint16_t port) {
}
#ifndef WIN32
if ( ::connect(_fd, (struct sockaddr *)&addr, addrlen) == -1 ) {
if ( errno != EINPROGRESS ) {
setNonBlocking(true);
int res2 = ::connect(_fd, &addr, addrlen);
if ( res2 == -1 ) {
if ( errno == EINPROGRESS || errno == EAGAIN ) {
fd_set wfds;
FD_ZERO(&wfds);
FD_SET(_fd, &wfds);
struct timeval tv{timeout, 0};
int res = select(_fd + 1, nullptr, &wfds, nullptr, &tv);
if ( res == -1 ) {
close();
return ConnectError;
}
else if ( res ) {
res = ::connect(_fd, &addr, addrlen);
if ( res == -1 ) {
close();
CAPS_DEBUG("Socket::connect(%s:%d): %s",
hostname.c_str(), port, strerror(errno));
return ConnectError;
}
}
else {
close();
return Timeout;
}
}
else {
/*CAPS_DEBUG("Socket::connect(%s:%d): %s",
hostname.c_str(), port, strerror(errno));*/
close();
return errno == ETIMEDOUT?Timeout:ConnectError;
return errno == ETIMEDOUT ? Timeout : ConnectError;
}
}
setNonBlocking(false);
#else
if ( ::connect(_fd, (struct sockaddr *)&addr, addrlen) == SOCKET_ERROR ) {
int err = WSAGetLastError();
@@ -445,7 +477,8 @@ int SSLSocket::read(char *data, int len) {
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Socket::Status SSLSocket::connect(const std::string &hostname, uint16_t port) {
Socket::Status SSLSocket::connect(const std::string &hostname, uint16_t port,
int timeout) {
cleanUp();
_ctx = SSL_CTX_new(SSLv23_client_method());
@@ -469,14 +502,52 @@ Socket::Status SSLSocket::connect(const std::string &hostname, uint16_t port) {
SSL_set_fd(_ssl, _fd);
SSL_set_shutdown(_ssl, 0);
SSL_set_connect_state(_ssl);
int err = SSL_connect(_ssl);
if ( err < 0 ) {
CAPS_ERROR("Failed to connect with SSL, error %d",
SSL_get_error(_ssl, err));
setNonBlocking(true);
int res = 0;
while ( (res = SSL_connect(_ssl)) != 1 ) {
fd_set fds;
FD_ZERO(&fds);
FD_SET(_fd, &fds);
struct timeval tv{timeout, 0};
switch ( SSL_get_error(_ssl, res) ) {
case SSL_ERROR_WANT_READ:
res = select(_fd + 1, &fds, nullptr, nullptr, &tv);
if ( res == 0 ) {
close();
return Timeout;
}
else if ( res == - 1 ) {
close();
return ConnectError;
}
break;
case SSL_ERROR_WANT_WRITE:
res = select(_fd + 1, nullptr, &fds, nullptr, &tv);
if ( res == 0 ) {
close();
return Timeout;
}
else if ( res == - 1 ) {
close();
return ConnectError;
}
break;
default: {
CAPS_ERROR("Failed to connect with SSL, error %d",
SSL_get_error(_ssl, res));
close();
return ConnectError;
}
}
}
setNonBlocking(false);
return Success;
}
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

View File

@@ -22,14 +22,14 @@
#include <openssl/ssl.h>
#include <stdint.h>
#include <cstdint>
#include <streambuf>
#include <iostream>
#include <fstream>
namespace Gempa {
namespace CAPS {
class SC_GEMPA_CAPS_API Socket {
public:
typedef uint64_t count_t;
@@ -89,7 +89,8 @@ class SC_GEMPA_CAPS_API Socket {
Device::Status setNonBlocking(bool nb);
virtual Status connect(const std::string &hostname, uint16_t port);
virtual Status connect(const std::string &hostname, uint16_t port,
int timeout = 30);
count_t rx() const { return _bytesReceived; }
count_t tx() const { return _bytesSent; }
@@ -122,7 +123,7 @@ class SSLSocket : public Socket {
int write(const char *data, int len);
int read(char *data, int len);
Status connect(const std::string &hostname, uint16_t port);
Status connect(const std::string &hostname, uint16_t port, int timeout = 30);
virtual const unsigned char *sessionID() const;
virtual unsigned int sessionIDLength() const;
@@ -161,10 +162,6 @@ class socketbuf : public std::streambuf {
_sock = sock;
}
void settimeout(const struct timeval &tv) {
_timeout = tv;
}
void set_read_limit(int bytes) {
_allowed_reads = bytes;
@@ -258,7 +255,6 @@ class socketbuf : public std::streambuf {
private:
T *_sock;
timeval _timeout;
char _in[N];
char _out[N];
bool _block_write;
@@ -266,6 +262,7 @@ class socketbuf : public std::streambuf {
int _allowed_reads;
};
}
}

View File

@@ -14,9 +14,12 @@
#ifndef STRPTIME_H
#define STRPTIME_H
struct tm;
/*
* Version of "strptime()", for the benefit of OSes that don't have it.
*/
extern char *strptime(const char *, const char *, struct tm *);
#endif

View File

@@ -6,6 +6,7 @@ SET(TESTS
datetime_time.cpp
endianess.cpp
mseedpacket.cpp
url.cpp
)
FOREACH(testSrc ${TESTS})

View File

@@ -17,7 +17,7 @@
#define SEISCOMP_TEST_MODULE gempa
#include <seiscomp3/unittest/unittests.h>
#include <seiscomp/unittest/unittests.h>
#include <gempa/caps/datetime.h>
#include <string>

View File

@@ -17,7 +17,7 @@
#define SEISCOMP_TEST_MODULE gempa
#include <seiscomp3/unittest/unittests.h>
#include <seiscomp/unittest/unittests.h>
#include <gempa/caps/datetime.h>
@@ -146,9 +146,9 @@ BOOST_AUTO_TEST_CASE(construction) {
// date
gc::Time date(1971,1,3,1,1,4,6544);
double dayInSeconds = 86400;
double yearInSeconds = 31536000;
BOOST_WARN_CLOSE(double(date), dayInSeconds*2 + yearInSeconds,0.3);
double secondsPerDay = 86400;
double secondsPerYear = 31536000;
BOOST_WARN_CLOSE(double(date), secondsPerDay*2 + secondsPerYear,0.3);
}
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
@@ -393,13 +393,14 @@ BOOST_AUTO_TEST_CASE(setAndGet) {
BOOST_CHECK_EQUAL(year, 1965);
BOOST_CHECK_EQUAL(yday , 0);
gc::Time before1900;
day = 28, month = 2, year = 1900;
before1900.set(year,month,day,h,min,sec,uSec);
BOOST_CHECK(before1900.get(&year,&month,&day,&h,&min,&sec,&uSec) == true);
BOOST_CHECK_EQUAL(year , 1900);
BOOST_CHECK_EQUAL(day , 28);
BOOST_CHECK_EQUAL(month, 2);
// Won't work on 32-bit systems
// gc::Time before1900;
// day = 28, month = 2, year = 1900;
// before1900.set(year,month,day,h,min,sec,uSec);
// BOOST_CHECK(before1900.get(&year,&month,&day,&h,&min,&sec,&uSec) == true);
// BOOST_CHECK_EQUAL(year , 1900);
// BOOST_CHECK_EQUAL(day , 28);
// BOOST_CHECK_EQUAL(month, 2);
gc::Time pure;
pure.get(&year,&month,&day,&h,&min,&sec,&uSec);
@@ -409,12 +410,12 @@ BOOST_AUTO_TEST_CASE(setAndGet) {
pure.get(&year,&month,&day,&h,&min,&sec,&uSec);
BOOST_CHECK_EQUAL(year, 1969);
day = 50, month = 4, year = 1566;
before1900.set(year,month,day,h,min,sec,uSec);
BOOST_CHECK(before1900.get(&year,&month,&day,&h,&min,&sec,&uSec) == true);
BOOST_CHECK_EQUAL(year , 1566);
BOOST_CHECK_EQUAL(day , 20);
BOOST_CHECK_EQUAL(month, 5);
// day = 50, month = 4, year = 1566;
// before1900.set(year,month,day,h,min,sec,uSec);
// BOOST_CHECK(before1900.get(&year,&month,&day,&h,&min,&sec,&uSec) == true);
// BOOST_CHECK_EQUAL(year , 1566);
// BOOST_CHECK_EQUAL(day , 20);
// BOOST_CHECK_EQUAL(month, 5);
}
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
@@ -477,9 +478,9 @@ BOOST_AUTO_TEST_CASE(localTime) {
BOOST_CHECK_EQUAL(check1, check2);
gc::Time yearDay = yearDay.FromYearDay(1971, 3);
double dayInSeconds = 86400;
double yearInSeconds = 31536000;
BOOST_CHECK_EQUAL(double(yearDay),dayInSeconds*2 + yearInSeconds);
double secondsPerDay = 86400;
double secondsPerYear = 31536000;
BOOST_CHECK_EQUAL(double(yearDay),secondsPerDay*2 + secondsPerYear);
}
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

View File

@@ -17,7 +17,7 @@
#define SEISCOMP_TEST_MODULE gempa
#include <seiscomp3/unittest/unittests.h>
#include <seiscomp/unittest/unittests.h>
#include <gempa/caps/endianess.h>
#include <gempa/caps/rawpacket.cpp>

View File

@@ -19,7 +19,7 @@
#define SEISCOMP_TEST_MODULE gempa
#include "test_utils.h"
#include <seiscomp3/unittest/unittests.h>
#include <seiscomp/unittest/unittests.h>
#include <gempa/caps/mseedpacket.h>
#include <gempa/caps/rawpacket.cpp>
@@ -27,10 +27,8 @@
#include <gempa/caps/datetime.h>
#include <fstream>
#include <iostream>
#include <math.h>
#include <string>
#include <streambuf>
#include <cmath>
namespace bu = boost::unit_test;

View File

@@ -17,7 +17,7 @@
#define SEISCOMP_TEST_MODULE gempa
#include <seiscomp3/unittest/unittests.h>
#include <seiscomp/unittest/unittests.h>
#include <gempa/caps/packet.h>
#include <gempa/caps/rawpacket.cpp>

View File

@@ -23,7 +23,7 @@
#define SEISCOMP_TEST_MODULE gempa
#include "test_utils.h"
#include <seiscomp3/unittest/unittests.h>
#include <seiscomp/unittest/unittests.h>
#include <gempa/caps/rawpacket.cpp>
#include <gempa/caps/mseedpacket.cpp>

View File

@@ -0,0 +1,101 @@
/***************************************************************************
* Copyright (C) 2021 by gempa GmbH *
* *
* All Rights Reserved. *
* *
* NOTICE: All information contained herein is, and remains *
* the property of gempa GmbH and its suppliers, if any. The intellectual *
* and technical concepts contained herein are proprietary to gempa GmbH *
* and its suppliers. *
* Dissemination of this information or reproduction of this material *
* is strictly forbidden unless prior written permission is obtained *
* from gempa GmbH. *
***************************************************************************/
#define SEISCOMP_TEST_MODULE gempa
#include <seiscomp/unittest/unittests.h>
#include <gempa/caps/url.h>
namespace gc = Gempa::CAPS;
namespace bu = boost::unit_test;
using namespace gc;
using namespace std;
BOOST_AUTO_TEST_SUITE(gempa_common_caps_url)
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
BOOST_AUTO_TEST_CASE(url_construct) {
Url url;
BOOST_CHECK_EQUAL(url.host, "");
BOOST_CHECK_EQUAL(url.user, "");
BOOST_CHECK_EQUAL(url.password, "");
BOOST_CHECK_EQUAL(url.port, 0);
BOOST_CHECK_EQUAL(url.protocol, "");
}
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
BOOST_AUTO_TEST_CASE(url_parse) {
{
Url url;
BOOST_CHECK(url.parse("localhost"));
BOOST_CHECK_EQUAL(url.host, "localhost");
BOOST_CHECK_EQUAL(url.port, 18002);
}
{
Url url;
BOOST_CHECK(url.parse("localhost:18003"));
BOOST_CHECK_EQUAL(url.host, "localhost");
BOOST_CHECK_EQUAL(url.port, 18003);
BOOST_CHECK_EQUAL(url.protocol, "caps");
}
{
Url url;
BOOST_CHECK(!url.parse("localhost:65536"));
BOOST_CHECK(!url.parse("localhost:-1"));
BOOST_CHECK(!url.parse("localhost:0"));
}
{
Url url;
BOOST_CHECK(url.parse("caps:gempa@localhost:18003"));
BOOST_CHECK_EQUAL(url.host, "localhost");
BOOST_CHECK_EQUAL(url.user, "caps");
BOOST_CHECK_EQUAL(url.password, "gempa");
BOOST_CHECK_EQUAL(url.port, 18003);
BOOST_CHECK_EQUAL(url.protocol, "caps");
}
{
Url url;
BOOST_CHECK(url.parse("capss://guest:guest@caps.gempa.de:18008"));
BOOST_CHECK_EQUAL(url.host, "caps.gempa.de");
BOOST_CHECK_EQUAL(url.user, "guest");
BOOST_CHECK_EQUAL(url.password, "guest");
BOOST_CHECK_EQUAL(url.port, 18008);
BOOST_CHECK_EQUAL(url.protocol, "capss");
}
}
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
BOOST_AUTO_TEST_SUITE_END()

View File

@@ -17,7 +17,7 @@
#define SEISCOMP_TEST_MODULE gempa
#include <seiscomp3/unittest/unittests.h>
#include <seiscomp/unittest/unittests.h>
#include <gempa/caps/utils.h>

View File

@@ -16,11 +16,15 @@
#ifndef GEMPA_CAPS_URL_H
#define GEMPA_CAPS_URL_H
#include <string>
#include <cstdint>
namespace Gempa {
namespace CAPS {
struct Url {
Url();
@@ -41,7 +45,9 @@ struct Url {
std::string errorString;
};
}
}
#endif

View File

@@ -16,6 +16,7 @@
#ifndef GEMPA_CAPS_UTILS_H
#define GEMPA_CAPS_UTILS_H
#include <gempa/caps/packet.h>
#include <cerrno>
@@ -32,6 +33,7 @@
namespace Gempa {
namespace CAPS {
class arraybuf : public std::streambuf {
public:
typedef std::streambuf::pos_type pos_type;
@@ -371,7 +373,9 @@ inline uint8_t dataTypeSize(DataType dt) {
return 0;
}
}
}
#endif

View File

@@ -18,12 +18,24 @@
#define LIB_CAPS_VERSION_PATCH(v) (v & 0xff)
/* LIB_CAPS_VERSION is (major << 16) + (minor << 8) + patch. */
#define LIB_CAPS_VERSION 0x010000
#define LIB_CAPS_VERSION_NAME "2.0.0"
#define LIB_CAPS_VERSION 0x030200
#define LIB_CAPS_VERSION_NAME "3.2.0"
/******************************************************************************
API Changelog
******************************************************************************
"3.2.0" 0x030200
- Add Plugin::lastEndTime
- Add Plugin::setConnectionIDFile
- Add Plugin::connectionID
- Add Plugin::setConnectedCallback
- Add Plugin::setDisconnectedCallback
- Add Plugin::setHostInfo
- Add Plugin::setRuntimeInfo
- Add Plugin::pushAny
- Add Plugin::pushRaw
- Add Plugin::pushRecord
- Change Plugin push signature
"2.0.0" 0x020000
- Add Plugin::setAddress
- Add Plugin::flushEncoders

View File

@@ -1,4 +1,4 @@
%feature("notabstract") Gempa::CAPS::AnyDataRecord;
%feature("notabstract") Gempa::CAPS::AnyDataRecord;
%include "stdint.i"
%init
@@ -31,14 +31,13 @@ typedef Gempa::CAPS::Plugin::Buffer Buffer;
const std::string &loc, const std::string &cha,
const Time &stime, int16_t numerator, int16_t denominator,
const std::string &uom,
PyObject *obj, int type
) {
PyObject *obj, int type) {
PyArrayObject *arr = NULL;
Gempa::CAPS::DataType dataType = (Gempa::CAPS::DataType)type;
Gempa::CAPS::Plugin::Status status = Gempa::CAPS::Plugin::PacketLoss;
switch(type) {
switch ( type ) {
case Gempa::CAPS::DT_INT8:
arr = (PyArrayObject*) PyArray_ContiguousFromObject(obj, PyArray_CHAR, 1, 1);
arr = (PyArrayObject*) PyArray_ContiguousFromObject(obj, NPY_INT8, 1, 1);
if ( arr == NULL )
break;
@@ -46,7 +45,7 @@ typedef Gempa::CAPS::Plugin::Buffer Buffer;
uom, (int8_t*)(arr->data), arr->dimensions[0], dataType);
break;
case Gempa::CAPS::DT_INT16:
arr = (PyArrayObject*) PyArray_ContiguousFromObject(obj, PyArray_INT16, 1, 1);
arr = (PyArrayObject*) PyArray_ContiguousFromObject(obj, NPY_INT16, 1, 1);
if ( arr == NULL )
break;
@@ -54,7 +53,7 @@ typedef Gempa::CAPS::Plugin::Buffer Buffer;
uom, (int16_t*)(arr->data), arr->dimensions[0], dataType);
break;
case Gempa::CAPS::DT_INT32:
arr = (PyArrayObject*) PyArray_ContiguousFromObject(obj, PyArray_INT32, 1, 1);
arr = (PyArrayObject*) PyArray_ContiguousFromObject(obj, NPY_INT32, 1, 1);
if ( arr == NULL )
break;
@@ -62,7 +61,7 @@ typedef Gempa::CAPS::Plugin::Buffer Buffer;
uom, (int32_t*)(arr->data), arr->dimensions[0], dataType);
break;
case Gempa::CAPS::DT_FLOAT:
arr = (PyArrayObject*) PyArray_ContiguousFromObject(obj, PyArray_FLOAT32, 1, 1);
arr = (PyArrayObject*) PyArray_ContiguousFromObject(obj, NPY_FLOAT32, 1, 1);
if ( arr == NULL )
break;
@@ -70,7 +69,7 @@ typedef Gempa::CAPS::Plugin::Buffer Buffer;
uom, (float*)(arr->data), arr->dimensions[0], dataType);
break;
case Gempa::CAPS::DT_DOUBLE:
arr = (PyArrayObject*) PyArray_ContiguousFromObject(obj, PyArray_FLOAT64, 1, 1);
arr = (PyArrayObject*) PyArray_ContiguousFromObject(obj, NPY_FLOAT64, 1, 1);
if ( arr == NULL )
break;
@@ -85,6 +84,21 @@ typedef Gempa::CAPS::Plugin::Buffer Buffer;
return status;
}
Gempa::CAPS::Plugin::Status push(const std::string &net, const std::string &sta,
const std::string &loc, const std::string &cha,
const std::string &uom, PyObject *obj) {
char *data;
Py_ssize_t len;
if ( PyBytes_AsStringAndSize(obj, &data, &len) == -1 )
return Gempa::CAPS::Plugin::PacketLoss;
Gempa::CAPS::MSEEDDataRecord *mseed = new Gempa::CAPS::MSEEDDataRecord;
Gempa::CAPS::DataRecordPtr rec(mseed);
mseed->setData(data ,len);
return self->push(net, sta, loc, cha, rec, uom);
}
Gempa::CAPS::Plugin::Status push(const std::string &net, const std::string &sta,
const std::string &loc, const std::string &cha,
const Time &stime, uint16_t numerator,
@@ -98,12 +112,37 @@ typedef Gempa::CAPS::Plugin::Buffer Buffer;
return self->push(net, sta, loc, cha, stime, numerator,
denominator, format, data, len);
}
Gempa::CAPS::Plugin::Status pushAny(const std::string &net, const std::string &sta,
const std::string &loc, const std::string &cha,
const Time &stime, const Time &etime,
uint16_t numerator, uint16_t denominator,
const std::string &format, const std::string &uom,
PyObject *obj) {
char *data;
Py_ssize_t len;
if ( PyBytes_AsStringAndSize(obj, &data, &len) == -1 )
return Gempa::CAPS::Plugin::PacketLoss;
return self->pushAny(net, sta, loc, cha, stime, etime,
numerator, denominator, format, uom, data, len);
}
};
%include "exception.i"
%include "std_string.i"
%include "numpy.i"
%exception Gempa::CAPS::Plugin::push {
Py_BEGIN_ALLOW_THREADS
$action
Py_END_ALLOW_THREADS
}
%include "gempa/caps/api.h"
%include "gempa/caps/datetime.h"
%include "gempa/caps/packet.h"
@@ -115,7 +154,9 @@ typedef Gempa::CAPS::Plugin::Buffer Buffer;
%include "gempa/caps/encoderfactory.h"
%include "gempa/caps/mseedpacket.h"
typedef Gempa::CAPS::Plugin::Buffer Buffer;
%apply SWIGTYPE *DISOWN {Gempa::CAPS::EncoderFactory *factory};
%include "gempa/caps/plugin.h"
%clear Gempa::CAPS::EncoderFactory *factory;
%include "gempa/caps/rawpacket.h"
%include "gempa/caps/riff.h"
%include "gempa/caps/rtcm2packet.h"

View File

@@ -1,13 +1,10 @@
# This file was automatically generated by SWIG (http://www.swig.org).
# Version 4.0.2
# This file was automatically generated by SWIG (https://www.swig.org).
# Version 4.3.0
#
# Do not make changes to this file unless you know what you are doing--modify
# Do not make changes to this file unless you know what you are doing - modify
# the SWIG interface file instead.
from sys import version_info as _swig_python_version_info
if _swig_python_version_info < (2, 7, 0):
raise RuntimeError("Python 2.7 or later required")
# Import the low-level C/C++ module
if __package__ or "." in __name__:
from . import _CAPS
@@ -29,10 +26,10 @@ def _swig_repr(self):
def _swig_setattr_nondynamic_instance_variable(set):
def set_instance_attr(self, name, value):
if name == "thisown":
self.this.own(value)
elif name == "this":
if name == "this":
set(self, name, value)
elif name == "thisown":
self.this.own(value)
elif hasattr(self, name) and isinstance(getattr(type(self), name), property):
set(self, name, value)
else:
@@ -122,7 +119,6 @@ class TimeSpan(object):
# Register TimeSpan in _CAPS:
_CAPS.TimeSpan_swigregister(TimeSpan)
class Time(TimeSpan):
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
__repr__ = _swig_repr
@@ -190,12 +186,12 @@ class Time(TimeSpan):
def iso(self):
return _CAPS.Time_iso(self)
def fromString(self, str, fmt):
return _CAPS.Time_fromString(self, str, fmt)
def fromString(self, *args):
return _CAPS.Time_fromString(self, *args)
@staticmethod
def FromString(str, fmt):
return _CAPS.Time_FromString(str, fmt)
def FromString(*args):
return _CAPS.Time_FromString(*args)
__swig_destroy__ = _CAPS.delete_Time
# Register Time in _CAPS:
@@ -203,18 +199,6 @@ _CAPS.Time_swigregister(Time)
cvar = _CAPS.cvar
Time.Null = _CAPS.cvar.Time_Null
def Time_LocalTime():
return _CAPS.Time_LocalTime()
def Time_GMT():
return _CAPS.Time_GMT()
def Time_FromYearDay(year, year_day):
return _CAPS.Time_FromYearDay(year, year_day)
def Time_FromString(str, fmt):
return _CAPS.Time_FromString(str, fmt)
UnknownPacket = _CAPS.UnknownPacket
RawDataPacket = _CAPS.RawDataPacket
MSEEDPacket = _CAPS.MSEEDPacket
@@ -242,7 +226,6 @@ class UOM(object):
# Register UOM in _CAPS:
_CAPS.UOM_swigregister(UOM)
class Quality(object):
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
__repr__ = _swig_repr
@@ -255,7 +238,6 @@ class Quality(object):
# Register Quality in _CAPS:
_CAPS.Quality_swigregister(Quality)
class TimeStamp(object):
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
__repr__ = _swig_repr
@@ -273,7 +255,6 @@ class TimeStamp(object):
# Register TimeStamp in _CAPS:
_CAPS.TimeStamp_swigregister(TimeStamp)
class PacketDataHeader(object):
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
__repr__ = _swig_repr
@@ -305,7 +286,6 @@ class PacketDataHeader(object):
# Register PacketDataHeader in _CAPS:
_CAPS.PacketDataHeader_swigregister(PacketDataHeader)
class PacketDataHeaderV2(PacketDataHeader):
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
__repr__ = _swig_repr
@@ -331,7 +311,6 @@ class PacketDataHeaderV2(PacketDataHeader):
# Register PacketDataHeaderV2 in _CAPS:
_CAPS.PacketDataHeaderV2_swigregister(PacketDataHeaderV2)
NetworkCode = _CAPS.NetworkCode
StationCode = _CAPS.StationCode
LocationCode = _CAPS.LocationCode
@@ -355,7 +334,6 @@ class PacketHeaderV1(object):
# Register PacketHeaderV1 in _CAPS:
_CAPS.PacketHeaderV1_swigregister(PacketHeaderV1)
class PacketHeaderV2(object):
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
__repr__ = _swig_repr
@@ -374,7 +352,6 @@ class PacketHeaderV2(object):
# Register PacketHeaderV2 in _CAPS:
_CAPS.PacketHeaderV2_swigregister(PacketHeaderV2)
class ResponseHeader(object):
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
__repr__ = _swig_repr
@@ -390,7 +367,6 @@ class ResponseHeader(object):
# Register ResponseHeader in _CAPS:
_CAPS.ResponseHeader_swigregister(ResponseHeader)
class DataRecord(object):
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
@@ -405,6 +381,9 @@ class DataRecord(object):
RS_Max = _CAPS.DataRecord_RS_Max
__swig_destroy__ = _CAPS.delete_DataRecord
def clone(self):
return _CAPS.DataRecord_clone(self)
def formatName(self):
return _CAPS.DataRecord_formatName(self)
@@ -449,7 +428,6 @@ class DataRecord(object):
# Register DataRecord in _CAPS:
_CAPS.DataRecord_swigregister(DataRecord)
class RawPacket(object):
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
__repr__ = _swig_repr
@@ -464,7 +442,6 @@ class RawPacket(object):
# Register RawPacket in _CAPS:
_CAPS.RawPacket_swigregister(RawPacket)
class MetaPacket(object):
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
__repr__ = _swig_repr
@@ -482,7 +459,6 @@ class MetaPacket(object):
# Register MetaPacket in _CAPS:
_CAPS.MetaPacket_swigregister(MetaPacket)
class Packet(object):
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
__repr__ = _swig_repr
@@ -503,6 +479,7 @@ class Packet(object):
dt_us = property(_CAPS.Packet_dt_us_get, _CAPS.Packet_dt_us_set)
uom = property(_CAPS.Packet_uom_get, _CAPS.Packet_uom_set)
timingQuality = property(_CAPS.Packet_timingQuality_get, _CAPS.Packet_timingQuality_set)
context = property(_CAPS.Packet_context_get, _CAPS.Packet_context_set)
def size(self):
return _CAPS.Packet_size(self)
@@ -510,7 +487,6 @@ class Packet(object):
# Register Packet in _CAPS:
_CAPS.Packet_swigregister(Packet)
class AnyDataRecord(DataRecord):
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
__repr__ = _swig_repr
@@ -524,6 +500,9 @@ class AnyDataRecord(DataRecord):
def type(self):
return _CAPS.AnyDataRecord_type(self)
def clone(self):
return _CAPS.AnyDataRecord_clone(self)
def formatName(self):
return _CAPS.AnyDataRecord_formatName(self)
@@ -578,7 +557,6 @@ class AnyDataRecord(DataRecord):
# Register AnyDataRecord in _CAPS:
_CAPS.AnyDataRecord_swigregister(AnyDataRecord)
LL_UNDEFINED = _CAPS.LL_UNDEFINED
LL_ERROR = _CAPS.LL_ERROR
LL_WARNING = _CAPS.LL_WARNING
@@ -613,7 +591,6 @@ class SPClock(object):
# Register SPClock in _CAPS:
_CAPS.SPClock_swigregister(SPClock)
class Encoder(object):
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
@@ -643,18 +620,20 @@ class Encoder(object):
def currentTime(self):
return _CAPS.Encoder_currentTime(self)
def setTimingQuality(self, quality):
return _CAPS.Encoder_setTimingQuality(self, quality)
def timingQuality(self):
return _CAPS.Encoder_timingQuality(self)
def setTimingQuality(self, quality):
return _CAPS.Encoder_setTimingQuality(self, quality)
def setContext(self, context):
return _CAPS.Encoder_setContext(self, context)
def pop(self):
return _CAPS.Encoder_pop(self)
# Register Encoder in _CAPS:
_CAPS.Encoder_swigregister(Encoder)
class EncoderFactory(object):
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
@@ -674,7 +653,6 @@ class EncoderFactory(object):
# Register EncoderFactory in _CAPS:
_CAPS.EncoderFactory_swigregister(EncoderFactory)
class MSEEDEncoderFactory(EncoderFactory):
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
@@ -688,7 +666,6 @@ class MSEEDEncoderFactory(EncoderFactory):
# Register MSEEDEncoderFactory in _CAPS:
_CAPS.MSEEDEncoderFactory_swigregister(MSEEDEncoderFactory)
class SteimEncoderFactory(MSEEDEncoderFactory):
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
@@ -702,7 +679,6 @@ class SteimEncoderFactory(MSEEDEncoderFactory):
# Register SteimEncoderFactory in _CAPS:
_CAPS.SteimEncoderFactory_swigregister(SteimEncoderFactory)
class IdentityEncoderFactory(MSEEDEncoderFactory):
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
__repr__ = _swig_repr
@@ -719,7 +695,6 @@ class IdentityEncoderFactory(MSEEDEncoderFactory):
# Register IdentityEncoderFactory in _CAPS:
_CAPS.IdentityEncoderFactory_swigregister(IdentityEncoderFactory)
class Steim1EncoderFactory(SteimEncoderFactory):
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
__repr__ = _swig_repr
@@ -733,7 +708,6 @@ class Steim1EncoderFactory(SteimEncoderFactory):
# Register Steim1EncoderFactory in _CAPS:
_CAPS.Steim1EncoderFactory_swigregister(Steim1EncoderFactory)
class Steim2EncoderFactory(SteimEncoderFactory):
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
__repr__ = _swig_repr
@@ -747,7 +721,6 @@ class Steim2EncoderFactory(SteimEncoderFactory):
# Register Steim2EncoderFactory in _CAPS:
_CAPS.Steim2EncoderFactory_swigregister(Steim2EncoderFactory)
class MSEEDDataRecord(DataRecord):
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
@@ -755,6 +728,9 @@ class MSEEDDataRecord(DataRecord):
raise AttributeError("No constructor defined - class is abstract")
__repr__ = _swig_repr
def clone(self):
return _CAPS.MSEEDDataRecord_clone(self)
def formatName(self):
return _CAPS.MSEEDDataRecord_formatName(self)
@@ -800,7 +776,6 @@ class MSEEDDataRecord(DataRecord):
# Register MSEEDDataRecord in _CAPS:
_CAPS.MSEEDDataRecord_swigregister(MSEEDDataRecord)
class Plugin(object):
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
__repr__ = _swig_repr
@@ -875,11 +850,14 @@ class Plugin(object):
def setPacketAckFunc(self, func):
return _CAPS.Plugin_setPacketAckFunc(self, func)
def setSendTimeout(self, timeout):
return _CAPS.Plugin_setSendTimeout(self, timeout)
def setConnectionTimeout(self, sec):
return _CAPS.Plugin_setConnectionTimeout(self, sec)
def setTimeouts(self, *args):
return _CAPS.Plugin_setTimeouts(self, *args)
def setSendTimeout(self, sec):
return _CAPS.Plugin_setSendTimeout(self, sec)
def setTimeouts(self, ack, lastAck, send):
return _CAPS.Plugin_setTimeouts(self, ack, lastAck, send)
def readJournal(self):
return _CAPS.Plugin_readJournal(self)
@@ -896,6 +874,15 @@ class Plugin(object):
def writeJournal(self, *args):
return _CAPS.Plugin_writeJournal(self, *args)
def lastEndTime(self, id):
return _CAPS.Plugin_lastEndTime(self, id)
def pushRecord(self, net, sta, loc, cha, rec, uom, timingQuality=-1, context=None):
return _CAPS.Plugin_pushRecord(self, net, sta, loc, cha, rec, uom, timingQuality, context)
def pushRaw(self, net, sta, loc, cha, stime, numerator, denominator, uom, data, count, dt, timingQuality=-1, context=None):
return _CAPS.Plugin_pushRaw(self, net, sta, loc, cha, stime, numerator, denominator, uom, data, count, dt, timingQuality, context)
def flushEncoders(self):
return _CAPS.Plugin_flushEncoders(self)
@@ -905,8 +892,26 @@ class Plugin(object):
def packetBuffer(self):
return _CAPS.Plugin_packetBuffer(self)
def setAgent(self, agent):
return _CAPS.Plugin_setAgent(self, agent)
def setHostInfo(self, info):
return _CAPS.Plugin_setHostInfo(self, info)
def setRuntimeInfo(self, info):
return _CAPS.Plugin_setRuntimeInfo(self, info)
def setConnectionID(self, id):
return _CAPS.Plugin_setConnectionID(self, id)
def setConnectionIDFile(self, filename):
return _CAPS.Plugin_setConnectionIDFile(self, filename)
def connectionID(self):
return _CAPS.Plugin_connectionID(self)
def setConnectedCallback(self, f):
return _CAPS.Plugin_setConnectedCallback(self, f)
def setDisconnectedCallback(self, f):
return _CAPS.Plugin_setDisconnectedCallback(self, f)
def version(self):
return _CAPS.Plugin_version(self)
@@ -914,9 +919,11 @@ class Plugin(object):
def push(self, *args):
return _CAPS.Plugin_push(self, *args)
def pushAny(self, *args):
return _CAPS.Plugin_pushAny(self, *args)
# Register Plugin in _CAPS:
_CAPS.Plugin_swigregister(Plugin)
class RawResponseHeader(object):
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
__repr__ = _swig_repr
@@ -935,7 +942,6 @@ class RawResponseHeader(object):
# Register RawResponseHeader in _CAPS:
_CAPS.RawResponseHeader_swigregister(RawResponseHeader)
class RawDataRecord(DataRecord):
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
@@ -943,6 +949,9 @@ class RawDataRecord(DataRecord):
raise AttributeError("No constructor defined - class is abstract")
__repr__ = _swig_repr
def clone(self):
return _CAPS.RawDataRecord_clone(self)
def formatName(self):
return _CAPS.RawDataRecord_formatName(self)
@@ -1000,7 +1009,6 @@ class RawDataRecord(DataRecord):
# Register RawDataRecord in _CAPS:
_CAPS.RawDataRecord_swigregister(RawDataRecord)
class FixedRawDataRecord(RawDataRecord):
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
@@ -1029,7 +1037,6 @@ class FixedRawDataRecord(RawDataRecord):
# Register FixedRawDataRecord in _CAPS:
_CAPS.FixedRawDataRecord_swigregister(FixedRawDataRecord)
class ChunkHeader(object):
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
__repr__ = _swig_repr
@@ -1065,7 +1072,6 @@ class ChunkHeader(object):
# Register ChunkHeader in _CAPS:
_CAPS.ChunkHeader_swigregister(ChunkHeader)
class ChunkIterator(object):
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
__repr__ = _swig_repr
@@ -1121,7 +1127,6 @@ class Chunk(object):
# Register Chunk in _CAPS:
_CAPS.Chunk_swigregister(Chunk)
class HeadChunk(Chunk):
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
__repr__ = _swig_repr
@@ -1142,7 +1147,6 @@ class HeadChunk(Chunk):
# Register HeadChunk in _CAPS:
_CAPS.HeadChunk_swigregister(HeadChunk)
class SID(object):
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
__repr__ = _swig_repr
@@ -1166,7 +1170,6 @@ class SID(object):
# Register SID in _CAPS:
_CAPS.SID_swigregister(SID)
class SIDChunk(Chunk, SID):
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
__repr__ = _swig_repr
@@ -1186,7 +1189,6 @@ class SIDChunk(Chunk, SID):
# Register SIDChunk in _CAPS:
_CAPS.SIDChunk_swigregister(SIDChunk)
class RTCM2DataRecord(DataRecord):
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
@@ -1194,6 +1196,9 @@ class RTCM2DataRecord(DataRecord):
raise AttributeError("No constructor defined - class is abstract")
__repr__ = _swig_repr
def clone(self):
return _CAPS.RTCM2DataRecord_clone(self)
def formatName(self):
return _CAPS.RTCM2DataRecord_formatName(self)
@@ -1239,7 +1244,6 @@ class RTCM2DataRecord(DataRecord):
# Register RTCM2DataRecord in _CAPS:
_CAPS.RTCM2DataRecord_swigregister(RTCM2DataRecord)
class Socket(object):
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
__repr__ = _swig_repr
@@ -1297,8 +1301,8 @@ class Socket(object):
def setNonBlocking(self, nb):
return _CAPS.Socket_setNonBlocking(self, nb)
def connect(self, hostname, port):
return _CAPS.Socket_connect(self, hostname, port)
def connect(self, hostname, port, timeout=30):
return _CAPS.Socket_connect(self, hostname, port, timeout)
def rx(self):
return _CAPS.Socket_rx(self)
@@ -1308,10 +1312,6 @@ class Socket(object):
# Register Socket in _CAPS:
_CAPS.Socket_swigregister(Socket)
def Socket_toString(arg1):
return _CAPS.Socket_toString(arg1)
class SSLSocket(Socket):
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
__repr__ = _swig_repr
@@ -1326,8 +1326,8 @@ class SSLSocket(Socket):
def read(self, data, len):
return _CAPS.SSLSocket_read(self, data, len)
def connect(self, hostname, port):
return _CAPS.SSLSocket_connect(self, hostname, port)
def connect(self, hostname, port, timeout=30):
return _CAPS.SSLSocket_connect(self, hostname, port, timeout)
def sessionID(self):
return _CAPS.SSLSocket_sessionID(self)
@@ -1340,7 +1340,6 @@ class SSLSocket(Socket):
# Register SSLSocket in _CAPS:
_CAPS.SSLSocket_swigregister(SSLSocket)
class charArray(object):
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
__repr__ = _swig_repr
@@ -1364,10 +1363,6 @@ class charArray(object):
# Register charArray in _CAPS:
_CAPS.charArray_swigregister(charArray)
def charArray_frompointer(t):
return _CAPS.charArray_frompointer(t)
class arraybuf(object):
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
__repr__ = _swig_repr
@@ -1397,7 +1392,6 @@ class arraybuf(object):
# Register arraybuf in _CAPS:
_CAPS.arraybuf_swigregister(arraybuf)
def splitAddress(host, port, address, default_port):
return _CAPS.splitAddress(host, port, address, default_port)
@@ -1431,4 +1425,3 @@ def timeSpanToSamplesFloor(head, span):
def dataTypeSize(dt):
return _CAPS.dataTypeSize(dt)

File diff suppressed because it is too large Load Diff