SE450: Simulation: TimeServer Implementation [8/13] Previous pageContentsNext page

file:Agent.java [source] [doc-public] [doc-private]
01
02
03
04
05
package agent;

public interface Agent {
  public void run();
}

file:TimeServer.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
package agent;

public interface TimeServer {
  public double currentTime();
  public void enqueue(double waketime, Agent thing);
  public void run(double duration);
  public void addObserver(java.util.Observer o);
  public void deleteObserver(java.util.Observer o);
}
If you want to use this for the project:

The idea is that whenever an agent adds itself to the timeserver, it
specifies its waketime.  The waketime must be greater than
currenttime, or the enqueue method should throw an exception.  The
timeserver orders agents by their waketime, so the one to wake first is
always at the front.  So in order to run the next agent, the first
agent is dequeued, and then the currenttime is set to its waketime,
then the agent is run.

For a car:
Start the car with waketime=currenttime.
When the car is run(), it should re-enqueue itself with
waketime=currenttime+timestep.

For a light:
Start the lightController with waketime=currenttime.
When the lightController is run(), it should re-enqueue itself with
waketime=currenttime+this._state.getDuration(), where the state
duration is whatever is appropriate for this state.

For a source:
Start the source with waketime=currenttime.
When the source is run(), it should re-enqueue itself with
waketime=currenttime+this._carCreationInterval, where the interval
indicates the time between car creations for this source.        

I implemented the TimeServer using a linked list with a dummy node.

file:TimeServerLinked.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package agent;

import java.util.Observable;

public final class TimeServerLinked extends Observable implements TimeServer {
  private static final class Node {
    final double waketime;
    final Agent agent;
    Node next;

    public Node(double waketime, Agent agent, Node next) {
      this.waketime = waketime;
      this.agent = agent;
      this.next = next;
    }
  }
  private double currentTime;
  private int size;
  private Node head;

  /*
   * Invariant: head != null
   * Invariant: head.agent == null
   * Invariant: (size == 0) iff (head.next == null)
   */
  public TimeServerLinked() {
    size = 0;
    head = new Node(0, null, null);
  }

  public String toString() {
    StringBuilder sb = new StringBuilder("[");
    Node node = head.next;
    String sep = "";
    while (node != null) {
      sb.append(sep).append("(").append(node.waketime).append(",")
      .append(node.agent).append(")");
      node = node.next;
      sep = ";";
    }
    sb.append("]");
    return (sb.toString());
  }

  public double currentTime() {
    return currentTime;
  }

  public void enqueue(double waketime, Agent agent)
      throws IllegalArgumentException
  {
    if (waketime < currentTime)
      throw new IllegalArgumentException();
    Node prevElement = head;
    while ((prevElement.next != null) &&
        (prevElement.next.waketime <= waketime)) {
      prevElement = prevElement.next;
    }
    Node newElement = new Node(waketime, agent, prevElement.next);
    prevElement.next = newElement;
    size++;
  }

  Agent dequeue()
  {
    if (size < 1)
      throw new java.util.NoSuchElementException();
    Agent rval = head.next.agent;
    head.next = head.next.next;
    size--;
    return rval;
  }

  int size() {
    return size;
  }

  boolean empty() {
    return size() == 0;
  }

  public void run(double duration) {
    double endtime = currentTime + duration;
    while ((!empty()) && (head.next.waketime <= endtime)) {
      if ((currentTime - head.next.waketime) < 1e-09) {
        super.setChanged();
        super.notifyObservers();
      }
      currentTime = head.next.waketime;
      dequeue().run();
    }
    currentTime = endtime;
  }
}

Note below how one tests TimeServer without any "real" agents. Instead we use Mock Objects. We also saw this in the tests for CommandHistoryObj. Tools such as easymock make this easy.

file:TimeServerTEST.java [source] [doc-public] [doc-private]
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package agent;
import static org.junit.Assert.*;
import org.junit.Test;

public class TimeServerTEST {
  TimeServerQueue q = new TimeServerQueue();
  // TimeServerLinked q = new TimeServerLinked();

  @Test
  public void testThatEmptySizeIsZero() {
    assertEquals(0, q.size());
  }

  @Test
  public void testThatDequeueOnEmptyThrowsIndexOutOfBoundsException() {
    boolean exceptionOccurred = false;

    try {
      q.dequeue();
    } catch (java.util.NoSuchElementException e) {
      exceptionOccurred = true;
    }

    assertTrue(exceptionOccurred);
  }

  @Test
  public void testThatEnqueueFollowedByDequeueReturnsSameReference() {
    class TestThatEnqueueFollowedByDequeueReturnsSameReference
    implements Agent
    {
      public void run() {}
    }

    Agent x1 = new TestThatEnqueueFollowedByDequeueReturnsSameReference();
    q.enqueue(0, x1);
    assertSame(x1, q.dequeue());
    assertEquals(0, q.size());
  }

  @Test
  public void testThatElementsAreInsertedInOrder() {
    class TestThatElementsAreInsertedInOrder implements Agent {
      public void run() {}
    }

    Agent x1 = new TestThatElementsAreInsertedInOrder();
    Agent x2 = new TestThatElementsAreInsertedInOrder();
    q.enqueue(0, x2);
    q.enqueue(1, x1);
    assertSame(x2, q.dequeue());
    assertSame(x1, q.dequeue());
    q.enqueue(1, x1);
    q.enqueue(0, x2);
    assertSame(x2, q.dequeue());
    assertSame(x1, q.dequeue());
    q.enqueue(0, x1);
    q.enqueue(0, x2);
    assertSame(x1, q.dequeue());
    assertSame(x2, q.dequeue());
    q.enqueue(0, x2);
    q.enqueue(0, x1);
    assertSame(x2, q.dequeue());
    assertSame(x1, q.dequeue());
  }

  @Test
  public void testToString() {
    class TestToString implements Agent {
      public void run() {}
      public String toString() { return "x"; }
    }

    q.enqueue(0, new TestToString());
    q.enqueue(1, new TestToString());
    assertEquals("[(0.0,x);(1.0,x)]", q.toString());
  }

  @Test
  public void testCurrentTime() {
    class TestCurrentTime implements Agent {
      public void run() {}
    }

    double expected = 1230;
    q.enqueue(expected, new TestCurrentTime());

    assertEquals(0.0, q.currentTime(), .1e-6);
    q.run(expected);

    assertEquals(expected, q.currentTime(), .1e-6);
  }

  private double scratch;
  @Test
  public void testDoActionsAtOrBefore() {
    class TestDoActionsAtOrBefore implements Agent {
      private double myScratch;
      TestDoActionsAtOrBefore(double myScratch) {
        this.myScratch = myScratch;
      }
      public void run() {
        scratch = myScratch;
      }
    }

    double time1 = 12;
    double time2 = 23;
    double value1 = 42;
    double value2 = 27;

    q.enqueue(time1, new TestDoActionsAtOrBefore(value1));

    scratch = 0;
    q.run(time1 - 1);
    assertEquals(0.0, scratch, .1e-6);

    scratch = 0;
    q.run(1);
    assertEquals(value1, scratch, .1e-6);

    q.enqueue(time2, new TestDoActionsAtOrBefore(value2));

    scratch = 0;
    q.run(time2);
    assertEquals(value2, scratch, .1e-6);
  }
}

Previous pageContentsNext page