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 |