Scicos: speedup model transformation
[scilab.git] / scilab / modules / scicos / src / cpp / view_scilab / ports_management.hxx
1 /*
2  *  Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  *  Copyright (C) 2014 - Scilab Enterprises - Paul Bignier
4  *  Copyright (C) 2014 - Scilab Enterprises - Clement DAVID
5  *
6  * Copyright (C) 2012 - 2016 - Scilab Enterprises
7  *
8  * This file is hereby licensed under the terms of the GNU GPL v2.0,
9  * pursuant to article 5.3.4 of the CeCILL v.2.1.
10  * This file was originally licensed under the terms of the CeCILL v2.1,
11  * and continues to be available under such terms.
12  * For more information, see the COPYING file which you should have received
13  * along with this program.
14  *
15  */
16
17 #ifndef PORTS_MANAGEMENT_HXX_
18 #define PORTS_MANAGEMENT_HXX_
19
20 #include <cmath>
21
22 #include <deque>
23 #include <string>
24 #include <vector>
25 #include <algorithm>
26 #include <iterator>
27 #include <cassert>
28
29 #include "internal.hxx"
30 #include "bool.hxx"
31 #include "double.hxx"
32 #include "string.hxx"
33
34 #include "utilities.hxx"
35 #include "Controller.hxx"
36 #include "controller_helpers.hxx"
37 #include "model/Port.hxx"
38 #include "LinkAdapter.hxx"
39
40 extern "C" {
41 #include "sci_malloc.h"
42 #include "charEncoding.h"
43 #include "localization.h"
44 }
45
46 namespace org_scilab_modules_scicos
47 {
48 namespace view_scilab
49 {
50
51 /*
52  * Utilities function to emit the error messages
53  */
54 template<object_properties_t p>
55 std::string adapterName(const object_properties_t /*port_kind*/)
56 {
57     switch (p)
58     {
59         case CONNECTED_SIGNALS:
60         case IMPLICIT:
61         case LABEL:
62         case STYLE:
63             return "graphics";
64         case DATATYPE_ROWS:
65         case DATATYPE_COLS:
66         case DATATYPE_TYPE:
67         case FIRING:
68             return "model";
69         default:
70             return "";
71     }
72 }
73
74 template<object_properties_t p>
75 std::string adapterFieldName(const object_properties_t port_kind)
76 {
77     std::string postfix;
78     switch (p)
79     {
80         case CONNECTED_SIGNALS:
81         {
82             switch (port_kind)
83             {
84                 case INPUTS:
85                     return "pin";
86                 case OUTPUTS:
87                     return "pout";
88                 case EVENT_INPUTS:
89                     return "pein";
90                 case EVENT_OUTPUTS:
91                     return "peout";
92                 default:
93                     break;
94             }
95         }
96         break;
97         case IMPLICIT:
98             postfix = "_implicit";
99             break;
100         case LABEL:
101             postfix = "_label";
102             break;
103         case STYLE:
104             postfix = "_style";
105             break;
106         case DATATYPE_TYPE:
107             postfix = "typ";
108             break;
109         case DATATYPE_ROWS:
110             postfix = "2";
111             break;
112         case DATATYPE_COLS:
113             break;
114         case FIRING:
115             return "firing";
116         default:
117             break;
118     }
119
120     std::string prefix;
121     switch (port_kind)
122     {
123         case INPUTS:
124             prefix = "in";
125             break;
126         case OUTPUTS:
127             prefix = "out";
128             break;
129         case EVENT_INPUTS:
130             prefix = "evtin";
131             break;
132         case EVENT_OUTPUTS:
133             prefix = "evtout";
134             break;
135         default:
136             break;
137     }
138
139     return prefix + postfix;
140 }
141
142 /*
143  * Return a Scilab encoded value for a property.
144  */
145 template<typename Adaptor, object_properties_t p>
146 types::InternalType* get_ports_property(const Adaptor& adaptor, const object_properties_t port_kind, const Controller& controller)
147 {
148     model::Block* adaptee = adaptor.getAdaptee();
149
150     // Retrieve the identifiers
151     std::vector<ScicosID> ids;
152     controller.getObjectProperty(adaptee, port_kind, ids);
153
154     // Translate identifiers: shared variables
155     int i = 0;
156     size_t datatypeIndex = -1;
157     // Translate identifiers to return values
158     switch (p)
159     {
160         case STYLE:
161         case LABEL:
162         {
163             if (ids.empty())
164             {
165                 return new types::String(L"");
166             }
167             types::String* o = new types::String((int)ids.size(), 1);
168             for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
169             {
170                 std::string s;
171                 controller.getObjectProperty(*it, PORT, p, s);
172                 o->set(i, s.data());
173             }
174             return o;
175         }
176         case DATATYPE_TYPE:
177             // The type defaults to [1] if no port has been defined
178             if (ids.empty())
179             {
180                 return new types::Double(1);
181             }
182             datatypeIndex++;
183         // no break
184         case DATATYPE_COLS:
185             datatypeIndex++;
186         // no break
187         case DATATYPE_ROWS:
188         {
189             datatypeIndex++;
190             double* data;
191             types::Double* o = new types::Double((int)ids.size(), 1, &data);
192             for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
193             {
194                 std::vector<int> v;
195                 controller.getObjectProperty(*it, PORT, DATATYPE, v);
196                 data[i] = v[datatypeIndex];
197             }
198             return o;
199         }
200         case FIRING:
201         {
202             double* data;
203             types::Double* o = new types::Double((int)ids.size(), 1, &data);
204             for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
205             {
206                 controller.getObjectProperty(*it, PORT, p, data[i]);
207             }
208             return o;
209         }
210         case IMPLICIT:
211         {
212             if (ids.empty())
213             {
214                 // When no port is present, return an empty matrix
215                 return types::Double::Empty();
216             }
217
218             types::String* o = new types::String((int)ids.size(), 1);
219             for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
220             {
221                 bool v;
222                 controller.getObjectProperty(*it, PORT, p, v);
223                 o->set(i, (v == false) ? L"E" : L"I");
224             }
225             return o;
226         }
227         case CONNECTED_SIGNALS:
228         {
229             double* v;
230             types::Double* o = new types::Double((int)ids.size(), 1, &v);
231
232             std::vector<ScicosID> children;
233
234             ScicosID parentBlock;
235             controller.getObjectProperty(adaptee, PARENT_BLOCK, parentBlock);
236             if (parentBlock == ScicosID())
237             {
238                 // Adding to a diagram
239                 ScicosID parentDiagram;
240                 controller.getObjectProperty(adaptee, PARENT_DIAGRAM, parentDiagram);
241
242                 controller.getObjectProperty(parentDiagram, DIAGRAM, CHILDREN, children);
243             }
244             else
245             {
246                 // Adding to a superblock
247                 controller.getObjectProperty(parentBlock, BLOCK, CHILDREN, children);
248             }
249
250             for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
251             {
252                 ScicosID id;
253                 controller.getObjectProperty(*it, PORT, p, id);
254
255                 if (id == ScicosID())
256                 {
257                     // Unconnected port, no need to search in 'children'
258                     std::cerr << "unconnected port " << id << std::endl;
259                     v[i] = 0;
260                 }
261                 else
262                 {
263                     std::vector<ScicosID>::iterator found = std::find(children.begin(), children.end(), id);
264                     if (found != children.end())
265                     {
266                         v[i] = static_cast<double>(std::distance(children.begin(), found)) + 1;
267                     }
268                     else
269                     {
270                         // connected link not found ; discard it !
271                         v[i] = 0;
272                         std::cerr << "connected port out of hierarchy " << id << std::endl;
273                     }
274                 }
275             }
276             return o;
277         }
278         default:
279             return 0;
280     }
281 }
282
283 /*
284  * Set a Scilab encoded values as a property.
285  *
286  * \note this method will ignore or return false if one of the ports does not exist, depending on the property setted.
287  */
288 template<typename Adaptor, object_properties_t p>
289 bool set_ports_property(const Adaptor& adaptor, const object_properties_t port_kind, Controller& controller, types::InternalType* v)
290 {
291     model::Block* adaptee = adaptor.getAdaptee();
292
293     // Retrieve the ports identifiers
294     std::vector<ScicosID> ids;
295     controller.getObjectProperty(adaptee, port_kind, ids);
296
297     if (v->getType() == types::InternalType::ScilabString)
298     {
299         types::String* current = v->getAs<types::String>();
300
301         // Translate identifiers: shared variables
302         int i = 0;
303         // Translate identifiers from values
304         switch (p)
305         {
306             case STYLE:
307             case LABEL:
308             {
309                 for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
310                 {
311                     char* c_str = NULL;
312                     if (i >= current->getSize())
313                     {
314                         // If the input isn't large enough, fill each port with empty strings
315                         c_str = wide_string_to_UTF8(L"");
316                     }
317                     else
318                     {
319                         c_str = wide_string_to_UTF8(current->get(i));
320                     }
321                     controller.setObjectProperty(*it, PORT, p, std::string(c_str));
322                     FREE(c_str);
323                 }
324                 return true;
325             }
326             case IMPLICIT:
327             {
328                 int maxSize = static_cast<int>(ids.size());
329                 if (current->getSize() < maxSize)
330                 {
331                     maxSize = current->getSize();
332                 }
333
334                 std::wstring Explicit = L"E";
335                 std::wstring Implicit = L"I";
336                 for (; i < maxSize; ++i)
337                 {
338                     if (current->get(i) == Implicit)
339                     {
340                         controller.setObjectProperty(ids[i], PORT, p, true);
341                     }
342                     else if (current->get(i) == Explicit)
343                     {
344                         controller.setObjectProperty(ids[i], PORT, p, false);
345                     }
346                     else
347                     {
348                         std::string adapter = adapterName<p>(port_kind);
349                         std::string field = adapterFieldName<p>(port_kind);
350                         get_or_allocate_logger()->log(LOG_WARNING, _("Wrong value for field %s.%s: '%s' unrecognized, only expected '%s' or '%s' vector. Switching to '%s'.\n"), adapter.data(), field.data(), current->get(i), Explicit.c_str(), Implicit.c_str(), Explicit.c_str());
351                         controller.setObjectProperty(ids[i], PORT, p, false);
352                     }
353                 }
354                 for (i = maxSize; i < (int)ids.size(); ++i)
355                 {
356                     // Tag the missing ports as Explicit. This is done to fix the resizing of pin & pout.
357                     controller.setObjectProperty(ids[i], PORT, p, false);
358                 }
359                 return true;
360             }
361             default:
362                 std::string adapter = adapterName<p>(port_kind);
363                 std::string field = adapterFieldName<p>(port_kind);
364                 get_or_allocate_logger()->log(LOG_ERROR, _("Wrong type for field %s.%s.\n"), adapter.data(), field.data());
365                 return false;
366         }
367     }
368     else if (v->getType() == types::InternalType::ScilabDouble)
369     {
370         types::Double* current = v->getAs<types::Double>();
371
372         // Translate identifiers: shared variables
373         int i = 0;
374         size_t datatypeIndex = -1;
375         // Translate identifiers from values
376         switch (p)
377         {
378             case FIRING:
379                 if (current->isEmpty())
380                 {
381                     return true;
382                 }
383
384                 if (current->getSize() < static_cast<int>(ids.size()))
385                 {
386                     std::string adapter = adapterName<p>(port_kind);
387                     std::string field = adapterFieldName<p>(port_kind);
388                     get_or_allocate_logger()->log(LOG_ERROR, _("Wrong dimension for field %s.%s: %d-by-%d expected.\n"), adapter.data(), field.data(), ids.size(), 1);
389                     return false;
390                 }
391
392                 for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
393                 {
394                     double firing = current->get(i);
395
396                     controller.setObjectProperty(*it, PORT, p, firing);
397                 }
398                 return true;
399             case STYLE:
400             case LABEL:
401                 // Do nothing, because if the sizes match, then there are already zero concerned ports, so no ports to update
402                 return true;
403
404             case DATATYPE_TYPE:
405                 datatypeIndex++;
406             // no break
407             case DATATYPE_COLS:
408                 datatypeIndex++;
409             // no break
410             case DATATYPE_ROWS:
411             {
412                 datatypeIndex++;
413
414                 // ignore the set without error
415                 if (current->getSize() != static_cast<int>(ids.size()))
416                 {
417                     return true;
418                 }
419
420                 for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
421                 {
422                     std::vector<int> newDataType;
423                     controller.getObjectProperty(*it, PORT, DATATYPE, newDataType);
424
425                     double data = current->get(i);
426                     if (std::floor(data) != data)
427                     {
428                         std::string adapter = adapterName<p>(port_kind);
429                         std::string field = adapterFieldName<p>(port_kind);
430                         get_or_allocate_logger()->log(LOG_ERROR, _("Wrong value for field %s.%s: Round number expected.\n"), adapter.data(), field.data());
431                         return false;
432                     }
433
434                     newDataType[datatypeIndex] = static_cast<int>(data);
435                     controller.setObjectProperty(*it, PORT, DATATYPE, newDataType);
436                 }
437                 return true;
438             }
439
440             case IMPLICIT:
441                 // Do nothing, because if the sizes match, then there are already zero concerned ports, so no ports to update
442                 return true;
443
444             default:
445                 std::string adapter = adapterName<p>(port_kind);
446                 std::string field = adapterFieldName<p>(port_kind);
447                 get_or_allocate_logger()->log(LOG_ERROR, _("Wrong type for field %s.%s .\n"), adapter.data(), field.data());
448                 return false;
449         }
450
451     }
452     else if (v->getType() == types::InternalType::ScilabBool)
453     {
454         switch (p)
455         {
456             case FIRING:
457                 // firing=%f is interpreted as "no initial event on the corresponding port", so set a negative value.
458                 for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it)
459                 {
460                     controller.setObjectProperty(*it, PORT, p, -1);
461                 }
462                 return true;
463             default:
464                 std::string adapter = adapterName<p>(port_kind);
465                 std::string field = adapterFieldName<p>(port_kind);
466                 get_or_allocate_logger()->log(LOG_ERROR, _("Wrong type for field %s.%s .\n"), adapter.data(), field.data());
467                 return false;
468         }
469     }
470     std::string adapter = adapterName<p>(port_kind);
471     std::string field = adapterFieldName<p>(port_kind);
472     get_or_allocate_logger()->log(LOG_ERROR, _("Wrong type for field %s.%s .\n"), adapter.data(), field.data());
473     return false;
474 }
475
476 /**
477  * Fill \a newPorts with \a d values checking content if possible.
478  *
479  * \param newPorts new ports children's value to be filled
480  * \param d the C-array values to set
481  * \return true on success, false otherwise
482  */
483 inline void fillNewPorts(std::deque<int>& newPorts, const double* d)
484 {
485     for (std::deque<int>::iterator it = newPorts.begin(); it != newPorts.end(); ++it, ++d)
486     {
487         *it = static_cast<int>(*d);
488     }
489 }
490
491 /**
492  * Set the port value
493  *
494  * \param oldPortObject the old port object
495  * \param controller current transaction instance
496  * \param children all object in the current layer (diagram or superblock)
497  */
498 template<object_properties_t p>
499 inline bool updateNewPort(model::Port* oldPortObject, int newPort, Controller& controller)
500 {
501     // update the p property, using newPort as a value
502     int datatypeIndex = -1;
503     switch (p)
504     {
505         case DATATYPE_TYPE:
506             datatypeIndex++;
507         // no break
508         case DATATYPE_COLS:
509             datatypeIndex++;
510         // no break
511         case DATATYPE_ROWS:
512         {
513             datatypeIndex++;
514             std::vector<int> datatype;
515             controller.getObjectProperty(oldPortObject, DATATYPE, datatype);
516             datatype[datatypeIndex] = newPort;
517             return controller.setObjectProperty(oldPortObject, DATATYPE, datatype) != FAIL;
518         }
519         default:
520             return controller.setObjectProperty(oldPortObject, p, newPort) != FAIL;
521     }
522 }
523
524 /**
525  * Add a new port
526  *
527  * \param newPortObject the old port object
528  * \param newPort new port children's index or value
529  * \param controller current transaction instance
530  * \return true on success, false otherwise
531  */
532 template<object_properties_t p>
533 inline bool addNewPort(model::Port* newPortObject, int newPort, Controller& controller)
534 {
535     // set the requested property, using newPort as a value
536     int datatypeIndex = -1;
537     switch (p)
538     {
539         case DATATYPE_TYPE:
540             datatypeIndex++;
541         // no break
542         case DATATYPE_COLS:
543             datatypeIndex++;
544         // no break
545         case DATATYPE_ROWS:
546         {
547             datatypeIndex++;
548             std::vector<int> datatype;
549             controller.getObjectProperty(newPortObject, DATATYPE, datatype);
550             datatype[datatypeIndex] = newPort;
551             return controller.setObjectProperty(newPortObject, DATATYPE, datatype) != FAIL;
552         }
553         default:
554             return controller.setObjectProperty(newPortObject, p, newPort) != FAIL;
555     }
556 }
557
558 /**
559  * Update the ports with a specific property.
560  *
561  * Create ports if needed, remove ports if needed and set a default property on each port.
562  */
563 template<typename Adaptor, object_properties_t p>
564 bool update_ports_property(const Adaptor& adaptor, const object_properties_t port_kind, Controller& controller, types::InternalType* v)
565 {
566     model::Block* adaptee = adaptor.getAdaptee();
567
568     if (v->getType() != types::InternalType::ScilabDouble)
569     {
570         std::string adapter = adapterName<p>(port_kind);
571         std::string field = adapterFieldName<p>(port_kind);
572         get_or_allocate_logger()->log(LOG_ERROR, _("Wrong type for field %s.%s: Real matrix expected.\n"), adapter.data(), field.data());
573         return false;
574     }
575     types::Double* value = v->getAs<types::Double>();
576
577     ScicosID parentBlock;
578     controller.getObjectProperty(adaptee, PARENT_BLOCK, parentBlock);
579     ScicosID parentDiagram;
580     controller.getObjectProperty(adaptee, PARENT_DIAGRAM, parentDiagram);
581
582     std::vector<ScicosID> children;
583     if (parentBlock != ScicosID())
584     {
585         // Adding to a superblock
586         controller.getObjectProperty(parentBlock, BLOCK, CHILDREN, children);
587     }
588     else
589     {
590         // Adding to a diagram
591         controller.getObjectProperty(parentDiagram, DIAGRAM, CHILDREN, children);
592     }
593
594     std::deque<int> newPorts (value->getSize());
595
596     // retrieve old data
597     std::vector<ScicosID> previousPorts;
598     controller.getObjectProperty(adaptee, port_kind, previousPorts);
599     std::deque<model::Port*> oldPorts;
600     for (ScicosID pre : previousPorts)
601     {
602         oldPorts.emplace_back(controller.getBaseObject<model::Port>(pre));
603     }
604
605     double* d = value->getReal();
606     fillNewPorts(newPorts, d);
607
608     std::vector<model::BaseObject*> deletedObjects;
609
610     // updated ports
611     while (!oldPorts.empty() && !newPorts.empty())
612     {
613         model::Port* oldPort = oldPorts.front();
614         oldPorts.pop_front();
615         int newPort = newPorts.front();
616         newPorts.pop_front();
617
618         if (!updateNewPort<p>(oldPort, newPort, controller))
619         {
620             std::string adapter = adapterName<p>(port_kind);
621             std::string field = adapterFieldName<p>(port_kind);
622             get_or_allocate_logger()->log(LOG_ERROR, _("Wrong value for field %s.%s: FIXME port has not been updated.\n"), adapter.data(), field.data(), 1, children.size());
623             return false;
624         }
625     }
626
627     // removed ports
628     if (!oldPorts.empty())
629     {
630         previousPorts.erase(previousPorts.end() - oldPorts.size(), previousPorts.end());
631
632         while (!oldPorts.empty())
633         {
634             model::Port* oldPort = oldPorts.front();
635             oldPorts.pop_front();
636
637             ScicosID signal;
638             controller.getObjectProperty(oldPort, CONNECTED_SIGNALS, signal);
639             if (signal != ScicosID())
640             {
641                 model::Link* signalObject = controller.getBaseObject<model::Link>(signal);
642
643                 // the link is connected, disconnect the other side
644                 ScicosID oldSignalSrc;
645                 controller.getObjectProperty(signalObject, SOURCE_PORT, oldSignalSrc);
646                 ScicosID oldSignalDst;
647                 controller.getObjectProperty(signalObject, DESTINATION_PORT, oldSignalDst);
648
649                 ScicosID unconnected = 0;
650                 if (oldSignalSrc == oldPort->id())
651                 {
652                     controller.setObjectProperty(oldSignalDst, PORT, CONNECTED_SIGNALS, unconnected);
653                 }
654                 else     // oldSignalDst == oldPort
655                 {
656                     controller.setObjectProperty(oldSignalSrc, PORT, CONNECTED_SIGNALS, unconnected);
657                 }
658
659                 children.erase(std::find(children.begin(), children.end(), signalObject->id()));
660                 deletedObjects.push_back(signalObject);
661             }
662
663             deletedObjects.push_back(oldPort);
664         }
665
666         controller.setObjectProperty(adaptee, port_kind, previousPorts);
667     }
668
669     // added ports
670     if (!newPorts.empty())
671     {
672         while (!newPorts.empty())
673         {
674             int newPort = newPorts.front();
675             newPorts.pop_front();
676
677             model::Port* createdPort = controller.createBaseObject<model::Port>(PORT);
678             controller.setObjectProperty(createdPort, SOURCE_BLOCK, adaptee->id());
679             switch (port_kind)
680             {
681                 case INPUTS:
682                     controller.setObjectProperty(createdPort, PORT_KIND, static_cast<int>(PORT_IN));
683                     break;
684                 case OUTPUTS:
685                     controller.setObjectProperty(createdPort, PORT_KIND, static_cast<int>(PORT_OUT));
686                     break;
687                 case EVENT_INPUTS:
688                     controller.setObjectProperty(createdPort, PORT_KIND, static_cast<int>(PORT_EIN));
689                     break;
690                 case EVENT_OUTPUTS:
691                     controller.setObjectProperty(createdPort, PORT_KIND, static_cast<int>(PORT_EOUT));
692                     break;
693                 default:
694                     // should never happen
695                     assert(!"Not managed kind of port");
696                     return false;
697             }
698             addNewPort<p>(createdPort, newPort, controller);
699             previousPorts.push_back(createdPort->id());
700         }
701
702         controller.setObjectProperty(adaptee, port_kind, previousPorts);
703     }
704
705     // remove objects from the model after de-association
706     for (auto o : deletedObjects)
707     {
708         controller.deleteBaseObject(o);
709     }
710
711     return true;
712 }
713
714 } /* namespace view_scilab */
715 } /* namespace org_scilab_modules_scicos */
716
717 #endif /* PORTS_MANAGEMENT_HXX_ */