Skip to content

Commit

Permalink
Merge pull request #1158 from virtualsatellite/feature/1044-state-mac…
Browse files Browse the repository at this point in the history
…hine-simulator

State Machine Simulator
PhilMFischer authored Oct 8, 2024

Verified

This commit was signed with the committer’s verified signature.
zner0L Lorenz Sieben
2 parents 21c05a9 + 6d831cc commit 9483b75
Showing 13 changed files with 1,393 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
/*******************************************************************************
* Copyright (c) 2008-2019 German Aerospace Center (DLR), Simulation and Software Technology, Germany.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package de.dlr.sc.virsat.model.extension.statemachines.statespace;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.CoreMatchers.hasItems;
import static org.hamcrest.CoreMatchers.not;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import org.eclipse.core.runtime.CoreException;
import org.junit.Before;
import org.junit.Test;

import de.dlr.sc.virsat.concept.unittest.util.test.AConceptProjectTestCase;
import de.dlr.sc.virsat.model.dvlm.concepts.Concept;
import de.dlr.sc.virsat.model.extension.statemachines.Activator;
import de.dlr.sc.virsat.model.extension.statemachines.model.AllowsConstraint;
import de.dlr.sc.virsat.model.extension.statemachines.model.ForbidsConstraint;
import de.dlr.sc.virsat.model.extension.statemachines.model.State;
import de.dlr.sc.virsat.model.extension.statemachines.model.StateMachine;
import de.dlr.sc.virsat.model.extension.statemachines.model.Transition;

public class StateSpaceExplorerTest extends AConceptProjectTestCase {

private abstract class StateMachineGen {
final StateMachine stateMachine;

StateMachineGen(String name) {
stateMachine = new StateMachine(concept);
stateMachine.setName(name);
}

public StateMachine getStateMachine() {
return stateMachine;
}

protected State state(String name) {
var s = new State(concept);
s.setName(name);
stateMachine.getStates().add(s);
return s;
}

protected State initialState(String name) {
var s = state(name);
stateMachine.setInitialState(s);
return s;
}

/**
* Add a transition to the state machine.
*/
protected Transition trans(State from, String name, State to) {
var t = new Transition(concept);
t.setName(name);
t.setStateFrom(from);
t.setStateTo(to);
stateMachine.getTransitions().add(t);
return t;
}

protected ForbidsConstraint forbids(State from, State to) {
var c = new ForbidsConstraint(concept);
c.setStateConstraining(from);
c.setStateInfluenced(to);
stateMachine.getConstraints().add(c);
return c;
}

protected AllowsConstraint requires(State from, State to) {
var c = new AllowsConstraint(concept);
c.setStateConstraining(from);
c.setStateInfluenced(to);
stateMachine.getConstraints().add(c);
return c;
}
}

private class SimpleStateMachine extends StateMachineGen {
final State off;
final State on;

/**
* Create a simple state machine which can only be turned on or off.
*/
SimpleStateMachine(String name) {
super(name);

off = initialState(name + "Off");
on = state(name + "On");

trans(off, "TurnOn", on);
trans(on, "TurnOff", off);
}
}

private class StandbyStateMachine extends StateMachineGen {
final State off;
final State standby;
final State on;

/**
* Create a state machine with three consecutive states.
*/
StandbyStateMachine(String name) {
super(name);

off = initialState(name + "Off");
standby = state(name + "Standby");
on = state(name + "On");

trans(off, "IntoStandby", standby);
trans(standby, "TurnOn", on);
trans(on, "TurnOff", standby);
trans(standby, "ShutOff", off);
}
}

public static final String CONCEPT_ID_STATEMACHINES = Activator.getPluginId();

private Concept concept;

@Before
public void setUp() throws CoreException {
super.setUp();
addEditingDomainAndRepository();
concept = loadConceptFromPlugin(CONCEPT_ID_STATEMACHINES);
}

@Test
public void testGetInitialStates() {
var first = new SimpleStateMachine("First");
var second = new SimpleStateMachine("Second");
var explorer = createStateSpaceExplorer(first, second);

assertThat(explorer.getInitialStates().get(0).getLocalStates(), hasItems(first.off, second.off));
}

@Test
public void testGetInitialStatesInvalid() {
var first = new SimpleStateMachine("First");
var second = new SimpleStateMachine("Second");
first.forbids(first.off, second.off);

var explorer = createStateSpaceExplorer(first, second);

assertTrue(explorer.getInitialStates().isEmpty());
}

