diff --git a/sml/include/sml/sml_time.h b/sml/include/sml/sml_time.h index 13bc99c..d8d4b7b 100644 --- a/sml/include/sml/sml_time.h +++ b/sml/include/sml/sml_time.h @@ -28,15 +28,28 @@ extern "C" { #define SML_TIME_SEC_INDEX 0x01 #define SML_TIME_TIMESTAMP 0x02 +#define SML_TIME_LOCAL_TIMESTAMP 0x03 + +typedef struct { + u32 *timestamp; + i16 *local_offset; + i16 *season_time_offset; +} sml_timestamp_local; typedef struct { u8 *tag; union { u32 *sec_index; u32 *timestamp; + sml_timestamp_local *local_timestamp; } data; } sml_time; +sml_timestamp_local *sml_timestamp_local_init(); +sml_timestamp_local *sml_timestamp_local_parse(sml_buffer *buf); +void sml_timestamp_local_write(sml_timestamp_local *time, sml_buffer *buf); +void sml_timestamp_local_free(sml_timestamp_local *time); + sml_time *sml_time_init(); sml_time *sml_time_parse(sml_buffer *buf); void sml_time_write(sml_time *time, sml_buffer *buf); diff --git a/sml/src/sml_time.c b/sml/src/sml_time.c index 6e7beba..bc11a92 100644 --- a/sml/src/sml_time.c +++ b/sml/src/sml_time.c @@ -21,6 +21,63 @@ #include #include +sml_timestamp_local *sml_timestamp_local_init() { + sml_timestamp_local *t = (sml_timestamp_local *)malloc(sizeof(sml_timestamp_local)); + *t = (sml_timestamp_local){.timestamp = NULL, .local_offset = NULL, .season_time_offset = NULL}; + return t; +} + +sml_timestamp_local *sml_timestamp_local_parse(sml_buffer *buf) { + if (sml_buf_optional_is_skipped(buf)) { + return NULL; + } + + if (sml_buf_get_next_length(buf) != 3) { + buf->error = 1; + goto error; + } + + sml_timestamp_local *time = sml_timestamp_local_init(); + + time->timestamp = sml_u32_parse(buf); + if (sml_buf_has_errors(buf)) + goto error; + + time->local_offset = sml_i16_parse(buf); + if (sml_buf_has_errors(buf)) + goto error; + + time->season_time_offset = sml_i16_parse(buf); + if (sml_buf_has_errors(buf)) + goto error; + + return time; + +error: + sml_timestamp_local_free(time); + return NULL; +} + +void sml_timestamp_local_write(sml_timestamp_local *time, sml_buffer *buf) { + if (time == 0) { + sml_buf_optional_write(buf); + return; + } + + sml_u32_write(time->timestamp, buf); + sml_i16_write(time->local_offset, buf); + sml_i16_write(time->season_time_offset, buf); +} + +void sml_timestamp_local_free(sml_timestamp_local *time) { + if (time) { + sml_number_free(time->timestamp); + sml_number_free(time->local_offset); + sml_number_free(time->season_time_offset); + free(time); + } +} + sml_time *sml_time_init() { sml_time *t = (sml_time *)malloc(sizeof(sml_time)); *t = (sml_time){.tag = NULL, .data.sec_index = NULL}; @@ -67,44 +124,9 @@ sml_time *sml_time_parse(sml_buffer *buf) { goto error; break; case SML_TYPE_LIST: - // Some meters (e.g. FROETEC Multiflex ZG22) giving not one uint32 - // as timestamp, but a list of 3 values. - // Ignoring these values, so that parsing does not fail. - sml_buf_get_next_length(buf); // should we check the length here? - u32 *t1 = sml_u32_parse(buf); - if (sml_buf_has_errors(buf)) { - if (t1) - sml_number_free(t1); - goto error; - } - i16 *t2 = sml_i16_parse(buf); - if (sml_buf_has_errors(buf)) { - if (t1) - sml_number_free(t1); - if (t2) - sml_number_free(t2); - goto error; - } - i16 *t3 = sml_i16_parse(buf); - if (sml_buf_has_errors(buf)) { - if (t1) - sml_number_free(t1); - if (t2) - sml_number_free(t2); - if (t3) - sml_number_free(t3); + tme->data.local_timestamp = sml_timestamp_local_parse(buf); + if (sml_buf_has_errors(buf)) goto error; - } - fprintf( - stderr, - "libsml: error: sml_time as list[3]: ignoring value[0]=%u value[1]=%d value[2]=%d\n", - t1 ? *t1 : 0, t2 ? *t2 : 0, t3 ? *t3 : 0); - if (t1) - sml_number_free(t1); - if (t2) - sml_number_free(t2); - if (t3) - sml_number_free(t3); break; default: goto error; @@ -125,13 +147,26 @@ void sml_time_write(sml_time *t, sml_buffer *buf) { sml_buf_set_type_and_length(buf, SML_TYPE_LIST, 2); sml_u8_write(t->tag, buf); - sml_u32_write(t->data.timestamp, buf); + if (*t->tag == SML_TIME_LOCAL_TIMESTAMP) { + sml_buf_set_type_and_length(buf, SML_TYPE_LIST, 3); + sml_timestamp_local_write(t->data.local_timestamp, buf); + } else { + sml_u32_write(t->data.timestamp, buf); + } } void sml_time_free(sml_time *tme) { if (tme) { + if (tme->tag) { + switch (*tme->tag) { + case SML_TIME_LOCAL_TIMESTAMP: + sml_timestamp_local_free(tme->data.local_timestamp); + break; + default: + sml_number_free(tme->data.timestamp); + } + } sml_number_free(tme->tag); - sml_number_free(tme->data.timestamp); free(tme); } } diff --git a/test/src/sml_time_test.c b/test/src/sml_time_test.c index 861bfa2..b7ec393 100644 --- a/test/src/sml_time_test.c +++ b/test/src/sml_time_test.c @@ -24,18 +24,14 @@ TEST_GROUP(sml_time); static sml_buffer *buf; -TEST_SETUP(sml_time) { - buf = sml_buffer_init(512); -} +TEST_SETUP(sml_time) { buf = sml_buffer_init(512); } -TEST_TEAR_DOWN(sml_time) { - sml_buffer_free(buf); -} +TEST_TEAR_DOWN(sml_time) { sml_buffer_free(buf); } TEST(sml_time, init) { sml_time *t = sml_time_init(); TEST_ASSERT_NOT_NULL(t); - sml_time_free( t ); + sml_time_free(t); } TEST(sml_time, parse_sec_index) { @@ -46,7 +42,7 @@ TEST(sml_time, parse_sec_index) { TEST_ASSERT_EQUAL(SML_TIME_SEC_INDEX, *(t->tag)); TEST_ASSERT_EQUAL(8, buf->cursor); - sml_time_free( t ); + sml_time_free(t); } TEST(sml_time, parse_timestamp) { @@ -57,7 +53,18 @@ TEST(sml_time, parse_timestamp) { TEST_ASSERT_EQUAL(SML_TIME_TIMESTAMP, *(t->tag)); TEST_ASSERT_EQUAL(8, buf->cursor); - sml_time_free( t ); + sml_time_free(t); +} + +TEST(sml_time, parse_timestamp_local) { + hex2binary("7262037365000000FF53003C530001", sml_buf_get_current_buf(buf)); + sml_time *t = sml_time_parse(buf); + + TEST_ASSERT_NOT_NULL(t); + TEST_ASSERT_EQUAL(SML_TIME_LOCAL_TIMESTAMP, *(t->tag)); + TEST_ASSERT_EQUAL(15, buf->cursor); + + sml_time_free(t); } TEST(sml_time, parse_optional) { @@ -67,7 +74,7 @@ TEST(sml_time, parse_optional) { TEST_ASSERT_NULL(t); TEST_ASSERT_EQUAL(1, buf->cursor); - sml_time_free( t ); + sml_time_free(t); } TEST(sml_time, write_sec_index) { @@ -78,7 +85,22 @@ TEST(sml_time, write_sec_index) { sml_time_write(t, buf); expected_buf(buf, "72620165000000FF", 8); - sml_time_free( t ); + sml_time_free(t); +} + +TEST(sml_time, write_timestamp_local) { + sml_time *t = sml_time_init(); + sml_timestamp_local *local_t = sml_timestamp_local_init(); + local_t->timestamp = sml_u32_init(255); + local_t->local_offset = sml_i16_init(20); + local_t->season_time_offset = sml_i16_init(10); + t->data.local_timestamp = local_t; + t->tag = sml_u8_init(SML_TIME_LOCAL_TIMESTAMP); + + sml_time_write(t, buf); + expected_buf(buf, "7262037365000000FF53001453000A", 15); + + sml_time_free(t); } TEST(sml_time, write_optional) { @@ -90,8 +112,9 @@ TEST_GROUP_RUNNER(sml_time) { RUN_TEST_CASE(sml_time, init); RUN_TEST_CASE(sml_time, parse_sec_index); RUN_TEST_CASE(sml_time, parse_timestamp); + RUN_TEST_CASE(sml_time, parse_timestamp_local); RUN_TEST_CASE(sml_time, parse_optional); RUN_TEST_CASE(sml_time, write_sec_index); + RUN_TEST_CASE(sml_time, write_timestamp_local); RUN_TEST_CASE(sml_time, write_optional); } -