17 currentTriggerName[0] = 0;
45 this->interval = interval;
46 this->rotate = rotate;
59 this->root = this->root.substr(0, this->root.length() - 2);
78 LogPrint(0, 0, 0,
"Cannot find MessagePlayer storage location '%s' or any backup locations", this->root.c_str());
82 LogPrint(0, 0, 0,
"Couldn't find MessagePlayer storage location '%s', using detected location '%s' instead", this->root.c_str(), altRoot.c_str());
87 LogPrint(0, 0, 0,
"Reading from MessagePlayer storage location '%s'", this->root.c_str());
88 indexFile = this->root +
"/index.bin";
89 metadataFile = this->root +
"/metadata.bin";
90 metadataTextFile = this->root +
"/metadata.text";
91 csvFile = this->root +
"/data.csv";
93 if (!(indexData =
utils::ReadAFile(indexFile.c_str(), indexFileSize,
true))) {
94 LogPrint(0, 0, 0,
"Cannot read MessagePlayer storage location index file '%s'", indexFile.c_str());
99 LogPrint(0, 0, 0,
"Index file '%s' corrupted", indexFile.c_str());
103 LogPrint(0, 0, 0,
"MessagePlayer storage index for location '%s' empty", this->root.c_str());
111 this->maxCount = maxCount;
112 this->maxSize = maxSize;
113 if (maxCount && maxSize)
114 LogPrint(0, 0, 1,
"Starting ReplayCatalog recording, maxcount: %u and maxsize: %s", maxCount,
utils::BytifySize((
double)maxSize).c_str());
116 LogPrint(0, 0, 1,
"Starting ReplayCatalog recording, maxcount: %u", maxCount);
123 this->root = this->root.substr(0, this->root.length() - 2);
143 LogPrint(0, 0, 0,
"Cannot move the old storage location '%s' - using the alternate location '%s' instead", this->root.c_str(), altRoot.c_str());
144 this->root = altRoot;
147 LogPrint(0, 0, 0,
"Old storage location '%s' backed up to '%s'", this->root.c_str(), altRoot.c_str());
149 indexFile = this->root +
"/index.bin";
150 metadataFile = this->root +
"/metadata.bin";
151 metadataTextFile = this->root +
"/metadata.text";
152 csvFile = this->root +
"/data.csv";
156 LogPrint(0, 0, 0,
"Cannot create index file in storage location '%s'", this->root.c_str());
160 LogPrint(0, 0, 0,
"Using existing unused recording storage location '%s'", this->root.c_str());
164 LogPrint(0, 0, 0,
"Cannot create storage location '%s'", this->root.c_str());
169 LogPrint(0, 0, 0,
"Cannot create index file in storage location '%s'", this->root.c_str());
173 LogPrint(0, 0, 0,
"Using new recording storage location '%s'", this->root.c_str());
179 indexSize = maxCount * 2;
193 LogPrint(0, 0, 0,
"Trigger name '%s' too long to store", name);
205 curSize += entry->
size;
207 if (curIndex == indexSize)
209 if (curCount > indexSize - 10) {
211 char* newIndexData = (
char*)malloc(indexSize * 2 *
sizeof(
ReplayIndexEntry));
212 if (curIndex > curStart)
214 else if (curIndex < curStart) {
220 if (curWrite < curStart)
221 curWrite += (indexSize - curStart);
223 curWrite -= curStart;
227 indexData = newIndexData;
232 if (lastReceiveTime && (lastReceiveTime - lastWriteTime > 1000000))
240 return writeBuffer();
250 const char* triggerName;
258 uint64 nextReadTime = lastReadTime + (nextMsgTime * 1000);
259 if (nextReadTime > now + (ms*1000)) {
265 else if (nextReadTime > now) {
284 if ((curIndex == indexSize) && rotate) {
285 LogPrint(0, 0, 1,
"Finished sending %u messages, rotating back to start...", curIndex);
288 if (curIndex < indexSize) {
299 triggerName = currentTriggerName;
301 if (curIndex == indexSize - 1)
302 nextMsgTime = msToNext = 1000;
304 nextMsgTime = msToNext = (uint32)((((
ReplayIndexEntry*)indexData)[curIndex + 1].msgTime - entry->
msgTime) / 1000);
306 nextMsgTime = msToNext = 0;
310 LogPrint(0, 0, 0,
"Couldn't find message for index %u...", curIndex);
314 else if (curIndex == indexSize) {
315 LogPrint(0, 0, 1,
"Finished sending %u messages...", curIndex);
317 nextMsgTime = msToNext = 0;
328 return currentTriggerName;
332bool MessagePlayer::writeBuffer() {
340 std::string csvOutput;
343 if (curWrite > curIndex) {
345 while (curWrite < indexSize) {
356 while (curWrite < curIndex) {
357 entry = &((ReplayIndexEntry*)indexData)[curWrite];
365 while ((maxCount && (curCount > maxCount)) || (maxSize && (curSize > maxSize))) {
366 entry = &((ReplayIndexEntry*)indexData)[curStart];
370 curSize -= entry->
size;
373 if (curStart == indexSize)
379 if (curIndex > curStart) {
380 if (!
utils::WriteAFile(indexFile.c_str(), indexData + (curStart *
sizeof(ReplayIndexEntry)), curCount *
sizeof(ReplayIndexEntry),
true))
381 LogPrint(0, 0, 0,
"Couldn't write index file '%s'", indexFile.c_str());
385 else if (curIndex < curStart) {
386 datasize = curCount *
sizeof(ReplayIndexEntry);
387 data =
new char[datasize];
388 memcpy(data, indexData + (curStart *
sizeof(ReplayIndexEntry)),
389 (indexSize - curStart) *
sizeof(ReplayIndexEntry));
390 memcpy(data + ((indexSize - curStart) *
sizeof(ReplayIndexEntry)),
391 indexData, curIndex *
sizeof(ReplayIndexEntry));
393 LogPrint(0, 0, 0,
"Couldn't write index file '%s'", indexFile.c_str());
405bool MessagePlayer::readBuffer() {
412 bool json = format ? (
utils::stristr(format,
"json") != NULL) :
false;
413 bool xml = format ? (
utils::stristr(format,
"xml") != NULL) :
false;
414 bool text = format ? (
utils::stristr(format,
"text") != NULL) :
false;
422 output +=
utils::StringFormat(
"Msg recording at %s contains %u messages\n\n", root.c_str(), curCount);
424 for (uint32 i = 0; i < indexSize; i++) {
434 output += msg->
toJSON(&subtypeList, &subcontextList, &componentNameList) +
"\n\n";
436 output += msg->
toXML(&subtypeList, &subcontextList, &componentNameList) +
"\n";
465 for (uint32 i = 0; i < indexSize; i++) {
482 subtypeList.insert(subtypes.begin(), subtypes.end());
487 subcontextList.insert(subcontexts.begin(), subcontexts.end());
492 componentNameList.insert(names.begin(), names.end());
501 std::map<uint16, std::string>::iterator iType = subtypeList.begin(), eType = subtypeList.end();
502 std::map<uint16, std::string>::iterator iContext = subcontextList.begin(), eContext = subcontextList.end();
503 std::map<uint32, std::string>::iterator iComp = componentNameList.begin(), eComp = componentNameList.end();
505 std::stringstream metadataStream;
507 while (iType != eType) {
508 metadata->
setString(iType->first,
"subtype", iType->second.c_str());
509 metadataStream <<
"Subtype: " << iType->first <<
"=" << iType->second <<
"\n";
513 while (iContext != eContext) {
514 metadata->
setString(iContext->first,
"subcontext", iContext->second.c_str());
515 metadataStream <<
"Subcontext: " << iContext->first <<
"=" << iContext->second <<
"\n";
519 while (iComp != eComp) {
520 metadata->
setString(iComp->first,
"component", iComp->second.c_str());
521 metadataStream <<
"Component: " << iComp->first <<
"=" << iComp->second <<
"\n";
527 if (!
utils::WriteAFile(metadataTextFile.c_str(), metadataStream.str().c_str(), (uint32)metadataStream.str().length(),
false))
537 std::map<int64, std::string> stringArray;
538 std::map<int64, std::string>::iterator i, e;
541 if (stringArray.size()) {
543 i = stringArray.begin();
544 e = stringArray.end();
546 subtypeList[(uint16)(i->first)] = i->second;
552 if (stringArray.size()) {
553 subcontextList.clear();
554 i = stringArray.begin();
555 e = stringArray.end();
557 subcontextList[(uint16)(i->first)] = i->second;
563 if (stringArray.size()) {
564 componentNameList.clear();
565 i = stringArray.begin();
566 e = stringArray.end();
568 componentNameList[(uint32)(i->first)] = i->second;
Record and replay of DataMessage streams to/from disk.
static struct PsyType NOTYPE
The empty/unset message type (isValid() == false).
static struct PsyContext NOCONTEXT
The empty/unset context (isValid() == false).
The central Psyclone data container: a self-contained binary message with typed, named user entries.
std::string toXML(std::map< uint16, std::string > *subtypes=NULL, std::map< uint16, std::string > *subcontexts=NULL, std::map< uint32, std::string > *compNames=NULL)
toXML()
bool setString(const char *key, const char *value)
setString(const char* key, const char* value)
DataMessageHeader * data
Pointer to the message's flat memory block (header + user entries).
std::string toJSON(std::map< uint16, std::string > *subtypes=NULL, std::map< uint16, std::string > *subcontexts=NULL, std::map< uint32, std::string > *compNames=NULL)
toJSON()
std::string toCSV(const char *separator=NULL, const char *preample=NULL, std::map< uint16, std::string > *subtypes=NULL, std::map< uint16, std::string > *subcontexts=NULL, std::map< uint32, std::string > *compNames=NULL)
toCSV()
uint32 getSize()
getSize() Get message size Many types of data of any size can be put into a message as user entries; ...
std::map< int64, std::string > getStringArray(const char *key)
getStringArray(const char* key)
bool setCreatedTime(uint64 t)
setCreatedTime(uint64 t)
bool setType(PsyType &type)
setType(PsyType &type)
uint64 getCreatedTime()
getCreatedTime()
static std::string GetCSVHeader(const char *separator=NULL, const char *preample=NULL)
toCSVHeader()
bool setContextChange(PsyContext &context)
setContextChange(PsyContext& context)
std::string getUserEntriesAsString()
getUserEntriesAsString() Returns the textual representation of the full message including all user en...
std::string printAllString(const char *format=NULL)
Render a human-readable dump of all recorded messages.
bool addMessage(const char *name, DataMessage *msg)
Append a message to the recording (write mode).
std::string exportToCSV(const char *separator=NULL)
Export the whole recording as a CSV string.
bool readMetadata()
Load the metadata files of an opened recording.
DataMessage * waitForNextMessage(uint32 ms, const char *&triggerName)
Wait for and return the next message in the recording (read mode), honouring the replay timing.
bool flushToDisk()
Force buffered messages and the index out to disk (write mode).
bool setComponentNameList(std::map< uint32, std::string > &names)
Provide readable names for component ids, used in metadata/CSV export.
bool setSubTypeList(std::map< uint16, std::string > &subtypes)
Provide readable names for type level ids, used in metadata/CSV export.
bool writeMetadata()
Write the metadata (name lists etc.) files alongside the recording.
bool setSystemIDs(DataMessage *msg)
Stamp system-identifying header fields onto msg for recording.
~MessagePlayer()
Destructor; releases buffers (does not delete messages already handed out).
bool initRead(const char *root, uint32 interval, bool rotate=false)
Open a recording for replay with a fixed playback interval.
MessagePlayer()
Create an uninitialised player; call initRead() or initWrite() before use.
bool initWrite(const char *root, uint32 maxCount, uint64 maxSize)
Open a recording for writing.
std::string getCurrentTriggerName()
bool exportToCSVFile(const char *filename, const char *separator=NULL)
Export the whole recording to a CSV file.
bool setSubContextList(std::map< uint16, std::string > &subcontexts)
Provide readable names for context level ids, used in metadata/CSV export.
std::string PrintTimeString(uint64 t, bool local=true, bool us=true, bool ms=true)
uint64 GetTimeNow()
Return the current absolute time (µs since year 0) according to the TMC.
bool CreateADir(const char *dirname)
Create a directory (parents included where supported).
bool Sleep(uint32 ms)
Suspend the calling thread.
bool AppendToAFile(const char *filename, const char *data, uint32 length, bool binary=false)
Append to a file, creating it if missing.
bool MoveAFile(const char *oldfilename, const char *newfilename, bool force=false)
Move/rename a file.
FileDetails GetFileDetails(const char *filename)
Stat a file.
bool DoesAFileExist(const char *filename)
Test whether a regular file exists.
const char * stristr(const char *str, const char *substr, uint32 len=0)
Case-insensitive strstr.
bool DeleteAFile(const char *filename, bool force)
Delete a file.
std::string BytifySize(double val)
Format a byte count with binary units, e.g.
bool WriteAFile(const char *filename, const char *data, uint32 length, bool binary=false)
Write (create/overwrite) a file.
std::string StringFormat(const char *format,...)
printf into a std::string.
char * ReadAFile(const char *filename, uint32 &length, bool binary=false)
Read an entire file into a new buffer.
bool TextEndsWith(const char *str, const char *end, bool caseSensitive=true)
Test whether str ends with end.
uint32 strcpyavail(char *dst, const char *src, uint32 maxlen, bool copyAvailable)
Bounded strcpy that always NUL-terminates.
Fixed-size on-disk index record describing one recorded message.
char msgTriggerName[128]
Name of the trigger that produced the message.
bool reset()
Zero the entry and stamp it with DATAMESSAGEINFOID.
DataMessage * msg
In-memory pointer to the loaded message; not valid on disk.
uint64 serial
Sequence number of the message within the recording.
uint64 msgTime
Original message time in microseconds (drives replay timing).
uint32 size
Binary size of the recorded message block in bytes.
Existence, type, permission and timestamp information for one file, as returned by GetFileDetails().