package sushi.application.pages.simulator; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior; import org.apache.wicket.ajax.markup.html.form.AjaxButton; import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn; import org.apache.wicket.extensions.markup.html.repeater.data.table.HeadersToolbar; import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn; import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn; import org.apache.wicket.extensions.markup.html.repeater.tree.table.TreeColumn; import org.apache.wicket.extensions.markup.html.tabs.AbstractTab; import org.apache.wicket.extensions.markup.html.tabs.ITab; import org.apache.wicket.markup.html.form.DropDownChoice; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.form.TextField; import org.apache.wicket.markup.html.panel.Panel; import org.apache.wicket.markup.repeater.Item; import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; import de.agilecoders.wicket.markup.html.bootstrap.tabs.Collapsible; import sushi.application.components.form.BlockingAjaxButton; import sushi.application.components.form.WarnOnExitForm; import sushi.application.components.table.SelectEntryPanel; import sushi.application.components.tree.SushiLabelTreeTable; import sushi.application.pages.AbstractSushiPage; import sushi.application.pages.correlation.AdvancedCorrelationPanel; import sushi.application.pages.correlation.SimpleCorrelationPanel; import sushi.application.pages.correlation.SimpleCorrelationWithRulesPanel; import sushi.application.pages.simulator.model.SimulationTreeTableElement; import sushi.application.pages.simulator.model.SimulationTreeTableExpansion; import sushi.application.pages.simulator.model.SimulationTreeTableExpansionModel; import sushi.application.pages.simulator.model.SimulationTreeTableProvider; import sushi.application.pages.simulator.model.SimulationTreeTableToModelConverter; import sushi.bpmn.decomposition.ANDComponent; import sushi.bpmn.decomposition.IPattern; import sushi.bpmn.decomposition.LoopComponent; import sushi.bpmn.decomposition.SequenceComponent; import sushi.bpmn.decomposition.XORComponent; import sushi.bpmn.element.AbstractBPMNElement; import sushi.bpmn.element.BPMNProcess; import sushi.bpmn.element.BPMNXORGateway; import sushi.event.SushiEventType; import sushi.event.attribute.SushiAttribute; import sushi.event.collection.SushiTree; import sushi.process.SushiProcess; import sushi.simulation.DerivationType; import sushi.simulation.SimulationUtils; import sushi.simulation.Simulator; import sushi.simulation.ValueRule; import sushi.util.Tuple; /** * Panel representing the content panel for the first tab. */ public class SimpleSimulationPanel extends SimulationPanel { private static final long serialVersionUID = -7896431319431474548L; private DropDownChoice<String> processSelect; private DropDownChoice<String> eventTypeSelect; private List<String> processNameList; private List<String> eventTypeAndPatternList = new ArrayList<String>(); private SushiProcess selectedProcess; private SushiLabelTreeTable<SimulationTreeTableElement<Object>, String> treeTable; private UnexpectedEventPanel unexpectedEventPanel; public SimpleSimulationPanel(String id, final AbstractSushiPage abstractSushiPage) { super(id, abstractSushiPage); createProcessList(); createEventTypeList(selectedProcess); buildMainLayout(); } private void buildMainLayout() { layoutForm = new WarnOnExitForm("layoutForm"); addProcessSelect(layoutForm); addEventTypeSelect(layoutForm); addButtons(layoutForm); createTreeTable(layoutForm); instanceNumberInput = new TextField<String>("instanceNumberInput", Model.of("")); layoutForm.add(instanceNumberInput); daysNumberInput = new TextField<String>("daysNumberInput", Model.of("")); layoutForm.add(daysNumberInput); add(layoutForm); addTabs(); } private void addButtons(Form<Void> layoutForm) { AjaxButton addButton = new AjaxButton("addButton", layoutForm) { private static final long serialVersionUID = 1L; @Override public void onSubmit(AjaxRequestTarget target, Form form) { if(treeTableProvider.getSelectedTreeTableElements().size() > 1){ abstractSushiPage.getFeedbackPanel().error("Please select only one value to add elements!"); abstractSushiPage.getFeedbackPanel().setVisible(true); target.add(abstractSushiPage.getFeedbackPanel()); } else if(!eventTypeSelect.getValue().isEmpty()){ //TODO: Element im TreeTableProvider hinzufügen String eventTypeSelectValue = eventTypeSelect.getChoices().get(Integer.parseInt(eventTypeSelect.getValue())); SimulationTreeTableElement<Object> treeTableElement; if(IPattern.contains(eventTypeSelectValue)){ AbstractBPMNElement component = null; if(eventTypeSelectValue.equals(IPattern.AND.value)){ component = new ANDComponent(null, null, null, null); } else if(eventTypeSelectValue.equals(IPattern.XOR.value)){ component = new XORComponent(null, null, null, null); } else if(eventTypeSelectValue.equals(IPattern.SEQUENCE.value)){ component = new SequenceComponent(null, null, null, null); } else if(eventTypeSelectValue.equals(IPattern.LOOP.value)){ component = new LoopComponent(null, null, null, null); } treeTableElement = new SimulationTreeTableElement<Object>(treeTableProvider.getNextID(), component); treeTableProvider.addTreeTableElement(treeTableElement); } else { SushiEventType eventType = SushiEventType.findByTypeName(eventTypeSelectValue); if(eventType != null){ treeTableElement = new SimulationTreeTableElement<Object>(treeTableProvider.getNextID(), eventType); treeTableProvider.addTreeTableElement(treeTableElement); SimulationTreeTableElement<Object> childTreeTableElement; for(SushiAttribute attribute : eventType.getValueTypes()){ childTreeTableElement = new SimulationTreeTableElement<Object>(treeTableProvider.getNextID(), attribute); treeTableProvider.addTreeTableElementWithParent(childTreeTableElement, treeTableElement); } } } advancedValuesPanel.refreshAttributeChoice(); target.add(advancedValuesPanel); unexpectedEventPanel.refreshUsedEventTypes(); target.add(unexpectedEventPanel); target.add(treeTable); } } }; layoutForm.add(addButton); AjaxButton editButton = new AjaxButton("editButton", layoutForm) { private static final long serialVersionUID = 1L; @Override public void onSubmit(AjaxRequestTarget target, Form form) { } }; layoutForm.add(editButton); AjaxButton deleteButton = new AjaxButton("deleteButton", layoutForm) { private static final long serialVersionUID = 1L; @Override public void onSubmit(AjaxRequestTarget target, Form form) { treeTableProvider.deleteSelectedEntries(); target.add(treeTable); } }; layoutForm.add(deleteButton); AjaxButton simulateButton = new BlockingAjaxButton("simulateButton", layoutForm) { private static final long serialVersionUID = 1L; @Override public void onSubmit(AjaxRequestTarget target, Form form) { super.onSubmit(target, form); String instanceNumber = instanceNumberInput.getValue(); int numberOfInstances; if(instanceNumber != null && !instanceNumber.isEmpty()){ numberOfInstances = Integer.parseInt(instanceNumberInput.getValue()); } else { numberOfInstances = 1; } String daysNumber = daysNumberInput.getValue(); int numberOfDays; if(daysNumber != null && !instanceNumber.isEmpty()){ numberOfDays = Integer.parseInt(daysNumberInput.getValue()); } else { numberOfDays = 1; } SushiTree<Object> modelTree = treeTableProvider.getModelAsTree(); Map<SushiAttribute, List<Serializable>> attributeValues = treeTableProvider.getAttributeValuesFromModel(); BPMNProcess model = new SimulationTreeTableToModelConverter().convertTreeToModel(modelTree); //TODO: wahrscheinlichkeiten auslesen Map<BPMNXORGateway, List<Tuple<AbstractBPMNElement, String>>> xorSplitsWithSuccessorProbabilityStrings = getProbabilityForXorSuccessors(model); System.out.println(xorSplitsWithSuccessorProbabilityStrings); Map<BPMNXORGateway, List<Tuple<AbstractBPMNElement, Integer>>> xorSplitsWithSuccessorProbabilities = SimulationUtils.convertProbabilityStrings(xorSplitsWithSuccessorProbabilityStrings); System.out.println(xorSplitsWithSuccessorProbabilities); Map<SushiEventType, String> eventTypesDurationStrings = treeTableProvider.getEventTypesWithDuration(); Map<AbstractBPMNElement, String> tasksDurationString = SimulationUtils.getBPMNElementsFromEventTypes(eventTypesDurationStrings, model); Map<SushiEventType, String> eventTypesDerivationStrings = treeTableProvider.getEventTypesWithDuration(); Map<AbstractBPMNElement, String> tasksDerivationString = SimulationUtils.getBPMNElementsFromEventTypes(eventTypesDerivationStrings, model); Map<SushiEventType, DerivationType> eventTypesDerivationTypes = treeTableProvider.getEventTypesWithDerivationType(); Map<AbstractBPMNElement, DerivationType> tasksDerivationTypes = SimulationUtils.getBPMNElementsFromEventTypes2(eventTypesDerivationTypes, model); SushiProcess process = SushiProcess.findByName(processSelect.getModelObject()).get(0); Simulator simulator = new Simulator(process, model, attributeValues, tasksDurationString, tasksDerivationString, tasksDerivationTypes, xorSplitsWithSuccessorProbabilities); simulator.addAdvancedValueRules(advancedValuesPanel.getValueRules()); simulator.simulate(numberOfInstances, numberOfDays); } private Map<BPMNXORGateway, List<Tuple<AbstractBPMNElement, String>>> getProbabilityForXorSuccessors(BPMNProcess model) { Map<BPMNXORGateway, List<Tuple<AbstractBPMNElement, SushiEventType>>> xorPathProbabilityEvents = SimulationUtils.getXORSplitsWithFollowingEventTypes(model); System.out.println(xorPathProbabilityEvents); Map<BPMNXORGateway, List<Tuple<AbstractBPMNElement, String>>> xorSuccessorsProbability = new HashMap<BPMNXORGateway, List<Tuple<AbstractBPMNElement, String>>>(); //TODO: eventTypes finden --> elternelement besuchen bis xor gefunden -> probability auslesen List<SimulationTreeTableElement<Object>> eventTypeElements = treeTableProvider.getEventTypeElements(); //Paare von Nachfolgeelementen des XOR-Splits und 1. folgendem eventTyp durchlaufen for(BPMNXORGateway xorGateway : xorPathProbabilityEvents.keySet()){ List<Tuple<AbstractBPMNElement, SushiEventType>> listOfTuples = xorPathProbabilityEvents.get(xorGateway); List<Tuple<AbstractBPMNElement, String>> oneXorSuccessors = new ArrayList<Tuple<AbstractBPMNElement, String>>(); for(Tuple<AbstractBPMNElement, SushiEventType> tuple : listOfTuples){ if(tuple.x != null){ SimulationTreeTableElement<Object> targetElement = findEventTypeElementWithEventType(eventTypeElements, tuple); while(!(targetElement.getParent().getContent() instanceof XORComponent)){ targetElement = targetElement.getParent(); } Tuple<AbstractBPMNElement, String> successorProbability = new Tuple<AbstractBPMNElement, String>(tuple.x, targetElement.getProbability()); oneXorSuccessors.add(successorProbability); xorSuccessorsProbability.put(xorGateway, oneXorSuccessors); } else{ //leerer Pfad } } } return xorSuccessorsProbability; } }; layoutForm.add(simulateButton); } private void createTreeTable(Form<Void> layoutForm) { List<IColumn<SimulationTreeTableElement<Object>, String>> columns = createColumns(); treeTable = new SushiLabelTreeTable<SimulationTreeTableElement<Object>, String>( "sequenceTree", columns, treeTableProvider, Integer.MAX_VALUE, new SimulationTreeTableExpansionModel<Object>()); treeTable.setOutputMarkupId(true); treeTable.getTable().addTopToolbar(new HeadersToolbar<String>(treeTable.getTable(), treeTableProvider)); SimulationTreeTableExpansion.get().expandAll(); layoutForm.add(treeTable); } private List<IColumn<SimulationTreeTableElement<Object>, String>> createColumns() { List<IColumn<SimulationTreeTableElement<Object>, String>> columns = new ArrayList<IColumn<SimulationTreeTableElement<Object>, String>>(); columns.add(new PropertyColumn<SimulationTreeTableElement<Object>, String>(Model.of("ID"), "ID")); columns.add(new TreeColumn<SimulationTreeTableElement<Object>, String>(Model.of("Sequence"), "content")); columns.add(new AbstractColumn(new Model("Probability")) { @Override public void populateItem(Item cellItem, String componentId, IModel rowModel) { int entryId = ((SimulationTreeTableElement<Object>) rowModel.getObject()).getID(); if(((SimulationTreeTableElement<Object>) rowModel.getObject()).hasParent() && ((SimulationTreeTableElement<Object>) rowModel.getObject()).getParent().getContent() instanceof XORComponent){ ProbabilityEntryPanel probabilityEntryPanel = new ProbabilityEntryPanel(componentId, entryId, treeTableProvider); cellItem.add(probabilityEntryPanel); probabilityEntryPanel.setTable(treeTable); } else{ cellItem.add(new EmptyPanel(componentId, entryId, treeTableProvider)); } } }); columns.add(new AbstractColumn<SimulationTreeTableElement<Object>, String>(new Model("Select")) { @Override public void populateItem(Item cellItem, String componentId, IModel rowModel) { int entryId = ((SimulationTreeTableElement<Object>) rowModel.getObject()).getID(); if(((SimulationTreeTableElement<Object>) rowModel.getObject()).canHaveSubElements()){ cellItem.add(new SelectEntryPanel(componentId, entryId, treeTableProvider)); } else{ cellItem.add(new EmptyPanel(componentId, entryId, treeTableProvider)); } } }); columns.add(new AbstractColumn<SimulationTreeTableElement<Object>, String>(new Model("Value")) { @Override public void populateItem(Item cellItem, String componentId, IModel rowModel) { int entryId = ((SimulationTreeTableElement<Object>) rowModel.getObject()).getID(); if(((SimulationTreeTableElement<Object>) rowModel.getObject()).editableColumnsVisible()){ TextFieldEntryPanel textFieldEntryPanel = new TextFieldEntryPanel(componentId, entryId, treeTableProvider); cellItem.add(textFieldEntryPanel); textFieldEntryPanel.setTable(treeTable); } else{ cellItem.add(new EmptyPanel(componentId, entryId, treeTableProvider)); } } }); columns.add(new AbstractColumn(new Model("Derivation-Type")) { @Override public void populateItem(Item cellItem, String componentId, IModel rowModel) { int entryId = ((SimulationTreeTableElement<Object>) rowModel.getObject()).getID(); if(((SimulationTreeTableElement<Object>) rowModel.getObject()).getContent() instanceof SushiEventType){ DerivationTypeDropDownChoicePanel derivationChoicePanel = new DerivationTypeDropDownChoicePanel(componentId, entryId, treeTableProvider); cellItem.add(derivationChoicePanel); derivationChoicePanel.setTable(treeTable); } else{ cellItem.add(new EmptyPanel(componentId, entryId, treeTableProvider)); } } }); columns.add(new AbstractColumn(new Model("Duration / Time difference to previous")) { @Override public void populateItem(Item cellItem, String componentId, IModel rowModel) { int entryId = ((SimulationTreeTableElement<Object>) rowModel.getObject()).getID(); if(((SimulationTreeTableElement<Object>) rowModel.getObject()).getContent() instanceof SushiEventType){ DurationEntryPanel textFieldEntryPanel = new DurationEntryPanel(componentId, entryId, treeTableProvider); cellItem.add(textFieldEntryPanel); textFieldEntryPanel.setTable(treeTable); } else{ cellItem.add(new EmptyPanel(componentId, entryId, treeTableProvider)); } } }); return columns; } private void addProcessSelect(Form<Void> layoutForm) { processSelect = new DropDownChoice<String>("processSelect", new Model<String>(), processNameList); processSelect.setOutputMarkupId(true); processSelect.add(new AjaxFormComponentUpdatingBehavior("onchange") { private static final long serialVersionUID = 1L; @Override protected void onUpdate(AjaxRequestTarget target) { String processValue = processSelect.getValue(); if(processValue != null && !processValue.isEmpty()){ List<SushiProcess> processList = SushiProcess.findByName(processSelect.getChoices().get(Integer.parseInt(processSelect.getValue()))); if(processList.size() > 0){ selectedProcess = processList.get(0); createEventTypeList(selectedProcess); target.add(eventTypeSelect); } } treeTableProvider.setCorrelationAttributes(selectedProcess.getCorrelationAttributes()); } }); layoutForm.add(processSelect); } private void addEventTypeSelect(Form<Void> layoutForm) { eventTypeSelect = new DropDownChoice<String>("eventTypeSelect", new Model<String>(), eventTypeAndPatternList); eventTypeSelect.setOutputMarkupId(true); layoutForm.add(eventTypeSelect); } private void createProcessList() { processNameList = new ArrayList<String>(); for (SushiProcess process : SushiProcess.findAll()) { processNameList.add(process.getName()); } } private void createEventTypeList(SushiProcess selectedProcess) { eventTypeAndPatternList.clear(); if(selectedProcess != null){ for (SushiEventType eventType : selectedProcess.getEventTypes()) { eventTypeAndPatternList.add(eventType.getTypeName()); } eventTypeAndPatternList.addAll(IPattern.getValues()); } } private SimulationTreeTableElement<Object> findEventTypeElementWithEventType(List<SimulationTreeTableElement<Object>> eventTypeElements, Tuple<AbstractBPMNElement, SushiEventType> tuple) { for(SimulationTreeTableElement<Object> eventTypeElement : eventTypeElements){ if(eventTypeElement.getContent() == tuple.y){ return eventTypeElement; } } return null; } @Override protected void addUnexpectedEventPanel(List<ITab> tabs) { tabs.add(new AbstractTab(new Model<String>("Unexpected Events (instance-dependent)")) { public Panel getPanel(String panelId) { unexpectedEventPanel = new UnexpectedEventPanel(panelId, simulationPanel); return unexpectedEventPanel; } }); } };