@Test
public void testGetSuccessors() {
var first = new SimpleStateMachine("First");
var second = new SimpleStateMachine("Second");
var explorer = createStateSpaceExplorer(first, second);

var initState = explorer.getInitialStates().get(0);
var succStates = explorer.getSuccessors(initState).stream().map(t -> t.getTo().getLocalStates()).collect(Collectors.toList());

assertContainsState(succStates, first.on, second.off);
assertContainsState(succStates, first.off, second.on);
}

@Test
public void testGetReachableStates() {
var first = new SimpleStateMachine("First");
var second = new SimpleStateMachine("Second");
var reach = getReachableStates(first, second);

assertContainsState(reach, first.off, second.off);
assertContainsState(reach, first.on, second.off);
assertContainsState(reach, first.off, second.on);
assertContainsState(reach, first.on, second.on);
}

@Test
public void testForbidsConstraint() {
var first = new SimpleStateMachine("First");
var second = new SimpleStateMachine("Second");
first.forbids(first.on, second.on);

assertNotContainsState(getReachableStates(first, second), first.on, second.on);
}

@Test
public void testSimpleRequiresConstraint() {
var first = new SimpleStateMachine("First");
var second = new SimpleStateMachine("Second");
first.requires(first.on, second.on);

var reach = getReachableStates(first, second);

assertContainsState(reach, first.off, second.off);
assertContainsState(reach, first.off, second.on);
assertContainsState(reach, first.on, second.on);
assertNotContainsState(reach, first.on, second.off);
}

@Test
public void testDisjunctiveRequiresConstraint() {
var first = new SimpleStateMachine("First");
var second = new StandbyStateMachine("Second");
first.requires(first.on, second.standby);
first.requires(first.on, second.on);

var reach = getReachableStates(first, second);

assertContainsState(reach, first.on, second.standby);
assertContainsState(reach, first.on, second.on);
assertNotContainsState(reach, first.on, second.off);
}

@Test
public void testConjunctiveRequiresConstraint() {
var first = new SimpleStateMachine("First");
var second = new SimpleStateMachine("Second");
var third = new SimpleStateMachine("Third");
first.requires(first.on, second.on);
first.requires(first.on, third.on);

var reach = getReachableStates(first, second, third);

assertContainsState(reach, first.on, second.on, third.on);
assertNotContainsState(reach, first.on, second.off);
assertNotContainsState(reach, first.on, third.off);
}

@Test
public void testSystemStateConsistency() {
var first = new SimpleStateMachine("First");
var second = new SimpleStateMachine("Second");

var sseFirst = createStateSpaceExplorer(first);
var sseSecond = createStateSpaceExplorer(second);

var firstInit = sseFirst.getInitialStates().get(0);

assertThrows(IllegalArgumentException.class, () -> sseSecond.getSuccessors(firstInit));
}

private static StateSpaceExplorer createStateSpaceExplorer(StateMachineGen... gens) {
return new StateSpaceExplorer(Arrays.stream(gens).map(StateMachineGen::getStateMachine).collect(Collectors.toList()));
}

private static List<List<State>> getReachableStates(StateSpaceExplorer explorer) {
return explorer.getReachableStates().stream().map(StateSpaceExplorer.SystemState::getLocalStates).collect(Collectors.toList());
}

private static List<List<State>> getReachableStates(StateMachineGen... gens) {
return getReachableStates(createStateSpaceExplorer(gens));
}

private static void assertContainsState(List<List<State>> systemStates, State... localStates) {
assertThat(systemStates, hasItem(hasItems(localStates)));
}

