# Copyright (C) 2015-2016 Bart Smeets and Iñaki Ucar
# Copyright (C) 2016-2022 Iñaki Ucar
#
# This file is part of simmer.
#
# simmer 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 2 of the License, or
# (at your option) any later version.
#
# simmer 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 simmer. If not, see <http://www.gnu.org/licenses/>.
#' Convenience Functions for Generators
#'
#' These convenience functions facilitate the definition of generators of arrivals
#' for some common cases.
#'
#' @return Returns a generator function (a closure).
#' @seealso \code{\link{add_generator}}.
#' @name generators
#' @examples
#' ## common to all examples below
#' # some trajectory
#' t0 <- trajectory() %>%
#' timeout(0)
#' # some distribution
#' distr <- function() runif(1, 1, 2)
#'
NULL
#' @rdname generators
#' @param ... a vector or multiple parameters of times at which to initiate an arrival.
#'
#' @details \code{\link{at}} generates arrivals at specific absolute times.
#' @export
#' @examples
#' # arrivals at 0, 1, 10, 30, 40 and 43
#' simmer() %>%
#' add_generator("dummy", t0, at(0, c(1,10,30), 40, 43)) %>%
#' run(100) %>%
#' get_mon_arrivals()
#'
at <- function(...) {
time_vec <- c(...)
time_diffs <- c(time_vec[1], diff(time_vec))
function() return(c(time_diffs, -1))
}
#' @rdname generators
#' @param start_time the time at which to launch the initial arrival
#' (numeric or function).
#' @param dist a function modelling the interarrival times. It is supposed to be
#' an infinite source of values \code{>= 0} (e.g., \code{rexp} and the like). If
#' the function provided returns any negative value, the behaviour is undefined.
#' @param arrive if set to \code{TRUE} (default) the first arrival will be
#' generated at \code{start_time} and will follow \code{dist} from then on.
#' If set to \code{FALSE}, will initiate \code{dist} at \code{start_time}
#' (and the first arrival will most likely start at a time later than
#' \code{start_time}).
#'
#' @details \code{\link{from}} generates inter-arrivals following a given distribution
#' with a specified start time.
#' union of the last two.
#' @export
#' @examples
#' # apply distribution starting at 5 (and no end)
#' simmer() %>%
#' add_generator("dummy", t0, from(5, distr)) %>%
#' run(10) %>%
#' get_mon_arrivals()
#'
from <- function(start_time, dist, arrive=TRUE) {
replace_env(start_time, dist)
.started <- FALSE
function() {
if (!.started) {
.started <<- TRUE
if (arrive) {
dt <- getval(start_time)
} else {
dt <- dist()
if (dt[1] >= 0)
dt[1] <- dt[1] + getval(start_time)
}
} else {
dt <- dist()
}
dt
}
}
#' @rdname generators
#' @param stop_time the time at which to stop the generator (numeric or function).
#'
#' @details \code{\link{to}} generates inter-arrivals following a given
#' distribution with a specified stop time.
#' @export
#' @examples
#' # apply distribution until 5 (starting at 0)
#' simmer() %>%
#' add_generator("dummy", t0, to(5, distr)) %>%
#' run(10) %>%
#' get_mon_arrivals()
#'
to <- function(stop_time, dist) {
replace_env(stop_time, dist)
.counter <- 0
function() {
dt <- dist()
len <- length(dt)
dt <- dt[cumsum(dt) + .counter < getval(stop_time)]
.counter <<- .counter + sum(dt)
if (len == length(dt)) return(dt)
return(c(dt, -1))
}
}
#' @rdname generators
#' @param every repeat with this time cycle (numeric or function).
#'
#' @details \code{\link{from_to}} is the union of \code{from} and \code{to}.
#' @export
#' @examples
#' # apply distribution from 8 to 16 h every 24 h:
#' simmer() %>%
#' add_generator("dummy", t0, from_to(8, 16, distr, every=24)) %>%
#' run(48) %>%
#' get_mon_arrivals()
#'
from_to <- function(start_time, stop_time, dist, arrive=TRUE, every=NULL) {
replace_env(start_time, stop_time, every, dist)
.started <- FALSE
.init <- 0
.counter <- 0
.every <- 0
function() {
while (TRUE){
if (!.started) {
.started <<- TRUE
if (arrive) {
dt <- getval(start_time) + .every - .init
} else {
dt <- dist()
if (dt[1] >= 0)
dt[1] <- dt[1] + getval(start_time) + .every - .init
}
} else {
dt <- dist()
}
len <- length(dt)
dt <- dt[cumsum(dt) + .counter < getval(stop_time) + .every]
.counter <<- .counter + sum(dt)
if (len == length(dt))
return(dt)
if (is.null(every))
return(c(dt, -1))
if (length(dt))
return(dt)
.started <<- FALSE
.init <<- .counter
.every <<- .every + getval(every)
}
}
}
#' @rdname generators
#' @param n an integer or a callable object (a function) which must return
#' a number of arrivals to generate when activated.
#'
#' @details \code{\link{when_activated}} sets up an initially inactive generator
#' which generates \code{n} arrivals each time it is activated from any
#' trajectory using the activity \code{\link{activate}}.
#' @export
#' @examples
#' # triggering arrivals on demand from a trajectory
#' t1 <- trajectory() %>%
#' activate("dummy")
#'
#' simmer() %>%
#' add_generator("dummy", t0, when_activated()) %>%
#' add_generator("trigger", t1, at(2)) %>%
#' run() %>%
#' get_mon_arrivals()
#'
when_activated <- function(n=1) {
replace_env(n)
first <- TRUE
function() {
if (first) {
first <<- FALSE
return(-1)
}
c(rep(0, getval(n)), -1)
}
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.