Skip to content

Commit 734adc9

Browse files
author
Nabil Belahouane
authored
Add files via upload
1 parent e7f93f4 commit 734adc9

File tree

58 files changed

+810
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+810
-0
lines changed

trafficmon/lib/hamcrest-all-1.3.jar

299 KB
Binary file not shown.

trafficmon/lib/jmock-2.8.1.jar

96.9 KB
Binary file not shown.

trafficmon/lib/jmock-junit4-2.8.1.jar

7.28 KB
Binary file not shown.

trafficmon/lib/junit-4.12.jar

308 KB
Binary file not shown.
6.95 KB
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
package com.trafficmon;
2+
3+
import java.math.BigDecimal;
4+
import java.text.SimpleDateFormat;
5+
import java.util.*;
6+
7+
public class CongestionChargeSystem {
8+
//Variables
9+
public static final long HOURS_TO_MILLI_SECONDS = 60*60*1000L;
10+
private Map<VehicleInterface, List<ZoneBoundaryCrossing>> crossingsByVehicle;
11+
private CustomerAccountsServiceInterface customerAccountService;
12+
private OperationsServiceInterface operationsService;
13+
//Variables*
14+
15+
//Package Private Constructor for testing
16+
CongestionChargeSystem(Map<VehicleInterface, List<ZoneBoundaryCrossing>> crossingsByVehicle,CustomerAccountsServiceInterface customerAccountService,OperationsServiceInterface opsi){
17+
this.crossingsByVehicle = crossingsByVehicle;
18+
this.customerAccountService = customerAccountService;
19+
this.operationsService = opsi;
20+
}
21+
22+
public CongestionChargeSystem(){
23+
this.operationsService = new OperationsService();
24+
this.customerAccountService = new CustomerAccountsService();
25+
this.crossingsByVehicle = new HashMap<VehicleInterface, List<ZoneBoundaryCrossing>>();
26+
}
27+
28+
public void vehicleEnteringZone(VehicleInterface vehicle) {
29+
if (!crossingsByVehicle.containsKey(vehicle)) {
30+
crossingsByVehicle.put(vehicle, new ArrayList<ZoneBoundaryCrossing>());
31+
}
32+
crossingsByVehicle.get(vehicle).add(new EntryEvent(vehicle));
33+
}
34+
35+
public void vehicleLeavingZone(VehicleInterface vehicle) {
36+
//Make sure vehicle is registered
37+
if ((!crossingsByVehicle.containsKey(vehicle))||(crossingsByVehicle.get(vehicle).size()<=0)) {
38+
return;
39+
}
40+
crossingsByVehicle.get(vehicle).add(new ExitEvent(vehicle));
41+
}
42+
43+
public void calculateCharges() {
44+
//crossingsByVehicle.clear();//reset hashmap
45+
//For each vehicle make sure the crossing are not illegal and then calculate the charges and deduct from account
46+
for (Map.Entry<VehicleInterface, List<ZoneBoundaryCrossing>> vehicleCrossings : crossingsByVehicle.entrySet()) {
47+
VehicleInterface vehicle = vehicleCrossings.getKey();
48+
List<ZoneBoundaryCrossing> crossings = vehicleCrossings.getValue();
49+
if (!checkOrderingOf(crossings)) {
50+
operationsService.triggerInvestigationInto(vehicle);
51+
} else {
52+
53+
BigDecimal charge = calculateChargeForTimeInZone(crossings);
54+
try {
55+
customerAccountService.deductChargeFrom(vehicle,charge);
56+
} catch (InsufficientCreditException | AccountNotRegisteredException ex) {
57+
operationsService.issuePenaltyNotice(vehicle, charge);
58+
}
59+
}
60+
}
61+
62+
}
63+
64+
private double timeFromEpochToHourOfDay(long millis){
65+
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
66+
Date resultantDate = new Date(millis);
67+
String[] timeOfDay = sdf.format(resultantDate).split(":");
68+
return (Double.parseDouble(timeOfDay[0])+(Double.parseDouble(timeOfDay[1])/60.0));
69+
70+
}
71+
72+
private BigDecimal calculateChargeForTimeInZone(List<ZoneBoundaryCrossing> crossings){
73+
BigDecimal charge;
74+
ZoneBoundaryCrossing previousEvent = crossings.get(0);//stores the previous crossing
75+
ZoneBoundaryCrossing previousEntryEventCharged = crossings.get(0);//stores the last entry event in which you were charged
76+
long totalTimeInZone = 0;
77+
78+
if (timeFromEpochToHourOfDay(previousEntryEventCharged.timestamp()) < 14.0){
79+
charge= new BigDecimal(6.0);//before 2 pm
80+
}else{
81+
charge= new BigDecimal(4.0);//2pm onwards will be 4 gbp
82+
}
83+
84+
for (ZoneBoundaryCrossing crossing : crossings.subList(1, crossings.size())) {
85+
if (crossing instanceof ExitEvent) {
86+
totalTimeInZone += crossing.timestamp()-previousEvent.timestamp();
87+
//For the case where you weren't charged when you entered but stayed past the 4 hour mark from the previous charge
88+
if ( (crossing.timestamp()-previousEntryEventCharged.timestamp()) > (4*HOURS_TO_MILLI_SECONDS) ){
89+
long tmp_crossing_time = previousEntryEventCharged.timestamp()+(4*HOURS_TO_MILLI_SECONDS);
90+
if (timeFromEpochToHourOfDay(tmp_crossing_time) < 14.0){
91+
charge = charge.add(new BigDecimal(6.0));
92+
}else{
93+
charge = charge.add(new BigDecimal(4.0));
94+
}
95+
previousEntryEventCharged = new EntryEvent(crossing.getVehicle(),tmp_crossing_time);//create a phantom crossing(entry event) to make the system work
96+
}
97+
98+
}else{
99+
//Add Charges if previous charge was more than 4 hours ago
100+
if ( (crossing.timestamp()-previousEntryEventCharged.timestamp()) > (4*HOURS_TO_MILLI_SECONDS) ){
101+
if (timeFromEpochToHourOfDay(crossing.timestamp()) < 14.0) {
102+
charge = charge.add(new BigDecimal(6.0));
103+
} else{
104+
charge = charge.add(new BigDecimal(4.0));
105+
}
106+
previousEntryEventCharged = crossing;
107+
}
108+
}
109+
previousEvent = crossing;
110+
}
111+
112+
//if total time spent in the zone greater than 4 hours return 12 gbp
113+
if (totalTimeInZone > (4* HOURS_TO_MILLI_SECONDS)){
114+
return new BigDecimal(12);
115+
}
116+
117+
return charge;
118+
}
119+
120+
private boolean checkOrderingOf(List<ZoneBoundaryCrossing> crossings) {
121+
122+
ZoneBoundaryCrossing previousEvent = crossings.get(0);
123+
//Even though the vehicleLeavingZone function does not allow for exit events to be registered as the first event, it is always a good idea to perform checks in multiple places
124+
if (previousEvent instanceof ExitEvent){
125+
return false;
126+
}
127+
//Make sure a vehicle always leaves after entering (the list size is even and every Entry is followed by and Exit)
128+
if ((crossings.size() % 2) != 0){
129+
return false;
130+
}
131+
for (ZoneBoundaryCrossing crossing : crossings.subList(1, crossings.size())) {
132+
if (crossing.timestamp() < previousEvent.timestamp()) {
133+
return false;
134+
}
135+
if (crossing instanceof EntryEvent && previousEvent instanceof EntryEvent) {
136+
return false;
137+
}
138+
if (crossing instanceof ExitEvent && previousEvent instanceof ExitEvent) {
139+
return false;
140+
}
141+
previousEvent = crossing;
142+
}
143+
144+
return true;
145+
}
146+
147+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.trafficmon;
2+
3+
import java.math.BigDecimal;
4+
5+
/**
6+
* Created by Shoaib on 11/19/18.
7+
*/
8+
9+
public class CustomerAccountsService implements CustomerAccountsServiceInterface {
10+
private AccountsService registeredCustomerAccountsService = RegisteredCustomerAccountsService.getInstance();
11+
12+
@Override
13+
public void deductChargeFrom(VehicleInterface vehicle, BigDecimal charge) throws InsufficientCreditException,AccountNotRegisteredException {
14+
registeredCustomerAccountsService.accountFor((Vehicle) vehicle).deduct(charge);
15+
}
16+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.trafficmon;
2+
3+
import java.math.BigDecimal;
4+
5+
/**
6+
* Created by Shoaib on 11/19/18.
7+
*/
8+
9+
public interface CustomerAccountsServiceInterface {
10+
11+
void deductChargeFrom(VehicleInterface vehicle, BigDecimal charge) throws InsufficientCreditException,AccountNotRegisteredException ;
12+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.trafficmon;
2+
3+
public class EntryEvent extends ZoneBoundaryCrossing{
4+
public EntryEvent(VehicleInterface vehicleRegistration) {
5+
super(vehicleRegistration);
6+
}
7+
// Package-Private Method For Testing
8+
EntryEvent(VehicleInterface vehicleRegistration,long time) {super(vehicleRegistration, time);}
9+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.trafficmon;
2+
3+
public class ExitEvent extends ZoneBoundaryCrossing {
4+
public ExitEvent(VehicleInterface vehicle) {
5+
super(vehicle);
6+
}
7+
// Package-Private Method For Testing
8+
ExitEvent(VehicleInterface vehicle, long time) {
9+
super(vehicle,time);
10+
}
11+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.trafficmon;
2+
3+
/**
4+
* Created by Shoaib on 11/17/18.
5+
*/
6+
7+
public class Main {
8+
public static void main(String[] args) throws Exception {
9+
10+
CongestionChargeSystem congestionChargeSystem = new CongestionChargeSystem();
11+
congestionChargeSystem.vehicleEnteringZone(Vehicle.withRegistration("A123 XYZ"));
12+
//delaySeconds(15);
13+
congestionChargeSystem.vehicleEnteringZone(Vehicle.withRegistration("J091 4PY"));
14+
//delayMinutes(30);
15+
//congestionChargeSystem.vehicleLeavingZone(Vehicle.withRegistration("A123 XYZ"));
16+
//delayMinutes(10);
17+
congestionChargeSystem.vehicleLeavingZone(Vehicle.withRegistration("J091 4PY"));
18+
congestionChargeSystem.calculateCharges();
19+
}
20+
private static void delayMinutes(int mins) throws InterruptedException {
21+
delaySeconds(mins * 60);
22+
}
23+
private static void delaySeconds(int secs) throws InterruptedException {
24+
Thread.sleep(secs * 1000);
25+
}
26+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.trafficmon;
2+
3+
import java.math.BigDecimal;
4+
5+
public class OperationsService implements OperationsServiceInterface {
6+
private PenaltiesService OpTeam = OperationsTeam.getInstance();
7+
8+
@Override
9+
public void issuePenaltyNotice(VehicleInterface vehicle, BigDecimal charge) {
10+
OpTeam.issuePenaltyNotice((Vehicle) vehicle,charge);
11+
}
12+
13+
@Override
14+
public void triggerInvestigationInto(VehicleInterface vehicle){
15+
OpTeam.triggerInvestigationInto((Vehicle) vehicle);
16+
}
17+
18+
19+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.trafficmon;
2+
3+
import java.math.BigDecimal;
4+
5+
public interface OperationsServiceInterface {
6+
void issuePenaltyNotice(VehicleInterface vehicle, BigDecimal charge);
7+
void triggerInvestigationInto(VehicleInterface vehicle);
8+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.trafficmon;
2+
3+
public class Vehicle implements VehicleInterface {
4+
5+
private final String registration;
6+
7+
private Vehicle(String registration) {
8+
this.registration = registration;
9+
}
10+
11+
static Vehicle withRegistration(String registration) {
12+
return new Vehicle(registration);
13+
}
14+
15+
@Override
16+
public String toString() {
17+
return "Vehicle [" + registration + "]";
18+
}
19+
20+
@Override
21+
public boolean equals(Object o) {
22+
if (this == o) return true;
23+
if (o == null || getClass() != o.getClass()) return false;
24+
25+
Vehicle vehicle = (Vehicle) o;
26+
27+
return (registration != null) ? registration.equals(vehicle.registration) : vehicle.registration == null;
28+
29+
/*if (registration != null){
30+
return registration.equals(vehicle.registration);
31+
}
32+
return (vehicle.registration == null);*/
33+
}
34+
35+
@Override
36+
public int hashCode() {
37+
return registration != null ? registration.hashCode() : 0;
38+
}
39+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.trafficmon;
2+
3+
/**
4+
* Created by Shoaib on 11/17/18.
5+
*/
6+
7+
interface VehicleInterface {
8+
9+
@Override
10+
String toString();
11+
12+
@Override
13+
boolean equals(Object o);
14+
15+
@Override
16+
int hashCode();
17+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.trafficmon;
2+
3+
public abstract class ZoneBoundaryCrossing {
4+
5+
private final VehicleInterface vehicle;
6+
private final long time;
7+
8+
public ZoneBoundaryCrossing(VehicleInterface vehicle) {
9+
this.vehicle = vehicle;
10+
this.time = System.currentTimeMillis();
11+
}
12+
//Package private constructor for testing purposes
13+
ZoneBoundaryCrossing(VehicleInterface vehicle, long time) {
14+
this.vehicle = vehicle;
15+
this.time = time;
16+
}
17+
18+
public VehicleInterface getVehicle() {
19+
return vehicle;
20+
}
21+
22+
public long timestamp() {
23+
return time;
24+
}
25+
}

0 commit comments

Comments
 (0)