hu.unideb.fksz.view.TrafficCounterController.java Source code

Java tutorial

Introduction

Here is the source code for hu.unideb.fksz.view.TrafficCounterController.java

Source

package hu.unideb.fksz.view;

/*
 * #%L
 * Traffic-counter
 * %%
 * Copyright (C) 2016 FKSZSoft
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/gpl-3.0.html>.
 * #L%
 */

import hu.unideb.fksz.FileSaver;
import hu.unideb.fksz.Main;
import hu.unideb.fksz.VideoProcessor;
import hu.unideb.fksz.model.Observation;
import hu.unideb.fksz.model.ObservationDAO;
import hu.unideb.fksz.model.User;
import hu.unideb.fksz.model.UserDAO;
import hu.unideb.fksz.FileOpener;
import hu.unideb.fksz.FileNameParser;
import hu.unideb.fksz.TrafficCounterLogger;

import java.io.IOException;
import java.net.URL;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.Timer;
import java.util.TimerTask;

import org.opencv.core.Point;
import org.opencv.imgcodecs.Imgcodecs;

import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.ButtonBar.ButtonData;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.InputEvent;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;

public class TrafficCounterController implements Initializable {

    private VideoProcessor videoProcessor = new VideoProcessor();
    private boolean pauseVideo = false;
    private boolean startButtonClicked = false;
    private boolean otherFileSelected = false;
    private String currentlyPlaying;
    private String lastVideoName;
    private Point mousePosition = new Point();

    private Timer timer = new Timer();
    private List<String> items = new ArrayList<String>();
    private List<String> results = new ArrayList<String>();
    private List<Observation> userObservations;

    private List<String> videoDetails = new ArrayList<String>();
    private Map<String, String> itemsWithPath = new HashMap<String, String>();
    private User loggedUser;

    @FXML
    private ImageView imageView;
    @FXML
    private Button loadButton;
    @FXML
    private Button saveImageButton;
    @FXML
    private Button startButton;
    @FXML
    private Label currentVideoDetailsLabel;
    @FXML
    private ListView<String> listViewForFileNames;
    @FXML
    private ListView<String> listViewForResults;
    @FXML
    private ListView<String> listViewForVideoDetails;
    @FXML
    private AnchorPane root;
    @FXML
    private ProgressBar progressBar;
    @FXML
    private Button observationsButton;
    @FXML
    private Button logInButton;
    @FXML
    private Button increaseTrafficCountButton;
    @FXML
    private Button commitResultsButton;
    @FXML
    private Button logOutButton;
    @FXML
    private Label trafficCounterWindowTrafficCountLabel;
    private int trafficCount;
    private boolean firstLogin = false;
    private boolean firstAdminAccess = false;
    private boolean firstMonitorAccess = false;

    private Scene logInScene;
    private Scene trafficCounterScene;
    private Scene adminAccessScene;
    private Scene monitorAccessScene;
    private Scene logOutScene;
    private Stage trafficCounterStage;

    public Scene getLogOutScene() {
        return logOutScene;
    }

    public void setLogOutScene(Scene logOutScene) {
        this.logOutScene = logOutScene;
    }

    public Scene getTrafficCounterScene() {
        return trafficCounterScene;
    }

    public void setTrafficCounterScene(Scene trafficCounterScene) {
        this.trafficCounterScene = trafficCounterScene;
    }

    public Scene getLogInScene() {
        return logInScene;
    }

    public void setLogInScene(Scene logInScene) {
        this.logInScene = logInScene;
    }

    public Scene getAdminAccessScene() {
        return adminAccessScene;
    }

    public void setAdminAccessScene(Scene adminAccessScene) {
        this.adminAccessScene = adminAccessScene;
    }

    public Scene getMonitorAccessScene() {
        return monitorAccessScene;
    }

    public void setMonitorAccessScene(Scene monitorAccessScene) {
        this.monitorAccessScene = monitorAccessScene;
    }

