The following defines the SimpleQueue class.

SimpleQueue <- R6::R6Class(
  "SimpleQueue",

  public = list(
    initialize = function(id, service_time){

      #initialize with unique turnstile id and zero agents waiting
      private$.id <- id
      private$.service_time <- service_time
      private$.agents_waiting <- 0
    },

    add_agent = function(current_time){
      if(!missing(current_time)){
        private$.agents_waiting <- private$.agents_waiting + 1

        if(is.na(private$.service_complete_at)){
          private$.service_complete_at <- current_time + private$.service_time
        }
      } else {
        stop("Cannot add agent without knowing the current time\n")
      }
      invisible(self)
    },

    remove_agent = function(){

      private$.agents_waiting = private$.agents_waiting - 1
      private$.service_complete_at <- private$.service_complete_at + private$.service_time

      if (private$.agents_waiting <= 0){
        private$.agents_waiting = 0
        private$.service_complete_at <- NA
      }

      invisible(self)
    },

    calculate_wait_time = function(current_time){
      if(missing(current_time)){
        stop("Cannot calculate current wait time without current time\n")
      } else {
        current_wait_time <- (private$.service_time * (private$.agents_waiting - 1)) +
          (private$.service_complete_at - current_time)

        #if we return a negative wait time, that means no one is in line, so
        #replace with zero
        if (is.na(current_wait_time) || current_wait_time < 0){
          current_wait_time <- 0
        }

        return(current_wait_time)
      }
    }
  ),

  active = list(
    id = function(value){
      if(missing(value)){
        private$.id
      } else{
        stop("Cannot set `$turnstile_id` after instantiation\n")
      }
    },

    service_time = function(value){
      if(missing(value)){
        private$.service_time
      } else{
        stop("Cannot change `$service_time` after instantiation\n")
      }
    },

    agents_waiting = function(value){
      if(missing(value)){
        private$.agents_waiting
      } else {
        stop("Cannot set `$agents_waiting`\n")
      }
    },

    service_complete_at = function(value){
      if(missing(value)){
        private$.service_complete_at
      } else{
        stop("cannot set `$service_complete_at`, use `$add_agent\n`")
      }
    }
  ),

  private = list(
    .id = -1,
    .service_time = -1,
    .agents_waiting = -1,
    .service_complete_at = NA
  )
)

Testing

We test the class as follows:

Creating queues

queue_id = 1
queue_service_time = 5

#create queue with above
test_queue <- SimpleQueue$new(queue_id, queue_service_time)

#test default configuration
stopifnot(test_queue$id == queue_id)
stopifnot(test_queue$service_time == queue_service_time)
stopifnot(test_queue$agents_waiting == 0)
stopifnot(is.na(test_queue$service_complete_at))

#if we made it here, the test passed
print("Pass: Creating Queue")
## [1] "Pass: Creating Queue"

Adding agents to queues

queue_id = 1
queue_service_time = 5

#create queue with above
test_queue <- SimpleQueue$new(queue_id, queue_service_time)

#define current time
cur_time = 0

#add 3 agents at cur_time (using method chaining)
test_queue$add_agent(cur_time)$add_agent(cur_time)$add_agent(cur_time)

#setup tests
stopifnot(test_queue$agents_waiting == 3)
#we know that the very first item should be complete at cur_time + queue_service_time
stopifnot(test_queue$service_complete_at == cur_time + queue_service_time) 

#recreate queue with above
test_queue <- SimpleQueue$new(queue_id, queue_service_time)

#add agents with a current time other than zero
cur_time = 13

#add 3 agents at cur_time (using method chaining)
test_queue$add_agent(cur_time)$add_agent(cur_time)$add_agent(cur_time)

#setup tests
stopifnot(test_queue$agents_waiting == 3)
#we know that the very first item should be complete at cur_time + queue_service_time
stopifnot(test_queue$service_complete_at == cur_time + queue_service_time) 

#recreate queue with above
test_queue <- SimpleQueue$new(queue_id, queue_service_time)

#add agents with a current time other than zero
cur_time = 13.2

#add 3 agents at cur_time (using method chaining)
test_queue$add_agent(cur_time)$add_agent(cur_time)$add_agent(cur_time)

#setup tests
stopifnot(test_queue$agents_waiting == 3)
#we know that the very first item should be complete at cur_time + queue_service_time
stopifnot(test_queue$service_complete_at == cur_time + queue_service_time) 

#if we made it here, the test passesd
print("Pass: Adding Agents")
## [1] "Pass: Adding Agents"

Removing agents from queues

queue_id = 10
queue_service_time = 2.5

#create queue with above
test_queue <- SimpleQueue$new(queue_id, queue_service_time)

cur_time = 2.2

#add 3 agents at cur_time (using method chaining)
test_queue$add_agent(cur_time)$add_agent(cur_time)$add_agent(cur_time)

#remove 2 agents (using method chaining)
test_queue$remove_agent()$remove_agent()

#setup tests
stopifnot(test_queue$agents_waiting == 1)
#since all three agents where added at the same time, the last agent will be done
#after waiting for itself and the two agents in front of it, mathematically
#cur_time + (3*\mu)
stopifnot(test_queue$service_complete_at == cur_time + 3 * queue_service_time)

#remove the final agent and then some
#unless bounds checking in place, should cause negative agents waiting
test_queue$remove_agent()$remove_agent()$remove_agent()

stopifnot(test_queue$agents_waiting == 0)
stopifnot(is.na(test_queue$service_complete_at))

#if we made it here, the test passed
print("Pass: Removing Agents")
## [1] "Pass: Removing Agents"

Calculating wait times

queue_id = 10
queue_service_time = 2.5

#create queue with above
test_queue <- SimpleQueue$new(queue_id, queue_service_time)

#set current time
cur_time = 0

#test that with nothing it it, the wait time is zero
stopifnot(test_queue$calculate_wait_time(cur_time) == 0)

#add an agent at cur_time + 1
test_queue$add_agent(cur_time + 1)

#the wait time should be the time it was added plus the service time
stopifnot(test_queue$calculate_wait_time(cur_time) == cur_time + 1 + queue_service_time)

#test wait time after waiting 2
stopifnot(test_queue$calculate_wait_time(cur_time + 2) == (cur_time + 1 + queue_service_time) - 2)

#add another agent at cur_time + 2
test_queue$add_agent(cur_time + 2)

#our wait time should now be the time for the newly added agent plus the remaining time for currently being served agent which entered at time cur_time + 1 and has a service time of 2.5 seconds
stopifnot(test_queue$calculate_wait_time(cur_time + 2) == (1*queue_service_time) + 2.5 - (2 - 1))

#also test that since everything should be over by 2.5 * 2 = 5 + 1 (starting offset), anytime after 6 has a wait of zero
stopifnot(test_queue$calculate_wait_time(10) == 0)
stopifnot(test_queue$calculate_wait_time(6) == 0)

#remove agent
test_queue$remove_agent()

#test that after removing the starting agent, the remaining time until the second agent is finished
#is simply the service time
stopifnot(test_queue$calculate_wait_time(cur_time + 1 + 2.5) == queue_service_time)

#check that the new service c
print("Pass: Current Wait Time Calculations")
## [1] "Pass: Current Wait Time Calculations"