Welcome to mirror list, hosted at ThFree Co, Russian Federation.

ObjectModel.h « ObjectModel « src - github.com/Duet3D/RepRapFirmware.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: f400a60e51864629e514a75172c911c7d46d1ac6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
/*
 * ObjectModel.h
 *
 *  Created on: 27 Aug 2018
 *      Author: David
 */

#ifndef SRC_OBJECTMODEL_OBJECTMODEL_H_
#define SRC_OBJECTMODEL_OBJECTMODEL_H_

#include <RepRapFirmware.h>
#include <GCodes/GCodeException.h>

#if SUPPORT_OBJECT_MODEL

#include <General/IPAddress.h>
#include <General/Bitmap.h>
#include <RTOSIface/RTOSIface.h>
#include <Networking/NetworkDefs.h>

// Type codes to indicate what type of expression we have and how it is represented.
// The "Special" type is for items that we have to evaluate when we are ready to write them out, in particular strings whose storage might disappear.
enum class TypeCode : uint8_t
{
	None = 0,
	Bool,
	Char,
	Uint32,
	Int32,
	Uint64,			// only 56 bits actually available
	Float,
	Bitmap16,
	Bitmap32,
	Bitmap64,		// only 56 bits actually available
	Enum32,
	ObjectModel,
	CString,
	IPAddress,
	Array,
	DateTime,
	DriverId,
	MacAddress,
	Special,
#if SUPPORT_CAN_EXPANSION
	CanExpansionBoardDetails
#endif
};

// Dummy types, used to define type codes
class Bitmap32;
class Bitmap64;
class Enum32;
class SpecialString;

#if SUPPORT_CAN_EXPANSION

class CanExpansionBoardDetails;

enum class ExpansionDetail : uint32_t
{
	shortName, firmwareVersion, firmwareFileName
};

#endif

class ObjectModel;					// forward declaration
class ObjectModelArrayDescriptor;	// forward declaration

// Encapsulated time_t, used to facilitate overloading the ExpressionValue constructor
struct DateTime
{
	DateTime(time_t t) : tim(t) { }

	time_t tim;
};

// Forward declarations
class ObjectModelTableEntry;
class ObjectModel;

// Struct used to hold the expressions with polymorphic types
struct ExpressionValue
{
	uint32_t type : 8,								// what type is stored in the union
			 param : 24;							// additional parameter, e.g. number of usual displayed decimal places for a float,
													// or table # for an ObjectModel, or 24 extra bits for a date/time or a long bitmap
	union
	{
		bool bVal;
		char cVal;
		float fVal;
		int32_t iVal;
		uint32_t uVal;								// used for enumerations, bitmaps and IP addresses (not for integers, we always use int32_t for those)
		const char *sVal;
		const ObjectModel *omVal;					// object of some class derived form ObjectModel
		const ObjectModelArrayDescriptor *omadVal;
	};

	enum class SpecialType : uint32_t
	{
		sysDir = 0,
	};

