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.
548 lines
18 KiB
C
548 lines
18 KiB
C
1 year ago
|
/***************************************************************************
|
||
|
* 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_DATAMODEL_UTIL_H__
|
||
|
#define SEISCOMP_DATAMODEL_UTIL_H__
|
||
|
|
||
|
#include <seiscomp/core/datetime.h>
|
||
|
#include <seiscomp/core/exceptions.h>
|
||
|
#include <seiscomp/core.h>
|
||
|
#include <seiscomp/datamodel/notifier.h>
|
||
|
#include <seiscomp/datamodel/object.h>
|
||
|
#include <seiscomp/datamodel/types.h>
|
||
|
#include <seiscomp/datamodel/creationinfo.h>
|
||
|
#include <seiscomp/geo/coordinate.h>
|
||
|
#include <iostream>
|
||
|
#include <string>
|
||
|
#include <vector>
|
||
|
|
||
|
|
||
|
namespace Seiscomp {
|
||
|
namespace DataModel {
|
||
|
|
||
|
class Event;
|
||
|
class Origin;
|
||
|
class Pick;
|
||
|
class Inventory;
|
||
|
class Network;
|
||
|
class Station;
|
||
|
class SensorLocation;
|
||
|
class Stream;
|
||
|
class ConfigStation;
|
||
|
class Setup;
|
||
|
|
||
|
|
||
|
SC_SYSTEM_CORE_API std::string eventRegion(const DataModel::Event *);
|
||
|
|
||
|
|
||
|
template <typename T>
|
||
|
char objectEvaluationStatusToChar(const T *o) {
|
||
|
try {
|
||
|
switch ( o->evaluationStatus() ) {
|
||
|
case PRELIMINARY:
|
||
|
return 'P';
|
||
|
case CONFIRMED:
|
||
|
return 'C';
|
||
|
case REVIEWED:
|
||
|
return 'V';
|
||
|
case FINAL:
|
||
|
return 'F';
|
||
|
case REJECTED:
|
||
|
return 'X';
|
||
|
case REPORTED:
|
||
|
return 'R';
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
catch ( ... ) {}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
template <typename T>
|
||
|
char objectStatusToChar(const T *o) {
|
||
|
char evalStat = objectEvaluationStatusToChar(o);
|
||
|
if ( evalStat ) return evalStat;
|
||
|
|
||
|
try {
|
||
|
if ( o->evaluationMode() == MANUAL )
|
||
|
return 'M';
|
||
|
}
|
||
|
catch ( ... ) {}
|
||
|
|
||
|
return 'A';
|
||
|
}
|
||
|
|
||
|
|
||
|
template <typename T>
|
||
|
std::string objectAgencyID(const T *o) {
|
||
|
try {
|
||
|
return o->creationInfo().agencyID();
|
||
|
}
|
||
|
catch ( Core::ValueException & ) {}
|
||
|
|
||
|
return "";
|
||
|
}
|
||
|
|
||
|
|
||
|
template <typename T>
|
||
|
std::string objectAuthor(const T *o) {
|
||
|
try {
|
||
|
return o->creationInfo().author();
|
||
|
}
|
||
|
catch ( Core::ValueException & ) {}
|
||
|
|
||
|
return "";
|
||
|
}
|
||
|
|
||
|
|
||
|
template <typename T>
|
||
|
double quantityUncertainty(const T &o) {
|
||
|
try {
|
||
|
return o.uncertainty();
|
||
|
}
|
||
|
catch ( Core::ValueException & ) {
|
||
|
return (o.lowerUncertainty() + o.upperUncertainty()) * 0.5;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Sets the creation time or modification time of an object.
|
||
|
* If no creationInfo is set then it will create an empty
|
||
|
* object and set the creation time. If a creationInfo is
|
||
|
* present, it will set the modification time if the creation
|
||
|
* time is not yet set. Otherwise it sets the creation time.
|
||
|
*/
|
||
|
template <typename T>
|
||
|
void touch(T &o) {
|
||
|
Core::Time now = Core::Time::GMT();
|
||
|
|
||
|
try {
|
||
|
CreationInfo &ci = o.creationInfo();
|
||
|
try {
|
||
|
ci.modificationTime();
|
||
|
ci.setModificationTime(now);
|
||
|
}
|
||
|
catch ( ... ) {
|
||
|
// No modification time set
|
||
|
try {
|
||
|
ci.creationTime();
|
||
|
ci.setModificationTime(now);
|
||
|
}
|
||
|
catch ( ... ) {
|
||
|
// No creation time set
|
||
|
ci.setCreationTime(now);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
catch ( ... ) {
|
||
|
o.setCreationInfo(CreationInfo());
|
||
|
o.creationInfo().setCreationTime(now);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
template <typename T>
|
||
|
void touch(T *o) {
|
||
|
touch(*o);
|
||
|
}
|
||
|
|
||
|
|
||
|
MAKEENUM(
|
||
|
InventoryError,
|
||
|
EVALUES(
|
||
|
NETWORK_CODE_NOT_FOUND,
|
||
|
NETWORK_EPOCH_NOT_FOUND,
|
||
|
STATION_CODE_NOT_FOUND,
|
||
|
STATION_EPOCH_NOT_FOUND,
|
||
|
SENSOR_CODE_NOT_FOUND,
|
||
|
SENSOR_EPOCH_NOT_FOUND,
|
||
|
STREAM_CODE_NOT_FOUND,
|
||
|
STREAM_EPOCH_NOT_FOUND
|
||
|
),
|
||
|
ENAMES(
|
||
|
"network code not found",
|
||
|
"no matching network epoch found",
|
||
|
"station code not found",
|
||
|
"no matching station epoch found",
|
||
|
"sensor location code not found",
|
||
|
"no matching sensor location epoch found",
|
||
|
"stream code not found",
|
||
|
"no matching stream epoch found"
|
||
|
)
|
||
|
);
|
||
|
|
||
|
|
||
|
//! Returns the station for a network- and stationcode and
|
||
|
//! a time. If the station has not been found nullptr will be returned.
|
||
|
SC_SYSTEM_CORE_API
|
||
|
Station* getStation(const Inventory *inventory,
|
||
|
const std::string &networkCode,
|
||
|
const std::string &stationCode,
|
||
|
const Core::Time &, InventoryError *error = nullptr);
|
||
|
|
||
|
//! Returns the sensorlocation for a network-, station- and locationcode and
|
||
|
//! a time. If the sensorlocation has not been found nullptr will be returned.
|
||
|
SC_SYSTEM_CORE_API
|
||
|
SensorLocation* getSensorLocation(const Inventory *inventory,
|
||
|
const std::string &networkCode,
|
||
|
const std::string &stationCode,
|
||
|
const std::string &locationCode,
|
||
|
const Core::Time &,
|
||
|
InventoryError *error = nullptr);
|
||
|
|
||
|
//! Returns the stream for a network-, station-, location- and channelcode and
|
||
|
//! a time. If the stream has not been found nullptr will be returned.
|
||
|
SC_SYSTEM_CORE_API
|
||
|
Stream* getStream(const Inventory *inventory,
|
||
|
const std::string &networkCode,
|
||
|
const std::string &stationCode,
|
||
|
const std::string &locationCode,
|
||
|
const std::string &channelCode,
|
||
|
const Core::Time &,
|
||
|
InventoryError *error = nullptr);
|
||
|
|
||
|
//! Returns the station used for a pick. If the station has not been found
|
||
|
//! nullptr will be returned.
|
||
|
SC_SYSTEM_CORE_API
|
||
|
Station* getStation(const Inventory *inventory, const Pick *pick);
|
||
|
|
||
|
|
||
|
//! Returns the sensor location used for a pick. If the sensor location has
|
||
|
//! not been found nullptr will be returned.
|
||
|
SC_SYSTEM_CORE_API
|
||
|
SensorLocation* getSensorLocation(const Inventory *inventory,
|
||
|
const Pick *pick);
|
||
|
|
||
|
|
||
|
//! Returns the stream used for a pick. If the stream has
|
||
|
//! not been found nullptr will be returned.
|
||
|
Stream* getStream(const Inventory *inventory,
|
||
|
const Pick *pick);
|
||
|
|
||
|
|
||
|
struct ThreeComponents {
|
||
|
enum Component {
|
||
|
Vertical = 0, /* usually Z */
|
||
|
FirstHorizontal = 1, /* usually N */
|
||
|
SecondHorizontal = 2 /* usually E */
|
||
|
};
|
||
|
|
||
|
Stream *vertical() const { return comps[Vertical]; }
|
||
|
Stream *firstHorizontal() const { return comps[FirstHorizontal]; }
|
||
|
Stream *secondHorizontal() const { return comps[SecondHorizontal]; }
|
||
|
|
||
|
ThreeComponents();
|
||
|
|
||
|
Stream *comps[3];
|
||
|
};
|
||
|
|
||
|
|
||
|
//! Returns the best matching stream for the vertical component having
|
||
|
//! the stream code set to streamCode (without component code, first two letters).
|
||
|
//! The method returns the component with the lowest horizontal dip. If several
|
||
|
//! components share the same dip the first one found is returned, e.g. UVW or ABC
|
||
|
//! orientations.
|
||
|
SC_SYSTEM_CORE_API Stream *
|
||
|
getVerticalComponent(const SensorLocation *loc, const char *streamCode, const Core::Time &time);
|
||
|
|
||
|
//! Returns the best matching streams for the vertical and horizontal components
|
||
|
//! having the stream code set to streamCode (without component code, first two letters).
|
||
|
//! Returns true if the resulting 3 components are forming an orthogonal system,
|
||
|
//! false otherwise.
|
||
|
//! The method tries to find 3 orthogonal components and select the first with
|
||
|
//! the lowest dip (largest Z value) as vertical. The remaining two are returned
|
||
|
//! as 1st horizontal (Y axis or North) and 2nd horizontal (X axis or East)
|
||
|
//! respectively.
|
||
|
//! NOTE: Each of the comps entries in res can be nullptr.
|
||
|
SC_SYSTEM_CORE_API bool
|
||
|
getThreeComponents(ThreeComponents &res, const SensorLocation *loc, const char *streamCode, const Core::Time &time);
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Looks for a setup which name is [setupName] (or "global" as fallback).
|
||
|
* @param configStation The ConfigStation object with Setup's attached
|
||
|
* @param setupName The name compared with Setup::name()
|
||
|
* @param allowGlobal Defines if "global" is allowed as fallback setup
|
||
|
* if setupName has not been found explicitely.
|
||
|
* @return The setup if available, nullptr otherwise
|
||
|
*/
|
||
|
SC_SYSTEM_CORE_API Setup *
|
||
|
findSetup(const ConfigStation *configStation, const std::string &setupName,
|
||
|
bool allowGlobal = true);
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Creates a deep copy of an object including all child objects.
|
||
|
*/
|
||
|
SC_SYSTEM_CORE_API Object *copy(const Object* obj);
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @brief Returns the id of a network. This is essentially the network code.
|
||
|
* @param net The network to get the id from.
|
||
|
* @return The id
|
||
|
*/
|
||
|
SC_SYSTEM_CORE_API std::string id(const Network *net);
|
||
|
|
||
|
/**
|
||
|
* @brief Returns the id of a station as networkCode + '.' + stationCode.
|
||
|
*
|
||
|
* @param sta The station to get the id from.
|
||
|
* @return The id
|
||
|
*/
|
||
|
SC_SYSTEM_CORE_API std::string id(const Station *sta);
|
||
|
|
||
|
/**
|
||
|
* @brief Returns the id of a sensor location as
|
||
|
* networkCode + '.' + stationCode + '.' + locationCode.
|
||
|
*
|
||
|
* If any of the parent objects is not set then \p unsetCode is used for
|
||
|
* the corresponding code.
|
||
|
*
|
||
|
* @param loc The sensor location to get the id from.
|
||
|
* @param unsetCode The code used if a parent object is not set or if \p loc is null
|
||
|
* @return The id
|
||
|
*/
|
||
|
SC_SYSTEM_CORE_API std::string id(const SensorLocation *loc,
|
||
|
const char *unsetCode = "");
|
||
|
|
||
|
/**
|
||
|
* @brief Returns the id of a stream as
|
||
|
* networkCode + '.' + stationCode + '.' + locationCode + '.' + streamCode
|
||
|
*
|
||
|
* If any of the parent objects is not set then \p unsetCode is used for
|
||
|
* the corresponding code.
|
||
|
*
|
||
|
* @param loc The stream to get the id from.
|
||
|
* @param unsetCode The code used if a parent object is not set or if \p stream is null
|
||
|
* @param includeComponent Whether to include the component code or not. The
|
||
|
* component code is the last character of the stream
|
||
|
* code.
|
||
|
* @return The id
|
||
|
*/
|
||
|
SC_SYSTEM_CORE_API std::string id(const Stream *stream,
|
||
|
const char *unsetCode = "",
|
||
|
bool includeComponent = true);
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @brief Retrieves the geo coordinate (WGS84) from a station.
|
||
|
*
|
||
|
* This requires that the station has both latitude and longitude attributes
|
||
|
* set. If any of the attributes is unset then an exception is thrown.
|
||
|
*
|
||
|
* @param sta The station to get the coordinate from
|
||
|
* @return The geo coordinate of the station
|
||
|
*/
|
||
|
SC_SYSTEM_CORE_API Geo::GeoCoordinate getLocation(const Station *sta);
|
||
|
|
||
|
/**
|
||
|
* @brief Retrieves the geo coordinate (WGS84) from a sensor location.
|
||
|
*
|
||
|
* This requires that either the sensor location or the parent station has
|
||
|
* the latitude as well as longitude set. If one attribute is not set in
|
||
|
* the sensor location then the parent station will be lookup up.
|
||
|
* If any of the attributes cannot be extracted then an exception is thrown.
|
||
|
*
|
||
|
* @param loc The sensor location to get the coordinate from
|
||
|
* @return The geo coordinate of the sensor location
|
||
|
*/
|
||
|
SC_SYSTEM_CORE_API Geo::GeoCoordinate getLocation(const SensorLocation *loc);
|
||
|
|
||
|
/**
|
||
|
* @brief Retrieves the geo coordinate (WGS84) from a stream.
|
||
|
*
|
||
|
* This looks up the location of the parent sensor location which must be set.
|
||
|
* If it is incomplete or unset then the parent station of the sensor location
|
||
|
* will be lookup up.
|
||
|
* If any of the attributes cannot be extracted then an exception is thrown.
|
||
|
*
|
||
|
* @param coord The output geo location
|
||
|
* @param stream The stream to get the location from
|
||
|
* @return Success flag
|
||
|
*/
|
||
|
SC_SYSTEM_CORE_API Geo::GeoCoordinate getLocation(const Stream *stream);
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
// DataModel Diff
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
class SC_SYSTEM_CORE_API DiffMerge {
|
||
|
public:
|
||
|
DiffMerge();
|
||
|
|
||
|
|
||
|
public:
|
||
|
void setLoggingLevel(int level);
|
||
|
void showLog(std::ostream &os = std::cerr, int padding = 0,
|
||
|
int indent = 1, bool ignoreFirstPad = false);
|
||
|
|
||
|
/**
|
||
|
* Scans a object tree for a particular node. Objects are compared on base of
|
||
|
* their indexes, @see equalsIndex
|
||
|
* @param tree object tree to scan
|
||
|
* @param node item to search for
|
||
|
* @return pointer to the item within the object tree or nullptr if the node was
|
||
|
* not found
|
||
|
*/
|
||
|
Object *find(Object *tree, Object *node);
|
||
|
|
||
|
/**
|
||
|
* Recursively compares two objects and collects all differences.
|
||
|
* The root element of one of the objects must be included in the other object
|
||
|
* tree, @see find(o1, o2)
|
||
|
* @param o1 first object to compare
|
||
|
* @param o2 second object to compare
|
||
|
* @param diffList list to collect differences in
|
||
|
* @return true if the diff could be performed, false if one object was null or
|
||
|
* no common child object could be found
|
||
|
* @throw TypeException if any type restriction is violated
|
||
|
*/
|
||
|
bool diff(Object *o1, Object *o2, std::vector<NotifierPtr> &diffList);
|
||
|
|
||
|
/**
|
||
|
* Recursively compares two objects and collects all differences.
|
||
|
* The root element of one of the objects must be included in the other object
|
||
|
* tree, @see find(o1, o2)
|
||
|
* @param o1 first object to compare
|
||
|
* @param o2 second object to compare
|
||
|
* @return A notifier message with all diff notifiers
|
||
|
* @throw TypeException if any type restriction is violated
|
||
|
*/
|
||
|
NotifierMessage *diff2Message(Object *o1, Object *o2);
|
||
|
|
||
|
/**
|
||
|
* Recursively merges the node object (and its children) into the tree object.
|
||
|
* The node must be part of the tree, @ref find(o1, o2). Properties of the node
|
||
|
* object override properties of the tree.
|
||
|
* @param tree main object to merge the node into
|
||
|
* @param node object to be merged into the tree
|
||
|
* @param idMap map that keeps track of any publicID attribute changes applied
|
||
|
* during the merge
|
||
|
* @return true if the merge could be performed, false if the node was not found
|
||
|
* in the tree
|
||
|
* @throw TypeException if any type restriction is violated
|
||
|
*/
|
||
|
bool merge(Object *tree, Object *node, std::map<std::string, std::string> &idMap);
|
||
|
|
||
|
/**
|
||
|
* Merges all all objects in the vector in order of their appearance into the
|
||
|
* mergeResult object, @ref merge(Object*, Object*). The mergeResult object must
|
||
|
* be not null and must serve as a parent for the objects being merged. In a
|
||
|
* subsequent processing step changes to publicIDs are applied to references,
|
||
|
* @ref mapReferences.
|
||
|
* @param mergeResult object to merge the vector into
|
||
|
* @param objects vector of objects to merge
|
||
|
* @return true if all objects could be merged successfully, else false.
|
||
|
*/
|
||
|
bool merge(Object *mergeResult, const std::vector<Object*> &objects);
|
||
|
|
||
|
/**
|
||
|
* Validates the internal publicID references of the specified object. In a
|
||
|
* first step all publicIDs are collected, then the object is traversed top-down
|
||
|
* and each reference's value is searched in the publicID set.
|
||
|
* @param o object to validate
|
||
|
* @return true if all references point to an existing publicID, else false
|
||
|
*/
|
||
|
bool validateReferences(Object *o);
|
||
|
|
||
|
/**
|
||
|
* Maps publicID references of the specified object. While the object is
|
||
|
* traversed top-down, a lookup for each reference in the publicID map is
|
||
|
* performed. If a matching entry is found the reference's value is updated.
|
||
|
* @param o object which references should be mapped
|
||
|
* @param map publicIDMap of deprecated to current publicIDs
|
||
|
* @return number of mappings performed
|
||
|
*/
|
||
|
size_t mapReferences(Object *o, const std::map<std::string, std::string> &publicIDMap);
|
||
|
|
||
|
bool compareObjects(const Object *o1, const Object *o2);
|
||
|
|
||
|
|
||
|
private:
|
||
|
std::string getPublicID(Object* o);
|
||
|
void diffRecursive(Object *o1, Object *o2, const std::string &o1ParentID,
|
||
|
std::vector<NotifierPtr> &diffList);
|
||
|
|
||
|
bool equalsIndex(Object *o1, Object *o2);
|
||
|
bool compareNonArrayProperty(const Core::BaseObject *o1, const Core::BaseObject *o2);
|
||
|
bool compareNonArrayProperty(const Core::MetaProperty* prop,
|
||
|
const Core::BaseObject *o1, const Core::BaseObject *o2);
|
||
|
void mergeRecursive(Object *o1, Object *o2,
|
||
|
std::map<std::string, std::string> &idMap);
|
||
|
|
||
|
|
||
|
private:
|
||
|
DEFINE_SMARTPOINTER(LogNode);
|
||
|
class LogNode: public Core::BaseObject {
|
||
|
private:
|
||
|
LogNode *_parent;
|
||
|
std::string _title;
|
||
|
|
||
|
// Logging level
|
||
|
// 2 Logs all (Default)
|
||
|
// 1 Logs only differences
|
||
|
// 0 Logs none - no messages are accepted
|
||
|
int _level;
|
||
|
std::vector<LogNodePtr> _childs;
|
||
|
std::vector<std::string> _messages;
|
||
|
|
||
|
std::string o2t(const Object *o);
|
||
|
void setParent(LogNode *n);
|
||
|
void add(std::string s1, std::string n);
|
||
|
|
||
|
template <class T>
|
||
|
std::string compare(T a, T b, bool quotes = false);
|
||
|
|
||
|
void reset ();
|
||
|
|
||
|
public:
|
||
|
LogNode(const Object* o1, int level);
|
||
|
LogNode(std::string title, int level);
|
||
|
|
||
|
public:
|
||
|
template <class T>
|
||
|
void add(std::string title, T a, T b);
|
||
|
|
||
|
void add(std::string title, bool status, std::string comment);
|
||
|
void add(std::string title, const Object *o1);
|
||
|
LogNodePtr add(LogNodePtr child);
|
||
|
|
||
|
void show(std::ostream &os, int padding = 0, int indent = 1,
|
||
|
bool ignoreFirstPad = false);
|
||
|
};
|
||
|
|
||
|
|
||
|
private:
|
||
|
LogNodePtr _currentNode;
|
||
|
int _logLevel;
|
||
|
};
|
||
|
|
||
|
|
||
|
} // of ns DataModel
|
||
|
} // of ns Seiscomp
|
||
|
|
||
|
|
||
|
#endif
|