private static void assertNotContainsState(List<List<State>> systemStates, State... localStates) {
assertThat(systemStates, not(hasItem(hasItems(localStates))));
}
}
11 changes: 11 additions & 0 deletions de.dlr.sc.virsat.model.extension.statemachines.ui/plugin.xml
Original file line number Diff line number Diff line change
@@ -579,6 +579,17 @@
id="de.dlr.sc.virsat.model.extension.statemachines.ui.transition.default"
level="0">
</LabelProvider>
</extension>
<extension
point="org.eclipse.ui.views">
<view
category="de.dlr.sc.virsat.project.ui.view.catgeory.virsat.core"
class="de.dlr.sc.virsat.model.extension.statemachines.ui.views.SimulatorView"
icon="resources/icons/de.dlr.sc.virsat.model.extension.statemachines.gif"
id="de.dlr.sc.virsat.model.extension.statemachines.ui.views.simulator"
name="State Machine Simulator"
restorable="true">
</view>
</extension>
<!-- Plugin.XML Protected Region End -->
</plugin>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -13,19 +13,34 @@
import org.eclipse.graphiti.dt.IDiagramTypeProvider;
import org.eclipse.graphiti.features.context.IDoubleClickContext;
import org.eclipse.graphiti.features.custom.ICustomFeature;
import org.eclipse.graphiti.mm.pictograms.PictogramElement;
import org.eclipse.graphiti.tb.ColorDecorator;
import org.eclipse.graphiti.mm.algorithms.GraphicsAlgorithm;
import org.eclipse.graphiti.mm.algorithms.styles.Point;
import org.eclipse.graphiti.tb.DefaultToolBehaviorProvider;
import org.eclipse.graphiti.tb.IDecorator;
import org.eclipse.graphiti.tb.IToolBehaviorProvider;
import org.eclipse.graphiti.util.ColorConstant;
import org.eclipse.graphiti.util.IColorConstant;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.ui.PlatformUI;

import de.dlr.sc.virsat.model.extension.statemachines.model.State;
import de.dlr.sc.virsat.model.extension.statemachines.model.Transition;
import de.dlr.sc.virsat.model.extension.statemachines.statespace.StateSpaceExplorer;
import de.dlr.sc.virsat.model.extension.statemachines.statespace.TraceState;
import de.dlr.sc.virsat.model.extension.statemachines.ui.diagram.features.StateMachineDoubleClickFeature;
import de.dlr.sc.virsat.model.extension.statemachines.ui.views.SimulatorView;

/**
* Implements the tool behavior provider to catch double click events
*
*/
public class StateMachineDiagramToolBehaviorProvider extends DefaultToolBehaviorProvider implements IToolBehaviorProvider {
public class StateMachineDiagramToolBehaviorProvider extends DefaultToolBehaviorProvider implements IToolBehaviorProvider {

private static final IColorConstant SELECTED_STATE_BACKGROUND = new ColorConstant(255, 255, 255);
private static final IColorConstant SELECTED_TRANSITION_FOREGROUND = new ColorConstant(255, 128, 0);

/**
* @param diagramTypeProvider
*/
@@ -58,4 +73,42 @@ public void placeRelativeTo(Point point, GraphicsAlgorithm ga) {
point.setY(peLocation.getY() + relY);
}

@Override
public IDecorator[] getDecorators(PictogramElement pe) {
var activeWorkbenchWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow();

if (activeWorkbenchWindow.getActivePage().findView(SimulatorView.ID) != null) {
var selectionService = activeWorkbenchWindow.getSelectionService();
var selection = selectionService.getSelection(SimulatorView.ID);

if (selection != null && selection instanceof IStructuredSelection) {
var featureProvider = getFeatureProvider();
var bo = featureProvider.getBusinessObjectForPictogramElement(pe);

for (var selectedElement : (IStructuredSelection) selection) {
if (selectedElement instanceof TraceState && bo instanceof State) {
var state = (State) bo;
var localStates = ((TraceState) selectedElement).getSystemState().getLocalStates();

if (localStates.contains(state)) {
var decorator = new ColorDecorator();
decorator.setBackgroundColor(SELECTED_STATE_BACKGROUND);
return new IDecorator[] { decorator };
}
} else if (selectedElement instanceof StateSpaceExplorer.SystemTransition && bo instanceof Transition) {
var transition = (Transition) bo;
var localTransitions = ((StateSpaceExplorer.SystemTransition) selectedElement).getLocalTransitions();

if (localTransitions.contains(transition)) {
var decorator = new ColorDecorator();
decorator.setForegroundColor(SELECTED_TRANSITION_FOREGROUND);
return new IDecorator[] { decorator };
}
}
}
}
}

return super.getDecorators(pe);
}
}
Original file line number Diff line number Diff line change
@@ -19,7 +19,6 @@

/**
* The StateMachine diagram type provider delivers all feature providers for StateMachine diagrams.
* @author muel_s8
*
*/
public class StateMachineDiagramTypeProvider extends AbstractDiagramTypeProvider {
Loading

0 comments on commit 9483b75

Please sign in to comment.