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