    public Stage getTrafficCounterStage() {
        return trafficCounterStage;
    }

    public void setTrafficCounterStage(Stage trafficCounterStage) {
        this.trafficCounterStage = trafficCounterStage;
    }

    @FXML
    private void commitResultsButtonClicked() {
        if (!userObservations.isEmpty()) {
            Alert commitConfirmation = new Alert(AlertType.CONFIRMATION);
            commitConfirmation.setTitle("Commit confirmation");
            commitConfirmation.setHeaderText("Commit your results");
            commitConfirmation.setContentText("Are you sure you want to commit your results?");

            ButtonType noButton = new ButtonType("No", ButtonData.NO);
            ButtonType yesButton = new ButtonType("Yes", ButtonData.YES);

            commitConfirmation.getButtonTypes().setAll(yesButton, noButton);
            Optional<ButtonType> dialogResult = commitConfirmation.showAndWait();

            if (dialogResult.get() == yesButton) {
                commitResults();
                TrafficCounterLogger.infoMessage("Committing results of " + getLoggedUser().getName());
            } else if (dialogResult.get() == noButton) {
                TrafficCounterLogger.infoMessage("Committing canceled");
            }
        } else {
            Alert nothingToCommitInfo = new Alert(AlertType.INFORMATION);
            nothingToCommitInfo.setTitle("Nothing to commit");
            nothingToCommitInfo.setHeaderText("You have no results to commit!");
            nothingToCommitInfo.setContentText("Try again when you'll have new observations!");
            nothingToCommitInfo.showAndWait();
        }
    }

    @FXML
    private void logOutButtonClicked() {

        Alert logOutConfirmation = new Alert(AlertType.CONFIRMATION);
        logOutConfirmation.setTitle("Log out confirmation");
        logOutConfirmation.setHeaderText("Any uncommitted result will be lost if you log out");
        logOutConfirmation.setContentText("Choose whether you want to log out or commit and log out");

        ButtonType cancelButton = new ButtonType("Cancel", ButtonData.CANCEL_CLOSE);
        ButtonType logOutButton = new ButtonType("Log out");
        ButtonType commitAndLogOutButton = new ButtonType("Commit");
        if (!userObservations.isEmpty()) {
            logOutConfirmation.getButtonTypes().setAll(logOutButton, commitAndLogOutButton, cancelButton);
        } else {
            logOutConfirmation.getButtonTypes().setAll(logOutButton, cancelButton);
        }
        Optional<ButtonType> dialogResult = logOutConfirmation.showAndWait();

        if (dialogResult.get() == logOutButton) {
            onlogOutConfirmationDialogLogOutResult();
            TrafficCounterLogger.infoMessage("Logging out..");
        } else if (dialogResult.get() == commitAndLogOutButton) {
            commitResults();
            onlogOutConfirmationDialogLogOutResult();
            TrafficCounterLogger.infoMessage("Committing and logging out..");
        }
    }

    public AnchorPane getRoot() {
        return root;
    }

    public void setRoot(AnchorPane root) {
        this.root = root;
    }

    public void onlogOutConfirmationDialogLogOutResult() {
        setLoggedUser(new User());
        results = new ArrayList<String>();
        listViewForResults.setItems(FXCollections.observableList(results));
        setTitle("Traffic counter - No user");
        hideControls();
    }

    public void resetResults() {
        results = new ArrayList<String>();
        listViewForResults.setItems(FXCollections.observableList(results));
    }

    public void commitResults() {
        for (Observation o : userObservations) {
            ObservationDAO.insertObservation(o);
        }
        userObservations = new ArrayList<Observation>();
        resetResults();
    }

    public void resetTitle() {
        if (loggedUser == null) {
            setTitle("Traffic counter - No user");
        } else if (loggedUser.getName() == null) {
            setTitle("Traffic counter - No user");
        } else
            setTitle("Traffic counter - " + loggedUser.getName() + "-" + loggedUser.getRole());
    }

