* Bug 16365 fixed: median(m,'r'|'c') was wrong after 5dc990
[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, const std::vector<ScicosID>& children)
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             if (0 <= newPort && newPort < children.size())
519             {
520                 return controller.setObjectProperty(oldPortObject, p,
521                                                     children[newPort]) != FAIL;
522             }
523             else
524             {
525                 return false;
526             }
527         default:
528             return controller.setObjectProperty(oldPortObject, p, newPort) != FAIL;
529     }
530 }
531
532 /**
533  * Add a new port
534  *
535  * \param newPortObject the old port object
536  * \param newPort new port children's index or value
537  * \param controller current transaction instance
538  * \return true on success, false otherwise
539  */
540 template<object_properties_t p>
541 inline bool addNewPort(model::Port* newPortObject, int newPort, Controller& controller)
542 {
543     // set the requested property, using newPort as a value
544     int datatypeIndex = -1;
545     switch (p)
546     {
547         case DATATYPE_TYPE:
548             datatypeIndex++;
549         // no break
550         case DATATYPE_COLS:
551             datatypeIndex++;
552         // no break
553         case DATATYPE_ROWS:
554         {
555             datatypeIndex++;
556             std::vector<int> datatype;
557             controller.getObjectProperty(newPortObject, DATATYPE, datatype);
558             datatype[datatypeIndex] = newPort;
559             return controller.setObjectProperty(newPortObject, DATATYPE, datatype) != FAIL;
560         }
561         case CONNECTED_SIGNALS:
562             return controller.setObjectProperty(newPortObject, p, (ScicosID) newPort) != FAIL;
563         default:
564             return controller.setObjectProperty(newPortObject, p, newPort) != FAIL;
565     }
566 }
567
568 /**
569  * Update the ports with a specific property.
570  *
571  * Create ports if needed, remove ports if needed and set a default property on each port.
572  */
573 template<typename Adaptor, object_properties_t p>
574 bool update_ports_property(const Adaptor& adaptor, const object_properties_t port_kind, Controller& controller, types::InternalType* v)
575 {
576     model::Block* adaptee = adaptor.getAdaptee();
577
578     if (v->getType() != types::InternalType::ScilabDouble)
579     {
580         std::string adapter = adapterName<p>(port_kind);
581         std::string field = adapterFieldName<p>(port_kind);
582         get_or_allocate_logger()->log(LOG_ERROR, _("Wrong type for field %s.%s: Real matrix expected.\n"), adapter.data(), field.data());
583         return false;
584     }
585     types::Double* value = v->getAs<types::Double>();
586
587     ScicosID parentBlock;
588     controller.getObjectProperty(adaptee, PARENT_BLOCK, parentBlock);
589     ScicosID parentDiagram;
590     controller.getObjectProperty(adaptee, PARENT_DIAGRAM, parentDiagram);
591
592     std::vector<ScicosID> children;
593     if (parentBlock != ScicosID())
594     {
595         // Adding to a superblock
596         controller.getObjectProperty(parentBlock, BLOCK, CHILDREN, children);
597     }
598     else
599     {
600         // Adding to a diagram
601         controller.getObjectProperty(parentDiagram, DIAGRAM, CHILDREN, children);
602     }
603
604     std::deque<int> newPorts (value->getSize());
605
606     // retrieve old data
607     std::vector<ScicosID> previousPorts;
608     controller.getObjectProperty(adaptee, port_kind, previousPorts);
609     std::deque<model::Port*> oldPorts;
610     for (ScicosID pre : previousPorts)
611     {
612         oldPorts.emplace_back(controller.getBaseObject<model::Port>(pre));
613     }
614
615     double* d = value->getReal();
616     fillNewPorts(newPorts, d);
617
618     std::vector<model::BaseObject*> deletedObjects;
619
620     // updated ports
621     while (!oldPorts.empty() && !newPorts.empty())
622     {
623         model::Port* oldPort = oldPorts.front();
624         oldPorts.pop_front();
625         int newPort = newPorts.front();
626         newPorts.pop_front();
627
628         if (!updateNewPort<p>(oldPort, newPort, controller, children))
629         {
630             return false;
631         }
632     }
633
634     // removed ports
635     if (!oldPorts.empty())
636     {
637         previousPorts.erase(previousPorts.end() - oldPorts.size(), previousPorts.end());
638
639         while (!oldPorts.empty())
640         {
641             model::Port* oldPort = oldPorts.front();
642             oldPorts.pop_front();
643
644             ScicosID signal;
645             controller.getObjectProperty(oldPort, CONNECTED_SIGNALS, signal);
646             if (signal != ScicosID())
647             {
648                 model::Link* signalObject = controller.getBaseObject<model::Link>(signal);
649
650                 // the link is connected, disconnect the other side
651                 ScicosID oldSignalSrc;
652                 controller.getObjectProperty(signalObject, SOURCE_PORT, oldSignalSrc);
653                 ScicosID oldSignalDst;
654                 controller.getObjectProperty(signalObject, DESTINATION_PORT, oldSignalDst);
655
656                 ScicosID unconnected = 0;
657                 if (oldSignalSrc == oldPort->id())
658                 {
659                     controller.setObjectProperty(oldSignalDst, PORT, CONNECTED_SIGNALS, unconnected);
660                 }
661                 else     // oldSignalDst == oldPort
662                 {
663                     controller.setObjectProperty(oldSignalSrc, PORT, CONNECTED_SIGNALS, unconnected);
664                 }
665
666                 children.erase(std::find(children.begin(), children.end(), signalObject->id()));
667                 deletedObjects.push_back(signalObject);
668             }
669
670             deletedObjects.push_back(oldPort);
671         }
672
673         controller.setObjectProperty(adaptee, port_kind, previousPorts);
674     }
675
676     // added ports
677     if (!newPorts.empty())
678     {
679         while (!newPorts.empty())
680         {
681             int newPort = newPorts.front();
682             newPorts.pop_front();
683
684             model::Port* createdPort = controller.createBaseObject<model::Port>(PORT);
685             controller.setObjectProperty(createdPort, SOURCE_BLOCK, adaptee->id());
686             switch (port_kind)
687             {
688                 case INPUTS:
689                     controller.setObjectProperty(createdPort, PORT_KIND, static_cast<int>(PORT_IN));
690                     break;
691                 case OUTPUTS:
692                     controller.setObjectProperty(createdPort, PORT_KIND, static_cast<int>(PORT_OUT));
693                     break;
694                 case EVENT_INPUTS:
695                     controller.setObjectProperty(createdPort, PORT_KIND, static_cast<int>(PORT_EIN));
696                     break;
697                 case EVENT_OUTPUTS:
698                     controller.setObjectProperty(createdPort, PORT_KIND, static_cast<int>(PORT_EOUT));
699                     break;
700                 default:
701                     // should never happen
702                     assert(!"Not managed kind of port");
703                     return false;
704             }
705             addNewPort<p>(createdPort, newPort, controller);
706             previousPorts.push_back(createdPort->id());
707         }
708
709         controller.setObjectProperty(adaptee, port_kind, previousPorts);
710     }
711
712     // remove objects from the model after de-association
713     for (const auto& o : deletedObjects)
714     {
715         controller.deleteBaseObject(o);
716     }
717
718     return true;
719 }
720
721 } /* namespace view_scilab */
722 } /* namespace org_scilab_modules_scicos */
723
724 #endif /* PORTS_MANAGEMENT_HXX_ */