CMSDK 2.0.1
Cross-platform C++ base library and SDK for the Psyclone AIOS platform
Loading...
Searching...
No Matches
MessagePlayer.cpp
Go to the documentation of this file.
1
6#include "MessagePlayer.h"
7
8namespace cmlabs {
9
11 interval = 0;
12 maxCount = 0;
13 maxSize = 0;
14 rotate = false;
15 indexData = NULL;
16 indexSize = 0;
17 currentTriggerName[0] = 0;
18 indexFileSize = 0;
19
20 curStart = 0;
21 curWrite = 0;
22 curIndex = 0;
23 curCount = 0;
24 curSize = 0;
25 lastWriteTime = GetTimeNow();
26 lastReceiveTime = 0;
27 lastReadTime = 0;
28 serial = 0;
29 nextMsgTime = 0;
30}
31
33 writeBuffer();
34 if (indexData)
35 delete(indexData);
36 indexData = NULL;
37 indexSize = 0;
38}
39
40bool MessagePlayer::initRead(const char* root, bool rotate) {
41 return initRead(root, 0, rotate);
42}
43
44bool MessagePlayer::initRead(const char* root, uint32 interval, bool rotate) {
45 this->interval = interval;
46 this->rotate = rotate;
47
48 if (interval) {
49 //LogPrint(0, 0, 1, "Starting ReplayCatalog playback, interval: %u", interval);
50 }
51 else {
52 this->interval = 1;
53 //LogPrint(0, 0, 1, "Starting ReplayCatalog playback with actual message intervals");
54 }
55
56 // Find directory location
57 this->root = root;
58 while (utils::TextEndsWith(this->root.c_str(), "/") || utils::TextEndsWith(this->root.c_str(), "\\")) {
59 this->root = this->root.substr(0, this->root.length() - 2);
60 }
61
62 // Backup directory, if already exists
63 utils::FileDetails dirDetails = utils::GetFileDetails((this->root + "/index.bin").c_str());
64
65 uint32 n = 0;
66 utils::FileDetails altDetails;
67 // Find backup location
68 std::string altRoot = utils::StringFormat("%s.%u", this->root.c_str(), n);
69
70 ReplayIndexEntry* entry;
71 ReplayIndexEntry initEntry;
72 initEntry.reset();
73
74 if (!dirDetails.doesExist) {
75 while (!(altDetails = utils::GetFileDetails((altRoot + "/index.bin").c_str())).doesExist && n < 100)
76 altRoot = utils::StringFormat("%s.%u", this->root.c_str(), ++n);
77 if (!altDetails.doesExist) {
78 LogPrint(0, 0, 0, "Cannot find MessagePlayer storage location '%s' or any backup locations", this->root.c_str());
79 return false;
80 }
81 else {
82 LogPrint(0, 0, 0, "Couldn't find MessagePlayer storage location '%s', using detected location '%s' instead", this->root.c_str(), altRoot.c_str());
83 this->root = altRoot;
84 }
85 }
86 else
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";
92
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());
95 }
96 curCount = indexSize = indexFileSize / sizeof(ReplayIndexEntry);
97 entry = (ReplayIndexEntry*)indexData;
98 if (!entry->isValid()) {
99 LogPrint(0, 0, 0, "Index file '%s' corrupted", indexFile.c_str());
100 return false;
101 }
102 if (!entry->size) {
103 LogPrint(0, 0, 0, "MessagePlayer storage index for location '%s' empty", this->root.c_str());
104 return false;
105 }
106 readMetadata();
107 return true;
108}
109
110bool MessagePlayer::initWrite(const char* root, uint32 maxCount, uint64 maxSize) {
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());
115 else if (maxCount)
116 LogPrint(0, 0, 1, "Starting ReplayCatalog recording, maxcount: %u", maxCount);
117 else
118 LogPrint(0, 0, 1, "Starting ReplayCatalog recording, maxsize: %s", utils::BytifySize((double)maxSize).c_str());
119
120 // Find directory location
121 this->root = root;
122 while (utils::TextEndsWith(this->root.c_str(), "/") || utils::TextEndsWith(this->root.c_str(), "\\")) {
123 this->root = this->root.substr(0, this->root.length() - 2);
124 }
125
126 // Backup directory, if already exists
127 utils::FileDetails dirDetails = utils::GetFileDetails((this->root + "/index.bin").c_str());
128
129 uint32 n = 0;
130 utils::FileDetails altDetails;
131 // Find backup location
132 std::string altRoot = utils::StringFormat("%s.%u", this->root.c_str(), n);
133
134 ReplayIndexEntry initEntry;
135 initEntry.reset();
136
137 // if recording
138 if (dirDetails.doesExist) {
139 while ((altDetails = utils::GetFileDetails((altRoot + "/index.bin").c_str())).doesExist)
140 altRoot = utils::StringFormat("%s.%u", this->root.c_str(), ++n);
141
142 if (!utils::MoveAFile(this->root.c_str(), altRoot.c_str(), true)) {
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;
145 }
146 else
147 LogPrint(0, 0, 0, "Old storage location '%s' backed up to '%s'", this->root.c_str(), altRoot.c_str());
148 }
149 indexFile = this->root + "/index.bin";
150 metadataFile = this->root + "/metadata.bin";
151 metadataTextFile = this->root + "/metadata.text";
152 csvFile = this->root + "/data.csv";
153
154 if (utils::GetFileDetails(this->root.c_str()).doesExist) {
155 if (!utils::WriteAFile(indexFile.c_str(), (char*)&initEntry, sizeof(ReplayIndexEntry), true)) {
156 LogPrint(0, 0, 0, "Cannot create index file in storage location '%s'", this->root.c_str());
157 return false;
158 }
159 else
160 LogPrint(0, 0, 0, "Using existing unused recording storage location '%s'", this->root.c_str());
161 }
162 else {
163 if (!utils::CreateADir(this->root.c_str())) {
164 LogPrint(0, 0, 0, "Cannot create storage location '%s'", this->root.c_str());
165 return false;
166 }
167 else {
168 if (!utils::WriteAFile(indexFile.c_str(), (char*)&initEntry, sizeof(ReplayIndexEntry), true)) {
169 LogPrint(0, 0, 0, "Cannot create index file in storage location '%s'", this->root.c_str());
170 return false;
171 }
172 else
173 LogPrint(0, 0, 0, "Using new recording storage location '%s'", this->root.c_str());
174 }
175 }
176
177 // create initial in-memory index
178 if (maxCount)
179 indexSize = maxCount * 2;
180 else
181 indexSize = 4096;
182 indexData = (char*)malloc(indexSize * sizeof(ReplayIndexEntry));
183 memset(indexData, 0, indexSize * sizeof(ReplayIndexEntry));
184 return true;
185}
186
187
188
189bool MessagePlayer::addMessage(const char* name, DataMessage* msg) {
190 if (!msg)
191 return false;
192 if (strlen(name) > MAXKEYNAMELEN) {
193 LogPrint(0, 0, 0, "Trigger name '%s' too long to store", name);
194 return false;
195 }
196
197 ReplayIndexEntry* entry = &((ReplayIndexEntry*)indexData)[curIndex];
198 entry->reset();
199 entry->serial = ++serial;
200 entry->msgTime = lastReceiveTime = msg->getCreatedTime();
201 entry->size = msg->getSize();
202 entry->msg = new DataMessage(*msg);
203 utils::strcpyavail(entry->msgTriggerName, name, 127, false);
204 curIndex++;
205 curSize += entry->size;
206 curCount++;
207 if (curIndex == indexSize)
208 curIndex = 0;
209 if (curCount > indexSize - 10) {
210 // expand memory
211 char* newIndexData = (char*)malloc(indexSize * 2 * sizeof(ReplayIndexEntry));
212 if (curIndex > curStart)
213 memcpy(newIndexData, indexData + (curStart * sizeof(ReplayIndexEntry)), curCount * sizeof(ReplayIndexEntry));
214 else if (curIndex < curStart) {
215 memcpy(newIndexData, indexData + (curStart * sizeof(ReplayIndexEntry)),
216 (indexSize - curStart) * sizeof(ReplayIndexEntry));
217 memcpy(newIndexData + ((indexSize - curStart) * sizeof(ReplayIndexEntry)),
218 indexData, curIndex * sizeof(ReplayIndexEntry));
219 }
220 if (curWrite < curStart)
221 curWrite += (indexSize - curStart);
222 else
223 curWrite -= curStart;
224 curStart = 0;
225 curIndex = curCount;
226 free(indexData);
227 indexData = newIndexData;
228 indexSize *= 2;
229 memset(indexData + (curCount * sizeof(ReplayIndexEntry)), 0, (indexSize - curCount) * sizeof(ReplayIndexEntry));
230 }
231
232 if (lastReceiveTime && (lastReceiveTime - lastWriteTime > 1000000))
233 writeBuffer();
234
235 //printf("Added message...\n");
236 return true;
237}
238
240 return writeBuffer();
241}
242
243
244DataMessage* MessagePlayer::waitForNextMessage(uint32 ms, const char* &triggerName) {
245 uint32 msToNext = 0;
246 return waitForNextMessage(ms, triggerName, msToNext);
247}
248
249DataMessage* MessagePlayer::waitForNextMessage(uint32 ms, uint32 &msToNext) {
250 const char* triggerName;
251 return waitForNextMessage(ms, triggerName, msToNext);
252}
253
254DataMessage* MessagePlayer::waitForNextMessage(uint32 ms, const char* &triggerName, uint32 &msToNext) {
255
256 uint64 now = GetTimeNow();
257 if (nextMsgTime) {
258 uint64 nextReadTime = lastReadTime + (nextMsgTime * 1000);
259 if (nextReadTime > now + (ms*1000)) {
260 //printf("Pretend waiting for %ums\n", ms);
261 utils::Sleep(ms);
262 msToNext -= ms;
263 return NULL;
264 }
265 else if (nextReadTime > now) {
266 //printf("Waiting for %ums\n", (uint32)((nextReadTime - now)/1000));
267 utils::Sleep((uint32)((nextReadTime - now) / 1000));
268 }
269 else {
270 //printf("Not waiting\n");
271 }
272 }
273 else {
274 //printf("Going straight for it\n");
275 }
276
277 // plan to send out next message
278 char* data;
279 uint32 datasize;
280 DataMessage *msg = NULL;
281
282 // ############ read into buffer
283
284 if ((curIndex == indexSize) && rotate) {
285 LogPrint(0, 0, 1, "Finished sending %u messages, rotating back to start...", curIndex);
286 curIndex = 0;
287 }
288 if (curIndex < indexSize) {
289 ReplayIndexEntry* entry = &((ReplayIndexEntry*)indexData)[curIndex];
290 if (entry->size) {
291 if (!(data = utils::ReadAFile(utils::StringFormat("%s/%llu.msg", root.c_str(), entry->serial).c_str(), datasize, true)))
292 return NULL;
293 msg = new DataMessage(data, false);
294 msg->setType(NOTYPE);
296 msg->setCreatedTime(lastReadTime = now);
297 // api->postOutputMessage(entry->msgTriggerName, msg);
298 utils::strcpyavail(currentTriggerName, entry->msgTriggerName, MAXKEYNAMELEN, true);
299 triggerName = currentTriggerName;
300 if (interval == 1) {
301 if (curIndex == indexSize - 1)
302 nextMsgTime = msToNext = 1000;
303 else if (((ReplayIndexEntry*)indexData)[curIndex + 1].msgTime > entry->msgTime)
304 nextMsgTime = msToNext = (uint32)((((ReplayIndexEntry*)indexData)[curIndex + 1].msgTime - entry->msgTime) / 1000);
305 else
306 nextMsgTime = msToNext = 0;
307 }
308 }
309 else
310 LogPrint(0, 0, 0, "Couldn't find message for index %u...", curIndex);
311 curIndex++;
312 return msg;
313 }
314 else if (curIndex == indexSize) {
315 LogPrint(0, 0, 1, "Finished sending %u messages...", curIndex);
316 curIndex++;
317 nextMsgTime = msToNext = 0;
318 }
319 return NULL;
320}
321
323 const char* temp;
324 return waitForNextMessage(ms, temp);
325}
326
328 return currentTriggerName;
329}
330
331
332bool MessagePlayer::writeBuffer() {
333 // If we are a reader
334 if (interval)
335 return true;
336
337 ReplayIndexEntry* entry;
338 char* data;
339 uint32 datasize;
340 std::string csvOutput;
341
342 // write unstored messages to files
343 if (curWrite > curIndex) {
344 // serial has looped, run to the end
345 while (curWrite < indexSize) {
346 entry = &((ReplayIndexEntry*)indexData)[curWrite];
347 utils::WriteAFile(utils::StringFormat("%s/%llu.msg", root.c_str(), entry->serial).c_str(), (char*)entry->msg->data, entry->size, true);
348 csvOutput += entry->msg->toCSV(",", utils::StringFormat("%llu,%s", entry->serial, entry->msgTriggerName).c_str(), &subtypeList, &subcontextList, &componentNameList);
349 //printf("Wrote message '%s'...\n", utils::StringFormat("%s/%llu.msg", root.c_str(), entry->serial).c_str());
350 delete(entry->msg);
351 entry->msg = NULL;
352 curWrite++;
353 }
354 curWrite = 0;
355 }
356 while (curWrite < curIndex) {
357 entry = &((ReplayIndexEntry*)indexData)[curWrite];
358 utils::WriteAFile(utils::StringFormat("%s/%llu.msg", root.c_str(), entry->serial).c_str(), (char*)entry->msg->data, entry->size, true);
359 csvOutput += entry->msg->toCSV(",", utils::StringFormat("%llu,%s", entry->serial, entry->msgTriggerName).c_str(), &subtypeList, &subcontextList, &componentNameList);
360 //printf("Wrote message '%s'...\n", utils::StringFormat("%s/%llu.msg", root.c_str(), entry->serial).c_str());
361 delete(entry->msg);
362 entry->msg = NULL;
363 curWrite++;
364 }
365 while ((maxCount && (curCount > maxCount)) || (maxSize && (curSize > maxSize))) {
366 entry = &((ReplayIndexEntry*)indexData)[curStart];
367 // delete oldest file
368 utils::DeleteAFile(utils::StringFormat("%s/%llu.msg", root.c_str(), entry->serial).c_str(), true);
369 //printf("Delete message '%s'...\n", utils::StringFormat("%s/%llu.msg", root.c_str(), entry->serial).c_str());
370 curSize -= entry->size;
371 curCount--;
372 curStart++;
373 if (curStart == indexSize)
374 curStart = 0;
375 entry->size = 0;
376 }
377 lastWriteTime = GetTimeNow();
378 // write index file
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());
382 //else
383 // api->logPrint(0, "Wrote index file '%s' size %llu [%u]", indexFile.c_str(), curCount * sizeof(ReplayIndexEntry), curCount);
384 }
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));
392 if (!utils::WriteAFile(indexFile.c_str(), data, datasize, true))
393 LogPrint(0, 0, 0, "Couldn't write index file '%s'", indexFile.c_str());
394 //else
395 // api->logPrint(0, "Wrote index temp file '%s' size %llu [%u]", indexFile.c_str(), datasize, curCount);
396 delete[] data;
397 }
398 if (!utils::DoesAFileExist(csvFile.c_str()))
399 csvOutput = utils::StringFormat("%s%s", DataMessage::GetCSVHeader(",", "serial,triggername").c_str(), csvOutput.c_str());
400 utils::AppendToAFile(csvFile.c_str(), csvOutput.c_str(), (uint32)csvOutput.length(), false);
401 //printf("Wrote messages...\n");
402 return true;
403}
404
405bool MessagePlayer::readBuffer() {
406 return false;
407}
408
409
410std::string MessagePlayer::printAllString(const char* format) {
411
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;
415
416 char* data;
417 uint32 datasize;
418 std::string output;
419 ReplayIndexEntry* entry;
420 DataMessage* msg;
421
422 output += utils::StringFormat("Msg recording at %s contains %u messages\n\n", root.c_str(), curCount);
423
424 for (uint32 i = 0; i < indexSize; i++) {
425 entry = &((ReplayIndexEntry*)indexData)[i];
426 if (entry->size) {
427 output += utils::StringFormat("[%u]\t Msg at %s\t size %s\t trigger: '%s'\n", i+1,
428 PrintTimeString(entry->msgTime).c_str(), utils::BytifySize(entry->size).c_str(), entry->msgTriggerName);
429 if (format) {
430 data = utils::ReadAFile(utils::StringFormat("%s/%llu.msg", root.c_str(), entry->serial).c_str(), datasize, true);
431 if (data) {
432 msg = new DataMessage(data, false);
433 if (json)
434 output += msg->toJSON(&subtypeList, &subcontextList, &componentNameList) + "\n\n";
435 if (xml)
436 output += msg->toXML(&subtypeList, &subcontextList, &componentNameList) + "\n";
437 if (text)
438 output += msg->getUserEntriesAsString() + "\n";
439 delete msg;
440 }
441 }
442 }
443 }
444
445 return output;
446}
447
448bool MessagePlayer::exportToCSVFile(const char* filename, const char* separator) {
449 std::string csv = exportToCSV(separator);
450 if (!csv.length())
451 return false;
452 return utils::WriteAFile(filename, csv.c_str(), (uint32)csv.length());
453}
454
455std::string MessagePlayer::exportToCSV(const char* separator) {
456
457 char* data;
458 uint32 datasize;
459 std::string output;
460 ReplayIndexEntry* entry;
461 DataMessage* msg;
462
463 output = DataMessage::GetCSVHeader(",", "serial,triggername");
464
465 for (uint32 i = 0; i < indexSize; i++) {
466 entry = &((ReplayIndexEntry*)indexData)[i];
467 if (entry->size) {
468 data = utils::ReadAFile(utils::StringFormat("%s/%llu.msg", root.c_str(), entry->serial).c_str(), datasize, true);
469 if (data) {
470 msg = new DataMessage(data, false);
471 output += msg->toCSV(",", utils::StringFormat("%llu,%s", entry->serial, entry->msgTriggerName).c_str(), &subtypeList, &subcontextList, &componentNameList);
472 delete msg;
473 }
474 }
475 }
476
477 return output;
478}
479
480
481bool MessagePlayer::setSubTypeList(std::map<uint16, std::string>& subtypes) {
482 subtypeList.insert(subtypes.begin(), subtypes.end());
483 return writeMetadata();
484}
485
486bool MessagePlayer::setSubContextList(std::map<uint16, std::string>& subcontexts) {
487 subcontextList.insert(subcontexts.begin(), subcontexts.end());
488 return writeMetadata();
489}
490
491bool MessagePlayer::setComponentNameList(std::map<uint32, std::string>& names) {
492 componentNameList.insert(names.begin(), names.end());
493 return writeMetadata();
494}
495
497 // If we are a reader
498 if (interval)
499 return false;
500
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();
504
505 std::stringstream metadataStream;
506 DataMessage* metadata = new DataMessage();
507 while (iType != eType) {
508 metadata->setString(iType->first, "subtype", iType->second.c_str());
509 metadataStream << "Subtype: " << iType->first << "=" << iType->second << "\n";
510 iType++;
511 }
512
513 while (iContext != eContext) {
514 metadata->setString(iContext->first, "subcontext", iContext->second.c_str());
515 metadataStream << "Subcontext: " << iContext->first << "=" << iContext->second << "\n";
516 iContext++;
517 }
518
519 while (iComp != eComp) {
520 metadata->setString(iComp->first, "component", iComp->second.c_str());
521 metadataStream << "Component: " << iComp->first << "=" << iComp->second << "\n";
522 iComp++;
523 }
524
525 if (!utils::WriteAFile(metadataFile.c_str(), (char*)(metadata->data), metadata->data->size, true))
526 return false;
527 if (!utils::WriteAFile(metadataTextFile.c_str(), metadataStream.str().c_str(), (uint32)metadataStream.str().length(), false))
528 return false;
529
530 return true;
531}
532
534 if (!msg || !msg->data->userCount)
535 return false;
536
537 std::map<int64, std::string> stringArray;
538 std::map<int64, std::string>::iterator i, e;
539
540 stringArray = msg->getStringArray("subtype");
541 if (stringArray.size()) {
542 subtypeList.clear();
543 i = stringArray.begin();
544 e = stringArray.end();
545 while (i != e) {
546 subtypeList[(uint16)(i->first)] = i->second;
547 i++;
548 }
549 }
550
551 stringArray = msg->getStringArray("subcontext");
552 if (stringArray.size()) {
553 subcontextList.clear();
554 i = stringArray.begin();
555 e = stringArray.end();
556 while (i != e) {
557 subcontextList[(uint16)(i->first)] = i->second;
558 i++;
559 }
560 }
561
562 stringArray = msg->getStringArray("component");
563 if (stringArray.size()) {
564 componentNameList.clear();
565 i = stringArray.begin();
566 e = stringArray.end();
567 while (i != e) {
568 componentNameList[(uint32)(i->first)] = i->second;
569 i++;
570 }
571 }
573 return true;
574}
575
577 // If we are a writer
578 if (!interval)
579 return false;
580
581 uint32 size;
582 char* data = utils::ReadAFile(metadataFile.c_str(), size, true);
583 if (!data || !size)
584 return false;
585
586 DataMessage* metadata = new DataMessage(data, false);
587
588 bool result = setSystemIDs(metadata);
589 delete metadata;
590 return result;
591}
592
593
594
595} // namespace cmlabs
Record and replay of DataMessage streams to/from disk.
static struct PsyType NOTYPE
The empty/unset message type (isValid() == false).
Definition Types.h:436
static struct PsyContext NOCONTEXT
The empty/unset context (isValid() == false).
Definition Types.h:437
#define MAXKEYNAMELEN
Definition Utils.h:85
#define LogPrint
Definition Utils.h:313
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)
Definition PsyTime.cpp:676
uint64 GetTimeNow()
Return the current absolute time (µs since year 0) according to the TMC.
Definition PsyTime.cpp:69
bool CreateADir(const char *dirname)
Create a directory (parents included where supported).
Definition Utils.cpp:7053
bool Sleep(uint32 ms)
Suspend the calling thread.
Definition Utils.cpp:2802
bool AppendToAFile(const char *filename, const char *data, uint32 length, bool binary=false)
Append to a file, creating it if missing.
Definition Utils.cpp:6905
bool MoveAFile(const char *oldfilename, const char *newfilename, bool force=false)
Move/rename a file.
Definition Utils.cpp:6957
FileDetails GetFileDetails(const char *filename)
Stat a file.
Definition Utils.cpp:7364
bool DoesAFileExist(const char *filename)
Test whether a regular file exists.
Definition Utils.cpp:7359
const char * stristr(const char *str, const char *substr, uint32 len=0)
Case-insensitive strstr.
Definition Utils.cpp:6035
bool DeleteAFile(const char *filename, bool force)
Delete a file.
Definition Utils.cpp:6933
std::string BytifySize(double val)
Format a byte count with binary units, e.g.
Definition Utils.cpp:7724
bool WriteAFile(const char *filename, const char *data, uint32 length, bool binary=false)
Write (create/overwrite) a file.
Definition Utils.cpp:6877
std::string StringFormat(const char *format,...)
printf into a std::string.
Definition Utils.cpp:6626
char * ReadAFile(const char *filename, uint32 &length, bool binary=false)
Read an entire file into a new buffer.
Definition Utils.cpp:6818
bool TextEndsWith(const char *str, const char *end, bool caseSensitive=true)
Test whether str ends with end.
Definition Utils.cpp:7233
uint32 strcpyavail(char *dst, const char *src, uint32 maxlen, bool copyAvailable)
Bounded strcpy that always NUL-terminates.
Definition Utils.cpp:6056
uint32 size
Total size of the whole message block in bytes (header + all entries).
uint32 userCount
Number of user entries currently in use.
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.
bool isValid()
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().
Definition Utils.h:1666