diff --git a/.classpath b/.classpath index 6561552..210b896 100644 --- a/.classpath +++ b/.classpath @@ -14,7 +14,7 @@ - + diff --git a/bin/.gitignore b/bin/.gitignore new file mode 100644 index 0000000..519d5f8 --- /dev/null +++ b/bin/.gitignore @@ -0,0 +1,3 @@ +/com/ +/maps/ +/devices/ diff --git a/bin/admin.properties b/bin/admin.properties index 601b2b8..cfdf0bc 100644 --- a/bin/admin.properties +++ b/bin/admin.properties @@ -1,3 +1,3 @@ group.name = simulator -nr.of.cars = 5 -nr.of.ambulances = 3 \ No newline at end of file +nr.of.cars = 4 +nr.of.ambulances = 4 \ No newline at end of file diff --git a/bin/application.properties b/bin/application.properties index 14dd591..0a060dd 100644 --- a/bin/application.properties +++ b/bin/application.properties @@ -1,4 +1,4 @@ -api-key = a-0yngjl-qd4wocrack -api-token = eA5HT)4ccFU2dULL7A -orga-id = 0yngjl +api-key = a-5rcpia-hxwn2ipopc +api-token = tSzH3*7wou5pLfZd4e +orga-id = 5rcpia devices = devices/test-devices.json diff --git a/bin/com/capgemini/hackathon/device/simulation/bo/Ambulance$EmergencyCommandHandler.class b/bin/com/capgemini/hackathon/device/simulation/bo/Ambulance$EmergencyCommandHandler.class index 3a6fbc4..62d20cd 100644 Binary files a/bin/com/capgemini/hackathon/device/simulation/bo/Ambulance$EmergencyCommandHandler.class and b/bin/com/capgemini/hackathon/device/simulation/bo/Ambulance$EmergencyCommandHandler.class differ diff --git a/bin/com/capgemini/hackathon/device/simulation/bo/Ambulance.class b/bin/com/capgemini/hackathon/device/simulation/bo/Ambulance.class index 294217d..1b8f22e 100644 Binary files a/bin/com/capgemini/hackathon/device/simulation/bo/Ambulance.class and b/bin/com/capgemini/hackathon/device/simulation/bo/Ambulance.class differ diff --git a/bin/com/capgemini/hackathon/device/simulation/bo/Car.class b/bin/com/capgemini/hackathon/device/simulation/bo/Car.class index a3daf50..6dce01d 100644 Binary files a/bin/com/capgemini/hackathon/device/simulation/bo/Car.class and b/bin/com/capgemini/hackathon/device/simulation/bo/Car.class differ diff --git a/bin/com/capgemini/hackathon/device/simulation/bo/Hospital$EmergencyDeviceCommand.class b/bin/com/capgemini/hackathon/device/simulation/bo/Hospital$EmergencyDeviceCommand.class index 66c1b1d..2397dfe 100644 Binary files a/bin/com/capgemini/hackathon/device/simulation/bo/Hospital$EmergencyDeviceCommand.class and b/bin/com/capgemini/hackathon/device/simulation/bo/Hospital$EmergencyDeviceCommand.class differ diff --git a/bin/com/capgemini/hackathon/device/simulation/bo/Hospital.class b/bin/com/capgemini/hackathon/device/simulation/bo/Hospital.class index 67ea732..f7b6798 100644 Binary files a/bin/com/capgemini/hackathon/device/simulation/bo/Hospital.class and b/bin/com/capgemini/hackathon/device/simulation/bo/Hospital.class differ diff --git a/bin/com/capgemini/hackathon/device/simulation/bo/Simulation.class b/bin/com/capgemini/hackathon/device/simulation/bo/Simulation.class index 7e845ae..5e57e38 100644 Binary files a/bin/com/capgemini/hackathon/device/simulation/bo/Simulation.class and b/bin/com/capgemini/hackathon/device/simulation/bo/Simulation.class differ diff --git a/bin/com/capgemini/hackathon/device/simulation/bo/Vehicle.class b/bin/com/capgemini/hackathon/device/simulation/bo/Vehicle.class index 2dd3280..6873f85 100644 Binary files a/bin/com/capgemini/hackathon/device/simulation/bo/Vehicle.class and b/bin/com/capgemini/hackathon/device/simulation/bo/Vehicle.class differ diff --git a/bin/devices/test-devices.json b/bin/devices/test-devices.json index 091cef9..95302a7 100644 --- a/bin/devices/test-devices.json +++ b/bin/devices/test-devices.json @@ -1,73 +1,73 @@ -[{ - "simulatorClazz": "com.capgemini.hackathon.device.simulation.bo.Car", - "id": "car0", - "orgId": "0yngjl", - "clientId": "d:0yngjl:car-simulator:car0", - "typeId": "car-simulator", - "deviceId": "car0", - "apiToken": "E3BF69B9-C5B6-41AE-A5AB-70A35F583E64" -},{ - "simulatorClazz": "com.capgemini.hackathon.device.simulation.bo.Car", - "id": "car1", - "orgId": "0yngjl", - "clientId": "d:0yngjl:car-simulator:car1", - "typeId": "car-simulator", - "deviceId": "car1", - "apiToken": "6CFA16A6-210F-4CDD-95FA-7C3158ED9C91" -},{ - "simulatorClazz": "com.capgemini.hackathon.device.simulation.bo.Car", - "id": "car2", - "orgId": "0yngjl", - "clientId": "d:0yngjl:car-simulator:car2", - "typeId": "car-simulator", - "deviceId": "car2", - "apiToken": "F36A8081-11BD-4088-A1A2-0F72069B44F8" -},{ - "simulatorClazz": "com.capgemini.hackathon.device.simulation.bo.Car", - "id": "car3", - "orgId": "0yngjl", - "clientId": "d:0yngjl:car-simulator:car3", - "typeId": "car-simulator", - "deviceId": "car3", - "apiToken": "BFFAE321-AE51-4A3C-BDE9-5F992A653C7C" -},{ - "simulatorClazz": "com.capgemini.hackathon.device.simulation.bo.Car", - "id": "car4", - "orgId": "0yngjl", - "clientId": "d:0yngjl:car-simulator:car4", - "typeId": "car-simulator", - "deviceId": "car4", - "apiToken": "D2340AED-C527-4DED-97EB-31EEF58CBFAB" -},{ - "simulatorClazz": "com.capgemini.hackathon.device.simulation.bo.Ambulance", - "id": "ambulance0", - "orgId": "0yngjl", - "clientId": "d:0yngjl:ambulance-simulator:ambulance0", - "typeId": "ambulance-simulator", - "deviceId": "ambulance0", - "apiToken": "FF7B5C52-98AA-4242-A110-44F306263DC5" -},{ - "simulatorClazz": "com.capgemini.hackathon.device.simulation.bo.Ambulance", - "id": "ambulance1", - "orgId": "0yngjl", - "clientId": "d:0yngjl:ambulance-simulator:ambulance1", - "typeId": "ambulance-simulator", - "deviceId": "ambulance1", - "apiToken": "A27E0DA5-B118-4E2E-8200-3F06112ECD64" -},{ - "simulatorClazz": "com.capgemini.hackathon.device.simulation.bo.Ambulance", - "id": "ambulance2", - "orgId": "0yngjl", - "clientId": "d:0yngjl:ambulance-simulator:ambulance2", - "typeId": "ambulance-simulator", - "deviceId": "ambulance2", - "apiToken": "41B2ED2B-2E05-44ED-A46A-0DA82CB5592C" -},{ - "simulatorClazz": "com.capgemini.hackathon.device.simulation.bo.Hospital", - "id": "hospital", - "orgId": "0yngjl", - "clientId": "d:0yngjl:hospital:hospital1", - "typeId": "hospital", - "deviceId": "hospital1", - "apiToken": "FAD566AC-4C27-43E8-8DF8-EEA6576DAFFB" -}] +[{ + "simulatorClazz": "com.capgemini.hackathon.device.simulation.bo.Car", + "id": "car0", + "orgId": "5rcpia", + "clientId": "d:5rcpia:car-simulator:car0", + "typeId": "car-simulator", + "deviceId": "car0", + "apiToken": "01562EAC-5459-498E-9B1C-8DA4F37D074C" +},{ + "simulatorClazz": "com.capgemini.hackathon.device.simulation.bo.Car", + "id": "car1", + "orgId": "5rcpia", + "clientId": "d:5rcpia:car-simulator:car1", + "typeId": "car-simulator", + "deviceId": "car1", + "apiToken": "1719BB4F-99A0-42D0-8FF2-4A76BC64EAFD" +},{ + "simulatorClazz": "com.capgemini.hackathon.device.simulation.bo.Car", + "id": "car2", + "orgId": "5rcpia", + "clientId": "d:5rcpia:car-simulator:car2", + "typeId": "car-simulator", + "deviceId": "car2", + "apiToken": "E3161649-B59E-4EC9-97E6-2A4711F7DB43" +},{ + "simulatorClazz": "com.capgemini.hackathon.device.simulation.bo.Car", + "id": "car3", + "orgId": "5rcpia", + "clientId": "d:5rcpia:car-simulator:car3", + "typeId": "car-simulator", + "deviceId": "car3", + "apiToken": "451326E3-BC56-4D78-8CD3-70B669F830F8" +},{ + "simulatorClazz": "com.capgemini.hackathon.device.simulation.bo.Ambulance", + "id": "ambulance0", + "orgId": "5rcpia", + "clientId": "d:5rcpia:ambulance-simulator:ambulance0", + "typeId": "ambulance-simulator", + "deviceId": "ambulance0", + "apiToken": "9DBB21F6-B888-4B36-8379-3F8C91912246" +},{ + "simulatorClazz": "com.capgemini.hackathon.device.simulation.bo.Ambulance", + "id": "ambulance1", + "orgId": "5rcpia", + "clientId": "d:5rcpia:ambulance-simulator:ambulance1", + "typeId": "ambulance-simulator", + "deviceId": "ambulance1", + "apiToken": "BC0CD3FD-F67E-4F23-945F-E805165D5590" +},{ + "simulatorClazz": "com.capgemini.hackathon.device.simulation.bo.Ambulance", + "id": "ambulance2", + "orgId": "5rcpia", + "clientId": "d:5rcpia:ambulance-simulator:ambulance2", + "typeId": "ambulance-simulator", + "deviceId": "ambulance2", + "apiToken": "AF289EC5-EE47-4458-AA3E-0A4ACB7EC058" +},{ + "simulatorClazz": "com.capgemini.hackathon.device.simulation.bo.Ambulance", + "id": "ambulance3", + "orgId": "5rcpia", + "clientId": "d:5rcpia:ambulance-simulator:ambulance3", + "typeId": "ambulance-simulator", + "deviceId": "ambulance3", + "apiToken": "CEC839DD-F3D4-4E97-889E-FBA8D3E0BBC7" +},{ + "simulatorClazz": "com.capgemini.hackathon.device.simulation.bo.Hospital", + "id": "hospital", + "orgId": "5rcpia", + "clientId": "d:5rcpia:hospital:hospital1", + "typeId": "hospital", + "deviceId": "hospital1", + "apiToken": "48E34C00-8A32-4C8B-93AC-6C38FE642A46" +}] diff --git a/src/main/java/com/capgemini/hackathon/device/service/DeviceClientCustom.java b/src/main/java/com/capgemini/hackathon/device/service/DeviceClientCustom.java new file mode 100644 index 0000000..abbc219 --- /dev/null +++ b/src/main/java/com/capgemini/hackathon/device/service/DeviceClientCustom.java @@ -0,0 +1,223 @@ +package com.capgemini.hackathon.device.service; + +import java.util.Properties; +import java.util.logging.Level; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.nio.charset.Charset; +import java.util.Date; + +import org.eclipse.paho.client.mqttv3.MqttAsyncClient; +import org.eclipse.paho.client.mqttv3.MqttClient; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.MqttMessage; +import org.eclipse.paho.client.mqttv3.MqttPersistenceException; +import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; +import org.eclipse.paho.client.mqttv3.MqttCallback; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.ibm.iotf.client.api.APIClient; +import com.ibm.iotf.client.device.Command; +import com.ibm.iotf.client.device.CommandCallback; +import com.ibm.iotf.client.device.DeviceClient; +import com.ibm.iotf.util.LoggerUtility; + +public class DeviceClientCustom extends DeviceClient{ + + /** + * Overrided version of DeviceClient to change the JSON Messages + * to format Geospatial needs. + */ + +private static final String CLASS_NAME = DeviceClientCustom.class.getName(); + + private static final Pattern COMMAND_PATTERN = Pattern.compile("iot-2/cmd/(.+)/fmt/(.+)"); + + private CommandCallback commandCallback = null; + + private APIClient apiClient; + + public DeviceClientCustom(MqttAsyncClient arg0) { + super(arg0); + + } + + public DeviceClientCustom(MqttClient arg0) { + super(arg0); + + } + + public DeviceClientCustom(Properties options) throws Exception { + super(options); + LoggerUtility.fine(CLASS_NAME, "DeviceClientCustom", "options = " + options); + this.clientId = "d" + CLIENT_ID_DELIMITER + getOrgId() + CLIENT_ID_DELIMITER + getDeviceType() + CLIENT_ID_DELIMITER + getDeviceId(); + + if (getAuthMethod() == null) { + this.clientUsername = null; + this.clientPassword = null; + } + else if (!getAuthMethod().equals("token")) { + throw new Exception("Unsupported Authentication Method: " + getAuthMethod()); + } + else { + // use-token-auth is the only authentication method currently supported + this.clientUsername = "use-token-auth"; + this.clientPassword = getAuthToken(); + } + createClient(this.new MqttDeviceCallBack()); + + options.setProperty("auth-method", "device"); + + apiClient = new APIClient(options); + } + + public String getDeviceType() { + String type; + type = options.getProperty("type"); + if(type == null) { + type = options.getProperty("Device-Type"); + } + return trimedValue(type); + } + + public String getFormat() { + String format = options.getProperty("format"); + if(format != null && ! format.equals("")) + return format; + else + return "json"; + + } + + + private void subscribeToCommands() { + try { + mqttAsyncClient.subscribe("iot-2/cmd/+/fmt/+", 2); + } catch (MqttException e) { + e.printStackTrace(); + } + } + + public APIClient api() { + return this.apiClient; + } + + public boolean publishEvent(String event, Object data) { + return publishEvent(event, data, 0); + } + + + + @Override + public boolean publishEvent(String event, Object data, int qos) { + if (!isConnected()) { + return false; + } + final String METHOD = "publishEvent(2)"; + JsonObject payload = new JsonObject(); + + String timestamp = ISO8601_DATE_FORMAT.format(new Date()); + payload.addProperty("ts", timestamp); + + // Handle null object + if(data == null) { + data = new JsonObject(); + } + + JsonElement dataElement = gson.toJsonTree(data); + payload.add("d", dataElement); + + String topic = "iot-2/evt/" + event + "/fmt/json"; + + JsonObject dataJson = (JsonObject) data; + payload.add("latitude", dataJson.get("latitude")); + payload.add("longitude", dataJson.get("longitude")); + payload.add("vin", dataJson.get("vin")); + + MqttMessage msg = new MqttMessage(payload.toString().getBytes(Charset.forName("UTF-8"))); + msg.setQos(qos); + msg.setRetained(false); + + try { + mqttAsyncClient.publish(topic, msg).waitForCompletion(); + } catch (MqttPersistenceException e) { + e.printStackTrace(); + return false; + } catch (MqttException e) { + e.printStackTrace(); + return false; + } + return true; + } + + private class MqttDeviceCallBack implements MqttCallbackExtended { + + /** + * If we lose connection trigger the connect logic to attempt to + * reconnect to the IBM Watson IoT Platform. + * + * @param exception + * Throwable which caused the connection to get lost + */ + public void connectionLost(Throwable exception) { + final String METHOD = "connectionLost"; + LoggerUtility.log(Level.SEVERE, CLASS_NAME, METHOD, exception.getMessage()); + try { + reconnect(); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + /** + * A completed deliver does not guarantee that the message is received by the service + * because devices send messages with Quality of Service (QoS) 0.
+ * + * The message count + * represents the number of messages that were sent by the device without an error on + * from the perspective of the device. + * @param token + * MQTT delivery token + */ + public void deliveryComplete(IMqttDeliveryToken token) { + final String METHOD = "deliveryComplete"; + LoggerUtility.fine(CLASS_NAME, METHOD, "token " + token.getMessageId()); + messageCount++; + } + + /** + * The Device client does not currently support subscriptions. + */ + public void messageArrived(String topic, MqttMessage msg) throws Exception { + final String METHOD = "messageArrived"; + if (commandCallback != null) { + /* Only check whether the message is a command if a callback + * has been defined, otherwise it is a waste of time + * as without a callback there is nothing to process the generated + * command. + */ + Matcher matcher = COMMAND_PATTERN.matcher(topic); + if (matcher.matches()) { + String command = matcher.group(1); + String format = matcher.group(2); + Command cmd = new Command(command, format, msg); + LoggerUtility.fine(CLASS_NAME, METHOD, "Event received: " + cmd.toString()); + commandCallback.processCommand(cmd); + } + } + } + + @Override + public void connectComplete(boolean reconnect, String serverURI) { + // TODO Auto-generated method stub + + } + + } + + public void setCommandCallback(CommandCallback callback) { + this.commandCallback = callback; + } + } diff --git a/src/main/java/com/capgemini/hackathon/device/service/MqttCallbackExtended.java b/src/main/java/com/capgemini/hackathon/device/service/MqttCallbackExtended.java new file mode 100644 index 0000000..7de0514 --- /dev/null +++ b/src/main/java/com/capgemini/hackathon/device/service/MqttCallbackExtended.java @@ -0,0 +1,11 @@ +package com.capgemini.hackathon.device.service; + +import org.eclipse.paho.client.mqttv3.MqttCallback; + +public interface MqttCallbackExtended extends MqttCallback{ + /** + * Overrided version of MqttCallbackExtended. Not included in the IBM API. + * Required for DeviceClientCustom. + */ + public void connectComplete(boolean reconnect, java.lang.String serverURI); +} diff --git a/src/main/java/com/capgemini/hackathon/device/simulation/bo/Ambulance.java b/src/main/java/com/capgemini/hackathon/device/simulation/bo/Ambulance.java index dca328b..db213ac 100644 --- a/src/main/java/com/capgemini/hackathon/device/simulation/bo/Ambulance.java +++ b/src/main/java/com/capgemini/hackathon/device/simulation/bo/Ambulance.java @@ -1,5 +1,6 @@ package com.capgemini.hackathon.device.simulation.bo; +import com.capgemini.hackathon.device.service.DeviceClientCustom; import com.capgemini.hackathon.device.simulation.DeviceClientConfig; import com.capgemini.hackathon.device.simulation.model.Emergency; import com.capgemini.hackathon.device.simulation.model.Location; @@ -94,7 +95,7 @@ public boolean isFree() { } @Override - protected void configureDeviceClient(DeviceClient deviceClient) { + protected void configureDeviceClient(DeviceClientCustom deviceClient) { } diff --git a/src/main/java/com/capgemini/hackathon/device/simulation/bo/Car.java b/src/main/java/com/capgemini/hackathon/device/simulation/bo/Car.java index c4c87cb..021d86f 100644 --- a/src/main/java/com/capgemini/hackathon/device/simulation/bo/Car.java +++ b/src/main/java/com/capgemini/hackathon/device/simulation/bo/Car.java @@ -1,5 +1,6 @@ package com.capgemini.hackathon.device.simulation.bo; +import com.capgemini.hackathon.device.service.DeviceClientCustom; import com.capgemini.hackathon.device.simulation.DeviceClientConfig; import com.capgemini.hackathon.device.simulation.model.Location; import com.google.gson.JsonObject; @@ -26,7 +27,7 @@ protected void addMetainformationWhenPublishLocation(JsonObject event) { } @Override - protected void configureDeviceClient(DeviceClient deviceClient) { + protected void configureDeviceClient(DeviceClientCustom deviceClient) { // nothing to do } } diff --git a/src/main/java/com/capgemini/hackathon/device/simulation/bo/Hospital.java b/src/main/java/com/capgemini/hackathon/device/simulation/bo/Hospital.java index 0325950..41dd72c 100644 --- a/src/main/java/com/capgemini/hackathon/device/simulation/bo/Hospital.java +++ b/src/main/java/com/capgemini/hackathon/device/simulation/bo/Hospital.java @@ -1,164 +1,166 @@ -package com.capgemini.hackathon.device.simulation.bo; - -import java.util.List; -import java.util.UUID; -import java.util.concurrent.CopyOnWriteArrayList; - -import com.capgemini.hackathon.device.simulation.DeviceClientConfig; -import com.capgemini.hackathon.device.simulation.model.Emergency; -import com.capgemini.hackathon.device.simulation.model.Emergency.Status; -import com.capgemini.hackathon.device.simulation.model.Location; -import com.capgemini.hackathon.device.simulation.routing.RouteCalculator; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.graphhopper.GHResponse; -import com.ibm.iotf.client.device.Command; -import com.ibm.iotf.client.device.CommandCallback; -import com.ibm.iotf.client.device.DeviceClient; - -/** - * Hospital simulator class which handles emergencies, and notfies next - * ambulance. - * - * To notify the hospital about an emergency a json command with following - * structure must be send to the hospital device: - * - * - */ -public class Hospital extends Simulation { - - public static final String HOSPITAL_ID = "Hospital_Kings_Cross"; - - private List emergencies = new CopyOnWriteArrayList(); - - public Hospital(DeviceClientConfig deviceClientConfig, Object id) { - super(deviceClientConfig, HOSPITAL_ID); - } - - @Override - protected void configureDeviceClient(DeviceClient deviceClient) { - deviceClient.setCommandCallback(new EmergencyDeviceCommand()); - - } - - @Override - protected void process() { - while (true) { - try { - Thread.sleep(2000); - - synchronized (emergencies) { - for (Emergency emergency : emergencies) { - publishEmergency(emergency); - handleEmergency(emergency); - - } - } - - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - } - - private void handleEmergency(Emergency emergency) { - // find ambulance to solve emergency - if (emergency.getStatus() == Emergency.Status.OPEN) { - Ambulance ambulance = findNearestAmbulance(emergency); - if (ambulance != null) { - ambulance.sendToEmergency(emergency); - emergency.setAmbulanceVin(ambulance.getId().toString()); - emergency.setStatus(Emergency.Status.ONGING); - } - } else if (emergency.getStatus() == Emergency.Status.SOLVED) { - synchronized (emergencies) { - emergencies.remove(emergency); - } - } - } - - private void publishEmergency(Emergency emergency) { - try { - // Publish event to IoT - getDeviceClient().publishEvent(Emergency.EVENT_LOCATION, emergency.asJson()); - - } catch (Exception e) { - e.printStackTrace(); - } - } - - /** - * This method finds which ambulance is the closest ambulance to the - * emergency location - * - * @param emergencyLatitude - * the latitude of the emergency location - * - * @param emergencyLongitude - * the longitude of the emergency location - */ - - public Ambulance findNearestAmbulance(Emergency emergency) { - List ambulances = BORegistry.getInstance().getAmbulances(); - - Ambulance nearestAmbulance = null; - double nearestDistance = Double.MAX_VALUE; - for (Ambulance ambulance : ambulances) { - - if (ambulance.isFree()) { - GHResponse response = RouteCalculator.getInstance().calculateRoute( - ambulance.getCurrentLocation().getLatitude(), ambulance.getCurrentLocation().getLongitude(), - emergency.getLocation().getLatitude(), emergency.getLocation().getLongitude()); - - if (response.getDistance() < nearestDistance) { - nearestAmbulance = ambulance; - nearestDistance = response.getDistance(); - } - - } - } - return nearestAmbulance; - } - - public void solveEmergency(Emergency solvedEmergency) { - synchronized (emergencies) { - for (Emergency emergency : emergencies) { - if (emergency.getEmergencyId().equals(solvedEmergency.getEmergencyId())) { - emergency.setStatus(Status.SOLVED); - System.out.println("Hospital: Emergency " + emergency.getEmergencyId() + " solved"); - - } - } - } - - } - - private class EmergencyDeviceCommand implements CommandCallback { - - @Override - public void processCommand(Command command) { - JsonObject jsonCmd = new JsonParser().parse(command.getPayload()).getAsJsonObject().get("d") - .getAsJsonObject(); - String latitude = jsonCmd.get("latitude").getAsString(); - String longtitude = jsonCmd.get("longitude").getAsString(); - - handleNewEmergency(latitude, longtitude); - - } - - private void handleNewEmergency(String latitude, String longtitude) { - // new emergency - Emergency emergency = new Emergency(); - emergency.setEmergencyId(UUID.randomUUID().toString()); - emergency.setLocation(new Location(Double.valueOf(latitude), Double.valueOf(longtitude))); - - synchronized (emergencies) { - emergencies.add(emergency); - System.out.println("Hospital: Emergency " + emergency.getEmergencyId() + " happend"); - - } - - } - - } -} +package com.capgemini.hackathon.device.simulation.bo; + +import java.util.List; +import java.util.UUID; +import java.util.concurrent.CopyOnWriteArrayList; + +import com.capgemini.hackathon.device.simulation.DeviceClientConfig; +import com.capgemini.hackathon.device.simulation.model.Emergency; +import com.capgemini.hackathon.device.simulation.model.Emergency.Status; +import com.capgemini.hackathon.device.simulation.model.Location; +import com.capgemini.hackathon.device.simulation.routing.RouteCalculator; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.graphhopper.GHResponse; +import com.ibm.iotf.client.device.Command; +import com.ibm.iotf.client.device.CommandCallback; +import com.ibm.iotf.client.device.DeviceClient; +import com.capgemini.hackathon.device.service.DeviceClientCustom; + +/** + * Hospital simulator class which handles emergencies, and notfies next + * ambulance. + * + * To notify the hospital about an emergency a json command with following + * structure must be send to the hospital device: + * + * + */ +public class Hospital extends Simulation { + + public static final String HOSPITAL_ID = "Hospital_Kings_Cross"; + + private List emergencies = new CopyOnWriteArrayList(); + + public Hospital(DeviceClientConfig deviceClientConfig, Object id) { + super(deviceClientConfig, HOSPITAL_ID); + } + + @Override + protected void configureDeviceClient(DeviceClientCustom deviceClient) { + deviceClient.setCommandCallback(new EmergencyDeviceCommand()); + + } + + @Override + protected void process() { + while (true) { + try { + Thread.sleep(2000); + + synchronized (emergencies) { + for (Emergency emergency : emergencies) { + publishEmergency(emergency); + handleEmergency(emergency); + + } + } + + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + private void handleEmergency(Emergency emergency) { + // find ambulance to solve emergency + if (emergency.getStatus() == Emergency.Status.OPEN) { + Ambulance ambulance = findNearestAmbulance(emergency); + if (ambulance != null) { + ambulance.sendToEmergency(emergency); + emergency.setAmbulanceVin(ambulance.getId().toString()); + emergency.setStatus(Emergency.Status.ONGING); + } + } else if (emergency.getStatus() == Emergency.Status.SOLVED) { + synchronized (emergencies) { + emergencies.remove(emergency); + } + } + } + + private void publishEmergency(Emergency emergency) { + try { + // Publish event to IoT + getDeviceClient().publishEvent(Emergency.EVENT_LOCATION, emergency.asJson()); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * This method finds which ambulance is the closest ambulance to the + * emergency location + * + * @param emergencyLatitude + * the latitude of the emergency location + * + * @param emergencyLongitude + * the longitude of the emergency location + */ + + public Ambulance findNearestAmbulance(Emergency emergency) { + List ambulances = BORegistry.getInstance().getAmbulances(); + + Ambulance nearestAmbulance = null; + double nearestDistance = Double.MAX_VALUE; + for (Ambulance ambulance : ambulances) { + + if (ambulance.isFree()) { + GHResponse response = RouteCalculator.getInstance().calculateRoute( + ambulance.getCurrentLocation().getLatitude(), ambulance.getCurrentLocation().getLongitude(), + emergency.getLocation().getLatitude(), emergency.getLocation().getLongitude()); + + if (response.getDistance() < nearestDistance) { + nearestAmbulance = ambulance; + nearestDistance = response.getDistance(); + } + + } + } + return nearestAmbulance; + } + + public void solveEmergency(Emergency solvedEmergency) { + synchronized (emergencies) { + for (Emergency emergency : emergencies) { + if (emergency.getEmergencyId().equals(solvedEmergency.getEmergencyId())) { + emergency.setStatus(Status.SOLVED); + System.out.println("Hospital: Emergency " + emergency.getEmergencyId() + " solved"); + + } + } + } + + } + + private class EmergencyDeviceCommand implements CommandCallback { + //To control Geospatial, the emergency id is created in the REST Service of the RouteWebApplication + @Override + public void processCommand(Command command) { + JsonObject jsonCmd = new JsonParser().parse(command.getPayload()).getAsJsonObject().get("d") + .getAsJsonObject(); + String latitude = jsonCmd.get("latitude").getAsString(); + String longtitude = jsonCmd.get("longitude").getAsString(); + String newEmergencyID = jsonCmd.get("newEmergencyID").getAsString(); + + handleNewEmergency(latitude, longtitude, newEmergencyID); + + } + + private void handleNewEmergency(String latitude, String longtitude, String newEmergencyID) { + // new emergency + Emergency emergency = new Emergency(); + emergency.setEmergencyId(newEmergencyID); + emergency.setLocation(new Location(Double.valueOf(latitude), Double.valueOf(longtitude))); + + synchronized (emergencies) { + emergencies.add(emergency); + System.out.println("Hospital: Emergency " + emergency.getEmergencyId() + " happend"); + + } + + } + + } +} diff --git a/src/main/java/com/capgemini/hackathon/device/simulation/bo/Simulation.java b/src/main/java/com/capgemini/hackathon/device/simulation/bo/Simulation.java index 20ffafc..f26b139 100644 --- a/src/main/java/com/capgemini/hackathon/device/simulation/bo/Simulation.java +++ b/src/main/java/com/capgemini/hackathon/device/simulation/bo/Simulation.java @@ -2,12 +2,13 @@ import java.util.concurrent.Callable; +import com.capgemini.hackathon.device.service.DeviceClientCustom; import com.capgemini.hackathon.device.simulation.DeviceClientConfig; import com.ibm.iotf.client.device.DeviceClient; public abstract class Simulation implements Callable { - private DeviceClient deviceClient; + private DeviceClientCustom deviceClient; private DeviceClientConfig deviceConfig; private Object id; @@ -18,7 +19,7 @@ public Simulation(DeviceClientConfig deviceClientConfig, Object id) { public void connect() { try { - deviceClient = new DeviceClient(deviceConfig.asProperties()); + deviceClient = new DeviceClientCustom(deviceConfig.asProperties()); // Connect to Internet of Things Foundation deviceClient.connect(); configureDeviceClient(deviceClient); @@ -35,7 +36,7 @@ public void disconnect() { BORegistry.getInstance().unregister(this); } - public DeviceClient getDeviceClient() { + public DeviceClientCustom getDeviceClient() { return deviceClient; } @@ -54,6 +55,6 @@ public String call() { protected abstract void process(); - protected abstract void configureDeviceClient(DeviceClient deviceClient); + protected abstract void configureDeviceClient(DeviceClientCustom deviceClient); } diff --git a/src/main/java/com/capgemini/hackathon/device/simulation/bo/Vehicle.java b/src/main/java/com/capgemini/hackathon/device/simulation/bo/Vehicle.java index 817cea8..3acabad 100644 --- a/src/main/java/com/capgemini/hackathon/device/simulation/bo/Vehicle.java +++ b/src/main/java/com/capgemini/hackathon/device/simulation/bo/Vehicle.java @@ -1,5 +1,7 @@ package com.capgemini.hackathon.device.simulation.bo; +import org.joda.time.DateTime; + import com.capgemini.hackathon.device.simulation.DeviceClientConfig; import com.capgemini.hackathon.device.simulation.model.Location; import com.capgemini.hackathon.device.simulation.model.VehicleLocation; @@ -13,9 +15,9 @@ public abstract class Vehicle extends Simulation { // How close the vehicles reach their destination private static final double DIST_LAT_LONG = 0.00005; - private static final double DEFAULT_SPEED = 0.00005; + private static final double DEFAULT_SPEED = 0.0001; // The steps driving the vehicles per iteration - private final static double SPEED = Vehicle.getSpeed(); + private final static double SPEED = Vehicle.getSPEED(); // current Location private Location currentLocation; @@ -63,69 +65,67 @@ protected void driveToDestination(Location destination) { protected void driveToDestination(Location destination, Interruption interruption) { // Calculate the route between the current and destination location + GHResponse response = RouteCalculator.getInstance().calculateRoute(currentLocation.getLatitude(), currentLocation.getLongitude(), destination.getLatitude(), destination.getLongitude()); int i = 0; + long millis = DateTime.now().getMillis(); + long previousMillis = millis; + if (response.getPoints() != null) { while (response.getPoints().getSize() > i) { - + double nextPointLatitude = response.getPoints().getLatitude(i); double nextpointLongitude = response.getPoints().getLongitude(i); - // Take the difference between current location and the - // destination - double distLat = currentLocation.getLatitude() - nextPointLatitude; - double distLong = currentLocation.getLongitude() - nextpointLongitude; - + // Take the difference between current location and the destination + double distLat = nextPointLatitude - currentLocation.getLatitude(); + double distLong = nextpointLongitude - currentLocation.getLongitude(); + // While the difference is bigger than the threshold x - //int j = 0; while ((Math.abs(distLat) > DIST_LAT_LONG || Math.abs(distLong) > DIST_LAT_LONG)) { - // check if we have to move in lat direction - if (Math.abs(distLat) > DIST_LAT_LONG) { - // go xxx steps in direction - if (distLat < 0) { - currentLocation.setLatitude(currentLocation.getLatitude() + SPEED); - } else { - currentLocation.setLatitude(currentLocation.getLatitude() - SPEED); - } - } - // check if we have to move in long direction - if (Math.abs(distLong) > DIST_LAT_LONG) { - if (distLong < 0) { - currentLocation.setLongitude(currentLocation.getLongitude() + SPEED); - } else { - currentLocation.setLongitude(currentLocation.getLongitude() - SPEED); - } + + double absVec = Math.sqrt((distLat * distLat) + (distLong * distLong)); + double factor = SPEED / absVec; + + if (factor > 1) { + factor = 1; } + currentLocation.setLatitude(currentLocation.getLatitude() + (distLat * factor)); + currentLocation.setLongitude(currentLocation.getLongitude() + (distLong * factor)); + try { - try { - Thread.sleep(20); - } catch (InterruptedException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - //if (j % 2 == 0) this.publishLocation(); - if (interruption.interrupt()) { - System.out.println("Driving interrupted"); - return; - } - } catch (Exception e) { e.printStackTrace(); } - distLat = currentLocation.getLatitude() - nextPointLatitude; - distLong = currentLocation.getLongitude() - nextpointLongitude; - //j++; + if (interruption.interrupt()) { + System.out.println("Driving interrupted"); + return; + } + distLat = nextPointLatitude - currentLocation.getLatitude(); + distLong = nextpointLongitude - currentLocation.getLongitude(); + + try { + // whatever time has passed so far, wait until 1s has passed + millis = DateTime.now().getMillis(); + long diffMillies = millis - previousMillis; + if (diffMillies < 1000) { + // System.out.println("Sleeping :" + (1000 - diffMillies) + "ms"); + Thread.sleep(1000 - diffMillies); + } + + previousMillis = DateTime.now().getMillis(); + } catch (Exception e) { + e.printStackTrace(); + } } - currentLocation.setLatitude(nextPointLatitude); - currentLocation.setLongitude(nextpointLongitude); i = i + 1; } @@ -145,13 +145,14 @@ public boolean interrupt() { } - public static double getSpeed() { - String speed = System.getenv("SPEED"); - if (speed != null) { - return Double.valueOf(speed); + public static double getSPEED() { + String SPEED = System.getenv("SPEED"); + if (SPEED != null) { + return Double.valueOf(SPEED); } else { return DEFAULT_SPEED; } } } + diff --git a/src/main/ressources/admin.properties b/src/main/ressources/admin.properties index 601b2b8..cfdf0bc 100644 --- a/src/main/ressources/admin.properties +++ b/src/main/ressources/admin.properties @@ -1,3 +1,3 @@ group.name = simulator -nr.of.cars = 5 -nr.of.ambulances = 3 \ No newline at end of file +nr.of.cars = 4 +nr.of.ambulances = 4 \ No newline at end of file diff --git a/src/main/ressources/application.properties b/src/main/ressources/application.properties index 14dd591..0a060dd 100644 --- a/src/main/ressources/application.properties +++ b/src/main/ressources/application.properties @@ -1,4 +1,4 @@ -api-key = a-0yngjl-qd4wocrack -api-token = eA5HT)4ccFU2dULL7A -orga-id = 0yngjl +api-key = a-5rcpia-hxwn2ipopc +api-token = tSzH3*7wou5pLfZd4e +orga-id = 5rcpia devices = devices/test-devices.json diff --git a/src/main/ressources/devices/test-devices.json b/src/main/ressources/devices/test-devices.json index 091cef9..95302a7 100644 --- a/src/main/ressources/devices/test-devices.json +++ b/src/main/ressources/devices/test-devices.json @@ -1,73 +1,73 @@ -[{ - "simulatorClazz": "com.capgemini.hackathon.device.simulation.bo.Car", - "id": "car0", - "orgId": "0yngjl", - "clientId": "d:0yngjl:car-simulator:car0", - "typeId": "car-simulator", - "deviceId": "car0", - "apiToken": "E3BF69B9-C5B6-41AE-A5AB-70A35F583E64" -},{ - "simulatorClazz": "com.capgemini.hackathon.device.simulation.bo.Car", - "id": "car1", - "orgId": "0yngjl", - "clientId": "d:0yngjl:car-simulator:car1", - "typeId": "car-simulator", - "deviceId": "car1", - "apiToken": "6CFA16A6-210F-4CDD-95FA-7C3158ED9C91" -},{ - "simulatorClazz": "com.capgemini.hackathon.device.simulation.bo.Car", - "id": "car2", - "orgId": "0yngjl", - "clientId": "d:0yngjl:car-simulator:car2", - "typeId": "car-simulator", - "deviceId": "car2", - "apiToken": "F36A8081-11BD-4088-A1A2-0F72069B44F8" -},{ - "simulatorClazz": "com.capgemini.hackathon.device.simulation.bo.Car", - "id": "car3", - "orgId": "0yngjl", - "clientId": "d:0yngjl:car-simulator:car3", - "typeId": "car-simulator", - "deviceId": "car3", - "apiToken": "BFFAE321-AE51-4A3C-BDE9-5F992A653C7C" -},{ - "simulatorClazz": "com.capgemini.hackathon.device.simulation.bo.Car", - "id": "car4", - "orgId": "0yngjl", - "clientId": "d:0yngjl:car-simulator:car4", - "typeId": "car-simulator", - "deviceId": "car4", - "apiToken": "D2340AED-C527-4DED-97EB-31EEF58CBFAB" -},{ - "simulatorClazz": "com.capgemini.hackathon.device.simulation.bo.Ambulance", - "id": "ambulance0", - "orgId": "0yngjl", - "clientId": "d:0yngjl:ambulance-simulator:ambulance0", - "typeId": "ambulance-simulator", - "deviceId": "ambulance0", - "apiToken": "FF7B5C52-98AA-4242-A110-44F306263DC5" -},{ - "simulatorClazz": "com.capgemini.hackathon.device.simulation.bo.Ambulance", - "id": "ambulance1", - "orgId": "0yngjl", - "clientId": "d:0yngjl:ambulance-simulator:ambulance1", - "typeId": "ambulance-simulator", - "deviceId": "ambulance1", - "apiToken": "A27E0DA5-B118-4E2E-8200-3F06112ECD64" -},{ - "simulatorClazz": "com.capgemini.hackathon.device.simulation.bo.Ambulance", - "id": "ambulance2", - "orgId": "0yngjl", - "clientId": "d:0yngjl:ambulance-simulator:ambulance2", - "typeId": "ambulance-simulator", - "deviceId": "ambulance2", - "apiToken": "41B2ED2B-2E05-44ED-A46A-0DA82CB5592C" -},{ - "simulatorClazz": "com.capgemini.hackathon.device.simulation.bo.Hospital", - "id": "hospital", - "orgId": "0yngjl", - "clientId": "d:0yngjl:hospital:hospital1", - "typeId": "hospital", - "deviceId": "hospital1", - "apiToken": "FAD566AC-4C27-43E8-8DF8-EEA6576DAFFB" -}] +[{ + "simulatorClazz": "com.capgemini.hackathon.device.simulation.bo.Car", + "id": "car0", + "orgId": "5rcpia", + "clientId": "d:5rcpia:car-simulator:car0", + "typeId": "car-simulator", + "deviceId": "car0", + "apiToken": "01562EAC-5459-498E-9B1C-8DA4F37D074C" +},{ + "simulatorClazz": "com.capgemini.hackathon.device.simulation.bo.Car", + "id": "car1", + "orgId": "5rcpia", + "clientId": "d:5rcpia:car-simulator:car1", + "typeId": "car-simulator", + "deviceId": "car1", + "apiToken": "1719BB4F-99A0-42D0-8FF2-4A76BC64EAFD" +},{ + "simulatorClazz": "com.capgemini.hackathon.device.simulation.bo.Car", + "id": "car2", + "orgId": "5rcpia", + "clientId": "d:5rcpia:car-simulator:car2", + "typeId": "car-simulator", + "deviceId": "car2", + "apiToken": "E3161649-B59E-4EC9-97E6-2A4711F7DB43" +},{ + "simulatorClazz": "com.capgemini.hackathon.device.simulation.bo.Car", + "id": "car3", + "orgId": "5rcpia", + "clientId": "d:5rcpia:car-simulator:car3", + "typeId": "car-simulator", + "deviceId": "car3", + "apiToken": "451326E3-BC56-4D78-8CD3-70B669F830F8" +},{ + "simulatorClazz": "com.capgemini.hackathon.device.simulation.bo.Ambulance", + "id": "ambulance0", + "orgId": "5rcpia", + "clientId": "d:5rcpia:ambulance-simulator:ambulance0", + "typeId": "ambulance-simulator", + "deviceId": "ambulance0", + "apiToken": "9DBB21F6-B888-4B36-8379-3F8C91912246" +},{ + "simulatorClazz": "com.capgemini.hackathon.device.simulation.bo.Ambulance", + "id": "ambulance1", + "orgId": "5rcpia", + "clientId": "d:5rcpia:ambulance-simulator:ambulance1", + "typeId": "ambulance-simulator", + "deviceId": "ambulance1", + "apiToken": "BC0CD3FD-F67E-4F23-945F-E805165D5590" +},{ + "simulatorClazz": "com.capgemini.hackathon.device.simulation.bo.Ambulance", + "id": "ambulance2", + "orgId": "5rcpia", + "clientId": "d:5rcpia:ambulance-simulator:ambulance2", + "typeId": "ambulance-simulator", + "deviceId": "ambulance2", + "apiToken": "AF289EC5-EE47-4458-AA3E-0A4ACB7EC058" +},{ + "simulatorClazz": "com.capgemini.hackathon.device.simulation.bo.Ambulance", + "id": "ambulance3", + "orgId": "5rcpia", + "clientId": "d:5rcpia:ambulance-simulator:ambulance3", + "typeId": "ambulance-simulator", + "deviceId": "ambulance3", + "apiToken": "CEC839DD-F3D4-4E97-889E-FBA8D3E0BBC7" +},{ + "simulatorClazz": "com.capgemini.hackathon.device.simulation.bo.Hospital", + "id": "hospital", + "orgId": "5rcpia", + "clientId": "d:5rcpia:hospital:hospital1", + "typeId": "hospital", + "deviceId": "hospital1", + "apiToken": "48E34C00-8A32-4C8B-93AC-6C38FE642A46" +}]