add /templates endpoint and include types+templates+conditions+chains in dump output

This commit is contained in:
John
2023-10-22 12:17:43 +02:00
parent b752062d5c
commit b901232678
9 changed files with 311 additions and 22 deletions
+119 -10
View File
@@ -2,7 +2,7 @@ openapi: 3.1.0
info: info:
title: ebusd-http title: ebusd-http
description: The API that ebusd provides on HTTP port. description: The API that ebusd provides on HTTP port.
version: "23.2" version: "23.3"
servers: servers:
- url: http://127.0.0.1:8080/ - url: http://127.0.0.1:8080/
paths: paths:
@@ -145,6 +145,50 @@ paths:
500: 500:
description: General error. description: General error.
content: { } content: { }
/templates:
get:
summary: Get all known field templates for the root.
responses:
200:
description: Success.
content:
application/json;charset=utf-8:
schema:
$ref: '#/components/schemas/Templates'
400:
description: Invalid request parameters.
content: { }
403:
description: User not authorized.
content: { }
500:
description: General error.
content: { }
/templates/{path}:
get:
summary: Get all known field templates for the path.
parameters:
- name: path
in: path
required: true
schema:
type: string
responses:
200:
description: Success.
content:
application/json;charset=utf-8:
schema:
$ref: '#/components/schemas/Templates'
400:
description: Invalid request parameters.
content: { }
403:
description: User not authorized.
content: { }
500:
description: General error.
content: { }
/raw: /raw:
get: get:
summary: Retrieve raw data from grabbed and/or decoded messages. summary: Retrieve raw data from grabbed and/or decoded messages.
@@ -392,8 +436,12 @@ components:
type: integer type: integer
minimum: 0 minimum: 0
condition: condition:
type: string description: the condition(s) in case of a conditional message (only with full).
description: the condition string in case of a conditional message (only with full). oneOf:
- $ref: '#/components/schemas/Condition'
- type: array
items:
$ref: '#/components/schemas/Condition'
lastup: lastup:
$ref: '#/components/schemas/Seconds' $ref: '#/components/schemas/Seconds'
description: the time in UTC seconds of the last update of the message (0 description: the time in UTC seconds of the last update of the message (0
@@ -407,8 +455,12 @@ components:
description: destination master or slave address. description: destination master or slave address.
example: 8 example: 8
id: id:
$ref: '#/components/schemas/Symbols' description: the message ID composed of PBSB and further master data bytes (only with def), or an array thereof in case of a chained message.
description: the message ID composed of PBSB and further master data bytes (only with def). oneOf:
- $ref: '#/components/schemas/Symbols'
- type: array
items:
$ref: '#/components/schemas/Symbols'
comment: comment:
type: string type: string
description: the message comment (only with verbose). description: the message comment (only with verbose).
@@ -465,10 +517,10 @@ components:
- type: string - type: string
- type: number - type: number
nullable: true nullable: true
FieldDef: FieldTemplate:
description: a single field template.
required: required:
- name - name
- slave
- type - type
- isbits - isbits
- length - length
@@ -477,9 +529,6 @@ components:
name: name:
type: string type: string
description: the field name. description: the field name.
slave:
type: boolean
description: whether the field is part of the slave data.
type: type:
type: string type: string
description: the field type. description: the field type.
@@ -513,6 +562,66 @@ components:
comment: comment:
type: string type: string
description: the field comment. description: the field comment.
FieldDef:
description: a single field definition.
allOf:
- $ref: '#/components/schemas/FieldTemplate'
- type: object
required:
- slave
properties:
slave:
type: boolean
description: whether the field is part of the slave data.
Templates:
type: array
description: list of known field templates.
items:
oneOf:
- $ref: '#/components/schemas/FieldTemplate'
- type: object
required:
- name
- sequence
properties:
name:
type: string
description: the template set name.
sequence:
type: array
description: the sequence of fields.
items:
$ref: '#/components/schemas/FieldTemplate'
Condition:
description: a single condition.
required:
- name
- message
properties:
name:
type: string
description: name of the condition.
message:
type: string
description: name of the referenced message.
circuit:
type: string
description: name of the referenced circuit.
zz:
maximum: 255
minimum: 0
type: integer
description: the circuit slave address.
field:
type: string
description: the field name in the referenced message.
value:
type: array
description: the value ranges for the condition.
items:
oneOf:
- type: number
- type: string
DataType: DataType:
description: a known field data type. description: a known field data type.
type: object type: object
+7
View File
@@ -352,7 +352,14 @@ int main(int argc, char* argv[], char* envp[]) {
} else { } else {
logNotice(lf_main, "configuration dump:"); logNotice(lf_main, "configuration dump:");
} }
*out << "{\"datatypes\":[";
DataTypeList::getInstance()->dump(s_opt.dumpConfig, true, out);
*out << "],\"templates\":[";
const auto tmpl = s_scanHelper->getTemplates("");
tmpl->dump(s_opt.dumpConfig, out);
*out << "],\"messages\":";
s_messageMap->dump(true, s_opt.dumpConfig, out); s_messageMap->dump(true, s_opt.dumpConfig, out);
*out << "}";
if (fout.is_open()) { if (fout.is_open()) {
fout.close(); fout.close();
} }
+12
View File
@@ -2254,6 +2254,18 @@ result_t MainLoop::executeGet(const vector<string>& args, bool* connected, ostri
return formatHttpResult(ret, type, ostream); return formatHttpResult(ret, type, ostream);
} }
if (uri == "/templates" || uri.substr(0, 11) == "/templates/") {
*ostream << "[";
OutputFormat verbosity = OF_NAMES|OF_JSON|OF_ALL_ATTRS;
string name = uri == "/templates" ? "" : uri.substr(11) + "/";
const auto tmpl = m_scanHelper->getTemplates(name);
tmpl->dump(verbosity, ostream);
*ostream << "\n]";
type = 6;
*connected = false;
return formatHttpResult(ret, type, ostream);
}
if (uri == "/raw") { if (uri == "/raw") {
time_t since = 0, until = 0; time_t since = 0, until = 0;
bool onlyUnknown = false; bool onlyUnknown = false;
+13
View File
@@ -202,6 +202,19 @@ bool ScanHelper::readTemplates(const string relPath, const string extension, boo
return false; return false;
} }
void ScanHelper::dumpTemplates(OutputFormat outputFormat, ostream* output) const {
bool prependSeparator = false;
for (auto it : m_templatesByPath) {
if (prependSeparator) {
*output << ",";
}
const auto templates = it.second;
if (templates->dump(outputFormat, output)) {
prependSeparator = true;
}
}
}
result_t ScanHelper::readConfigFiles(const string& relPath, const string& extension, bool recursive, result_t ScanHelper::readConfigFiles(const string& relPath, const string& extension, bool recursive,
string* errorDescription) { string* errorDescription) {
vector<string> files, dirs; vector<string> files, dirs;
+7
View File
@@ -158,6 +158,13 @@ class ScanHelper : public Resolver {
*/ */
bool readTemplates(const string relPath, const string extension, bool available); bool readTemplates(const string relPath, const string extension, bool available);
/**
* Dump the loaded @a DataFieldTemplates to the output.
* @param outputFormat the @a OutputFormat options.
* @param output the @a ostream to dump to.
*/
void dumpTemplates(OutputFormat outputFormat, ostream* output) const;
/** /**
* Read the configuration files from the specified path. * Read the configuration files from the specified path.
* @param relPath the relative path from which to read the files (without trailing "/"). * @param relPath the relative path from which to read the files (without trailing "/").
+27 -1
View File
@@ -482,7 +482,10 @@ void SingleDataField::dumpPrefix(bool prependFieldSeparator, OutputFormat output
dumpString(prependFieldSeparator, m_name, output); dumpString(prependFieldSeparator, m_name, output);
} }
if (outputFormat & OF_JSON) { if (outputFormat & OF_JSON) {
*output << ", \"slave\": " << (m_partType == pt_slaveData ? "true" : "false") << ", "; if (m_partType != pt_any) {
*output << ", \"slave\": " << (m_partType == pt_slaveData ? "true" : "false");
}
*output << ", ";
} else { } else {
*output << FIELD_SEPARATOR; *output << FIELD_SEPARATOR;
if (m_partType == pt_masterData) { if (m_partType == pt_masterData) {
@@ -1459,4 +1462,27 @@ const DataField* DataFieldTemplates::get(const string& name) const {
return ref->second; return ref->second;
} }
bool DataFieldTemplates::dump(OutputFormat outputFormat, ostream* output) const {
bool prependFieldSeparator = false;
for (const auto &it : m_fieldsByName) {
const DataField *dataField = it.second;
if (outputFormat & OF_JSON) {
if (dataField->isSet()) {
if (prependFieldSeparator) {
*output << ",\n";
}
*output << "{\"name\":\"" << dataField->getName(-1) << "\", \"sequence\": [";
dataField->dump(false, outputFormat, output);
*output << "]}";
} else {
dataField->dump(prependFieldSeparator, outputFormat, output);
}
} else {
dataField->dump(prependFieldSeparator, outputFormat, output);
}
prependFieldSeparator = true;
}
return !prependFieldSeparator;
}
} // namespace ebusd } // namespace ebusd
+8
View File
@@ -836,6 +836,14 @@ class DataFieldTemplates : public MappedFileReader {
*/ */
const DataField* get(const string& name) const; const DataField* get(const string& name) const;
/**
* Dump the templates to the output.
* @param outputFormat the @a OutputFormat options.
* @param output the @a ostream to dump to.
* @return true when a template was written to the output.
*/
bool dump(OutputFormat outputFormat, ostream* output) const;
private: private:
/** the known template @a DataField instances by name. */ /** the known template @a DataField instances by name. */
+86 -11
View File
@@ -970,9 +970,8 @@ void Message::decodeJson(bool leadingSeparator, bool appendDirectionCondition, b
*output << ",\n \"pollprio\": " << setw(0) << dec << getPollPriority(); *output << ",\n \"pollprio\": " << setw(0) << dec << getPollPriority();
} }
if (isConditional()) { if (isConditional()) {
*output << ",\n \"condition\": \""; *output << ",\n \"condition\": ";
m_condition->dump(false, output); m_condition->dumpJson(output);
*output << "\"";
} }
} }
if (withData) { if (withData) {
@@ -985,13 +984,8 @@ void Message::decodeJson(bool leadingSeparator, bool appendDirectionCondition, b
} }
*output << ",\n \"zz\": " << dec << static_cast<unsigned>(m_dstAddress); *output << ",\n \"zz\": " << dec << static_cast<unsigned>(m_dstAddress);
if (withDefinition) { if (withDefinition) {
*output << ",\n \"id\": [" << dec; *output << ",\n \"id\": [";
for (auto it = m_id.begin(); it < m_id.end(); it++) { dumpIdsJson(output);
if (it > m_id.begin()) {
*output << ", ";
}
*output << dec << static_cast<unsigned>(*it);
}
*output << "]"; *output << "]";
} }
appendAttributes(outputFormat, output); appendAttributes(outputFormat, output);
@@ -1022,6 +1016,15 @@ void Message::decodeJson(bool leadingSeparator, bool appendDirectionCondition, b
*output << "\n }"; *output << "\n }";
} }
void Message::dumpIdsJson(ostringstream* output) const {
for (auto it = m_id.begin(); it < m_id.end(); it++) {
if (it > m_id.begin()) {
*output << ", ";
}
*output << dec << static_cast<unsigned>(*it);
}
}
bool Message::setDataHandlerState(int state, bool addBits) { bool Message::setDataHandlerState(int state, bool addBits) {
if (addBits ? state == (m_dataHandlerState&state) : state == m_dataHandlerState) { if (addBits ? state == (m_dataHandlerState&state) : state == m_dataHandlerState) {
return false; return false;
@@ -1296,6 +1299,22 @@ void ChainedMessage::dumpField(const string& fieldName, bool withConditions, Out
} }
} }
void ChainedMessage::dumpIdsJson(ostringstream* output) const {
for (auto idsit = m_ids.begin(); idsit < m_ids.end(); idsit++) {
if (idsit > m_ids.begin()) {
*output << ",";
}
*output << "[";
for (auto it = (*idsit).begin(); it < (*idsit).end(); it++) {
if (it > (*idsit).begin()) {
*output << ", ";
}
*output << dec << static_cast<unsigned>(*it);
}
*output << "]";
}
}
/** /**
* Get the first available @a Message from the list. * Get the first available @a Message from the list.
@@ -1527,6 +1546,27 @@ void SimpleCondition::dump(bool matched, ostream* output) const {
} }
} }
void SimpleCondition::dumpJson(ostream* output) const {
*output << "{\"name\": \"" << m_refName << "\"";
if (!m_circuit.empty()) {
*output << ",\"circuit\":\"" << m_circuit << "\"";
}
*output << ",\"message\":\"" << (m_name.empty() ? "scan" : m_name) << "\"";
if (m_dstAddress != SYN) {
*output << ",\"zz\":" << dec << static_cast<unsigned>(m_dstAddress);
}
if (!m_field.empty()) {
*output << ",\"field\":\"" << m_field << "\"";
}
if (m_hasValues) {
*output << ",\"value\":[";
dumpValuesJson(output);
*output << "]";
}
*output << "}";
}
CombinedCondition* SimpleCondition::combineAnd(Condition* other) { CombinedCondition* SimpleCondition::combineAnd(Condition* other) {
CombinedCondition* ret = new CombinedCondition(); CombinedCondition* ret = new CombinedCondition();
return ret->combineAnd(this)->combineAnd(other); return ret->combineAnd(this)->combineAnd(other);
@@ -1625,6 +1665,17 @@ bool SimpleNumericCondition::checkValue(const Message* message, const string& fi
return false; return false;
} }
void SimpleNumericCondition::dumpValuesJson(ostream* output) const {
bool first = true;
for (const auto value : m_valueRanges) {
if (!first) {
*output << ",";
}
*output << static_cast<unsigned>(value);
first = false;
}
}
bool SimpleStringCondition::checkValue(const Message* message, const string& field) { bool SimpleStringCondition::checkValue(const Message* message, const string& field) {
ostringstream output; ostringstream output;
@@ -1641,6 +1692,17 @@ bool SimpleStringCondition::checkValue(const Message* message, const string& fie
return false; return false;
} }
void SimpleStringCondition::dumpValuesJson(ostream* output) const {
bool first = true;
for (const auto value : m_values) {
if (!first) {
*output << ",";
}
*output << "\"" << value << "\"";
first = false;
}
}
void CombinedCondition::dump(bool matched, ostream* output) const { void CombinedCondition::dump(bool matched, ostream* output) const {
for (const auto condition : m_conditions) { for (const auto condition : m_conditions) {
@@ -1648,6 +1710,19 @@ void CombinedCondition::dump(bool matched, ostream* output) const {
} }
} }
void CombinedCondition::dumpJson(ostream* output) const {
*output << "[";
bool first = true;
for (const auto condition : m_conditions) {
if (!first) {
*output << ",";
}
condition->dumpJson(output);
first = false;
}
*output << "]";
}
result_t CombinedCondition::resolve(void (*readMessageFunc)(Message* message), MessageMap* messages, result_t CombinedCondition::resolve(void (*readMessageFunc)(Message* message), MessageMap* messages,
ostringstream* errorMessage) { ostringstream* errorMessage) {
for (const auto condition : m_conditions) { for (const auto condition : m_conditions) {
@@ -2817,7 +2892,7 @@ void MessageMap::dump(bool withConditions, OutputFormat outputFormat, ostream* o
bool first = true; bool first = true;
bool isJson = (outputFormat & OF_JSON) != 0; bool isJson = (outputFormat & OF_JSON) != 0;
if (isJson) { if (isJson) {
*output << (m_addAll ? "[" : "}"); *output << (m_addAll ? "[" : "{");
} else { } else {
Message::dumpHeader(nullptr, output); Message::dumpHeader(nullptr, output);
} }
+32
View File
@@ -601,6 +601,12 @@ class Message : public AttributedItem {
OutputFormat outputFormat, ostringstream* output) const; OutputFormat outputFormat, ostringstream* output) const;
protected: protected:
/**
* Dump the ID(s) to the output in JSON format.
* @param output the @a ostringstream to append to.
*/
virtual void dumpIdsJson(ostringstream* output) const;
/** the source filename. */ /** the source filename. */
const string m_filename; const string m_filename;
@@ -748,6 +754,8 @@ class ChainedMessage : public Message {
result_t prepareMasterPart(size_t index, const char separator, istringstream* input, result_t prepareMasterPart(size_t index, const char separator, istringstream* input,
MasterSymbolString* master) override; MasterSymbolString* master) override;
// @copydoc
void dumpIdsJson(ostringstream* output) const override;
public: public:
// @copydoc // @copydoc
@@ -884,6 +892,18 @@ class Condition {
*/ */
virtual void dump(bool matched, ostream* output) const = 0; virtual void dump(bool matched, ostream* output) const = 0;
/**
* Write the condition definition in JSON to the @a ostream.
* @param output the @a ostream to append to.
*/
virtual void dumpJson(ostream* output) const = 0;
/**
* Write the values part of the condition definition in JSON to the @a ostream.
* @param output the @a ostream to append to.
*/
virtual void dumpValuesJson(ostream* output) const { /* empty on top level*/ };
/** /**
* Combine this condition with another instance using a logical and. * Combine this condition with another instance using a logical and.
* @param other the @a Condition to combine with. * @param other the @a Condition to combine with.
@@ -951,6 +971,9 @@ class SimpleCondition : public Condition {
// @copydoc // @copydoc
void dump(bool matched, ostream* output) const override; void dump(bool matched, ostream* output) const override;
// @copydoc
void dumpJson(ostream* output) const override;
// @copydoc // @copydoc
CombinedCondition* combineAnd(Condition* other) override; CombinedCondition* combineAnd(Condition* other) override;
@@ -1043,6 +1066,9 @@ class SimpleNumericCondition : public SimpleCondition {
// @copydoc // @copydoc
bool checkValue(const Message* message, const string& field) override; bool checkValue(const Message* message, const string& field) override;
// @copydoc
void dumpValuesJson(ostream* output) const override;
private: private:
/** the valid value ranges (pairs of from/to inclusive), empty for @a m_message seen check. */ /** the valid value ranges (pairs of from/to inclusive), empty for @a m_message seen check. */
@@ -1084,6 +1110,9 @@ class SimpleStringCondition : public SimpleCondition {
// @copydoc // @copydoc
bool checkValue(const Message* message, const string& field) override; bool checkValue(const Message* message, const string& field) override;
// @copydoc
void dumpValuesJson(ostream* output) const override;
private: private:
/** the valid values. */ /** the valid values. */
@@ -1110,6 +1139,9 @@ class CombinedCondition : public Condition {
// @copydoc // @copydoc
void dump(bool matched, ostream* output) const override; void dump(bool matched, ostream* output) const override;
// @copydoc
void dumpJson(ostream* output) const override;
// @copydoc // @copydoc
CombinedCondition* combineAnd(Condition* other) override { m_conditions.push_back(other); return this; } CombinedCondition* combineAnd(Condition* other) override { m_conditions.push_back(other); return this; }