/******************************************************************************* * gMix open source project - https://svs.informatik.uni-hamburg.de/gmix/ * Copyright (C) 2014 SVS * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. *******************************************************************************/ package userGeneratedContent.simulatorPlugIns.plugins.outputStrategy; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Vector; import staticContent.evaluation.simulator.Simulator; import staticContent.evaluation.simulator.annotations.plugin.Plugin; import staticContent.evaluation.simulator.annotations.property.DoubleSimulationProperty; import staticContent.evaluation.simulator.core.event.Event; import staticContent.evaluation.simulator.core.event.EventExecutor; import staticContent.evaluation.simulator.core.message.MessageFragment; import staticContent.evaluation.simulator.core.message.MixMessage; import staticContent.evaluation.simulator.core.message.TransportMessage; import staticContent.evaluation.simulator.core.networkComponent.AbstractClient; import staticContent.evaluation.simulator.core.networkComponent.IdGenerator; import staticContent.evaluation.simulator.core.networkComponent.Identifiable; import staticContent.evaluation.simulator.core.networkComponent.Mix; import staticContent.evaluation.simulator.core.networkComponent.NetworkNode; import staticContent.evaluation.simulator.core.statistics.Statistics; import userGeneratedContent.simulatorPlugIns.pluginRegistry.ClientSendStyle; import userGeneratedContent.simulatorPlugIns.pluginRegistry.StatisticsType; import userGeneratedContent.simulatorPlugIns.plugins.clientSendStyle.ClientSendStyleImpl; import userGeneratedContent.simulatorPlugIns.plugins.mixSendStyle.MixSendStyleImpl; import userGeneratedContent.simulatorPlugIns.plugins.mixSendStyle.ReplyReceiver; @Plugin(pluginKey = "LOSSY_SYNCHRONOUS_BATCH", pluginName="Lossy Synchronous Batch") public class LossySynchronousBatch extends OutputStrategyImpl implements Identifiable { private Statistics statistics; private int numericIdentifier; private SimplexLossySynchronousBatch requestBatch; private SimplexLossySynchronousBatch replyBatch; private Map<String, Vector<TransportMessage>> clientReplyWaitingQueues; @DoubleSimulationProperty( name = "Request rate (Hz)", key = "LSB_REQUEST_RATE", min = 0) private double requestRate; @DoubleSimulationProperty( name = "Reply rate (Hz)", key = "LSB_REPLY_RATE", min = 0) private double replyRate; public LossySynchronousBatch(Mix mix, Simulator simulator) { super(mix, simulator); this.statistics = new Statistics(this); this.numericIdentifier = IdGenerator.getId(); int numberOfClients = Simulator.getSimulator().getNumberOfClients(); this.requestRate = Simulator.settings.getPropertyAsDouble("LSB_REQUEST_RATE"); this.requestBatch = new SimplexLossySynchronousBatch(true, requestRate, numberOfClients); if (Simulator.settings.getProperty("COMMUNICATION_MODE").equals("SIMPLEX_REPLY") || Simulator.settings.getProperty("COMMUNICATION_MODE").equals("DUPLEX")) { replyRate = Simulator.settings.getPropertyAsDouble("LSB_REPLY_RATE"); this.replyBatch = new SimplexLossySynchronousBatch(true, replyRate, numberOfClients); if (mix.isLastMix()) clientReplyWaitingQueues = new HashMap<String, Vector<TransportMessage>>(); } } @Override public void incomingRequest(MixMessage mixMessage) { requestBatch.addMessage(mixMessage); } @Override public void incomingReply(MixMessage mixMessage) { replyBatch.addMessage(mixMessage); } public class SimplexLossySynchronousBatch implements EventExecutor { private final boolean isRequestBatch; private int sendInterval; private MixMessage[] batch; private boolean isFirstMessage = true; public SimplexLossySynchronousBatch(boolean isRequestBatch, double sendingRate, int numberOfClients) { this.isRequestBatch = isRequestBatch; double interval = 1d/sendingRate * 1000; this.sendInterval = (int)Math.floor(interval + 0.5d); System.out.println("SENDINTERVAL: " +sendInterval); this.batch = new MixMessage[numberOfClients]; } public void addMessage(MixMessage mixMessage) { if (isFirstMessage) { isFirstMessage = false; scheduleNextOutput(); } int index = mixMessage.getOwner().getClientId(); boolean drop; if (batch[index] == null) { // user has no message in current batch -> add his message batch[index] = mixMessage; drop = true; } else { // user has message in current batch -> drop message drop = false; statistics.addValue(drop, StatisticsType.DLPA_REQUEST_MESSAGE_DROP_PERCENTAGE_INCL_DUMMIES); statistics.addValue(drop, StatisticsType.DLPA_MESSAGE_DROP_PERCENTAGE_INCL_DUMMIES); } // record statistics if (isRequestBatch) { statistics.addValue(drop, StatisticsType.DLPA_REQUEST_MESSAGE_DROP_PERCENTAGE); statistics.addValue(drop, StatisticsType.DLPA_MESSAGE_DROP_PERCENTAGE); } else { statistics.addValue(drop, StatisticsType.DLPA_REPLY_MESSAGE_DROP_PERCENTAGE); statistics.addValue(drop, StatisticsType.DLPA_MESSAGE_DROP_PERCENTAGE); } } @Override public void executeEvent(Event e) { putOutMessages(); scheduleNextOutput(); } private void putOutMessages() { if (mix.isLastMix() && !isRequestBatch) { createReplyBatchFromUserData(); } else { for (int i=0; i<batch.length; i++) { if (batch[i] == null) batch[i] = createDummyMessage(simulator.getClientById(i)); if (isRequestBatch) { statistics.addValue(true, StatisticsType.DLPA_REQUEST_MESSAGE_DROP_PERCENTAGE_INCL_DUMMIES); statistics.addValue(true, StatisticsType.DLPA_MESSAGE_DROP_PERCENTAGE_INCL_DUMMIES); } else { statistics.addValue(true, StatisticsType.DLPA_REPLY_MESSAGE_DROP_PERCENTAGE_INCL_DUMMIES); statistics.addValue(true, StatisticsType.DLPA_MESSAGE_DROP_PERCENTAGE_INCL_DUMMIES); } } List<MixMessage> result = Arrays.asList(batch); Collections.shuffle(result); if (isRequestBatch) { for (MixMessage msg:result) mix.putOutRequest(msg); } else { for (MixMessage msg:result) mix.putOutReply(msg); } batch = new MixMessage[batch.length]; } } private void scheduleNextOutput() { Event outputEvent = new Event(this, Simulator.getNow() + sendInterval, OutputStrategyEvent.TIMEOUT); simulator.scheduleEvent(outputEvent, this); } private void createReplyBatchFromUserData() { Vector<MixMessage> batch = new Vector<MixMessage>(simulator.getClients().size()); for (AbstractClient client: simulator.getClients().values()) { Vector<TransportMessage> replyWaitingQueue = clientReplyWaitingQueues.get(client.getIdentifier()); boolean isDummy = replyWaitingQueue.size() == 0 ? true: false; MixMessage mixMessage = createDummyMessage(client); if (isDummy) { batch.add(mixMessage); } else { for (int i=0; i<replyWaitingQueue.size(); i++) { TransportMessage noneMixMessage = replyWaitingQueue.get(i); if (mixMessage.getFreeSpace() >= noneMixMessage.getLength() && !noneMixMessage.isFragmented()) { // noneMixMessage fits in mixMessage completely replyWaitingQueue.remove(noneMixMessage); i--; mixMessage.addPayloadObject(noneMixMessage); } else { // add Fragment if (noneMixMessage.hasNextFragment()) { MessageFragment messageFragment = noneMixMessage.getFragment(mixMessage.getFreeSpace()); mixMessage.addPayloadObject(messageFragment); } if (!noneMixMessage.hasNextFragment()) { replyWaitingQueue.remove(i); i--; } } if (mixMessage.getFreeSpace() == 0) break; } batch.add(mixMessage); } } Collections.shuffle(batch); for (MixMessage mixMessage:batch) mix.putOutRequest(mixMessage); } private MixMessage createDummyMessage(AbstractClient owner) { NetworkNode source = isRequestBatch ? owner : mix; NetworkNode destination = isRequestBatch ? simulator.getDistantProxy() : owner; return MixMessage.getInstance(isRequestBatch, source, destination, owner, Simulator.getNow(), true); } } private class ReplyStyle extends MixSendStyleImpl { public ReplyStyle(NetworkNode owner, Simulator simulator, ReplyReceiver replyReceiver) { super(owner, simulator, replyReceiver); } @Override public void incomingDataFromServer(TransportMessage transportMessage) { if (!mix.isLastMix()) throw new RuntimeException("ERROR: BasicSynchronousBatch only supports NoneMixMessages as reply from distant proxy!"); clientReplyWaitingQueues.get(transportMessage.getOwner().getIdentifier()).add(transportMessage); } } @Override public ClientSendStyleImpl getClientSendStyle(AbstractClient client) { return ClientSendStyle.getInstance(client); } @Override public MixSendStyleImpl getMixSendStyle() { return new ReplyStyle(mix, Simulator.getSimulator(), mix); } @Override public int getGlobalId() { return numericIdentifier; } }