#ifndef __AlertsOutput_h_ #define __AlertsOutput_h_ #include "Strategy.h" /* This is aimed at sending the results of the streaming alerts from the * fast_alert_search server to the ax_alert_server. We want these in a format * where ax_alert_server can continue to manipulate them. This is different * from the top list requests, where fast_alert_search accumulates the data * AND formats it into XML. In that case ax_alert_server copies the message * as is. * * This class makes heavy use of the Record class because that class already * knows how to stream itself and how to deal with variant data types. * Originally I had hoped that each alert result could fit perfectly into a * record object. Parse::ValuesByName turned out to be much more convenient. * I considered a way to serialize Parse::ValuesByName directly. But it seemed * much more efficient to serialize the entire AlertsOutput at once. * * Each AlertsOutput ojbect is serialized into a series of serialized Record * objects. Each Record is preceeded by a 3 byte integer (Intel byte order) * saying how long the record is. The first Record is a header. After that, * each alert / row is put into a Record. * * The header contains four things. * a) A list of any string that is used as a field name for any field in any * alert in this object. (By having just one copy of this, the serialized * form of the object is smaller. But the object will also take less space * when you deserialize it. * b) The id of the next alert that we want to look at after this batch. * c) The number of alerts in each of the windows. * d) The name / window id of each window. * * Field 0 contains the first name, perhaps "Price". Field 1 contains the next * name, perhaps "RV". The reader stops looking when he gets to the first * NULL field. * * Field 0xffff contains the next alert id number. * * Field 0xfffe contains the number of alerts in the first window. * Field 0xfffd contains the name of the first window. * Fields 0xffc and 0xffb describe the second alert window. * When you read, keep working backwards until you get to a null. * * Each record directly contains the values from the ValuesByName. The names * are stored in the header. The field ids must match. For example, if field * 0 in the header is "Price" and field 0 in the row is 5.25, then 5.25 is the * price. If field 1 in the header is "RV" and field 1 is missing from the * row, then this row doesn't have a relative volume. If you ask for the * relative volume, it's would be NULL. Presumably some other row does have a * relative volume, or it wouldn't have been in the header. */ struct AlertsOutput { private: void clear(); void testOne() const; // This is a bit rough. It was designed for our test methods. It should // be rewritten if we ever need to export it. bool operator ==(AlertsOutput const &other) const; static void reportError(std::string const &type); public: AlertId startFromHere; std::map< std::string, std::vector< Parse::ValuesByName > > values; AlertsOutput() { clear(); } bool loadFromString(std::string const &source); // true on success std::string saveToString() const; std::string debugDump() const; static void test(); }; #endif