]> gerrit.simantics Code Review - simantics/fmil.git/blob - org.simantics.fmil.core/native/FMILibrary/src/Import/src/FMI1/fmi1_import_convenience.c
Switch to full JavaSE-11+ compatibility
[simantics/fmil.git] / org.simantics.fmil.core / native / FMILibrary / src / Import / src / FMI1 / fmi1_import_convenience.c
1 /*
2     Copyright (C) 2012 Modelon AB
3
4     This program is free software: you can redistribute it and/or modify
5     it under the terms of the BSD style license.
6
7      This program is distributed in the hope that it will be useful,
8     but WITHOUT ANY WARRANTY; without even the implied warranty of
9     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10     FMILIB_License.txt file for more details.
11
12     You should have received a copy of the FMILIB_License.txt file
13     along with this program. If not, contact Modelon AB <http://www.modelon.com>.
14 */
15
16 #include <string.h>
17 #include <stdio.h>
18 #include <ctype.h>
19
20 #include <FMI1/fmi1_xml_model_description.h>
21 #include <FMI1/fmi1_functions.h>
22
23 #include "fmi1_import_impl.h"
24
25 /**
26         \brief Collect model information by counting the number of variables with specific properties and fillinf in fmi1_import_model_counts_t struct.
27         \param fmu - An fmu object as returned by fmi1_import_parse_xml().
28         \param counts - a pointer to a preallocated struct.
29 */
30 void fmi1_import_collect_model_counts(fmi1_import_t* fmu, fmi1_import_model_counts_t* counts) {
31         jm_vector(jm_voidp)* vars = fmi1_xml_get_variables_original_order(fmu->md);
32     size_t nv, i;
33         memset(counts,0,sizeof(fmi1_import_model_counts_t));
34         if(!vars) return;
35     nv = jm_vector_get_size(jm_voidp)(vars);    
36     for(i = 0; i< nv; i++) {
37                 fmi1_xml_variable_t* var = (fmi1_xml_variable_t*)jm_vector_get_item(jm_voidp)(vars, i); 
38                 switch (fmi1_xml_get_variability(var)) {
39                 case fmi1_variability_enu_constant:
40                         counts->num_constants++;
41                         break;
42                 case fmi1_variability_enu_parameter:
43                         counts->num_parameters++;
44                         break;
45                 case fmi1_variability_enu_discrete:
46                         counts->num_discrete++;
47                         break;
48                 case fmi1_variability_enu_continuous:
49                         counts->num_continuous++;
50                         break;
51                 default:
52                         assert(0);
53                 }
54                 switch(fmi1_xml_get_causality(var)) {
55                 case fmi1_causality_enu_none:
56                         counts->num_causality_none++;
57                         break;
58                 case fmi1_causality_enu_input:
59                         counts->num_inputs++;
60                         break;
61                 case fmi1_causality_enu_output:
62                         counts->num_outputs++;
63                         break;
64                 case fmi1_causality_enu_internal:
65                         counts->num_internal++;
66                         break;
67                 default: assert(0);
68                 }
69                 switch(fmi1_xml_get_variable_base_type(var)) {
70                 case fmi1_base_type_real:
71                         counts->num_real_vars++;
72                         break;
73                 case fmi1_base_type_int:
74                         counts->num_integer_vars++;
75                         break;
76                 case fmi1_base_type_bool:
77                         counts->num_bool_vars++;
78                         break;
79                 case fmi1_base_type_str:
80                         counts->num_string_vars++;
81                         break;
82                 case fmi1_base_type_enum:
83                         counts->num_enum_vars++;
84                         break;
85                 default:
86                         assert(0);
87                 }
88     }
89     return;
90 }
91
92 void fmi1_import_expand_variable_references_impl(fmi1_import_t* fmu, const char* msgIn);
93
94 void fmi1_import_expand_variable_references(fmi1_import_t* fmu, const char* msgIn, char* msgOut, size_t maxMsgSize) {
95         fmi1_import_expand_variable_references_impl(fmu, msgIn);
96         strncpy(msgOut, jm_vector_get_itemp(char)(&fmu->logMessageBufferExpanded,0),maxMsgSize);
97         msgOut[maxMsgSize - 1] = '\0';
98 }
99
100 /* Print msgIn into msgOut by expanding variable references of the form #<Type><VR># into variable names
101   and replacing '##' with a single # */
102 void fmi1_import_expand_variable_references_impl(fmi1_import_t* fmu, const char* msgIn){
103         jm_vector(char)* msgOut = &fmu->logMessageBufferExpanded;
104         fmi1_xml_model_description_t* md = fmu->md;
105         jm_callbacks* callbacks = fmu->callbacks;
106     char curCh;
107         const char* firstRef;
108     size_t i; /* next char index after curCh in msgIn*/ 
109         size_t msgLen = strlen(msgIn)+1; /* original message length including terminating 0 */
110
111         if(jm_vector_reserve(char)(msgOut, msgLen + 100) < msgLen + 100) {
112                 jm_log(fmu->callbacks,"LOGGER", jm_log_level_warning, "Could not allocate memory for the log message");
113                 jm_vector_resize(char)(msgOut, 6);
114                 memcpy(jm_vector_get_itemp(char)(msgOut,0),"ERROR",6); /* at least 16 chars are always there */
115                 return;
116         }
117
118         /* check if there are any refs at all and copy the head of the string without references */
119         firstRef = strchr(msgIn, '#');
120         if(firstRef) {
121                 i = firstRef - msgIn;
122                 jm_vector_resize(char)(msgOut, i);
123                 if(i) {
124                         memcpy(jm_vector_get_itemp(char)(msgOut, 0), msgIn, i);
125                 }
126                 curCh = msgIn[i++];
127         }
128         else {
129                 jm_vector_resize(char)(msgOut, msgLen);
130                 memcpy(jm_vector_get_itemp(char)(msgOut, 0), msgIn, msgLen);
131                 return;
132         }
133     do {
134         if (curCh!='#') {
135             jm_vector_push_back(char)(msgOut, curCh); /* copy in to out */
136         }
137                 else if(msgIn[i] == '#') {
138                         jm_vector_push_back(char)(msgOut, '#');
139                         i++; /* skip the second # */
140                 }
141                 else {
142                         fmi1_value_reference_t vr = fmi1_undefined_value_reference;
143                         char typeChar = msgIn[i++];
144                         size_t pastePos = jm_vector_get_size(char)(msgOut);
145                         fmi1_base_type_enu_t baseType;
146                         size_t num_digits;
147                         fmi1_xml_variable_t* var;
148                         const char* name;
149                         size_t nameLen;
150                         switch(typeChar) {
151                                 case 'r': 
152                                         baseType = fmi1_base_type_real;
153                                         break;
154                                 case 'i': 
155                                         baseType = fmi1_base_type_int;
156                                         break;
157                                 case 'b': 
158                                         baseType = fmi1_base_type_bool;
159                                         break;
160                                 case 's': 
161                                         baseType = fmi1_base_type_str;
162                                         break;
163                                 default:
164                                         jm_vector_push_back(char)(msgOut, 0);
165                                         jm_log(callbacks,"LOGGER", jm_log_level_warning, 
166                                                 "Expected type specification character 'r', 'i', 'b' or 's' in log message here: '%s'", 
167                                         jm_vector_get_itemp(char)(msgOut,0));
168                     jm_vector_resize(char)(msgOut, msgLen);
169                                         memcpy(jm_vector_get_itemp(char)(msgOut,0),msgIn,msgLen);
170                                         return;
171                         }
172             curCh = msgIn[i++];
173                         while( isdigit(curCh) ) {
174                                 jm_vector_push_back(char)(msgOut, curCh);
175                     curCh = msgIn[i++];
176                         }
177                         num_digits = jm_vector_get_size(char)(msgOut) - pastePos;
178                         jm_vector_push_back(char)(msgOut, 0);
179                         if(num_digits == 0) {
180                                 jm_log(callbacks,"LOGGER", jm_log_level_warning, "Expected value reference in log message here: '%s'", jm_vector_get_itemp(char)(msgOut,0));
181                                 jm_vector_resize(char)(msgOut, msgLen);
182                 jm_vector_resize(char)(msgOut, msgLen);
183                                 memcpy(jm_vector_get_itemp(char)(msgOut,0),msgIn,msgLen);
184                                 return;
185                         }
186                         else if(curCh != '#') {
187                                 jm_log(callbacks,"LOGGER", jm_log_level_warning, "Expected terminating '#' in log message here: '%s'", jm_vector_get_itemp(char)(msgOut,0));
188                                 jm_vector_resize(char)(msgOut, msgLen);
189                 jm_vector_resize(char)(msgOut, msgLen);
190                                 memcpy(jm_vector_get_itemp(char)(msgOut,0),msgIn,msgLen);
191                                 return;
192                         }
193                         
194                         if(sscanf(jm_vector_get_itemp(char)(msgOut, pastePos), "%u",&vr) != 1) {
195                                 jm_log(callbacks,"LOGGER", jm_log_level_warning, "Could not decode value reference in log message here: '%s'", jm_vector_get_itemp(char)(msgOut,0));
196                                 jm_vector_resize(char)(msgOut, msgLen);
197                 jm_vector_resize(char)(msgOut, msgLen);
198                                 memcpy(jm_vector_get_itemp(char)(msgOut,0),msgIn,msgLen);
199                                 return;
200                         }
201                         var = fmi1_xml_get_variable_by_vr(md,baseType,vr);
202                         if(!var) {
203                                 jm_log(callbacks,"LOGGER", jm_log_level_warning, "Could not find variable referenced in log message here: '%s'", jm_vector_get_itemp(char)(msgOut,0));
204                                 jm_vector_resize(char)(msgOut, msgLen);
205                 jm_vector_resize(char)(msgOut, msgLen);
206                                 memcpy(jm_vector_get_itemp(char)(msgOut,0),msgIn,msgLen);
207                                 return;
208                         }
209                         name = fmi1_xml_get_variable_name(var);
210                         nameLen = strlen(name);
211                         if(jm_vector_resize(char)(msgOut, pastePos + nameLen) != pastePos + nameLen) {
212                                 jm_log(callbacks,"LOGGER", jm_log_level_warning, "Could not allocate memory for the log message");
213                                 jm_vector_resize(char)(msgOut, msgLen);
214                 jm_vector_resize(char)(msgOut, msgLen);
215                                 memcpy(jm_vector_get_itemp(char)(msgOut,0),msgIn,msgLen);
216                                 return;
217                         };
218                         memcpy(jm_vector_get_itemp(char)(msgOut, pastePos), name, nameLen);
219         }
220         curCh = msgIn[i++];
221     } while (curCh);
222     jm_vector_push_back(char)(msgOut, 0);
223 }
224
225 jm_callbacks fmi1_import_active_fmu_store_callbacks;
226
227 jm_vector(jm_voidp) fmi1_import_active_fmu_store;
228
229 jm_vector(jm_voidp)* fmi1_import_active_fmu = 0;
230
231 void  fmi1_log_forwarding(fmi1_component_t c, fmi1_string_t instanceName, fmi1_status_t status, fmi1_string_t category, fmi1_string_t message, ...) {
232     va_list args;
233     va_start (args, message);
234         fmi1_log_forwarding_v(c, instanceName, status, category, message, args);
235     va_end (args);
236 }
237
238 void  fmi1_log_forwarding_v(fmi1_component_t c, fmi1_string_t instanceName, fmi1_status_t status, fmi1_string_t category, fmi1_string_t message, va_list args) {
239 #define BUFSIZE JM_MAX_ERROR_MESSAGE_SIZE
240     char buffer[BUFSIZE], *buf, *curp, *msg;
241         const char* statusStr;
242         fmi1_import_t* fmu = 0;
243         jm_callbacks* cb = jm_get_default_callbacks();
244         jm_log_level_enu_t logLevel = jm_log_level_error;
245         if(fmi1_import_active_fmu) {
246                 size_t n = jm_vector_get_size(jm_voidp)(fmi1_import_active_fmu);
247                 size_t i;
248                 for(i= 0; i < n; i++) {
249                         fmu = (fmi1_import_t*)jm_vector_get_item(jm_voidp)(fmi1_import_active_fmu, i);
250                         if(fmu->capi->c == c) {
251                                 cb = fmu->callbacks;
252                                 break;
253                         }
254                 }
255                 if(i >= n) { /* Could not find matching FMU -> use default callbacks */
256                         fmu = 0;
257                         cb = jm_get_default_callbacks();
258                 }
259         }
260     if(fmu) {
261          buf = jm_vector_get_itemp(char)(&fmu->logMessageBufferCoded,0);
262         }
263         else  {
264          buf = buffer;
265     }
266         switch(status) {
267                 case fmi1_status_discard:
268                 case fmi1_status_pending:
269                 case fmi1_status_ok:
270                         logLevel = jm_log_level_info;
271                         break;
272                 case fmi1_status_warning:
273                         logLevel = jm_log_level_warning;
274                         break;
275                 case fmi1_status_error:
276                         logLevel = jm_log_level_error;
277                         break;
278                 case fmi1_status_fatal:
279                 default:
280                         logLevel = jm_log_level_fatal;
281         }
282
283         if(logLevel > cb->log_level) return;
284
285         curp = buf;
286     *curp = 0;
287
288         if(category) {
289         curp += jm_snprintf(curp, 100, "[%s]", category);
290     }
291         statusStr = fmi1_status_to_string(status);
292     curp += jm_snprintf(curp, 100,"[FMU status:%s] ", statusStr);        
293
294         if(fmu) {
295             int bufsize = jm_vector_get_size(char)(&fmu->logMessageBufferCoded);
296         int len;
297 #ifdef JM_VA_COPY
298         va_list argscp;
299         JM_VA_COPY(argscp, args);
300 #endif
301         len = jm_vsnprintf(curp, bufsize -(curp-buf), message, args);
302         if(len > (bufsize -(curp-buf+1))) {
303             int offset = (curp-buf);
304             len = jm_vector_resize(char)(&fmu->logMessageBufferCoded, len + offset + 1) - offset;
305             buf = jm_vector_get_itemp(char)(&fmu->logMessageBufferCoded,0);
306             curp = buf + offset;
307 #ifdef JM_VA_COPY
308             jm_vsnprintf(curp, len, message, argscp);
309 #endif
310         }
311 #ifdef JM_VA_COPY
312         va_end(argscp);
313 #endif
314             fmi1_import_expand_variable_references(fmu, buf, cb->errMessageBuffer,JM_MAX_ERROR_MESSAGE_SIZE);
315                 msg = jm_vector_get_itemp(char)(&fmu->logMessageBufferExpanded,0);
316         }
317         else {
318                 jm_vsnprintf(curp, BUFSIZE -(curp-buf), message, args);
319         strncpy(cb->errMessageBuffer, buf, JM_MAX_ERROR_MESSAGE_SIZE);
320                 cb->errMessageBuffer[JM_MAX_ERROR_MESSAGE_SIZE - 1] = '\0';
321                 msg = cb->errMessageBuffer;
322         }
323         if(cb->logger) {
324                 cb->logger(cb, instanceName, logLevel, msg);
325         }
326 }
327
328 void  fmi1_default_callback_logger(fmi1_component_t c, fmi1_string_t instanceName, fmi1_status_t status, fmi1_string_t category, fmi1_string_t message, ...) {
329     va_list args;
330
331     char buf[BUFSIZE], *curp;
332     va_start (args, message);
333     curp = buf;
334     *curp = 0;
335     if(instanceName) {
336         curp += jm_snprintf(curp, 200, "[%s]", instanceName);
337     }
338     if(category) {
339         curp += jm_snprintf(curp, 200, "[%s]", category);
340     }
341     fprintf(stdout, "%s[status=%s]", buf, fmi1_status_to_string(status));
342     vfprintf (stdout, message, args);
343     fprintf(stdout, "\n");
344     va_end (args);
345 }
346
347 void fmi1_logger(jm_callbacks* cb, jm_string module, jm_log_level_enu_t log_level, jm_string message) {
348         fmi1_callback_functions_t* c = (fmi1_callback_functions_t*)cb->context;
349         fmi1_status_t status;
350         if(!c ||!c->logger) return;
351
352         if(log_level > jm_log_level_all) {
353                 assert(0);
354                 status = fmi1_status_error;
355         }
356         else if(log_level >= jm_log_level_info)
357                 status = fmi1_status_ok;
358         else if(log_level >= jm_log_level_warning)
359                 status = fmi1_status_warning;
360         else if(log_level >= jm_log_level_error)
361                 status = fmi1_status_error;
362         else if(log_level >= jm_log_level_fatal)
363                 status = fmi1_status_fatal;
364         else {
365                 status = fmi1_status_ok;
366         }
367
368         c->logger( c, module, status, jm_log_level_to_string(log_level), message);
369 }
370
371 void fmi1_import_init_logger(jm_callbacks* cb, fmi1_callback_functions_t* fmiCallbacks) {
372         cb->logger = fmi1_logger;
373         cb->context = fmiCallbacks;
374 }