/**
   * Agenda eventos de envio e de chegada de pacote no roteador.
   *
   * @param tx Servidor de saída do pacote.
   */
  private void newTxPacketHeadsToRouter(Tx tx) {
    Pack nextTcpPack = tx.sendPacket(currentTime);
    tx.setTransmiting(true);
    double propagation = setPropagationDelay(tx);

    Event endOfTransmition = new Event(EventType.TxPacketHeadsToRouter);
    endOfTransmition.setTime(currentTime + transmissionTimeMs);
    endOfTransmition.setTxIndex(tx.getIndex());
    getEvents().add(endOfTransmition);

    Event routerReceivesTcp = new Event(EventType.TxPacketIntoRouter);
    routerReceivesTcp.setTime(currentTime + transmissionTimeMs + propagation);
    routerReceivesTcp.setPack(nextTcpPack);
    getEvents().add(routerReceivesTcp);

    Event timeout = new Event(EventType.Timeout);
    timeout.setTime(currentTime + tx.getRTO());
    timeout.setTxIndex(tx.getIndex());
    nextTcpPack.setTimeout(timeout);
    getEvents().add(timeout);

    if (tx.getNotDeliveredPacks().contains(nextTcpPack)) {
      int index = tx.getNotDeliveredPacks().indexOf(nextTcpPack);
      Pack p = tx.getNotDeliveredPacks().get(index);
      getEvents().remove(p.getTimeout());
      tx.getNotDeliveredPacks().remove(index);
    }

    tx.getNotDeliveredPacks().add(nextTcpPack);
  }
  private void handleCongestionPacketIntoRouterEvent(Event event) {
    double scheduleTime = random.generateExponential(1.0 / congestionPacketFrequency);
    Event scheduleNextCongestionPack = new Event(EventType.CongestionPacketIntoRouter);
    scheduleNextCongestionPack.setTime(currentTime + scheduleTime);
    getEvents().add(scheduleNextCongestionPack);

    if (getSystem().getRouter().getBuffer().quantityOfRemainingPackets().equals(0)) {
      Event sendPack = new Event(EventType.RouterSuccessfullySentPacket);
      double packTransmission = packTransmission(SimulationProperties.getMSS());
      sendPack.setTime(currentTime + packTransmission);
      getEvents().add(sendPack);
    }

    long numberOfCongestionPacks =
        Math.round(random.generateGeometric(1.0 / congestionPacketFrequency));
    for (long i = 0; i < numberOfCongestionPacks; i++)
      getSystem().getRouter().receivePacket(new Pack(PackType.Congestion, 0), currentTime);
  }
 private void handleTxPacketIntoRouter(Event event) {
   // Sempre que o roteador fica vazio, um novo envio é agendado.
   if (getSystem().getRouter().getBuffer().quantityOfRemainingPackets().intValue() == 0) {
     Event sendPack = new Event(EventType.RouterSuccessfullySentPacket);
     double packTransmission = packTransmission(SimulationProperties.getMSS());
     sendPack.setTime(currentTime + packTransmission);
     getEvents().add(sendPack);
   }
   getSystem().getRouter().receivePacket(event.getPack(), currentTime);
 }
 private void handleRouterSuccessfullySentPacket(Event event) {
   SACK sack = getSystem().getRouter().sendPackToRx(currentTime);
   if (sack != null) { // Tráfego de fundo se perde
     int ackPropagation =
         getSystem().getTxs().get(sack.getDestination()).getGroup().equals(Group.Group1)
             ? SimulationProperties.getAckG1PropagationTime()
             : SimulationProperties.getAckG2PropagationTime();
     Event sendSack = new Event(EventType.SackArrives);
     sendSack.setTime(currentTime + ackPropagation);
     sendSack.setSack(sack);
     getEvents().add(sendSack);
   }
   // Mais um pacote sai do roteador para um Rx.
   if (getSystem().getRouter().getBuffer().quantityOfRemainingPackets().intValue() >= 1) {
     Event sendPack = new Event(EventType.RouterSuccessfullySentPacket);
     double packTransmission = packTransmission(SimulationProperties.getMSS());
     sendPack.setTime(currentTime + packTransmission);
     getEvents().add(sendPack);
   }
 }
  /**
   * A fila de eventos precisa ser setada com eventos iniciais. Este método seta envios de pacotes
   * de congestionamento (tráfego de fundo) e também envios de pacotes vindo dos Txs.
   */
  private void setFirstEvents() {
    resetSimulation();

    // Prepara o tráfego de fundo.
    if (SimulationProperties.isWithCongestion()) {
      Event firstCongestionPack =
          new Event(
              EventType
                  .CongestionPacketIntoRouter); // Evento "Pacote de Tráfego de Fundo chegar ao
                                                // roteador"
      firstCongestionPack.setTime(random.generateExponential(1.0 / congestionPacketFrequency));
      getEvents().add(firstCongestionPack);
    }

    // Prepara os servidores para transmissão de Tx-Rx.
    for (Tx tx : getSystem().getTxs()) {
      double propagationDelay = setPropagationDelay(tx);
      // Fator aleatório além da propagação e da transmissão.
      // É zero por padrão, mas este intervalo muda nos cenários 2 e 3.
      double randTime = random.generateDouble() * SimulationProperties.getAssyncInterval();

      Event packSent = new Event(EventType.TxPacketHeadsToRouter); // Evento "Pacote sair do Tx"
      packSent.setTime(randTime + transmissionTime);
      packSent.setTxIndex(tx.getIndex());
      getEvents().add(packSent);

      Pack pack = tx.sendPacket(randTime);
      Event firstTcpPack =
          new Event(EventType.TxPacketIntoRouter); // Evento "Pacote TCP chegar ao roteador"
      firstTcpPack.setTime(randTime + transmissionTime + propagationDelay);
      firstTcpPack.setPack(pack);
      getEvents().add(firstTcpPack);

      Event possibleTimeout = new Event(EventType.Timeout); // Evento "Possível Timeout do pacote"
      possibleTimeout.setTime(randTime + tx.getRTO());
      possibleTimeout.setTxIndex(tx.getIndex());
      pack.setTimeout(possibleTimeout);
      getEvents().add(possibleTimeout);
    }
  }