You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

414 lines
14 KiB
C

/***************************************************************************
* Copyright (C) gempa GmbH *
* All rights reserved. *
* Contact: gempa GmbH (seiscomp-dev@gempa.de) *
* *
* GNU Affero General Public License Usage *
* This file may be used under the terms of the GNU Affero *
* Public License version 3.0 as published by the Free Software Foundation *
* and appearing in the file LICENSE included in the packaging of this *
* file. Please review the following information to ensure the GNU Affero *
* Public License version 3.0 requirements will be met: *
* https://www.gnu.org/licenses/agpl-3.0.html. *
* *
* Other Usage *
* Alternatively, this file may be used in accordance with the terms and *
* conditions contained in a signed written agreement between you and *
* gempa GmbH. *
***************************************************************************/
#ifndef SEISCOMP_IO_QUAKELINK_CONNECTION_H
#define SEISCOMP_IO_QUAKELINK_CONNECTION_H
#include <seiscomp/core/baseobject.h>
#include <seiscomp/io/socket.h>
#include <string>
#include <vector>
namespace Seiscomp {
namespace IO {
namespace QuakeLink {
extern const char *SummaryTimeFormat;
extern const char *RequestTimeFormat;
enum RequestFormat {
rfSummary = 0,
rfXML = 1,
rfGZXML = 2,
rfNative = 3,
rfGZNative = 4
};
enum ContentType {
ctUndefined = -1,
ctXML = 0,
ctEvSum = 1,
ctEvLog = 2,
ctText = 3
};
enum Options {
opIgnore = 0x0000,
opDefaults = 0x0001,
opXMLIndent = 0x0002,
opDataPicks = 0x0004,
opDataAmplitudes = 0x0008,
opDataStaMags = 0x0010,
opDataArrivals = 0x0020,
opDataStaMts = 0x0040,
opDataPreferred = 0x0080,
opKeepAlive = 0x8000, // since API 1.6.0
opAll = 0xFFFF
};
enum OrderBy {
obUndefined = -1,
obOTimeAsc = 0,
obOTimeDesc = 1
};
enum Domain {
doEvents = 0,
doDYFI = 1
};
// API and format version type
typedef unsigned int Version;
// Maps a version of a specific RequestFormat to a minimum required API version
typedef std::vector<Version> APIList;
// Maps a RequestFormat to the list of required API versions
typedef std::map<RequestFormat, APIList> FormatAPIMap;
class RequestFormatVersion {
public:
RequestFormatVersion(RequestFormat format, Version version=1)
: _format(format), _version(version < 1 ? 1 : version) {}
operator RequestFormat() const { return _format; }
RequestFormat format() const { return _format; }
Version version() const { return _version; }
private:
RequestFormat _format;
Version _version;
};
/**
* @brief The Response class
*/
DEFINE_SMARTPOINTER(Response);
class Response : public Core::BaseObject {
DECLARE_CASTS(Response);
public:
// Header
ContentType type;
uint length;
Core::Time timestamp;
std::string format;
bool gzip;
OPT(int) revision; // since API 1.6.0
bool disposed; // since API 4.0.0
// Payload
std::string data;
Response() { reset(); }
Response(const Response &other) { *this = other; }
const Response& operator=(const Response &other) {
type = other.type;
length = other.length;
timestamp = other.timestamp;
format = other.format;
gzip = other.gzip;
data = other.data;
revision = other.revision;
disposed = other.disposed;
return *this;
}
void reset() {
type = ctUndefined;
length = 0;
timestamp = Core::Time::Null;
gzip = false;
revision = Core::None;
disposed = false;
format.clear();
data.clear();
}
};
typedef std::vector<Response> Responses;
typedef std::vector<Response*> ResponsesPtr;
DEFINE_SMARTPOINTER(Connection);
class Connection : public Core::BaseObject {
public:
/** Default constructor */
Connection();
/** Destructor */
virtual ~Connection();
/**
* @brief Initializes client instance with connection URL and options
* @param url URL of the QuakeLink including user name and password if
* required
* @param options Bit mask of all options to enable
* @return
*/
bool init(const std::string &url, int options = opIgnore);
/**
* @brief Returns connection state
* @return True if a connection to the QuakeLink server is established
*/
bool connected() const;
/** Disconnects from QL server */
void disconnect();
/**
* @brief Executes the hello command which retrieves the server ID and
* API version.
* @param id Variable to store the server identifier including build
* version, e.g. QuakeLink (gempa GmbH) v2020.083#da048827b
* @param api Variable to store the server API version
* @return True on success. Note: The first QuakeLink server versions
* did not report a API version. If such a server is encountered the
* version is set to 0.
* @since SeisComP ABI version 13.0.0
*/
bool hello(std::string &id, Version &api);
/**
* @brief Sets connection options
* @param options Bit mask of all options to enable
* @return True if the options could be set
*/
bool setOptions(int options);
/**
* @brief Gets all updates of a particular event. The response format is
* restricted to 'summary'.
* @param resp Response object to store the result
* @param eventID Event ID to retrieve all updates from
* @param formatVersion Response format and version. Note currently only
* the rfSummary format is supported but in different versions.
* since API 13.0.0
* @return True if the updates of an event could be fetched
*/
bool getUpdates(Response &resp, const std::string &eventID,
const RequestFormatVersion &formatVersion = rfSummary);
/**
* @brief Gets one particular event revision
* @param response Response object to store the result
* @param eventID Event ID to retrive the particular revision from
* @param revision Event revision, use a negative number to query the
* latest revision
* @param formatVersion Response format and version
* @return True if the event revision could be fetched
*/
bool get(Response &response, const std::string &eventID,
int revision = -1,
const RequestFormatVersion &formatVersion = rfSummary);
/**
* @brief Selects archived events. Returns when all matching events have
* been processed.
* @param responses List of Response objects to store the results
* @param from Begin of time window
* @param to End of time window
* @param formatVersion Response format
* will request the default version.
* @param where SQL like filter clause, of form
* clause := condition[ AND|OR [(]clause[)]]
* condition := MAG|DEPTH|LAT|LON|PHASES op {float} |
* UPDATED|OTIME op time |
* MAG|DEPTH|LAT|LON|PHASES|OTIME|UPDATED IS [NOT] nullptr
* op := =|>|>=|<|<=|eq|gt|ge|lt|ge
* @param orderBy sort order of the results,
* Note: Requires server API >= 1,
* Since: SeisComP ABI version 13.0.0
* @param limit maximum number of results to return, a value of 0 will
* disable any limit,
* Note: Requires server API >= 1,
* Since: SeisComP ABI version 13.0.0
* @param offset of the requested result set, a value of 0 will return
* the first item
* Note: Requires server API >= 1,
* Since: SeisComP ABI version 13.0.0
* @return True if the query could be executed
*/
bool selectArchived(Responses &responses,
const Core::Time &from = Core::Time(),
const Core::Time &to = Core::Time(),
const RequestFormatVersion &formatVersion = rfSummary,
const std::string &where = "",
Domain domain = doEvents,
OrderBy orderBy = obUndefined,
unsigned long limit = 0,
unsigned long offset = 0);
/**
* @brief Selects updated and (optional) archived events. This call
* blocks until 'disconnect()', or 'abort()' is called from a different
* thread or a connection failure occurs and automatic reconnection is
* disabled. The results are send as notifier objects.
* @param archived If enabled also archived events are returned
* @param from Begin of time window
* @param to End of time window
* @param formatVersion Response format
* @param where SQL like filter clause, of form
* clause := condition[ AND|OR [(]clause[)]]
* condition := MAG|DEPTH|LAT|LON|PHASES op {float} |
* UPDATED|OTIME op time |
* MAG|DEPTH|LAT|LON|PHASES|OTIME|UPDATED IS [NOT] nullptr
* op := =|>|>=|<|<=|eq|gt|ge|lt|ge
* @param updatedBufferSize Size of the buffer to store updated events
* until all archived events have been processed. If set to a negative
* value the buffer will be disabled. In this case archived and updated
* events may be mixed. If set to an insufficient positive value updated
* events may be lost if a buffer overflow occurs while archived events
* are still processed.
* @param orderBy sort order of the results,
* Note: Requires server API >= 1,
* Since: SeisComP ABI version 13.0.0
* @param limit maximum number of results to return, a value of 0 will
* disable any limit,
* Note: Requires server API >= 1,
* Since: SeisComP ABI version 13.0.0
* @param offset of the requested result set, a value of 0 will return
* the first item
* Note: Requires server API >= 1,
* Since: SeisComP ABI version 13.0.0
* @return True if the query could be executed and then was gracefully
* aborted.
*/
bool select(bool archived = false,
const Core::Time &from = Core::Time(),
const Core::Time &to = Core::Time(),
const RequestFormatVersion &formatVersion = rfSummary,
const std::string &where = "",
Domain domain = doEvents,
int updatedBufferSize = 1000,
OrderBy orderBy = obUndefined,
unsigned long limit = 0,
unsigned long offset = 0);
/**
* @brief Gracefully aborts data transmission by sending an abort
* command to the server.
* @return True if the abort command could be sent
*/
bool abort();
/**
* @brief serverID
* @return The server ID or an empty string if no connection could be
* established.
* @since SeisComP ABI version 13.0.0
*/
const std::string& serverID();
/**
* @brief serverAPI
* @return The server API version or 0 if the version could not be
* obtained
* @since SeisComP ABI version 13.0.0
*/
Version serverAPI();
/**
* @brief initialized
* @return True if this instance was sucessfully initialized via the
* init() call
*/
inline bool initialized() { return _sock; }
/**
* @brief interrupted
* @return True if underlying socket of this instance was interrupted
*/
inline bool interrupted() { return _sock && _sock->isInterrupted(); }
/**
* @brief isSupported
* @param formatVersion The format version to check
* @param log If enabled negative answers will be logged
* @return True if the format version combination is supported by the
* current server connection
* @since SeisComP ABI version 13.0.0
*/
bool isSupported(const RequestFormatVersion &formatVersion,
bool log=false);
/**
* @brief maximumSupportedVersion
* @param format The request format
* @return Maximum format version supported by the current server
* connection. Returns 0 if no connection could be established.
* @since SeisComP ABI version 13.0.0
*/
Version maximumSupportedVersion(RequestFormat format);
protected:
/**
* @brief Processes a response recevied by a select request.
* @param response Response object received,
* the ownership is transfered to the method
*/
virtual void processResponse(Response* response) { delete response; }
inline void setLogPrefix(const std::string &prefix) {
_logPrefix = prefix;
}
bool connect();
bool sendRequest(const std::string &req, bool log = true);
bool sendOptions(int changedOptions);
bool updateOption(Options option, const char *cmd,
int changedOptions = opAll);
bool readResponse(Response &response);
size_t readLine(std::string &line);
void logAndDisconnect(const char *msg, const char *detail = 0);
void logInvalidResp(const char *expected, const char *got);
bool readResponseCode(std::string &code);
bool assertResponseCode(const std::string &expected);
bool assertLineBreak();
bool readPayload(std::string &data, uint count);
bool checkFormatVersion(std::string &error,
const RequestFormatVersion &formatVerion);
protected:
std::string _logPrefix;
std::string _service;
std::string _user;
std::string _pass;
Socket *_sock;
int _options;
std::string _serverID;
Version _serverAPI;
};
} // ns QuakeLink
} // ns IO
} // ns Seiscomp
#endif // SEISCOMP_IO_QUAKELINK_CONNECTION_H__