Linux GNU 11.4.0 Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 61.5% 193 / 0 / 314
Functions: 70.0% 14 / 1 / 21
Branches: 54.5% 104 / 0 / 191

OMCompiler/SimulationRuntime/c/simulation/simulation_info_json.c
Line Branch Exec Source
1 /*
2 * This file belongs to the OpenModelica Run-Time System
3 *
4 * Copyright (c) 1998-2026, Open Source Modelica Consortium (OSMC), c/o Linköpings
5 * universitet, Department of Computer and Information Science, SE-58183 Linköping, Sweden. All rights
6 * reserved.
7 *
8 * THIS PROGRAM IS PROVIDED UNDER THE TERMS OF THE BSD NEW LICENSE OR THE
9 * AGPL VERSION 3 LICENSE OR THE OSMC PUBLIC LICENSE (OSMC-PL) VERSION 1.8. ANY
10 * USE, REPRODUCTION OR DISTRIBUTION OF THIS PROGRAM CONSTITUTES RECIPIENT'S
11 * ACCEPTANCE OF THE BSD NEW LICENSE OR THE OSMC PUBLIC LICENSE OR THE AGPL
12 * VERSION 3, ACCORDING TO RECIPIENTS CHOICE.
13 *
14 * The OpenModelica software and the OSMC (Open Source Modelica Consortium) Public License
15 * (OSMC-PL) are obtained from OSMC, either from the above address, from the URLs:
16 * http://www.openmodelica.org or https://github.com/OpenModelica/ or
17 * http://www.ida.liu.se/projects/OpenModelica, and in the OpenModelica distribution. GNU
18 * AGPL version 3 is obtained from: https://www.gnu.org/licenses/licenses.html#GPL. The BSD NEW
19 * License is obtained from: http://www.opensource.org/licenses/BSD-3-Clause.
20 *
21 * This program is distributed WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, EXCEPT AS EXPRESSLY
23 * SET FORTH IN THE BY RECIPIENT SELECTED SUBSIDIARY LICENSE CONDITIONS OF
24 * OSMC-PL.
25 *
26 */
27
28 #include "simulation_info_json.h"
29 #include "options.h"
30 #include <string.h>
31 #include <stdio.h>
32 #include "../util/omc_mmap.h"
33 #include "../util/omc_numbers.h"
34 #include "solver/model_help.h"
35 #include "../util/omc_file.h"
36
37 /**
38 * @brief Skip whitespace.
39 *
40 * @param str Points to some locating inside JSON.
41 * @return const char* Points to next non-whitespace character.
42 */
43 static inline const char* skipSpace(const char* str)
44 {
45 do {
46
24/40
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 8 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 8 times.
✓ Branch 8 taken 6 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 8 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 8 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 6 times.
✗ Branch 16 not taken.
✓ Branch 17 taken 8 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 8 times.
✗ Branch 20 not taken.
✓ Branch 21 taken 8 times.
✗ Branch 22 not taken.
✓ Branch 23 taken 8 times.
✓ Branch 24 taken 10 times.
✓ Branch 25 taken 64 times.
✓ Branch 26 taken 4 times.
✓ Branch 27 taken 15 times.
✓ Branch 28 taken 3 times.
✓ Branch 29 taken 209 times.
✗ Branch 30 not taken.
✓ Branch 31 taken 7 times.
✓ Branch 32 taken 7 times.
✗ Branch 33 not taken.
✓ Branch 34 taken 1 time.
✓ Branch 35 taken 30 times.
✗ Branch 36 not taken.
✓ Branch 37 taken 92 times.
✓ Branch 38 taken 92 times.
✓ Branch 39 taken 1 time.
611 switch (*str) {
47 case '\0': return str;
48 19 case ' ':
49 case '\n':
50 case '\r':
51 19 str++;
52 break;
53 default: return str;
54 }
55 } while (1);
56 }
57
58 static const char* skipValue(const char* str, const char* fileName);
59
60 /**
61 * @brief Skip rest of JSON object.
62 *
63 * Move forward until next '}' is reached.
64 *
65 * @param str Points to some locating inside JSON object.
66 * @param first 1 if first location in JSON object, 0 otherwise.
67 * @param fileName Name of JSON to parse. Used for error messages.
68 * @return const char* Points to location directly after JSON object.
69 */
70 30 static inline const char* skipObjectRest(const char* str, int first, const char* fileName)
71 {
72 str=skipSpace(str);
73
2/2
✓ Branch 0 taken 92 times.
✓ Branch 1 taken 30 times.
122 while (*str != '}') {
74
2/2
✓ Branch 0 taken 70 times.
✓ Branch 1 taken 22 times.
92 if (!first) {
75
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 70 times.
70 if (*str != ',') {
76 ✗ errorStreamPrint(OMC_LOG_STDOUT, 1, "Failed to parse %s", fileName);
77 ✗ errorStreamPrint(OMC_LOG_STDOUT, 0, "JSON object expected ',' or '}', got: %.20s\n", str);
78 ✗ messageClose(OMC_LOG_STDOUT);
79 ✗ omc_throw_function(NULL);
80 }
81 70 str++;
82 } else {
83 first = 0;
84 }
85 92 str = skipValue(str, fileName);
86 str = skipSpace(str);
87
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 92 times.
92 if (*str++ != ':') {
88 ✗ errorStreamPrint(OMC_LOG_STDOUT, 1, "Failed to parse %s", fileName);
89 ✗ errorStreamPrint(OMC_LOG_STDOUT, 0, "JSON object expected ':', got: %.20s\n", str);
90 ✗ messageClose(OMC_LOG_STDOUT);
91 ✗ omc_throw_function(NULL);
92 }
93 92 str = skipValue(str, fileName);
94 str = skipSpace(str);
95 }
96 30 return str+1;
97 }
98
99 /**
100 * @brief Skip JSON value.
101 *
102 * @param str Points to beginning of JSON value.
103 * @param fileName Name of JSON to parse. Used for error messages.
104 * @return const char* Points to location directly after JSON value.
105 */
106 209 static const char* skipValue(const char* str, const char* fileName)
107 {
108 str = skipSpace(str);
109
4/5
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 141 times.
✓ Branch 3 taken 39 times.
✗ Branch 4 not taken.
209 switch (*str) {
110 22 case '{':
111 {
112 22 str = skipObjectRest(str+1, 1, fileName);
113 22 return str;
114 }
115 7 case '[':
116 {
117 int first = 1;
118 7 str = skipSpace(str+1);
119
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 7 times.
14 while (*str != ']') {
120
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
7 if (!first && *str++ != ',') {
121 ✗ errorStreamPrint(OMC_LOG_STDOUT, 1, "Failed to parse %s", fileName);
122 ✗ errorStreamPrint(OMC_LOG_STDOUT, 0, "JSON array expected ',' or ']', got: %.20s\n", str);
123 ✗ messageClose(OMC_LOG_STDOUT);
124 ✗ omc_throw_function(NULL);
125 }
126 first = 0;
127 7 str = skipValue(str, fileName);
128 str = skipSpace(str);
129 }
130 7 return str+1;
131 }
132 141 case '"':
133 141 str++;
134 do {
135
2/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 141 times.
✓ Branch 3 taken 792 times.
933 switch (*str) {
136 ✗ case '\0':
137 ✗ errorStreamPrint(OMC_LOG_STDOUT, 1, "Failed to parse %s", fileName);
138 ✗ errorStreamPrint(OMC_LOG_STDOUT, 0, "Found end of file, expected end of string");
139 ✗ messageClose(OMC_LOG_STDOUT);
140 ✗ omc_throw_function(NULL);
141 ✗ case '\\':
142 ✗ if (*(str+1) == '\0') {
143 ✗ errorStreamPrint(OMC_LOG_STDOUT, 1, "Failed to parse %s", fileName);
144 ✗ errorStreamPrint(OMC_LOG_STDOUT, 0, "Found end of file, expected end of string");
145 ✗ messageClose(OMC_LOG_STDOUT);
146 ✗ omc_throw_function(NULL);
147 }
148 ✗ str+=2;
149 ✗ break;
150 141 case '"':
151 141 return str+1;
152 792 default:
153 792 str++;
154 }
155 } while (1);
156 errorStreamPrint(OMC_LOG_STDOUT, 1, "Failed to parse %s", fileName);
157 errorStreamPrint(OMC_LOG_STDOUT, 0, "Reached state that should be impossible to reach.");
158 messageClose(OMC_LOG_STDOUT);
159 omc_throw_function(NULL);
160 39 case '-':
161 case '0':
162 case '1':
163 case '2':
164 case '3':
165 case '4':
166 case '5':
167 case '6':
168 case '7':
169 case '8':
170 case '9':
171 {
172 39 char *endptr = NULL;
173 39 om_strtod(str,&endptr);
174
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
39 if (str == endptr) {
175 ✗ errorStreamPrint(OMC_LOG_STDOUT, 1, "Failed to parse %s", fileName);
176 ✗ errorStreamPrint(OMC_LOG_STDOUT, 0, "Not a number, got %.20s\n", str);
177 ✗ messageClose(OMC_LOG_STDOUT);
178 ✗ omc_throw_function(NULL);
179 }
180 return endptr;
181 }
182 ✗ default:
183 ✗ errorStreamPrint(OMC_LOG_STDOUT, 1, "Failed to parse %s", fileName);
184 ✗ errorStreamPrint(OMC_LOG_STDOUT, 0, "JSON value expected, got: %.20s\n", str);
185 ✗ messageClose(OMC_LOG_STDOUT);
186 ✗ omc_throw_function(NULL);
187 }
188 }
189
190 /**
191 * @brief Assert str points to given string.
192 *
193 * Does not work for escaped strings. Returns the rest of the string to parse.
194 *
195 * @param str Points to beginning of string to assert.
196 * @param value Expected value of string.
197 * @param fileName Name of JSON to parse. Used for error messages.
198 * @return const char* Points to location directly after string.
199 */
200 15 static inline const char* assertStringValue(const char *str, const char *value, const char* fileName)
201 {
202 15 int len = strlen(value);
203 str = skipSpace(str);
204
3/6
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 15 times.
15 if ('\"' != *str || strncmp(str+1,value,len) || str[len+1] != '\"') {
205 ✗ errorStreamPrint(OMC_LOG_STDOUT, 1, "Failed to parse %s", fileName);
206 ✗ errorStreamPrint(OMC_LOG_STDOUT, 0, "JSON string value %s expected, got: %.20s\n", value, str);
207 ✗ messageClose(OMC_LOG_STDOUT);
208 ✗ omc_throw_function(NULL);
209 }
210 15 return str + len + 2;
211 }
212
213 /**
214 * @brief Assert str points to specific character.
215 *
216 * @param str Pointer to character to assert.
217 * @param c Character str should be equal to.
218 * @param fileName Name of JSON to parse. Used for error messages.
219 * @return const char* Point to next location after character.
220 */
221 64 static inline const char* assertChar(const char *str, char c, const char *fileName)
222 {
223 str = skipSpace(str);
224
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 64 times.
64 if (c != *str) {
225 ✗ errorStreamPrint(OMC_LOG_STDOUT, 1, "Failed to parse %s", fileName);
226 ✗ errorStreamPrint(OMC_LOG_STDOUT, 0,"Expected '%c', got: %.20s\n", c, str);
227 ✗ messageClose(OMC_LOG_STDOUT);
228 ✗ omc_throw_function(NULL);
229 }
230 64 return str + 1;
231 }
232
233 /**
234 * @brief Assert str point to specific number.
235 *
236 * @param str Pointer to number to assert.
237 * @param expected Expected number.
238 * @param fileName Name of JSON to parse. Used for error messages.
239 * @return const char* Point to next location after number.
240 */
241 8 static inline const char* assertNumber(const char *str, double expected, const char *fileName)
242 {
243 8 char *endptr = NULL;
244 double d;
245 str = skipSpace(str);
246 8 d = om_strtod(str, &endptr);
247
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (str == endptr) {
248 ✗ errorStreamPrint(OMC_LOG_STDOUT, 1, "Failed to parse %s", fileName);
249 ✗ errorStreamPrint(OMC_LOG_STDOUT, 0, "Expected number, got: %.20s\n", str);
250 ✗ messageClose(OMC_LOG_STDOUT);
251 ✗ omc_throw_function(NULL);
252 }
253
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (d != expected) {
254 ✗ errorStreamPrint(OMC_LOG_STDOUT, 1, "Failed to parse %s", fileName);
255 ✗ errorStreamPrint(OMC_LOG_STDOUT, 0, "Got number %f, expected: %f\n", d, expected);
256 ✗ messageClose(OMC_LOG_STDOUT);
257 ✗ omc_throw_function(NULL);
258 }
259 8 return endptr;
260 }
261
262 /**
263 * @brief Skip name if it exists.
264 *
265 * Returns back `str` if name does not exist.
266 *
267 * @param str Pointer to name to skip.
268 * @param name Name to skip.
269 * @param fileName Name of JSON to parse. Used for error messages.
270 * @return const char* Point to next location after name.
271 */
272 48 static inline const char *skipNameIfExist(const char *str, const char *name, const char* fileName)
273 {
274 const char *s = str;
275 size_t len;
276
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 4 times.
48 if (*s != ',') return str;
277 ++s;
278 44 len = strlen(name);
279
3/4
✓ Branch 0 taken 44 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
✓ Branch 3 taken 28 times.
44 if (*s != '\"' || strncmp(s+1,name,len)) return str;
280 16 s += len + 1;
281
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 if (strncmp("\":", s, 2)) return str;
282 16 s += 2;
283 16 return s;
284 }
285
286 /**
287 * @brief Skip JSON object if it exists.
288 *
289 * @param str Pointer to object/field to skip.
290 * @param name Name of object to skip.
291 * @param fileName Name of JSON to parse. Used for error messages.
292 * @return const char* Point to next location after object.
293 */
294 32 static inline const char *skipFieldIfExist(const char *str, const char *name, const char* fileName)
295 {
296 const char *s = str;
297 32 s = skipNameIfExist(str, name, fileName);
298
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 24 times.
32 if (s == str) return str;
299 s = skipSpace(s);
300 8 s = skipValue(s, fileName);
301 s = skipSpace(s);
302 s = skipSpace(s);
303 return s;
304 }
305
306 /**
307 * @brief Read field with string array as value if it exists.
308 *
309 * @param str Points to beginning of equation object.
310 * @param name Name of the field.
311 * @param numValues Pointer to number of values in array.
312 * @param values Pointer where to write the values.
313 * @param fileName Name of JSON to parse. Used for error messages.
314 * @return const char* Point to next location after character.
315 */
316 16 static const char* readFieldStringArrayIfExist(const char *str, const char *name, int *numValues, const char ***values, const char* fileName)
317 {
318 16 *numValues = 0;
319 16 *values = NULL;
320
321 16 const char *s = skipNameIfExist(str, name, fileName);
322
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 8 times.
16 if (s == str) return str;
323
324 str = skipSpace(s);
325
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (*str != '[') return skipValue(str, fileName);
326 8 ++str;
327
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 6 times.
8 if (*str == ']') return str+1;
328
329 const char *str2 = skipSpace(str);
330 int n = 0;
331 while (1) {
332 8 str = skipValue(str, fileName);
333 8 ++n;
334 str = skipSpace(str);
335
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 6 times.
8 if (*str != ',') break;
336 2 ++str;
337 };
338 6 assertChar(str, ']', fileName);
339 6 *numValues = n;
340 6 *values = malloc(sizeof(const char*)*n);
341 str = str2;
342
343
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 6 times.
14 for (int j = 0; j<n; ++j) {
344 const char *str3 = skipSpace(str);
345 char *tmp;
346 int len = 0;
347 8 str = assertChar(str, '\"', fileName);
348
2/2
✓ Branch 0 taken 45 times.
✓ Branch 1 taken 8 times.
53 while (*str != '\"' && *str) {
349 45 ++len;
350 45 ++str;
351 }
352 8 str = assertChar(str, '\"', fileName);
353 8 tmp = malloc(len+1);
354
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 6 times.
8 strncpy(tmp, str3+1, len);
355 8 tmp[len] = '\0';
356 8 (*values)[j] = tmp;
357
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 6 times.
8 if (j != n-1) {
358 2 str = assertChar(str, ',', fileName);
359 }
360 }
361 6 str = assertChar(skipSpace(str), ']', fileName);
362 6 return str;
363 }
364
365 /**
366 * @brief Parse single equation info from JSON.
367 *
368 * @param str Points to beginning of equation object.
369 * @param xml Equation info to fill
370 * @param i Index of equation inside "equations" array.
371 * @param fileName Name of JSON to parse. Used for error messages.
372 * @return const char* Point to next location after character.
373 */
374 8 static const char* readEquation(const char *str, EQUATION_INFO *xml, int i, const char* fileName)
375 {
376 8 str = assertChar(str, '{', fileName);
377 8 str = assertStringValue(str, "eqIndex", fileName);
378 8 str = assertChar(str, ':', fileName);
379 8 str = assertNumber(str, i, fileName);
380 str = skipSpace(str);
381 8 xml->id = i;
382 8 str = skipFieldIfExist(str, "parent", fileName);
383
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (0 == strncmp(",\"section\":\"initial-lambda0\"", str, 28)) {
384 ✗ xml->section = EQUATION_SECTION_INIT_LAMBDA0;
385 ✗ str += 28;
386
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
8 } else if (0 == strncmp(",\"section\":\"initial\"", str, 20)) {
387 4 xml->section = EQUATION_SECTION_INITIAL;
388 4 str += 20;
389
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 } else if (0 == strncmp(",\"section\":\"regular\"", str, 20)) {
390 ✗ xml->section = EQUATION_SECTION_REGULAR;
391 ✗ str += 20;
392 } else {
393 4 xml->section = EQUATION_SECTION_UNKNOWN;
394 }
395 str=skipSpace(str);
396
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
8 if ((measure_time_flag & 1) && 0==strncmp(",\"tag\":\"system\"", str, 15)) {
397 ✗ xml->profileBlockIndex = -1;
398 ✗ str += 15;
399
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
8 } else if ((measure_time_flag & 1) && 0==strncmp(",\"tag\":\"tornsystem\"", str, 19)) {
400 ✗ xml->profileBlockIndex = -1;
401 ✗ str += 19;
402 } else {
403 8 xml->profileBlockIndex = 0;
404 }
405 8 str = skipFieldIfExist(str, "tag", fileName);
406 8 str = skipFieldIfExist(str, "display", fileName);
407 8 str = skipFieldIfExist(str, "unknowns", fileName);
408 8 str = readFieldStringArrayIfExist(str, "defines", &xml->numVar, &xml->vars, fileName);
409 8 str = readFieldStringArrayIfExist(str, "uses", &xml->numVarUsed, &xml->varsUsed, fileName);
410 8 return skipObjectRest(str,0, fileName);
411 }
412
413 /**
414 * @brief Parse equations from info.json.
415 *
416 * @param str Point to beginning of equation array at '['.
417 * @param xml Model data from xml
418 * @return const char* Point to end of equation array directly after ']'.
419 */
420 1 static const char* readEquations(const char *str, MODEL_DATA_XML *xml)
421 {
422 int i;
423 1 xml->nProfileBlocks = measure_time_flag & 2 ? 1 : 0;
424 1 str=assertChar(str,'[', xml->fileName);
425 1 str = readEquation(str, xml->equationInfo, 0, xml->fileName);
426
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 1 time.
8 for (i=1; i<xml->nEquations; i++) {
427
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 if (*str != ',') {
428 ✗ errorStreamPrint(OMC_LOG_STDOUT, 1, "Failed to parse %s", xml->fileName);
429 ✗ errorStreamPrint(OMC_LOG_STDOUT, 0, "Expected %ld equations, but only found %i equations.", xml->nEquations, i-1);
430 ✗ messageClose(OMC_LOG_STDOUT);
431 ✗ omc_throw_function(NULL);
432 } else {
433 7 str = str + 1;
434 }
435 7 str = readEquation(str, xml->equationInfo+i, i, xml->fileName);
436 /* TODO: Odd, it seems there is 1 fewer equation than expected... */
437 /*
438 if (i != xml->nEquations-1) {
439 str=assertChar(str,',');
440 }
441 */
442
3/6
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 7 times.
7 if (measure_time_flag & 2 || ((measure_time_flag & 1) && xml->equationInfo[i].profileBlockIndex == -1)) {
443 ✗ xml->equationInfo[i].profileBlockIndex = xml->nProfileBlocks++;
444 }
445 }
446 1 str=assertChar(str,']', xml->fileName);
447 1 return str;
448 }
449
450 ✗ static const char* readFunction(const char *str, FUNCTION_INFO *xml, int i, const char* fileName)
451 {
452 size_t len;
453 char *name;
454 const char *str2;
455 str=skipSpace(str);
456 ✗ str2=assertChar(str,'"', fileName);
457 ✗ str=skipValue(str, fileName);
458 ✗ xml->id = i;
459 ✗ len = str-str2;
460 ✗ name = malloc(len);
461 ✗ memcpy(name, str2, len-1);
462 ✗ name[len-1] = '\0';
463 ✗ xml->name = name;
464 ✗ xml->info = omc_dummyFileInfo;
465 ✗ return str;
466 }
467
468 1 static const char* readFunctions(const char *str, MODEL_DATA_XML *xml)
469 {
470 int i;
471
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 if (xml->nFunctions == 0) {
472 1 str=assertChar(str,'[', xml->fileName);
473 1 str=assertChar(str,']', xml->fileName);
474 1 return str;
475 }
476 ✗ str=assertChar(str,'[', xml->fileName);
477 ✗ for (i=0; i<xml->nFunctions; i++) {
478 ✗ str = readFunction(str, xml->functionNames+i, i, xml->fileName);
479 ✗ str=assertChar(str,xml->nFunctions==i+1 ? ']' : ',', xml->fileName);
480 }
481 return str;
482 }
483
484 1 static void readInfoJson(const char *str, MODEL_DATA_XML *xml)
485 {
486 1 str=assertChar(str,'{', xml->fileName);
487 1 str=assertStringValue(str,"format", xml->fileName);
488 1 str=assertChar(str,':', xml->fileName);
489 1 str=assertStringValue(str,"Transformational debugger info", xml->fileName);
490 1 str=assertChar(str,',', xml->fileName);
491 1 str=assertStringValue(str,"version", xml->fileName);
492 1 str=assertChar(str,':', xml->fileName);
493 1 str=assertChar(str,'1', xml->fileName);
494 1 str=assertChar(str,',', xml->fileName);
495 1 str=assertStringValue(str,"info", xml->fileName);
496 1 str=assertChar(str,':', xml->fileName);
497 1 str=skipValue(str, xml->fileName);
498 1 str=assertChar(str,',', xml->fileName);
499 1 str=assertStringValue(str,"variables", xml->fileName);
500 1 str=assertChar(str,':', xml->fileName);
501 1 str=skipValue(str, xml->fileName);
502 1 str=assertChar(str,',', xml->fileName);
503 1 str=assertStringValue(str,"equations", xml->fileName);
504 1 str=assertChar(str,':', xml->fileName);
505 1 str=readEquations(str,xml);
506 1 str=assertChar(str,',', xml->fileName);
507 1 str=assertStringValue(str,"functions", xml->fileName);
508 1 str=assertChar(str,':', xml->fileName);
509 1 str=readFunctions(str,xml);
510 1 assertChar(str,'}', xml->fileName);
511 1 }
512
513 /**
514 * @brief Initialize model data xml structure by parsing info.json.
515 *
516 * @param xml Model info struct to initialize.
517 */
518 1 void modelInfoInit(MODEL_DATA_XML* xml)
519 {
520 // check for file exists, as --fmiFilter=blackBox or protected will not export the _info.json file
521 int fileExists;
522 /* An FMI 1.0 FMU compiles the info JSON in and leaves fileName NULL, so there is
523 no file to look for. */
524
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 if (!xml->fileName) {
525 return;
526 }
527
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if (omc_flag[FLAG_INPUT_PATH])
528 {
529 const char *jsonFile;
530 ✗ GC_asprintf(&jsonFile, "%s/%s", omc_flagValue[FLAG_INPUT_PATH], xml->fileName);
531 ✗ fileExists = omc_file_exists(jsonFile);
532 ✗ omc_rc_release((void*) jsonFile);
533 }
534 else
535 {
536 1 fileExists = omc_file_exists(xml->fileName);
537 }
538
539
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if (!fileExists)
540 {
541 ✗ omc_rc_release((void*) xml->fileName);
542 ✗ xml->fileName = NULL;
543 ✗ return;
544 }
545
546 #if !defined(OMC_NO_FILESYSTEM)
547 omc_mmap_read mmap_reader = {0};
548 #endif
549 //rt_tick(0);
550 #if !defined(OMC_NO_FILESYSTEM)
551
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 if (!xml->infoXMLData) {
552 const char *filename;
553
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if (omc_flag[FLAG_INPUT_PATH]) { /* read the input path from the command line (if any) */
554 ✗ if (0 > GC_asprintf(&filename, "%s/%s", omc_flagValue[FLAG_INPUT_PATH], xml->fileName)) {
555 ✗ throwStreamPrint(NULL, "simulation_info_json.c: Error: can not allocate memory.");
556 }
557 ✗ mmap_reader = omc_mmap_open_read(filename);
558 ✗ omc_rc_release((void*) filename);
559 } else {
560 1 mmap_reader = omc_mmap_open_read(xml->fileName);
561 }
562 1 xml->infoXMLData = mmap_reader.data;
563 1 xml->modelInfoXmlLength = mmap_reader.size;
564 // fprintf(stderr, "Loaded the JSON (%ld kB)...\n", (long) (s.st_size+1023)/1024);
565 }
566 #endif
567
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 assert(xml->functionNames == NULL);
568 1 xml->functionNames = (FUNCTION_INFO*) calloc(xml->nFunctions, sizeof(FUNCTION_INFO));
569
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 assert(xml->equationInfo == NULL);
570 1 xml->equationInfo = (EQUATION_INFO*) calloc(1+xml->nEquations, sizeof(EQUATION_INFO));
571 1 xml->equationInfo[0].id = 0;
572 1 xml->equationInfo[0].section = EQUATION_SECTION_UNKNOWN;
573 1 xml->equationInfo[0].profileBlockIndex = -1;
574 1 xml->equationInfo[0].numVar = 0;
575 1 xml->equationInfo[0].vars = NULL;
576 1 xml->equationInfo[0].numVarUsed = 0;
577 1 xml->equationInfo[0].varsUsed = NULL;
578
579 // fprintf(stderr, "Loaded the JSON file in %fms...\n", rt_tock(0) * 1000.0);
580 // fprintf(stderr, "Parse the JSON %s\n", xml->infoXMLData);
581 // fprintf(stderr, "Parse the JSON %ld...\n", (long) xml->infoXMLData);
582 1 readInfoJson(xml->infoXMLData, xml);
583 // fprintf(stderr, "Parsed the JSON in %fms...\n", rt_tock(0) * 1000.0);
584 #if !defined(OMC_NO_FILESYSTEM)
585 1 omc_mmap_close_read(mmap_reader);
586 #endif
587 }
588
589 /**
590 * @brief Deinitialize memory allocated by modelInfoInit
591 *
592 * @param xml Pointer to model info xml data.
593 */
594 1 void modelInfoDeinit(MODEL_DATA_XML* xml)
595 {
596 int i,j;
597
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 if (xml->functionNames != NULL) {
598
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 for (i = 0; i < xml->nFunctions; ++i) {
599 ✗ free((void*) xml->functionNames[i].name);
600 }
601 1 free(xml->functionNames); xml->functionNames = NULL;
602 }
603
604
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 if (xml->equationInfo != NULL) {
605
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 1 time.
9 for (i = 1; i < 1+xml->nEquations; ++i) {
606
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
8 if (xml->equationInfo[i].vars != NULL) {
607
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
8 for (j = 0; j < xml->equationInfo[i].numVar; ++j) {
608 4 free((void*) xml->equationInfo[i].vars[j]);
609 }
610 4 free(xml->equationInfo[i].vars);
611 }
612
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 6 times.
8 if (xml->equationInfo[i].varsUsed != NULL) {
613
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
6 for (j = 0; j < xml->equationInfo[i].numVarUsed; ++j) {
614 4 free((void*) xml->equationInfo[i].varsUsed[j]);
615 }
616 2 free(xml->equationInfo[i].varsUsed);
617 }
618 }
619 1 free(xml->equationInfo); xml->equationInfo = NULL;
620 }
621
622 /* Built by the generated setupDataStruc. */
623 1 omc_rc_release((void*) xml->fileName); xml->fileName = NULL;
624 1 }
625
626 ✗ FUNCTION_INFO modelInfoGetFunction(MODEL_DATA_XML* xml, size_t ix)
627 {
628 /* check for xml->fileName == NULL for --fmiFilter=blackBox and protected
629 * and return dummy function info to make the fmu's simulation work, as
630 * the _json.info will not be exported for the above --fmiFilter combinations
631 */
632 ✗ if (xml->fileName == NULL)
633 ✗ return modelInfoGetDummyFunction(xml);
634
635 ✗ if(xml->functionNames == NULL)
636 {
637 ✗ modelInfoInit(xml);
638 }
639 ✗ assert(xml->functionNames);
640 ✗ return xml->functionNames[ix];
641 }
642
643 ✗ FUNCTION_INFO modelInfoGetDummyFunction(MODEL_DATA_XML* xml)
644 {
645 FUNCTION_INFO functionInfo = omc_dummyFunctionInfo;
646 ✗ return functionInfo;
647 }
648
649 ✗ EQUATION_INFO modelInfoGetDummyEquation(MODEL_DATA_XML* xml)
650 {
651 const char * var = "";
652 EQUATION_INFO equationInfo = {-1, EQUATION_SECTION_UNKNOWN, 0, 0, -1, &var}; // omc_dummyEquationInfo is not working in mingw
653 ✗ return equationInfo;
654 }
655
656 /**
657 * @brief Get equation info for equation with index `ix`.
658 *
659 * Return dummy equation info if xml->fileName == NULL, e.g. for
660 * --fmiFilter=blackBox and protected.
661 * Return dummy equation info if `ix` is out of range.
662 *
663 * @param xml Model info XML.
664 * @param ix Equation index.
665 * @return EQUATION_INFO Equation info for equation `ix`.
666 */
667 ✗ EQUATION_INFO modelInfoGetEquation(MODEL_DATA_XML* xml, size_t ix)
668 {
669 /* check for xml->fileName == NULL for --fmiFilter=blackBox and protected
670 * and return dummy equation info to make the fmu's simulation work, as
671 * the _json.info will not be exported for the above --fmiFilter combinations
672 */
673 ✗ if (xml->fileName == NULL)
674 ✗ return modelInfoGetDummyEquation(xml);
675
676 ✗ if (xml->equationInfo == NULL) {
677 ✗ modelInfoInit(xml);
678 }
679 ✗ assert(xml->equationInfo);
680 ✗ if (ix<0 || ix > xml->nEquations) {
681 ✗ errorStreamPrint(OMC_LOG_STDOUT, 0, "modelInfoGetEquation failed to get info for equation %zu, out of range.\n", ix);
682 ✗ return modelInfoGetDummyEquation(xml);
683 }
684 ✗ return xml->equationInfo[ix];
685 }
686
687 ✗ EQUATION_INFO modelInfoGetEquationIndexByProfileBlock(MODEL_DATA_XML* xml, size_t ix)
688 {
689 /* check for xml->fileName == NULL for --fmiFilter=blackBox and protected
690 * and return dummy equation info to make the fmu's simulation work, as
691 * the _json.info will not be exported for the above --fmiFilter combinations
692 */
693 ✗ if (xml->fileName == NULL)
694 ✗ return modelInfoGetDummyEquation(xml);
695
696 int i;
697 ✗ if(xml->equationInfo == NULL)
698 {
699 ✗ modelInfoInit(xml);
700 }
701 ✗ if(ix > xml->nProfileBlocks)
702 {
703 ✗ throwStreamPrint(NULL, "Requested equation with profiler index %ld, but we only have %ld such blocks", (long int)ix, xml->nProfileBlocks);
704 }
705 ✗ for(i=0; i<xml->nEquations; i++)
706 {
707 ✗ if(xml->equationInfo[i].profileBlockIndex == ix)
708 {
709 ✗ return xml->equationInfo[i];
710 }
711 }
712 ✗ throwStreamPrint(NULL, "Requested equation with profiler index %ld, but could not find it!", (long int)ix);
713 }
714