From c74d1e60264fea66393dbca7cb2f4adb153867f3 Mon Sep 17 00:00:00 2001 From: Edward Emelianov Date: Thu, 26 Mar 2026 20:29:29 +0300 Subject: [PATCH] simplest local weather proxy over SHM --- .gitignore | 3 + Daemons/Readme.md | 16 +- Daemons/weather_proxy/Makefile | 33 ++ Daemons/weather_proxy/weather_client.c | 57 ++++ Daemons/weather_proxy/weather_clt_example.c | 13 + Daemons/weather_proxy/weather_daemon.c | 335 +++++++++++++++++++ Daemons/weather_proxy/weather_data.h | 26 ++ Daemons/weather_proxy/weather_proxy.cflags | 1 + Daemons/weather_proxy/weather_proxy.config | 2 + Daemons/weather_proxy/weather_proxy.creator | 1 + Daemons/weather_proxy/weather_proxy.cxxflags | 1 + Daemons/weather_proxy/weather_proxy.files | 4 + Daemons/weather_proxy/weather_proxy.includes | 0 13 files changed, 487 insertions(+), 5 deletions(-) create mode 100644 Daemons/weather_proxy/Makefile create mode 100644 Daemons/weather_proxy/weather_client.c create mode 100644 Daemons/weather_proxy/weather_clt_example.c create mode 100644 Daemons/weather_proxy/weather_daemon.c create mode 100644 Daemons/weather_proxy/weather_data.h create mode 100644 Daemons/weather_proxy/weather_proxy.cflags create mode 100644 Daemons/weather_proxy/weather_proxy.config create mode 100644 Daemons/weather_proxy/weather_proxy.creator create mode 100644 Daemons/weather_proxy/weather_proxy.cxxflags create mode 100644 Daemons/weather_proxy/weather_proxy.files create mode 100644 Daemons/weather_proxy/weather_proxy.includes diff --git a/.gitignore b/.gitignore index f61c398..7421526 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ * !*.* !*/ +!Makefile +![Rr][Ee][Aa][Dd][Mm][Ee] # Prerequisites *.d @@ -33,6 +35,7 @@ mk/ build/ # diff +.qtcreator/* *.log *.tgz *.tar.gz diff --git a/Daemons/Readme.md b/Daemons/Readme.md index 1c080f9..f86efe0 100644 --- a/Daemons/Readme.md +++ b/Daemons/Readme.md @@ -2,11 +2,17 @@ Different daemons & tools ========================= - *10micron_stellarium* - simple daemon for 10-micron mount management from stellarium interface -- *domedaemon* - open/close Baaden dome by network query +- *astrosib* - some scripts used during observations +- *deprecated* - deprecated code +- *domedaemon-astrosib* - deprecated astrosib daemon +- *domedaemon_baader* - open/close Baaden dome by network query - *domedaemon-astrosib* - open/close Astrosib dome by network query - *netdaemon* - template for net-daemons - *netsocket* - scripts for management of network 220V-socket -- *send_coordinates* - get/send coordinates to 10-micron mount through stellarium daemon -- *teldaemon* - open/close Astrosib-500 scope covers by network query -- *weatherdaemon* - weather daemon for old meteostation -- *weatherdaemon_newmeteo* - daemon for new (chinese) meteostation +- *send_coordinates* - get/send coordinates to 10-micron mount through stellarium daemon (almost deprecated) +- *teldaemon_astrosib* - open/close Astrosib-500 scope covers by network query +- *weatherdaemon* - weather daemon for old meteostation (almost deprecated) +- *weatherdaemon_multimeteo* - (pre-developed) version of weather daemon for ALL sensors on "Astro-M" complex +- *weatherdaemon_newmeteo* - daemon for new (chinese) meteostation (almost deprecated) +- *weather_database* - make database by data of almost deprecated weather daemons +- *weather_proxy* - (pre-developed) daemon gathering meteo data and sharing it on localhost over SHM diff --git a/Daemons/weather_proxy/Makefile b/Daemons/weather_proxy/Makefile new file mode 100644 index 0000000..286d3a0 --- /dev/null +++ b/Daemons/weather_proxy/Makefile @@ -0,0 +1,33 @@ +CC = gcc +CFLAGS = -Wall -Wextra -fPIC +LDFLAGS = -lrt -pthread + +all: weather_daemon libweather.so weather_clt_example + +weather_daemon: weather_daemon.o + $(CC) -o $@ $^ $(LDFLAGS) + +weather_clt_example: weather_clt_example.o + $(CC) -o $@ $^ $(LDFLAGS) -l weather -L. + +weather_daemon.o: weather_daemon.c weather_data.h + $(CC) $(CFLAGS) -c $< + +libweather.so: weather_client.o + $(CC) -shared -o $@ $^ $(LDFLAGS) + +#libweather.a: weather_client.o +# ar rcs $@ $^ + +weather_client.o: weather_client.c weather_data.h + $(CC) $(CFLAGS) -c $< + +weather_clt_example.o: weather_clt_example.c libweather.so + $(CC) $(CFLAGS) -c $< + +clean: + rm -f *.o weather_daemon libweather.so libweather.a weather_clt_example + +install: + cp libweather.so /usr/local/lib/ + cp weather_data.h /usr/local/include/ \ No newline at end of file diff --git a/Daemons/weather_proxy/weather_client.c b/Daemons/weather_proxy/weather_client.c new file mode 100644 index 0000000..fec0f08 --- /dev/null +++ b/Daemons/weather_proxy/weather_client.c @@ -0,0 +1,57 @@ +#include "weather_data.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SHM_NAME "/weather_shm" +#define SEM_NAME "/weather_sem" + +int get_weather_data(weather_data_t *data) { + int shm_fd; + sem_t *sem; + weather_data_t *shared_data; + int ret = 0; + + shm_fd = shm_open(SHM_NAME, O_RDONLY, 0600); + if (shm_fd == -1) { + perror("shm_open"); + return -1; + } + + shared_data = mmap(NULL, sizeof(weather_data_t), PROT_READ, MAP_SHARED, shm_fd, 0); + if (shared_data == MAP_FAILED) { + perror("mmap"); + close(shm_fd); + return -1; + } + + sem = sem_open(SEM_NAME, 0); + if (sem == SEM_FAILED) { + perror("sem_open"); + munmap(shared_data, sizeof(weather_data_t)); + close(shm_fd); + return -1; + } + + if (sem_wait(sem) == -1) { + perror("sem_wait"); + ret = -1; + goto cleanup; + } + + memcpy(data, shared_data, sizeof(weather_data_t)); + + sem_post(sem); + +cleanup: + sem_close(sem); + munmap(shared_data, sizeof(weather_data_t)); + close(shm_fd); + return ret; +} diff --git a/Daemons/weather_proxy/weather_clt_example.c b/Daemons/weather_proxy/weather_clt_example.c new file mode 100644 index 0000000..109cfc4 --- /dev/null +++ b/Daemons/weather_proxy/weather_clt_example.c @@ -0,0 +1,13 @@ +#include "weather_data.h" +#include + +int main() { + weather_data_t wd; + if (get_weather_data(&wd) == 0) { + printf("Weather: %d, Max wind: %.1f, Wind: %.1f, Temp: %.1f; updated @%.1f\n", + wd.weather, wd.windmax, wd.wind, wd.exttemp, wd.last_update); + } else { + fprintf(stderr, "Failed to get weather data\n"); + } + return 0; +} diff --git a/Daemons/weather_proxy/weather_daemon.c b/Daemons/weather_proxy/weather_daemon.c new file mode 100644 index 0000000..6b76413 --- /dev/null +++ b/Daemons/weather_proxy/weather_daemon.c @@ -0,0 +1,335 @@ +#include "weather_data.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEAD_TMOUT 15 +#define RECONN_TMOUT 5 +#define STAT_TMOUT 60 +#define WEAT_TMOUT 5 + +#define SHM_NAME "/weather_shm" +#define SEM_NAME "/weather_sem" + +static int shm_fd = -1; +static sem_t *sem = NULL; +static weather_data_t *shared_data = NULL; +static volatile int running = 1; + +#if 0 +static void log_message(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + vsyslog(LOG_DAEMON | LOG_INFO, fmt, ap); + va_end(ap); +} + +static void log_error(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + vsyslog(LOG_DAEMON | LOG_ERR, fmt, ap); + va_end(ap); +} +#endif + +#define log_message(...) do{printf("message: "); printf(__VA_ARGS__); printf("\n");}while(0) +#define log_error(...) do{printf("error: "); printf(__VA_ARGS__); printf("\n");}while(0) + +static void signal_handler(int sig) { + if (sig == SIGTERM || sig == SIGINT) { + running = 0; + log_message("Received signal %d, shutting down", sig); + } +} + +static int init_ipc(void) { + shm_fd = shm_open(SHM_NAME, O_RDWR, 0600); // try to open existant SHM + if (shm_fd == -1) { + printf("Create new shared memory\n"); + // no - create new + shm_fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0600); + if (shm_fd == -1) { + log_error("shm_open (create) failed: %s", strerror(errno)); + return -1; + } + if (ftruncate(shm_fd, sizeof(weather_data_t)) == -1) { + log_error("ftruncate failed: %s", strerror(errno)); + return -1; + } + shared_data = mmap(NULL, sizeof(weather_data_t), PROT_READ | PROT_WRITE, + MAP_SHARED, shm_fd, 0); + if (shared_data == MAP_FAILED) { + log_error("mmap failed: %s", strerror(errno)); + return -1; + } + // default values to data + } else { + printf("Use existant SHM\n"); + // use existant SHM + shared_data = mmap(NULL, sizeof(weather_data_t), PROT_READ | PROT_WRITE, + MAP_SHARED, shm_fd, 0); + if (shared_data == MAP_FAILED) { + log_error("mmap failed: %s", strerror(errno)); + return -1; + } + } + + memset(shared_data, 0, sizeof(weather_data_t)); + + // create samaphore if no + sem = sem_open(SEM_NAME, O_CREAT, 0600, 1); + if (sem == SEM_FAILED) { + log_error("sem_open failed: %s", strerror(errno)); + return -1; + } + return 0; +} + +// free IPC +static void cleanup_ipc(void) { + if (sem != NULL) { + sem_close(sem); + if(-1 == sem_unlink(SEM_NAME)) perror("Can't delete semaphore"); + } + if (shared_data != NULL) { + munmap(shared_data, sizeof(weather_data_t)); + } + if (shm_fd != -1) { + close(shm_fd); + if (shm_unlink(SHM_NAME) == -1) { + perror("can't unlink SHM"); + } + } +} + +static void parse_line(const char *line, weather_data_t *data) { + char key[64]; + char value[256]; + if (sscanf(line, "%63[^=]=%255s", key, value) == 2) { + if (strcmp(key, "weather") == 0) { + if (strcmp(value, "good") == 0) + data->weather = WEATHER_GOOD; + else if (strcmp(value, "bad") == 0) + data->weather = WEATHER_BAD; + else if (strcmp(value, "terrible") == 0) + data->weather = WEATHER_TERRIBLE; + printf("got weather: %d\n", data->weather); + } else if (strcmp(key, "Windmax") == 0) { + data->windmax = atof(value); + printf("got windmax: %g\n", data->windmax); + } else if (strcmp(key, "Rain") == 0) { + data->rain = atoi(value); + printf("got rain: %d\n", data->rain); + } else if (strcmp(key, "Clouds") == 0) { + data->clouds = atof(value); + printf("got clouds: %g\n", data->clouds); + } else if (strcmp(key, "Wind") == 0) { + data->wind = atof(value); + printf("got wind: %g\n", data->wind); + } else if (strcmp(key, "Temperature") == 0) { + data->exttemp = atof(value); + printf("got temp: %g\n", data->exttemp); + } else if (strcmp(key, "Pressure") == 0) { + data->pressure = atof(value); + printf("got pressure: %g\n", data->pressure); + } else if (strcmp(key, "Humidity") == 0) { + data->humidity = atof(value); + printf("got humidity: %g\n", data->humidity); + } else if (strcmp(key, "prohibited") == 0) { + data->prohibited = atoi(value); + } else if (strcmp(key, "Time") == 0) { + data->last_update = atof(value); + // update all + if (sem_wait(sem) == -1) { + log_error("sem_wait failed: %s", strerror(errno)); + } else { + memcpy(shared_data, data, sizeof(weather_data_t)); + sem_post(sem); + log_message("Weather data updated"); + } + } + } +} + +static int sock = -1; +static FILE *sock_file = NULL; + +static int opensock(const char *server_ip, int port){ + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) { + log_error("socket creation failed: %s", strerror(errno)); + return -1; + } + + struct sockaddr_in server_addr; + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(port); + if (inet_pton(AF_INET, server_ip, &server_addr.sin_addr) <= 0) { + log_error("invalid address: %s", server_ip); + close(sock); + sock = -1; + return -1; + } + + if (connect(sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) { + log_error("connect failed: %s", strerror(errno)); + close(sock); + sock = -1; + return -1; + } + + int flags = fcntl(sock, F_GETFL, 0); + if (flags == -1){ + perror("fcntl F_GETFL"); + }else{ + if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) perror("fcntl F_SETFL"); + } + + sock_file = fdopen(sock, "r"); + if (!sock_file) { + log_error("fdopen failed: %s", strerror(errno)); + close(sock); + sock = -1; + return -1; + } + + return 0; +} + +static int request_weather_data() { + static time_t tstat = 0, tcur = 0; + + char *request = "\n"; + time_t tnow = time(NULL); + if(tnow - tcur >= WEAT_TMOUT){ + tcur = tnow; + }else if(tnow - tstat >= STAT_TMOUT){ + tstat = tnow; + request = "stat60\n"; + }else return 1; // not now + + printf("try to send request: '%s", request); + if (send(sock, request, strlen(request), 0) < 0) { + log_error("send failed: %s", strerror(errno)); + close(sock); + sock = -1; + return -1; + } + + return 0; +} + +static void run_daemon(const char *server_ip, int port) { + char line[256]; + weather_data_t new_data; + opensock(server_ip, port); // just try: in case of error reopen next time + memcpy(&new_data, shared_data, sizeof(weather_data_t)); + time_t lastert = time(NULL); + while (running) { + time_t tnow = time(NULL); + if (-1 == sock || request_weather_data() == -1) { + if(tnow - lastert > RECONN_TMOUT){ // try to reconnect + log_error("Failed to request weather data, retry"); + if(-1 == opensock(server_ip, port)) lastert += 5; + else lastert = tnow; + } + }else lastert = tnow; + + while (fgets(line, sizeof(line), sock_file)) { + line[strcspn(line, "\r\n")] = '\0'; + printf("parse '%s'\n", line); + parse_line(line, &new_data); + } + } + close(sock); +} + + +#if 0 +static void daemonize(void) { + pid_t pid = fork(); + if (pid < 0) { + perror("fork"); + exit(EXIT_FAILURE); + } + if (pid > 0) exit(EXIT_SUCCESS); + + if (setsid() < 0) { + perror("setsid"); + exit(EXIT_FAILURE); + } + + signal(SIGHUP, SIG_IGN); + + pid = fork(); + if (pid < 0) { + perror("second fork"); + exit(EXIT_FAILURE); + } + if (pid > 0) exit(EXIT_SUCCESS); + + if (chdir("/") < 0) { + perror("chdir"); + exit(EXIT_FAILURE); + } + + umask(0); + + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + open("/dev/null", O_RDWR); + dup(0); + dup(0); +} +#endif + +int main(int argc, char *argv[]) { + if (argc != 3) { + fprintf(stderr, "Usage: %s \n", argv[0]); + exit(EXIT_FAILURE); + } + + const char *server_ip = argv[1]; + int port = atoi(argv[2]); + if (port <= 0 || port > 65535) { + fprintf(stderr, "Invalid port\n"); + exit(EXIT_FAILURE); + } + + //daemonize(); + + log_message("Starting weather daemon, server %s:%d", server_ip, port); + + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = signal_handler; + sigemptyset(&sa.sa_mask); + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + signal(SIGPIPE, SIG_IGN); + + if (init_ipc() != 0) { + log_error("IPC initialization failed"); + exit(EXIT_FAILURE); + } + + run_daemon(server_ip, port); + + cleanup_ipc(); + log_message("Weather daemon stopped"); + return 0; +} diff --git a/Daemons/weather_proxy/weather_data.h b/Daemons/weather_proxy/weather_data.h new file mode 100644 index 0000000..4557249 --- /dev/null +++ b/Daemons/weather_proxy/weather_data.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +typedef enum { + WEATHER_GOOD = 0, + WEATHER_BAD = 1, + WEATHER_TERRIBLE = 2 +} weather_condition_t; + +typedef struct { + weather_condition_t weather; + float windmax; + int rain; + float clouds; + float wind; + float exttemp; + float pressure; + float humidity; + int prohibited; + double last_update; +} weather_data_t; + +int get_weather_data(weather_data_t *data); + diff --git a/Daemons/weather_proxy/weather_proxy.cflags b/Daemons/weather_proxy/weather_proxy.cflags new file mode 100644 index 0000000..68d5165 --- /dev/null +++ b/Daemons/weather_proxy/weather_proxy.cflags @@ -0,0 +1 @@ +-std=c17 \ No newline at end of file diff --git a/Daemons/weather_proxy/weather_proxy.config b/Daemons/weather_proxy/weather_proxy.config new file mode 100644 index 0000000..e0284f4 --- /dev/null +++ b/Daemons/weather_proxy/weather_proxy.config @@ -0,0 +1,2 @@ +// Add predefined macros for your project here. For example: +// #define THE_ANSWER 42 diff --git a/Daemons/weather_proxy/weather_proxy.creator b/Daemons/weather_proxy/weather_proxy.creator new file mode 100644 index 0000000..e94cbbd --- /dev/null +++ b/Daemons/weather_proxy/weather_proxy.creator @@ -0,0 +1 @@ +[General] diff --git a/Daemons/weather_proxy/weather_proxy.cxxflags b/Daemons/weather_proxy/weather_proxy.cxxflags new file mode 100644 index 0000000..6435dfc --- /dev/null +++ b/Daemons/weather_proxy/weather_proxy.cxxflags @@ -0,0 +1 @@ +-std=c++17 \ No newline at end of file diff --git a/Daemons/weather_proxy/weather_proxy.files b/Daemons/weather_proxy/weather_proxy.files new file mode 100644 index 0000000..809b8ac --- /dev/null +++ b/Daemons/weather_proxy/weather_proxy.files @@ -0,0 +1,4 @@ +weather_client.c +weather_clt_example.c +weather_daemon.c +weather_data.h diff --git a/Daemons/weather_proxy/weather_proxy.includes b/Daemons/weather_proxy/weather_proxy.includes new file mode 100644 index 0000000..e69de29