	ExpressionValue() noexcept : type((uint32_t)TypeCode::None) { }
	explicit constexpr ExpressionValue(bool b) noexcept : type((uint32_t)TypeCode::Bool), param(0), bVal(b) { }
	explicit constexpr ExpressionValue(char c) noexcept : type((uint32_t)TypeCode::Char), param(0), cVal(c) { }
	explicit constexpr ExpressionValue(float f) noexcept : type((uint32_t)TypeCode::Float), param(MaxFloatDigitsDisplayedAfterPoint), fVal(f) { }
	constexpr ExpressionValue(float f, uint8_t numDecimalPlaces) noexcept : type((uint32_t)TypeCode::Float), param(numDecimalPlaces), fVal(f) { }
	explicit constexpr ExpressionValue(int32_t i) noexcept : type((uint32_t)TypeCode::Int32), param(0), iVal(i) { }
	explicit ExpressionValue(uint64_t u) noexcept : type((uint32_t)TypeCode::Uint64) { Set56BitValue(u); }
	explicit constexpr ExpressionValue(const ObjectModel *om) noexcept : type((om == nullptr) ? (uint32_t)TypeCode::None : (uint32_t)TypeCode::ObjectModel), param(0), omVal(om) { }
	constexpr ExpressionValue(const ObjectModel *om, uint8_t tableNumber) noexcept : type((om == nullptr) ? (uint32_t)TypeCode::None : (uint32_t)TypeCode::ObjectModel), param(tableNumber), omVal(om) { }
	explicit constexpr ExpressionValue(const char *s) noexcept : type((uint32_t)TypeCode::CString), param(0), sVal(s) { }
	explicit constexpr ExpressionValue(const ObjectModelArrayDescriptor *omad) noexcept : type((uint32_t)TypeCode::Array), param(0), omadVal(omad) { }
	explicit constexpr ExpressionValue(IPAddress ip) noexcept : type((uint32_t)TypeCode::IPAddress), param(0), uVal(ip.GetV4LittleEndian()) { }
	explicit constexpr ExpressionValue(nullptr_t dummy) noexcept : type((uint32_t)TypeCode::None), param(0), uVal(0) { }
	explicit ExpressionValue(DateTime t) noexcept : type((t.tim == 0) ? (uint32_t)TypeCode::None : (uint32_t)TypeCode::DateTime) { Set56BitValue(t.tim); }
	explicit ExpressionValue(DriverId id) noexcept : type((uint32_t)TypeCode::DriverId), param(0), uVal(id.AsU32()) { }
	explicit ExpressionValue(Bitmap<uint16_t> bm) noexcept : type((uint32_t)TypeCode::Bitmap16), param(0), uVal(bm.GetRaw()) { }
	explicit ExpressionValue(Bitmap<uint32_t> bm) noexcept : type((uint32_t)TypeCode::Bitmap32), param(0), uVal(bm.GetRaw()) { }
	explicit ExpressionValue(Bitmap<uint64_t> bm) noexcept : type((uint32_t)TypeCode::Bitmap64) { Set56BitValue(bm.GetRaw()); }
	explicit ExpressionValue(const MacAddress& mac) noexcept;
	explicit ExpressionValue(SpecialType s, uint32_t u) noexcept : type((uint32_t)TypeCode::Special), param((uint32_t)s), uVal(u) { }
#if SUPPORT_CAN_EXPANSION
	ExpressionValue(const char*s, ExpansionDetail p) noexcept : type((uint32_t)TypeCode::CanExpansionBoardDetails), param((uint32_t)p), sVal(s) { }
#endif

	TypeCode GetType() const noexcept { return (TypeCode)type; }
	void SetType(TypeCode t) noexcept { type = (uint32_t)t; }

	void Set(bool b) noexcept { type = (uint32_t)TypeCode::Bool; bVal = b; }
	void Set(char c) noexcept { type = (uint32_t)TypeCode::Char; cVal = c; }
	void Set(int32_t i) noexcept { type = (uint32_t)TypeCode::Int32; iVal = i; }
	void Set(float f) noexcept { type = (uint32_t)TypeCode::Float; fVal = f; param = 1; }
	void Set(const char *s) noexcept { type = (uint32_t)TypeCode::CString; sVal = s; }

	// Sore a 56-bit value
	void Set56BitValue(uint64_t v) { param = (uint32_t)(v >> 32) & 0x00FFFFFF; uVal = (uint32_t)v; }

	// Extract a 56-bit value that we have stored. Used to retrieve date/times and large bitmaps.
	uint64_t Get56BitValue() const noexcept { return ((uint64_t)param << 32) | uVal; }

	// Get the format string to use assuming this is a floating point number
	const char *GetFloatFormatString() const noexcept { return ::GetFloatFormatString(param); }

	// Append a string representation of this value to a string
	void AppendAsString(const StringRef& str) const noexcept;

#if SUPPORT_CAN_EXPANSION
	void ExtractRequestedPart(const StringRef& rslt) const noexcept pre(type == TYPE_OF(CanExpansionBoardDetails));
#endif
};

// Flags field of a table entry
enum class ObjectModelEntryFlags : uint8_t
{
	// none, live and verbose are alternatives occupying the bottom 2 bits
	none = 0,				// nothing special
	live = 1,				// fast changing data, included in common status response
	verbose = 2,			// omit reporting this value by default

	// canAlter can be or'ed in
	canAlter = 4,			// we can alter this value
	liveCanAlter = 5,		// we can alter this value
};

// Context passed to object model functions
class ObjectExplorationContext
{
public:
	// Constructor used when reporting the OM as JSON
	ObjectExplorationContext(bool wal, const char *reportFlags, unsigned int initialMaxDepth) noexcept;

	// Constructor used when evaluating expressions
	ObjectExplorationContext(bool wal, int p_line, int p_col) noexcept;

