add /templates endpoint and include types+templates+conditions+chains in dump output
This commit is contained in:
+119
-10
@@ -2,7 +2,7 @@ openapi: 3.1.0
|
||||
info:
|
||||
title: ebusd-http
|
||||
description: The API that ebusd provides on HTTP port.
|
||||
version: "23.2"
|
||||
version: "23.3"
|
||||
servers:
|
||||
- url: http://127.0.0.1:8080/
|
||||
paths:
|
||||
@@ -145,6 +145,50 @@ paths:
|
||||
500:
|
||||
description: General error.
|
||||
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:
|
||||
get:
|
||||
summary: Retrieve raw data from grabbed and/or decoded messages.
|
||||
@@ -392,8 +436,12 @@ components:
|
||||
type: integer
|
||||
minimum: 0
|
||||
condition:
|
||||
type: string
|
||||
description: the condition string in case of a conditional message (only with full).
|
||||
description: the condition(s) in case of a conditional message (only with full).
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/Condition'
|
||||
- type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Condition'
|
||||
lastup:
|
||||
$ref: '#/components/schemas/Seconds'
|
||||
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.
|
||||
example: 8
|
||||
id:
|
||||
$ref: '#/components/schemas/Symbols'
|
||||
description: the message ID composed of PBSB and further master data bytes (only with def).
|
||||
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.
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/Symbols'
|
||||
- type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Symbols'
|
||||
comment:
|
||||
type: string
|
||||
description: the message comment (only with verbose).
|
||||
@@ -465,10 +517,10 @@ components:
|
||||
- type: string
|
||||
- type: number
|
||||
nullable: true
|
||||
FieldDef:
|
||||
FieldTemplate:
|
||||
description: a single field template.
|
||||
required:
|
||||
- name
|
||||
- slave
|
||||
- type
|
||||
- isbits
|
||||
- length
|
||||
@@ -477,9 +529,6 @@ components:
|
||||
name:
|
||||
type: string
|
||||
description: the field name.
|
||||
slave:
|
||||
type: boolean
|
||||
description: whether the field is part of the slave data.
|
||||
type:
|
||||
type: string
|
||||
description: the field type.
|
||||
@@ -513,6 +562,66 @@ components:
|
||||
comment:
|
||||
type: string
|
||||
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:
|
||||
description: a known field data type.
|
||||
type: object
|
||||
|
||||
@@ -352,7 +352,14 @@ int main(int argc, char* argv[], char* envp[]) {
|
||||
} else {
|
||||
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);
|
||||
*out << "}";
|
||||
if (fout.is_open()) {
|
||||
fout.close();
|
||||
}
|
||||
|
||||
@@ -2254,6 +2254,18 @@ result_t MainLoop::executeGet(const vector<string>& args, bool* connected, ostri
|
||||
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") {
|
||||
time_t since = 0, until = 0;
|
||||
bool onlyUnknown = false;
|
||||
|
||||
@@ -202,6 +202,19 @@ bool ScanHelper::readTemplates(const string relPath, const string extension, boo
|
||||
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,
|
||||
string* errorDescription) {
|
||||
vector<string> files, dirs;
|
||||
|
||||
@@ -158,6 +158,13 @@ class ScanHelper : public Resolver {
|
||||
*/
|
||||
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.
|
||||
* @param relPath the relative path from which to read the files (without trailing "/").
|
||||
|
||||
+27
-1
@@ -482,7 +482,10 @@ void SingleDataField::dumpPrefix(bool prependFieldSeparator, OutputFormat output
|
||||
dumpString(prependFieldSeparator, m_name, output);
|
||||
}
|
||||
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 {
|
||||
*output << FIELD_SEPARATOR;
|
||||
if (m_partType == pt_masterData) {
|
||||
@@ -1459,4 +1462,27 @@ const DataField* DataFieldTemplates::get(const string& name) const {
|
||||
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
|
||||
|
||||
@@ -836,6 +836,14 @@ class DataFieldTemplates : public MappedFileReader {
|
||||
*/
|
||||
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:
|
||||
/** the known template @a DataField instances by name. */
|
||||
|
||||
+86
-11
@@ -970,9 +970,8 @@ void Message::decodeJson(bool leadingSeparator, bool appendDirectionCondition, b
|
||||
*output << ",\n \"pollprio\": " << setw(0) << dec << getPollPriority();
|
||||
}
|
||||
if (isConditional()) {
|
||||
*output << ",\n \"condition\": \"";
|
||||
m_condition->dump(false, output);
|
||||
*output << "\"";
|
||||
*output << ",\n \"condition\": ";
|
||||
m_condition->dumpJson(output);
|
||||
}
|
||||
}
|
||||
if (withData) {
|
||||
@@ -985,13 +984,8 @@ void Message::decodeJson(bool leadingSeparator, bool appendDirectionCondition, b
|
||||
}
|
||||
*output << ",\n \"zz\": " << dec << static_cast<unsigned>(m_dstAddress);
|
||||
if (withDefinition) {
|
||||
*output << ",\n \"id\": [" << dec;
|
||||
for (auto it = m_id.begin(); it < m_id.end(); it++) {
|
||||
if (it > m_id.begin()) {
|
||||
*output << ", ";
|
||||
}
|
||||
*output << dec << static_cast<unsigned>(*it);
|
||||
}
|
||||
*output << ",\n \"id\": [";
|
||||
dumpIdsJson(output);
|
||||
*output << "]";
|
||||
}
|
||||
appendAttributes(outputFormat, output);
|
||||
@@ -1022,6 +1016,15 @@ void Message::decodeJson(bool leadingSeparator, bool appendDirectionCondition, b
|
||||
*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) {
|
||||
if (addBits ? state == (m_dataHandlerState&state) : state == m_dataHandlerState) {
|
||||
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.
|
||||
@@ -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* ret = new CombinedCondition();
|
||||
return ret->combineAnd(this)->combineAnd(other);
|
||||
@@ -1625,6 +1665,17 @@ bool SimpleNumericCondition::checkValue(const Message* message, const string& fi
|
||||
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) {
|
||||
ostringstream output;
|
||||
@@ -1641,6 +1692,17 @@ bool SimpleStringCondition::checkValue(const Message* message, const string& fie
|
||||
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 {
|
||||
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,
|
||||
ostringstream* errorMessage) {
|
||||
for (const auto condition : m_conditions) {
|
||||
@@ -2817,7 +2892,7 @@ void MessageMap::dump(bool withConditions, OutputFormat outputFormat, ostream* o
|
||||
bool first = true;
|
||||
bool isJson = (outputFormat & OF_JSON) != 0;
|
||||
if (isJson) {
|
||||
*output << (m_addAll ? "[" : "}");
|
||||
*output << (m_addAll ? "[" : "{");
|
||||
} else {
|
||||
Message::dumpHeader(nullptr, output);
|
||||
}
|
||||
|
||||
@@ -601,6 +601,12 @@ class Message : public AttributedItem {
|
||||
OutputFormat outputFormat, ostringstream* output) const;
|
||||
|
||||
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. */
|
||||
const string m_filename;
|
||||
|
||||
@@ -748,6 +754,8 @@ class ChainedMessage : public Message {
|
||||
result_t prepareMasterPart(size_t index, const char separator, istringstream* input,
|
||||
MasterSymbolString* master) override;
|
||||
|
||||
// @copydoc
|
||||
void dumpIdsJson(ostringstream* output) const override;
|
||||
|
||||
public:
|
||||
// @copydoc
|
||||
@@ -884,6 +892,18 @@ class Condition {
|
||||
*/
|
||||
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.
|
||||
* @param other the @a Condition to combine with.
|
||||
@@ -951,6 +971,9 @@ class SimpleCondition : public Condition {
|
||||
// @copydoc
|
||||
void dump(bool matched, ostream* output) const override;
|
||||
|
||||
// @copydoc
|
||||
void dumpJson(ostream* output) const override;
|
||||
|
||||
// @copydoc
|
||||
CombinedCondition* combineAnd(Condition* other) override;
|
||||
|
||||
@@ -1043,6 +1066,9 @@ class SimpleNumericCondition : public SimpleCondition {
|
||||
// @copydoc
|
||||
bool checkValue(const Message* message, const string& field) override;
|
||||
|
||||
// @copydoc
|
||||
void dumpValuesJson(ostream* output) const override;
|
||||
|
||||
|
||||
private:
|
||||
/** 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
|
||||
bool checkValue(const Message* message, const string& field) override;
|
||||
|
||||
// @copydoc
|
||||
void dumpValuesJson(ostream* output) const override;
|
||||
|
||||
|
||||
private:
|
||||
/** the valid values. */
|
||||
@@ -1110,6 +1139,9 @@ class CombinedCondition : public Condition {
|
||||
// @copydoc
|
||||
void dump(bool matched, ostream* output) const override;
|
||||
|
||||
// @copydoc
|
||||
void dumpJson(ostream* output) const override;
|
||||
|
||||
// @copydoc
|
||||
CombinedCondition* combineAnd(Condition* other) override { m_conditions.push_back(other); return this; }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user