diff --git a/AlgebraicDataflowArchitectureModel/models/Timer.model b/AlgebraicDataflowArchitectureModel/models/Timer.model new file mode 100644 index 0000000..83a66d5 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/Timer.model @@ -0,0 +1,15 @@ +native channel TimersUpdated { + in timers(curTimers: Map, update(curTimers, nextTimers)) = nextTimers +} + +native channel TimerEvent(tid: Str) { + out timers.{tid}.count(count: Long, tick()) = count + 1 +} + +channel StartTimer { + out timers(timers: Map, startTimer(tid: Str, interval: Long)) = insert(timers, tid, {"interval": interval, "count": 0}) +} + +channel ClearTimer { + out timers(timers: Map, clearTimer(tid: Str)) = delete(timers, tid) +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/simulator/UISimulatorWindow.java b/AlgebraicDataflowArchitectureModel/src/application/simulator/UISimulatorWindow.java index 24ac3c0..1c246b9 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/simulator/UISimulatorWindow.java +++ b/AlgebraicDataflowArchitectureModel/src/application/simulator/UISimulatorWindow.java @@ -5,6 +5,7 @@ import simulator.Simulator; import simulator.interfaces.swing.SwingPresenter; +import simulator.interfaces.timers.TimerService; public class UISimulatorWindow extends JFrame { @@ -12,6 +13,7 @@ private SwingPresenter presenter; private Simulator simulator; private JPanel mainPanel; + private TimerService timerService; public UISimulatorWindow(Simulator simulator) { setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); @@ -19,6 +21,7 @@ mainPanel = new JPanel(); presenter = new SwingPresenter(mainPanel, simulator); this.add(mainPanel); + timerService = new TimerService(simulator); setSize(870,640); setVisible(true); diff --git a/AlgebraicDataflowArchitectureModel/src/parser/Parser.java b/AlgebraicDataflowArchitectureModel/src/parser/Parser.java index a137bf7..ec5f920 100644 --- a/AlgebraicDataflowArchitectureModel/src/parser/Parser.java +++ b/AlgebraicDataflowArchitectureModel/src/parser/Parser.java @@ -326,6 +326,7 @@ int arity = 0; do { stream.next(); // LEFT_BRACKET or COMMA + if (stream.checkNext().equals(RIGHT_BRACKET)) break; arity++; Expression subTerm = parseTerm(stream, model); term.addChild(subTerm, true); diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/timers/TimerEventSender.java b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/timers/TimerEventSender.java new file mode 100644 index 0000000..1dce61c --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/timers/TimerEventSender.java @@ -0,0 +1,24 @@ +package simulator.interfaces.timers; + +import models.algebra.Expression; +import models.algebra.Term; +import models.dataConstraintModel.ResourcePath; +import models.dataFlowModel.DataTransferChannel; +import simulator.Resource; +import simulator.Simulator; +import simulator.interfaces.NativeSender; + +public class TimerEventSender extends NativeSender implements Runnable { + + public TimerEventSender(Simulator simulator, DataTransferChannel channel, ResourcePath resourcePath, Resource resource) { + super(simulator, channel, resourcePath, resource); + } + + @Override + public void run() { + Expression message = channel.getOutputChannelMembers().iterator().next().getStateTransition().getMessageExpression(); + message = (Expression) message.clone(); + sendToModel(message); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/timers/TimerService.java b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/timers/TimerService.java new file mode 100644 index 0000000..70d6b51 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/timers/TimerService.java @@ -0,0 +1,139 @@ +package simulator.interfaces.timers; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.ScheduledThreadPoolExecutor; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Term; +import models.dataConstraintModel.JsonTerm; +import models.dataConstraintModel.MapTerm; +import models.dataConstraintModel.ResourcePath; +import models.dataFlowModel.DataTransferChannel; +import simulator.Event; +import simulator.Resource; +import simulator.Simulator; +import simulator.SystemState; +import simulator.interfaces.INativeReceiver; + +public class TimerService implements INativeReceiver { + public final String timersUpdatedChannelName = "TimersUpdated"; + public final String timerEventChannelName = "TimerEvent"; + + protected final Simulator simulator; + protected final Map timers; + protected DataTransferChannel timersUpdatedChannel; + protected DataTransferChannel timerEventChannel; + + public TimerService(Simulator simulator) { + this.simulator = simulator; + timers = new HashMap<>(); + timersUpdatedChannel = (DataTransferChannel) simulator.getModel().getChannel(timersUpdatedChannelName); + timerEventChannel = (DataTransferChannel) simulator.getModel().getInputChannel(timerEventChannelName); + simulator.addNativeReceiver(this, timersUpdatedChannel); + } + + @Override + public void onReceiveFromModel(Event event, SystemState nextSystemState) { + Expression message = event.getMessage(); + if (message instanceof Term && ((Term) message).getChildren().size() >= 2) { + Expression curTimersExp = ((Term) message).getChild(0); + Expression nextTimersExp = ((Term) message).getChild(1); + if (curTimersExp instanceof MapTerm && nextTimersExp instanceof MapTerm) { + MapTerm curTimers = (MapTerm) curTimersExp; + MapTerm nextTimers = (MapTerm) nextTimersExp; + Set oldTidSet = new HashSet<>(curTimers.keySet()); + Set newTidSet = new HashSet<>(nextTimers.keySet()); + oldTidSet.removeAll(nextTimers.keySet()); + newTidSet.removeAll(curTimers.keySet()); + if (!oldTidSet.isEmpty() || !newTidSet.isEmpty()) { + // If the set of timers is changed. + + // Remove old timers and their native receivers. + for (String tid: oldTidSet) { + ScheduledThreadPoolExecutor timer = timers.get(tid); + timer.shutdown(); + timers.remove(tid); + } + + // Add new timers. + Resource timersResource = nextSystemState.getResource(event.getInputResource().getResourceIdentifier()); + for (String tid: newTidSet) { + Expression value = nextTimers.get(tid); + if (value instanceof JsonTerm) { + JsonTerm timerValue = (JsonTerm) value; + Resource timerResource = timersResource.getChildrenMap().get(tid); + // Add a timer. + Expression intervalExp = timerValue.get("\"interval\""); + long interval = Long.parseLong(((Constant) intervalExp).toString()); + ScheduledThreadPoolExecutor timer = new ScheduledThreadPoolExecutor(0); + timers.put(tid, timer); + + // Connect Java timer and model. + ResourcePath resPath = timerEventChannel.getOutputResources().iterator().next(); + TimerEventSender sender = new TimerEventSender(simulator, timerEventChannel, resPath, timerResource); // timer => timerResource + timer.scheduleAtFixedRate(sender, 0, interval, TimeUnit.MILLISECONDS); + } + } + } + } + } + } + + public class TimerStart implements INativeReceiver { + public final String timerStartChannelName = "TimerStart"; + protected DataTransferChannel timerStartChannel; + protected DataTransferChannel timerEventChannel; + + public TimerStart() { + timerStartChannel = (DataTransferChannel) simulator.getModel().getChannel(timerStartChannelName); + timerEventChannel = (DataTransferChannel) simulator.getModel().getInputChannel(timerEventChannelName); + simulator.addNativeReceiver(this, timerStartChannel); + } + + @Override + public void onReceiveFromModel(Event event, SystemState nextSystemState) { + Expression message = event.getMessage(); + if (message instanceof Term && ((Term) message).getChildren().size() >= 1) { + Expression tidExp = event.getInputResource().getResourceIdentifier().getLastParam(); + Expression intervalExp = ((Term) message).getChild(0); + if (tidExp instanceof Constant && intervalExp instanceof Constant) { + String tid = ((Constant) tidExp).toString(); + long interval = Long.parseLong(((Constant) intervalExp).toString()); + ScheduledThreadPoolExecutor timer = timers.get(tid); + TimerEventSender sender = new TimerEventSender(simulator, timerEventChannel, event.getInputResourcePath(), event.getInputResource()); + timer.scheduleAtFixedRate(sender, 0, interval, TimeUnit.MILLISECONDS); + } + } + } + } + + public class TimerClear implements INativeReceiver { + public final String timerClearChannelName = "TimerClear"; + protected DataTransferChannel timerClearChannel; + + public TimerClear() { + timerClearChannel = (DataTransferChannel) simulator.getModel().getChannel(timerClearChannelName); + simulator.addNativeReceiver(this, timerClearChannel); + } + + @Override + public void onReceiveFromModel(Event event, SystemState nextSystemState) { + Expression message = event.getMessage(); + if (message instanceof Term && ((Term) message).getChildren().size() >= 1) { + Expression tidExp = event.getInputResource().getResourceIdentifier().getLastParam(); + if (tidExp instanceof Constant) { + String tid = ((Constant) tidExp).toString(); + ScheduledThreadPoolExecutor timer = timers.get(tid); + timer.shutdown(); + timers.remove(tid); + } + } + } + + } +}