	void SetMaxDepth(unsigned int d) noexcept { maxDepth = d; }
	bool IncreaseDepth() noexcept { if (currentDepth < maxDepth) { ++currentDepth; return true; } return false; }
	void DecreaseDepth() noexcept { --currentDepth; }
	void AddIndex(int32_t index) THROWS(GCodeException);
	void AddIndex() THROWS(GCodeException);
	void RemoveIndex() THROWS(GCodeException);
	void ProvideIndex(int32_t index) THROWS(GCodeException);
	int32_t GetIndex(size_t n) const THROWS(GCodeException);
	int32_t GetLastIndex() const THROWS(GCodeException);
	size_t GetNumIndicesCounted() const noexcept { return numIndicesCounted; }
	bool ShortFormReport() const noexcept { return shortForm; }
	bool ShouldReport(const ObjectModelEntryFlags f) const noexcept;
	bool WantArrayLength() const noexcept { return wantArrayLength; }
	bool ShouldIncludeNulls() const noexcept { return includeNulls; }
	uint64_t GetStartMillis() const { return startMillis; }

	GCodeException ConstructParseException(const char *msg) const noexcept;
	GCodeException ConstructParseException(const char *msg, const char *sparam) const noexcept;

private:
	static constexpr size_t MaxIndices = 4;			// max depth of array nesting

	uint64_t startMillis;							// the milliseconds counter when we started exploring the OM. Stored so that upTime and msUpTime are consistent.
	unsigned int maxDepth;
	unsigned int currentDepth;
	size_t numIndicesProvided;						// the number of indices provided, when we are doing a value lookup
	size_t numIndicesCounted;						// the number of indices passed in the search string
	int32_t indices[MaxIndices];
	int line;
	int column;
	unsigned int shortForm : 1,
				onlyLive : 1,
				includeVerbose : 1,
				wantArrayLength : 1,
				includeNulls : 1;
};

// Entry to describe an array of objects or values. These must be brace-initializable into flash memory.
class ObjectModelArrayDescriptor
{
public:
	ReadWriteLock *lockPointer;
	size_t (*GetNumElements)(const ObjectModel*, const ObjectExplorationContext&) noexcept;
	ExpressionValue (*GetElement)(const ObjectModel*, ObjectExplorationContext&) noexcept;
};

struct ObjectModelClassDescriptor;

// Class from which other classes that represent part of the object model are derived
class ObjectModel
{
public:
	ObjectModel() noexcept;
	virtual ~ObjectModel() { }

	// Construct a JSON representation of those parts of the object model requested by the user. This version is called on the root of the tree.
	void ReportAsJson(OutputBuffer *buf, const char *filter, const char *reportFlags, bool wantArrayLength) const THROWS(GCodeException);

	// Get the value of an object via the table
	ExpressionValue GetObjectValue(ObjectExplorationContext& context, const ObjectModelClassDescriptor * null classDescriptor, const char *idString, uint8_t tableNumber = 0) const THROWS(GCodeException);

	// Function to report a value or object as JSON
	void ReportItemAsJson(OutputBuffer *buf, ObjectExplorationContext& context, const ObjectModelClassDescriptor *classDescriptor,
							const ExpressionValue& val, const char *filter) const THROWS(GCodeException);

	// Skip the current element in the ID or filter string
	static const char* GetNextElement(const char *id) noexcept;

protected:
	// Construct a JSON representation of those parts of the object model requested by the user
	void ReportAsJson(OutputBuffer *buf, ObjectExplorationContext& context, const ObjectModelClassDescriptor * null classDescriptor, uint8_t tableNumber, const char *filter) const THROWS(GCodeException);

	// Report an entire array as JSON
	void ReportArrayAsJson(OutputBuffer *buf, ObjectExplorationContext& context, const ObjectModelClassDescriptor *classDescriptor, const ObjectModelArrayDescriptor *omad, const char *filter) const THROWS(GCodeException);

	// Get the value of an object that we hold
	ExpressionValue GetObjectValue(ObjectExplorationContext& context, const ObjectModelClassDescriptor *classDescriptor, const ExpressionValue& val, const char *idString) const THROWS(GCodeException);

	// Get the object model table entry for the current level object in the query
	const ObjectModelTableEntry *FindObjectModelTableEntry(const ObjectModelClassDescriptor *classDescriptor, uint8_t tableNumber, const char *idString) const noexcept;

