1 | #region License Information |
---|
2 | /* |
---|
3 | * This file is part of SimSharp which is licensed under the MIT license. |
---|
4 | * See the LICENSE file in the project root for more information. |
---|
5 | */ |
---|
6 | #endregion |
---|
7 | |
---|
8 | using System; |
---|
9 | using System.Collections.Generic; |
---|
10 | |
---|
11 | namespace SimSharp { |
---|
12 | /// <summary> |
---|
13 | /// A container holds a variable amount of a single continuous entity, e.g. water, coal, grain, etc. |
---|
14 | /// |
---|
15 | /// Put and Get are in FIFO order only when they can be satisfied. |
---|
16 | /// Any put or get that can be satisfied takes precedence. |
---|
17 | /// Put events that attempt to add more to the Container than there is capacity for and |
---|
18 | /// Get events that remove more than there is are backlogged. |
---|
19 | /// </summary> |
---|
20 | public class Container { |
---|
21 | |
---|
22 | public double Capacity { get; protected set; } |
---|
23 | |
---|
24 | public double Level { get; protected set; } |
---|
25 | |
---|
26 | protected Simulation Environment { get; private set; } |
---|
27 | |
---|
28 | protected Queue<ContainerPut> PutQueue { get; private set; } |
---|
29 | protected Queue<ContainerGet> GetQueue { get; private set; } |
---|
30 | protected SimplePriorityQueue<Event, double> WhenAtLeastQueue { get; private set; } |
---|
31 | protected SimplePriorityQueue<Event, double> WhenAtMostQueue { get; private set; } |
---|
32 | protected List<Event> WhenChangeQueue { get; private set; } |
---|
33 | |
---|
34 | public ITimeSeriesMonitor Fillrate { get; set; } |
---|
35 | public ITimeSeriesMonitor PutQueueLength { get; set; } |
---|
36 | public ISampleMonitor PutWaitingTime { get; set; } |
---|
37 | public ITimeSeriesMonitor GetQueueLength { get; set; } |
---|
38 | public ISampleMonitor GetWaitingTime { get; set; } |
---|
39 | |
---|
40 | public Container(Simulation environment, double capacity = double.MaxValue, double initial = 0) { |
---|
41 | if (capacity <= 0) throw new ArgumentException("Capacity must be > 0", "capacity"); |
---|
42 | if (initial < 0) throw new ArgumentException("Initial must be >= 0", "initial"); |
---|
43 | if (initial > capacity) throw new ArgumentException("Initial must be <= capacity", "initial"); |
---|
44 | Environment = environment; |
---|
45 | Capacity = capacity; |
---|
46 | Level = initial; |
---|
47 | PutQueue = new Queue<ContainerPut>(); |
---|
48 | GetQueue = new Queue<ContainerGet>(); |
---|
49 | WhenAtLeastQueue = new SimplePriorityQueue<Event, double>(); |
---|
50 | WhenAtMostQueue = new SimplePriorityQueue<Event, double>(new ReverseComparer<double>()); |
---|
51 | WhenChangeQueue = new List<Event>(); |
---|
52 | } |
---|
53 | |
---|
54 | public virtual ContainerPut Put(double amount) { |
---|
55 | if (amount < 0) throw new ArgumentException("Cannot put negative amount", "amount"); |
---|
56 | if (amount > Capacity) throw new ArgumentException("Cannot put more than capacity", "amount"); |
---|
57 | var put = new ContainerPut(Environment, TriggerGet, amount); |
---|
58 | PutQueue.Enqueue(put); |
---|
59 | TriggerPut(); |
---|
60 | return put; |
---|
61 | } |
---|
62 | |
---|
63 | public virtual ContainerGet Get(double amount) { |
---|
64 | if (amount < 0) throw new ArgumentException("Cannot get negative amount", "amount"); |
---|
65 | if (amount > Capacity) throw new ArgumentException("Cannot get more than capacity", "amount"); |
---|
66 | var get = new ContainerGet(Environment, TriggerPut, amount); |
---|
67 | GetQueue.Enqueue(get); |
---|
68 | TriggerGet(); |
---|
69 | return get; |
---|
70 | } |
---|
71 | |
---|
72 | public virtual Event WhenAtLeast(double level) { |
---|
73 | var whenAtLeast = new Event(Environment); |
---|
74 | WhenAtLeastQueue.Enqueue(whenAtLeast, level); |
---|
75 | TriggerWhenAtLeast(); |
---|
76 | return whenAtLeast; |
---|
77 | } |
---|
78 | |
---|
79 | public virtual Event WhenFull() { |
---|
80 | return WhenAtLeast(Capacity); |
---|
81 | } |
---|
82 | |
---|
83 | public virtual Event WhenAtMost(double level) { |
---|
84 | var whenAtMost = new Event(Environment); |
---|
85 | WhenAtMostQueue.Enqueue(whenAtMost, level); |
---|
86 | TriggerWhenAtMost(); |
---|
87 | return whenAtMost; |
---|
88 | } |
---|
89 | |
---|
90 | public virtual Event WhenEmpty() { |
---|
91 | return WhenAtMost(0); |
---|
92 | } |
---|
93 | |
---|
94 | public virtual Event WhenChange() { |
---|
95 | var whenChange = new Event(Environment); |
---|
96 | WhenChangeQueue.Add(whenChange); |
---|
97 | return whenChange; |
---|
98 | } |
---|
99 | |
---|
100 | protected virtual void DoPut(ContainerPut put) { |
---|
101 | if (Capacity - Level >= put.Amount) { |
---|
102 | PutWaitingTime?.Add(Environment.ToDouble(Environment.Now - put.Time)); |
---|
103 | Level += put.Amount; |
---|
104 | put.Succeed(); |
---|
105 | } |
---|
106 | } |
---|
107 | |
---|
108 | protected virtual void DoGet(ContainerGet get) { |
---|
109 | if (Level >= get.Amount) { |
---|
110 | GetWaitingTime?.Add(Environment.ToDouble(Environment.Now - get.Time)); |
---|
111 | Level -= get.Amount; |
---|
112 | get.Succeed(); |
---|
113 | } |
---|
114 | } |
---|
115 | |
---|
116 | protected virtual void TriggerPut(Event @event = null) { |
---|
117 | while (PutQueue.Count > 0) { |
---|
118 | var put = PutQueue.Peek(); |
---|
119 | DoPut(put); |
---|
120 | if (put.IsTriggered) { |
---|
121 | PutQueue.Dequeue(); |
---|
122 | TriggerWhenAtLeast(); |
---|
123 | TriggerWhenChange(); |
---|
124 | } else break; |
---|
125 | } |
---|
126 | Fillrate?.UpdateTo(Level / Capacity); |
---|
127 | PutQueueLength?.UpdateTo(PutQueue.Count); |
---|
128 | } |
---|
129 | |
---|
130 | protected virtual void TriggerGet(Event @event = null) { |
---|
131 | while (GetQueue.Count > 0) { |
---|
132 | var get = GetQueue.Peek(); |
---|
133 | DoGet(get); |
---|
134 | if (get.IsTriggered) { |
---|
135 | GetQueue.Dequeue(); |
---|
136 | TriggerWhenAtMost(); |
---|
137 | TriggerWhenChange(); |
---|
138 | } else break; |
---|
139 | } |
---|
140 | Fillrate?.UpdateTo(Level / Capacity); |
---|
141 | GetQueueLength?.UpdateTo(GetQueue.Count); |
---|
142 | } |
---|
143 | |
---|
144 | protected virtual void TriggerWhenAtLeast() { |
---|
145 | while (WhenAtLeastQueue.Count > 0 && Level >= WhenAtLeastQueue.Peek) { |
---|
146 | var whenAtLeast = WhenAtLeastQueue.Dequeue(); |
---|
147 | whenAtLeast.Succeed(); |
---|
148 | } |
---|
149 | } |
---|
150 | |
---|
151 | protected virtual void TriggerWhenAtMost() { |
---|
152 | while (WhenAtMostQueue.Count > 0 && Level <= WhenAtMostQueue.Peek) { |
---|
153 | var whenAtMost = WhenAtMostQueue.Dequeue(); |
---|
154 | whenAtMost.Succeed(); |
---|
155 | } |
---|
156 | } |
---|
157 | |
---|
158 | protected virtual void TriggerWhenChange() { |
---|
159 | if (WhenChangeQueue.Count == 0) return; |
---|
160 | foreach (var evt in WhenChangeQueue) |
---|
161 | evt.Succeed(); |
---|
162 | WhenChangeQueue.Clear(); |
---|
163 | } |
---|
164 | } |
---|
165 | } |
---|