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"
42 #include "../model/stateinstance/StateInstance.h"
43 #include "GraphvizConverter.h"
44 #include "Layout.h"
45 
46 
47 #define INITIAL_TRANSITION_NAME ""
48 const 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 
70 size_t
72 {
73  return id;
74 }
75 
76 void
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 
99  emit substateFound(substate, statechartmodel::eAdded);
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 
116  stateChanged(statechartmodel::eAdded);
117  emit scheduleMe(id, true);
118 }
119 
120 void
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 
153 void
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 
211 void
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 
265 void
267 {
268  // ARMARX_INFO_S << "Layouting " << armarx::LocalException::generateBacktrace();
269  counter--;
270  layoutRepetition = counter;
271  emit layout(layoutAll);
272 }
273 
274 QRectF
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 
285 void
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 
305 void
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 
327 bool
328 armarx::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 
422 QPointF
423 armarx::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 
434 float
435 armarx::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 
446 QPointF
447 armarx::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 
457 void
458 armarx::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 
490 void
491 armarx::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 
509 void
510 armarx::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 
529 std::string
530 armarx::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 
547 bool
548 armarx::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 
564 std::string
565 armarx::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 
583 armarx::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!
598 armarx::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 
617 void
618 armarx::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 
647 void
648 armarx::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 
664 void
665 armarx::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 
683 std::string
684 armarx::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 
701 bool
702 armarx::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!
720 armarx::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 
773 bool
774 armarx::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 
854 void
855 armarx::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 
894 void
895 armarx::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 
907 void
908 armarx::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 
955 void
956 armarx::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 
970 void
971 armarx::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 
999 void
1000 armarx::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 
1068 bool
1069 armarx::StateModelLayoutMediator::transitionRepresentable(
1071 {
1072  return (transition->sourceState && transition->destinationState) ||
1073  (!transition->sourceState && transition->destinationState);
1074 }
armarx::StateModelLayoutMediator::workerFinishedLayouting
void workerFinishedLayouting()
workerFinishedLayouting To be called when the worker is finished layouting.
Definition: StateModelLayoutMediator.cpp:286
GraphvizConverter.h
armarx::LockableGraphPtr
std::shared_ptr< LockableGraph > LockableGraphPtr
Definition: Layout.h:64
armarx::StateModelLayoutMediator::transitionChanged
void transitionChanged(statechartmodel::TransitionCPtr transition, statechartmodel::SignalType signalType)
transitionAdded To be called when a transition of state has changed.
Definition: StateModelLayoutMediator.cpp:212
armarx::FloatPtr
std::shared_ptr< float > FloatPtr
Definition: Transition.h:39
armarx::GraphvizConverter::convertToSpline
static SupportPoints convertToSpline(const std::string &graphVizSplineType)
Definition: GraphvizConverter.cpp:179
armarx::StateModelLayoutMediator::getState
statechartmodel::StatePtr getState() const
getState Returns the state associated with this mediator.
Definition: StateModelLayoutMediator.cpp:65
armarx::statechartmodel::eUnchanged
@ eUnchanged
Definition: SignalType.h:36
armarx::StateModelLayoutMediator::buildUpGraph
void buildUpGraph(LockableGraphPtr graph)
buildUpGraph To be called when the corresponding worker initialized the graph.
Definition: StateModelLayoutMediator.cpp:77
armarx::StateModelLayoutMediator::substateChanged
void substateChanged(statechartmodel::StateInstancePtr substate, statechartmodel::SignalType signalType)
substateAdded To be called when a substate of state has changed.
Definition: StateModelLayoutMediator.cpp:154
StateModelLayoutMediator.h
armarx::StateModelLayoutMediator::stateDeleted
void stateDeleted()
stateDeleted To be called when the corresponding state is deleted.
Definition: StateModelLayoutMediator.cpp:306
armarx::statechartmodel::StateInstancePtr
std::shared_ptr< StateInstance > StateInstancePtr
Definition: StateInstance.h:146
armarx::QPointPtr
std::shared_ptr< QPointF > QPointPtr
Definition: Transition.h:40
armarx::StateModelLayoutMediator::startLayouting
void startLayouting(bool layoutAll, int counter)
layout Tells the worker to layout edges and, if layoutAlgo is true, nodes of the graph.
Definition: StateModelLayoutMediator.cpp:266
Layout.h
armarx::GetHandledExceptionString
std::string GetHandledExceptionString()
Definition: Exception.cpp:165
cxxopts::value
std::shared_ptr< Value > value()
Definition: cxxopts.hpp:855
ARMARX_ERROR_S
#define ARMARX_ERROR_S
Definition: Logging.h:216
armarx::GraphvizConverter::convertToRectangle
static QRectF convertToRectangle(const std::string &graphvizPoint)
Definition: GraphvizConverter.cpp:146
armarx::StateModelLayoutMediator::StateModelLayoutMediator
StateModelLayoutMediator(statechartmodel::StatePtr state, size_t mediator_id)
LayoutWorker Sets state and id as specified.
Definition: StateModelLayoutMediator.cpp:50
DotDefaultDPI
const qreal DotDefaultDPI
Definition: StateModelLayoutMediator.cpp:48
max
T max(T t1, T t2)
Definition: gdiam.h:51
armarx::statechartmodel::eAdded
@ eAdded
Definition: SignalType.h:35
boost::source
Vertex source(const detail::edge_base< Directed, Vertex > &e, const PCG &)
Definition: point_cloud_graph.h:661
armarx::statechartmodel::eActivated
@ eActivated
Definition: SignalType.h:38
ARMARX_WARNING_S
#define ARMARX_WARNING_S
Definition: Logging.h:213
armarx::StateModelLayoutMediator::getID
size_t getID() const
getID Returns this mediator's id.
Definition: StateModelLayoutMediator.cpp:71
ARMARX_CHECK_EXPRESSION
#define ARMARX_CHECK_EXPRESSION(expression)
This macro evaluates the expression and if it turns out to be false it will throw an ExpressionExcept...
Definition: ExpressionException.h:73
armarx::StateModelLayoutMediator::stateChanged
void stateChanged(statechartmodel::SignalType signalType)
Definition: StateModelLayoutMediator.cpp:121
armarx::statechartmodel::eRemoved
@ eRemoved
Definition: SignalType.h:40
ArmarXWidgetController.h
armarx::NodePtr
Agnode_t * NodePtr
Definition: Layout.h:42
armarx::statechartmodel::SignalType
SignalType
The SignalType enum.
Definition: SignalType.h:33
armarx::statechartmodel::TransitionCPtr
std::shared_ptr< const Transition > TransitionCPtr
Definition: Transition.h:91
armarx::statechartmodel::StatePtr
std::shared_ptr< State > StatePtr
Definition: State.h:48
armarx::statechartmodel::eChanged
@ eChanged
Definition: SignalType.h:37
armarx::EdgePtr
Agedge_t * EdgePtr
Definition: Layout.h:43
armarx::StateModelLayoutMediator::boundingRectForAgraph
QRectF boundingRectForAgraph(Agraph_t *graph) const
Definition: StateModelLayoutMediator.cpp:275
ARMARX_INFO_S
#define ARMARX_INFO_S
Definition: Logging.h:202
Logging.h
ARMARX_WARNING
#define ARMARX_WARNING
Definition: Logging.h:193
armarx::statechartmodel::StateCPtr
std::shared_ptr< const State > StateCPtr
Definition: XmlWriter.h:45
INITIAL_TRANSITION_NAME
#define INITIAL_TRANSITION_NAME
Definition: StateModelLayoutMediator.cpp:47