	virtual const ObjectModelClassDescriptor *GetObjectModelClassDescriptor() const noexcept = 0;

private:
	// These functions have been separated from ReportItemAsJson to avoid high stack usage in the recursive functions, therefore they must not be inlined
	__attribute__ ((noinline)) void ReportArrayLengthAsJson(OutputBuffer *buf, ObjectExplorationContext& context, const ExpressionValue& val) const noexcept;
	__attribute__ ((noinline)) void ReportItemAsJsonFull(OutputBuffer *buf, ObjectExplorationContext& context, const ObjectModelClassDescriptor *classDescriptor,
															const ExpressionValue& val, const char *filter) const THROWS(GCodeException);
	__attribute__ ((noinline)) static void ReportDateTime(OutputBuffer *buf, const ExpressionValue& val) noexcept;
	__attribute__ ((noinline)) static void ReportFloat(OutputBuffer *buf, const ExpressionValue& val) noexcept;
	__attribute__ ((noinline)) static void ReportBitmap1632Long(OutputBuffer *buf, const ExpressionValue& val) noexcept;
	__attribute__ ((noinline)) static void ReportBitmap64Long(OutputBuffer *buf, const ExpressionValue& val) noexcept;

#if SUPPORT_CAN_EXPANSION
	__attribute__ ((noinline)) static void ReportExpansionBoardDetail(OutputBuffer *buf, const ExpressionValue& val) noexcept;
	__attribute__ ((noinline)) static ExpressionValue GetExpansionBoardDetailLength(const ExpressionValue& val) noexcept;
#endif

};

// Function used for compile-time check for the correct number of entries in an object model table
static inline constexpr size_t ArraySum(const uint8_t *arr, size_t numEntries) noexcept
{
	return (numEntries == 0) ? 0 : arr[0] + ArraySum(arr + 1, numEntries - 1);
}

// Object model table entry
// It must be possible to construct these in the form of initialised data in flash memory, to avoid using large amounts of RAM.
// Therefore we can't use a class hierarchy to represent different types of entry.
class ObjectModelTableEntry
{
public:
	// Type declarations
	// Type of the function pointer in the table entry, that returns the data
	typedef ExpressionValue(*DataFetchPtr_t)(const ObjectModel*, ObjectExplorationContext&) noexcept;

	// Member data. This must be public so that we can brace-initialise table entries.
	const char * name;				// name of this field
	DataFetchPtr_t func;			// function that yields this value
	ObjectModelEntryFlags flags;	// information about this value

	// Member functions. These must all be 'const'.

	// Return true if this object table entry matches a filter or query
	bool Matches(const char *filter, const ObjectExplorationContext& context) const noexcept;

	// See whether we should add the value of this element to the buffer, returning true if it matched the filter and we did add it
	bool ReportAsJson(OutputBuffer* buf, ObjectExplorationContext& context, const ObjectModelClassDescriptor *classDescriptor, const ObjectModel *self, const char* filter, bool first) const noexcept;

	// Return the name of this field
	const char* GetName() const noexcept { return name; }

	// Compare the name of this field with the filter string that we are trying to match
	int IdCompare(const char *id) const noexcept;

	// Return true if a section of the OMT is ordered
	static inline constexpr bool IsOrdered(const ObjectModelTableEntry *omt, size_t len) noexcept
	{
		return len <= 1 || (strcmp(omt[1].name, omt[0].name) == 1 && IsOrdered(omt + 1, len - 1));
	}

	// Return true if a section of the OMT specified by the descriptor is ordered
	static inline constexpr bool IsOrdered(uint8_t sectionsLeft, const uint8_t *descriptorSection, const ObjectModelTableEntry *omt) noexcept
	{
		return sectionsLeft == 0 || (IsOrdered(omt, *descriptorSection) && IsOrdered(sectionsLeft - 1, descriptorSection + 1, omt + *descriptorSection));
	}

	// Return true if the whole OMT is ordered
	static inline constexpr bool IsOrdered(const uint8_t *descriptor, const ObjectModelTableEntry *omt) noexcept
	{
		return IsOrdered(descriptor[0], descriptor + 1, omt);
	}
};

struct ObjectModelClassDescriptor
{
	const ObjectModelTableEntry *omt;
	const uint8_t *omd;
	const ObjectModelClassDescriptor *parent;
};

// Use this macro to inherit form ObjectModel
#define INHERIT_OBJECT_MODEL	: public ObjectModel

