kopia lustrzana https://github.com/weetmuts/wmbusmeters
Add fractions to force_scale = 1/3
rodzic
d7c6058c14
commit
2b97ac189d
|
|
@ -9,33 +9,26 @@ driver {
|
|||
mvt = SEN,7c,07
|
||||
}
|
||||
field {
|
||||
name = total
|
||||
quantity = Volume
|
||||
name = total
|
||||
quantity = Volume
|
||||
info = 'The total water consumption.'
|
||||
match {
|
||||
measurement_type = Instantaneous
|
||||
vif_range = Volume
|
||||
}
|
||||
about {
|
||||
de = 'Der Gesamtwasserverbrauch.'
|
||||
en = 'The total water consumption.'
|
||||
fr = '''La consommation totale d'eau.'''
|
||||
sv = 'Den totala vattenförbrukningen.'
|
||||
}
|
||||
}
|
||||
field {
|
||||
name = max_flow
|
||||
quantity = Flow
|
||||
name = max_flow
|
||||
quantity = Flow
|
||||
info = 'The maximum water flow recorded during previous period.'
|
||||
match {
|
||||
measurement_type = Instantaneous
|
||||
vif_range = VolumeFlow
|
||||
}
|
||||
about {
|
||||
en = 'The maximum flow recorded during previous period.'
|
||||
}
|
||||
}
|
||||
test {
|
||||
args = 'MoreWater iperl 12345699 NOKEY'
|
||||
coment = 'Test iPerl T1 telegram, that after decryption, has 2f2f markers.'
|
||||
comment = 'Test iPerl T1 telegram, that after decryption, has 2f2f markers.'
|
||||
telegram = 1E44AE4C9956341268077A36001000_2F2F0413181E0000023B00002F2F2F2F
|
||||
json = '{"media":"water","meter":"iperl","name":"MoreWater","id":"12345699","total_m3":7.704,"max_flow_m3h":0,"timestamp":"1111-11-11T11:11:11Z"}'
|
||||
fields = 'MoreWater;12345699;7.704;0;1111-11-11 11:11.11'
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ PrintProperties check_print_properties(const char *print_properties_s, DriverDyn
|
|||
string get_translation(XMQDoc *doc, XMQNode *node, string name, string lang);
|
||||
string check_calculate(const char *formula, DriverDynamic *dd);
|
||||
Unit check_display_unit(const char *display_unit, DriverDynamic *dd);
|
||||
double check_force_scale(double force_scale, DriverDynamic *dd);
|
||||
double check_force_scale(const char *force_scale, DriverDynamic *dd);
|
||||
|
||||
bool checked_set_difvifkey(const char *difvifkey_s, FieldMatcher *fm, DriverDynamic *dd);
|
||||
void checked_set_measurement_type(const char *measurement_type_s, FieldMatcher *fm, DriverDynamic *dd);
|
||||
|
|
@ -286,8 +286,9 @@ XMQProceed DriverDynamic::add_field(XMQDoc *doc, XMQNode *field, DriverDynamic *
|
|||
// The display unit is usually based on the quantity. But you can override it.
|
||||
Unit display_unit = check_display_unit(xmqGetStringRel(doc, "display_unit", field), dd);
|
||||
|
||||
// A field can force a scale factor. Defaults to 1.0
|
||||
double force_scale = check_force_scale(xmqGetDoubleRel(doc, "force_scale", field), dd);
|
||||
// A field can force a scale factor. Defaults to 1.0 but you can override
|
||||
// with 1.123 or 1/32 or 0.33333 or 3.14/2.5
|
||||
double force_scale = check_force_scale(xmqGetStringRel(doc, "force_scale", field), dd);
|
||||
|
||||
// Now find all matchers.
|
||||
FieldMatcher match = FieldMatcher::build();
|
||||
|
|
@ -752,12 +753,71 @@ Unit check_display_unit(const char *display_unit_s, DriverDynamic *dd)
|
|||
return u;
|
||||
}
|
||||
|
||||
double check_force_scale(double force_scale, DriverDynamic *dd)
|
||||
double check_force_scale(const char *force_scale, DriverDynamic *dd)
|
||||
{
|
||||
// TODO improve error detection.
|
||||
if (force_scale == 0) return 1.0;
|
||||
|
||||
return force_scale;
|
||||
const char *start = force_scale;
|
||||
const char *stop = force_scale+strlen(force_scale);
|
||||
while (start < stop && (*start == ' ' || *start == '\n')) start++;
|
||||
while (start < stop && (*stop == ' ' || *stop == '\n')) stop--;
|
||||
|
||||
const char *slash = strchr(force_scale, '/');
|
||||
if (slash == NULL)
|
||||
{
|
||||
char *end;
|
||||
double d = strtod(start, &end);
|
||||
if (end != stop)
|
||||
{
|
||||
warning("(driver) error in %s, unparseable force_scale: %s\n"
|
||||
"You can force scales such as:\n"
|
||||
"3.14\n"
|
||||
"2/3\n"
|
||||
"12.5\n"
|
||||
"12.5/5.3\n",
|
||||
dd->fileName().c_str(),
|
||||
force_scale);
|
||||
throw 1;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
const char *numerator_stop = slash;
|
||||
const char *denominator_start = slash+1;
|
||||
while (start < numerator_stop && (*numerator_stop == ' ' || *numerator_stop == '\n')) numerator_stop--;
|
||||
while (denominator_start < stop && (*denominator_start == ' ' || *denominator_start == '\n')) denominator_start++;
|
||||
|
||||
char *end;
|
||||
double num = strtod(start, &end);
|
||||
if (end != numerator_stop)
|
||||
{
|
||||
warning("(driver) error in %s, unparseable numerator in force_scale: %s\n"
|
||||
"You can force scales such as:\n"
|
||||
"3.14\n"
|
||||
"2/3\n"
|
||||
"12.5\n"
|
||||
"12.5/5.3\n",
|
||||
dd->fileName().c_str(),
|
||||
force_scale);
|
||||
throw 1;
|
||||
}
|
||||
|
||||
double denom = strtod(denominator_start, &end);
|
||||
if (end != stop)
|
||||
{
|
||||
warning("(driver) error in %s, unparseable denominator in force_scale: %s\n"
|
||||
"You can force scales such as:\n"
|
||||
"3.14\n"
|
||||
"2/3\n"
|
||||
"12.5\n"
|
||||
"12.5/5.3\n",
|
||||
dd->fileName().c_str(),
|
||||
force_scale);
|
||||
throw 1;
|
||||
}
|
||||
|
||||
double d = num / denom;
|
||||
return d;
|
||||
}
|
||||
|
||||
bool checked_set_difvifkey(const char *difvifkey_s, FieldMatcher *fm, DriverDynamic *dd)
|
||||
|
|
|
|||
305
src/xmq.c
305
src/xmq.c
|
|
@ -452,7 +452,9 @@ struct XMQParseState
|
|||
Stack *element_stack; // Top is last created node
|
||||
void *element_last; // Last added sibling to stack top node.
|
||||
bool parsing_doctype; // True when parsing a doctype.
|
||||
void *add_doctype_before; // Used when retrofitting a doctype found in json.
|
||||
void *add_pre_node_before; // Used when retrofitting pre-root comments and doctype found in json.
|
||||
bool root_found; // Used to decide if _// should be printed before or after root.
|
||||
void *add_post_node_after; // Used when retrofitting post-root comments found in json.
|
||||
bool doctype_found; // True after a doctype has been parsed.
|
||||
bool parsing_pi; // True when parsing a processing instruction, pi.
|
||||
bool merge_text; // Merge text nodes and character entities.
|
||||
|
|
@ -511,7 +513,10 @@ struct XMQPrintState
|
|||
const char *replay_active_color_pre;
|
||||
const char *restart_line;
|
||||
const char *last_namespace;
|
||||
xmlNode *doctype; // Used to remember doctype when printing json.
|
||||
Stack *pre_nodes; // Used to remember leading comments/doctype when printing json.
|
||||
size_t pre_post_num_comments_total; // Number of comments outside of the root element.
|
||||
size_t pre_post_num_comments_used; // Active number of comment outside of the root element.
|
||||
Stack *post_nodes; // Used to remember ending comments when printing json.
|
||||
XMQOutputSettings *output_settings;
|
||||
XMQDoc *doq;
|
||||
};
|
||||
|
|
@ -877,8 +882,10 @@ typedef struct Stack Stack;
|
|||
Stack *new_stack();
|
||||
void free_stack(Stack *stack);
|
||||
void push_stack(Stack *s, void *);
|
||||
size_t size_stack();
|
||||
// Pop the top element.
|
||||
void *pop_stack(Stack *s);
|
||||
// Pull the bottom element.
|
||||
void *rock_stack(Stack *s);
|
||||
|
||||
#define STACK_MODULE
|
||||
|
||||
|
|
@ -1237,7 +1244,9 @@ struct XMQParseState
|
|||
Stack *element_stack; // Top is last created node
|
||||
void *element_last; // Last added sibling to stack top node.
|
||||
bool parsing_doctype; // True when parsing a doctype.
|
||||
void *add_doctype_before; // Used when retrofitting a doctype found in json.
|
||||
void *add_pre_node_before; // Used when retrofitting pre-root comments and doctype found in json.
|
||||
bool root_found; // Used to decide if _// should be printed before or after root.
|
||||
void *add_post_node_after; // Used when retrofitting post-root comments found in json.
|
||||
bool doctype_found; // True after a doctype has been parsed.
|
||||
bool parsing_pi; // True when parsing a processing instruction, pi.
|
||||
bool merge_text; // Merge text nodes and character entities.
|
||||
|
|
@ -1296,7 +1305,10 @@ struct XMQPrintState
|
|||
const char *replay_active_color_pre;
|
||||
const char *restart_line;
|
||||
const char *last_namespace;
|
||||
xmlNode *doctype; // Used to remember doctype when printing json.
|
||||
Stack *pre_nodes; // Used to remember leading comments/doctype when printing json.
|
||||
size_t pre_post_num_comments_total; // Number of comments outside of the root element.
|
||||
size_t pre_post_num_comments_used; // Active number of comment outside of the root element.
|
||||
Stack *post_nodes; // Used to remember ending comments when printing json.
|
||||
XMQOutputSettings *output_settings;
|
||||
XMQDoc *doq;
|
||||
};
|
||||
|
|
@ -1578,6 +1590,7 @@ typedef struct XMQPrintState XMQPrintState;
|
|||
|
||||
void xmq_fixup_json_before_writeout(XMQDoc *doq);
|
||||
void json_print_object_nodes(XMQPrintState *ps, xmlNode *container, xmlNode *from, xmlNode *to);
|
||||
void collect_leading_ending_comments_doctype(XMQPrintState *ps, xmlNodePtr *first, xmlNodePtr *last);
|
||||
void json_print_array_nodes(XMQPrintState *ps, xmlNode *container, xmlNode *from, xmlNode *to);
|
||||
bool xmq_tokenize_buffer_json(XMQParseState *state, const char *start, const char *stop);
|
||||
|
||||
|
|
@ -2103,6 +2116,10 @@ void setup_tex_coloring(XMQOutputSettings *os, XMQTheme *theme, bool dark_mode,
|
|||
theme->attr_value_compound_quote.post = "}";
|
||||
theme->attr_value_compound_entity.pre = "\\xmqE{";
|
||||
theme->attr_value_compound_entity.post = "}";
|
||||
theme->ns_declaration.pre = "\\xmqNSD{";
|
||||
theme->ns_declaration.post = "}";
|
||||
theme->ns_override_xsl.pre = "\\xmqXSL{";
|
||||
theme->ns_override_xsl.post = "}";
|
||||
theme->ns_colon.pre = NULL;
|
||||
}
|
||||
|
||||
|
|
@ -3503,7 +3520,21 @@ void do_comment(XMQParseState*state,
|
|||
size_t indent = col-1;
|
||||
char *trimmed = (state->no_trim_quotes)?strndup(start, stop-start):xmq_un_comment(indent, ' ', start, stop);
|
||||
xmlNodePtr n = xmlNewDocComment(state->doq->docptr_.xml, (const xmlChar *)trimmed);
|
||||
xmlAddChild(parent, n);
|
||||
|
||||
if (state->add_pre_node_before)
|
||||
{
|
||||
// Insert comment before this node.
|
||||
xmlAddPrevSibling((xmlNodePtr)state->add_pre_node_before, n);
|
||||
}
|
||||
else if (state->add_post_node_after)
|
||||
{
|
||||
// Insert comment after this node.
|
||||
xmlAddNextSibling((xmlNodePtr)state->add_post_node_after, n);
|
||||
}
|
||||
else
|
||||
{
|
||||
xmlAddChild(parent, n);
|
||||
}
|
||||
state->element_last = n;
|
||||
free(trimmed);
|
||||
}
|
||||
|
|
@ -3613,10 +3644,10 @@ void do_element_value_quote(XMQParseState *state,
|
|||
longjmp(state->error_handler, 1);
|
||||
}
|
||||
state->doq->docptr_.xml->intSubset = dtd;
|
||||
if (state->add_doctype_before)
|
||||
if (state->add_pre_node_before)
|
||||
{
|
||||
// Insert doctype before this node.
|
||||
xmlAddPrevSibling((xmlNodePtr)state->add_doctype_before, (xmlNodePtr)dtd);
|
||||
xmlAddPrevSibling((xmlNodePtr)state->add_pre_node_before, (xmlNodePtr)dtd);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -4167,6 +4198,8 @@ void xmq_print_json(XMQDoc *doq, XMQOutputSettings *os)
|
|||
void *last = doq->docptr_.xml->last;
|
||||
|
||||
XMQPrintState ps = {};
|
||||
ps.pre_nodes = new_stack();
|
||||
ps.post_nodes = new_stack();
|
||||
XMQWrite write = os->content.write;
|
||||
void *writer_state = os->content.writer_state;
|
||||
ps.doq = doq;
|
||||
|
|
@ -4174,8 +4207,14 @@ void xmq_print_json(XMQDoc *doq, XMQOutputSettings *os)
|
|||
ps.output_settings = os;
|
||||
assert(os->content.write);
|
||||
|
||||
// Find any leading (doctype/comments) and ending (comments) nodes and store in pre_nodes and post_nodes inside ps.
|
||||
// Adjust the first and last pointer.
|
||||
collect_leading_ending_comments_doctype(&ps, (xmlNode**)&first, (xmlNode**)&last);
|
||||
json_print_object_nodes(&ps, NULL, (xmlNode*)first, (xmlNode*)last);
|
||||
write(writer_state, "\n", NULL);
|
||||
|
||||
free_stack(ps.pre_nodes);
|
||||
free_stack(ps.post_nodes);
|
||||
}
|
||||
|
||||
void text_print_node(XMQPrintState *ps, xmlNode *node)
|
||||
|
|
@ -6342,6 +6381,8 @@ void hashmap_free_iterator(HashMapIterator *i)
|
|||
|
||||
#ifdef STACK_MODULE
|
||||
|
||||
StackElement *find_element_above(Stack *s, StackElement *b);
|
||||
|
||||
Stack *new_stack()
|
||||
{
|
||||
Stack *s = (Stack*)malloc(sizeof(Stack));
|
||||
|
|
@ -6395,6 +6436,41 @@ void *pop_stack(Stack *stack)
|
|||
return data;
|
||||
}
|
||||
|
||||
StackElement *find_element_above(Stack *s, StackElement *b)
|
||||
{
|
||||
StackElement *e = s->top;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (!e) return NULL;
|
||||
if (e->below == b) return e;
|
||||
e = e->below;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *rock_stack(Stack *stack)
|
||||
{
|
||||
assert(stack);
|
||||
assert(stack->bottom);
|
||||
|
||||
assert(stack->size > 0);
|
||||
|
||||
if (stack->size == 1) return pop_stack(stack);
|
||||
|
||||
StackElement *element = stack->bottom;
|
||||
void *data = element->data;
|
||||
StackElement *above = find_element_above(stack, element);
|
||||
assert(above);
|
||||
stack->bottom = above;
|
||||
above->below = NULL;
|
||||
free(element);
|
||||
stack->size--;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
#endif // STACK_MODULE
|
||||
|
||||
// PARTS MEMBUFFER_C ////////////////////////////////////////
|
||||
|
|
@ -6582,7 +6658,8 @@ void json_print_attributes(XMQPrintState *ps, xmlNodePtr node);
|
|||
void json_print_array_with_children(XMQPrintState *ps,
|
||||
xmlNode *container,
|
||||
xmlNode *node);
|
||||
void json_print_comment_node(XMQPrintState *ps, xmlNodePtr node);
|
||||
void json_print_comment_node(XMQPrintState *ps, xmlNodePtr node, bool prefix_ul, size_t total, size_t used);
|
||||
void json_print_doctype_node(XMQPrintState *ps, xmlNodePtr node);
|
||||
void json_print_entity_node(XMQPrintState *ps, xmlNodePtr node);
|
||||
void json_print_standalone_quote(XMQPrintState *ps, xmlNode *container, xmlNodePtr node, size_t total, size_t used);
|
||||
void json_print_object_nodes(XMQPrintState *ps, xmlNode *container, xmlNode *from, xmlNode *to);
|
||||
|
|
@ -6756,7 +6833,7 @@ void parse_json_quote(XMQParseState *state, const char *key_start, const char *k
|
|||
return;
|
||||
}
|
||||
|
||||
if (key_start && *key_start == '/' && *(key_start+1) == '/' && key_stop == key_start+2)
|
||||
if (key_start && key_stop == key_start+2 && *key_start == '/' && *(key_start+1) == '/')
|
||||
{
|
||||
// This is "//":"symbol" which means a comment node in xml.
|
||||
DO_CALLBACK_SIM(comment, state, start_line, start_col, content_start, content_stop, content_stop);
|
||||
|
|
@ -6764,12 +6841,24 @@ void parse_json_quote(XMQParseState *state, const char *key_start, const char *k
|
|||
return;
|
||||
}
|
||||
|
||||
if (key_start && *key_start == '_' && key_stop == key_start+1)
|
||||
if (key_start && key_stop == key_start+3 && *key_start == '_' && *(key_start+1) == '/' && *(key_start+2) == '/')
|
||||
{
|
||||
// This is "_//":"symbol" which means a comment node in xml prefixing the root xml node.
|
||||
if (!state->root_found) state->add_pre_node_before = (xmlNode*)state->element_stack->top->data;
|
||||
else state->add_post_node_after = (xmlNode*)state->element_stack->top->data;
|
||||
DO_CALLBACK_SIM(comment, state, start_line, start_col, content_start, content_stop, content_stop);
|
||||
if (!state->root_found) state->add_pre_node_before = NULL;
|
||||
else state->add_post_node_after = NULL;
|
||||
free(content_start);
|
||||
return;
|
||||
}
|
||||
|
||||
if (key_start && key_stop == key_start+1 && *key_start == '_' )
|
||||
{
|
||||
// This is the element name "_":"symbol" stored inside the json object,
|
||||
// in situations where the name is not visible as a key. For example
|
||||
// the root json object and any object in arrays.
|
||||
xmlNodePtr container = (xmlNodePtr)state->element_last;
|
||||
xmlNodePtr container = (xmlNodePtr)state->element_stack->top->data;
|
||||
size_t len = content_stop - content_start;
|
||||
char *name = (char*)malloc(len+1);
|
||||
memcpy(name, content_start, len);
|
||||
|
|
@ -6777,6 +6866,8 @@ void parse_json_quote(XMQParseState *state, const char *key_start, const char *k
|
|||
xmlNodeSetName(container, (xmlChar*)name);
|
||||
free(name);
|
||||
free(content_start);
|
||||
// This will be set many times.
|
||||
state->root_found = true;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -6788,9 +6879,10 @@ void parse_json_quote(XMQParseState *state, const char *key_start, const char *k
|
|||
// This is the one and only !DOCTYPE element.
|
||||
DO_CALLBACK_SIM(element_key, state, state->line, state->col, key_start, key_stop, key_stop);
|
||||
state->parsing_doctype = true;
|
||||
state->add_doctype_before = (xmlNode*)state->element_stack->top->data;
|
||||
state->add_pre_node_before = (xmlNode*)state->element_stack->top->data;
|
||||
DO_CALLBACK_SIM(element_value_quote, state, state->line, state->col, content_start, content_stop, content_stop);
|
||||
state->add_doctype_before = NULL;
|
||||
state->add_pre_node_before = NULL;
|
||||
free(content_start);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -7274,21 +7366,19 @@ void json_print_object_nodes(XMQPrintState *ps, xmlNode *container, xmlNode *fro
|
|||
|
||||
while (i)
|
||||
{
|
||||
if (!is_doctype_node(i))
|
||||
const char *name = (const char*)i->name;
|
||||
if (name && strcmp(name, "_")) // We have a name and it is NOT a single _
|
||||
{
|
||||
const char *name = (const char*)i->name;
|
||||
if (name && strcmp(name, "_"))
|
||||
Counter *c = (Counter*)hashmap_get(map, name);
|
||||
if (!c)
|
||||
{
|
||||
Counter *c = (Counter*)hashmap_get(map, name);
|
||||
if (!c)
|
||||
{
|
||||
c = (Counter*)malloc(sizeof(Counter));
|
||||
memset(c, 0, sizeof(Counter));
|
||||
hashmap_put(map, name, c);
|
||||
}
|
||||
c->total++;
|
||||
c = (Counter*)malloc(sizeof(Counter));
|
||||
memset(c, 0, sizeof(Counter));
|
||||
hashmap_put(map, name, c);
|
||||
}
|
||||
c->total++;
|
||||
}
|
||||
if (i == to) break;
|
||||
i = xml_next_sibling(i);
|
||||
}
|
||||
|
||||
|
|
@ -7306,6 +7396,7 @@ void json_print_object_nodes(XMQPrintState *ps, xmlNode *container, xmlNode *fro
|
|||
{
|
||||
json_print_node(ps, container, i, 1, 0);
|
||||
}
|
||||
if (i == to) break;
|
||||
i = xml_next_sibling(i);
|
||||
}
|
||||
|
||||
|
|
@ -7351,6 +7442,13 @@ bool has_attr_other_than_AS_(xmlNode *node)
|
|||
|
||||
void json_print_node(XMQPrintState *ps, xmlNode *container, xmlNode *node, size_t total, size_t used)
|
||||
{
|
||||
// This is a comment translated into "//":"Comment text"
|
||||
if (is_comment_node(node))
|
||||
{
|
||||
json_print_comment_node(ps, node, false, total, used);
|
||||
return;
|
||||
}
|
||||
|
||||
// Standalone quote must be quoted: 'word' 'some words'
|
||||
if (is_content_node(node))
|
||||
{
|
||||
|
|
@ -7365,23 +7463,9 @@ void json_print_node(XMQPrintState *ps, xmlNode *container, xmlNode *node, size_
|
|||
return;
|
||||
}
|
||||
|
||||
// This is a comment translated into "_//":"Comment text"
|
||||
if (is_comment_node(node))
|
||||
{
|
||||
json_print_comment_node(ps, node);
|
||||
return;
|
||||
}
|
||||
|
||||
// This is doctype node.
|
||||
if (is_doctype_node(node))
|
||||
{
|
||||
ps->doctype = node;
|
||||
return;
|
||||
}
|
||||
|
||||
// This is a node with no children, but the only such valid json nodes are
|
||||
// the empty object _ ---> {} or _(A) ---> [].
|
||||
if (is_leaf_node(node))
|
||||
if (is_leaf_node(node) && container)
|
||||
{
|
||||
return json_print_leaf_node(ps, container, node, total, used);
|
||||
}
|
||||
|
|
@ -7578,8 +7662,12 @@ void json_print_array_with_children(XMQPrintState *ps,
|
|||
// Top level object or object inside array. [ {} {} ]
|
||||
// Dump the element name! It cannot be represented!
|
||||
}
|
||||
while (xml_prev_sibling((xmlNode*)from)) from = xml_prev_sibling((xmlNode*)from);
|
||||
assert(from != NULL);
|
||||
|
||||
if (from)
|
||||
{
|
||||
while (xml_prev_sibling((xmlNode*)from)) from = xml_prev_sibling((xmlNode*)from);
|
||||
assert(from != NULL);
|
||||
}
|
||||
|
||||
json_print_array_nodes(ps, NULL, (xmlNode*)from, (xmlNode*)to);
|
||||
|
||||
|
|
@ -7667,22 +7755,24 @@ void json_print_element_with_children(XMQPrintState *ps,
|
|||
|
||||
ps->line_indent += ps->output_settings->add_indent;
|
||||
|
||||
if (ps->doctype)
|
||||
while (!container && ps->pre_nodes && ps->pre_nodes->size > 0)
|
||||
{
|
||||
// Print !DOCTYPE inside top level object.
|
||||
// I.e. !DOCTYPE=html html { body = a } -> { "!DOCTYPE":"html", "html":{ "body":"a"}}
|
||||
print_utf8(ps, COLOR_none, 1, "\"!DOCTYPE\":", NULL);
|
||||
ps->last_char = ':';
|
||||
xmlBuffer *buffer = xmlBufferCreate();
|
||||
xmlNodeDump(buffer, (xmlDocPtr)ps->doq->docptr_.xml, (xmlNodePtr)ps->doctype, 0, 0);
|
||||
char *c = (char*)xmlBufferContent(buffer);
|
||||
char *quoted_value = xmq_quote_as_c(c+10, c+strlen(c)-1);
|
||||
print_utf8(ps, COLOR_none, 3, "\"", NULL, quoted_value, NULL, "\"", NULL);
|
||||
free(quoted_value);
|
||||
xmlBufferFree(buffer);
|
||||
ps->doctype = NULL;
|
||||
ps->last_char = '"';
|
||||
xmlNodePtr node = (xmlNodePtr)rock_stack(ps->pre_nodes);
|
||||
|
||||
if (is_doctype_node(node))
|
||||
{
|
||||
json_print_doctype_node(ps, node);
|
||||
}
|
||||
else if (is_comment_node(node))
|
||||
{
|
||||
json_print_comment_node(ps, node, true, ps->pre_post_num_comments_total, ps->pre_post_num_comments_used++);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
const char *name = xml_element_name(node);
|
||||
bool is_underline = (name[0] == '_' && name[1] == 0);
|
||||
if (!container && name && !is_underline)
|
||||
|
|
@ -7698,11 +7788,28 @@ void json_print_element_with_children(XMQPrintState *ps,
|
|||
|
||||
json_print_attributes(ps, node);
|
||||
|
||||
while (xml_prev_sibling((xmlNode*)from)) from = xml_prev_sibling((xmlNode*)from);
|
||||
assert(from != NULL);
|
||||
if (from)
|
||||
{
|
||||
while (xml_prev_sibling((xmlNode*)from)) from = xml_prev_sibling((xmlNode*)from);
|
||||
assert(from != NULL);
|
||||
}
|
||||
|
||||
json_print_object_nodes(ps, node, (xmlNode*)from, (xmlNode*)to);
|
||||
|
||||
while (!container && ps->post_nodes && ps->post_nodes->size > 0)
|
||||
{
|
||||
xmlNodePtr node = (xmlNodePtr)rock_stack(ps->post_nodes);
|
||||
|
||||
if (is_comment_node(node))
|
||||
{
|
||||
json_print_comment_node(ps, node, true, ps->pre_post_num_comments_total, ps->pre_post_num_comments_used++);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
ps->line_indent -= ps->output_settings->add_indent;
|
||||
|
||||
print_utf8(ps, COLOR_brace_right, 1, "}", NULL);
|
||||
|
|
@ -7800,16 +7907,50 @@ void json_print_comma(XMQPrintState *ps)
|
|||
}
|
||||
|
||||
void json_print_comment_node(XMQPrintState *ps,
|
||||
xmlNode *node)
|
||||
xmlNode *node,
|
||||
bool prefix_ul,
|
||||
size_t total,
|
||||
size_t used)
|
||||
{
|
||||
json_check_comma(ps);
|
||||
|
||||
print_utf8(ps, COLOR_equals, 1, "\"//\":", NULL);
|
||||
if (prefix_ul) print_utf8(ps, COLOR_equals, 1, "\"_//", NULL);
|
||||
else print_utf8(ps, COLOR_equals, 1, "\"//", NULL);
|
||||
|
||||
if (total > 1)
|
||||
{
|
||||
char buf[32];
|
||||
buf[31] = 0;
|
||||
snprintf(buf, 32, "[%zu]\":", used);
|
||||
print_utf8(ps, COLOR_equals, 1, buf, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
print_utf8(ps, COLOR_equals, 1, "\":", NULL);
|
||||
}
|
||||
ps->last_char = ':';
|
||||
json_print_value(ps, node, node, LEVEL_XMQ, true);
|
||||
ps->last_char = '"';
|
||||
}
|
||||
|
||||
void json_print_doctype_node(XMQPrintState *ps, xmlNodePtr node)
|
||||
{
|
||||
json_check_comma(ps);
|
||||
|
||||
// Print !DOCTYPE inside top level object.
|
||||
// I.e. !DOCTYPE=html html { body = a } -> { "!DOCTYPE":"html", "html":{ "body":"a"}}
|
||||
print_utf8(ps, COLOR_none, 1, "\"!DOCTYPE\":", NULL);
|
||||
ps->last_char = ':';
|
||||
xmlBuffer *buffer = xmlBufferCreate();
|
||||
xmlNodeDump(buffer, (xmlDocPtr)ps->doq->docptr_.xml, node, 0, 0);
|
||||
char *c = (char*)xmlBufferContent(buffer);
|
||||
char *quoted_value = xmq_quote_as_c(c+10, c+strlen(c)-1);
|
||||
print_utf8(ps, COLOR_none, 3, "\"", NULL, quoted_value, NULL, "\"", NULL);
|
||||
free(quoted_value);
|
||||
xmlBufferFree(buffer);
|
||||
ps->last_char = '"';
|
||||
}
|
||||
|
||||
void json_print_entity_node(XMQPrintState *ps, xmlNodePtr node)
|
||||
{
|
||||
json_check_comma(ps);
|
||||
|
|
@ -7944,6 +8085,46 @@ void xmq_fixup_json_before_writeout(XMQDoc *doq)
|
|||
}
|
||||
}
|
||||
|
||||
void collect_leading_ending_comments_doctype(XMQPrintState *ps, xmlNodePtr *first, xmlNodePtr *last)
|
||||
{
|
||||
xmlNodePtr f = *first;
|
||||
xmlNodePtr l = *last;
|
||||
xmlNodePtr node;
|
||||
|
||||
for (node = f; node && node != l; node = node->next)
|
||||
{
|
||||
if (is_doctype_node(node) || is_comment_node(node))
|
||||
{
|
||||
push_stack(ps->pre_nodes, node);
|
||||
if (is_comment_node(node)) ps->pre_post_num_comments_total++;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (*first != node)
|
||||
{
|
||||
*first = node;
|
||||
f = node;
|
||||
}
|
||||
|
||||
for (node = l; node && node != f; node = node->prev)
|
||||
{
|
||||
if (is_comment_node(node))
|
||||
{
|
||||
push_stack(ps->post_nodes, node);
|
||||
ps->pre_post_num_comments_total++;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (*last != node)
|
||||
{
|
||||
*last = node;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// Empty function when XMQ_NO_JSON is defined.
|
||||
|
|
@ -7962,6 +8143,10 @@ void json_print_object_nodes(XMQPrintState *ps, xmlNode *container, xmlNode *fro
|
|||
{
|
||||
}
|
||||
|
||||
void collect_leading_ending_comments_doctype(XMQPrintState *ps, xmlNodePtr *first, xmlNodePtr *last)
|
||||
{
|
||||
}
|
||||
|
||||
void json_print_array_nodes(XMQPrintState *ps, xmlNode *container, xmlNode *from, xmlNode *to)
|
||||
{
|
||||
}
|
||||
|
|
|
|||
3
test.sh
3
test.sh
|
|
@ -203,6 +203,9 @@ if [ "$?" != "0" ]; then RC="1"; fi
|
|||
./tests/test_loadable_drivers.sh $PROG
|
||||
if [ "$?" != "0" ]; then RC="1"; fi
|
||||
|
||||
tests/test_force_scaling.sh $PROG
|
||||
if [ "$?" != "0" ]; then RC="1"; fi
|
||||
|
||||
./tests/test_dyndriver_key_with_date.sh $PROG
|
||||
if [ "$?" != "0" ]; then RC="1"; fi
|
||||
|
||||
|
|
|
|||
|
|
@ -264,6 +264,8 @@ Energy
|
|||
Reactive_Energy
|
||||
Apparent_Energy
|
||||
Power
|
||||
Reactive_Power
|
||||
Apparent_Power
|
||||
Volume
|
||||
Flow
|
||||
Voltage
|
||||
|
|
@ -307,6 +309,8 @@ Energy
|
|||
Reactive_Energy
|
||||
Apparent_Energy
|
||||
Power
|
||||
Reactive_Power
|
||||
Apparent_Power
|
||||
Volume
|
||||
Flow
|
||||
Voltage
|
||||
|
|
|
|||
|
|
@ -0,0 +1,92 @@
|
|||
#!/bin/sh
|
||||
|
||||
PROG="$1"
|
||||
|
||||
rm -rf testoutput
|
||||
mkdir -p testoutput
|
||||
TEST=testoutput
|
||||
|
||||
TESTNAME="Test force_scale."
|
||||
TESTRESULT="ERROR"
|
||||
|
||||
cat <<EOF > $TEST/test.xmq
|
||||
driver {
|
||||
name = ime
|
||||
default_fields = alfa_kwh,beta_kwh,gamma_kwh,delta_kwh
|
||||
meter_type = ElectricityMeter
|
||||
detect {
|
||||
mvt = IME,55,08
|
||||
}
|
||||
field {
|
||||
name = alfa
|
||||
quantity = Energy
|
||||
vif_scaling = None
|
||||
dif_signedness = Unsigned
|
||||
display_unit = kwh
|
||||
force_scale = 1
|
||||
match {
|
||||
difvifkey = 849010FF80843B
|
||||
}
|
||||
}
|
||||
field {
|
||||
name = beta
|
||||
quantity = Energy
|
||||
vif_scaling = None
|
||||
dif_signedness = Unsigned
|
||||
display_unit = kwh
|
||||
force_scale = 1/3
|
||||
match {
|
||||
difvifkey = 849010FF80843B
|
||||
}
|
||||
}
|
||||
field {
|
||||
name = gamma
|
||||
quantity = Energy
|
||||
vif_scaling = None
|
||||
dif_signedness = Unsigned
|
||||
display_unit = kwh
|
||||
force_scale = 1000.0
|
||||
match {
|
||||
difvifkey = 849010FF80843B
|
||||
}
|
||||
}
|
||||
field {
|
||||
name = delta
|
||||
quantity = Energy
|
||||
vif_scaling = None
|
||||
dif_signedness = Unsigned
|
||||
display_unit = kwh
|
||||
force_scale = 3.3/3.3
|
||||
match {
|
||||
difvifkey = 849010FF80843B
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
$PROG --format=fields \
|
||||
68CBCB6808017278563412A525660267000000849010FF80843B7A820700849010FF80843C00000000849010FF81843BF35B0400849010FF81843C010000008410FF80843B427107008420FF80843B381100008410FF80843C000000008420FF80843C000000008410FF81843B5A4F04008420FF81843B980C00008410FF81843C010000008420FF81843C0000000084A010FF80843B7A82070084A010FF80843C0000000084A010FF81843BF35B040084A010FF81843C0100000004FF90290000000002FF912B00001F0000000000f11623442D2C998734761B168D2087D19EAD217F1779EDA86AB6_710008190000081900007F13 \
|
||||
METERU $TEST/test.xmq 12345678 NOKEY \
|
||||
> $TEST/test_output.txt
|
||||
|
||||
cat <<EOF > $TEST/test_expected.txt
|
||||
492154;164051.333333;492154000;492154
|
||||
EOF
|
||||
|
||||
if [ "$?" = "0" ]
|
||||
then
|
||||
cat $TEST/test_output.txt | sed 's/"timestamp": "....-..-..T..:..:..Z"/"timestamp": "1111-11-11T11:11:11Z"/' > $TEST/test_responses.txt
|
||||
diff $TEST/test_expected.txt $TEST/test_responses.txt
|
||||
if [ "$?" = "0" ]
|
||||
then
|
||||
echo "OK: $TESTNAME"
|
||||
TESTRESULT="OK"
|
||||
else
|
||||
if [ "$USE_MELD" = "true" ]
|
||||
then
|
||||
meld $TEST/test_expected.txt $TEST/test_responses.txt
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$TESTRESULT" = "ERROR" ]; then echo ERROR: $TESTNAME; exit 1; fi
|
||||
Ładowanie…
Reference in New Issue