@@ -75,7 +75,21 @@ typedef int fmi2Type;
7575#define fmi2Discard 2
7676#define fmi2Error 3
7777#define fmi2Fatal 4
78+ #define fmi2Pending 5
79+ #define fmi2ModelExchange 0
7880#define fmi2CoSimulation 1
81+ #define fmi2True 1
82+ #define fmi2False 0
83+
84+ typedef unsigned char fmi2Byte;
85+ typedef struct {
86+ fmi2Boolean newDiscreteStatesNeeded;
87+ fmi2Boolean terminateSimulation;
88+ fmi2Boolean nominalsOfContinuousStatesChanged;
89+ fmi2Boolean valuesOfContinuousStatesChanged;
90+ fmi2Boolean nextEventTimeDefined;
91+ fmi2Real nextEventTime;
92+ } fmi2EventInfo;
7993
8094/* FMI 2.0 callback types */
8195typedef void (*fmi2LoggerTF)(void*, fmi2String, fmi2Status, fmi2String, fmi2String, ...);
@@ -110,6 +124,16 @@ typedef fmi2Status (*fmi2TerminateTF)(fmi2Component);
110124typedef void (*fmi2FreeInstanceTF)(fmi2Component);
111125typedef fmi2Status (*fmi2GetFMUstateTF)(fmi2Component, fmi2FMUstate*);
112126typedef fmi2Status (*fmi2SetFMUstateTF)(fmi2Component, fmi2FMUstate);
127+ /* Model Exchange function pointer types */
128+ typedef fmi2Status (*fmi2SetTimeTF)(fmi2Component, fmi2Real);
129+ typedef fmi2Status (*fmi2SetContinuousStatesTF)(fmi2Component, const fmi2Real*, size_t);
130+ typedef fmi2Status (*fmi2GetDerivativesTF)(fmi2Component, fmi2Real*, size_t);
131+ typedef fmi2Status (*fmi2GetContinuousStatesTF)(fmi2Component, fmi2Real*, size_t);
132+ typedef fmi2Status (*fmi2GetEventIndicatorsTF)(fmi2Component, fmi2Real*, size_t);
133+ typedef fmi2Status (*fmi2CompletedIntegratorStepTF)(fmi2Component, fmi2Boolean, fmi2Boolean*, fmi2Boolean*);
134+ typedef fmi2Status (*fmi2NewDiscreteStatesTF)(fmi2Component, fmi2EventInfo*);
135+ typedef fmi2Status (*fmi2EnterContinuousTimeModeTF)(fmi2Component);
136+ typedef fmi2Status (*fmi2EnterEventModeTF)(fmi2Component);
113137typedef fmi2Status (*fmi2FreeFMUstateTF)(fmi2Component, fmi2FMUstate*);
114138
115139/* ── FMU handle ── */
@@ -139,10 +163,21 @@ typedef struct {
139163 fmi2GetFMUstateTF GetFMUstate;
140164 fmi2SetFMUstateTF SetFMUstate;
141165 fmi2FreeFMUstateTF FreeFMUstate;
166+ /* Model Exchange */
167+ fmi2SetTimeTF SetTime;
168+ fmi2SetContinuousStatesTF SetContinuousStates;
169+ fmi2GetDerivativesTF GetDerivatives;
170+ fmi2GetContinuousStatesTF GetContinuousStates;
171+ fmi2GetEventIndicatorsTF GetEventIndicators;
172+ fmi2CompletedIntegratorStepTF CompletedIntegratorStep;
173+ fmi2NewDiscreteStatesTF NewDiscreteStates;
174+ fmi2EnterContinuousTimeModeTF EnterContinuousTimeMode;
175+ fmi2EnterEventModeTF EnterEventMode;
142176
143177 /* State management */
144178 fmi2FMUstate savedStates[64];
145179 int nextStateId;
180+ int isME; /* 1 if Model Exchange, 0 if Co-Simulation */
146181} FMUHandle;
147182
148183/* ── Simple JSON helpers (minimal, no external deps) ── */
@@ -265,8 +300,19 @@ static int load_fmu(FMUHandle* handle, const char* fmuPath) {
265300 LOAD_FN(handle, GetFMUstate);
266301 LOAD_FN(handle, SetFMUstate);
267302 LOAD_FN(handle, FreeFMUstate);
303+ /* Model Exchange */
304+ LOAD_FN(handle, SetTime);
305+ LOAD_FN(handle, SetContinuousStates);
306+ LOAD_FN(handle, GetDerivatives);
307+ LOAD_FN(handle, GetContinuousStates);
308+ LOAD_FN(handle, GetEventIndicators);
309+ LOAD_FN(handle, CompletedIntegratorStep);
310+ LOAD_FN(handle, NewDiscreteStates);
311+ LOAD_FN(handle, EnterContinuousTimeMode);
312+ LOAD_FN(handle, EnterEventMode);
268313
269314 handle->nextStateId = 0;
315+ handle->isME = 0;
270316 memset(handle->savedStates, 0, sizeof(handle->savedStates));
271317
272318 return 0;
@@ -328,12 +374,16 @@ int main(int argc, char* argv[]) {
328374 double startTime = parse_json_number(line, "startTime");
329375 double stopTime = parse_json_number(line, "stopTime");
330376 double stepSize = parse_json_number(line, "stepSize");
377+ char fmuType[16];
378+ parse_json_string(line, "fmuType", fmuType, sizeof(fmuType));
379+ int typeId = (strcmp(fmuType, "me") == 0) ? fmi2ModelExchange : fmi2CoSimulation;
380+ handle.isME = (typeId == fmi2ModelExchange);
331381
332382 if (!handle.Instantiate) { json_respond_error(id, "Instantiate not loaded"); continue; }
333383
334384 fmi2CallbackFunctions callbacks = { dummyLogger, calloc, free, NULL, NULL };
335385 handle.comp = handle.Instantiate(
336- handle.modelIdentifier, fmi2CoSimulation , handle.guid,
386+ handle.modelIdentifier, typeId , handle.guid,
337387 handle.extractDir, &callbacks, 0, 0
338388 );
339389
@@ -353,6 +403,19 @@ int main(int argc, char* argv[]) {
353403 if (s > fmi2Warning) { json_respond_error(id, "ExitInitializationMode failed"); continue; }
354404 }
355405
406+ /* For ME: enter event mode, iterate discrete states, then enter continuous mode */
407+ if (handle.isME) {
408+ if (handle.EnterEventMode) handle.EnterEventMode(handle.comp);
409+ if (handle.NewDiscreteStates) {
410+ fmi2EventInfo info;
411+ do {
412+ info.newDiscreteStatesNeeded = fmi2False;
413+ handle.NewDiscreteStates(handle.comp, &info);
414+ } while (info.newDiscreteStatesNeeded);
415+ }
416+ if (handle.EnterContinuousTimeMode) handle.EnterContinuousTimeMode(handle.comp);
417+ }
418+
356419 json_respond_ok(id);
357420 }
358421 else if (strcmp(method, "doStep") == 0) {
@@ -362,8 +425,23 @@ int main(int argc, char* argv[]) {
362425 if (!handle.DoStep || !handle.comp) { json_respond_error(id, "Not initialized"); continue; }
363426
364427 fmi2Status s = handle.DoStep(handle.comp, currentTime, stepSize, 1);
365- if (s > fmi2Warning) { json_respond_error(id, "doStep failed"); continue; }
366-
428+ if (s == fmi2Pending) {
429+ printf("{\\"result\\":\\"pending\\",\\"id\\":%d}\\n", id);
430+ fflush(stdout);
431+ } else if (s > fmi2Warning) {
432+ json_respond_error(id, "doStep failed");
433+ } else {
434+ json_respond_ok(id);
435+ }
436+ }
437+ else if (strcmp(method, "getStepStatus") == 0) {
438+ if (!handle.comp) { json_respond_error(id, "Not initialized"); continue; }
439+ /* fmi2GetStatus for doStep — check if async step completed */
440+ printf("{\\"result\\":\\"ok\\",\\"id\\":%d}\\n", id);
441+ fflush(stdout);
442+ }
443+ else if (strcmp(method, "cancelStep") == 0) {
444+ if (!handle.comp) { json_respond_error(id, "Not initialized"); continue; }
367445 json_respond_ok(id);
368446 }
369447 else if (strcmp(method, "getOutputs") == 0) {
@@ -463,6 +541,110 @@ int main(int argc, char* argv[]) {
463541 handle.savedStates[stateId] = NULL;
464542 json_respond_ok(id);
465543 }
544+ /* ── Model Exchange methods ── */
545+ else if (strcmp(method, "setTime") == 0) {
546+ double t = parse_json_number(line, "time");
547+ if (!handle.comp || !handle.SetTime) { json_respond_error(id, "setTime not supported"); continue; }
548+ fmi2Status s = handle.SetTime(handle.comp, t);
549+ if (s > fmi2Warning) { json_respond_error(id, "setTime failed"); continue; }
550+ json_respond_ok(id);
551+ }
552+ else if (strcmp(method, "setContinuousStates") == 0) {
553+ if (!handle.comp || !handle.SetContinuousStates) { json_respond_error(id, "setContinuousStates not supported"); continue; }
554+ /* Parse "states" array: {"states":[1.0,2.0,...]} */
555+ const char* arr = strstr(line, "\\"states\\":[");
556+ fmi2Real states[256];
557+ size_t nx = 0;
558+ if (arr) {
559+ arr += strlen("\\"states\\":[");
560+ while (*arr && *arr != ']' && nx < 256) {
561+ while (*arr == ' ' || *arr == ',') arr++;
562+ if (*arr == ']') break;
563+ states[nx++] = atof(arr);
564+ while (*arr && *arr != ',' && *arr != ']') arr++;
565+ }
566+ }
567+ fmi2Status s = handle.SetContinuousStates(handle.comp, states, nx);
568+ if (s > fmi2Warning) { json_respond_error(id, "setContinuousStates failed"); continue; }
569+ json_respond_ok(id);
570+ }
571+ else if (strcmp(method, "getDerivatives") == 0) {
572+ int nx = parse_json_int(line, "nx");
573+ if (nx <= 0) nx = 256;
574+ if (!handle.comp || !handle.GetDerivatives) { json_respond_error(id, "getDerivatives not supported"); continue; }
575+ fmi2Real dx[256];
576+ if (nx > 256) nx = 256;
577+ fmi2Status s = handle.GetDerivatives(handle.comp, dx, (size_t)nx);
578+ if (s > fmi2Warning) { json_respond_error(id, "getDerivatives failed"); continue; }
579+ printf("{\\"result\\":[");
580+ for (int i = 0; i < nx; i++) { if (i > 0) printf(","); printf("%g", dx[i]); }
581+ printf("],\\"id\\":%d}\\n", id);
582+ fflush(stdout);
583+ }
584+ else if (strcmp(method, "getContinuousStates") == 0) {
585+ int nx = parse_json_int(line, "nx");
586+ if (nx <= 0) nx = 256;
587+ if (!handle.comp || !handle.GetContinuousStates) { json_respond_error(id, "getContinuousStates not supported"); continue; }
588+ fmi2Real x[256];
589+ if (nx > 256) nx = 256;
590+ fmi2Status s = handle.GetContinuousStates(handle.comp, x, (size_t)nx);
591+ if (s > fmi2Warning) { json_respond_error(id, "getContinuousStates failed"); continue; }
592+ printf("{\\"result\\":[");
593+ for (int i = 0; i < nx; i++) { if (i > 0) printf(","); printf("%g", x[i]); }
594+ printf("],\\"id\\":%d}\\n", id);
595+ fflush(stdout);
596+ }
597+ else if (strcmp(method, "getEventIndicators") == 0) {
598+ int nz = parse_json_int(line, "nz");
599+ if (nz <= 0) nz = 64;
600+ if (!handle.comp || !handle.GetEventIndicators) { json_respond_error(id, "getEventIndicators not supported"); continue; }
601+ fmi2Real z[256];
602+ if (nz > 256) nz = 256;
603+ fmi2Status s = handle.GetEventIndicators(handle.comp, z, (size_t)nz);
604+ if (s > fmi2Warning) { json_respond_error(id, "getEventIndicators failed"); continue; }
605+ printf("{\\"result\\":[");
606+ for (int i = 0; i < nz; i++) { if (i > 0) printf(","); printf("%g", z[i]); }
607+ printf("],\\"id\\":%d}\\n", id);
608+ fflush(stdout);
609+ }
610+ else if (strcmp(method, "completedIntegratorStep") == 0) {
611+ if (!handle.comp || !handle.CompletedIntegratorStep) { json_respond_error(id, "completedIntegratorStep not supported"); continue; }
612+ fmi2Boolean enterEventMode = fmi2False, terminateSim = fmi2False;
613+ fmi2Status s = handle.CompletedIntegratorStep(handle.comp, fmi2True, &enterEventMode, &terminateSim);
614+ if (s > fmi2Warning) { json_respond_error(id, "completedIntegratorStep failed"); continue; }
615+ printf("{\\"result\\":{\\"enterEventMode\\":%s,\\"terminateSimulation\\":%s},\\"id\\":%d}\\n",
616+ enterEventMode ? "true" : "false", terminateSim ? "true" : "false", id);
617+ fflush(stdout);
618+ }
619+ else if (strcmp(method, "newDiscreteStates") == 0) {
620+ if (!handle.comp || !handle.NewDiscreteStates) { json_respond_error(id, "newDiscreteStates not supported"); continue; }
621+ fmi2EventInfo info;
622+ memset(&info, 0, sizeof(info));
623+ fmi2Status s = handle.NewDiscreteStates(handle.comp, &info);
624+ if (s > fmi2Warning) { json_respond_error(id, "newDiscreteStates failed"); continue; }
625+ printf("{\\"result\\":{\\"newDiscreteStatesNeeded\\":%s,\\"terminateSimulation\\":%s,"
626+ "\\"nominalsChanged\\":%s,\\"valuesChanged\\":%s,"
627+ "\\"nextEventTimeDefined\\":%s,\\"nextEventTime\\":%g},\\"id\\":%d}\\n",
628+ info.newDiscreteStatesNeeded ? "true" : "false",
629+ info.terminateSimulation ? "true" : "false",
630+ info.nominalsOfContinuousStatesChanged ? "true" : "false",
631+ info.valuesOfContinuousStatesChanged ? "true" : "false",
632+ info.nextEventTimeDefined ? "true" : "false",
633+ info.nextEventTime, id);
634+ fflush(stdout);
635+ }
636+ else if (strcmp(method, "enterContinuousTimeMode") == 0) {
637+ if (!handle.comp || !handle.EnterContinuousTimeMode) { json_respond_error(id, "enterContinuousTimeMode not supported"); continue; }
638+ fmi2Status s = handle.EnterContinuousTimeMode(handle.comp);
639+ if (s > fmi2Warning) { json_respond_error(id, "enterContinuousTimeMode failed"); continue; }
640+ json_respond_ok(id);
641+ }
642+ else if (strcmp(method, "enterEventMode") == 0) {
643+ if (!handle.comp || !handle.EnterEventMode) { json_respond_error(id, "enterEventMode not supported"); continue; }
644+ fmi2Status s = handle.EnterEventMode(handle.comp);
645+ if (s > fmi2Warning) { json_respond_error(id, "enterEventMode failed"); continue; }
646+ json_respond_ok(id);
647+ }
466648 else if (strcmp(method, "terminate") == 0) {
467649 if (handle.comp) {
468650 if (handle.Terminate) handle.Terminate(handle.comp);
0 commit comments