// Use this macro in the 'protected' section of every class declaration that derived from ObjectModel
#define DECLARE_OBJECT_MODEL \
	const ObjectModelClassDescriptor *GetObjectModelClassDescriptor() const noexcept override; \
	static const ObjectModelTableEntry objectModelTable[]; \
	static const uint8_t objectModelTableDescriptor[]; \
	static const ObjectModelClassDescriptor objectModelClassDescriptor;

#define DECLARE_OBJECT_MODEL_VIRTUAL \
	virtual const ObjectModelClassDescriptor *GetObjectModelClassDescriptor() const noexcept override = 0;

#define DESCRIPTOR_OK(_class) 	(ARRAY_SIZE(_class::objectModelTableDescriptor) == _class::objectModelTableDescriptor[0] + 1)
#define OMT_SIZE_OK(_class)		(ARRAY_SIZE(_class::objectModelTable) == ArraySum(_class::objectModelTableDescriptor + 1, ARRAY_SIZE(_class::objectModelTableDescriptor) - 1))
#define OMT_ORDERING_OK(_class)	(ObjectModelTableEntry::IsOrdered(_class::objectModelTableDescriptor, _class::objectModelTable))

#define DEFINE_GET_OBJECT_MODEL_TABLE(_class) \
	const ObjectModelClassDescriptor _class::objectModelClassDescriptor = { _class::objectModelTable, _class::objectModelTableDescriptor, nullptr }; \
	const ObjectModelClassDescriptor *_class::GetObjectModelClassDescriptor() const noexcept \
	{ \
		static_assert(DESCRIPTOR_OK(_class), "Bad descriptor length"); \
		static_assert(!DESCRIPTOR_OK(_class) || OMT_SIZE_OK(_class), "Mismatched object model table and descriptor"); \
		static_assert(!DESCRIPTOR_OK(_class) || !OMT_SIZE_OK(_class) || OMT_ORDERING_OK(_class), "Object model table must be ordered"); \
		return &objectModelClassDescriptor; \
	}

#define DEFINE_GET_OBJECT_MODEL_TABLE_WITH_PARENT(_class, _parent) \
	const ObjectModelClassDescriptor _class::objectModelClassDescriptor = { _class::objectModelTable, _class::objectModelTableDescriptor, &_parent::objectModelClassDescriptor }; \
	const ObjectModelClassDescriptor *_class::GetObjectModelClassDescriptor() const noexcept \
	{ \
		static_assert(DESCRIPTOR_OK(_class), "Bad descriptor length"); \
		static_assert(!DESCRIPTOR_OK(_class) || OMT_SIZE_OK(_class), "Mismatched object model table and descriptor"); \
		static_assert(!DESCRIPTOR_OK(_class) || !OMT_SIZE_OK(_class) || OMT_ORDERING_OK(_class), "Object model table must be ordered"); \
		return &objectModelClassDescriptor; \
	}

#define OBJECT_MODEL_FUNC_BODY(_class,...) [] (const ObjectModel* arg, ObjectExplorationContext& context) noexcept \
	{ const _class * const self = static_cast<const _class*>(arg); return ExpressionValue(__VA_ARGS__); }
#define OBJECT_MODEL_FUNC_IF_BODY(_class,_condition,...) [] (const ObjectModel* arg, ObjectExplorationContext& context) noexcept \
	{ const _class * const self = static_cast<const _class*>(arg); return (_condition) ? ExpressionValue(__VA_ARGS__) : ExpressionValue(nullptr); }
#define OBJECT_MODEL_FUNC_NOSELF(...) [] (const ObjectModel* arg, ObjectExplorationContext& context) noexcept { return ExpressionValue(__VA_ARGS__); }
#define OBJECT_MODEL_FUNC_IF_NOSELF(_condition,...) [] (const ObjectModel* arg, ObjectExplorationContext& context) noexcept \
	{ return (_condition) ? ExpressionValue(__VA_ARGS__) : ExpressionValue(nullptr); }
#define OBJECT_MODEL_ARRAY(_name)	static const ObjectModelArrayDescriptor _name ## ArrayDescriptor;

#else

#define INHERIT_OBJECT_MODEL			// nothing
#define DECLARE_OBJECT_MODEL			// nothing
#define DECLARE_OBJECT_MODEL_VIRTUAL	// nothing
#define DEFINE_GET_OBJECT_MODEL_TABLE	// nothing
#define OBJECT_MODEL_ARRAY(_name)		// nothing

#endif

#endif /* SRC_OBJECTMODEL_OBJECTMODEL_H_ */