    @FXML
    private void increaseTrafficCountButtonOnAction() {
        setTrafficCount(getTrafficCount() + 1);
        trafficCounterWindowTrafficCountLabel.setText("Traffic count: " + Integer.toString(trafficCount));
    }

    public void hideControls() {
        observationsButton.setVisible(false);
        increaseTrafficCountButton.setVisible(false);
        trafficCounterWindowTrafficCountLabel.setVisible(false);
        logOutButton.setVisible(false);
        commitResultsButton.setVisible(false);
        setTrafficCount(0);
    }

    public void showControls() {
        observationsButton.setVisible(true);
        increaseTrafficCountButton.setVisible(true);
        trafficCounterWindowTrafficCountLabel.setVisible(true);
        logOutButton.setVisible(true);
        commitResultsButton.setVisible(true);
        setTrafficCount(0);
    }

    public User getLoggedUser() {
        return loggedUser;
    }

    public void setLoggedUser(User loggedUser) {
        this.loggedUser = loggedUser;
        setTitle("Traffic counter - " + loggedUser.getName() + "-" + loggedUser.getRole());
        showControls();
        userObservations = new ArrayList<Observation>();
    }

    @FXML
    private void trafficCounterWindowAnchorPaneOnKeyPressed(KeyEvent event) {
        if (event.getCode() == KeyCode.PLUS) {
            trafficCount++;
            trafficCounterWindowTrafficCountLabel.setText("Traffic count: " + Integer.toString(trafficCount));
        }
    }

    @FXML
    private void logInButtonClicked() {
        if (!firstLogin) {
            try {
                firstLogin = true;
                Stage stage;
                Parent root;
                stage = (Stage) logInButton.getScene().getWindow();
                FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/LogInWindow.fxml"));
                root = loader.load();
                Scene scene = new Scene(root);
                stage.setTitle("Log in");
                stage.setScene(scene);
                loader.<LogInController>getController().setTrafficCounterController(this);
                setLogInScene(scene);
                stage.show();
            } catch (IOException e) {
                TrafficCounterLogger.errorMessage(e.toString());
            }
        } else {
            getTrafficCounterStage().setScene(getLogInScene());
            getTrafficCounterStage().setTitle("Log in");
            getTrafficCounterStage().show();
        }
        System.gc();
    }

    @FXML
    private void observationsButtonClicked() {
        if (loggedUser.getName() != null) {
            try {
                Stage stage;
                Parent root;
                Scene scene = null;
                FXMLLoader loader;
                stage = (Stage) logInButton.getScene().getWindow();
                if (getLoggedUser().getRole().equals(UserDAO.ADMIN_ROLE)) {
                    if (!firstAdminAccess) {
                        firstAdminAccess = true;
                        loader = new FXMLLoader(getClass().getResource("/fxml/AdminAccessWindow.fxml"));
                        root = loader.load();
                        scene = new Scene(root);
                        loader.<AdminAccessController>getController().populateUserList();
                        loader.<AdminAccessController>getController().setTrafficCounterController(this);
                        setAdminAccessScene(scene);
                        stage.setScene(scene);
                        stage.show();
                    } else {
                        getTrafficCounterStage().setScene(getAdminAccessScene());
                        getTrafficCounterStage().show();
                    }
                } else if (getLoggedUser().getRole().equals(UserDAO.MONITOR_ROLE)) {
                    if (!firstMonitorAccess) {
                        firstAdminAccess = true;
                        loader = new FXMLLoader(getClass().getResource("/fxml/MonitorAccessWindow.fxml"));
                        root = loader.load();
                        scene = new Scene(root);
                        setTitle("Observations of " + loggedUser.getName());
                        loader.<MonitorAccessController>getController().populateObservationsTable(getLoggedUser());
                        loader.<MonitorAccessController>getController().setTrafficCounterController(this);
                        setMonitorAccessScene(scene);
                        stage.setScene(scene);
                        stage.show();
                    } else {
                        getTrafficCounterStage().setScene(getMonitorAccessScene());

                    }
                }
            } catch (IOException e) {
                TrafficCounterLogger.errorMessage(e.toString());
            }
        }
    }

