]> gerrit.simantics Code Review - simantics/fmil.git/blob - org.simantics.fmil.core/native/FMILibrary/src/Import/src/FMI2/fmi2_import_convenience.c
2ddec2a363456bd0ab33a4f8a4cea38488f83fd9
[simantics/fmil.git] / org.simantics.fmil.core / native / FMILibrary / src / Import / src / FMI2 / fmi2_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 <FMI2/fmi2_xml_model_description.h>
21 #include <FMI2/fmi2_functions.h>
22
23 #include "fmi2_import_impl.h"
24
25 /**
26         \brief Collect model information by counting the number of variables with specific properties and fillinf in fmi2_import_model_counts_t struct.
27         \param fmu - An fmu object as returned by fmi2_import_parse_xml().
28         \param counts - a pointer to a preallocated struct.
29 */
30 void fmi2_import_collect_model_counts(fmi2_import_t* fmu, fmi2_import_model_counts_t* counts) {
31         jm_vector(jm_voidp)* vars = fmi2_xml_get_variables_original_order(fmu->md);
32     size_t nv, i;
33         memset(counts,0,sizeof(fmi2_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                 fmi2_xml_variable_t* var = (fmi2_xml_variable_t*)jm_vector_get_item(jm_voidp)(vars, i); 
38                                 switch (fmi2_xml_get_variability(var)) {
39                 case fmi2_variability_enu_constant:
40                         counts->num_constants++;
41                         break;
42                 case fmi2_variability_enu_fixed:
43                         counts->num_fixed++;
44                         break;
45                 case fmi2_variability_enu_tunable:
46                         counts->num_tunable++;
47                         break;
48                 case fmi2_variability_enu_discrete:
49                         counts->num_discrete++;
50                         break;
51                 case fmi2_variability_enu_continuous:
52                         counts->num_continuous++;
53                         break;
54                 default:
55                         assert(0);
56                 }
57                 switch(fmi2_xml_get_causality(var)) {
58                 case fmi2_causality_enu_parameter:
59                         counts->num_parameters++;
60                         break;
61                 case fmi2_causality_enu_calculated_parameter:
62                         counts->num_calculated_parameters++;
63                         break;
64                 case fmi2_causality_enu_input:
65                         counts->num_inputs++;
66                         break;
67                 case fmi2_causality_enu_output:
68                         counts->num_outputs++;
69                         break;
70                 case fmi2_causality_enu_local:
71                         counts->num_local++;
72                         break;
73                 case fmi2_causality_enu_independent:
74                         counts->num_local++;
75                         break;
76                 default: assert(0);
77                 }
78                 switch(fmi2_xml_get_variable_base_type(var)) {
79                 case fmi2_base_type_real:
80                         counts->num_real_vars++;
81                         break;
82                 case fmi2_base_type_int:
83                         counts->num_integer_vars++;
84                         break;
85                 case fmi2_base_type_bool:
86                         counts->num_bool_vars++;
87                         break;
88                 case fmi2_base_type_str:
89                         counts->num_string_vars++;
90                         break;
91                 case fmi2_base_type_enum:
92                         counts->num_enum_vars++;
93                         break;
94                 default:
95                         assert(0);
96                 }
97     }
98     return;
99 }
100
101 void fmi2_import_expand_variable_references_impl(fmi2_import_t* fmu, const char* msgIn);
102
103 void fmi2_import_expand_variable_references(fmi2_import_t* fmu, const char* msgIn, char* msgOut, size_t maxMsgSize) {
104         fmi2_import_expand_variable_references_impl(fmu, msgIn);
105         strncpy(msgOut, jm_vector_get_itemp(char)(&fmu->logMessageBufferExpanded,0),maxMsgSize);
106         msgOut[maxMsgSize - 1] = '\0';
107 }
108
109 /* Print msgIn into msgOut by expanding variable references of the form #<Type><VR># into variable names
110   and replacing '##' with a single # */
111 void fmi2_import_expand_variable_references_impl(fmi2_import_t* fmu, const char* msgIn){
112         jm_vector(char)* msgOut = &fmu->logMessageBufferExpanded;
113         fmi2_xml_model_description_t* md = fmu->md;
114         jm_callbacks* callbacks = fmu->callbacks;
115     char curCh;
116         const char* firstRef;
117     size_t i; /* next char index after curCh in msgIn*/ 
118         size_t msgLen = strlen(msgIn)+1; /* original message length including terminating 0 */
119
120         if(jm_vector_reserve(char)(msgOut, msgLen + 100) < msgLen + 100) {
121                 jm_log(fmu->callbacks,"LOGGER", jm_log_level_warning, "Could not allocate memory for the log message");
122                 jm_vector_resize(char)(msgOut, 6);
123                 memcpy(jm_vector_get_itemp(char)(msgOut,0),"ERROR",6); /* at least 16 chars are always there */
124                 return;
125         }
126
127         /* check if there are any refs at all and copy the head of the string without references */
128         firstRef = strchr(msgIn, '#');
129         if(firstRef) {
130                 i = firstRef - msgIn;
131                 jm_vector_resize(char)(msgOut, i);
132                 if(i) {
133                         memcpy(jm_vector_get_itemp(char)(msgOut, 0), msgIn, i);
134                 }
135                 curCh = msgIn[i++];
136         }
137         else {
138                 jm_vector_resize(char)(msgOut, msgLen);
139                 memcpy(jm_vector_get_itemp(char)(msgOut, 0), msgIn, msgLen);
140                 return;
141         }
142     do {
143         if (curCh!='#') {
144             jm_vector_push_back(char)(msgOut, curCh); /* copy in to out */
145         }
146                 else if(msgIn[i] == '#') {
147                         jm_vector_push_back(char)(msgOut, '#');
148                         i++; /* skip the second # */
149                 }
150                 else {
151             unsigned int bufVR;
152                         fmi2_value_reference_t vr;
153                         char typeChar = msgIn[i++];
154                         size_t pastePos = jm_vector_get_size(char)(msgOut);
155                         fmi2_base_type_enu_t baseType;
156                         size_t num_digits;
157                         fmi2_xml_variable_t* var;
158                         const char* name;
159                         size_t nameLen;
160                         switch(typeChar) {
161                                 case 'r': 
162                                         baseType = fmi2_base_type_real;
163                                         break;
164                                 case 'i': 
165                                         baseType = fmi2_base_type_int;
166                                         break;
167                                 case 'b': 
168                                         baseType = fmi2_base_type_bool;
169                                         break;
170                                 case 's': 
171                                         baseType = fmi2_base_type_str;
172                                         break;
173                                 default:
174                                         jm_vector_push_back(char)(msgOut, 0);
175                                         jm_log(callbacks,"LOGGER", jm_log_level_warning, 
176                                                 "Expected type specification character 'r', 'i', 'b' or 's' in log message here: '%s'", 
177                                         jm_vector_get_itemp(char)(msgOut,0));
178                     jm_vector_resize(char)(msgOut, msgLen);
179                                         memcpy(jm_vector_get_itemp(char)(msgOut,0),msgIn,msgLen);
180                                         return;
181                         }
182             curCh = msgIn[i++];
183                         while( isdigit(curCh) ) {
184                                 jm_vector_push_back(char)(msgOut, curCh);
185                     curCh = msgIn[i++];
186                         }
187                         num_digits = jm_vector_get_size(char)(msgOut) - pastePos;
188                         jm_vector_push_back(char)(msgOut, 0);
189                         if(num_digits == 0) {
190                                 jm_log(callbacks,"LOGGER", jm_log_level_warning, "Expected value reference in log message here: '%s'", jm_vector_get_itemp(char)(msgOut,0));
191                                 jm_vector_resize(char)(msgOut, msgLen);
192                 jm_vector_resize(char)(msgOut, msgLen);
193                                 memcpy(jm_vector_get_itemp(char)(msgOut,0),msgIn,msgLen);
194                                 return;
195                         }
196                         else if(curCh != '#') {
197                                 jm_log(callbacks,"LOGGER", jm_log_level_warning, "Expected terminating '#' in log message here: '%s'", jm_vector_get_itemp(char)(msgOut,0));
198                                 jm_vector_resize(char)(msgOut, msgLen);
199                 jm_vector_resize(char)(msgOut, msgLen);
200                                 memcpy(jm_vector_get_itemp(char)(msgOut,0),msgIn,msgLen);
201                                 return;
202                         }
203                         
204                         if(sscanf(jm_vector_get_itemp(char)(msgOut, pastePos), "%u",&bufVR) != 1) {
205                                 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));
206                                 jm_vector_resize(char)(msgOut, msgLen);
207                 jm_vector_resize(char)(msgOut, msgLen);
208                                 memcpy(jm_vector_get_itemp(char)(msgOut,0),msgIn,msgLen);
209                                 return;
210                         }
211             vr = bufVR;
212                         var = fmi2_xml_get_variable_by_vr(md,baseType,vr);
213                         if(!var) {
214                                 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));
215                                 jm_vector_resize(char)(msgOut, msgLen);
216                 jm_vector_resize(char)(msgOut, msgLen);
217                                 memcpy(jm_vector_get_itemp(char)(msgOut,0),msgIn,msgLen);
218                                 return;
219                         }
220                         name = fmi2_xml_get_variable_name(var);
221                         nameLen = strlen(name);
222                         if(jm_vector_resize(char)(msgOut, pastePos + nameLen) != pastePos + nameLen) {
223                                 jm_log(callbacks,"LOGGER", jm_log_level_warning, "Could not allocate memory for the log message");
224                                 jm_vector_resize(char)(msgOut, msgLen);
225                 jm_vector_resize(char)(msgOut, msgLen);
226                                 memcpy(jm_vector_get_itemp(char)(msgOut,0),msgIn,msgLen);
227                                 return;
228                         };
229                         memcpy(jm_vector_get_itemp(char)(msgOut, pastePos), name, nameLen);
230         }
231         curCh = msgIn[i++];
232     } while (curCh);
233     jm_vector_push_back(char)(msgOut, 0);
234 }
235
236 void  fmi2_log_forwarding(fmi2_component_environment_t c, fmi2_string_t instanceName, fmi2_status_t status, fmi2_string_t category, fmi2_string_t message, ...) {
237     va_list args;
238     va_start (args, message);
239         fmi2_log_forwarding_v(c, instanceName, status, category, message, args);
240     va_end (args);
241 }
242
243 void  fmi2_log_forwarding_v(fmi2_component_environment_t c, fmi2_string_t instanceName, fmi2_status_t status, fmi2_string_t category, fmi2_string_t message, va_list args) {
244 #define BUFSIZE JM_MAX_ERROR_MESSAGE_SIZE
245     char buffer[BUFSIZE], *buf, *curp, *msg;
246         const char* statusStr;
247         fmi2_import_t* fmu = (fmi2_import_t*)c;
248         jm_callbacks* cb;
249         jm_log_level_enu_t logLevel;
250
251         if(fmu) {
252                  cb = fmu->callbacks;
253          buf = jm_vector_get_itemp(char)(&fmu->logMessageBufferCoded,0);
254         }
255         else  {
256                 cb = jm_get_default_callbacks();
257         buf = buffer;
258     }
259         logLevel = cb->log_level;
260         switch(status) {
261                 case fmi2_status_discard:
262                 case fmi2_status_pending:
263                 case fmi2_status_ok:
264                         logLevel = jm_log_level_info;
265                         break;
266                 case fmi2_status_warning:
267                         logLevel = jm_log_level_warning;
268                         break;
269                 case fmi2_status_error:
270                         logLevel = jm_log_level_error;
271                         break;
272                 case fmi2_status_fatal:
273                 default:
274                         logLevel = jm_log_level_fatal;
275         }
276
277     if(logLevel > cb->log_level) return;
278
279         curp = buf;
280     *curp = 0;
281
282         if(category) {
283         curp += jm_snprintf(curp, 100, "[%s]", category);        
284     }
285         statusStr = fmi2_status_to_string(status);
286     curp += jm_snprintf(curp, 200, "[FMU status:%s] ", statusStr);              
287
288         if(fmu) {
289             int bufsize = jm_vector_get_size(char)(&fmu->logMessageBufferCoded);
290         int len;
291 #ifdef JM_VA_COPY
292         va_list argscp;
293         JM_VA_COPY(argscp, args);
294 #endif
295         len = jm_vsnprintf(curp, bufsize -(curp-buf), message, args);
296         if(len > (bufsize -(curp-buf+1))) {
297             int offset = (curp-buf);
298             len = jm_vector_resize(char)(&fmu->logMessageBufferCoded, len + offset + 1) - offset;
299             buf = jm_vector_get_itemp(char)(&fmu->logMessageBufferCoded,0);
300             curp = buf + offset;
301 #ifdef JM_VA_COPY
302             jm_vsnprintf(curp, len, message, argscp);
303 #endif
304         }
305 #ifdef JM_VA_COPY
306         va_end(argscp);
307 #endif
308                 fmi2_import_expand_variable_references(fmu, buf, cb->errMessageBuffer,JM_MAX_ERROR_MESSAGE_SIZE);
309                 msg = jm_vector_get_itemp(char)(&fmu->logMessageBufferExpanded,0);
310         }
311         else {
312         jm_vsnprintf(curp, BUFSIZE -(curp-buf), message, args);
313                 strncpy(cb->errMessageBuffer, buf, JM_MAX_ERROR_MESSAGE_SIZE);
314                 cb->errMessageBuffer[JM_MAX_ERROR_MESSAGE_SIZE - 1] = '\0';
315                 msg = cb->errMessageBuffer;
316         }
317         if(cb->logger) {
318                 cb->logger(cb, instanceName, logLevel, msg);
319         }
320
321 }
322
323 void  fmi2_default_callback_logger(fmi2_component_environment_t c, fmi2_string_t instanceName, fmi2_status_t status, fmi2_string_t category, fmi2_string_t message, ...) {
324     va_list args;
325     char buf[BUFSIZE], *curp;
326     va_start (args, message);
327     curp = buf;
328     *curp = 0;
329     if(instanceName) {
330         curp += jm_snprintf(curp, 200, "[%s]", instanceName);        
331     }
332     if(category) {
333         curp += jm_snprintf(curp, 200, "[%s]", category);
334     }
335     fprintf(stdout, "%s[status=%s]", buf, fmi2_status_to_string(status));
336     vfprintf (stdout, message, args);
337     fprintf(stdout, "\n");
338     va_end (args);
339 }
340
341 void fmi2_logger(jm_callbacks* cb, jm_string module, jm_log_level_enu_t log_level, jm_string message) {
342         fmi2_callback_functions_t* c = (fmi2_callback_functions_t*)cb->context;
343         fmi2_status_t status;
344         if(!c ||!c->logger) return;
345
346         if(log_level > jm_log_level_all) {
347                 assert(0);
348                 status = fmi2_status_error;
349         }
350         else if(log_level >= jm_log_level_info)
351                 status = fmi2_status_ok;
352         else if(log_level >= jm_log_level_warning)
353                 status = fmi2_status_warning;
354         else if(log_level >= jm_log_level_error)
355                 status = fmi2_status_error;
356         else if(log_level >= jm_log_level_fatal)
357                 status = fmi2_status_fatal;
358         else {
359                 status = fmi2_status_ok;
360         }
361
362         c->logger( c, module, status, jm_log_level_to_string(log_level), message);
363 }
364
365 void fmi2_import_init_logger(jm_callbacks* cb, fmi2_callback_functions_t* fmiCallbacks) {
366         cb->logger = fmi2_logger;
367         cb->context = fmiCallbacks;
368 }