diff --git a/AlgebraicDataflowArchitectureModel/src/application/SimulatorWindow.java b/AlgebraicDataflowArchitectureModel/src/application/SimulatorWindow.java index 915eb6c..a8b4281 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/SimulatorWindow.java +++ b/AlgebraicDataflowArchitectureModel/src/application/SimulatorWindow.java @@ -12,6 +12,7 @@ import javax.swing.JFrame; +import application.editor.SimulationLayout; import com.mxgraph.model.mxCell; import com.mxgraph.model.mxGeometry; import com.mxgraph.model.mxGraphModel; @@ -129,22 +130,22 @@ DataTransferModel model = this.editor.getModel(); TypeInference.infer(model); simulator = new Simulator(model); - constructSimulateGraph(simulator.getCurState().getRootResources(), this.editor.getModel(),this.editor.getDataFlowGraph()); + SimulationLayout layout = new SimulationLayout(simulator.getCurState()); + layout.constructSimulateGraph(graph, simulator); graphComponent.setCellEditor(new InputEventCellEditor(graphComponent, simulator, this.editor, graph)); uiWindow = new UIWindow(simulator); } - - + public mxGraph constructSimulateGraph(Set simulateRes, DataTransferModel model, DataFlowGraph dataFlowGraph) { bReflectingArchitectureModel = true; ((mxGraphModel) graph.getModel()).clear(); Object parent = graph.getDefaultParent(); graph.getModel().beginUpdate(); - + try { Map resources = new HashMap<>(); - + // create resource vertices for (Resource resNode: simulateRes) { int w = 80; @@ -157,11 +158,11 @@ createChildSimulateResourceVerticies(resource, resNode, resources, w, h); x+=80; } - + } finally { graph.getModel().endUpdate(); } - + bReflectingArchitectureModel = false; return graph; } diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/InputEventCellEditor.java b/AlgebraicDataflowArchitectureModel/src/application/editor/InputEventCellEditor.java index e945773..ea39086 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/editor/InputEventCellEditor.java +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/InputEventCellEditor.java @@ -169,8 +169,10 @@ Event newEvent = new Event(eventChs.get(eventNum), eventMessage, eventResPath, simulator.getCurState().getResource(resId)); simulator.transition(newEvent); - - constructNextSimulateGraph(simulator.getCurState().getRootResources(), simulator.getModel(),simulator.getModel().getDataFlowGraph(), graph); + + SimulationLayout layout = new SimulationLayout(simulator.getCurState()); + layout.constructSimulateGraph(graph, simulator); + graphComponent.setCellEditor(new InputEventCellEditor(graphComponent, simulator, this.editor, graph)); } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork @@ -208,10 +210,10 @@ ((mxGraphModel) nextGraph.getModel()).clear(); Object parent = nextGraph.getDefaultParent(); nextGraph.getModel().beginUpdate(); - + try { Map resources = new HashMap<>(); - + // create resource vertices for (Resource resNode: simulateRes) { int w = 400; @@ -221,15 +223,15 @@ res.toString(), x, y, w, h, "shape=ellipse;perimeter=ellipsePerimeter;verticalAlign=top;"); // insert a resource as a vertex resources.put(resNode, resource); - + createNextChildSimulateResourceVerticies(resource, resNode, resources, w, h); x+=400; } - + } finally { nextGraph.getModel().endUpdate(); } - + bReflectingArchitectureModel = false; return nextGraph; } @@ -241,7 +243,7 @@ int i = 1; ResourcePath childRes = childNode.getResourceIdentifier(); Object childResource = graph.insertVertex(resource, null, - childRes.toString(), x/i, y, w/(i+1), h/(i+2), + childRes.toString(), x/i, 0, w/(i+1), h/(i+2), "shape=ellipse;perimeter=ellipsePerimeter;verticalAlign=top;"); // insert a resource as a vertex resources.put(childNode, childResource); i++; diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/SimulationLayout.java b/AlgebraicDataflowArchitectureModel/src/application/editor/SimulationLayout.java new file mode 100644 index 0000000..99bbdff --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/SimulationLayout.java @@ -0,0 +1,157 @@ +package application.editor; + +import com.mxgraph.model.mxGraphModel; +import com.mxgraph.view.mxGraph; +import models.dataConstraintModel.ResourcePath; +import simulator.Resource; +import simulator.ResourceIdentifier; +import simulator.Simulator; +import simulator.SystemState; + +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class SimulationLayout { + // Hold the division level for each resource + HashMap divisionLevelMap = new HashMap<>(); + // Hold the scaling factor for each resource + HashMap resourceScaleMap = new HashMap<>(); + int maxDivisionLevel = 1; + public final double BASE_WIDTH = 400; + public final double BASE_HEIGHT = BASE_WIDTH/2; + public final double MARGIN_SCALE = 0.92; + SystemState systemState; + public SimulationLayout(SystemState systemState){ + this.systemState = systemState; + // Calculate the division level for each resource + for (Resource res:systemState.getRootResources()) { + setDivisionLevel(divisionLevelMap, res.getResourceIdentifier(), 1); + } + // Calculate the scaling factor for each resource + for (ResourceIdentifier res:divisionLevelMap.keySet()) { + double scale = (double) maxDivisionLevel / divisionLevelMap.get(res); + resourceScaleMap.put(res, scale); + } + } + public double getScale(ResourceIdentifier resource){ + return resourceScaleMap.get(resource); + } + public double getDivision(ResourceIdentifier resource){ + return divisionLevelMap.get(resource); + } + + public int getMaxDivisionLevel() { + return maxDivisionLevel; + } + + public boolean isExistResource(ResourceIdentifier resourceIdentifier){ + return divisionLevelMap.containsKey(resourceIdentifier); + } + private void setDivisionLevel(HashMap resourceSet, ResourceIdentifier resource, int childCount){ + int divisionLevel; + if(resourceSet.get(resource.getParent()) == null){ + divisionLevel = 1; + }else{ + divisionLevel = resourceSet.get(resource.getParent()) * childCount; + } + if (divisionLevel > maxDivisionLevel)maxDivisionLevel = divisionLevel; + resourceSet.put(resource,divisionLevel); + + Collection identifiers = systemState.getResource(resource).getChildren(); + if (identifiers != null) { + for (Resource child:identifiers) { + setDivisionLevel(resourceSet, child.getResourceIdentifier(), identifiers.size()); + } + } + } + /** + * + * Draw an object corresponding to the target resource on the mxGraph. + * @param graph The mxGraph for drawing. + * @param parent The parent object of the target resource. + * @param resourceIdentifier The ID of the target resource. + * @param index The index. + * @param length The number of sibling resources of the target resource. + * @return The drawing object of the target resource. + */ + + private Object setLayout(mxGraph graph,Object parent, ResourceIdentifier resourceIdentifier,double index, double length){ + double width, height, x, y; + double parentWidth = graph.getCellGeometry(parent).getWidth(); + double parentHeight = graph.getCellGeometry(parent).getHeight(); + + width = parentWidth / length * MARGIN_SCALE; + height = width/2; + x = parentWidth * (index - 1)/length + (parentWidth/length)*(1-MARGIN_SCALE)/2; + + // Process to avoid hiding the parent's resource name + if ((int)length == 1) { + y = 20; + height -=20; + } else { + y = parentHeight/2 - height/2; + } + + Object result = graph.insertVertex(parent, null, + resourceIdentifier.toString(), x, y, width, height, + "shape=ellipse;perimeter=ellipsePerimeter;verticalAlign=top;"); + System.out.println(result); + return result; + } + + public mxGraph constructSimulateGraph(mxGraph graph, Simulator simulator){ + ((mxGraphModel) graph.getModel()).clear(); + Object parent = graph.getDefaultParent(); + graph.getModel().beginUpdate(); + + try { + Map resources = new HashMap<>(); + int i = 0; + // create resource vertices + for (Resource resNode: simulator.getCurState().getRootResources()) { + double scale = this.getScale(resNode.getResourceIdentifier()); + double maxDiv = this.getMaxDivisionLevel(); + double w = this.BASE_WIDTH * scale /maxDiv; + double h = this.BASE_HEIGHT * scale /maxDiv; + int childCount = simulator.getCurState().getRootResources().size(); + double x = w * (1 + i - childCount / 2.0); + double y = 20 ; + ResourcePath res = resNode.getResourceIdentifier(); + Object resource = graph.insertVertex(parent, null, + res.toString(), x, y, w, h, + "shape=ellipse;perimeter=ellipsePerimeter;verticalAlign=top;"); // insert a resource as a vertex + resources.put(resNode, resource); + i++; + createNextChildSimulateResourceVerticies(graph, resource, resNode, resources); + + } + + } finally { + graph.getModel().endUpdate(); + } + + return graph; + } + private void createNextChildSimulateResourceVerticies(mxGraph graph, Object parent, Resource resNode, Map resources) { //sample + Collection children = resNode.getChildren(); + if (children != null) { + //List children = resNode.getChildren(); + double i = 1; + double childCount = children.size(); + for (Resource childNode: children) { + Object childResource = setLayout(graph, parent, childNode.getResourceIdentifier(), i, childCount); + resources.put(childNode, childResource); + i++; + createNextChildSimulateResourceVerticies(graph, childResource, childNode, resources); + + } + } + if (children == null || children.size() == 0) { + Object state = graph.insertVertex(parent, null, + resNode.getState().getValue().toString(), 0.5, 0.5, 0.25, 0.25, "opacity=0;verticalAlign=down;", true); // insert a state label as a vertex + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/html/HtmlElement.java b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/html/HtmlElement.java index 0ffb191..1f2480d 100644 --- a/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/html/HtmlElement.java +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/html/HtmlElement.java @@ -3,15 +3,15 @@ import java.util.HashMap; import java.util.Map; +import models.algebra.Constant; +import models.dataConstraintModel.MapTerm; + public class HtmlElement { private final String type; private final String id; private String text; private Map styles; - //多分ここにもwebsocketのインスタンスが必要になりそう? - //styleなどが変更されたときに通知しなければならないから - //それかそれぞれのreceiverのほうにその処理を作るか HtmlElement(String type, String id, String text){ this.type = type; @@ -22,16 +22,23 @@ public void setStyle(String property, String value) { styles.put(property, value); - /* - * こちら側にwebsocketの処理を書く場合 - * ws.send("styleChange", id, { property : value } ); - * みたいな感じになりそう - */ } public void resetStyle(String property) { styles.remove(property); - // ws.send("styleChange", id, { property : null }); + } + + public MapTerm toMap() { + MapTerm res = new MapTerm(); + res.insert("type", new Constant(type)); + res.insert("id", new Constant(id)); + res.insert("text", new Constant(text)); + MapTerm stylesMap = new MapTerm(); + for(String prop : styles.keySet()) { + stylesMap.insert(prop, new Constant(styles.get(prop))); + } + res.insert("styles", stylesMap); + return res; } diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/html/HtmlElementVisibilityReceiver.java b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/html/HtmlElementVisibilityReceiver.java index 986e170..e264a89 100644 --- a/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/html/HtmlElementVisibilityReceiver.java +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/html/HtmlElementVisibilityReceiver.java @@ -4,6 +4,8 @@ import models.algebra.Expression; import models.algebra.Term; import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.JsonTerm; +import models.dataConstraintModel.MapTerm; import simulator.Event; import simulator.interfaces.INativeReceiver; @@ -14,8 +16,11 @@ // protected HtmlElement element; - public HtmlElementVisibilityReceiver(HtmlElement elem){ + private final IWebSocketMessageSender ws; + + public HtmlElementVisibilityReceiver(HtmlElement elem, IWebSocketMessageSender ws){ this.element = elem; + this.ws = ws; } @Override @@ -26,10 +31,28 @@ if (visible instanceof Constant) { if (((Constant) visible).getSymbol() == DataConstraintModel.true_) { element.setStyle("display", "none"); - //ws.send("styleChange", element.id, "{\"display\" : \"none\"}");みたいな? + + JsonTerm json = new JsonTerm(); + json.addMember("method", new Constant("changeStyle")); + json.addMember("id", new Constant(element.getId())); + MapTerm datas = new MapTerm(); + datas.insert("display", new Constant("none")); + json.addMember("datas", datas); + + ws.send(json.toString()); + + } else { element.resetStyle("display"); - //ws.send("styleChange", element.id, "{\"display\" : \"null\" }"); + + JsonTerm json = new JsonTerm(); + json.addMember("method", new Constant("changeStyle")); + json.addMember("id", new Constant(element.getId())); + MapTerm datas = new MapTerm(); + datas.insert("display", new Constant("null")); + json.addMember("datas", datas); + + ws.send(json.toString()); } } } diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/html/HtmlPresenter.java b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/html/HtmlPresenter.java index b6fa822..bd32c17 100644 --- a/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/html/HtmlPresenter.java +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/html/HtmlPresenter.java @@ -5,8 +5,10 @@ import java.util.Map; import java.util.Set; +import models.algebra.Constant; import models.algebra.Expression; import models.dataConstraintModel.JsonTerm; +import models.dataConstraintModel.ListTerm; import models.dataConstraintModel.MapTerm; import models.dataFlowModel.DataTransferChannel; import simulator.Event; @@ -26,26 +28,28 @@ protected DataTransferChannel setVisibleChannel; protected DataTransferChannel mouseEventChannel; protected DataTransferChannel textEventChannel; + protected Map> channelAndResourcesForReceiving = new HashMap<>();// ??? protected Map elements; - public HtmlPresenter(Simulator simulator) { + protected IWebSocketMessageSender ws; + + public HtmlPresenter(Simulator simulator, IWebSocketMessageSender ws) { this.simulator = simulator; + this.ws = ws; screenUpdateChannel = (DataTransferChannel) simulator.getModel().getChannel(screenUpdateChannelName); setVisibleChannel = (DataTransferChannel) simulator.getModel().getChannel(setVisibleChannelName); - mouseEventChannel = (DataTransferChannel) simulator.getModel().getChannel(mouseEventChannelName); - textEventChannel = (DataTransferChannel) simulator.getModel().getChannel(textEventChannelName); + mouseEventChannel = (DataTransferChannel) simulator.getModel().getInputChannel(mouseEventChannelName); + textEventChannel = (DataTransferChannel) simulator.getModel().getInputChannel(textEventChannelName); simulator.addNativeReceiver(this, screenUpdateChannel); + + elements = new HashMap<>(); } @Override public void onReceiveFromModel(Event event) { - //websocketでjavascriptにinsertAdjacentHTMLを実行するように指示するのが簡単そう - //どうやってwebsocketを送るようにサーバーに指示するかが微妙 - //websocketのインスタンスをもらってきて送るみたいな? - //Javaでhtmlのエレメントをどう扱うかも考える必要がありそう - //エレメントを表すクラスが必要? + //画面遷移するときに呼ばれそう Expression message = event.getMessage(); @@ -63,8 +67,10 @@ JsonTerm screenContent = (JsonTerm) message; //受け取ったmessageをjsonとして扱う Resource screenResource = simulator.getCurState().getResource(event.getInputResource().getResourceIdentifier()); //出力チャンネルに接続されたresourceを取得? - Expression widgets = screenContent.get("\"widgets\""); //json化したmessageから"widgets"を取得 - Resource widgetsResource = screenResource.getChildrenMap().get("\"widgets\"");//??? + Expression widgets = screenContent.get("widgets"); //json化したmessageから"widgets"を取得 + Resource widgetsResource = screenResource.getChildrenMap().get("widgets");//??? + + elements.clear(); if(widgets instanceof MapTerm) { for(String key : ((MapTerm) widgets).keySet()) { // keyはなに? どうやって決められる? @@ -73,11 +79,11 @@ JsonTerm widget = (JsonTerm) value; //jsonとして扱えるように Resource widgetResouce = widgetsResource.getChildrenMap().get(key); //??? Expression type = widget.get("\"type\""); //widgetのtypeを取得 - if(type.toString().equals("button")) { + if(type.toString().equals("\"button\"")) { //こんな感じになりそう Expression text = widget.get("\"text\""); - HtmlElement button = new HtmlElement(type.toString(), key, text.toString()); + HtmlElement button = new HtmlElement(type.toString(), key, text.toString().replace("\"", "")); elements.put(key, button); //ボタンが押された時の処理 @@ -85,7 +91,7 @@ //そもそもボタンが押されたらREST API が叩かれるのでは? //可視化、不可視化のやつ - HtmlElementVisibilityReceiver nativeReceiver = new HtmlElementVisibilityReceiver(button); + HtmlElementVisibilityReceiver nativeReceiver = new HtmlElementVisibilityReceiver(button, ws); simulator.addNativeReceiver(nativeReceiver, setVisibleChannel, widgetResouce); Set resources = channelAndResourcesForReceiving.get(setVisibleChannel);//この辺謎 if (resources == null) { @@ -97,7 +103,7 @@ } else if(type.toString().equals("label")) { Expression text = widget.get("\"text\""); - HtmlElement elem = new HtmlElement(type.toString(), key, text.toString()); + HtmlElement elem = new HtmlElement(type.toString(), key, text.toString().replace("\"", "")); elements.put(key, elem); } else if(type.toString().equals("inputText")) { @@ -109,7 +115,13 @@ } } - + JsonTerm json = new JsonTerm(); + json.addMember("method", new Constant("updateHtml")); + ListTerm elements = new ListTerm(); + for(String id : this.elements.keySet()) { + elements.append(this.elements.get(id).toMap()); + } + ws.send(json.toString()); } diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/html/IWebSocketMessageSender.java b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/html/IWebSocketMessageSender.java new file mode 100644 index 0000000..e48fe6b --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/html/IWebSocketMessageSender.java @@ -0,0 +1,7 @@ +package simulator.interfaces.html; + +public interface IWebSocketMessageSender { + + void send(String message); + +}