2 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 * Copyright (C) 2016-2016 - Scilab Enterprises - Clement DAVID
5 * This file is hereby licensed under the terms of the GNU GPL v2.0,
6 * pursuant to article 5.3.4 of the CeCILL v.2.1.
7 * This file was originally licensed under the terms of the CeCILL v2.1,
8 * and continues to be available under such terms.
9 * For more information, see the COPYING file which you should have received
10 * along with this program.
13 #include "XMIResource.hxx"
19 #include <cmath> // for trunc
22 #include "sci_types.h"
24 #include <libxml/xmlwriter.h>
27 namespace org_scilab_modules_scicos
30 int XMIResource::save(const char* uri)
34 xmlTextWriterPtr writer = xmlNewTextWriterFilename(uri, 0);
40 status = xmlTextWriterSetIndent(writer, 1);
43 xmlFreeTextWriter(writer);
47 status = xmlTextWriterStartDocument(writer, "1.0", "UTF-8", NULL);
50 xmlFreeTextWriter(writer);
54 status = writeDiagram(writer);
57 xmlFreeTextWriter(writer);
61 status = xmlTextWriterEndDocument(writer);
62 xmlFreeTextWriter(writer);
66 static bool is_empty_matrix(const std::vector<double>& v)
68 // v == {1, 2, 0, 0, 0}
69 return v.size() == 5 && v[0] == sci_matrix && v[1] == 2. && v[2] == 0. && v[3] == 0. && v[4] == 0.;
72 static bool is_empty_list(const std::vector<double>& v)
75 return v.size() == 2 && v[0] == sci_list && v[1] == 0.;
78 static bool is_string_vector(const std::vector<double>& v)
80 return v.size() > 2 && v[0] == sci_strings && v[1] != 0;
83 static std::string to_string(int v)
85 return std::to_string(v);
88 static std::string to_string(bool v)
100 static std::string to_string(double v)
102 if (std::trunc(v) == v)
104 return to_string((int) v);
107 std::string str(15, '\0');
108 // std::snprintf(const_cast<char*>(str.data()), str.size(), "%.6E", v);
109 std::sprintf(const_cast<char*>(str.data()), "%.6E", v);
113 /* helper function to decode simple string EXPRS */
114 static std::vector<std::string> to_string_vector(const std::vector<double>& v)
116 std::vector<std::string> ret;
117 std::vector<double>::const_iterator it = v.begin();
119 int strHeader = *it++;
120 if (strHeader != sci_strings)
124 unsigned int iDims = *it++;
126 // manage multi-dimensional arrays (will be serialized as a vector)
127 unsigned int iElements = 1;
128 for (unsigned int i = 0; i < iDims; ++i)
130 iElements *= static_cast<unsigned int>(*it++);
133 // retrieve the length of each encoded string, stored as a stack
134 std::vector<unsigned int> stringsLength;
135 stringsLength.reserve(iElements + 1);
136 stringsLength.push_back(0);
137 for (unsigned int i = 0; i < iElements; ++i)
139 stringsLength.push_back(*it++);
142 // Retrieving the pointers (already UTF-8 encoded char*) and store them as strings
143 ret.reserve(ret.size() + iElements);
144 for (unsigned int i = 0; i < iElements; ++i)
147 const double* strData = &(*(it + stringsLength[i]));
148 ret.emplace_back((char*) strData);
155 static int writeBase64(xmlTextWriterPtr writer, const char* name, const std::vector<double>& v)
159 // convert values as big endian (network endianess)
160 // convert the big endian value to Base64
161 std::string content = base64::encode(v);
163 // write the XML data
164 status = xmlTextWriterStartElement(writer, BAD_CAST(name));
170 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("base64"), BAD_CAST(content.data()));
176 status = xmlTextWriterEndElement(writer);
180 int XMIResource::writeDatatype(xmlTextWriterPtr writer, const std::vector<int>& datatype)
184 status = xmlTextWriterStartElement(writer, BAD_CAST("datatype"));
190 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("type"), BAD_CAST(to_string(datatype[2]).c_str()));
196 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("rows"), BAD_CAST(to_string(datatype[0]).c_str()));
202 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("columns"), BAD_CAST(to_string(datatype[1]).c_str()));
208 status = xmlTextWriterEndElement(writer);
217 int XMIResource::writePoint(xmlTextWriterPtr writer, double x, double y)
221 status = xmlTextWriterStartElement(writer, BAD_CAST("controlPoint"));
227 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("x"), BAD_CAST(to_string(x).c_str()));
233 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("y"), BAD_CAST(to_string(y).c_str()));
239 status = xmlTextWriterEndElement(writer);
248 int XMIResource::writeGeometry(xmlTextWriterPtr writer, ScicosID id, kind_t kind)
252 status = xmlTextWriterStartElement(writer, BAD_CAST("geometry"));
258 std::vector<double> doubleArrayValue;
259 controller.getObjectProperty(id, kind, GEOMETRY, doubleArrayValue);
261 if (doubleArrayValue.size() > i && doubleArrayValue[i])
263 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("x"), BAD_CAST(to_string(doubleArrayValue[i]).c_str()));
271 if (doubleArrayValue.size() > i && doubleArrayValue[i])
273 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("y"), BAD_CAST(to_string(doubleArrayValue[i]).c_str()));
281 if (doubleArrayValue.size() > i && doubleArrayValue[i])
283 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("width"), BAD_CAST(to_string(doubleArrayValue[i]).c_str()));
291 if (doubleArrayValue.size() > i && doubleArrayValue[i])
293 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("height"), BAD_CAST(to_string(doubleArrayValue[i]).c_str()));
300 status = xmlTextWriterEndElement(writer);
309 int XMIResource::writeAbstractLayer(xmlTextWriterPtr writer, ScicosID id, kind_t kind)
313 std::vector<std::string> context;
314 controller.getObjectProperty(id, kind, DIAGRAM_CONTEXT, context);
315 for (const std::string& c : context)
317 status = xmlTextWriterStartElement(writer, BAD_CAST("context"));
325 status = xmlTextWriterWriteCDATA(writer, BAD_CAST(c.c_str()));
332 status = xmlTextWriterEndElement(writer);
339 std::vector<ScicosID> children;
340 controller.getObjectProperty(id, kind, CHILDREN, children);
341 for (ScicosID child : children)
343 kind_t kind = controller.getKind(child);
347 status = writeBlock(writer, child);
350 status = writeLink(writer, child);
353 status = writeAnnotation(writer, child);
369 int XMIResource::writeAbstractBaseObject(xmlTextWriterPtr writer, ScicosID id, kind_t kind)
374 controller.getObjectProperty(id, kind, UID, uid);
375 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("uid"), BAD_CAST(uid.c_str()));
381 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("parentDiagram"), BAD_CAST("/"));
387 /* parent / child relation is not serialized as this relation is the XML tree */
391 int XMIResource::writeDiagram(xmlTextWriterPtr writer)
395 status = xmlTextWriterStartElementNS(writer, BAD_CAST("xcos"), BAD_CAST("Diagram"), BAD_CAST("org.scilab.modules.xcos"));
402 * Write default xmlns
404 status = xmlTextWriterWriteAttributeNS(writer, BAD_CAST("xmi"), BAD_CAST("version"), BAD_CAST("http://www.omg.org/XMI"), BAD_CAST("2.0"));
409 status = xmlTextWriterWriteAttributeNS(writer, BAD_CAST("xsi"), BAD_CAST("schemaLocation"), BAD_CAST("http://www.w3.org/2001/XMLSchema-instance"), BAD_CAST("org.scilab.modules.xcos xcos.ecore"));
418 std::string strValue;
419 controller.getObjectProperty(root, DIAGRAM, TITLE, strValue);
420 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("title"), BAD_CAST(strValue.c_str()));
427 controller.getObjectProperty(root, DIAGRAM, PATH, strValue);
428 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("path"), BAD_CAST(strValue.c_str()));
435 controller.getObjectProperty(root, DIAGRAM, DEBUG_LEVEL, intValue);
436 strValue = to_string(intValue);
437 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("debugLevel"), BAD_CAST(strValue.c_str()));
444 controller.getObjectProperty(root, DIAGRAM, VERSION_NUMBER, strValue);
445 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("version"), BAD_CAST(strValue.c_str()));
451 status = writeAbstractLayer(writer, root, DIAGRAM);
457 status = writeSimulationConfig(writer, root);
463 status = xmlTextWriterEndElement(writer);
472 int XMIResource::writeSimulationConfig(xmlTextWriterPtr writer, ScicosID id)
476 status = xmlTextWriterStartElement(writer, BAD_CAST("properties"));
482 std::vector<double> doubleArrayValue;
483 controller.getObjectProperty(id, DIAGRAM, PROPERTIES, doubleArrayValue);
485 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("finalTime"), BAD_CAST(to_string(doubleArrayValue[i]).c_str()));
492 if (i >= doubleArrayValue.size())
496 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("absoluteTolerance"), BAD_CAST(to_string(doubleArrayValue[i]).c_str()));
503 if (i >= doubleArrayValue.size())
507 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("relativeTolerance"), BAD_CAST(to_string(doubleArrayValue[i]).c_str()));
514 if (i >= doubleArrayValue.size())
518 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("timeTolerance"), BAD_CAST(to_string(doubleArrayValue[i]).c_str()));
525 if (i >= doubleArrayValue.size())
529 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("deltaT"), BAD_CAST(to_string(doubleArrayValue[i]).c_str()));
536 if (i >= doubleArrayValue.size())
540 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("realtimeScale"), BAD_CAST(to_string(doubleArrayValue[i]).c_str()));
547 if (i >= doubleArrayValue.size())
551 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("solver"), BAD_CAST(to_string(doubleArrayValue[i]).c_str()));
558 if (i >= doubleArrayValue.size())
562 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("deltaH"), BAD_CAST(to_string(doubleArrayValue[i]).c_str()));
568 status = xmlTextWriterEndElement(writer);
577 int XMIResource::writeBlock(xmlTextWriterPtr writer, ScicosID id)
581 status = xmlTextWriterStartElement(writer, BAD_CAST("child"));
587 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("xsi:type"), BAD_CAST("xcos:Block"));
593 status = writeAbstractBaseObject(writer, id, BLOCK);
599 std::string strValue;
600 controller.getObjectProperty(id, BLOCK, DESCRIPTION, strValue);
601 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("description"), BAD_CAST(strValue.c_str()));
608 controller.getObjectProperty(id, BLOCK, LABEL, strValue);
609 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("label"), BAD_CAST(strValue.c_str()));
616 controller.getObjectProperty(id, BLOCK, STYLE, strValue);
617 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("style"), BAD_CAST(strValue.c_str()));
624 controller.getObjectProperty(id, BLOCK, INTERFACE_FUNCTION, strValue);
625 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("interfaceFunction"), BAD_CAST(strValue.c_str()));
632 controller.getObjectProperty(id, BLOCK, SIM_FUNCTION_NAME, strValue);
633 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("functionName"), BAD_CAST(strValue.c_str()));
640 controller.getObjectProperty(id, BLOCK, SIM_FUNCTION_API, intValue);
641 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("functionAPI"), BAD_CAST(to_string(intValue).c_str()));
647 std::vector<int> intArrayValue;
648 controller.getObjectProperty(id, BLOCK, SIM_DEP_UT, intArrayValue);
650 if (intArrayValue.size() > i && intArrayValue[i])
652 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("dependsOnU"), BAD_CAST(to_string(intArrayValue[i]).c_str()));
659 if (intArrayValue.size() > i && intArrayValue[i])
661 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("dependsOnT"), BAD_CAST(to_string(intArrayValue[i]).c_str()));
669 controller.getObjectProperty(id, BLOCK, SIM_BLOCKTYPE, strValue);
670 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("blocktype"), BAD_CAST(strValue.c_str()));
676 status = writeAbstractLayer(writer, id, BLOCK);
682 status = writeGeometry(writer, id, BLOCK);
688 std::vector<double> doubleArrayValue;
689 controller.getObjectProperty(id, BLOCK, EXPRS, doubleArrayValue);
690 if (is_empty_matrix(doubleArrayValue))
692 // we do not serialize the default value
694 else if (is_string_vector(doubleArrayValue))
696 // if this is a string the expression is used
697 std::vector<std::string> values = to_string_vector(doubleArrayValue);
699 for (const std::string& s : values)
701 status = xmlTextWriterStartElement(writer, BAD_CAST("expression"));
706 status = xmlTextWriterWriteCDATA(writer, BAD_CAST(s.c_str()));
712 status = xmlTextWriterEndElement(writer);
721 // encode the value as base64 binary
722 status = writeBase64(writer, "exprs", doubleArrayValue);
729 intArrayValue.clear();
730 controller.getObjectProperty(id, BLOCK, NZCROSS, intArrayValue);
731 for (int i : intArrayValue)
733 status = xmlTextWriterWriteElement(writer, BAD_CAST("nzcross"), BAD_CAST(to_string(i).c_str()));
740 intArrayValue.clear();
741 controller.getObjectProperty(id, BLOCK, NMODE, intArrayValue);
742 for (int i : intArrayValue)
744 status = xmlTextWriterWriteElement(writer, BAD_CAST("nmode"), BAD_CAST(to_string(i).c_str()));
751 doubleArrayValue.clear();
752 controller.getObjectProperty(id, BLOCK, EQUATIONS, doubleArrayValue);
753 if (!doubleArrayValue.empty() && !is_empty_list(doubleArrayValue))
755 status = writeBase64(writer, "equations", doubleArrayValue);
762 std::vector<ScicosID> scicosIDArrayValue;
763 controller.getObjectProperty(id, BLOCK, INPUTS, scicosIDArrayValue);
764 for (ScicosID p : scicosIDArrayValue)
766 status = writePort(writer, INPUTS, p);
773 scicosIDArrayValue.clear();
774 controller.getObjectProperty(id, BLOCK, OUTPUTS, scicosIDArrayValue);
775 for (ScicosID p : scicosIDArrayValue)
777 status = writePort(writer, OUTPUTS, p);
784 controller.getObjectProperty(id, BLOCK, EVENT_INPUTS, scicosIDArrayValue);
785 for (ScicosID p : scicosIDArrayValue)
787 status = writePort(writer, EVENT_INPUTS, p);
794 controller.getObjectProperty(id, BLOCK, EVENT_OUTPUTS, scicosIDArrayValue);
795 for (ScicosID p : scicosIDArrayValue)
797 status = writePort(writer, EVENT_OUTPUTS, p);
804 doubleArrayValue.clear();
805 controller.getObjectProperty(id, BLOCK, RPAR, doubleArrayValue);
806 for (double d : doubleArrayValue)
808 status = xmlTextWriterWriteElement(writer, BAD_CAST("rpar"), BAD_CAST(to_string(d).c_str()));
815 intArrayValue.clear();
816 controller.getObjectProperty(id, BLOCK, IPAR, intArrayValue);
817 for (int i : intArrayValue)
819 status = xmlTextWriterWriteElement(writer, BAD_CAST("ipar"), BAD_CAST(to_string(i).c_str()));
826 doubleArrayValue.clear();
827 controller.getObjectProperty(id, BLOCK, OPAR, doubleArrayValue);
828 if (!is_empty_list(doubleArrayValue))
830 status = writeBase64(writer, "opar", doubleArrayValue);
837 doubleArrayValue.clear();
838 controller.getObjectProperty(id, BLOCK, STATE, doubleArrayValue);
839 for (double d : doubleArrayValue)
841 status = xmlTextWriterWriteElement(writer, BAD_CAST("state"), BAD_CAST(to_string(d).c_str()));
848 doubleArrayValue.clear();
849 controller.getObjectProperty(id, BLOCK, DSTATE, doubleArrayValue);
850 for (double d : doubleArrayValue)
852 status = xmlTextWriterWriteElement(writer, BAD_CAST("dstate"), BAD_CAST(to_string(d).c_str()));
859 doubleArrayValue.clear();
860 controller.getObjectProperty(id, BLOCK, ODSTATE, doubleArrayValue);
861 if (!is_empty_list(doubleArrayValue))
863 status = writeBase64(writer, "odstate", doubleArrayValue);
870 status = xmlTextWriterEndElement(writer);
879 int XMIResource::writePort(xmlTextWriterPtr writer, enum object_properties_t container, ScicosID id)
902 status = xmlTextWriterStartElement(writer, BAD_CAST(element.c_str()));
908 std::string strValue;
909 controller.getObjectProperty(id, PORT, UID, strValue);
910 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("uid"), BAD_CAST(strValue.c_str()));
917 controller.getObjectProperty(id, PORT, SOURCE_BLOCK, idValue);
918 controller.getObjectProperty(idValue, BLOCK, UID, strValue);
919 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("sourceBlock"), BAD_CAST(strValue.c_str()));
925 const std::vector<std::string> elementName = {"portUndefined", "in", "out", "ein", "eout"};
927 controller.getObjectProperty(id, PORT, PORT_KIND, portKind);
928 if (portKind < 0 && elementName.size() <= (unsigned int) portKind)
932 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("kind"), BAD_CAST(elementName[portKind].c_str()));
939 controller.getObjectProperty(id, PORT, IMPLICIT, implicit);
940 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("implicit"), BAD_CAST(to_string(implicit).c_str()));
946 controller.getObjectProperty(id, PORT, CONNECTED_SIGNALS, idValue);
950 controller.getObjectProperty(idValue, LINK, UID, strValue);
952 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("connectedSignal"), BAD_CAST(strValue.c_str()));
960 controller.getObjectProperty(id, PORT, STYLE, strValue);
961 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("style"), BAD_CAST(strValue.c_str()));
968 controller.getObjectProperty(id, PORT, LABEL, strValue);
969 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("label"), BAD_CAST(strValue.c_str()));
975 std::vector<int> intArrayValue;
976 controller.getObjectProperty(id, PORT, DATATYPE, intArrayValue);
977 status = writeDatatype(writer, intArrayValue);
983 status = xmlTextWriterEndElement(writer);
992 int XMIResource::writeLink(xmlTextWriterPtr writer, ScicosID id)
996 status = xmlTextWriterStartElement(writer, BAD_CAST("child"));
1002 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("xsi:type"), BAD_CAST("xcos:Link"));
1008 status = writeAbstractBaseObject(writer, id, LINK);
1015 std::string strValue;
1016 controller.getObjectProperty(id, LINK, SOURCE_PORT, idValue);
1020 controller.getObjectProperty(idValue, PORT, UID, strValue);
1022 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("sourcePort"), BAD_CAST(strValue.c_str()));
1029 controller.getObjectProperty(id, LINK, DESTINATION_PORT, idValue);
1033 controller.getObjectProperty(idValue, PORT, UID, strValue);
1035 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("destinationPort"), BAD_CAST(strValue.c_str()));
1043 controller.getObjectProperty(id, LINK, STYLE, strValue);
1044 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("style"), BAD_CAST(strValue.c_str()));
1051 controller.getObjectProperty(id, LINK, LABEL, strValue);
1052 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("label"), BAD_CAST(strValue.c_str()));
1059 controller.getObjectProperty(id, LINK, COLOR, intValue);
1060 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("color"), BAD_CAST(to_string(intValue).c_str()));
1066 std::vector<int> intArrayValue;
1067 controller.getObjectProperty(id, LINK, THICK, intArrayValue);
1069 if (i < intArrayValue.size())
1071 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("lineWidth"), BAD_CAST(to_string(intArrayValue[i]).c_str()));
1078 if (i < intArrayValue.size())
1080 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("lineHeight"), BAD_CAST(to_string(intArrayValue[i]).c_str()));
1087 status = writeGeometry(writer, id, LINK);
1093 std::vector<double> dblArrayValue;
1094 controller.getObjectProperty(id, LINK, CONTROL_POINTS, dblArrayValue);
1095 for (unsigned int i = 0; i < dblArrayValue.size(); i += 2)
1097 status = writePoint(writer, dblArrayValue[i], dblArrayValue[i + 1]);
1104 status = xmlTextWriterEndElement(writer);
1113 int XMIResource::writeAnnotation(xmlTextWriterPtr writer, ScicosID id)
1117 status = xmlTextWriterStartElement(writer, BAD_CAST("child"));
1123 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("xsi:type"), BAD_CAST("xcos:Annotation"));
1129 status = writeAbstractBaseObject(writer, id, ANNOTATION);
1135 std::string strValue;
1136 controller.getObjectProperty(id, ANNOTATION, DESCRIPTION, strValue);
1137 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("description"), BAD_CAST(strValue.c_str()));
1144 controller.getObjectProperty(id, ANNOTATION, FONT, strValue);
1145 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("font"), BAD_CAST(strValue.c_str()));
1152 controller.getObjectProperty(id, ANNOTATION, FONT_SIZE, strValue);
1153 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("fontSize"), BAD_CAST(strValue.c_str()));
1160 controller.getObjectProperty(id, ANNOTATION, STYLE, strValue);
1161 status = xmlTextWriterWriteAttribute(writer, BAD_CAST("style"), BAD_CAST(strValue.c_str()));
1167 status = xmlTextWriterEndElement(writer);
1176 } /* namespace org_scilab_modules_xcos */