Xcos: "Region to superblock" fixed
[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                     v[i] = 0;
259                 }
260                 else
261                 {
262                     std::vector<ScicosID>::iterator found = std::find(children.begin(), children.end(), id);
263                     if (found != children.end())
264                     {
265                         v[i] = static_cast<double>(std::distance(children.begin(), found)) + 1;
266                     }
267                     else
268                     {
269                         // connected link not found ; discard it !
270                         v[i] = 0;
271                     }
272                 }
273             }
274             return o;
275         }
276         default:
277             return 0;
278     }
279 }
280
281 /*
282  * Set a Scilab encoded values as a property.
283  *
284  * \note this method will ignore or return false if one of the ports does not exist, depending on the property set.
285  */
286 template<typename Adaptor, object_properties_t p>
287 bool set_ports_property(const Adaptor& adaptor, const object_properties_t port_kind, Controller& controller, types::InternalType* v)
288 {
289     model::Block* adaptee = adaptor.getAdaptee();
290
291     // Retrieve the ports identifiers
292     std::vector<ScicosID> ids;
293     controller.getObjectProperty(adaptee, port_kind, ids);
294
295     if (v->getType() == types::InternalType::ScilabString)
296     {
297         types::String* current = v->getAs<types::String>();
298
299         // Translate identifiers: shared variables
300         int i = 0;
301         // Translate identifiers from values
302         switch (p)
303         {
304             case STYLE:
305             case LABEL:
306             {
307                 for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
308                 {
309                     char* c_str = NULL;
310                     if (i >= current->getSize())
311                     {
312                         // If the input isn't large enough, fill each port with empty strings
313                         c_str = wide_string_to_UTF8(L"");
314                     }
315                     else
316                     {
317                         c_str = wide_string_to_UTF8(current->get(i));
318                     }
319                     controller.setObjectProperty(*it, PORT, p, std::string(c_str));
320                     FREE(c_str);
321                 }
322                 return true;
323             }
324             case IMPLICIT:
325             {
326                 int maxSize = static_cast<int>(ids.size());
327                 if (current->getSize() < maxSize)
328                 {
329                     maxSize = current->getSize();
330                 }
331
332                 std::wstring Explicit = L"E";
333                 std::wstring Implicit = L"I";
334                 for (; i < maxSize; ++i)
335                 {
336                     if (current->get(i) == Implicit)
337                     {
338                         controller.setObjectProperty(ids[i], PORT, p, true);
339                     }
340                     else if (current->get(i) == Explicit)
341                     {
342                         controller.setObjectProperty(ids[i], PORT, p, false);
343                     }
344                     else
345                     {
346                         std::string adapter = adapterName<p>(port_kind);
347                         std::string field = adapterFieldName<p>(port_kind);
348                         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());
349                         controller.setObjectProperty(ids[i], PORT, p, false);
350                     }
351                 }
352                 for (i = maxSize; i < (int)ids.size(); ++i)
353                 {
354                     // Tag the missing ports as Explicit. This is done to fix the resizing of pin & pout.
355                     controller.setObjectProperty(ids[i], PORT, p, false);
356                 }
357                 return true;
358             }
359             default:
360                 std::string adapter = adapterName<p>(port_kind);
361                 std::string field = adapterFieldName<p>(port_kind);
362                 get_or_allocate_logger()->log(LOG_ERROR, _("Wrong type for field %s.%s.\n"), adapter.data(), field.data());
363                 return false;
364         }
365     }
366     else if (v->getType() == types::InternalType::ScilabDouble)
367     {
368         types::Double* current = v->getAs<types::Double>();
369
370         // Translate identifiers: shared variables
371         int i = 0;
372         size_t datatypeIndex = -1;
373         // Translate identifiers from values
374         switch (p)
375         {
376             case FIRING:
377                 if (current->isEmpty())
378                 {
379                     return true;
380                 }
381
382                 if (current->getSize() < static_cast<int>(ids.size()))
383                 {
384                     std::string adapter = adapterName<p>(port_kind);
385                     std::string field = adapterFieldName<p>(port_kind);
386                     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);
387                     return false;
388                 }
389
390                 for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
391                 {
392                     double firing = current->get(i);
393
394                     controller.setObjectProperty(*it, PORT, p, firing);
395                 }
396                 return true;
397             case STYLE:
398             case LABEL:
399                 // Do nothing, because if the sizes match, then there are already zero concerned ports, so no ports to update
400                 return true;
401
402             case DATATYPE_TYPE:
403                 datatypeIndex++;
404             // no break
405             case DATATYPE_COLS:
406                 datatypeIndex++;
407             // no break
408             case DATATYPE_ROWS:
409             {
410                 datatypeIndex++;
411
412                 // ignore the set without error
413                 if (current->getSize() != static_cast<int>(ids.size()))
414                 {
415                     return true;
416                 }
417
418                 for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
419                 {
420                     std::vector<int> newDataType;
421                     controller.getObjectProperty(*it, PORT, DATATYPE, newDataType);
422
423                     double data = current->get(i);
424                     if (std::floor(data) != data)
425                     {
426                         std::string adapter = adapterName<p>(port_kind);
427                         std::string field = adapterFieldName<p>(port_kind);
428                         get_or_allocate_logger()->log(LOG_ERROR, _("Wrong value for field %s.%s: Round number expected.\n"), adapter.data(), field.data());
429                         return false;
430                     }
431
432                     newDataType[datatypeIndex] = static_cast<int>(data);
433                     controller.setObjectProperty(*it, PORT, DATATYPE, newDataType);
434                 }
435                 return true;
436             }
437
438             case IMPLICIT:
439                 // Do nothing, because if the sizes match, then there are already zero concerned ports, so no ports to update
440                 return true;
441
442             default:
443                 std::string adapter = adapterName<p>(port_kind);
444                 std::string field = adapterFieldName<p>(port_kind);
445                 get_or_allocate_logger()->log(LOG_ERROR, _("Wrong type for field %s.%s .\n"), adapter.data(), field.data());
446                 return false;
447         }
448
449     }
450     else if (v->getType() == types::InternalType::ScilabBool)
451     {
452         switch (p)
453         {
454             case FIRING:
455                 // firing=%f is interpreted as "no initial event on the corresponding port", so set a negative value.
456                 for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it)
457                 {
458                     controller.setObjectProperty(*it, PORT, p, -1);
459                 }
460                 return true;
461             default:
462                 std::string adapter = adapterName<p>(port_kind);
463                 std::string field = adapterFieldName<p>(port_kind);
464                 get_or_allocate_logger()->log(LOG_ERROR, _("Wrong type for field %s.%s .\n"), adapter.data(), field.data());
465                 return false;
466         }
467     }
468     std::string adapter = adapterName<p>(port_kind);
469     std::string field = adapterFieldName<p>(port_kind);
470     get_or_allocate_logger()->log(LOG_ERROR, _("Wrong type for field %s.%s .\n"), adapter.data(), field.data());
471     return false;
472 }
473
474 /**
475  * Fill \a newPorts with \a d values checking content if possible.
476  *
477  * \param newPorts new ports children's value to be filled
478  * \param d the C-array values to set
479  * \return true on success, false otherwise
480  */
481 inline void fillNewPorts(std::deque<int>& newPorts, const double* d)
482 {
483     for (std::deque<int>::iterator it = newPorts.begin(); it != newPorts.end(); ++it, ++d)
484     {
485         *it = static_cast<int>(*d);
486     }
487 }
488
489 /**
490  * Set the port value
491  *
492  * \param oldPortObject the old port object
493  * \param controller current transaction instance
494  * \param children all object in the current layer (diagram or superblock)
495  */
496 template<object_properties_t p>
497 inline bool updateNewPort(model::Port* oldPortObject, int newPort, Controller& controller)
498 {
499     // update the p property, using newPort as a value
500     int datatypeIndex = -1;
501     switch (p)
502     {
503         case DATATYPE_TYPE:
504             datatypeIndex++;
505         // no break
506         case DATATYPE_COLS:
507             datatypeIndex++;
508         // no break
509         case DATATYPE_ROWS:
510         {
511             datatypeIndex++;
512             std::vector<int> datatype;
513             controller.getObjectProperty(oldPortObject, DATATYPE, datatype);
514             datatype[datatypeIndex] = newPort;
515             return controller.setObjectProperty(oldPortObject, DATATYPE, datatype) != FAIL;
516         }
517         case CONNECTED_SIGNALS:
518             return controller.setObjectProperty(oldPortObject, p, (ScicosID) newPort) != FAIL;
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         case CONNECTED_SIGNALS:
554             return controller.setObjectProperty(newPortObject, p, (ScicosID) newPort) != FAIL;
555         default:
556             return controller.setObjectProperty(newPortObject, p, newPort) != FAIL;
557     }
558 }
559
560 /**
561  * Update the ports with a specific property.
562  *
563  * Create ports if needed, remove ports if needed and set a default property on each port.
564  */
565 template<typename Adaptor, object_properties_t p>
566 bool update_ports_property(const Adaptor& adaptor, const object_properties_t port_kind, Controller& controller, types::InternalType* v)
567 {
568     model::Block* adaptee = adaptor.getAdaptee();
569
570     if (v->getType() != types::InternalType::ScilabDouble)
571     {
572         std::string adapter = adapterName<p>(port_kind);
573         std::string field = adapterFieldName<p>(port_kind);
574         get_or_allocate_logger()->log(LOG_ERROR, _("Wrong type for field %s.%s: Real matrix expected.\n"), adapter.data(), field.data());
575         return false;
576     }
577     types::Double* value = v->getAs<types::Double>();
578
579     ScicosID parentBlock;
580     controller.getObjectProperty(adaptee, PARENT_BLOCK, parentBlock);
581     ScicosID parentDiagram;
582     controller.getObjectProperty(adaptee, PARENT_DIAGRAM, parentDiagram);
583
584     std::vector<ScicosID> children;
585     if (parentBlock != ScicosID())
586     {
587         // Adding to a superblock
588         controller.getObjectProperty(parentBlock, BLOCK, CHILDREN, children);
589     }
590     else
591     {
592         // Adding to a diagram
593         controller.getObjectProperty(parentDiagram, DIAGRAM, CHILDREN, children);
594     }
595
596     std::deque<int> newPorts (value->getSize());
597
598     // retrieve old data
599     std::vector<ScicosID> previousPorts;
600     controller.getObjectProperty(adaptee, port_kind, previousPorts);
601     std::deque<model::Port*> oldPorts;
602     for (ScicosID pre : previousPorts)
603     {
604         oldPorts.emplace_back(controller.getBaseObject<model::Port>(pre));
605     }
606
607     double* d = value->getReal();
608     fillNewPorts(newPorts, d);
609
610     std::vector<model::BaseObject*> deletedObjects;
611
612     // updated ports
613     while (!oldPorts.empty() && !newPorts.empty())
614     {
615         model::Port* oldPort = oldPorts.front();
616         oldPorts.pop_front();
617         int newPort = newPorts.front();
618         newPorts.pop_front();
619
620         if (!updateNewPort<p>(oldPort, newPort, controller))
621         {
622             std::string adapter = adapterName<p>(port_kind);
623             std::string field = adapterFieldName<p>(port_kind);
624             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());
625             return false;
626         }
627     }
628
629     // removed ports
630     if (!oldPorts.empty())
631     {
632         previousPorts.erase(previousPorts.end() - oldPorts.size(), previousPorts.end());
633
634         while (!oldPorts.empty())
635         {
636             model::Port* oldPort = oldPorts.front();
637             oldPorts.pop_front();
638
639             ScicosID signal;
640             controller.getObjectProperty(oldPort, CONNECTED_SIGNALS, signal);
641             if (signal != ScicosID())
642             {
643                 model::Link* signalObject = controller.getBaseObject<model::Link>(signal);
644
645                 // the link is connected, disconnect the other side
646                 ScicosID oldSignalSrc;
647                 controller.getObjectProperty(signalObject, SOURCE_PORT, oldSignalSrc);
648                 ScicosID oldSignalDst;
649                 controller.getObjectProperty(signalObject, DESTINATION_PORT, oldSignalDst);
650
651                 ScicosID unconnected = 0;
652                 if (oldSignalSrc == oldPort->id())
653                 {
654                     controller.setObjectProperty(oldSignalDst, PORT, CONNECTED_SIGNALS, unconnected);
655                 }
656                 else     // oldSignalDst == oldPort
657                 {
658                     controller.setObjectProperty(oldSignalSrc, PORT, CONNECTED_SIGNALS, unconnected);
659                 }
660
661                 children.erase(std::find(children.begin(), children.end(), signalObject->id()));
662                 deletedObjects.push_back(signalObject);
663             }
664
665             deletedObjects.push_back(oldPort);
666         }
667
668         controller.setObjectProperty(adaptee, port_kind, previousPorts);
669     }
670
671     // added ports
672     if (!newPorts.empty())
673     {
674         while (!newPorts.empty())
675         {
676             int newPort = newPorts.front();
677             newPorts.pop_front();
678
679             model::Port* createdPort = controller.createBaseObject<model::Port>(PORT);
680             controller.setObjectProperty(createdPort, SOURCE_BLOCK, adaptee->id());
681             switch (port_kind)
682             {
683                 case INPUTS:
684                     controller.setObjectProperty(createdPort, PORT_KIND, static_cast<int>(PORT_IN));
685                     break;
686                 case OUTPUTS:
687                     controller.setObjectProperty(createdPort, PORT_KIND, static_cast<int>(PORT_OUT));
688                     break;
689                 case EVENT_INPUTS:
690                     controller.setObjectProperty(createdPort, PORT_KIND, static_cast<int>(PORT_EIN));
691                     break;
692                 case EVENT_OUTPUTS:
693                     controller.setObjectProperty(createdPort, PORT_KIND, static_cast<int>(PORT_EOUT));
694                     break;
695                 default:
696                     // should never happen
697                     assert(!"Not managed kind of port");
698                     return false;
699             }
700             addNewPort<p>(createdPort, newPort, controller);
701             previousPorts.push_back(createdPort->id());
702         }
703
704         controller.setObjectProperty(adaptee, port_kind, previousPorts);
705     }
706
707     // remove objects from the model after de-association
708     for (auto o : deletedObjects)
709     {
710         controller.deleteBaseObject(o);
711     }
712
713     return true;
714 }
715
716 } /* namespace view_scilab */
717 } /* namespace org_scilab_modules_scicos */
718
719 #endif /* PORTS_MANAGEMENT_HXX_ */