aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/service/OperationServiceImpl.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/service/OperationServiceImpl.java')
-rw-r--r--src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/service/OperationServiceImpl.java286
1 files changed, 286 insertions, 0 deletions
diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/service/OperationServiceImpl.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/service/OperationServiceImpl.java
new file mode 100644
index 0000000..0c350fe
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/service/OperationServiceImpl.java
@@ -0,0 +1,286 @@
+package at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.service;
+
+import at.ac.tuwien.sepm.assignment.groupphase.exception.ElementNotFoundException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidOperationException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidVehicleException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.PersistenceException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.ServiceException;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dao.OperationDAO;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dao.VehicleDAO;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Operation;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Operation.Severity;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Operation.Status;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Vehicle;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Vehicle.VehicleType;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+@Service
+public class OperationServiceImpl implements OperationService {
+
+ private static final Logger LOG = LoggerFactory.getLogger(OperationServiceImpl.class);
+
+ private final OperationDAO operationDAO;
+ private final VehicleDAO vehicleDAO;
+ private final VehicleService vehicleService;
+
+ public OperationServiceImpl(
+ OperationDAO operationDAO, VehicleDAO vehicleDAO, VehicleService vehicleService) {
+ this.operationDAO = operationDAO;
+ this.vehicleDAO = vehicleDAO;
+ this.vehicleService = vehicleService;
+ }
+
+ @Override
+ public long add(Operation o) throws InvalidOperationException, ServiceException {
+ if (o.created() != null) {
+ throw new InvalidOperationException("Erstellungszeitpunkt darf nicht gesetzt sein");
+ }
+
+ if (o.severity() != null) {
+ throw new InvalidOperationException("Der Schweregrad darf nicht gesetzt sein");
+ }
+
+ if (o.id() != 0) {
+ throw new InvalidOperationException("Einsatz-ID muss 0 sein");
+ }
+
+ if (o.status() != Status.ACTIVE)
+ LOG.info("Status was set but will be overridden"); // TODO: nullable instead??
+
+ try {
+ for (long id : (Iterable<Long>) o.vehicles().stream().map(Vehicle::id)::iterator) {
+ Vehicle v = vehicleDAO.get(id);
+ VehicleServiceImpl.validateVehicle(v);
+
+ if (v.status() != Vehicle.Status.FREI_FUNK
+ && v.status() != Vehicle.Status.FREI_WACHE)
+ throw new InvalidOperationException("Fahrzeug nicht verfügbar: " + v.status());
+ }
+
+ validateOperation(o);
+
+ return operationDAO.add(
+ o.toBuilder()
+ .created(Instant.now())
+ .severity(extractSeverityFromOpCode(o.opCode()))
+ .status(Status.ACTIVE)
+ .build());
+ } catch (PersistenceException e) {
+ throw new ServiceException(e);
+ } catch (InvalidVehicleException e) {
+ // already logged as invalid vehicle
+ throw new InvalidOperationException("Enthaltenes Fahrzeug ist ungültig", e);
+ } catch (ElementNotFoundException e) {
+ throw new InvalidOperationException("Enthaltenes Fahrzeug existiert nicht", e);
+ }
+ }
+
+ @Override
+ public void requestVehicles(long operationId, Set<Long> vehicleIds)
+ throws InvalidOperationException, InvalidVehicleException, ServiceException {
+ Set<Vehicle> vs = new HashSet<>();
+
+ try {
+ if (operationId <= 0) {
+ throw new InvalidOperationException("Einsatz-ID ist ungültig");
+ }
+ Operation o = operationDAO.get(operationId);
+ validateOperation(o);
+
+ if (o.opCode().trim().isEmpty()
+ || extractSeverityFromOpCode(o.opCode()) != o.severity()) {
+ throw new InvalidOperationException("Einsatzcode ist ungültig");
+ }
+
+ if (o.status() != Status.ACTIVE) {
+ throw new InvalidOperationException("Einsatz ist ungültig");
+ }
+
+ if (o.created() == null) {
+ throw new InvalidOperationException("Erstellungszeitpunkt darf nicht leer sein");
+ }
+
+ for (Long id : vehicleIds) {
+ if (id <= 0) {
+ throw new InvalidVehicleException("Fahrzeug-ID ist ungültig");
+ }
+
+ try {
+ Vehicle v = vehicleDAO.get(id);
+ VehicleServiceImpl.validateVehicle(v);
+ if (v.status() != Vehicle.Status.FREI_FUNK
+ && v.status() != Vehicle.Status.FREI_WACHE)
+ throw new InvalidOperationException(
+ "Fahrzeug nicht verfügbar: " + v.status());
+
+ vs.add(v);
+ } catch (ElementNotFoundException e) {
+ throw new InvalidVehicleException("VehicleId ist invalid");
+ }
+ }
+
+ vs.addAll(o.vehicles());
+ if (vs.equals(o.vehicles())) return;
+
+ operationDAO.update(o.toBuilder().vehicles(vs).build());
+ } catch (ElementNotFoundException e) {
+ throw new InvalidOperationException("Kein Einsatz mit dieser ID existiert");
+ } catch (PersistenceException e) {
+ throw new ServiceException(e);
+ }
+ }
+
+ @Override
+ public void complete(long operationId, Status status)
+ throws InvalidOperationException, ServiceException {
+ try {
+ Operation o = operationDAO.get(operationId);
+ operationDAO.update(o.toBuilder().status(status).build());
+ } catch (ElementNotFoundException e) {
+ throw new InvalidOperationException(e);
+ } catch (PersistenceException e) {
+ throw new ServiceException(e);
+ }
+ }
+
+ @Override
+ public SortedSet<Vehicle> rankVehicles(String opCode)
+ throws InvalidOperationException, ServiceException {
+ Set<Vehicle> vehicles =
+ vehicleService.list(EnumSet.complementOf(EnumSet.of(Vehicle.Status.ABGEMELDET)));
+
+ List<Predicate<Vehicle>> priorities = new ArrayList<>();
+ Predicate<Vehicle> ktw = v -> v.type() == VehicleType.KTW;
+ Predicate<Vehicle> rtwNoNEF = v -> v.type() == VehicleType.RTW && !v.hasNef();
+ Predicate<Vehicle> rtwNEF = v -> v.type() == VehicleType.RTW && v.hasNef();
+ Predicate<Vehicle> nef = v -> v.type() == VehicleType.NEF;
+ Predicate<Vehicle> nah = v -> v.type() == VehicleType.NAH;
+
+ switch (extractSeverityFromOpCode(opCode)) {
+ case A:
+ // fallthrough
+ case B:
+ // fallthrough
+ case O:
+ priorities.add(ktw);
+ priorities.add(rtwNoNEF);
+ priorities.add(rtwNEF);
+ break;
+ case C:
+ priorities.add(rtwNEF);
+ priorities.add(rtwNoNEF);
+ priorities.add(ktw);
+ break;
+ case D:
+ priorities.add(rtwNEF);
+ priorities.add(nef);
+ priorities.add(nah);
+ priorities.add(rtwNoNEF);
+ priorities.add(ktw);
+ break;
+ case E:
+ priorities.add(nah);
+ priorities.add(nef);
+ priorities.add(rtwNEF);
+ priorities.add(rtwNoNEF);
+ priorities.add(ktw);
+ break;
+ }
+
+ Comparator<Vehicle> vehicleComparator =
+ (v1, v2) -> {
+ for (Predicate<Vehicle> priority : priorities) {
+ if (priority.test(v1)) {
+ return -1;
+ }
+ if (priority.test(v2)) {
+ return +1;
+ }
+ }
+ return 0;
+ };
+
+ Supplier<TreeSet<Vehicle>> supplier = () -> new TreeSet<>(vehicleComparator);
+
+ return vehicles.stream().collect(Collectors.toCollection(supplier));
+ }
+
+ @Override
+ public Set<Operation> list(EnumSet<Status> statuses) throws ServiceException {
+ try {
+ Set<Operation> operations = operationDAO.list(statuses);
+ for (Operation o : operations) validateOperation(o);
+
+ return operations;
+ } catch (PersistenceException e) {
+ throw new ServiceException(e);
+ } catch (InvalidOperationException e) {
+ // database returned invalid values
+ throw new ServiceException("DB returned invalid operation", e);
+ }
+ }
+
+ private static void validateOperation(Operation o) throws InvalidOperationException {
+ if (o.vehicles().isEmpty()) {
+ throw new InvalidOperationException(
+ "Es muss mindestens ein Fahrzeug ausgewählt werden!");
+ }
+
+ for (Vehicle v : o.vehicles()) {
+ try {
+ VehicleServiceImpl.validateVehicle(v);
+ } catch (InvalidVehicleException e) {
+ throw new InvalidOperationException("Fahrzeug " + v.name() + " ist ungültig", e);
+ }
+
+ // TODO: validate if NEF/RTW/NAH conditions?
+ }
+
+ Instant created = o.created();
+ if (created != null && created.isAfter(Instant.now())) {
+ throw new InvalidOperationException("Einsatz wurde in der Zukunft erstellt");
+ }
+
+ if (o.destination() == null || o.destination().trim().isEmpty()) {
+ throw new InvalidOperationException("Adresse darf nicht leer sein");
+ }
+
+ if (o.destination().length() > 100) {
+ throw new InvalidOperationException("Adresse darf 100 Zeichen nicht überschreiten");
+ }
+
+ if (o.additionalInfo() != null && o.additionalInfo().length() > 100) {
+ throw new InvalidOperationException("Anmerkung darf 100 Zeichen nicht überschreiten");
+ }
+ }
+
+ private static final Pattern opCodePattern =
+ Pattern.compile("(?:\\w{1,3}-\\d{0,2})([ABCDEO])(?:.*)");
+
+ private static Severity extractSeverityFromOpCode(String opCode)
+ throws InvalidOperationException {
+ Matcher m = opCodePattern.matcher(opCode);
+
+ if (!m.matches()) {
+ throw new InvalidOperationException("Einsatzcode ist ungültig");
+ }
+
+ return Severity.valueOf(m.group(1));
+ }
+}