Line data Source code
1 : #include "midas_c_compat.h"
2 : #include "midas.h"
3 : #include "mrpc.h"
4 : #include "msystem.h"
5 : #include <vector>
6 : #include <string>
7 : #include "string.h"
8 : #include "stdlib.h"
9 : #include "stdarg.h"
10 : #include "history.h"
11 :
12 : /*
13 : We define a simple free function to ensure that python clients can
14 : free any memory that was allocated by midas. We define it as part
15 : of this library (rather than importing libc directly in python) to
16 : ensure that the same version of libc is used for the alloc and free.
17 : */
18 0 : void c_free(void* mem) {
19 0 : free(mem);
20 0 : }
21 :
22 0 : void c_free_list(void** mem_list, int arr_len) {
23 0 : for (int i = 0; i < arr_len; i++) {
24 0 : free(mem_list[i]);
25 : }
26 :
27 0 : free(mem_list);
28 0 : }
29 :
30 : /*
31 : Copies the content for src to dest (at most dest_size bytes).
32 : dest should already have been allocated to the correct size.
33 : If the destination is not large enough to hold the entire src
34 : string, we return DB_TRUNCATED; otherwise we return SUCCESS.
35 :
36 : In general it's preferable to accept a char** from python rather than
37 : a buffer of a fixed size. Although python must then remember to free
38 : the memory we allocated.
39 : */
40 0 : INT copy_string_to_c(std::string src, char* dest, DWORD dest_size) {
41 0 : strncpy(dest, src.c_str(), dest_size);
42 :
43 0 : if (src.size() > dest_size) {
44 0 : return DB_TRUNCATED;
45 : }
46 :
47 0 : return SUCCESS;
48 : }
49 :
50 : /*
51 : Copies the content of vec into an array of type 'T' at dest. Will malloc the
52 : memory needed, so you must later call c_free() on dest. Fills dest_len with
53 : the size of the vector.
54 : */
55 0 : template <class T> INT copy_vector_to_c(std::vector<T> vec, void** dest, int& dest_len) {
56 0 : dest_len = vec.size();
57 0 : *dest = malloc(sizeof(T) * dest_len);
58 0 : std::copy(vec.begin(), vec.end(), (T*)*dest);
59 0 : return SUCCESS;
60 : }
61 :
62 : /*
63 : Copies the content of vec into an array of char* at dest. Will malloc the
64 : memory needed for each string (and for the array itself), so you must later call
65 : c_free_list() on dest. Fills dest_len with the size of the vector.
66 : */
67 0 : INT copy_vector_string_to_c(std::vector<std::string> vec, char*** dest, int& dest_len) {
68 0 : dest_len = vec.size();
69 0 : *dest = (char**) malloc(sizeof(char*) * dest_len);
70 :
71 0 : for (int i = 0; i < dest_len; i++) {
72 0 : (*dest)[i] = strdup(vec[i].c_str());
73 : }
74 :
75 0 : return SUCCESS;
76 : }
77 :
78 : /*
79 : Example of how one could wrap a midas function that returns/fills a std::string.
80 : In this version we accept a buffer of a specified size from the user.
81 :
82 : The python code would be:
83 : ```
84 : buffer = ctypes.create_string_buffer(64)
85 : lib.c_example_string_c_bufsize(buffer, 64)
86 : py_str = buffer.value.decode("utf-8")
87 : ```
88 : */
89 0 : INT c_example_string_c_bufsize(char* buffer, DWORD buffer_size) {
90 0 : std::string retval("My string that would come from a C++ function");
91 0 : return copy_string_to_c(retval, buffer, buffer_size);
92 0 : }
93 :
94 : /*
95 : Example of how one could wrap a midas function that returns/fills a std::string.
96 : In this version we allocate memory for the C char array. The caller must later
97 : free this memory themselves.
98 :
99 : The python code would be (note the final free!):
100 : ```
101 : buffer = ctypes.c_char_p()
102 : lib.c_example_string_c_alloc(ctypes.byref(buffer))
103 : py_str = buffer.value.decode("utf-8")
104 : lib.c_free(buffer)
105 : ```
106 : */
107 0 : INT c_example_string_c_alloc(char** dest) {
108 0 : std::string retval("My string that would come from a C++ function");
109 0 : *dest = strdup(retval.c_str());
110 0 : return SUCCESS;
111 0 : }
112 :
113 : /*
114 : Example of how one could wrap a midas function that returns/fills a std::vector.
115 : In this version we allocate memory for the C array. The caller must later
116 : free this memory themselves.
117 :
118 : The python code would be (note the final free!):
119 : ```
120 : import ctypes
121 : import midas.client
122 :
123 : client = midas.client.MidasClient("pytest")
124 : lib = client.lib
125 :
126 : arr = ctypes.c_void_p()
127 : arr_len = ctypes.c_int()
128 : lib.c_example_vector(ctypes.byref(arr), ctypes.byref(arr_len))
129 : casted = ctypes.cast(arr, ctypes.POINTER(ctypes.c_float))
130 : py_list = casted[:arr_len.value]
131 : lib.c_free(arr)
132 : ```
133 : */
134 0 : INT c_example_vector(void** dest, int& dest_len) {
135 0 : std::vector<float> retvec;
136 0 : for (int i = 0; i < 10; i++) {
137 0 : retvec.push_back(i/3.);
138 : }
139 :
140 0 : return copy_vector_to_c(retvec, dest, dest_len);
141 0 : }
142 :
143 : /*
144 : Example of how one could wrap a midas function that returns/fills a std::vector.
145 : In this version we allocate memory for the C array. The caller must later
146 : free this memory themselves.
147 :
148 : The python code would be (note the final free!):
149 : ```
150 : import ctypes
151 : import midas.client
152 : client = midas.client.MidasClient("pytest")
153 : lib = client.lib
154 :
155 : arr = ctypes.POINTER(ctypes.c_char_p)()
156 : arr_len = ctypes.c_int()
157 : lib.c_example_string_vector(ctypes.byref(arr), ctypes.byref(arr_len))
158 : casted = ctypes.cast(arr, ctypes.POINTER(ctypes.c_char_p))
159 : py_list = [casted[i].decode("utf-8") for i in range(arr_len.value)]
160 : lib.c_free_list(arr, arr_len)
161 : ```
162 : */
163 0 : INT c_example_string_vector(char*** dest, int& dest_len) {
164 0 : std::vector<std::string> retvec;
165 0 : retvec.push_back("Hello");
166 0 : retvec.push_back("world!");
167 :
168 0 : return copy_vector_string_to_c(retvec, dest, dest_len);
169 0 : }
170 :
171 0 : INT c_al_trigger_alarm(const char *alarm_name, const char *alarm_message, const char *default_class, const char *cond_str, INT type) {
172 0 : return al_trigger_alarm(alarm_name, alarm_message, default_class, cond_str, type);
173 : }
174 :
175 0 : INT c_al_reset_alarm(const char *alarm_name) {
176 0 : return al_reset_alarm(alarm_name);
177 : }
178 :
179 0 : INT c_al_define_odb_alarm(const char *name, const char *condition, const char *aclass, const char *message) {
180 0 : return al_define_odb_alarm(name, condition, aclass, message);
181 : }
182 :
183 0 : INT c_bm_flush_cache(INT buffer_handle, INT async_flag) {
184 0 : return bm_flush_cache(buffer_handle, async_flag);
185 : }
186 :
187 0 : INT c_bm_open_buffer(const char *buffer_name, INT buffer_size, INT * buffer_handle) {
188 0 : return bm_open_buffer(buffer_name, buffer_size, buffer_handle);
189 : }
190 :
191 0 : INT c_bm_receive_event(INT buffer_handle, void *destination, INT * buf_size, INT async_flag) {
192 0 : return bm_receive_event(buffer_handle, destination, buf_size, async_flag);
193 : }
194 :
195 0 : INT c_bm_remove_event_request(INT buffer_handle, INT request_id) {
196 0 : return bm_remove_event_request(buffer_handle, request_id);
197 : }
198 :
199 0 : INT c_bm_request_event(INT buffer_handle, short int event_id, short int trigger_mask, INT sampling_type, INT * request_id) {
200 : // Final argument is function pointer that python lib doesn't need.
201 0 : return bm_request_event(buffer_handle, event_id, trigger_mask, sampling_type, request_id, 0);
202 : }
203 :
204 0 : INT c_cm_check_deferred_transition(void) {
205 0 : return cm_check_deferred_transition();
206 : }
207 :
208 0 : INT c_cm_connect_client(const char *client_name, HNDLE * hConn) {
209 0 : return cm_connect_client(client_name, hConn);
210 : }
211 :
212 0 : INT c_cm_connect_experiment(const char *host_name, const char *exp_name, const char *client_name, void (*func) (char *)) {
213 0 : return cm_connect_experiment(host_name, exp_name, client_name, func);
214 : }
215 :
216 0 : INT c_cm_deregister_transition(INT transition) {
217 0 : return cm_deregister_transition(transition);
218 : }
219 :
220 0 : INT c_cm_disconnect_client(HNDLE hConn, BOOL bShutdown) {
221 0 : return cm_disconnect_client(hConn, bShutdown);
222 : }
223 :
224 0 : INT c_cm_disconnect_experiment() {
225 0 : return cm_disconnect_experiment();
226 : }
227 :
228 0 : INT c_cm_exist(const char *name, BOOL bUnique) {
229 0 : return cm_exist(name, bUnique);
230 : }
231 :
232 0 : INT c_cm_get_environment(char *host_name, int host_name_size, char *exp_name, int exp_name_size) {
233 0 : return cm_get_environment(host_name, host_name_size, exp_name, exp_name_size);
234 : }
235 :
236 0 : INT c_cm_get_experiment_database(HNDLE * hDB, HNDLE * hKeyClient) {
237 0 : return cm_get_experiment_database(hDB, hKeyClient);
238 : }
239 :
240 0 : INT c_cm_get_path(char *path, int path_size) {
241 0 : std::string str_path = cm_get_path();
242 0 : return copy_string_to_c(str_path, path, path_size);
243 0 : }
244 :
245 0 : const char* c_cm_get_revision(void) {
246 : // If this changes to returning a string, do:
247 : // return strdup(cm_get_revision().c_str());
248 0 : return cm_get_revision();
249 : }
250 :
251 0 : const char* c_cm_get_version(void) {
252 : // If this changes to returning a string, do:
253 : // return strdup(cm_get_version().c_str());
254 0 : return cm_get_version();
255 : }
256 :
257 0 : INT c_cm_msg(INT message_type, const char *filename, INT line, const char *facility, const char *routine, const char *format, ...) {
258 : va_list argptr;
259 : char message[1000];
260 0 : va_start(argptr, format);
261 0 : vsnprintf(message, 1000, (char *) format, argptr);
262 0 : va_end(argptr);
263 0 : return cm_msg1(message_type, filename, line, facility, routine, "%s", message);
264 : }
265 :
266 : /*
267 : Remember to call c_free_list on the dest afterwards. E.g.:
268 : ```
269 : import ctypes
270 : import midas.client
271 : lib = midas.client.MidasClient("pytest").lib
272 :
273 : arr = ctypes.POINTER(ctypes.c_char_p)()
274 : arr_len = ctypes.c_int()
275 : lib.c_cm_msg_facilities(ctypes.byref(arr), ctypes.byref(arr_len))
276 : casted = ctypes.cast(arr, ctypes.POINTER(ctypes.c_char_p))
277 : py_list = [casted[i].decode("utf-8") for i in range(arr_len.value)]
278 : lib.c_free_list(arr, arr_len)
279 : ```
280 : */
281 0 : INT c_cm_msg_facilities(char*** dest, int& dest_len) {
282 0 : std::vector<std::string> retvec;
283 0 : INT retcode = cm_msg_facilities(&retvec);
284 0 : if (retcode == SUCCESS) {
285 0 : return copy_vector_string_to_c(retvec, dest, dest_len);
286 : } else {
287 0 : return retcode;
288 : }
289 0 : }
290 :
291 0 : INT c_cm_msg_register(EVENT_HANDLER *func) {
292 0 : return cm_msg_register(func);
293 : }
294 :
295 0 : INT c_cm_msg_retrieve2(const char *facility, uint64_t before, INT min_messages, char **messages, int *num_messages_read) {
296 : // Python ctypes doesn't know the size of time_t, so just accept a uint64_t and convert here.
297 0 : time_t t = before;
298 0 : INT retval = cm_msg_retrieve2(facility, t, min_messages, messages, num_messages_read);
299 0 : return retval;
300 : }
301 :
302 0 : INT c_cm_msg_open_buffer() {
303 0 : return cm_msg_open_buffer();
304 : }
305 :
306 0 : INT c_cm_msg_close_buffer() {
307 0 : return cm_msg_close_buffer();
308 : }
309 :
310 0 : INT c_cm_register_deferred_transition(INT transition, BOOL(*func) (INT, BOOL)) {
311 0 : return cm_register_deferred_transition(transition, func);
312 : }
313 :
314 0 : INT c_cm_register_function(INT id, INT(*func) (INT, void **)) {
315 0 : return cm_register_function(id, func);
316 : }
317 :
318 0 : INT c_cm_register_transition(INT transition, INT(*func) (INT, char *), int sequence_number) {
319 0 : return cm_register_transition(transition, func, sequence_number);
320 : }
321 :
322 0 : INT c_cm_set_transition_sequence(INT transition, INT sequence_number) {
323 0 : return cm_set_transition_sequence(transition, sequence_number);
324 : }
325 :
326 0 : INT c_cm_shutdown(const char *name, BOOL bUnique) {
327 0 : return cm_shutdown(name, bUnique);
328 : }
329 :
330 0 : INT c_cm_start_watchdog_thread(void) {
331 0 : return cm_start_watchdog_thread();
332 : }
333 :
334 0 : INT c_cm_stop_watchdog_thread(void) {
335 0 : return cm_stop_watchdog_thread();
336 : }
337 :
338 0 : INT c_cm_transition(INT transition, INT run_number, char *error, INT strsize, INT async_flag, INT debug_flag) {
339 0 : return cm_transition(transition, run_number, error, strsize, async_flag, debug_flag);
340 : }
341 :
342 0 : INT c_cm_yield(INT millisec) {
343 0 : return cm_yield(millisec);
344 : }
345 :
346 0 : INT c_db_close_record(HNDLE hdb, HNDLE hkey) {
347 0 : return db_close_record(hdb, hkey);
348 : }
349 :
350 0 : INT c_db_copy_json_ls(HNDLE hDB, HNDLE hKey, char **buffer, int* buffer_size, int* buffer_end) {
351 0 : return db_copy_json_ls(hDB, hKey, buffer, buffer_size, buffer_end);
352 : }
353 :
354 0 : INT c_db_copy_json_save(HNDLE hDB, HNDLE hKey, char **buffer, int* buffer_size, int* buffer_end) {
355 0 : return db_copy_json_save(hDB, hKey, buffer, buffer_size, buffer_end);
356 : }
357 :
358 0 : INT c_db_create_key(HNDLE hdb, HNDLE key_handle, const char *key_name, DWORD type) {
359 0 : return db_create_key(hdb, key_handle, key_name, type);
360 : }
361 :
362 0 : INT c_db_create_link(HNDLE hdb, HNDLE key_handle, const char *link_name, const char *destination) {
363 0 : return db_create_link(hdb, key_handle, link_name, destination);
364 : }
365 :
366 0 : INT c_db_delete_key(HNDLE database_handle, HNDLE key_handle, BOOL follow_links) {
367 0 : return db_delete_key(database_handle, key_handle, follow_links);
368 : }
369 :
370 0 : INT c_db_enum_key(HNDLE hDB, HNDLE hKey, INT idx, HNDLE * subkey_handle) {
371 0 : return db_enum_key(hDB, hKey, idx, subkey_handle);
372 : }
373 :
374 0 : INT c_db_enum_link(HNDLE hDB, HNDLE hKey, INT idx, HNDLE * subkey_handle) {
375 0 : return db_enum_link(hDB, hKey, idx, subkey_handle);
376 : }
377 :
378 0 : INT c_db_find_key(HNDLE hdb, HNDLE hkey, const char *name, HNDLE * hsubkey) {
379 0 : return db_find_key(hdb, hkey, name, hsubkey);
380 : }
381 :
382 0 : INT c_db_find_link(HNDLE hDB, HNDLE hKey, const char *key_name, HNDLE * subhKey) {
383 0 : return db_find_link(hDB, hKey, key_name, subhKey);
384 : }
385 :
386 0 : INT c_db_get_key(HNDLE hdb, HNDLE key_handle, KEY * key) {
387 0 : return db_get_key(hdb, key_handle, key);
388 : }
389 :
390 0 : INT c_db_get_link_data(HNDLE hdb, HNDLE key_handle, void *data, INT * buf_size, DWORD type) {
391 0 : return db_get_link_data(hdb, key_handle, data, buf_size, type);
392 : }
393 :
394 0 : INT c_db_get_parent(HNDLE hDB, HNDLE hKey, HNDLE * parenthKey) {
395 0 : return db_get_parent(hDB, hKey, parenthKey);
396 : }
397 :
398 0 : INT c_db_get_value(HNDLE hdb, HNDLE hKeyRoot, const char *key_name, void *data, INT * size, DWORD type, BOOL create) {
399 0 : return db_get_value(hdb, hKeyRoot, key_name, data, size, type, create);
400 : }
401 :
402 0 : INT c_db_open_record(HNDLE hdb, HNDLE hkey, void *ptr, INT rec_size, WORD access, void (*dispatcher) (INT, INT, void *), void *info) {
403 0 : return db_open_record(hdb, hkey, ptr, rec_size, access, dispatcher, info);
404 : }
405 :
406 0 : INT c_db_rename_key(HNDLE hDB, HNDLE hKey, const char *name) {
407 0 : return db_rename_key(hDB, hKey, name);
408 : }
409 :
410 0 : INT c_db_reorder_key(HNDLE hDB, HNDLE hKey, INT index) {
411 0 : return db_reorder_key(hDB, hKey, index);
412 : }
413 :
414 0 : INT c_db_resize_string(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, int num_values, int max_string_size) {
415 0 : return db_resize_string(hDB, hKeyRoot, key_name, num_values, max_string_size);
416 : }
417 :
418 0 : INT c_db_set_link_data(HNDLE hdb, HNDLE key_handle, void *data, INT buf_size, int num_values, DWORD type) {
419 0 : return db_set_link_data(hdb, key_handle, data, buf_size, num_values, type);
420 : }
421 :
422 0 : INT c_db_set_num_values(HNDLE hDB, HNDLE hKey, INT num_values) {
423 0 : return db_set_num_values(hDB, hKey, num_values);
424 : }
425 :
426 0 : INT c_db_set_data(HNDLE hdb, HNDLE hKeyRoot, const void *data, INT size, INT num_values, DWORD type) {
427 0 : return db_set_data(hdb, hKeyRoot, data, size, num_values, type);
428 : }
429 :
430 0 : INT c_db_set_value(HNDLE hdb, HNDLE hKeyRoot, const char *key_name, const void *data, INT size, INT num_values, DWORD type) {
431 0 : return db_set_value(hdb, hKeyRoot, key_name, data, size, num_values, type);
432 : }
433 :
434 0 : INT c_db_set_value_index(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, const void *data, INT data_size, INT index, DWORD type, BOOL truncate) {
435 0 : return db_set_value_index(hDB, hKeyRoot, key_name, data, data_size, index, type, truncate);
436 : }
437 :
438 0 : INT c_db_unwatch(HNDLE hDB, HNDLE hKey) {
439 0 : return db_unwatch(hDB, hKey);
440 : }
441 :
442 0 : INT c_db_watch(HNDLE hDB, HNDLE hKey, void (*dispatcher) (INT, INT, INT, void*), void* info) {
443 0 : return db_watch(hDB, hKey, dispatcher, info);
444 : }
445 :
446 0 : INT c_jrpc_client_call(HNDLE hconn, char* cmd, char* args, char* buf, int buf_length) {
447 : // Specialized version of rpc_client_call that just deals with RPC_JRPC,
448 : // so we don't have to worry about variable arg lists.
449 : // You must already have malloc'd buf to be big enough for buf_length.
450 0 : return rpc_client_call(hconn, RPC_JRPC, cmd, args, buf, buf_length);
451 : }
452 :
453 0 : INT c_brpc_client_call(HNDLE hconn, char* cmd, char* args, char* buf, int& buf_length) {
454 : // Specialized version of rpc_client_call that just deals with RPC_BRPC,
455 : // so we don't have to worry about variable arg lists.
456 : // You must already have malloc'd buf to be big enough for buf_length.
457 0 : return rpc_client_call(hconn, RPC_BRPC, cmd, args, buf, &buf_length);
458 : }
459 :
460 0 : INT c_rpc_flush_event(void) {
461 0 : return rpc_flush_event();
462 : }
463 :
464 0 : INT c_rpc_is_remote(void) {
465 0 : return rpc_is_remote();
466 : }
467 :
468 0 : INT c_rpc_send_event(INT buffer_handle, const EVENT_HEADER *event, INT buf_size, INT async_flag, INT mode) {
469 0 : return rpc_send_event(buffer_handle, event, buf_size, async_flag, mode);
470 : }
471 :
472 0 : INT c_rpc_server_shutdown() {
473 0 : return rpc_server_shutdown();
474 : }
475 :
476 0 : INT c_ss_daemon_init(BOOL keep_stdout) {
477 0 : return ss_daemon_init(keep_stdout);
478 : }
479 :
480 0 : INT c_ss_exec(char* cmd, INT* child_pid) {
481 0 : return ss_exec(cmd, child_pid);
482 : }
483 :
484 0 : INT c_ss_suspend_reset_server_acceptions() {
485 0 : return ss_suspend_set_server_acceptions(nullptr);
486 : }
487 :
488 : MidasHistoryInterface* mh = nullptr;
489 :
490 0 : INT c_connect_history_if_needed(HNDLE hDB, bool debug=false) {
491 0 : INT status = SUCCESS;
492 :
493 0 : if (mh == nullptr) {
494 0 : status = hs_get_history(hDB, 0, HS_GET_DEFAULT|HS_GET_READER|HS_GET_INACTIVE, debug, &mh);
495 : }
496 :
497 0 : return status;
498 : }
499 :
500 0 : INT c_hs_get_events(HNDLE hDB, char*** dest, int& dest_len) {
501 0 : INT status = c_connect_history_if_needed(hDB);
502 :
503 0 : if (status != SUCCESS) {
504 0 : return status;
505 : }
506 :
507 0 : time_t t = 0;
508 0 : std::vector<std::string> events;
509 :
510 0 : status = mh->hs_get_events(t, &events);
511 :
512 0 : if (status != SUCCESS) {
513 0 : return status;
514 : }
515 :
516 0 : return copy_vector_string_to_c(events, dest, dest_len);
517 0 : }
518 :
519 0 : INT c_hs_get_tags(HNDLE hDB, char* event_name, char*** dest_names, void** dest_types, void** dest_n_data, int& dest_len) {
520 0 : INT status = c_connect_history_if_needed(hDB);
521 :
522 0 : if (status != SUCCESS) {
523 0 : return status;
524 : }
525 :
526 0 : time_t t = 0;
527 0 : std::vector<TAG> tags;
528 0 : status = mh->hs_get_tags(event_name, t, &tags);
529 :
530 0 : if (status != SUCCESS) {
531 0 : return status;
532 : }
533 :
534 0 : std::vector<std::string> tag_names;
535 0 : std::vector<DWORD> tag_types;
536 0 : std::vector<DWORD> tag_n_data;
537 :
538 0 : for (auto& tag : tags) {
539 0 : tag_names.push_back(tag.name);
540 0 : tag_types.push_back(tag.type);
541 0 : tag_n_data.push_back(tag.n_data);
542 : }
543 :
544 0 : copy_vector_string_to_c(tag_names, dest_names, dest_len);
545 0 : copy_vector_to_c(tag_types, dest_types, dest_len);
546 0 : copy_vector_to_c(tag_n_data, dest_n_data, dest_len);
547 0 : return SUCCESS;
548 0 : }
549 :
550 0 : INT c_hs_read(HNDLE hDB,
551 : uint32_t start_time, uint32_t end_time, uint32_t interval_secs,
552 : char* event_name, char* tag_name, int idx_start, int nvars,
553 : void** num_entries, void** times, void** values, void** hs_status) {
554 : // Note this function varies from hs_read() for the times/tbuffer and values/vbuffer
555 : // parameters! To simplify passing between python/C, we concatenate all the data for
556 : // all variables into one array, rather than using a 2D array.
557 : // So num_entries and hs_status are length "nvars", while
558 : // times and values are length "sum of num_entries".
559 0 : INT status = c_connect_history_if_needed(hDB);
560 :
561 0 : if (status != SUCCESS) {
562 0 : return status;
563 : }
564 :
565 0 : const char** event_names = (const char**)calloc(nvars, sizeof(const char*));
566 0 : const char** var_names = (const char**)calloc(nvars, sizeof(const char*));
567 0 : int* indices = (int*)calloc(nvars, sizeof(int));
568 :
569 0 : for (int i = 0; i < nvars; i++) {
570 0 : event_names[i] = event_name;
571 0 : var_names[i] = tag_name;
572 0 : indices[i] = idx_start + i;
573 : }
574 :
575 0 : *num_entries = calloc(nvars, sizeof(int));
576 0 : time_t** tbuffer_int = (time_t**)calloc(nvars, sizeof(time_t*));
577 0 : double** vbuffer_int = (double**)calloc(nvars, sizeof(double*));
578 0 : *hs_status = calloc(nvars, sizeof(int));
579 :
580 0 : status = mh->hs_read(start_time, end_time, interval_secs, nvars, event_names, var_names, indices, (int*)*num_entries, tbuffer_int, vbuffer_int, (int*)*hs_status);
581 :
582 : // Simplify passing back to python by concatenating timestamp buffers into
583 : // a single buffer rather than 2-D array. Same for value buffers.
584 0 : size_t tot_len = 0;
585 :
586 0 : for (int i = 0; i < nvars; i++) {
587 0 : tot_len += ((int*)(*num_entries))[i];
588 : }
589 :
590 0 : if (tot_len > 0) {
591 0 : *times = calloc(tot_len, sizeof(uint32_t));
592 0 : *values = calloc(tot_len, sizeof(double));
593 :
594 0 : uint32_t* cast_tbuffer = (uint32_t*)*times;
595 0 : double* cast_vbuffer = (double*)*values;
596 0 : size_t offset = 0;
597 :
598 0 : for (int i = 0; i < nvars; i++) {
599 0 : size_t this_n = ((int*)(*num_entries))[i];
600 0 : for (int n = 0; n < (int) this_n; n++) {
601 : // Deal with time_t vs uint32_t possibly being different sizes
602 : // by manually copying values rather than doing a memcpy.
603 0 : cast_tbuffer[offset+n] = tbuffer_int[i][n];
604 0 : cast_vbuffer[offset+n] = vbuffer_int[i][n];
605 : }
606 :
607 0 : offset += this_n;
608 : }
609 : }
610 :
611 0 : free(tbuffer_int);
612 0 : free(vbuffer_int);
613 0 : free(event_names);
614 0 : free(var_names);
615 0 : free(indices);
616 :
617 0 : return status;
618 : }
|