    private void setTitle(String title) {
        getTrafficCounterStage().setTitle(title);
    }

    /**
     * Gets called when {@code listViewForFileNames} is clicked.
     */
    @FXML
    private void listViewForFileNamesClicked() {
        if (!listViewForFileNames.getSelectionModel().getSelectedItem().equals(currentlyPlaying)) {
            startButton.setText("Start");
            otherFileSelected = true;
        } else {
            if (!pauseVideo) {
                startButton.setText("Pause");
            }
            otherFileSelected = false;
        }
    }

    /**
     * Tries to load a video, returns whether the {@code VideoProcessor}
     * successfully loaded the video or not.
     *
     * @param filename
     *            the absolute path of the video to be loaded.
     * @return whether the {@code VideoProcessor} successfully loaded the video
     *         or not.
     */
    public int loadVideo(String filename) {
        if (filename != null) {
            if (videoProcessor.initVideo(filename) == 0) {
                this.imageView.setImage(videoProcessor.getImageAtPos(1));

                TrafficCounterLogger.infoMessage("Video " + filename + " loaded!");
                if (this.saveImageButton.disableProperty().get() == true) {
                    this.saveImageButton.disableProperty().set(false);
                    this.startButton.disableProperty().set(false);
                }

                if (!videoDetails.isEmpty()) {
                    videoDetails.clear();
                }
                videoDetails.add("Video name: " + FileNameParser.getFileName(filename));
                videoDetails.add("City: " + FileNameParser.getCity(filename));
                videoDetails.add("Street: " + FileNameParser.getStreet(filename));
                videoDetails.add("Length: " + videoProcessor.getLengthFormatted());
                videoDetails.add("FPS: " + (int) videoProcessor.getFPS());
                videoDetails.add("Frame count: " + videoProcessor.getFrameCount());
                videoDetails.add("Extension: " + FileNameParser.getExtension(filename));

                listViewForVideoDetails.setItems(FXCollections.observableList(videoDetails));

                currentlyPlaying = FileNameParser.getCity(filename) + " - " + FileNameParser.getStreet(filename);
                if (!otherFileSelected)
                    videoProcessor.setDetectedCarsCount(0);
                addListViewItem(filename);
                setTrafficCount(0);
                return 0;
            }
        } else if (filename == null) {
            TrafficCounterLogger.warnMessage("No file was selected!");
            return 1;
        }
        return 0;
    }

    public int getTrafficCount() {
        return trafficCount;
    }

    public void setTrafficCount(int trafficCount) {
        this.trafficCount = trafficCount;
    }

    /**
     * Gets called when the {@code loadButton} is clicked. Pauses the video
     * while the file is being selected, if it was playing. Calls
     * {@code loadVideo()} to load the selected file.
     */
    @FXML
    private void loadButtonClicked() {
        if (!pauseVideo) {
            pauseVideo = true;
            this.startButton.setText("Start");
        }
        String filename = new FileOpener().getFileName((Stage) this.loadButton.getScene().getWindow());
        loadVideo(filename);
    }

    /**
     * Gets called when the {@code saveImageButton} is clicked.
     */
    @FXML
    private void saveImageClicked() {
        TrafficCounterLogger.infoMessage("Saving image..");
        if (!pauseVideo) {
            pauseVideo = true;
            startButton.setText("Start");
        }

        String filename = new FileSaver().getFileName((Stage) this.saveImageButton.getScene().getWindow());
        if (filename != null) {
            try {
                Imgcodecs.imwrite(filename, videoProcessor.getFrame());
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            pauseVideo = false;
            startButton.setText("Pause");
        } else {
            TrafficCounterLogger.errorMessage("File name was not specified, file was not saved!");
            pauseVideo = false;
        }
    }

    /**
     * Gets called when the {@code startButton} is clicked. Starts or pauses/
     * unpauses the video, or loads a new video if other than the currently
     * playing video is selected from the {@code listViewForFileNames}
     */
    @FXML
    private void startButtonClicked() {
        if (otherFileSelected) {
            pauseVideo = true;

            if (loadVideo(itemsWithPath
                    .get(listViewForFileNames.getSelectionModel().selectedItemProperty().get())) == 0) {
                // pauseVideo = true;
                otherFileSelected = false;
                startButton.setText("Pause");
                timer.cancel();
                if (loggedUser != null) {
                    if (loggedUser.getName() != null) {
                        Observation currentObservation = new Observation();
                        currentObservation.setMonitor_id(getLoggedUser().getId());
                        currentObservation.setObservationDate(Timestamp.valueOf(LocalDateTime.now()));
                        currentObservation.setTrafficCount(getTrafficCount());
                        currentObservation.setObservedVideoTitle(lastVideoName);
                        currentObservation.setComputerTrafficCount(videoProcessor.getDetectedCarsCount());
                        userObservations.add(currentObservation);
                        System.gc();
                    }
                }

                results.add(lastVideoName + ": " + videoProcessor.getDetectedCarsCount() + " cars detected, "
                        + videoProcessor.getCarsPerMinute() + " cars per minute.");
                listViewForResults.setItems(FXCollections.observableList(results));

                videoProcessor.setDetectedCarsCount(0);
                setTrafficCount(0);
                trafficCounterWindowTrafficCountLabel.setText("Traffic count: 0");
                startProcessing();
            } else {
                TrafficCounterLogger.errorMessage("Failed to load "
                        + itemsWithPath.get(listViewForFileNames.getSelectionModel().selectedItemProperty().get()));
            }
        }

        if (!startButtonClicked && pauseVideo) {
            startButtonClicked = true;
            pauseVideo = false;
            this.startButton.setText("Pause");
            startProcessing();
        } else if (!pauseVideo) {
            pauseVideo = true;
            this.startButton.setText("Start");
        } else if (pauseVideo) {
            pauseVideo = false;
            this.startButton.setText("Pause");
        }
    }

    /**
     * Gets called when {@code imageView} is clicked. Pauses or unpauses the
     * video.
     */
    @FXML
    private void imageViewClicked() {
        /*
         * if (startButtonClicked) { if (!mouseDragged)
         *
         * if (!pauseVideo) { pauseVideo = true;
         * this.startButton.setText("Start"); } else if (pauseVideo) {
         * this.startButton.setText("Pause"); pauseVideo = false; } }
         */
    }

    /**
     * Initializes some elements of the user interface.
     */
    private void init() {
        try {
            this.imageView
                    .setImage(new Image(Main.class.getClass().getResource("/image/load_video.jpg").toString()));
            TrafficCounterLogger.traceMessage("Initial picture loaded successfully!");
        } catch (Exception e) {
            TrafficCounterLogger.errorMessage("Initial picture failed to load!");
        }

        this.listViewForFileNames.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);

        this.saveImageButton.disableProperty().set(true);
        this.startButton.disableProperty().set(true);

        EventHandler<InputEvent> eventHandler = event -> {

            if (event.getEventType().equals(KeyEvent.KEY_PRESSED)) {
                KeyEvent keyevent = (KeyEvent) event.clone();
                if (keyevent.getCode().equals(KeyCode.ESCAPE)) {
                    timer.cancel();
                    videoProcessor.getVideoCap().release();
                    ((Node) (event.getSource())).getScene().getWindow().hide();
                    System.exit(0);
                }
            }
        };

        root.setOnKeyPressed(eventHandler);

        imageView.setOnMousePressed(event -> {

            mousePosition.x = event.getX();
            mousePosition.y = event.getY();

            if (mousePosition.inside(videoProcessor.getImageArea())) {
                videoProcessor.setPreviousControlPointsHeight((int) videoProcessor.getHeightOfAControlPoint());
            }

        });

        imageView.setOnMouseDragged(event -> {

            if (mousePosition.inside(videoProcessor.getImageArea())) {
                Point relativeMousePosition = new Point(mousePosition.x - event.getX(),
                        mousePosition.y - event.getY());
                videoProcessor.setHeightOfTheControlPoints(
                        videoProcessor.getPreviousControlPointsHeight() - relativeMousePosition.y);
            }

            if (videoProcessor.getHeightOfAControlPoint() < 100) {
                videoProcessor.setHeightOfTheControlPoints(100);
            }

            if (videoProcessor.getHeightOfAControlPoint() > videoProcessor.getImageArea().height - 100) {
                videoProcessor.setHeightOfTheControlPoints(videoProcessor.getImageArea().height - 100);
            }

        });
        setTrafficCount(0);
        TrafficCounterLogger.infoMessage("TrafficCounterController initialized!");
        hideControls();
    }

    /**
     * Adds an item to the {@code listViewForFileNames} {@code ListView} and to
     * {@code items} and {@code itemsWithPath} if it's not containing it.
     *
     * @param filename
     *            path to be added to the {@code itemsWithPath}.
     */
    private void addListViewItem(String filename) {
        if (items != null) {
            if (!items.contains(currentlyPlaying) && filename != null) {
                items.add(currentlyPlaying);
                itemsWithPath.put(currentlyPlaying, filename);
            }
            listViewForFileNames.selectionModelProperty().get().select(currentlyPlaying);
            ;
            listViewForFileNames.setItems(FXCollections.observableList(items));
        }
    }

    /**
     * Main loop, separately in a {@code Timer} thread.
     */
    private void startProcessing() {
        TimerTask frame_grabber = new TimerTask() {
            Image tmp;

            @Override
            public void run() {
                if (!pauseVideo) {
                    videoProcessor.processVideo();
                    videoProcessor.writeOnFrame("Detected cars count: " + videoProcessor.getDetectedCarsCount());

                    tmp = videoProcessor.convertCvMatToImage();
                    progressBar.setProgress(videoProcessor.getFramePos() / videoProcessor.getFrameCount());

                    if (videoProcessor.isFinished()) {
                        results.add(currentlyPlaying + ": " + videoProcessor.getDetectedCarsCount()
                                + " cars detected, " + videoProcessor.getCarsPerMinute() + " cars per minute.");
                        listViewForResults.setItems(FXCollections.observableList(results));
                        if (loggedUser != null) {
                            if (loggedUser.getName() != null) {
                                Observation currentObservation = new Observation();
                                currentObservation.setMonitor_id(getLoggedUser().getId());
                                currentObservation.setObservationDate(Timestamp.valueOf(LocalDateTime.now()));
                                currentObservation.setTrafficCount(getTrafficCount());
                                currentObservation.setObservedVideoTitle(lastVideoName);
                                currentObservation.setComputerTrafficCount(videoProcessor.getDetectedCarsCount());
                                userObservations.add(currentObservation);
                            }
                        }
                        setTrafficCount(0);
                        videoProcessor.setDetectedCarsCount(0);
                        videoProcessor.setFinished(false);

                        pauseVideo = true;
                    } else {

                    }
                    Platform.runLater(() -> imageView.setImage(tmp));
                }
            }
        };
        timer = new Timer();

        Double period = 1000 / videoProcessor.getFPS() * 2;
        this.timer.schedule(frame_grabber, 0, period.longValue());

        lastVideoName = currentlyPlaying;
    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        init();
    }
}