All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.jbotsim.core.DelayMessageEngine Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2008 - 2020, Arnaud Casteigts and the JBotSim contributors 
 *
 *
 * This file is part of JBotSim.
 *
 * JBotSim is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * JBotSim 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with JBotSim.  If not, see .
 *
 */
package io.jbotsim.core;

import java.util.*;

/**
 * 

The {@link DelayMessageEngine} is responsible for the regular transmission of the available * {@link Message Messages} from any sender {@link Node} of the {@link Topology} to their destination.

* *

By default, {@link Message Messages} sent during round n are delivered at the beginning of round * n+1; provided that the corresponding arc (a {@link Link} going, at least, from the * {@link Message}'s source {@link Node} to its destination) actually exists at the beginning of round n+1.

* *

Delay feature

*

The previously explained default delivery duration ({@link #DEFAULT_DELAY}) is said instantaneous * ({@link #DELAY_INSTANT}). * The {@link DelayMessageEngine} allows you to modify this duration by specifying the * amount of rounds {@link Message Messages} should take to be delivered, using {@link #setDelay(int)}.

* *

Link checks

*

By default, each round, the {@link DelayMessageEngine} checks for each {@link Message} * that the corresponding {@link Link} is still present. If not, the {@link Message} is dropped.

*

The time spent doing this rises with the delay and the number of {@link Node Nodes} and {@link Message Messages}. * Depending on your case, you might want to disable this using {@link #disableLinksContinuityChecks()}.

*/ public class DelayMessageEngine extends DefaultMessageEngine { /** * The delay value to use for the shortest delivery time possible; value: {@value #DELAY_INSTANT}. * @see #setDelay(int) * @see #getDelay() */ public static final int DELAY_INSTANT = 1; /** * The default number of round before message delivery; value: {@value #DELAY_INSTANT}. * @see #setDelay(int) * @see #getDelay() */ public static final int DEFAULT_DELAY = DELAY_INSTANT; private int delay; protected Map> delayedMessages = new HashMap<>(); protected int currentTime; private boolean shouldCheckLinksContinuity = true; /** *

Creates a {@link DelayMessageEngine}.

*

By default, the messages sent during one round are actually during the next.

* * @param topology the {@link Topology} to use. */ public DelayMessageEngine(Topology topology) { this(topology, DEFAULT_DELAY); } /** *

Creates a {@link DelayMessageEngine}.

* * @param topology the {@link Topology} to use. * @param delay the number of round a message should be delayed, as an integer. */ public DelayMessageEngine(Topology topology, int delay) { super(topology); assert(delay >= 0); setDelay(delay); this.shouldCheckLinksContinuity = true; } /** *

Sets the number of round a message should be delayed.

* @param speed the number of round a message should be delayed, as an integer. * @deprecated Please use {@link #setDelay(int)} instead. */ @Deprecated public void setSpeed(int speed) { setDelay(speed); } /** *

Sets the number of round a message should be delayed.

*

Any value below {@link #DELAY_INSTANT} (i.e. {@value #DELAY_INSTANT}) will be replaced by * {@link #DELAY_INSTANT}.

* * @param delay the number of round a message should be delayed, as an integer. * @see #getDelay() */ public void setDelay(int delay) { if(delay < DELAY_INSTANT) this.delay = DELAY_INSTANT; else this.delay = delay; } /** *

Gets the number of round a message should be delayed.

* * @return the number of round a message should be delayed, as an integer. * @see #setDelay(int) */ public int getDelay() { return delay; } /** *

Specifies whether a {@link Message} should be removed if the corresponding {@link Link} disappears at some * point during its waiting delay.

* *

Note: Whatever the status, the link presence will be checked at sending time and delivery time.

* * @return true if intermediate checks and removal should be performed; false otherwise. * @see #disableLinksContinuityChecks() */ public boolean shouldCheckLinksContinuity() { return shouldCheckLinksContinuity; } /** *

Disables the intermediate checks on {@link Link} existence for each {@link Message}.

* @see #shouldCheckLinksContinuity() */ public void disableLinksContinuityChecks() { this.shouldCheckLinksContinuity = false; } @Override public void onClock() { currentTime = topology.getTime(); List nodes = topology.getNodes(); clearMailboxes(nodes); List newMessages = collectMessages(nodes); removeIrrelevantMessages(newMessages.listIterator(), nodes); List messagesToSend = getMessagesToSend(newMessages, nodes); deliverMessages(messagesToSend); delayedMessages.remove(currentTime); } /** *

Constructs the {@link List} of {@link Message Messages} that must be sent during this round.

* @param newMessages the {@link List} of new {@link Message Messages} which has been collected during this round. * @param existingNodes the {@link Collection} of existing {@link Node Nodes}. * @return the {@link List} of {@link Message Messages} that must be sent during this round. */ protected List getMessagesToSend(List newMessages, Collection existingNodes) { List currentDateMessages; if (noCachingNeeded(newMessages)) currentDateMessages = newMessages; else { if(shouldCheckLinksContinuity()) removeIrrelevantMessages(existingNodes); cacheNewMessages(newMessages); currentDateMessages = getMessagesForCurrentDate(); if(!shouldCheckLinksContinuity()) removeIrrelevantMessages(currentDateMessages.listIterator(), existingNodes); } return currentDateMessages; } /** *

Tests whether the provided list of {@link Message Messages} should be cached or not.

* @param newMessages a {@link List} containing new {@link Message Messages}. * @return true if the provided messages should not be cached. */ protected boolean noCachingNeeded(List newMessages) { return getDelay() == DELAY_INSTANT && delayedMessages.isEmpty(); } /** *

Removes any irrelevant messages from the cached delayed messages, according to the {@link Collection} of * existing {@link Node Nodes}.

* @param existingNodes the {@link Collection} of existing {@link Node Nodes}. * @see #removeIrrelevantMessages(ListIterator, Collection) */ protected void removeIrrelevantMessages(Collection existingNodes) { for (List messageList : delayedMessages.values()) removeIrrelevantMessages(messageList.listIterator(), existingNodes); } /** *

Caches the provided {@link List} of new {@link Message Messages}.

* @param messages a {@link List} containing new {@link Message Messages}. * @see #prepareNewMessagesForCaching(List) * @see #cacheMessagesAtTime(List, int) */ protected void cacheNewMessages(List messages) { Map> newMessages = prepareNewMessagesForCaching(messages); for (Map.Entry> entry : newMessages.entrySet()) cacheMessagesAtTime(entry.getValue(), entry.getKey()); } /** *

Transforms the provided {@link List} of new {@link Message Messages} for caching into a suitable data * structure.

* @param newMessages a {@link List} containing new {@link Message Messages}. * @return a {@link Map} containing {@link List Lists} of {@link Message Messages} indexed by the date at which they * should be delivered. * @see #getCurrentDeliveryDate() */ protected Map> prepareNewMessagesForCaching(List newMessages) { Map> messagesMap = new HashMap<>(); messagesMap.put(getCurrentDeliveryDate(), newMessages); return messagesMap; } /** *

Caches a specific {@link Message} at it's planned delivery time (round number).

* @param messagesMap the {@link Map}, indexing {@link Message Messages} by their delivery date, in which the * message should be cached. * @param message the {@link Message} to cache. * @param deliveryTime the round number at which the message should be delivered. */ protected void cacheMessageAtTime(Map> messagesMap, Message message, int deliveryTime) { if(messagesMap.containsKey(deliveryTime)) messagesMap.get(deliveryTime).add(message); else { List messageList = new ArrayList<>(); messageList.add(message); messagesMap.put(deliveryTime, messageList); } } /** *

Caches (internally) the specified {@link List} of {@link Message Messages} at that the given delivery time * (round number).

* @param messages the {@link List} of {@link Message Messages} to cache internally. * @param deliveryTime the round number at which the messages should be delivered. */ protected void cacheMessagesAtTime(List messages, int deliveryTime) { if(delayedMessages.containsKey(deliveryTime)) delayedMessages.get(deliveryTime).addAll(messages); else delayedMessages.put(deliveryTime, messages); } /** *

Retrieves the list of {@link Message Messages} which should be delivered during the current round, from the * internal storage.

* @return a {@link List} of {@link Message Messages} containing all messages which should be delivered during the * current round. Can be empty, but not null. */ protected List getMessagesForCurrentDate() { List messages = delayedMessages.get(currentTime); return messages != null ? messages : new ArrayList<>(); } /** *

Computes the delay which should be applied to the provided {@link Message}.

* @param message the {@link Message} needing a delay. * @return the delay for the provided message. */ protected int getDelayForMessage(Message message) { return getDelay(); } /** *

Computes the delivery date (round number) for a {@link Message} collected at the start of the current * round.

* @return the delivery date for the current round, as an integer. */ protected int getCurrentDeliveryDate() { return currentTime + getDelay() - 1; } /** *

Resets the {@link DelayMessageEngine}.

*
    *
  • Any {@link Message} (ready to be sent, "in the air" or ready to be received) handled by the * {@link DelayMessageEngine} is discarded.
  • *
  • Other configurations (delay or debug) remain untouched.
  • *
*/ @Override public void reset() { super.reset(); delayedMessages.clear(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy