StateModelLayoutMediator.cpp
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * Copyright (C) 2011-2016, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved.
5 *
6 * ArmarX is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * ArmarX is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * @package
19 * @author
20 * @date
21 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
22 * GNU General Public License
23 */
25
26#include <cassert> //for testing only
27#include <map>
28#include <memory>
29#include <mutex>
30#include <string>
31
32#include <QApplication>
33#include <QDesktopWidget>
34#include <QSizeF>
35
37
39
40#include "../model/State.h"
41#include "../model/Transition.h"
43#include "GraphvizConverter.h"
44#include "Layout.h"
45
46
47#define INITIAL_TRANSITION_NAME ""
48const qreal DotDefaultDPI = 72;
49
51 size_t mediator_id) :
52 isConnectedToWorker(false), id(mediator_id), m_state(state)
53{
54 dpi.setWidth(qApp->desktop()->logicalDpiX());
55 dpi.setHeight(qApp->desktop()->logicalDpiY());
56 dpi.setWidth(92);
57 dpi.setHeight(92);
58
59 initialTransition = NULL;
60 invisibleStartNode = NULL;
61 m_graph = std::make_shared<LockableGraph>();
62}
63
66{
67 return m_state;
68}
69
70size_t
72{
73 return id;
74}
75
76void
78{
79 if (!graph || !graph->graph)
80 {
81 ARMARX_WARNING_S << "Graph pointer is NULL!";
82 return;
83 }
84
85 if (!m_state)
86 {
87 ARMARX_WARNING_S << "state pointer is NULL!";
88 return;
89 }
90
91 m_graph = graph;
92 isConnectedToWorker = true;
93
94 //add all substates to the graph
95 foreach (statechartmodel::StateInstancePtr substate, m_state->getSubstates())
96 {
97 addNode(substate);
98
100 }
101 statechartmodel::StateCPtr state = m_state;
102 statechartmodel::TransitionCPtr init = state->getStartTransition();
103 if (init)
104 {
105 updateInitialTransition(init, statechartmodel::eAdded);
106 }
107
108
109 //add all transitions
110 for (statechartmodel::TransitionCPtr transition : m_state->getTransitions())
111 {
112 addEdge(transition);
113 }
114
115
117 emit scheduleMe(id, true);
118}
119
120void
122{
123 return; // State changes do not matter at the moment
124 /** comment to prevent warnings
125 if (!isConnectedToWorker)
126 {
127 return;
128 }
129
130
131 switch (signalType)
132 {
133 case (statechartmodel::eUnchanged):
134 case (statechartmodel::eActivated):
135 //nothing to do
136 return;
137 case (statechartmodel::eAdded):
138 case (statechartmodel::eRemoved):
139 case (statechartmodel::eChanged):
140 // auto sizeString = GraphvizConverter::convertFromPoint(QPointF(m_state->getSize().width() / dpi.width(), m_state->getSize().height() / dpi.height()));
141 ARMARX_INFO_S << "state changed for" << m_state->getStateName();
142 // ARMARX_INFO_S << "Graphviz graph size: " << sizeString;
143 // setGraphAttribute(m_graph->graph, "size", sizeString);
144 // setGraphAttribute(m_graph->graph, "ratio", m_state->getSize().height() / m_state->getSize().width());
145 default:
146 ARMARX_WARNING << "CASE NOT HANDLED " << signalType;
147 }
148
149 emit scheduleMe(id, true);
150 */
151}
152
153void
157{
158 if (!isConnectedToWorker)
159 {
160 return;
161 }
162
163 bool layoutAll = false;
164
165 switch (signalType)
166 {
168 {
169 addNode(substate);
170 ARMARX_INFO_S << "new substate " << substate->getInstanceName();
171 layoutAll = true;
172 }
173 break;
174
176 {
177 ARMARX_INFO_S << "removed substate " << substate->getInstanceName();
178 removeNode(substate);
179 }
180 break;
181
184 //nothing to do
185 return;
186
188 {
189 // ARMARX_INFO_S << substate->getInstanceName() << " changed ";
190 //herausfinden ob Name geändert, ggf. Name im Graph ändern
191 updateNodeName(substate);
192 //herausfinden ob Position geändert, wenn ja neue Pos im Graph eintragen
193 if (!updateNodePositionAndDimension(substate))
194 {
195 return;
196 }
197 //TODO: Muss noch irgendeine andere Änderung beachtet werden?
198 // ARMARX_INFO_S << "substate changed for" << substate->getInstanceName();
199 ARMARX_INFO_S << "state changed " << substate->getInstanceName();
200 }
201 break;
202 default:
203 {
204 ARMARX_WARNING << "CASE NOT HANDLED " << signalType;
205 }
206 }
207
208 emit scheduleMe(id, layoutAll);
209}
210
211void
215{
216 if (!isConnectedToWorker)
217 {
218 return;
219 }
220
221 if (!transition->sourceState && transition->destinationState)
222 {
223 if (!updateInitialTransition(transition, signalType))
224 {
225 return; //No layouting need
226 }
227 }
228 else
229 {
230 switch (signalType)
231 {
233 addEdge(transition);
234 break;
235
237 removeEdge(transition);
238 break;
239
242 //nothing to do
243 return;
244
246 // ARMARX_INFO_S << transition->eventName << " changed ";
247
248 //mapping changed -> not interesting for us
249 //event changed
250 updateEdgeLabel(transition);
251 //target/source state changed
252 updateEdgeTargetAndSource(transition);
253 //support point changed/added/removed
254 updateEdgePosition(transition);
255 // ARMARX_INFO_S << "transition changed for" << m_state->getStateName();
256 break;
257 default:
258 ARMARX_WARNING << "CASE NOT HANDLED " << signalType;
259 }
260 }
261
262 emit scheduleMe(id, false);
263}
264
265void
267{
268 // ARMARX_INFO_S << "Layouting " << armarx::LocalException::generateBacktrace();
269 counter--;
270 layoutRepetition = counter;
271 emit layout(layoutAll);
272}
273
274QRectF
276{
277 // const qreal dpi = 96;
278 const qreal left = GD_bb(graph).LL.x * (dpi.width() / DotDefaultDPI);
279 const qreal top = (GD_bb(graph).UR.y - GD_bb(graph).LL.y) * (dpi.height() / DotDefaultDPI);
280 const qreal right = GD_bb(graph).UR.x * (dpi.width() / DotDefaultDPI);
281 const qreal bottom = (GD_bb(graph).UR.y - GD_bb(graph).UR.y) * (dpi.height() / DotDefaultDPI);
282 return QRectF(left, top, right - left, bottom - top);
283}
284
285void
287{
288 // const qreal dpi = 96;
289 graphVizBB = GraphvizConverter::convertToRectangle(getGraphAttribute(m_graph->graph, "bb"));
290 // ARMARX_INFO_S << graphVizBB.width() << "," << graphVizBB.height();
291 m_state->setSubstateAreaSize(QSizeF(graphVizBB.width() * (dpi.width() / DotDefaultDPI),
292 graphVizBB.height() * (dpi.height() / DotDefaultDPI)));
293 broadcastSubstatePositions();
294 broadcastTransitionSupportPoints();
295 if (layoutRepetition > 0)
296 {
297 startLayouting(false, layoutRepetition);
298 }
299 else
300 {
301 emit layoutingFinished();
302 }
303}
304
305void
307{
308 //disconnect some slots to ensure that no queueing or layouting or graph changing is done after
309 //the state was deleted
310 disconnect(
312 disconnect(
314 disconnect(SLOT(startLayouting(bool)));
315
316 //ensure that controller is not stuck waiting for our worker to finish although this worker is
317 //deleted
318 emit layoutingFinished();
319
320 //tell worker to let itself be deleted by the LayoutWorkerCreator
321 emit mediatorDeleted();
322
323 //tell controller to delete this mediator
324 emit deleteMe(id);
325}
326
327bool
328armarx::StateModelLayoutMediator::updateInitialTransition(
331{
332 std::unique_lock lock(m_graph->mutex);
333 if (transition->sourceState)
334 {
335 return false;
336 }
337 if (!transition->destinationState)
338 {
339 return false;
340 }
341
342 auto addFakeStartNode = [&, this]()
343 {
344 if (!invisibleStartNode)
345 {
346 invisibleStartNode = agnode(m_graph->graph, const_cast<char*>(""), TRUE);
347 std::string posString = converter.convertFromPoint(QPointF(0, 0));
348 setGraphAttribute(invisibleStartNode, "pos", posString + "!");
349 setGraphAttribute(invisibleStartNode, "shape", "circle");
350 setGraphAttribute(invisibleStartNode, "width", "0.4");
351 setGraphAttribute(invisibleStartNode, "height", "0.4");
352 }
353 };
354
355 switch (signalType)
356 {
358 {
359 addFakeStartNode();
360
361 auto destinationNode = getNode(transition->destinationState);
362 if (!initialTransition && invisibleStartNode && destinationNode)
363 {
364 initialTransition = agedge(m_graph->graph,
365 invisibleStartNode,
366 destinationNode,
367 const_cast<char*>(INITIAL_TRANSITION_NAME),
368 TRUE);
369 }
370 }
371
372 break;
373
375 if (invisibleStartNode)
376 {
377 agdelete(m_graph->graph, invisibleStartNode);
378 }
379
380 if (initialTransition)
381 {
382 agdelete(m_graph->graph, initialTransition);
383 }
384
385 initialTransition = NULL;
386 invisibleStartNode = NULL;
387 break;
388
391 //nothing to do
392 return false;
393
395 {
396 //mapping changed -> not interesting for us
397 //event changed
398 addFakeStartNode();
399
400 if (initialTransition)
401 {
402 agdelete(m_graph->graph, initialTransition);
403 initialTransition = NULL;
404 }
405 auto node = getNode(transition->destinationState);
406 if (!node)
407 {
408 return false;
409 }
411 << "could not find node for " << transition->destinationState->getInstanceName();
412 initialTransition =
413 agedge(m_graph->graph, invisibleStartNode, node, const_cast<char*>("NULL"), TRUE);
414 }
415 break;
416 default:
417 ARMARX_WARNING << "CASE NOT HANDLED " << signalType;
418 }
419 return true;
420}
421
422QPointF
423armarx::StateModelLayoutMediator::convertPositionFromGraphViz(QPointF gvPos) const
424{
425 float fac = convertScaleFromGraphViz();
426
427 gvPos.setY(graphVizBB.height() -
428 gvPos.y()); // graphviz's y value starts at bottom, so invert it
429 gvPos /= fac;
430 gvPos += m_state->margin.topLeft();
431 return gvPos;
432}
433
434float
435armarx::StateModelLayoutMediator::convertScaleFromGraphViz() const
436{
437 return DotDefaultDPI / dpi.width();
438 const QSizeF& size = getState()->getSize();
439
440 float widthFac = graphVizBB.width() / (size.width() - m_state->margin.left());
441 float heightFac = graphVizBB.height() / (size.height() - m_state->margin.top());
442 float fac = std::max(widthFac, heightFac);
443 return fac;
444}
445
446QPointF
447armarx::StateModelLayoutMediator::convertPositionToGraphViz(QPointF statePos) const
448{
449 float fac = convertScaleFromGraphViz();
450
451 statePos -= m_state->margin.topLeft();
452 statePos *= fac;
453 statePos.setY(graphVizBB.height() - statePos.y());
454 return statePos;
455}
456
457void
458armarx::StateModelLayoutMediator::addNode(armarx::statechartmodel::StateInstancePtr substate)
459{
460 std::unique_lock lock(m_graph->mutex);
461
462 //Sometimes the eAdded signal for a node reaches the mediator after an eChanged signal. Test whether
463 //there already is a node for the given substate and only add one if there isn't one already.
464 NodePtr node = getNode(substate);
465
466 if (!node)
467 {
468 std::string name;
469
470 if (!substate->getInstanceName().isEmpty())
471 {
472 name = substate->getInstanceName().toStdString();
473 }
474 else if (substate->getStateClass())
475 {
476 name = substate->getStateClass()->getStateName().toStdString();
477 }
478 else
479 {
480 name = "NoStateClass";
481 }
482
483 agnode(m_graph->graph, const_cast<char*>(name.c_str()), TRUE);
484 substateMap.insert(
485 std::pair<statechartmodel::StateInstancePtr, std::string>(substate, name));
486 updateNodePositionAndDimension(substate);
487 }
488}
489
490void
491armarx::StateModelLayoutMediator::removeNode(armarx::statechartmodel::StateInstancePtr substate)
492{
493 std::unique_lock lock(m_graph->mutex);
494
495 NodePtr node = getNode(substate);
496 if (!substate || !substate->getStateClass())
497 {
498 return;
499 }
500
501 //remove node and all adjacent edges from the graph
502 if (node)
503 {
504 ARMARX_INFO_S << "Removing node " << substate->getInstanceName();
505 agdelete(m_graph->graph, node);
506 }
507}
508
509void
510armarx::StateModelLayoutMediator::setNodeAttribute(
512 const std::string& attributeName,
513 const std::string& attributeValue)
514{
515 std::unique_lock lock(m_graph->mutex);
516
517 NodePtr node = getNode(substate);
518
519 if (!node)
520 {
521 ARMARX_WARNING_S << "Node null for state " << substate->getInstanceName()
522 << " - Could not set attribute";
523 return;
524 }
525
526 setGraphAttribute(node, attributeName, attributeValue);
527}
528
529std::string
530armarx::StateModelLayoutMediator::getNodeAttribute(
532 const std::string& attributeName)
533{
534 std::unique_lock lock(m_graph->mutex);
535
536 NodePtr node = getNode(substate);
537
538 if (!node)
539 {
540 return std::string();
541 }
542
543 std::string value = getGraphAttribute(node, attributeName);
544 return value;
545}
546
547bool
548armarx::StateModelLayoutMediator::hasNodeAttribute(
550 const std::string& attributeName)
551{
552 std::unique_lock lock(m_graph->mutex);
553
554 NodePtr node = getNode(substate);
555
556 if (!node)
557 {
558 return false;
559 }
560
561 return hasGraphAttribute(node, attributeName);
562}
563
564std::string
565armarx::StateModelLayoutMediator::getNodeName(armarx::NodePtr node)
566{
567 if (!node)
568 {
569 return std::string();
570 }
571
572 if (node->base.data->name)
573 {
574 return node->base.data->name;
575 }
576 else
577 {
578 return "";
579 }
580}
581
583armarx::StateModelLayoutMediator::getNode(const std::string& name)
584{
585 NodePtr node;
586 node = agnode(m_graph->graph, const_cast<char*>(name.c_str()), TRUE);
587
588 if (!node)
589 {
590 node = NULL;
591 }
592
593 return node;
594}
595
596//not thread-safe!! Always make sure to lock m_graph->mutex before calling this function!
598armarx::StateModelLayoutMediator::getNode(armarx::statechartmodel::StateInstancePtr substate)
599{
600 NodeMap::iterator iter = substateMap.find(substate);
601
602 if (iter != substateMap.end())
603 {
604 NodePtr node = NULL;
605 node = getNode(iter->second);
606
607 if (!node)
608 {
609 ARMARX_ERROR_S << "substateMap contains reference to non-exixtant node";
610 }
611 return node;
612 }
613
614 return NULL;
615}
616
617void
618armarx::StateModelLayoutMediator::addEdge(armarx::statechartmodel::TransitionCPtr transition)
619{
620 std::unique_lock lock(m_graph->mutex);
621
622 if (!transitionRepresentable(transition))
623 {
624 return;
625 }
626
627 NodePtr source = getNode(transition->sourceState);
628 NodePtr destination = getNode(transition->destinationState);
629 std::string name = transition->eventName.toStdString();
630
631 if (destination && source)
632 {
633 //add edge to graph
634 EdgePtr edge =
635 agedge(m_graph->graph, source, destination, const_cast<char*>(name.c_str()), TRUE);
636 setGraphAttribute(edge, const_cast<char*>("label"), const_cast<char*>(name.c_str()));
637
638 //add edge to transitionList
639 transitionList.append(transition);
640 }
641 else
642 {
643 ARMARX_ERROR_S << "The nodes requested for this transition don't exist in the graph";
644 }
645}
646
647void
648armarx::StateModelLayoutMediator::removeEdge(armarx::statechartmodel::TransitionCPtr transition)
649{
650 std::unique_lock lock(m_graph->mutex);
651
652 EdgePtr edge = getEdge(transition);
653
654 //remove from transitionList
655 transitionList.removeAll(transition);
656
657 //remove from graph
658 if (edge)
659 {
660 agdelete(m_graph->graph, edge);
661 }
662}
663
664void
665armarx::StateModelLayoutMediator::setEdgeAttribute(
667 std::string attributeName,
668 std::string attributeValue)
669{
670 std::unique_lock lock(m_graph->mutex);
671
672 EdgePtr edge = getEdge(transition);
673
674 if (!edge)
675 {
676 return;
677 }
678
679 agset(
680 edge, const_cast<char*>(attributeName.c_str()), const_cast<char*>(attributeValue.c_str()));
681}
682
683std::string
684armarx::StateModelLayoutMediator::getEdgeAttribute(
686 std::string attributeName)
687{
688 std::unique_lock lock(m_graph->mutex);
689
690 EdgePtr edge = getEdge(transition);
691
692 if (!edge)
693 {
694 return std::string();
695 }
696
697 std::string value{getGraphAttribute(edge, attributeName)};
698 return value;
699}
700
701bool
702armarx::StateModelLayoutMediator::hasEdgeAttribute(
704 std::string attributeName)
705{
706 std::unique_lock lock(m_graph->mutex);
707
708 EdgePtr edge = getEdge(transition);
709
710 if (!edge)
711 {
712 return false;
713 }
714
715 return hasGraphAttribute(edge, attributeName);
716}
717
718//not thread-safe!! Always make sure to lock m_graph->mutex before calling this function!
720armarx::StateModelLayoutMediator::getEdge(armarx::statechartmodel::TransitionCPtr transition)
721{
722 EdgePtr edge = NULL;
723
724 if (transitionRepresentable(transition))
725 {
726 QString transName = transition->eventName;
727 std::string name;
728
729 if (!(transName.isEmpty()))
730 {
731 name = transName.toStdString();
732 }
733
734 NodePtr sourceNode = getNode(transition->sourceState);
735 NodePtr destNode = getNode(transition->destinationState);
736
737 if (sourceNode)
738 {
739 //iterate over all edges incident to sourceNode
740 for (EdgePtr e = agfstedge(m_graph->graph, sourceNode); e;
741 e = agnxtedge(m_graph->graph, e, sourceNode))
742 {
743 std::string currentName{getGraphAttribute(e, "label")};
744
745 //test whether the name fits and whether it is an out-edge of sourceNode
746 if ((currentName == name) && (agtail(e) == sourceNode))
747 {
748 edge = e;
749 break;
750 }
751 }
752 }
753 else if (destNode) // for initial transition
754 {
755 for (EdgePtr e = agfstedge(m_graph->graph, destNode); e;
756 e = agnxtedge(m_graph->graph, e, destNode))
757 {
758 std::string currentName{getGraphAttribute(e, "label")};
759
760 //test whether the name fits and whether it is an out-edge of sourceNode
761 if ((currentName == name) && (aghead(e) == destNode))
762 {
763 edge = e;
764 break;
765 }
766 }
767 }
768 }
769
770 return edge;
771}
772
773bool
774armarx::StateModelLayoutMediator::updateNodePositionAndDimension(
776{
777 //TODO: Conversion from Qt positions to graphviz positions and sizes neccessary?
778 bool changed = false;
779 auto size = substate->getClassSize() * substate->getScale();
780 QPointF middlePosition = convertPositionToGraphViz(substate->getTopLeft() +
781 QPointF(size.width(), size.height()) * 0.5);
782 if (hasNodeAttribute(substate, "pos"))
783 {
784 auto posString = getNodeAttribute(substate, "pos");
785 if (posString.empty())
786 {
787 changed |= true;
788 }
789 else
790 {
791 auto l = (converter.convertToPoint(posString) - middlePosition).manhattanLength();
792 changed |= l > 3;
793 }
794 }
795 else
796 {
797 changed = true;
798 }
799
800
801 auto posString = converter.convertFromPoint(middlePosition);
802 setNodeAttribute(substate, "pos", posString + "!");
803 setNodeAttribute(substate, (char*)"shape", (char*)"box");
804 //change node dimensions
805
806 // if (substate->getStateClass())
807 {
808 // float multiplicator = sqrt(substate->getStateClass()->getSubstates().size());
809 // multiplicator = std::min(4.f, multiplicator);
810 size = substate->getClassSize() * substate->getScale();
811 // ARMARX_INFO_S << substate->getInstanceName() << ": " << VAROUT(multiplicator) << VAROUT(size);
812 }
813 // else
814 // {
815
816 // size = QSizeF(substate->getBoundingSquareSize(), substate->getBoundingSquareSize());
817 // // ARMARX_INFO_S << "no state class for " << substate->getInstanceName();
818 // }
819
820
821 if (hasNodeAttribute(substate, "width"))
822 {
823 auto oldWidthString = getNodeAttribute(substate, "width");
824 auto ow = size.width() / dpi.width();
825 changed |=
826 oldWidthString.empty() || fabs(converter.convertToFloat(oldWidthString) - ow) > 0.01;
827 }
828 else
829 {
830 changed = true;
831 }
832 std::string widthString{converter.convertFromFloat(size.width() / dpi.width())};
833 setNodeAttribute(substate, "width", widthString);
834
835 if (hasNodeAttribute(substate, "height"))
836 {
837 auto oldheightString = getNodeAttribute(substate, "height");
838 changed |= oldheightString.empty() || fabs(converter.convertToFloat(oldheightString) -
839 size.height() / dpi.height()) > 0.01;
840 }
841 else
842 {
843 changed = true;
844 }
845
846 std::string heightString{converter.convertFromFloat(size.height() / dpi.height())};
847 // ARMARX_INFO_S << m_graph->graph << ": " << substate->getInstanceName() << ": " << VAROUT(widthString) << VAROUT(heightString);
848 setNodeAttribute(substate, "height", heightString);
849
850 setNodeAttribute(substate, "pin", "true");
851 return changed;
852}
853
854void
855armarx::StateModelLayoutMediator::updateNodeName(armarx::statechartmodel::StateInstancePtr substate)
856{
857 std::string newName;
858
859 if (!substate->getInstanceName().isEmpty())
860 {
861 newName = substate->getInstanceName().toStdString();
862 }
863 else if (substate->getStateClass())
864 {
865 newName = substate->getStateClass()->getStateName().toStdString();
866 }
867 else
868 {
869 newName = "NoStateClass";
870 }
871
872 std::string oldName;
873 auto iter = substateMap.find(substate);
874
875 if (iter != substateMap.end())
876 {
877 oldName = iter->second;
878
879 if (newName != oldName)
880 {
881 std::unique_lock lock(m_graph->mutex);
882
883 NodePtr node = getNode(oldName);
884 setGraphAttribute(node, "label", newName);
885 }
886 }
887 else
888 {
889 ARMARX_ERROR_S << "The substate " << newName << " is not in the graph";
890 addNode(substate);
891 }
892}
893
894void
895armarx::StateModelLayoutMediator::updateEdgeLabel(
897{
898 std::string oldLabel{getEdgeAttribute(transition, "label")};
899 std::string newLabel{transition->eventName.toStdString()};
900
901 if ((!(oldLabel.empty())) && (oldLabel != newLabel))
902 {
903 setEdgeAttribute(transition, "label", newLabel);
904 }
905}
906
907void
908armarx::StateModelLayoutMediator::updateEdgeTargetAndSource(
910{
911 std::unique_lock lock(m_graph->mutex);
912
913 if (transitionRepresentable(transition))
914 {
915 //the transition can be represented in the graph
916 EdgePtr edge = getEdge(transition);
917
918 if (edge) // the transition has an edge
919 {
920 std::string oldSourceName = getNodeName(agtail(edge));
921 std::string oldDestinationName = getNodeName(aghead(edge));
922
923 if ((!oldSourceName.empty()) && (!oldDestinationName.empty()))
924 //the source and destination state have nodes
925 {
926 std::string newSourceName =
927 transition->sourceState->getInstanceName().toStdString();
928 std::string newDestName =
929 transition->destinationState->getInstanceName().toStdString();
930
931 if ((oldSourceName != newSourceName) || (oldDestinationName != newDestName))
932 //the names of the source and/or destination state changed
933 {
934 removeEdge(transition);
935 addEdge(transition);
936 }
937 }
938 else
939 {
940 ARMARX_ERROR_S << "Edge has invalid tail or head node";
941 }
942 }
943 else //the transition has no edge yet
944 {
945 addEdge(transition);
946 }
947 }
948 else
949 {
950 ARMARX_INFO_S << "Removing edge " << transition->eventName;
951 removeEdge(transition);
952 }
953}
954
955void
956armarx::StateModelLayoutMediator::updateEdgePosition(
958{
959 //TODO: wie wird Pfeilposition umgesetzt? Momentan werden alle support points als Kontrollpunkte
960 //angesehen
961 if (transitionRepresentable(transition))
962 {
963 SupportPoints spline;
964 spline = transition->supportPoints;
965 std::string posString = converter.convertFromSpline(spline);
966 setEdgeAttribute(transition, "pos", posString);
967 }
968}
969
970void
971armarx::StateModelLayoutMediator::broadcastSubstatePositions()
972{
973 // float scale = convertScaleFromGraphViz();
974
975 for (auto substate : substateMap)
976 {
977 try
978 {
979 std::string currentPosString = getNodeAttribute(substate.first, "pos");
980 // ARMARX_INFO_S << "Position of " << substate.second << ": " <<currentPosString;
981 std::string widthString = getNodeAttribute(substate.first, "width");
982 std::string heightString = getNodeAttribute(substate.first, "height");
983 float width = converter.convertToFloat(widthString) * dpi.width();
984 float height = converter.convertToFloat(heightString) * dpi.height();
985 QPointF position = converter.convertToPoint(currentPosString);
986 position = convertPositionFromGraphViz(position);
987 // ARMARX_INFO_S << "Position of " << substate.second << " in QT: " <<position;
988 statechartmodel::StateInstancePtr instance = substate.first;
989 instance->setBoundingBox(std::max(width, height)); // * (DotDefaultDPI / dpi.width()));
990 instance->setPosition(position - QPoint(width, height) * 0.5);
991 }
992 catch (...)
993 {
994 scheduleMe(getID(), true);
995 }
996 }
997}
998
999void
1000armarx::StateModelLayoutMediator::broadcastTransitionSupportPoints()
1001{
1002 auto tmpList = transitionList;
1003 if (initialTransition)
1004 {
1005 const auto* state = m_state.get();
1006 statechartmodel::TransitionCPtr t(state->getStartTransition());
1007 if (t)
1008 {
1009 tmpList.push_back(t);
1010 }
1011 }
1012 for (auto& transition : tmpList)
1013 {
1014 try
1015 {
1016
1017
1018 std::string currentSplineString = getEdgeAttribute(transition, "pos");
1019 SupportPoints supportPoints = GraphvizConverter::convertToSpline(currentSplineString);
1020
1021 for (auto& p : supportPoints.controlPoints)
1022 {
1023 p = convertPositionFromGraphViz(p);
1024 }
1025
1026 if (supportPoints.endPoint)
1027 {
1028 *supportPoints.endPoint = convertPositionFromGraphViz(*supportPoints.endPoint);
1029 }
1030
1031 if (supportPoints.startPoint)
1032 {
1033 *supportPoints.startPoint = convertPositionFromGraphViz(*supportPoints.startPoint);
1034 }
1035 QPointPtr labelCenter;
1036 if (hasEdgeAttribute(transition, "lp"))
1037 {
1038 std::string labelPosString = getEdgeAttribute(transition, "lp");
1039 if (!labelPosString.empty())
1040 {
1041 QPointF labelPosition = converter.convertToPoint(labelPosString);
1042 labelPosition = convertPositionFromGraphViz(labelPosition);
1043 labelCenter = std::make_shared<QPointF>(labelPosition);
1044 }
1045 }
1046 FloatPtr labelFontPointSize;
1047 if (hasEdgeAttribute(transition, "fontsize"))
1048 {
1049 std::string labelFontPointSizeString = getEdgeAttribute(transition, "fontsize");
1050 labelFontPointSize = std::make_shared<float>(
1051 converter.convertToFloat(labelFontPointSizeString) * 0.3);
1052 }
1053
1054 //TODO: ist toPointList richtig? sollten nicht eher nur controlPoints genommen werden?
1055 //wie geht View damit um, wenn start oder end point noch mit dazugegeben?
1056 //TODO: wie wird Pfeilposition umgesetzt?
1057 emit supportPointsChanged(
1058 transition, supportPoints.controlPointList(), labelCenter, labelFontPointSize);
1059 }
1060 catch (...)
1061 {
1062 ARMARX_INFO_S << "Exception during transition update:\n" << GetHandledExceptionString();
1063 emit scheduleMe(getID(), true);
1064 }
1065 }
1066}
1067
1068bool
1069armarx::StateModelLayoutMediator::transitionRepresentable(
1071{
1072 return (transition->sourceState && transition->destinationState) ||
1073 (!transition->sourceState && transition->destinationState);
1074}
#define INITIAL_TRANSITION_NAME
const qreal DotDefaultDPI
static QRectF convertToRectangle(const std::string &graphvizPoint)
static SupportPoints convertToSpline(const std::string &graphVizSplineType)
void stateChanged(statechartmodel::SignalType signalType)
void mediatorDeleted()
mediatorDeleted Tells worker that is has to be deleted.
std::string getGraphAttribute(ElementType *element, const std::string &attributeName) const
void buildUpGraph(LockableGraphPtr graph)
buildUpGraph To be called when the corresponding worker initialized the graph.
statechartmodel::StatePtr getState() const
getState Returns the state associated with this mediator.
void startLayouting(bool layoutAll, int counter)
layout Tells the worker to layout edges and, if layoutAlgo is true, nodes of the graph.
QRectF boundingRectForAgraph(Agraph_t *graph) const
void scheduleMe(size_t id, bool layoutAll)
scheduleMe To be called when its graph should be layouted.
void substateChanged(statechartmodel::StateInstancePtr substate, statechartmodel::SignalType signalType)
substateAdded To be called when a substate of state has changed.
void substateFound(statechartmodel::StateInstancePtr substate, statechartmodel::SignalType signalType)
substateFound To be emitted when a substate is found when building up the graph for the first time.
void layout(bool layoutAll)
layout Tells worker to layout the graph.
void transitionChanged(statechartmodel::TransitionCPtr transition, statechartmodel::SignalType signalType)
transitionAdded To be called when a transition of state has changed.
void layoutingFinished()
layoutingFinished Notifies that the latest layout task was finished.
void stateDeleted()
stateDeleted To be called when the corresponding state is deleted.
void workerFinishedLayouting()
workerFinishedLayouting To be called when the worker is finished layouting.
size_t getID() const
getID Returns this mediator's id.
void deleteMe(size_t id)
deleteMe To be emitted when this mediator and its worker ought to be deleted because their correspond...
StateModelLayoutMediator(statechartmodel::StatePtr state, size_t mediator_id)
LayoutWorker Sets state and id as specified.
#define ARMARX_CHECK_EXPRESSION(expression)
This macro evaluates the expression and if it turns out to be false it will throw an ExpressionExcept...
#define ARMARX_ERROR_S
The logging level for unexpected behaviour, that must be fixed.
Definition Logging.h:216
#define ARMARX_INFO_S
Definition Logging.h:202
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
#define ARMARX_WARNING_S
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:213
std::shared_ptr< State > StatePtr
Definition State.h:48
std::shared_ptr< StateInstance > StateInstancePtr
std::shared_ptr< const State > StateCPtr
Definition XmlWriter.h:45
std::shared_ptr< const Transition > TransitionCPtr
Definition Transition.h:91
SignalType
The SignalType enum.
Definition SignalType.h:34
std::string GetHandledExceptionString()
std::shared_ptr< QPointF > QPointPtr
Definition Transition.h:40
std::shared_ptr< LockableGraph > LockableGraphPtr
Definition Layout.h:64
Agnode_t * NodePtr
Definition Layout.h:42
Agedge_t * EdgePtr
Definition Layout.h:43
std::shared_ptr< float > FloatPtr
Definition Transition.h:39
Vertex source(const detail::edge_base< Directed, Vertex > &e, const PCG &)
std::shared_ptr< Value > value()
Definition cxxopts.hpp:855