GCC Code Coverage Report


Directory: src/gate/
File: src/gate/tech/webapis_weather.c
Date: 2025-09-14 13:10:38
Exec Total Coverage
Lines: 101 136 74.3%
Functions: 7 7 100.0%
Branches: 21 67 31.3%

Line Branch Exec Source
1 /* GATE PROJECT LICENSE:
2 +----------------------------------------------------------------------------+
3 | Copyright(c) 2018-2025, Stefan Meislinger |
4 | All rights reserved. |
5 | |
6 | Redistribution and use in source and binary forms, with or without |
7 | modification, are permitted provided that the following conditions are met:|
8 | |
9 | 1. Redistributions of source code must retain the above copyright notice, |
10 | this list of conditions and the following disclaimer. |
11 | 2. Redistributions in binary form must reproduce the above copyright |
12 | notice, this list of conditions and the following disclaimer in the |
13 | documentation and/or other materials provided with the distribution. |
14 | |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"|
16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 | ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
19 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
20 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
21 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
22 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
23 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
24 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
25 | THE POSSIBILITY OF SUCH DAMAGE. |
26 +----------------------------------------------------------------------------+
27 */
28
29 #include "gate/tech/webapis.h"
30 #include "gate/results.h"
31 #include "gate/encode/json.h"
32
33 #define GATE_WEBAPIS_WEATHER_REQUEST_NAME GATE_STRUCT_WEBAPIS_NAME("weather_request")
34 static gate_struct_item_t const weather_request_members[] =
35 {
36 GATE_STRUCT_ITEM_EX(gate_tech_webapi_weather_request_t, GATE_TYPE_STRING, provider, "provider", GATE_STRUCT_FLAG_DEFAULT),
37 GATE_STRUCT_ITEM_EX(gate_tech_webapi_weather_request_t, GATE_TYPE_R32, latitude, "latitude", GATE_STRUCT_FLAG_DEFAULT),
38 GATE_STRUCT_ITEM_EX(gate_tech_webapi_weather_request_t, GATE_TYPE_R32, longitude, "longitude", GATE_STRUCT_FLAG_DEFAULT),
39 GATE_STRUCT_ITEM_EX(gate_tech_webapi_weather_request_t, GATE_TYPE_STRING, location, "location", GATE_STRUCT_FLAG_DEFAULT),
40 GATE_STRUCT_ITEM_EX(gate_tech_webapi_weather_request_t, GATE_TYPE_TIME, record_time, "record_time", GATE_STRUCT_FLAG_DEFAULT),
41 };
42 static gate_struct_descriptor_t const weather_request_descriptor =
43 GATE_STRUCT_DESCRIPTOR(gate_tech_webapi_weather_request_t, GATE_WEBAPIS_WEATHER_REQUEST_NAME, weather_request_members);
44
45 1 void gate_tech_webapi_weather_request_init(gate_tech_webapi_weather_request_t* request)
46 {
47 1 gate_mem_clear(request, sizeof(gate_tech_webapi_weather_request_t));
48 1 request->struct_base.struct_descriptor = &weather_request_descriptor;
49 1 }
50
51
52
53 #define GATE_WEBAPIS_WEATHER_RESPONSE_NAME GATE_STRUCT_WEBAPIS_NAME("weather_response")
54 static gate_struct_item_t const weather_response_members[] =
55 {
56 GATE_STRUCT_ITEM_EX(gate_tech_webapi_weather_response_t, GATE_TYPE_R32, latitude, "latitude", GATE_STRUCT_FLAG_DEFAULT),
57 GATE_STRUCT_ITEM_EX(gate_tech_webapi_weather_response_t, GATE_TYPE_R32, longitude, "longitude", GATE_STRUCT_FLAG_DEFAULT),
58 GATE_STRUCT_ITEM_EX(gate_tech_webapi_weather_response_t, GATE_TYPE_STRING, location, "location", GATE_STRUCT_FLAG_DEFAULT),
59 GATE_STRUCT_ITEM_EX(gate_tech_webapi_weather_response_t, GATE_TYPE_ARRAYLIST, records, "records", GATE_STRUCT_FLAG_DEFAULT),
60 };
61 static gate_struct_descriptor_t const weather_response_descriptor =
62 GATE_STRUCT_DESCRIPTOR(gate_tech_webapi_weather_response_t, GATE_WEBAPIS_WEATHER_RESPONSE_NAME, weather_response_members);
63
64 1 void gate_tech_webapi_weather_response_init(gate_tech_webapi_weather_response_t* response)
65 {
66 1 gate_mem_clear(response, sizeof(gate_tech_webapi_weather_response_t));
67 1 response->struct_base.struct_descriptor = &weather_response_descriptor;
68 1 response->records = gate_arraylist_create(sizeof(gate_tech_webapi_weather_record_t), NULL, 0, NULL, NULL);
69 1 }
70
71
72 5 static void add_weather_record(gate_tech_webapi_weather_record_t* ptr_records, gate_size_t record_max, gate_size_t* records_count,
73 gate_real32_t value, gate_enumint_t value_type, gate_timestamp_t value_time)
74 {
75
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (*records_count >= record_max)
76 {
77 return;
78 }
79 5 ptr_records += *records_count;
80 5 ptr_records->record_value = value;
81 5 ptr_records->record_type = value_type;
82 5 ptr_records->record_time = value_time;
83 5 ++(*records_count);
84 }
85
86 4 static gate_bool_t extract_open_meteo_data(gate_string_t const* prop_name, gate_property_t const* values, gate_property_t const* units, gate_real32_t* ptr_value, gate_enumint_t ref_unit, gate_enumint_t* ptr_unit)
87 {
88 4 gate_bool_t ret = false;
89 gate_result_t result;
90 4 gate_real64_t r = 0.0;
91
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 gate_property_t const* prop_unit = units ? gate_property_member_get(units, prop_name) : NULL;
92 4 gate_property_t const* prop_value = gate_property_member_get(values, prop_name);
93
94 do
95 {
96
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (!prop_value)
97 {
98 break;
99 }
100 4 result = gate_property_get_real(prop_value, &r);
101
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 GATE_BREAK_IF_FAILED(result);
102
103 4 *ptr_value = (gate_real32_t)r;
104 4 *ptr_unit = ref_unit;
105 /* TODO: unit detection */
106 4 ret = true;
107 } while (0);
108 4 return ret;
109 }
110
111 1 static gate_result_t decode_open_meteo_values(gate_property_t const* props, gate_tech_webapi_weather_record_t* ptr_records, gate_size_t* records_count)
112 {
113 static gate_string_t const prop_lat = GATE_STRING_INIT_STATIC("latitude");
114 static gate_string_t const prop_long = GATE_STRING_INIT_STATIC("longitude");
115 static gate_string_t const prop_cur_units = GATE_STRING_INIT_STATIC("current_units");
116 static gate_string_t const prop_cur_values = GATE_STRING_INIT_STATIC("current");
117 static gate_string_t const prop_time = GATE_STRING_INIT_STATIC("time");
118 static gate_string_t const prop_temp = GATE_STRING_INIT_STATIC("temperature_2m");
119 static gate_string_t const prop_humidity = GATE_STRING_INIT_STATIC("relative_humidity_2m");
120 static gate_string_t const prop_rain = GATE_STRING_INIT_STATIC("rain");
121 static gate_string_t const prop_wcode = GATE_STRING_INIT_STATIC("weather_code");
122 static gate_string_t const prop_wind = GATE_STRING_INIT_STATIC("wind_speed_10m");
123 1 const gate_size_t max_records = *records_count;
124 1 gate_result_t ret = GATE_RESULT_FAILED;
125 1 gate_property_t const* ptr_units = NULL;
126 1 gate_property_t const* ptr_values = NULL;
127 1 gate_property_t const* ptr_unit = NULL;
128 1 gate_property_t const* ptr_value = NULL;
129 gate_real32_t value_content;
130 1 gate_int64_t int_value = 0;
131 gate_enumint_t unit_content;
132 1 gate_enumint_t wmo_code = 0;
133 1 gate_enumint_t weather_state = 0;
134 1 *records_count = 0;
135
136 do
137 {
138 1 ptr_units = gate_property_member_get(props, &prop_cur_units);
139 1 ptr_values = gate_property_member_get(props, &prop_cur_values);
140
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!ptr_values)
141 {
142 ret = GATE_RESULT_INVALIDCONTENT;
143 break;
144 }
145
146 /* temperature: */
147
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 if (extract_open_meteo_data(&prop_temp, ptr_values, ptr_units, &value_content, GATE_TECH_WEATHER_RECORD_TEMPERATURE_C, &unit_content))
148 {
149 1 add_weather_record(ptr_records, max_records, records_count, value_content, unit_content, 0);
150 }
151
152 /* humidity: */
153
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 if (extract_open_meteo_data(&prop_humidity, ptr_values, ptr_units, &value_content, GATE_TECH_WEATHER_RECORD_HUMIDITY_P, &unit_content))
154 {
155 1 add_weather_record(ptr_records, max_records, records_count, value_content, unit_content, 0);
156 }
157
158 /* rain: */
159
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 if (extract_open_meteo_data(&prop_rain, ptr_values, ptr_units, &value_content, GATE_TECH_WEATHER_RECORD_RAIN_MM, &unit_content))
160 {
161 1 add_weather_record(ptr_records, max_records, records_count, value_content, unit_content, 0);
162 }
163
164 /* wind: */
165
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 if (extract_open_meteo_data(&prop_wind, ptr_values, ptr_units, &value_content, GATE_TECH_WEATHER_RECORD_WIND_KMH, &unit_content))
166 {
167 1 add_weather_record(ptr_records, max_records, records_count, value_content, unit_content, 0);
168 }
169
170 /* wmo weather code */
171 1 ptr_value = gate_property_member_get(ptr_values, &prop_wcode);
172
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (ptr_value)
173 {
174 1 gate_property_get_int(ptr_value, &int_value);
175
176 1 weather_state = (gate_enumint_t)int_value;
177
1/29
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
1 switch (weather_state)
178 {
179 case 0: wmo_code = (GATE_TECH_WEATHER_STATE_CLEAR_SKY); break;
180
181 case 1: wmo_code = (GATE_TECH_WEATHER_STATE_CLOUDY | GATE_TECH_WEATHER_STATE_INTENSITY_LOW); break;
182 case 2: wmo_code = (GATE_TECH_WEATHER_STATE_CLOUDY | GATE_TECH_WEATHER_STATE_INTENSITY_MEDIUM); break;
183 1 case 3: wmo_code = (GATE_TECH_WEATHER_STATE_CLOUDY | GATE_TECH_WEATHER_STATE_INTENSITY_HIGH); break;
184
185 case 45: wmo_code = (GATE_TECH_WEATHER_STATE_FOG | GATE_TECH_WEATHER_STATE_INTENSITY_MEDIUM); break;
186 case 48: wmo_code = (GATE_TECH_WEATHER_STATE_FOG | GATE_TECH_WEATHER_STATE_INTENSITY_HIGH); break;
187
188 case 51: wmo_code = (GATE_TECH_WEATHER_STATE_DRIZZLE | GATE_TECH_WEATHER_STATE_INTENSITY_LOW); break;
189 case 53: wmo_code = (GATE_TECH_WEATHER_STATE_DRIZZLE | GATE_TECH_WEATHER_STATE_INTENSITY_MEDIUM); break;
190 case 55: wmo_code = (GATE_TECH_WEATHER_STATE_DRIZZLE | GATE_TECH_WEATHER_STATE_INTENSITY_HIGH); break;
191
192 case 56: wmo_code = (GATE_TECH_WEATHER_STATE_DRIZZLE | GATE_TECH_WEATHER_STATE_INTENSITY_LOW | GATE_TECH_WEATHER_STATE_INTENSITY_FREEZING); break;
193 case 57: wmo_code = (GATE_TECH_WEATHER_STATE_DRIZZLE | GATE_TECH_WEATHER_STATE_INTENSITY_HIGH | GATE_TECH_WEATHER_STATE_INTENSITY_FREEZING); break;
194
195 case 61: wmo_code = (GATE_TECH_WEATHER_STATE_RAIN | GATE_TECH_WEATHER_STATE_INTENSITY_LOW); break;
196 case 63: wmo_code = (GATE_TECH_WEATHER_STATE_RAIN | GATE_TECH_WEATHER_STATE_INTENSITY_MEDIUM); break;
197 case 65: wmo_code = (GATE_TECH_WEATHER_STATE_RAIN | GATE_TECH_WEATHER_STATE_INTENSITY_HIGH); break;
198
199 case 66: wmo_code = (GATE_TECH_WEATHER_STATE_RAIN | GATE_TECH_WEATHER_STATE_INTENSITY_LOW | GATE_TECH_WEATHER_STATE_INTENSITY_FREEZING); break;
200 case 67: wmo_code = (GATE_TECH_WEATHER_STATE_RAIN | GATE_TECH_WEATHER_STATE_INTENSITY_HIGH | GATE_TECH_WEATHER_STATE_INTENSITY_FREEZING); break;
201
202 case 71: wmo_code = (GATE_TECH_WEATHER_STATE_SNOW | GATE_TECH_WEATHER_STATE_INTENSITY_LOW | GATE_TECH_WEATHER_STATE_INTENSITY_FREEZING); break;
203 case 73: wmo_code = (GATE_TECH_WEATHER_STATE_SNOW | GATE_TECH_WEATHER_STATE_INTENSITY_MEDIUM | GATE_TECH_WEATHER_STATE_INTENSITY_FREEZING); break;
204 case 75: wmo_code = (GATE_TECH_WEATHER_STATE_SNOW | GATE_TECH_WEATHER_STATE_INTENSITY_HIGH | GATE_TECH_WEATHER_STATE_INTENSITY_FREEZING); break;
205
206 case 77: wmo_code = (GATE_TECH_WEATHER_STATE_SNOWGRAINS | GATE_TECH_WEATHER_STATE_INTENSITY_MEDIUM | GATE_TECH_WEATHER_STATE_INTENSITY_FREEZING); break;
207
208 case 80: wmo_code = (GATE_TECH_WEATHER_STATE_RAINSHOWER | GATE_TECH_WEATHER_STATE_INTENSITY_LOW); break;
209 case 81: wmo_code = (GATE_TECH_WEATHER_STATE_RAINSHOWER | GATE_TECH_WEATHER_STATE_INTENSITY_MEDIUM); break;
210 case 82: wmo_code = (GATE_TECH_WEATHER_STATE_RAINSHOWER | GATE_TECH_WEATHER_STATE_INTENSITY_HIGH); break;
211
212 case 85: wmo_code = (GATE_TECH_WEATHER_STATE_SNOWSHOWER | GATE_TECH_WEATHER_STATE_INTENSITY_LOW | GATE_TECH_WEATHER_STATE_INTENSITY_FREEZING); break;
213 case 86: wmo_code = (GATE_TECH_WEATHER_STATE_SNOWSHOWER | GATE_TECH_WEATHER_STATE_INTENSITY_HIGH | GATE_TECH_WEATHER_STATE_INTENSITY_FREEZING); break;
214
215 case 95: wmo_code = (GATE_TECH_WEATHER_STATE_THUNDERSTORM | GATE_TECH_WEATHER_STATE_INTENSITY_MEDIUM); break;
216
217 case 96: wmo_code = (GATE_TECH_WEATHER_STATE_THUNDERSTORM | GATE_TECH_WEATHER_STATE_INTENSITY_MEDIUM | GATE_TECH_WEATHER_STATE_INTENSITY_FREEZING); break;
218 case 99: wmo_code = (GATE_TECH_WEATHER_STATE_THUNDERSTORM | GATE_TECH_WEATHER_STATE_INTENSITY_HIGH | GATE_TECH_WEATHER_STATE_INTENSITY_FREEZING); break;
219 }
220 1 add_weather_record(ptr_records, max_records, records_count, (gate_real32_t)wmo_code, GATE_TECH_WEATHER_RECORD_STATE, 0);
221 }
222
223 1 ret = GATE_RESULT_OK;
224 } while (0);
225
226 1 return ret;
227 }
228
229 1 static gate_result_t get_open_meteo_weather(gate_tech_webapi_context_t* context, gate_real32_t latitude, gate_real32_t longitude,
230 gate_tech_webapi_weather_record_t* ptr_records, gate_size_t* records_count)
231 {
232 static gate_string_t const open_meteo_uri = GATE_STRING_INIT_STATIC("https://api.open-meteo.com/v1/forecast");
233
234 1 gate_result_t ret = GATE_RESULT_FAILED;
235 gate_uri_t uri;
236 gate_strbuilder_t builder;
237 1 gate_uint32_t status_code = 0;
238 1 gate_stringstream_t* ptr_stream = NULL;
239 1 gate_property_t prop = GATE_INIT_EMPTY;
240 1 gate_json_result_t json_result = GATE_INIT_EMPTY;
241 //https://api.open-meteo.com/v1/forecast?latitude=52.52&longitude=13.41&current=temperature_2m,relative_humidity_2m,rain,weather_code,wind_speed_10m&timezone=Europe%2FBerlin&forecast_days=3
242
243 do
244 {
245 1 gate_strbuilder_create(&builder, 32);
246 1 ret = gate_uri_parse(&uri, &open_meteo_uri);
247
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
248 1 ptr_stream = gate_stringstream_create(256);
249
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (NULL == ptr_stream)
250 {
251 ret = GATE_RESULT_OUTOFMEMORY;
252 break;
253 }
254
255 1 gate_strbuilder_append_cstr(&builder, "?latitude=");
256 1 gate_strbuilder_append_real(&builder, latitude, 0, 2, 0);
257 1 gate_strbuilder_append_cstr(&builder, "&longitude=");
258 1 gate_strbuilder_append_real(&builder, longitude, 0, 2, 0);
259 1 gate_strbuilder_append_cstr(&builder, "&current=temperature_2m,relative_humidity_2m,rain,weather_code,wind_speed_10m");
260
261 1 gate_string_release(&uri.query);
262 1 gate_strbuilder_to_string(&builder, &uri.query);
263
264 1 ret = gate_tech_webapi_get(context, &uri, NULL, &status_code, (gate_stream_t*)ptr_stream, NULL);
265
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
266
267
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 if ((status_code < 200) || (status_code > 300))
268 {
269 ret = GATE_RESULT_INVALIDOUTPUT;
270 break;
271 }
272
273 1 ret = gate_json_parse((gate_stream_t*)ptr_stream, &prop, &json_result);
274
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
275
276 1 ret = decode_open_meteo_values(&prop, ptr_records, records_count);
277
278 } while (0);
279
280 1 gate_property_destroy(&prop);
281
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (ptr_stream)
282 {
283 1 gate_object_release(ptr_stream);
284 }
285 1 gate_uri_destroy(&uri);
286 1 gate_strbuilder_release(&builder);
287
288 1 return ret;
289
290 }
291
292 1 gate_result_t gate_tech_webapi_weather(gate_tech_webapi_context_t* context,
293 gate_tech_webapi_weather_request_t* request,
294 gate_tech_webapi_weather_response_t* response)
295 {
296 1 gate_result_t ret = GATE_RESULT_FAILED;
297 gate_tech_webapi_weather_record_t records[64];
298 1 gate_size_t records_used = sizeof(records) / sizeof(records[0]);
299 gate_size_t ndx;
300
301 do
302 {
303 1 gate_mem_clear(&records[0], sizeof(records));
304 1 ret = get_open_meteo_weather(context, request->latitude, request->longitude, records, &records_used);
305
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
306
307 1 response->latitude = request->latitude;
308 1 response->longitude = request->longitude;
309 1 gate_string_clone(&response->location, &request->location);
310
311
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
6 for (ndx = 0; ndx != records_used; ++ndx)
312 {
313 5 gate_arraylist_add(response->records, &records[ndx]);
314 }
315 } while (0);
316
317 1 return ret;
318 }
319