11 intrinsicTimescaleMS = 60000;
13 motionStartedTime = 0;
30 XMLNode node = XMLNode::parseString(xml,
"vantagepoints", &xmlResults);
50 LogPrint(0, 0, 1,
"No points found");
54 inUseMap =
new uint32[
count+1];
55 memset(inUseMap, 0, (
count+1) *
sizeof(uint32));
57 std::vector<std::string> overlapsStr;
58 std::vector<std::string>::iterator i, e;
60 const char* id, *label, *data, *overlap;
62 while (!(subNode = node.
getChildNode(
"point", n++)).isEmpty()) {
69 LogPrint(0, 0, 1,
"Point %s ID is too high: %u > %u",
id, point.
id,
count);
72 if (points.find(point.
id) != points.end())
80 if (overlapsStr.size() == 0) {
82 for (uint32 n = 0; n <=
count; n++)
86 if (overlapsStr.size() !=
count) {
87 LogPrint(0, 0, 1,
"Not enough overlaps for point %s, need %u, only found %u",
id,
count, overlapsStr.size());
92 i = overlapsStr.begin();
93 e = overlapsStr.end();
101 points[point.
id] = point;
105 LogPrint(0, 0, 1,
"Point id or name not specified");
107 LogPrint(0, 0, 1,
"Point %s ID has no overlap specified", point.
id);
109 LogPrint(0, 0, 1,
"Point %s ID has no data specified", point.
id);
121 return perch->
data.c_str();
128 return perch->
label.c_str();
133 if (!
count || (motionStartedTime && (
GetTimeAgeMS(motionStartedTime) < timeoutMS)))
140 uint32 ownPerch = 0, bestPerch = 0, occPerch = 0, eID;
145 for (n = 1; n <=
count; n++) {
146 if (!(eID = inUseMap[n]))
148 perch = &(points[n]);
152 inUseMap[perch->
id] = 0;
156 if (!ownPerch && (eID == entityID)) {
166 perch = &(points[ownPerch]);
168 perch = &(points[occPerch]);
170 for (n = 1; n <=
count; n++) {
173 if (intrinsicTimescaleMS)
174 val *= ((double)
GetTimeAgeMS((&(points[n]))->lastVisit) / intrinsicTimescaleMS);
190 perch = &(points[ownPerch]);
194 inUseMap[perch->
id] = 0;
197 perch = &(points[bestPerch]);
201 inUseMap[perch->
id] = entityID;
203 motionStartedTime = now;
208 if (!
count || !
id || (
id >
count) || !entityID)
219 motionStartedTime = 0;
224 if (!
count || !
id || (
id >
count) || !entityID)
236 motionStartedTime = 0;
247 "<vantagepoints timescale=\"60\" timeout=\"60\">\n"
248 " <point id=\"1\" label=\"A\" data=\"2,2,45,6\" overlap=\"1.0,0.3,0.5\" />\n"
249 " <point id=\"2\" label=\"B\" data=\"3,3,90,7\" overlap=\"0.3,1.0,0.2\" />\n"
250 " <point id=\"3\" label=\"C\" data=\"4,4,30,8\" overlap=\"0.5,0.2,1.0\" />\n"
251 "</vantagepoints>\n";
255 if (!vp.
init(xml)) {
unittest::fail(
"VantagePoints test: init from XML failed");
return false; }
262 if (!lbl || strcmp(lbl,
"A") != 0) {
unittest::fail(
"VantagePoints test: getPointLabel(1) != A");
return false; }
264 if (!dat || strcmp(dat,
"3,3,90,7") != 0) {
unittest::fail(
"VantagePoints test: getPointData(2) mismatch");
return false; }
271 uint32 entity = 1001;
274 if (reserved != 1) {
unittest::fail(
"VantagePoints test: first reserve expected id 1 (got %u)", reserved);
return false; }
281 if (vp.
occupyPoint(reserved, 9999)) {
unittest::fail(
"VantagePoints test: occupy with wrong entity succeeded");
return false; }
283 if (!vp.
occupyPoint(reserved, entity)) {
unittest::fail(
"VantagePoints test: occupy with correct entity failed");
return false; }
286 if (vp.
occupyPoint(reserved, entity)) {
unittest::fail(
"VantagePoints test: re-occupy of OCCUPIED point succeeded");
return false; }
293 if (!vp.
init(xml)) {
unittest::fail(
"VantagePoints test: re-init failed");
return false; }
296 if (!reserved) {
unittest::fail(
"VantagePoints test: reserve returned 0");
return false; }
298 if (vp.
freePoint(reserved, 1234)) {
unittest::fail(
"VantagePoints test: free with wrong entity succeeded");
return false; }
300 if (!vp.
freePoint(reserved, entity)) {
unittest::fail(
"VantagePoints test: free with correct entity failed");
return false; }
302 if (vp.
freePoint(reserved, entity)) {
unittest::fail(
"VantagePoints test: free of FREE point succeeded");
return false; }
309 if (vp.
count != 0) {
unittest::fail(
"VantagePoints test: fresh count != 0");
return false; }
314 if (vp.
init((
const char*)NULL)) {
unittest::fail(
"VantagePoints test: init(NULL) succeeded");
return false; }
320 const uint32 iters = 2000;
323 for (uint32 i = 0; i < iters; i++) {
332 if (ok != iters) {
unittest::fail(
"VantagePoints test: throughput loop incomplete (%u/%u)", ok, iters);
return false; }
334 unittest::metric(
"reserve_occupy_throughput", (
double)iters / us * 1e6,
"ops/s",
true);
335 unittest::detail(
"completed %u reserve/occupy cycles in %.0f us", iters, us);
344 "Vantage point reserve/occupy/free lifecycle and XML init",
"data");
CMSDK time: µs-resolution 64-bit timestamps and the Time Mapping Constant (TMC).
Small, dependency-free unit test harness used by all CMSDK object tests.
Management of a fixed set of spatial "perch" points that entities can reserve, occupy and free.
static UnitTestRunner & instance()
Access the singleton (created on first use).
void registerTest(const char *name, UnitTestFunc func, const char *description="", const char *category="", bool inDefaultRun=true)
Register a test with the runner.
VantageState state
Current system state.
bool occupyPoint(uint32 id, uint32 entityID)
Mark a reserved point as occupied.
static bool UnitTest()
Self test.
const char * getPointData(uint32 id)
Opaque data string attached to a point in the XML.
const char * getPointLabel(uint32 id)
Label of a point.
uint32 count
Number of perch points.
uint32 reserveNextPoint(uint32 entityID)
Reserve the best available (least-overlapping free) point for an entity.
bool freePoint(uint32 id, uint32 entityID)
Release a point held by entityID.
bool init(const char *xml)
Parse the configuration from XML text.
uint64 GetTimeNow()
Return the current absolute time (µs since year 0) according to the TMC.
int32 GetTimeAgeMS(uint64 t)
Age of a timestamp relative to now, in milliseconds.
uint32 Ascii2Uint32(const char *ascii, uint32 start=0, uint32 end=0)
Parse an unsigned 32-bit decimal integer from a substring.
std::vector< std::string > TextListSplit(const char *text, const char *split, bool keepEmpty=true, bool autoTrim=false)
Split text on a separator.
float64 Ascii2Float64(const char *ascii, uint32 start=0, uint32 end=0)
Parse a 64-bit float from a substring (decimal point, not locale dependent).
void fail(const char *fmt,...)
Set an explanatory reason shown on the FAIL line.
void metric(const char *name, double value, const char *unit="", bool higherIsBetter=true)
Record a performance metric.
void detail(const char *fmt,...)
Verbose-only indented diagnostic line (shown only when verbose=1).
void progress(int percent, const char *action)
Report progress with a short description of the current action.
void Register_VantagePoints_Tests()
One perch: id, occupancy state, occupying entity, last-visit time, label, opaque data and overlap wei...
std::vector< double > overlaps
XMLAttribute getAttribute(int i=0) const
XMLNode getChildNode(int i=0) const
int nChildNode(XMLCSTR name) const