| 1 | package tartan.smarthome.resources; | |
| 2 | ||
| 3 | import java.sql.Date; | |
| 4 | import java.text.SimpleDateFormat; | |
| 5 | import java.util.HashMap; | |
| 6 | import java.util.Map; | |
| 7 | import java.util.function.Supplier; | |
| 8 | ||
| 9 | import org.slf4j.Logger; | |
| 10 | import org.slf4j.LoggerFactory; | |
| 11 | ||
| 12 | import tartan.smarthome.resources.iotcontroller.IoTValues; | |
| 13 | ||
| 14 | public class StaticTartanStateEvaluator implements TartanStateEvaluator { | |
| 15 | ||
| 16 | private final Supplier<Long> timeProvider; | |
| 17 | private static final Logger LOGGER = LoggerFactory.getLogger(StaticTartanStateEvaluator.class); | |
| 18 | ||
| 19 | public StaticTartanStateEvaluator(Supplier<Long> timeProvider) { | |
| 20 | this.timeProvider = timeProvider; | |
| 21 | } | |
| 22 | ||
| 23 | /** | |
| 24 | * Helper to format log entries with timestamps | |
| 25 | * | |
| 26 | * @param entry The log message to format | |
| 27 | * @return A formatted log entry with timestamp prefix | |
| 28 | */ | |
| 29 | private String formatLogEntry(String entry) { | |
| 30 | Long timeStamp = timeProvider.get(); | |
| 31 | SimpleDateFormat sdf = new SimpleDateFormat("MMM dd,yyyy HH:mm"); | |
| 32 |
1
1. formatLogEntry : replaced return value with "" for tartan/smarthome/resources/StaticTartanStateEvaluator::formatLogEntry → KILLED |
return "[" + sdf.format(new Date(timeStamp)) + "]: " + entry + "\n"; |
| 33 | } | |
| 34 | ||
| 35 | /** | |
| 36 | * Helper to get current time (both real and faked) | |
| 37 | * | |
| 38 | * @return current hour in the form of 0-23 (0 midnight) | |
| 39 | */ | |
| 40 | private Long getCurrentTime() { | |
| 41 |
3
1. getCurrentTime : Replaced long division with multiplication → KILLED 2. getCurrentTime : replaced Long return value with 0L for tartan/smarthome/resources/StaticTartanStateEvaluator::getCurrentTime → KILLED 3. getCurrentTime : Replaced long modulus with multiplication → KILLED |
return (timeProvider.get() / (1000 * 60 * 60)) % 24; |
| 42 | } | |
| 43 | ||
| 44 | /** | |
| 45 | * Ensure the requested state is permitted. This method checks each state | |
| 46 | * variable to ensure that the house remains in a consistent state. | |
| 47 | * | |
| 48 | * @param state The new state to evaluate | |
| 49 | * @param log The log of state evaluations | |
| 50 | * @return The evaluated state | |
| 51 | */ | |
| 52 | @SuppressWarnings("null") | |
| 53 | @Override | |
| 54 | public Map<String, Object> evaluateState(Map<String, Object> inState, StringBuilder log) { | |
| 55 | ||
| 56 | // state variables that reflect the current configuration of the house | |
| 57 | Integer tempReading = null; // the current temperature | |
| 58 | Integer targetTempSetting = null; // the user-desired temperature setting | |
| 59 | Boolean doorState = null; // the state of the door (true if open, false if closed) | |
| 60 | Boolean lightState = null; // the state of the light (true if on, false if off) | |
| 61 | Boolean proximityState = null; // the state of the proximity sensor (true of house occupied, false if vacant) | |
| 62 | Boolean alarmState = null; // the alarm state (true if enabled, false if disabled) | |
| 63 | Boolean humidifierState = null; // the humidifier state (true if on, false if off) | |
| 64 | Boolean heaterOnState = false; // the heater state (true if on, false if off) | |
| 65 | Boolean chillerOnState = false; // the chiller state (true if on, false if off) | |
| 66 | Boolean alarmActiveState = null; // the alarm active state (true if alarm sounding, false if alarm not sounding) | |
| 67 | Boolean awayTimerState = false; // assume that the away timer did not trigger this evaluation | |
| 68 | String alarmPasscode = null; // the alarm passcode | |
| 69 | String hvacState = null; // the HVAC mode setting, either Heater or Chiller | |
| 70 | String givenPasscode = ""; // the passcode entered by the user | |
| 71 | ||
| 72 | // smart lock variables | |
| 73 | Boolean smartLockState = null; // the state of the smart lock (true if locked, false if unlocked) | |
| 74 | String smartLockPasscode = null; // passcode for the smart lock | |
| 75 | String enteredPasscode = ""; // The smart lock passcode entered by the user | |
| 76 | Boolean nightLockState = false; // the night lock state (true if enabled, false if disabled) | |
| 77 | String nightStartTime = null; // configured start time for night mode | |
| 78 | String nightEndTime = null; // configured end time for night mode | |
| 79 | String smartProxyId = null; // the ID for keyless entry | |
| 80 | String givenProxyId = ""; // the ID 'detected' by the sensor | |
| 81 | Boolean intruderDetected = null; // whether an intruder has been detected | |
| 82 | Boolean allClear = true; // whether all clear (true if all clear, false if not all clear) | |
| 83 | Boolean requiresPasscode = true; // if lock requires a passcode (true if requires passcode, false if not) | |
| 84 | String experimentGroup = "1"; // the group for the A/B test | |
| 85 | ||
| 86 | LOGGER.debug("Evaluating new state statically"); | |
| 87 | ||
| 88 | for (String key : inState.keySet()) { | |
| 89 | switch (key) { | |
| 90 | case IoTValues.TEMP_READING -> tempReading = (Integer) inState.get(key); | |
| 91 | case IoTValues.TARGET_TEMP -> targetTempSetting = (Integer) inState.get(key); | |
| 92 | case IoTValues.DOOR_STATE -> doorState = (Boolean) inState.get(key); | |
| 93 | case IoTValues.LIGHT_STATE -> lightState = (Boolean) inState.get(key); | |
| 94 | case IoTValues.PROXIMITY_STATE -> proximityState = (Boolean) inState.get(key); | |
| 95 | case IoTValues.ALARM_STATE -> alarmState = (Boolean) inState.get(key); | |
| 96 | case IoTValues.HUMIDIFIER_STATE -> humidifierState = (Boolean) inState.get(key); | |
| 97 | case IoTValues.HVAC_MODE -> hvacState = (String) inState.get(key); | |
| 98 | case IoTValues.ALARM_PASSCODE -> alarmPasscode = (String) inState.get(key); | |
| 99 | case IoTValues.GIVEN_PASSCODE -> givenPasscode = (String) inState.get(key); | |
| 100 | case IoTValues.AWAY_TIMER -> awayTimerState = (Boolean) inState.getOrDefault(key, false); | |
| 101 | case IoTValues.ALARM_ACTIVE -> alarmActiveState = (Boolean) inState.get(key); | |
| 102 | case IoTValues.SMART_LOCK_STATE -> smartLockState = (Boolean) inState.get(key); | |
| 103 | case IoTValues.SMART_LOCK_PASSCODE -> smartLockPasscode = (String) inState.get(key); | |
| 104 | case IoTValues.ENTERED_PASSCODE -> enteredPasscode = (String) inState.get(key); | |
| 105 | case IoTValues.SMART_LOCK_REQUIRES_PASSCODE -> requiresPasscode = (Boolean) inState.get(key); | |
| 106 | case IoTValues.NIGHT_START_TIME -> nightStartTime = inState.get(key).toString(); | |
| 107 | case IoTValues.NIGHT_END_TIME -> nightEndTime = inState.get(key).toString(); | |
| 108 | case IoTValues.SMART_PROXY_ID -> smartProxyId = (String) inState.get(key); | |
| 109 | case IoTValues.GIVEN_PROXY_ID -> givenProxyId = (String) inState.get(key); | |
| 110 | case IoTValues.INTRUDER_DETECTED -> intruderDetected = (Boolean) inState.get(key); | |
| 111 | case IoTValues.ALL_CLEAR -> allClear = (Boolean) inState.get(key); | |
| 112 | default -> log.append(formatLogEntry("Unknown state variable: " + key)); | |
| 113 | } | |
| 114 | } | |
| 115 | ||
| 116 |
1
1. evaluateState : removed conditional - replaced equality check with false → KILLED |
if (Boolean.TRUE.equals(awayTimerState)) { |
| 117 | lightState = false; | |
| 118 | doorState = false; | |
| 119 | alarmState = true; | |
| 120 | awayTimerState = false; | |
| 121 | smartLockState = true; | |
| 122 | log.append(formatLogEntry("Away timer activated - securing house")); | |
| 123 | } | |
| 124 | ||
| 125 |
1
1. evaluateState : removed conditional - replaced equality check with false → KILLED |
if (Boolean.TRUE.equals(lightState)) { |
| 126 | // The light was activated | |
| 127 |
1
1. evaluateState : removed conditional - replaced equality check with false → KILLED |
if (Boolean.FALSE.equals(proximityState)) { |
| 128 | log.append(formatLogEntry("Cannot turn on light because user not home")); | |
| 129 | lightState = false; | |
| 130 | } else { | |
| 131 | log.append(formatLogEntry("Light on")); | |
| 132 | } | |
| 133 | } else { | |
| 134 | log.append(formatLogEntry("Light off")); | |
| 135 | } | |
| 136 | ||
| 137 | // Handle door state when door is open | |
| 138 |
1
1. evaluateState : removed conditional - replaced equality check with false → KILLED |
if (Boolean.TRUE.equals(doorState)) { |
| 139 |
1
1. evaluateState : removed conditional - replaced equality check with false → KILLED |
if (Boolean.FALSE.equals(proximityState)) { |
| 140 | // R3: If house is vacant, automatically close the door | |
| 141 | doorState = false; | |
| 142 | log.append(formatLogEntry("Closed door because house vacant")); | |
| 143 | ||
| 144 | // R2: If alarm is enabled when door opens in vacant house, trigger alarm | |
| 145 |
1
1. evaluateState : removed conditional - replaced equality check with false → KILLED |
if (Boolean.TRUE.equals(alarmState)) { |
| 146 | log.append(formatLogEntry("Break in detected: Activating alarm")); | |
| 147 | alarmActiveState = true; | |
| 148 | intruderDetected = true; | |
| 149 | } | |
| 150 | } else { | |
| 151 | log.append(formatLogEntry("Door open")); | |
| 152 | } | |
| 153 | } | |
| 154 | // The door is now closed | |
| 155 |
1
1. evaluateState : removed conditional - replaced equality check with false → KILLED |
else if (Boolean.FALSE.equals(doorState)) { |
| 156 | // the door is closed - if the house is suddenly occupied this is a break-in | |
| 157 |
2
1. evaluateState : removed conditional - replaced equality check with false → KILLED 2. evaluateState : removed conditional - replaced equality check with false → KILLED |
if (Boolean.TRUE.equals(alarmState) && Boolean.TRUE.equals(proximityState)) { |
| 158 | log.append(formatLogEntry("Break in detected: Activating alarm")); | |
| 159 | alarmActiveState = true; | |
| 160 | } else { | |
| 161 | log.append(formatLogEntry("Closed door")); | |
| 162 | } | |
| 163 | } | |
| 164 | ||
| 165 | // the user has arrived | |
| 166 |
2
1. evaluateState : removed conditional - replaced equality check with false → KILLED 2. evaluateState : removed conditional - replaced equality check with false → KILLED |
if (Boolean.TRUE.equals(proximityState) && Boolean.FALSE.equals(awayTimerState)) { |
| 167 | log.append(formatLogEntry("House is occupied")); | |
| 168 | ||
| 169 | // if the alarm has been disabled, then turn on the light for the user | |
| 170 |
2
1. evaluateState : removed conditional - replaced equality check with false → KILLED 2. evaluateState : removed conditional - replaced equality check with false → KILLED |
if (Boolean.FALSE.equals(lightState) && Boolean.FALSE.equals(alarmState)) { |
| 171 | lightState = true; | |
| 172 | log.append(formatLogEntry("Turning on light")); | |
| 173 | } | |
| 174 | } | |
| 175 | ||
| 176 | // set the alarm | |
| 177 |
1
1. evaluateState : removed conditional - replaced equality check with false → KILLED |
if (Boolean.TRUE.equals(alarmState)) { |
| 178 | log.append(formatLogEntry("Alarm enabled")); | |
| 179 | ||
| 180 |
1
1. evaluateState : removed conditional - replaced equality check with false → KILLED |
} else if (Boolean.FALSE.equals(alarmState)) { // attempt to disable alarm |
| 181 | ||
| 182 |
1
1. evaluateState : removed conditional - replaced equality check with false → KILLED |
if (Boolean.FALSE.equals(proximityState)) { |
| 183 | alarmState = true; | |
| 184 | log.append(formatLogEntry("Cannot disable the alarm, house is empty")); | |
| 185 | } | |
| 186 | ||
| 187 |
1
1. evaluateState : removed conditional - replaced equality check with false → KILLED |
if (Boolean.TRUE.equals(alarmActiveState)) { |
| 188 |
1
1. evaluateState : removed conditional - replaced equality check with false → KILLED |
if (!givenPasscode.equals(alarmPasscode)) { |
| 189 | log.append(formatLogEntry("Cannot disable alarm, invalid passcode given")); | |
| 190 | alarmState = true; | |
| 191 | ||
| 192 | } else { | |
| 193 | log.append(formatLogEntry("Correct passcode entered, disabled alarm")); | |
| 194 | alarmActiveState = false; | |
| 195 | } | |
| 196 | } | |
| 197 | ||
| 198 | // If house is occupied and wrong passcode is entered, enable the alarm | |
| 199 |
2
1. evaluateState : removed conditional - replaced equality check with false → KILLED 2. evaluateState : removed conditional - replaced equality check with false → KILLED |
else if (Boolean.TRUE.equals(proximityState) && !givenPasscode.equals(alarmPasscode)) { |
| 200 | alarmState = true; | |
| 201 | log.append(formatLogEntry("Cannot disable the alarm, wrong passcode")); | |
| 202 | } | |
| 203 | } | |
| 204 | ||
| 205 |
1
1. evaluateState : removed conditional - replaced equality check with false → KILLED |
if (Boolean.FALSE.equals(alarmState)) { |
| 206 | log.append(formatLogEntry("Alarm disabled")); | |
| 207 | alarmActiveState = false; | |
| 208 | } | |
| 209 | ||
| 210 | // manage the HVAC control | |
| 211 |
2
1. evaluateState : removed conditional - replaced comparison check with false → KILLED 2. evaluateState : changed conditional boundary → KILLED |
if (tempReading < targetTempSetting) { |
| 212 | heaterOnState = true; | |
| 213 | chillerOnState = false; | |
| 214 | humidifierState = false; | |
| 215 | hvacState = "Heater"; | |
| 216 | log.append(formatLogEntry( | |
| 217 | "Heater activated - Target: " + targetTempSetting + "F, Current: " + tempReading + "F")); | |
| 218 |
2
1. evaluateState : changed conditional boundary → KILLED 2. evaluateState : removed conditional - replaced comparison check with false → KILLED |
} else if (tempReading > targetTempSetting) { |
| 219 | heaterOnState = false; | |
| 220 | chillerOnState = true; | |
| 221 | hvacState = "Chiller"; | |
| 222 | log.append(formatLogEntry( | |
| 223 | "Cooling activated - Target: " + targetTempSetting + "F, Current: " + tempReading + "F")); | |
| 224 | } else { | |
| 225 | heaterOnState = false; | |
| 226 | chillerOnState = false; | |
| 227 | } | |
| 228 | ||
| 229 | // NIGHT LOCK LOGIC | |
| 230 | Long nightStart = Long.valueOf(nightStartTime); | |
| 231 | Long nightEnd = Long.valueOf(nightEndTime); | |
| 232 | Long currentTime = getCurrentTime(); | |
| 233 | ||
| 234 |
2
1. evaluateState : removed conditional - replaced comparison check with false → KILLED 2. evaluateState : changed conditional boundary → KILLED |
if (nightStart <= nightEnd) { |
| 235 |
4
1. evaluateState : changed conditional boundary → KILLED 2. evaluateState : removed conditional - replaced comparison check with false → KILLED 3. evaluateState : changed conditional boundary → KILLED 4. evaluateState : removed conditional - replaced comparison check with false → KILLED |
if (currentTime >= nightStart && currentTime < nightEnd) { |
| 236 | log.append(formatLogEntry("Night lock ON - Current Time b/w : " + nightStart + " & " + nightEnd)); | |
| 237 | nightLockState = true; | |
| 238 | smartLockState = true; | |
| 239 | } else { | |
| 240 | log.append(formatLogEntry("Night lock OFF - Outside night hours")); | |
| 241 | nightLockState = false; | |
| 242 | } | |
| 243 |
4
1. evaluateState : removed conditional - replaced comparison check with false → KILLED 2. evaluateState : changed conditional boundary → KILLED 3. evaluateState : removed conditional - replaced comparison check with false → KILLED 4. evaluateState : changed conditional boundary → KILLED |
} else if (currentTime >= nightStart || currentTime < nightEnd) { |
| 244 | log.append(formatLogEntry("Night lock ON - Current Time b/w : " + nightStart + " & " + nightEnd)); | |
| 245 | nightLockState = true; | |
| 246 | smartLockState = true; | |
| 247 | } else { | |
| 248 | log.append(formatLogEntry("Night lock OFF - Outside night hours")); | |
| 249 | nightLockState = false; | |
| 250 | } | |
| 251 | ||
| 252 | // Intruder defense logic | |
| 253 |
1
1. evaluateState : removed conditional - replaced equality check with false → KILLED |
if (Boolean.TRUE.equals(intruderDetected)) { |
| 254 | smartLockState = true; | |
| 255 | doorState = false; | |
| 256 | alarmActiveState = true; | |
| 257 | lightState = true; | |
| 258 | log.append(formatLogEntry("ALERT: Possible intruder detected - Full security response activated")); | |
| 259 | ||
| 260 |
3
1. evaluateState : removed conditional - replaced equality check with false → KILLED 2. evaluateState : removed conditional - replaced equality check with false → KILLED 3. evaluateState : removed conditional - replaced equality check with false → KILLED |
if (Boolean.TRUE.equals(allClear) && givenPasscode != null && givenPasscode.equals(alarmPasscode)) { |
| 261 | intruderDetected = false; | |
| 262 | log.append(formatLogEntry("All clear signal received with proper authorization")); | |
| 263 | } | |
| 264 | } | |
| 265 | ||
| 266 | // Keyless Entry Logic | |
| 267 |
3
1. evaluateState : removed conditional - replaced equality check with false → KILLED 2. evaluateState : removed conditional - replaced equality check with false → KILLED 3. evaluateState : removed conditional - replaced equality check with false → KILLED |
if (smartProxyId != null && givenProxyId != null && smartProxyId.equals(givenProxyId)) { |
| 268 | smartLockState = false; | |
| 269 | log.append(formatLogEntry("Keyless entry authorized - Smart lock unlocked")); | |
| 270 | } | |
| 271 | ||
| 272 | // Smart lock logic | |
| 273 |
1
1. evaluateState : removed conditional - replaced equality check with false → KILLED |
if (smartLockState != null) { |
| 274 |
1
1. evaluateState : removed conditional - replaced equality check with false → KILLED |
if (Boolean.TRUE.equals(nightLockState)) { |
| 275 | smartLockState = true; | |
| 276 |
1
1. evaluateState : removed conditional - replaced equality check with false → KILLED |
} else if (Boolean.TRUE.equals(smartLockState)) { |
| 277 |
1
1. evaluateState : removed conditional - replaced equality check with false → KILLED |
if (Boolean.FALSE.equals(requiresPasscode)) { |
| 278 | smartLockState = false; | |
| 279 | log.append(formatLogEntry("Smart lock unlocked - no passcode required")); | |
| 280 |
2
1. evaluateState : removed conditional - replaced equality check with false → KILLED 2. evaluateState : removed conditional - replaced equality check with false → KILLED |
} else if (enteredPasscode != null && smartLockPasscode != null && |
| 281 |
1
1. evaluateState : removed conditional - replaced equality check with false → KILLED |
enteredPasscode.equals(smartLockPasscode)) { |
| 282 | smartLockState = false; | |
| 283 | log.append(formatLogEntry("Smart lock unlocked with valid passcode")); | |
| 284 | } | |
| 285 | } | |
| 286 | } | |
| 287 | ||
| 288 | Map<String, Object> newState = new HashMap<>(); | |
| 289 | newState.put(IoTValues.DOOR_STATE, doorState); | |
| 290 | newState.put(IoTValues.AWAY_TIMER, awayTimerState); | |
| 291 | newState.put(IoTValues.LIGHT_STATE, lightState); | |
| 292 | newState.put(IoTValues.PROXIMITY_STATE, proximityState); | |
| 293 | newState.put(IoTValues.ALARM_STATE, alarmState); | |
| 294 | newState.put(IoTValues.HUMIDIFIER_STATE, humidifierState); | |
| 295 | newState.put(IoTValues.HEATER_STATE, heaterOnState); | |
| 296 | newState.put(IoTValues.CHILLER_STATE, chillerOnState); | |
| 297 | newState.put(IoTValues.ALARM_ACTIVE, alarmActiveState); | |
| 298 | newState.put(IoTValues.HVAC_MODE, hvacState); | |
| 299 | newState.put(IoTValues.ALARM_PASSCODE, alarmPasscode); | |
| 300 | newState.put(IoTValues.GIVEN_PASSCODE, givenPasscode); | |
| 301 | newState.put(IoTValues.SMART_LOCK_STATE, smartLockState); | |
| 302 | newState.put(IoTValues.NIGHT_LOCK_STATE, nightLockState); | |
| 303 | newState.put(IoTValues.SMART_LOCK_PASSCODE, smartLockPasscode); | |
| 304 | newState.put(IoTValues.NIGHT_START_TIME, nightStartTime); | |
| 305 | newState.put(IoTValues.NIGHT_END_TIME, nightEndTime); | |
| 306 | newState.put(IoTValues.INTRUDER_DETECTED, intruderDetected); | |
| 307 | newState.put(IoTValues.SMART_PROXY_ID, smartProxyId); | |
| 308 | newState.put(IoTValues.GIVEN_PROXY_ID, givenProxyId); | |
| 309 | newState.put(IoTValues.ALL_CLEAR, allClear); | |
| 310 | newState.put(IoTValues.SMART_LOCK_REQUIRES_PASSCODE, requiresPasscode); | |
| 311 | newState.put(IoTValues.ENTERED_PASSCODE, enteredPasscode); | |
| 312 | newState.put(IoTValues.EXPERIMENT_GROUP, experimentGroup); | |
| 313 | ||
| 314 |
1
1. evaluateState : replaced return value with Collections.emptyMap for tartan/smarthome/resources/StaticTartanStateEvaluator::evaluateState → KILLED |
return newState; |
| 315 | } | |
| 316 | } | |
Mutations | ||
| 32 |
1.1 |
|
| 41 |
1.1 2.2 3.3 |
|
| 116 |
1.1 |
|
| 125 |
1.1 |
|
| 127 |
1.1 |
|
| 138 |
1.1 |
|
| 139 |
1.1 |
|
| 145 |
1.1 |
|
| 155 |
1.1 |
|
| 157 |
1.1 2.2 |
|
| 166 |
1.1 2.2 |
|
| 170 |
1.1 2.2 |
|
| 177 |
1.1 |
|
| 180 |
1.1 |
|
| 182 |
1.1 |
|
| 187 |
1.1 |
|
| 188 |
1.1 |
|
| 199 |
1.1 2.2 |
|
| 205 |
1.1 |
|
| 211 |
1.1 2.2 |
|
| 218 |
1.1 2.2 |
|
| 234 |
1.1 2.2 |
|
| 235 |
1.1 2.2 3.3 4.4 |
|
| 243 |
1.1 2.2 3.3 4.4 |
|
| 253 |
1.1 |
|
| 260 |
1.1 2.2 3.3 |
|
| 267 |
1.1 2.2 3.3 |
|
| 273 |
1.1 |
|
| 274 |
1.1 |
|
| 276 |
1.1 |
|
| 277 |
1.1 |
|
| 280 |
1.1 2.2 |
|
| 281 |
1.1 |
|
| 314 |
1.1 |