| 1 | package tmcsim.highwaymodel; |
|---|
| 2 | |
|---|
| 3 | import tmcsim.highwaymodel.LoopDetector.DOTCOLOR; |
|---|
| 4 | import java.util.ArrayList; |
|---|
| 5 | import java.util.List; |
|---|
| 6 | import org.w3c.dom.Document; |
|---|
| 7 | import org.w3c.dom.Element; |
|---|
| 8 | |
|---|
| 9 | /** |
|---|
| 10 | * A Station (VDS or Vehicle Detector Station) represents a group of lane detectors |
|---|
| 11 | * across all lanes in ONE direction at a particular point on a highway. A station is identified |
|---|
| 12 | * by its highway number and postmile. A station has an associated direction |
|---|
| 13 | * used to establish which direction is Main and which is Opposite. The MLTotVol |
|---|
| 14 | * and OppTotVol for a station can be dynamically updated. A station has other |
|---|
| 15 | * attributes: lineNum, vdsID, drop, and location which are used by the FEP. A |
|---|
| 16 | * station can be compared to other stations by its postmile. |
|---|
| 17 | * |
|---|
| 18 | * @author John A. Torres, jdalbey |
|---|
| 19 | * @version 9/10/2017, 3/22/2019 |
|---|
| 20 | */ |
|---|
| 21 | public final class Station implements Comparable |
|---|
| 22 | { |
|---|
| 23 | /* Static Station meta data */ |
|---|
| 24 | final public int lineID; |
|---|
| 25 | final public int vdsID; // double check |
|---|
| 26 | final public int drop; |
|---|
| 27 | final public String location; |
|---|
| 28 | final public List<LoopDetector> loops; |
|---|
| 29 | final public int routeNumber; |
|---|
| 30 | final public double postmile; |
|---|
| 31 | final public DIRECTION direction; |
|---|
| 32 | |
|---|
| 33 | /* Dynamic Station data */ |
|---|
| 34 | private int totalVolume; |
|---|
| 35 | |
|---|
| 36 | /* Constructor */ |
|---|
| 37 | public Station(int lineID, int vdsID, int drop, |
|---|
| 38 | String location, List<LoopDetector> loops, int hwy, |
|---|
| 39 | DIRECTION direction, double postmile) |
|---|
| 40 | { |
|---|
| 41 | this.lineID = lineID; |
|---|
| 42 | this.vdsID = vdsID; |
|---|
| 43 | this.drop = drop; |
|---|
| 44 | this.loops = loops; |
|---|
| 45 | this.location = location; |
|---|
| 46 | this.postmile = postmile; |
|---|
| 47 | this.direction = direction; |
|---|
| 48 | this.routeNumber = hwy; |
|---|
| 49 | |
|---|
| 50 | this.totalVolume = calcTotalVolume(); |
|---|
| 51 | } |
|---|
| 52 | |
|---|
| 53 | /** |
|---|
| 54 | * Calculates the total lane Volume. |
|---|
| 55 | * Reserved for future use. |
|---|
| 56 | * @return total lane volume. |
|---|
| 57 | */ |
|---|
| 58 | private int calcTotalVolume() |
|---|
| 59 | { |
|---|
| 60 | int mlTotVol = 0; |
|---|
| 61 | for (LoopDetector loop : loops) |
|---|
| 62 | { |
|---|
| 63 | mlTotVol += loop.vol; |
|---|
| 64 | } |
|---|
| 65 | return mlTotVol; |
|---|
| 66 | } |
|---|
| 67 | |
|---|
| 68 | /** |
|---|
| 69 | * Compare this Station to another by postmile. Note: This might be better |
|---|
| 70 | * as a Comparator since it checks only one field. |
|---|
| 71 | */ |
|---|
| 72 | @Override |
|---|
| 73 | public int compareTo(Object otherStation) |
|---|
| 74 | { |
|---|
| 75 | // check for identity |
|---|
| 76 | if (this == otherStation) |
|---|
| 77 | { |
|---|
| 78 | return 0; |
|---|
| 79 | } |
|---|
| 80 | // check that Object is of type Station, if not throw exception |
|---|
| 81 | if (!(otherStation instanceof Station)) |
|---|
| 82 | { |
|---|
| 83 | throw new ClassCastException("A Station object expected."); |
|---|
| 84 | } |
|---|
| 85 | |
|---|
| 86 | // get difference of values |
|---|
| 87 | double otherStationPostmile = ((Station) otherStation).postmile; |
|---|
| 88 | double val = this.postmile - otherStationPostmile; |
|---|
| 89 | |
|---|
| 90 | // set appropriate comparable return value |
|---|
| 91 | int retval = 0; |
|---|
| 92 | if (val > 0) |
|---|
| 93 | { |
|---|
| 94 | retval = 1; |
|---|
| 95 | } |
|---|
| 96 | else if (val < 0) |
|---|
| 97 | { |
|---|
| 98 | retval = -1; |
|---|
| 99 | } |
|---|
| 100 | |
|---|
| 101 | return retval; |
|---|
| 102 | } |
|---|
| 103 | |
|---|
| 104 | /** |
|---|
| 105 | * See if this station matches the specified attributes. |
|---|
| 106 | * @param dir |
|---|
| 107 | * @param postmile |
|---|
| 108 | * @return true if this station's attributes match the given ones. |
|---|
| 109 | */ |
|---|
| 110 | public boolean matches(DIRECTION dir, double postmile) |
|---|
| 111 | { |
|---|
| 112 | double val = this.postmile - postmile; |
|---|
| 113 | return (Math.abs(val) < 0.01) && this.direction.equals(dir); |
|---|
| 114 | } |
|---|
| 115 | /** |
|---|
| 116 | * Determine which lane fields to update based on given direction and update |
|---|
| 117 | * all the loop detectors with the given color. |
|---|
| 118 | * |
|---|
| 119 | * @param direction desired highway direction |
|---|
| 120 | * @param dotColor desired dot color |
|---|
| 121 | */ |
|---|
| 122 | public void updateByDirection(DIRECTION direction, DOTCOLOR dotColor) |
|---|
| 123 | { |
|---|
| 124 | // Is this station going in the desired direction? |
|---|
| 125 | if (direction.equals(this.direction)) |
|---|
| 126 | { |
|---|
| 127 | outputUpdateMessage(dotColor, direction.toString()); |
|---|
| 128 | |
|---|
| 129 | // Set the values for all lanes at this station |
|---|
| 130 | for (LoopDetector loop : loops) |
|---|
| 131 | { |
|---|
| 132 | // Set loop detector attributes given the desired color |
|---|
| 133 | loop.setAttributes(dotColor); |
|---|
| 134 | } |
|---|
| 135 | |
|---|
| 136 | this.totalVolume = calcTotalVolume(); |
|---|
| 137 | } |
|---|
| 138 | } |
|---|
| 139 | /** |
|---|
| 140 | * Compute the color for the lanes in a given direction. |
|---|
| 141 | * @return DOTCOLOR of this station's traffic flow |
|---|
| 142 | */ |
|---|
| 143 | public DOTCOLOR getColor() |
|---|
| 144 | { |
|---|
| 145 | /* For now just use the color of the first lane. |
|---|
| 146 | * TODO: Average the color in ALL the lanes */ |
|---|
| 147 | |
|---|
| 148 | String laneDir = ""; |
|---|
| 149 | |
|---|
| 150 | // FOR FUTURE USE we will need to examine all detectors for this station |
|---|
| 151 | // and perform an average |
|---|
| 152 | // for (LoopDetector loop : loops) |
|---|
| 153 | { |
|---|
| 154 | // for now, Return color according to loop volume of first lane |
|---|
| 155 | if (loops.get(0).vol == 1) |
|---|
| 156 | { |
|---|
| 157 | return DOTCOLOR.RED; |
|---|
| 158 | } |
|---|
| 159 | if (loops.get(0).vol == 3) |
|---|
| 160 | { |
|---|
| 161 | return DOTCOLOR.YELLOW; |
|---|
| 162 | } |
|---|
| 163 | if (loops.get(0).vol == 0) |
|---|
| 164 | { |
|---|
| 165 | return DOTCOLOR.GREEN; |
|---|
| 166 | } |
|---|
| 167 | } |
|---|
| 168 | |
|---|
| 169 | // Default case for invalid data |
|---|
| 170 | return DOTCOLOR.GREEN; |
|---|
| 171 | } |
|---|
| 172 | |
|---|
| 173 | /** |
|---|
| 174 | * Output for updateByDirection. Logs the update to the console. |
|---|
| 175 | * |
|---|
| 176 | * @param dotcolor |
|---|
| 177 | * @param OPP_ML |
|---|
| 178 | */ |
|---|
| 179 | private void outputUpdateMessage(DOTCOLOR dotcolor, String OPP_ML) |
|---|
| 180 | { |
|---|
| 181 | System.out.printf("Updating %-3.3s %-5.5s %-3.3s lanes\t %-12.12s " |
|---|
| 182 | + "at postmile %-6.6s to %-7.7s\n", |
|---|
| 183 | Integer.toString(this.routeNumber), this.direction.name(), |
|---|
| 184 | OPP_ML, this.location, Double.toString(this.postmile), |
|---|
| 185 | dotcolor.name()); |
|---|
| 186 | } |
|---|
| 187 | |
|---|
| 188 | /** |
|---|
| 189 | * Enum for freeway direction. |
|---|
| 190 | * |
|---|
| 191 | * @author John A. Torres |
|---|
| 192 | * @version 9/10/2017 |
|---|
| 193 | */ |
|---|
| 194 | public static enum DIRECTION |
|---|
| 195 | { |
|---|
| 196 | NORTH, |
|---|
| 197 | SOUTH, |
|---|
| 198 | EAST, |
|---|
| 199 | WEST; |
|---|
| 200 | |
|---|
| 201 | // All the first letters of the values, in order. |
|---|
| 202 | private static String allLetters = "NSEW"; |
|---|
| 203 | |
|---|
| 204 | /** |
|---|
| 205 | * Return the first letter of this enum. |
|---|
| 206 | * |
|---|
| 207 | * @return String first letter of this enum. |
|---|
| 208 | */ |
|---|
| 209 | public String getLetter() |
|---|
| 210 | { |
|---|
| 211 | return this.toString().substring(0, 1); |
|---|
| 212 | } |
|---|
| 213 | |
|---|
| 214 | public DIRECTION getOpposite() |
|---|
| 215 | { |
|---|
| 216 | switch (this) |
|---|
| 217 | { |
|---|
| 218 | case NORTH: |
|---|
| 219 | return SOUTH; |
|---|
| 220 | case SOUTH: |
|---|
| 221 | return NORTH; |
|---|
| 222 | case EAST: |
|---|
| 223 | return WEST; |
|---|
| 224 | case WEST: |
|---|
| 225 | return EAST; |
|---|
| 226 | } |
|---|
| 227 | return null; |
|---|
| 228 | } |
|---|
| 229 | |
|---|
| 230 | /** |
|---|
| 231 | * Returns a direction given its first character. |
|---|
| 232 | * |
|---|
| 233 | * @param letter the first character of a direction |
|---|
| 234 | * @return direction corresponding to letter |
|---|
| 235 | * @pre letter must be one of allLetters |
|---|
| 236 | */ |
|---|
| 237 | public static DIRECTION toDirection(String letter) |
|---|
| 238 | { |
|---|
| 239 | if(letter.indexOf(letter.charAt(0)) == -1) |
|---|
| 240 | { |
|---|
| 241 | return null; |
|---|
| 242 | } |
|---|
| 243 | return values()[allLetters.indexOf(letter.charAt(0))]; |
|---|
| 244 | } |
|---|
| 245 | } |
|---|
| 246 | |
|---|
| 247 | @Override |
|---|
| 248 | public String toString() |
|---|
| 249 | { |
|---|
| 250 | return Integer.toString(this.vdsID)+this.getColor(); |
|---|
| 251 | } |
|---|
| 252 | } |
|---|