diff options
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.java | 286 |
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)); + } +} |