From 1cd56d52eb587abb8ff63f5a09995166c517c954 Mon Sep 17 00:00:00 2001 From: Imesha Sudasingha Date: Tue, 20 Mar 2018 12:48:57 +0530 Subject: [PATCH] Initial implementation of camera groups and multiprocessing cam server --- .../eduze/fyp/api/ConfigurationManager.java | 10 +- .../api/listeners/ProcessedMapListener.java | 3 +- .../org/eduze/fyp/api/model/CameraConfig.java | 46 +++- .../org/eduze/fyp/api/model/CameraGroup.java | 3 +- .../org/eduze/fyp/api/model/PointMapping.java | 20 +- .../org/eduze/fyp/api/resources/Point.java | 11 +- .../fyp/core/ConfigurationManagerImpl.java | 32 ++- .../org/eduze/fyp/core/MapProcessorImpl.java | 125 +++++++---- .../java/org/eduze/fyp/core/db/DBHandler.java | 9 +- .../eduze/fyp/core/db/dao/AbstractDAO.java | 8 + .../fyp/core/db/dao/AbstractDAOImpl.java | 74 ++++++- .../fyp/core/db/dao/CameraConfigDAO.java | 4 +- .../fyp/core/db/dao/CameraConfigDAOImpl.java | 36 ++-- .../web/controllers/AnalyticsController.java | 41 ++-- .../fyp/web/controllers/ConfigController.java | 17 +- .../fyp/web/services/AnalyticsService.java | 61 +++--- .../eduze/fyp/web/services/ConfigService.java | 48 +++-- .../realtime-map/realtime-map.component.html | 13 +- .../realtime-map/realtime-map.component.ts | 75 +++---- ngapp/src/app/resources/camera-config.ts | 15 +- ngapp/src/app/services/analytics.service.ts | 6 +- .../point-mapping.component.html | 2 +- sense/App.py | 64 ++++++ sense/CamServer.py | 201 ------------------ sense/DetectorService.py | 34 +++ sense/LightWeightCamServer.py | 36 ++-- sense/Sense.py | 9 +- sense/communication/APIAccess.py | 25 +-- sense/communication/ServerSession.py | 27 ++- 29 files changed, 580 insertions(+), 475 deletions(-) create mode 100644 sense/App.py delete mode 100755 sense/CamServer.py create mode 100644 sense/DetectorService.py diff --git a/cramp-api/src/main/java/org/eduze/fyp/api/ConfigurationManager.java b/cramp-api/src/main/java/org/eduze/fyp/api/ConfigurationManager.java index 81228477..6b96a357 100644 --- a/cramp-api/src/main/java/org/eduze/fyp/api/ConfigurationManager.java +++ b/cramp-api/src/main/java/org/eduze/fyp/api/ConfigurationManager.java @@ -20,9 +20,10 @@ import org.eduze.fyp.api.config.Startable; import org.eduze.fyp.api.listeners.ConfigurationListener; -import org.eduze.fyp.api.model.Zone; import org.eduze.fyp.api.model.CameraConfig; +import org.eduze.fyp.api.model.CameraGroup; import org.eduze.fyp.api.model.PointMapping; +import org.eduze.fyp.api.model.Zone; import java.awt.image.BufferedImage; import java.net.InetSocketAddress; @@ -37,6 +38,11 @@ */ public interface ConfigurationManager extends Startable { + /** + * Adds a new camera configuration. + * + * @param cameraConfig {@link CameraConfig} to be added + */ void addCameraConfig(CameraConfig cameraConfig); /** @@ -70,4 +76,6 @@ public interface ConfigurationManager extends Startable { boolean isConfigured(); List getZones(); + + Map getCameraGroups(); } diff --git a/cramp-api/src/main/java/org/eduze/fyp/api/listeners/ProcessedMapListener.java b/cramp-api/src/main/java/org/eduze/fyp/api/listeners/ProcessedMapListener.java index a9ad64a7..cf0de4a4 100644 --- a/cramp-api/src/main/java/org/eduze/fyp/api/listeners/ProcessedMapListener.java +++ b/cramp-api/src/main/java/org/eduze/fyp/api/listeners/ProcessedMapListener.java @@ -20,6 +20,7 @@ */ package org.eduze.fyp.api.listeners; +import org.eduze.fyp.api.model.CameraGroup; import org.eduze.fyp.api.resources.PersonSnapshot; import java.util.Date; @@ -27,7 +28,7 @@ public interface ProcessedMapListener { - void mapProcessed(List> snapshots); + void mapProcessed(CameraGroup cameraGroup, List> snapshots); void onFrame(List> snapshots, Date timestamp); } diff --git a/cramp-api/src/main/java/org/eduze/fyp/api/model/CameraConfig.java b/cramp-api/src/main/java/org/eduze/fyp/api/model/CameraConfig.java index 3a43eabe..36d044c0 100644 --- a/cramp-api/src/main/java/org/eduze/fyp/api/model/CameraConfig.java +++ b/cramp-api/src/main/java/org/eduze/fyp/api/model/CameraConfig.java @@ -36,7 +36,7 @@ @XmlRootElement @Entity @Table(name = "camera_configs") -public class CameraConfig { +public class CameraConfig implements Cloneable { @Id @Column(name = "id") @@ -46,15 +46,17 @@ public class CameraConfig { @Column(unique = true) private int cameraId; private String ipAndPort; + private int width; + private int height; @Lob @Column(columnDefinition = "LONGBLOB") private byte[] view; - @OneToOne(mappedBy = "cameraConfig", cascade = CascadeType.ALL, fetch = FetchType.EAGER) + @OneToOne(mappedBy = "cameraConfig", cascade = {CascadeType.ALL}, fetch = FetchType.EAGER) private PointMapping pointMapping = new PointMapping(); - @ManyToOne + @ManyToOne() private CameraGroup cameraGroup; public byte[] getView() { @@ -105,7 +107,43 @@ public void setCameraGroup(CameraGroup cameraGroup) { this.cameraGroup = cameraGroup; } + public int getWidth() { + return width; + } + + public void setWidth(int width) { + this.width = width; + } + + public int getHeight() { + return height; + } + + public void setHeight(int height) { + this.height = height; + } + + @Override + public CameraConfig clone() { + try { + super.clone(); + } catch (CloneNotSupportedException ignored) { } + + CameraConfig cameraConfig = new CameraConfig(); + cameraConfig.setView(getView()); + cameraConfig.setCameraGroup(getCameraGroup()); + cameraConfig.setPointMapping(getPointMapping().clone()); + cameraConfig.setHeight(getHeight()); + cameraConfig.setWidth(getWidth()); + cameraConfig.setCameraId(getCameraId()); + cameraConfig.setIpAndPort(getIpAndPort()); + cameraConfig.setIpAndPort(getIpAndPort()); + + return cameraConfig; + } + public String toString() { - return String.format("{ camera : %s, ipAndPort : %s, pointMappings: %s }", cameraId, ipAndPort, pointMapping); + return String.format("{ camera : %s, ipAndPort : %s, pointMappings: %s, cameraGroup: %s }", + cameraId, ipAndPort, pointMapping, cameraGroup); } } diff --git a/cramp-api/src/main/java/org/eduze/fyp/api/model/CameraGroup.java b/cramp-api/src/main/java/org/eduze/fyp/api/model/CameraGroup.java index eda2c782..1c246a82 100644 --- a/cramp-api/src/main/java/org/eduze/fyp/api/model/CameraGroup.java +++ b/cramp-api/src/main/java/org/eduze/fyp/api/model/CameraGroup.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; @@ -33,7 +34,7 @@ public class CameraGroup { private byte[] map; @XmlTransient - @OneToMany(mappedBy = "cameraGroup") + @OneToMany(mappedBy = "cameraGroup", cascade = {CascadeType.REMOVE}) private Set cameraConfigs; public int getId() { diff --git a/cramp-api/src/main/java/org/eduze/fyp/api/model/PointMapping.java b/cramp-api/src/main/java/org/eduze/fyp/api/model/PointMapping.java index f733fe3f..079b1f44 100644 --- a/cramp-api/src/main/java/org/eduze/fyp/api/model/PointMapping.java +++ b/cramp-api/src/main/java/org/eduze/fyp/api/model/PointMapping.java @@ -28,6 +28,7 @@ import javax.xml.bind.annotation.XmlTransient; import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; /** * Class to represent camera space to world space mappings of reference points (4 reference points usually). Points are @@ -38,7 +39,7 @@ @Entity @Table(name = "point_mappings") @JsonIgnoreProperties("cameraConfig") -public class PointMapping { +public class PointMapping implements Cloneable { @Id @Column(name = "id") @@ -52,7 +53,7 @@ public class PointMapping { private List worldSpacePoints = new ArrayList<>(4); @XmlTransient - @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) + @OneToOne(fetch = FetchType.LAZY, cascade = {CascadeType.ALL}) @JoinColumn(name = "camera_config_id", nullable = false) private CameraConfig cameraConfig; @@ -108,6 +109,21 @@ public void setId(int id) { this.id = id; } + @Override + public PointMapping clone() { + try { + super.clone(); + } catch (CloneNotSupportedException ignored) { } + + PointMapping pointMapping = new PointMapping(); + pointMapping.setCameraConfig(getCameraConfig()); + // Not sending ID since clone used for write tasks + // pointMapping.setId(getId()); + pointMapping.setScreenSpacePoints(getScreenSpacePoints().stream().map(Point::clone).collect(Collectors.toList())); + pointMapping.setWorldSpacePoints(getWorldSpacePoints().stream().map(Point::clone).collect(Collectors.toList())); + return pointMapping; + } + @Override public String toString() { StringBuilder sb = new StringBuilder(); diff --git a/cramp-api/src/main/java/org/eduze/fyp/api/resources/Point.java b/cramp-api/src/main/java/org/eduze/fyp/api/resources/Point.java index c2fc2f36..292aad95 100644 --- a/cramp-api/src/main/java/org/eduze/fyp/api/resources/Point.java +++ b/cramp-api/src/main/java/org/eduze/fyp/api/resources/Point.java @@ -25,7 +25,7 @@ * * @author Imesha Sudasingha */ -public class Point { +public class Point implements Cloneable { private double x; private double y; @@ -52,6 +52,15 @@ public void setY(double y) { this.y = y; } + @Override + public Point clone() { + try { + super.clone(); + } catch (CloneNotSupportedException ignored) { } + + return new Point(getX(), getY()); + } + @Override public String toString() { return String.format("[%f,%f]", x, y); diff --git a/cramp-core/src/main/java/org/eduze/fyp/core/ConfigurationManagerImpl.java b/cramp-core/src/main/java/org/eduze/fyp/core/ConfigurationManagerImpl.java index c7a49d9d..ecb37e5c 100644 --- a/cramp-core/src/main/java/org/eduze/fyp/core/ConfigurationManagerImpl.java +++ b/cramp-core/src/main/java/org/eduze/fyp/core/ConfigurationManagerImpl.java @@ -27,6 +27,7 @@ import org.eduze.fyp.api.annotations.Mode; import org.eduze.fyp.api.listeners.ConfigurationListener; import org.eduze.fyp.api.model.CameraConfig; +import org.eduze.fyp.api.model.CameraGroup; import org.eduze.fyp.api.model.PointMapping; import org.eduze.fyp.api.model.Zone; import org.eduze.fyp.core.db.dao.CameraConfigDAO; @@ -68,12 +69,13 @@ public class ConfigurationManagerImpl implements ConfigurationManager { private String propertiesFile; private BufferedImage map; - private Set cameraIds = new HashSet<>(); private Set configurationListeners = new HashSet<>(); - private CameraConfigDAO cameraConfigDAO; private Map cameraConfigs = new ConcurrentHashMap<>(); + private Map cameraGroups = new ConcurrentHashMap<>(); // TODO: 1/5/18 If zones are deleted through UI, it is not reflected here + private List zones = new ArrayList<>(); + private CameraConfigDAO cameraConfigDAO; private ZoneDAO zoneDAO; public ConfigurationManagerImpl() { } @@ -90,37 +92,46 @@ private void loadConfiguration() throws IOException { this.cameraConfigDAO.list() .forEach(cameraConfig -> this.cameraConfigs.put(cameraConfig.getCameraId(), cameraConfig)); + + this.cameraConfigDAO.cameraGroups().forEach(cameraGroup -> cameraGroups.put(cameraGroup.getId(), cameraGroup)); } + @Override public synchronized void addCameraConfig(CameraConfig cameraConfig) { stateManager.checkState(State.STARTED); - logger.debug("Adding camera config - {}", cameraConfig); + logger.debug("Adding camera config - {}. Total: {}", cameraConfig, cameraConfigs.size()); cameraConfigs.put(cameraConfig.getCameraId(), cameraConfig); notifyConfigurationChange(); } + @Override public synchronized void addPointMapping(int cameraId, PointMapping mappings) { stateManager.checkState(State.STARTED); cameraConfigs.get(cameraId).setPointMapping(mappings); notifyConfigurationChange(); } + /** + * Returns the next camera ID to be taken by a camera + * + * @return next camera ID + */ + @Override public synchronized int getNextCameraId() { stateManager.checkState(State.STARTED); - OptionalInt max = cameraIds.stream().mapToInt(Integer::intValue).max(); + OptionalInt max = cameraConfigs.keySet().stream().mapToInt(Integer::intValue).max(); int nextInt = 1; if (max.isPresent()) { nextInt = max.getAsInt() + 1; } - cameraIds.add(nextInt); return nextInt; } @Override public CameraConfig getCameraConfig(int cameraId) { - return cameraConfigs.get(cameraId); + return cameraConfigs.get(cameraId).clone(); } @Override @@ -149,7 +160,7 @@ public PointMapping getPointMapping(int cameraId) { @Override public Set getCameraIds() { - return cameraIds; + return cameraConfigs.keySet(); } @Override @@ -178,7 +189,8 @@ public synchronized void removeConfigurationListener(ConfigurationListener liste @Override public boolean isConfigured() { - return cameraIds.size() > 0 && cameraIds.size() == cameraConfigs.keySet().size(); + return cameraConfigs.values().size() > 0 && cameraConfigs.values().stream() + .noneMatch(cameraConfig -> cameraConfig.getCameraGroup() == null); } private void notifyConfigurationChange() { @@ -240,4 +252,8 @@ public void setZoneDAO(ZoneDAO zoneDAO) { public void setCameraConfigDAO(CameraConfigDAO cameraConfigDAO) { this.cameraConfigDAO = cameraConfigDAO; } + + public Map getCameraGroups() { + return cameraGroups; + } } diff --git a/cramp-core/src/main/java/org/eduze/fyp/core/MapProcessorImpl.java b/cramp-core/src/main/java/org/eduze/fyp/core/MapProcessorImpl.java index 2883dcbe..1e92db55 100644 --- a/cramp-core/src/main/java/org/eduze/fyp/core/MapProcessorImpl.java +++ b/cramp-core/src/main/java/org/eduze/fyp/core/MapProcessorImpl.java @@ -22,20 +22,31 @@ package org.eduze.fyp.core; import org.eduze.fyp.api.CameraCoordinator; +import org.eduze.fyp.api.ConfigurationManager; import org.eduze.fyp.api.MapProcessor; import org.eduze.fyp.api.State; import org.eduze.fyp.api.StateManager; import org.eduze.fyp.api.annotations.AutoStart; import org.eduze.fyp.api.listeners.ProcessedMapListener; -import org.eduze.fyp.core.resources.GlobalMap; +import org.eduze.fyp.api.model.CameraConfig; +import org.eduze.fyp.api.model.CameraGroup; import org.eduze.fyp.api.resources.LocalMap; import org.eduze.fyp.api.resources.PersonSnapshot; import org.eduze.fyp.api.util.Args; +import org.eduze.fyp.core.resources.GlobalMap; import org.eduze.fyp.core.util.AccuracyTester; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - -import java.util.*; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -47,7 +58,7 @@ public class MapProcessorImpl implements MapProcessor { private static final Logger logger = LoggerFactory.getLogger(MapProcessorImpl.class); - private final GlobalMap globalMap = new GlobalMap(); + private final Map globalMaps = new HashMap<>(); private final StateManager stateManager = new StateManager(State.STOPPED); private final Queue localMapQueue = new LinkedList<>(); private CameraCoordinator cameraCoordinator; @@ -55,40 +66,24 @@ public class MapProcessorImpl implements MapProcessor { private ExecutorService processor; private AccuracyTester accuracyTester = null; - - public AccuracyTester getAccuracyTester() { - return accuracyTester; - } - - public void setAccuracyTester(AccuracyTester accuracyTester) { - this.accuracyTester = accuracyTester; - } - private ZoneMapper zoneMapper = null; - private PhotoMapper photoMapper = null; - public ZoneMapper getZoneMapper() { - return zoneMapper; - } - - public PhotoMapper getPhotoMapper() { - return photoMapper; - } - - public void setPhotoMapper(PhotoMapper photoMapper) { - this.photoMapper = photoMapper; - } - - public void setZoneMapper(ZoneMapper zoneMapper) { - this.zoneMapper = zoneMapper; - } + @Autowired + private ConfigurationManager configurationManager; @Override public void addLocalMap(LocalMap map) { stateManager.checkState(State.STARTED); logger.debug("Adding a local map for camera : {}", map.getCameraId()); + // Point mapping conversion + // CameraConfig cameraConfig = configurationManager.getCameraConfig(map.getCameraId()); + // map.getPersonCoordinates().forEach(personCoordinate -> { + // personCoordinate.setX(personCoordinate.getX() * Constants.CAMERA_VIEW_WIDTH / cameraConfig.getWidth()); + // personCoordinate.setY(personCoordinate.getY() * Constants.CAMERA_VIEW_HEIGHT / cameraConfig.getHeight()); + // }); + synchronized (this) { localMapQueue.add(map); } @@ -106,20 +101,29 @@ public void removeProcessedMapListener(ProcessedMapListener listener) { @Override public void nextFrame(Date timestamp) { - List> snapshots = globalMap.getSnapshot(); - synchronized (this) { - mapListeners.forEach(listener -> listener.onFrame(snapshots,timestamp)); + //TODO need to change + for (GlobalMap globalMap : globalMaps.values()) { + List> snapshots = globalMap.getSnapshot(); + synchronized (this) { + mapListeners.forEach(listener -> listener.onFrame(snapshots, timestamp)); + } } } @Override public void start() { - globalMap.setZoneMapper(zoneMapper); - globalMap.setPhotoMapper(photoMapper); - globalMap.setAccuracyTester(accuracyTester); - Args.notNull(cameraCoordinator, "cameraCoordinator"); + configurationManager.getCameraGroups().keySet() + .forEach(id -> { + GlobalMap globalMap = new GlobalMap(); + + globalMap.setZoneMapper(zoneMapper); + globalMap.setPhotoMapper(photoMapper); + globalMap.setAccuracyTester(accuracyTester); + globalMaps.put(id, globalMap); + }); + stateManager.checkState(State.STOPPED); logger.debug("Starting map collector"); @@ -145,30 +149,31 @@ public void start() { } try { + GlobalMap globalMap = null; if (nextMap != null) { + CameraConfig cameraConfig = configurationManager.getCameraConfig(nextMap.getCameraId()); + CameraGroup cameraGroup = cameraConfig.getCameraGroup(); + globalMap = globalMaps.get(cameraGroup.getId()); globalMap.update(nextMap); - } - if (cameraCoordinator.getRealTimestamp() - lastTimestamp > MAP_REFRESH_INTERVAL) { - lastTimestamp = cameraCoordinator.getRealTimestamp(); - long minTimestamp = lastTimestamp - MAP_REFRESH_THRESHOLD; - globalMap.refresh(minTimestamp); - } - - - if (nextMap != null) { List> snapshots = globalMap.getSnapshot(); synchronized (this) { - mapListeners.forEach(listener -> listener.mapProcessed(snapshots)); + mapListeners.forEach(listener -> listener.mapProcessed(cameraGroup, snapshots)); } } + if (cameraCoordinator.getRealTimestamp() - lastTimestamp > MAP_REFRESH_INTERVAL) { + lastTimestamp = cameraCoordinator.getRealTimestamp(); + long minTimestamp = lastTimestamp - MAP_REFRESH_THRESHOLD; + globalMaps.values().forEach(map -> map.refresh(minTimestamp)); + } } catch (Exception e) { logger.error("Error occurred in map processing", e); } } }); + stateManager.setState(State.STARTED); logger.info("Map collector started"); } @@ -193,4 +198,32 @@ public synchronized void setMapListeners(Set listeners) { public void setCameraCoordinator(CameraCoordinator cameraCoordinator) { this.cameraCoordinator = cameraCoordinator; } + + public void setConfigurationManager(ConfigurationManager configurationManager) { + this.configurationManager = configurationManager; + } + + public AccuracyTester getAccuracyTester() { + return accuracyTester; + } + + public void setAccuracyTester(AccuracyTester accuracyTester) { + this.accuracyTester = accuracyTester; + } + + public ZoneMapper getZoneMapper() { + return zoneMapper; + } + + public PhotoMapper getPhotoMapper() { + return photoMapper; + } + + public void setPhotoMapper(PhotoMapper photoMapper) { + this.photoMapper = photoMapper; + } + + public void setZoneMapper(ZoneMapper zoneMapper) { + this.zoneMapper = zoneMapper; + } } diff --git a/cramp-core/src/main/java/org/eduze/fyp/core/db/DBHandler.java b/cramp-core/src/main/java/org/eduze/fyp/core/db/DBHandler.java index 0b5ce782..dbd983ab 100644 --- a/cramp-core/src/main/java/org/eduze/fyp/core/db/DBHandler.java +++ b/cramp-core/src/main/java/org/eduze/fyp/core/db/DBHandler.java @@ -23,13 +23,14 @@ import org.eduze.fyp.api.annotations.AutoStart; import org.eduze.fyp.api.listeners.ProcessedMapListener; +import org.eduze.fyp.api.model.CameraGroup; +import org.eduze.fyp.api.model.CaptureStamp; +import org.eduze.fyp.api.model.Person; import org.eduze.fyp.api.resources.PersonSnapshot; import org.eduze.fyp.core.PhotoMapper; import org.eduze.fyp.core.db.dao.CaptureStampDAO; import org.eduze.fyp.core.db.dao.PersonDAO; import org.eduze.fyp.core.db.dao.ZoneDAO; -import org.eduze.fyp.api.model.CaptureStamp; -import org.eduze.fyp.api.model.Person; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -66,8 +67,8 @@ public void setCaptureStampDAO(CaptureStampDAO captureStampDAO) { } @Override - public void mapProcessed(List> snapshots) { - //Nothing to do here. Code moved to on Frame. + public void mapProcessed(CameraGroup cameraGroup, List> snapshots) { + // Nothing here } @Override diff --git a/cramp-core/src/main/java/org/eduze/fyp/core/db/dao/AbstractDAO.java b/cramp-core/src/main/java/org/eduze/fyp/core/db/dao/AbstractDAO.java index 54f22500..24fc3057 100644 --- a/cramp-core/src/main/java/org/eduze/fyp/core/db/dao/AbstractDAO.java +++ b/cramp-core/src/main/java/org/eduze/fyp/core/db/dao/AbstractDAO.java @@ -22,4 +22,12 @@ public interface AbstractDAO { void save(Object object); + + void update(Object object); + + Object merge(Object object); + + void persist(Object object); + + void saveOrUpdate(Object object); } diff --git a/cramp-core/src/main/java/org/eduze/fyp/core/db/dao/AbstractDAOImpl.java b/cramp-core/src/main/java/org/eduze/fyp/core/db/dao/AbstractDAOImpl.java index 94d72d67..949479be 100644 --- a/cramp-core/src/main/java/org/eduze/fyp/core/db/dao/AbstractDAOImpl.java +++ b/cramp-core/src/main/java/org/eduze/fyp/core/db/dao/AbstractDAOImpl.java @@ -5,18 +5,84 @@ import org.hibernate.Session; import org.hibernate.SessionFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class AbstractDAOImpl implements AbstractDAO { - protected SessionFactory sessionFactory; + private static final Logger logger = LoggerFactory.getLogger(AbstractDAOImpl.class); + + SessionFactory sessionFactory; @Override public void save(Object object) { Session session = this.sessionFactory.openSession(); session.beginTransaction(); - session.persist(object); - session.getTransaction().commit(); - session.close(); + try { + session.persist(object); + session.getTransaction().commit(); + } catch (Exception e) { + session.getTransaction().rollback(); + throw new IllegalStateException("Unable to save", e); + } + } + + @Override + public void update(Object object) { + Session session = this.sessionFactory.openSession(); + session.beginTransaction(); + try { + session.update(object); + session.getTransaction().commit(); + } catch (Exception e) { + session.getTransaction().rollback(); + logger.error("Error occurred when updating - {}", object, e); + throw new IllegalStateException("Unable to update", e); + } + } + + @Override + public Object merge(Object object) { + Session session = this.sessionFactory.openSession(); + session.beginTransaction(); + Object persisted; + try { + persisted = session.merge(object); + session.getTransaction().commit(); + } catch (Exception e) { + session.getTransaction().rollback(); + logger.error("Error occurred when updating - {}", object, e); + throw new IllegalStateException("Unable to update", e); + } + return persisted; + } + + @Override + public void persist(Object object) { + Session session = this.sessionFactory.openSession(); + session.beginTransaction(); + try { + session.persist(object); + session.getTransaction().commit(); + } catch (Exception e) { + session.getTransaction().rollback(); + logger.error("Unable to persist", e); + throw new IllegalStateException("Unable to persist", e); + } + } + + @Override + public void saveOrUpdate(Object object) { + Session session = this.sessionFactory.getCurrentSession(); + session.beginTransaction(); + try { + session.saveOrUpdate(object); + session.getTransaction().commit(); + } catch (Exception e) { + session.getTransaction().rollback(); + logger.error("Unable to save or update", e); + throw new IllegalStateException("Unable to save or update", e); + } } public void setSessionFactory(SessionFactory sessionFactory) { diff --git a/cramp-core/src/main/java/org/eduze/fyp/core/db/dao/CameraConfigDAO.java b/cramp-core/src/main/java/org/eduze/fyp/core/db/dao/CameraConfigDAO.java index bf9baa1c..4a8858fd 100644 --- a/cramp-core/src/main/java/org/eduze/fyp/core/db/dao/CameraConfigDAO.java +++ b/cramp-core/src/main/java/org/eduze/fyp/core/db/dao/CameraConfigDAO.java @@ -34,9 +34,9 @@ public interface CameraConfigDAO extends AbstractDAO { CameraConfig findByCameraId(int cameraId); + CameraConfig findByCameraIpAndPort(String ipAndPort); + void delete(CameraConfig cameraConfig); List list(); - - void update(CameraConfig cameraConfig); } diff --git a/cramp-core/src/main/java/org/eduze/fyp/core/db/dao/CameraConfigDAOImpl.java b/cramp-core/src/main/java/org/eduze/fyp/core/db/dao/CameraConfigDAOImpl.java index 15e615b1..26ccfd52 100644 --- a/cramp-core/src/main/java/org/eduze/fyp/core/db/dao/CameraConfigDAOImpl.java +++ b/cramp-core/src/main/java/org/eduze/fyp/core/db/dao/CameraConfigDAOImpl.java @@ -62,7 +62,7 @@ public CameraConfig findById(int id) { @Override public CameraConfig findByCameraId(int cameraId) { Session session = this.sessionFactory.getCurrentSession(); - session.beginTransaction(); + session.getTransaction(); try { List cameraConfigs = session.createQuery("from CameraConfig where cameraId=:cameraId", CameraConfig.class).setParameter("cameraId", cameraId).list(); @@ -75,6 +75,24 @@ public CameraConfig findByCameraId(int cameraId) { return null; } + @Override + public CameraConfig findByCameraIpAndPort(String ipAndPort) { + Session session = this.sessionFactory.getCurrentSession(); + session.beginTransaction(); + CameraConfig cameraConfig; + try { + cameraConfig = session.createQuery("from CameraConfig where ipAndPort=:ipAndPort", CameraConfig.class) + .setParameter("ipAndPort", ipAndPort).uniqueResult(); + session.getTransaction().commit(); + } catch (Exception e) { + session.getTransaction().rollback(); + logger.error("Error finding camera config by ip: {}", ipAndPort, e); + throw new IllegalStateException("Error executing query", e); + } + + return cameraConfig; + } + @Override public List list() { Session session = this.sessionFactory.openSession(); @@ -84,22 +102,6 @@ public List list() { return cameraConfigs; } - @Override - public void update(CameraConfig cameraConfig) { - Session session = this.sessionFactory.openSession(); - session.beginTransaction(); - try { - session.update(cameraConfig); - session.getTransaction().commit(); - } catch (HibernateException e) { - session.getTransaction().rollback(); - logger.error("Error occurred when updating camera config - {}", cameraConfig, e); - throw new IllegalStateException("Unable to update camera config", e); - } finally { - session.close(); - } - } - @Override public void delete(CameraConfig cameraConfig) { Session session = this.sessionFactory.openSession(); diff --git a/cramp-web/src/main/java/org/eduze/fyp/web/controllers/AnalyticsController.java b/cramp-web/src/main/java/org/eduze/fyp/web/controllers/AnalyticsController.java index f985480b..6ee5dd25 100644 --- a/cramp-web/src/main/java/org/eduze/fyp/web/controllers/AnalyticsController.java +++ b/cramp-web/src/main/java/org/eduze/fyp/web/controllers/AnalyticsController.java @@ -46,25 +46,11 @@ public class AnalyticsController { private static final Logger logger = LoggerFactory.getLogger(AnalyticsController.class); private AnalyticsService analyticsService; - private ReIDSearchService reIDSearchService; - private DirectionAnalyticsService directionAnalyticsService; - public DirectionAnalyticsService getDirectionAnalyticsService() { - return directionAnalyticsService; - } - - public void setDirectionAnalyticsService(DirectionAnalyticsService directionAnalyticsService) { - this.directionAnalyticsService = directionAnalyticsService; - } - - public void setReIDSearchService(ReIDSearchService reIDSearchService) { - this.reIDSearchService = reIDSearchService; - } - - public ReIDSearchService getReIDSearchService() { - return reIDSearchService; + public AnalyticsController(AnalyticsService analyticsService) { + this.analyticsService = analyticsService; } @GET @@ -396,10 +382,10 @@ public Response getMap() { } @GET - @Path("/realTimeMap") - public Response getRealTimeMap() { + @Path("/realTimeMap/{cameraGroupId}") + public Response getRealTimeMap(@PathParam("cameraGroupId") int cameraGroupId) { try { - return Response.ok(analyticsService.getRealTimeMap()).build(); + return Response.ok(analyticsService.getRealTimeMap(cameraGroupId)).build(); } catch (Exception e) { logger.error("Error occurred when obtaining real time map. {}", e); return Response.status(500).build(); @@ -471,7 +457,7 @@ public Response getTrackingSnapsWithSegments(@PathParam("id") int id, @PathParam } @GET - @Path("/realTimeMap/{id}") + @Path("/realTimePhotos/{id}") public Response getRealTimeInfo(@PathParam("id") int id) { try { return Response.ok(analyticsService.getRealtimePhotos(id)).build(); @@ -527,8 +513,19 @@ public Response getStopPoints(@PathParam("from") long from, @PathParam("to") lon } } + public DirectionAnalyticsService getDirectionAnalyticsService() { + return directionAnalyticsService; + } - public AnalyticsController(AnalyticsService analyticsService) { - this.analyticsService = analyticsService; + public void setDirectionAnalyticsService(DirectionAnalyticsService directionAnalyticsService) { + this.directionAnalyticsService = directionAnalyticsService; + } + + public void setReIDSearchService(ReIDSearchService reIDSearchService) { + this.reIDSearchService = reIDSearchService; + } + + public ReIDSearchService getReIDSearchService() { + return reIDSearchService; } } diff --git a/cramp-web/src/main/java/org/eduze/fyp/web/controllers/ConfigController.java b/cramp-web/src/main/java/org/eduze/fyp/web/controllers/ConfigController.java index 6e6a2943..4c326b54 100644 --- a/cramp-web/src/main/java/org/eduze/fyp/web/controllers/ConfigController.java +++ b/cramp-web/src/main/java/org/eduze/fyp/web/controllers/ConfigController.java @@ -22,8 +22,6 @@ import org.eduze.fyp.api.model.CameraGroup; import org.eduze.fyp.api.model.Zone; import org.eduze.fyp.api.resources.Camera; -import org.eduze.fyp.web.resources.MapConfiguration; -import org.eduze.fyp.web.resources.Status; import org.eduze.fyp.web.services.ConfigService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -128,20 +126,20 @@ public Response getMap() { @GET @Path("/{cameraId}") - public Response getMap(@PathParam("cameraId") int cameraId) { - MapConfiguration mapConfiguration; + public Response getCameraConfig(@PathParam("cameraId") int cameraId) { + CameraConfig cameraConfig; try { - mapConfiguration = configService.getMap(cameraId); + cameraConfig = configService.getCameraConfig(cameraId); } catch (Exception e) { logger.error("Error occurred when obtaining map", e); return Response.status(500).build(); } - if (mapConfiguration == null) { - return Response.status(200).entity(new Status(false)).build(); + if (cameraConfig == null) { + return Response.status(404).build(); } - return Response.status(200).entity(mapConfiguration).build(); + return Response.ok(cameraConfig).build(); } @GET @@ -183,12 +181,11 @@ public Response getCameraConfigs() { @Path("/cameraConfig") public Response postCameraConfig(CameraConfig cameraConfig) { try { - configService.addCameraConfig(cameraConfig); + return Response.ok(configService.addCameraConfig(cameraConfig)).build(); } catch (Exception e) { logger.error("Error occurred when adding camera config : {}", cameraConfig, e); return Response.status(500).build(); } - return Response.status(200).build(); } public void setConfigService(ConfigService configService) { diff --git a/cramp-web/src/main/java/org/eduze/fyp/web/services/AnalyticsService.java b/cramp-web/src/main/java/org/eduze/fyp/web/services/AnalyticsService.java index 51483497..cacd43a8 100644 --- a/cramp-web/src/main/java/org/eduze/fyp/web/services/AnalyticsService.java +++ b/cramp-web/src/main/java/org/eduze/fyp/web/services/AnalyticsService.java @@ -21,17 +21,18 @@ import org.eduze.fyp.api.ConfigurationManager; import org.eduze.fyp.api.listeners.ProcessedMapListener; +import org.eduze.fyp.api.model.CameraGroup; import org.eduze.fyp.api.model.Person; import org.eduze.fyp.api.model.Zone; import org.eduze.fyp.api.resources.PersonCoordinate; import org.eduze.fyp.api.resources.PersonSnapshot; +import org.eduze.fyp.api.util.ImageUtils; import org.eduze.fyp.core.PhotoMapper; import org.eduze.fyp.core.db.dao.CaptureStampDAO; import org.eduze.fyp.core.db.dao.PersonDAO; import org.eduze.fyp.core.db.dao.ZoneDAO; import org.eduze.fyp.web.resources.TimelineZone; import org.eduze.fyp.web.resources.ZoneStatistics; -import org.eduze.fyp.api.util.ImageUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,7 +47,7 @@ public class AnalyticsService implements ProcessedMapListener { private static final Logger logger = LoggerFactory.getLogger(AnalyticsService.class); - private List> snapshots = new ArrayList<>(); + private Map>> snapshots = new HashMap<>(); private PersonDAO personDAO; private ZoneDAO zoneDAO; @@ -61,30 +62,6 @@ public class AnalyticsService implements ProcessedMapListener { private PhotoMapper photoMapper = null; - public PhotoMapper getPhotoMapper() { - return photoMapper; - } - - public void setPhotoMapper(PhotoMapper photoMapper) { - this.photoMapper = photoMapper; - } - - public void setCaptureStampDAO(CaptureStampDAO captureStampDAO) { - this.captureStampDAO = captureStampDAO; - } - - public CaptureStampDAO getCaptureStampDAO() { - return captureStampDAO; - } - - public void setZoneDAO(ZoneDAO zoneDAO) { - this.zoneDAO = zoneDAO; - } - - public ZoneDAO getZoneDAO() { - return zoneDAO; - } - public AnalyticsService() { } @@ -327,8 +304,8 @@ public Map getMap() throws IOException { return response; } - public List> getRealTimeMap() { - return snapshots; + public List> getRealTimeMap(int cameraGroupId) { + return snapshots.computeIfAbsent(cameraGroupId, key -> new ArrayList<>()); } public int[][] getHeatMap(long fromTimestamp, long toTimestamp) { @@ -650,8 +627,8 @@ public int[][] getStopPoints(long fromTimestamp, long toTimestamp, int r, int t, @Override - public void mapProcessed(List> snapshots) { - this.snapshots = snapshots; + public void mapProcessed(CameraGroup cameraGroup, List> snapshots) { + this.snapshots.put(cameraGroup.getId(), snapshots); } @Override @@ -885,4 +862,28 @@ public List getZoneStatistics(long fromTimestamp, long toTimesta } return results; } + + public PhotoMapper getPhotoMapper() { + return photoMapper; + } + + public void setPhotoMapper(PhotoMapper photoMapper) { + this.photoMapper = photoMapper; + } + + public void setCaptureStampDAO(CaptureStampDAO captureStampDAO) { + this.captureStampDAO = captureStampDAO; + } + + public CaptureStampDAO getCaptureStampDAO() { + return captureStampDAO; + } + + public void setZoneDAO(ZoneDAO zoneDAO) { + this.zoneDAO = zoneDAO; + } + + public ZoneDAO getZoneDAO() { + return zoneDAO; + } } diff --git a/cramp-web/src/main/java/org/eduze/fyp/web/services/ConfigService.java b/cramp-web/src/main/java/org/eduze/fyp/web/services/ConfigService.java index 7a45d2c9..30de5f1c 100644 --- a/cramp-web/src/main/java/org/eduze/fyp/web/services/ConfigService.java +++ b/cramp-web/src/main/java/org/eduze/fyp/web/services/ConfigService.java @@ -29,7 +29,6 @@ import org.eduze.fyp.api.util.ImageUtils; import org.eduze.fyp.core.db.dao.CameraConfigDAO; import org.eduze.fyp.core.db.dao.ZoneDAO; -import org.eduze.fyp.web.resources.MapConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -65,24 +64,45 @@ public Camera getCameraId() { * configuration * * @param cameraConfig {@link CameraConfig} instance to be configured + * @return camera id * @throws IOException */ - public void addCameraConfig(CameraConfig cameraConfig) throws IOException { + public int addCameraConfig(CameraConfig cameraConfig) throws IOException { logger.debug("Adding camera configuration - {}", cameraConfig); // resizing camera view byte[] resized = ImageUtils.resize(cameraConfig.getView(), Constants.CAMERA_VIEW_WIDTH, Constants.CAMERA_VIEW_HEIGHT); cameraConfig.setView(resized); + cameraConfig.setId(0); cameraConfig.getPointMapping().setCameraConfig(cameraConfig); - CameraConfig existing = cameraConfigDAO.findByCameraId(cameraConfig.getCameraId()); + CameraConfig existing = cameraConfigDAO.findByCameraIpAndPort(cameraConfig.getIpAndPort()); if (existing != null) { + logger.warn("Found existing camera config {}. Deleting", existing); + + cameraConfig.setCameraId(existing.getCameraId()); + + if (cameraConfig.getPointMapping() == null + || cameraConfig.getPointMapping().getScreenSpacePoints().size() == 0 + || cameraConfig.getPointMapping().getWorldSpacePoints().size() == 0) { + cameraConfig.setPointMapping(existing.getPointMapping().clone()); + cameraConfig.getPointMapping().setCameraConfig(cameraConfig); + } + + if (cameraConfig.getCameraGroup() == null) { + cameraConfig.setCameraGroup(existing.getCameraGroup()); + } + cameraConfigDAO.delete(existing); cameraConfigDAO.save(cameraConfig); } else { + cameraConfig.setCameraId(configurationManager.getNextCameraId()); cameraConfigDAO.save(cameraConfig); + logger.debug("Added camera configuration: {}", cameraConfig); } + configurationManager.addCameraConfig(cameraConfig); + return cameraConfig.getCameraId(); } /** @@ -91,21 +111,18 @@ public void addCameraConfig(CameraConfig cameraConfig) throws IOException { * @param cameraId camera ID * @return byte array of the map image */ - public MapConfiguration getMap(int cameraId) throws IOException { + public CameraConfig getCameraConfig(int cameraId) { if (!configurationManager.isConfigured()) { return null; } - BufferedImage map = configurationManager.getMap(); - byte[] mapImageBytes = ImageUtils.bufferedImageToByteArray(map); - - MapConfiguration mapConfiguration = new MapConfiguration(); - mapConfiguration.setMapImage(mapImageBytes); - mapConfiguration.setMapping(configurationManager.getPointMapping(cameraId)); - mapConfiguration.setMapHeight(map.getHeight()); - mapConfiguration.setMapWidth(map.getWidth()); + CameraConfig cameraConfig = configurationManager.getCameraConfig(cameraId); + cameraConfig.getPointMapping().getScreenSpacePoints().forEach(p -> { + p.setX(p.getX() * cameraConfig.getWidth() / Constants.CAMERA_VIEW_WIDTH); + p.setY(p.getY() * cameraConfig.getHeight() / Constants.CAMERA_VIEW_HEIGHT); + }); - return mapConfiguration; + return cameraConfig; } public Map getMap() throws IOException { @@ -176,10 +193,6 @@ public List getZones() { return zoneDAO.list(); } - public CameraConfig getCameraConfig(int cameraId) { - return configurationManager.getCameraConfig(cameraId); - } - public void setConfigurationManager(ConfigurationManager configurationManager) { this.configurationManager = configurationManager; } @@ -191,4 +204,5 @@ public void setZoneDAO(ZoneDAO zoneDAO) { public void setCameraConfigDAO(CameraConfigDAO cameraConfigDAO) { this.cameraConfigDAO = cameraConfigDAO; } + } diff --git a/ngapp/src/app/realtime-map/realtime-map.component.html b/ngapp/src/app/realtime-map/realtime-map.component.html index fe97de4d..9b172f3b 100644 --- a/ngapp/src/app/realtime-map/realtime-map.component.html +++ b/ngapp/src/app/realtime-map/realtime-map.component.html @@ -1,19 +1,20 @@ -
+

Real-time Map

- {{personSnapshots.length}} people + {{d.personSnapshots.length}} people
-
+
- + - + - + { - console.debug("Sending real time map request"); - this.analyticsService.getRealTimeMap() - .then(ps => { - this.personSnapshots = ps; - ps.forEach((item) => { - item[0]["colour"] = this.getColour(item[0].ids[0]); - item[0]["standSitColour"] = this.getStandSitColour(item[0]); - }); - this.updateToggle = !this.updateToggle; - console.log(this.updateToggle); - console.log(this.personSnapshots); - //this.drawOnCanvas(personSnapshots); - }) - .catch(reason => console.log(reason)); + Observable.interval(5000).subscribe(x => { + console.log('Sending real time map request'); + + this.data.forEach(d => { + this.analyticsService.getRealTimeMap(d.cameraGroup.id) + .then(ps => { + ps.forEach((item) => { + item[0]['colour'] = this.getColour(item[0].ids[0]); + item[0]['standSitColour'] = this.getStandSitColour(item[0]); + }); + + d.personSnapshots = ps; + }) + .catch(reason => console.log(reason)); + }); }); } ngOnInit() { - this.configService.getMap().then((globalMap) => { - this.globalMap = globalMap; - console.log(this.globalMap); + this.configService.getCameraGroups().then(groups => { + this.data = []; + groups.forEach(group => { + this.data.push({ + cameraGroup: group, + personSnapshots: [] + }); + }); }); this.startMap(); diff --git a/ngapp/src/app/resources/camera-config.ts b/ngapp/src/app/resources/camera-config.ts index a64308ee..2c885a5e 100644 --- a/ngapp/src/app/resources/camera-config.ts +++ b/ngapp/src/app/resources/camera-config.ts @@ -9,13 +9,19 @@ export class CameraConfig { pointMapping: PointMapping; cameraView: CameraView; cameraGroup: CameraGroup; + width: number; + height: number; - constructor(cameraId: number, ipAndPort: string, pointMapping: PointMapping, cameraView: CameraView, cameraGroup: CameraGroup) { + + constructor(cameraId: number, ipAndPort: string, pointMapping: PointMapping, cameraView: CameraView, + cameraGroup: CameraGroup, width: number, height: number) { this.cameraId = cameraId; this.ipAndPort = ipAndPort; this.pointMapping = pointMapping; this.cameraView = cameraView; this.cameraGroup = cameraGroup; + this.width = width; + this.height = height; } public static fromJSON(config: any): CameraConfig { @@ -23,7 +29,8 @@ export class CameraConfig { const view = 'data:image/JPEG;base64,' + config.view; const ipPort = config.ipAndPort; const cameraGroup = CameraGroup.fromJSON(config.cameraGroup); - const cameraConfig = new CameraConfig(cameraId, ipPort, new PointMapping(), new CameraView(view), cameraGroup); + const cameraConfig = new CameraConfig(cameraId, ipPort, new PointMapping(), new CameraView(view), cameraGroup, + config.width, config.height); for (const i in config.pointMapping.screenSpacePoints) { const screenSpacePoint = new Point(config.pointMapping.screenSpacePoints[i].x, config.pointMapping.screenSpacePoints[i].y); @@ -40,7 +47,9 @@ export class CameraConfig { ipAndPort: this.ipAndPort, pointMapping: this.pointMapping, view: this.cameraView.view.split(',')[1], - cameraGroup: this.cameraGroup + cameraGroup: this.cameraGroup, + width: this.width, + height: this.height }; } } diff --git a/ngapp/src/app/services/analytics.service.ts b/ngapp/src/app/services/analytics.service.ts index e0c0ebf2..3eb13b42 100644 --- a/ngapp/src/app/services/analytics.service.ts +++ b/ngapp/src/app/services/analytics.service.ts @@ -61,8 +61,8 @@ export class AnalyticsService { .catch(AnalyticsService.handleError); } - getRealTimeMap(): Promise { - return this.http.get(this.baseUrl + 'realTimeMap') + getRealTimeMap(cameraGroupId: number): Promise { + return this.http.get(`${this.baseUrl}realTimeMap/${cameraGroupId}`) .toPromise() .then(response => { return response as PersonSnapshot[][]; @@ -346,7 +346,7 @@ export class AnalyticsService { } getRealtimeInfo(trackingId: number): Promise { - return this.http.get(this.baseUrl + 'realTimeMap/' + trackingId) + return this.http.get(this.baseUrl + 'realTimePhotos/' + trackingId) .toPromise() .then(response => { let results = response as PersonImage[]; diff --git a/ngapp/src/app/settings/point-mapping/point-mapping.component.html b/ngapp/src/app/settings/point-mapping/point-mapping.component.html index 553ae91d..e9178251 100644 --- a/ngapp/src/app/settings/point-mapping/point-mapping.component.html +++ b/ngapp/src/app/settings/point-mapping/point-mapping.component.html @@ -31,7 +31,7 @@

{{group.name}}

-

Configure point mappings for {{config.cameraId}} {{config.cameraGroup.name}}

+

Configure point mappings for {{config.cameraId}}