commit e12efe45ddbafa16f099c3a7492ec7623897ebef
parent 1706926b082abc50e5785b7b37e2215ad377e1cc
Author: Vincent Forest <vincent.forest@meso-star.com>
Date: Wed, 14 May 2025 16:40:58 +0200
Add loading functions
Loading can be done from a path to a file on disk or from a stream.
Diffstat:
5 files changed, 316 insertions(+), 14 deletions(-)
diff --git a/Makefile b/Makefile
@@ -28,11 +28,11 @@ all: default tests
################################################################################
# Library
################################################################################
-SRC = src/smeteo.c
+SRC = src/smeteo.c src/smeteo_load.c
OBJ = $(SRC:.c=.o)
DEP = $(SRC:.c=.d)
-CFLAGS_LIB = $(CFLAGS_SO) $(INCS) -DSMETEO_SHARED_BUILD
+CFLAGS_LIB = -std=c99 $(CFLAGS_SO) $(INCS) -DSMETEO_SHARED_BUILD
LDFLAGS_LIB = $(LDFLAGS_SO) $(LIBS)
library: .config $(DEP)
@@ -67,7 +67,6 @@ libsmsh.o: $(OBJ)
.c.o:
$(CC) $(CFLAGS_LIB) -c $< -o $@
-
################################################################################
# Miscellaneous
################################################################################
@@ -122,7 +121,7 @@ PKG_CONFIG_LOCAL = PKG_CONFIG_PATH="./:$${PKG_CONFIG_PATH}" $(PKG_CONFIG)
INCS_TEST = $$($(PKG_CONFIG_LOCAL) $(PCFLAGS) --cflags rsys smeteo-local.pc)
LIBS_TEST = $$($(PKG_CONFIG_LOCAL) $(PCFLAGS) --libs rsys smeteo-local.pc)
-CFLAGS_TEST = $(CFLAGS_EXE) $(INCS_TEST)
+CFLAGS_TEST = -std=c89 $(CFLAGS_EXE) $(INCS_TEST)
LDFLAGS_TEST = $(LDFLAGS_EXE) $(LIBS_TEST)
tests: library $(TEST_DEP) $(TEST_BIN)
diff --git a/src/smeteo.c b/src/smeteo.c
@@ -14,18 +14,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "smeteo.h"
+#include "smeteo_c.h"
-#include <rsys/logger.h>
#include <rsys/mem_allocator.h>
-#include <rsys/ref_count.h>
-#include <rsys/str.h>
-
-struct smeteo {
- struct str filename;
- struct mem_allocator* allocator;
- struct logger* logger;
- ref_T ref;
-};
/*******************************************************************************
* Helper functions
@@ -43,6 +34,7 @@ release_smeteo(ref_T* ref)
struct smeteo* smeteo = CONTAINER_OF(ref, struct smeteo, ref);
ASSERT(smeteo);
str_release(&smeteo->filename);
+ darray_entry_release(&smeteo->entries);
MEM_RM(smeteo->allocator, smeteo);
}
@@ -69,7 +61,9 @@ smeteo_create
ref_init(&smeteo->ref);
smeteo->allocator = allocator;
smeteo->logger = args->logger ? args->logger : LOGGER_DEFAULT;
+ smeteo->verbose = args->verbose;
str_init(smeteo->allocator, &smeteo->filename);
+ darray_entry_init(smeteo->allocator, &smeteo->entries);
exit:
if(out_smeteo) *out_smeteo = smeteo;
diff --git a/src/smeteo.h b/src/smeteo.h
@@ -64,6 +64,17 @@ SMETEO_API res_T
smeteo_ref_put
(struct smeteo* smeteo);
+SMETEO_API res_T
+smeteo_load
+ (struct smeteo* smeteo,
+ const char* filename);
+
+SMETEO_API res_T
+smeteo_load_stream
+ (struct smeteo* smeteo,
+ FILE* fp,
+ const char* name);
+
END_DECLS
#endif /* SMETEO_H */
diff --git a/src/smeteo_c.h b/src/smeteo_c.h
@@ -0,0 +1,65 @@
+/* Copyright (C) 2025 |Méso|Star> (contact@meso-star.com)
+ *
+ * This program is free software: you can redismshbute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is dismshbuted in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef SMETEO_C_H
+#define SMETEO_C_H
+
+#include <rsys/dynamic_array.h>
+#include <rsys/logger.h>
+#include <rsys/ref_count.h>
+#include <rsys/str.h>
+
+#include <time.h> /* struct tm */
+
+/* Helper macros for logging */
+#define LOG__(Dev, Lvl, Type, ...) { \
+ if ((Dev)->verbose >= (Lvl)) \
+ logger_print((Dev)->logger, Type, __VA_ARGS__); \
+} (void)0
+#define ERROR(Dev, ...) LOG__(Dev, 1, LOG_ERROR, "error: "__VA_ARGS__)
+#define WARN(Dev, ...) LOG__(Dev, 2, LOG_WARNING, "warning: "__VA_ARGS__)
+#define INFO(Dev, ...) LOG__(Dev, 3, LOG_OUTPUT, __VA_ARGS__)
+
+struct entry {
+ struct tm time;
+ double Tsrf; /* Surface temperature [K] */
+ double Tatm; /* Atmosphere temperature [K] */
+ double SWdn; /* Shortwave downward flux [W/m^2] */
+ double SWup; /* Shortwave downward flux [W/m^2] */
+ double Trad; /* Radiative temperature [K] */
+ double H; /* Convection coefficient [W/K/m^2] */
+ double LE; /* ??? */
+ double day_1850; /* ??? */
+};
+#define ENTRY_NULL__ {0}
+static const struct entry ENTRY_NULL = ENTRY_NULL__;
+
+/* Generate the darray_entry data type and its API */
+#define DARRAY_NAME entry
+#define DARRAY_DATA struct entry
+#include <rsys/dynamic_array.h>
+
+struct smeteo {
+ struct str filename;
+ struct darray_entry entries;
+
+ int verbose; /* Verbosity level */
+
+ struct mem_allocator* allocator;
+ struct logger* logger;
+ ref_T ref;
+};
+
+#endif /* SMETEO_C_H */
diff --git a/src/smeteo_load.c b/src/smeteo_load.c
@@ -0,0 +1,233 @@
+/* Copyright (C) 2025 |Méso|Star> (contact@meso-star.com)
+ *
+ * This program is free software: you can redismshbute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is dismshbuted in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#define _XOPEN_SOURCE /* strptime support */
+
+#include "smeteo.h"
+#include "smeteo_c.h"
+
+#include <rsys/cstr.h>
+#include <rsys/text_reader.h>
+
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+
+/*******************************************************************************
+ * Helper functions
+ ******************************************************************************/
+static INLINE void
+reset(struct smeteo* smeteo)
+{
+ ASSERT(smeteo);
+ str_clear(&smeteo->filename);
+ darray_entry_clear(&smeteo->entries);
+}
+
+/* TODO pay attention to time zone and locale (?)*/
+static res_T
+parse_time
+ (struct smeteo* smeteo,
+ struct txtrdr* txtrdr,
+ const char* date,
+ const char* hour,
+ struct tm* time)
+{
+ char buf[64] = {0};
+ size_t ldate=0, lhour=0; /* Length of corresponding string */
+ res_T res = RES_OK;
+
+ ASSERT(smeteo && time);
+
+ if(!date || !time) { res = RES_BAD_ARG; goto error; }
+
+ /* Check memory space to build a string concatenating date and time */
+ ldate = strlen(date);
+ lhour = strlen(hour);
+ if(ldate + lhour > sizeof(buf)+1/*NUL byte*/+1/*date/hour separator*/) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+
+ /* Concatenate the data and time strings */
+ strcpy(buf, date);
+ buf[ldate] = 'T'; /* Separator between date and time */
+ strcpy(buf+ldate+1, hour);
+
+ /* Convert time */
+ if(!strptime(buf, "%d-%b-%YT%H:%M:%S", time)) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+exit:
+ return res;
+error:
+ ERROR(smeteo, "%s:%zu: error parsing time '%s' '%s' -- %s\n",
+ txtrdr_get_name(txtrdr),
+ txtrdr_get_line_num(txtrdr),
+ date ? date : "(null)",
+ hour ? hour : "(null)",
+ res_to_cstr(res));
+ goto exit;
+}
+
+static res_T
+parse_double
+ (struct smeteo* smeteo,
+ struct txtrdr* txtrdr,
+ const char* name,
+ const char* str,
+ double range[2], /* Inclusive boundaries */
+ double* out_dbl)
+{
+ double dbl = 0;
+ res_T res = RES_OK;
+ ASSERT(smeteo && txtrdr && name && out_dbl);
+
+ if(!str) { res = RES_BAD_ARG; goto error; }
+
+ res = cstr_to_double(str, &dbl);
+ if(res == RES_OK && (dbl < range[0] || range[1] < dbl)) res = RES_BAD_ARG;
+ if(res != RES_OK) goto error;
+
+ *out_dbl = dbl;
+
+exit:
+ return res;
+error:
+ ERROR(smeteo, "%s:%zu: error parsing %s '%s' -- %s\n",
+ txtrdr_get_name(txtrdr),
+ txtrdr_get_line_num(txtrdr),
+ name,
+ str ? str : "(null)",
+ res_to_cstr(res));
+ goto exit;
+}
+
+static res_T
+parse_line(struct smeteo* smeteo, struct txtrdr* txtrdr)
+{
+ struct entry entry = ENTRY_NULL;
+
+ char* line = NULL;
+ char* date = NULL;
+ char* hour = NULL;
+ char* tk_ctx = NULL;
+ res_T res = RES_OK;
+
+ ASSERT(smeteo && txtrdr && txtrdr_get_line(txtrdr));
+
+ line = txtrdr_get_line(txtrdr);
+
+ date = strtok_r(line, " \t", &tk_ctx);
+ hour = strtok_r(NULL, " \t", &tk_ctx);
+ res = parse_time(smeteo, txtrdr, date, hour, &entry.time);
+ if(res != RES_OK) goto error;
+
+ #define PARSE_DOUBLE(Name, Var, Min, Max) { \
+ double range[2] = {Min, Max}; /* Inclusive boundaries */ \
+ char* Var = strtok_r(NULL, " \t", &tk_ctx); \
+ res = parse_double(smeteo, txtrdr, Name, Var, range, &entry.Var); \
+ if(res != RES_OK) goto error; \
+ } (void)0
+ PARSE_DOUBLE("surface temperature", Tsrf, 0, INF);
+ PARSE_DOUBLE("atmosphere temperature", Tatm, 0, INF);
+ PARSE_DOUBLE("shortwave downward flux", SWdn, 0, INF);
+ PARSE_DOUBLE("shortwave upward flux", SWup, 0, INF);
+ PARSE_DOUBLE("radiative temperature", Trad, 0, INF);
+ PARSE_DOUBLE("convection coefficient", H, -INF, INF);
+ PARSE_DOUBLE("???", LE, -INF, INF);
+ PARSE_DOUBLE("???", day_1850, 0, INF);
+ #undef PARSE_DOUBLE
+
+ res = darray_entry_push_back(&smeteo->entries, &entry);
+ if(res != RES_OK) {
+ ERROR(smeteo, "%s:%zu: error registering data -- %s\n",
+ txtrdr_get_name(txtrdr), txtrdr_get_line_num(txtrdr), res_to_cstr(res));
+ goto error;
+ }
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+static res_T
+load_stream(struct smeteo* smeteo, FILE* fp, const char* name)
+{
+ struct txtrdr* txtrdr = NULL;
+ res_T res = RES_OK;
+ ASSERT(smeteo && name);
+
+ reset(smeteo);
+
+ res = txtrdr_stream(smeteo->allocator, fp, name, '#', &txtrdr);
+ if(res != RES_OK) goto error;
+
+ if((res = str_set(&smeteo->filename, name)) != RES_OK) {
+ ERROR(smeteo, "Error copying file name '%s' -- %s\n", name, res_to_cstr(res));
+ goto error;
+ }
+
+ for(;;) {
+ if((res = txtrdr_read_line(txtrdr)) != RES_OK) {
+ ERROR(smeteo, "%s: error reading line -- %s\n", name, res_to_cstr(res));
+ goto error;
+ }
+ if(!txtrdr_get_cline(txtrdr)) break; /* No more line */
+ if((res = parse_line(smeteo, txtrdr)) != RES_OK) goto error;
+ }
+
+exit:
+ if(txtrdr) txtrdr_ref_put(txtrdr);
+ return res;
+error:
+ goto exit;
+}
+
+/*******************************************************************************
+ * Exported functions
+ ******************************************************************************/
+SMETEO_API res_T
+smeteo_load(struct smeteo* smeteo, const char* filename)
+{
+ FILE* fp = NULL;
+ res_T res = RES_OK;
+
+ if(!smeteo || !filename) { res = RES_BAD_ARG; goto error; }
+
+ if(!(fp = fopen(filename, "r"))) {
+ ERROR(smeteo, "Error opening file %s -- %s\n", filename, strerror(errno));
+ res = RES_IO_ERR;
+ goto error;
+ }
+
+ if((res = load_stream(smeteo, fp, filename)) != RES_OK) goto error;
+
+exit:
+ if(fp) CHK(fclose(fp) == 0);
+ return res;
+error:
+ goto exit;
+}
+
+SMETEO_API res_T
+smeteo_load_stream(struct smeteo* smeteo, FILE* fp, const char* name)
+{
+ if(!smeteo || !fp || !name) return RES_BAD_ARG;
+ return load_stream(smeteo, fp, name);
+}