From fb52b59ad9eb08e34f1f5e639cca91b519f53511 Mon Sep 17 00:00:00 2001 From: Kendrick Chan Date: Fri, 24 Jan 2020 03:17:42 +0800 Subject: [PATCH 01/30] Refactor mqtt sender script #299 --- utils/mqtt_sender/mqtt_sender.py | 43 ++++++++------------------------ 1 file changed, 11 insertions(+), 32 deletions(-) diff --git a/utils/mqtt_sender/mqtt_sender.py b/utils/mqtt_sender/mqtt_sender.py index 54349755..a2d0ea8c 100644 --- a/utils/mqtt_sender/mqtt_sender.py +++ b/utils/mqtt_sender/mqtt_sender.py @@ -1,7 +1,6 @@ import paho.mqtt.client as mqtt +import yaml import ssl -from threading import Thread -from daemonize import Daemonize import redis import time import signal @@ -10,24 +9,18 @@ import os import logging - -def sigint_handler(signal, frame): - #logging.warning('Interrupted') - sys.exit(0) - os._exit(0) - +# TODO: yaml configuration file for redis and mqtt settings def on_connect(client, userdata, flags, rc): - #logging.info("Connected with result code "+str(rc)) - client.subscribe("$SYS/#") - + logging.info("Connected with result code "+str(rc)) + #client.subscribe("$SYS/#") def on_publish(client, userdata, mid): pass -def getRedis(i, client, logger): +def redis_listen(client, logger): logger.info("Listening in redis events") r = redis.StrictRedis(host='localhost', port=6379, db=0) while 1: @@ -40,9 +33,6 @@ def getRedis(i, client, logger): -def sigint_handler(signal, frame): - sys.exit(0) - def get_logger(): logger = logging.getLogger("mqtt_logger") logger.setLevel(logging.INFO) @@ -64,25 +54,14 @@ def main(): client.on_connect = on_connect client.on_publish = on_publish - client.tls_set( - "/etc/letsencrypt/live/resqaws.jankstudio.com/fullchain.pem", cert_reqs=ssl.CERT_NONE, - tls_version=ssl.PROTOCOL_TLSv1) + # configure mqtt broker to accept localhost + client.connect("localhost", 1883, 60) - client.connect("resqaws.jankstudio.com", 8883, 60) + client.loop_start() + redis_listen(client, logger) + client.loop_end() - logger.info("Starting redis thread") - t = Thread(target=getRedis, args=(1, client, logger)) - - t.start() - - signal.signal(signal.SIGINT, sigint_handler) - client.loop_forever() + #client.loop_forever() -#logging.basicConfig(filename='/tmp/mqtt_sender.log', level=logging.INFO) -#logging.info('Started mqtt_sender') - -#pid = "/tmp/mqtt_sender.pid" -#daemon = Daemonize(app="mqtt_sender", pid=pid, action=main) -#daemon.start() main() From fe7cfe6c86e654f62a551763f3c7878a2babbafc Mon Sep 17 00:00:00 2001 From: Kendrick Chan Date: Fri, 24 Jan 2020 03:19:51 +0800 Subject: [PATCH 02/30] Refactor RedisClientProvider #299 --- src/Service/RedisClientProvider.php | 43 +++++++++++++++++------------ 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/src/Service/RedisClientProvider.php b/src/Service/RedisClientProvider.php index 04ac1f80..2572370e 100644 --- a/src/Service/RedisClientProvider.php +++ b/src/Service/RedisClientProvider.php @@ -11,34 +11,41 @@ class RedisClientProvider protected $host; protected $port; protected $password; - protected $env_flag; - public function __construct($scheme, $host, $port, $password, $env_flag) + public function __construct($scheme, $host, $port, $password) { $this->scheme = $scheme; $this->host = $host; $this->port = $port; $this->password = $password; - $this->env_flag = $env_flag; + + $this->connect(); + } + + protected function connect() + { + // if password is specified attempt connection + if (strlen($this->password) > 0) + { + $this->redis = new PredisClient([ + "scheme" => $this->scheme, + "host" => $this->host, + "port" => $this->port, + "password" => $this->password]); + + return $this->redis; + } + + $this->redis = new PredisClient([ + "scheme" => $this->scheme, + "host" => $this->host, + "port" => $this->port]); + + return $this->redis; } public function getRedisClient() { - if ($this->env_flag == 'dev') - { - $this->redis = new PredisClient([ - "scheme"=>$this->scheme, - "host"=>$this->host, - "port"=>$this->port]); - } - else - { - $this->redis = new PredisClient([ - "scheme"=>$this->scheme, - "host"=>$this->host, - "port"=>$this->port, - "password"=>$this->password]); - } return $this->redis; } } From ce0d0fb29cc4c5d2c722fdc4bfd50e18f32ba3da Mon Sep 17 00:00:00 2001 From: Kendrick Chan Date: Fri, 24 Jan 2020 03:21:14 +0800 Subject: [PATCH 03/30] Add initial job order active cache that listens to JobOrder entity events #299 --- config/services.yaml | 16 ++++- src/EventListener/JobOrderActiveCache.php | 77 +++++++++++++++++++++++ translations/messages.en.yaml | 2 +- 3 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 src/EventListener/JobOrderActiveCache.php diff --git a/config/services.yaml b/config/services.yaml index 4249102e..e7cec9ea 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -86,7 +86,6 @@ services: $host: "%env(REDIS_CLIENT_HOST)%" $port: "%env(REDIS_CLIENT_PORT)%" $password: "%env(REDIS_CLIENT_PASSWORD)%" - $env_flag: "dev" App\Service\GeofenceTracker: arguments: @@ -204,3 +203,18 @@ services: App\Service\GISManagerInterface: "@App\\Service\\GISManager\\OpenStreet" #App\Service\GISManagerInterface: "@App\\Service\\GISManager\\Google" + App\EventListener\JobOrderActiveCache: + arguments: + $redis: "@App\\Service\\RedisClientProvider" + $key: "%env(JO_ACTIVE_CACHE_KEY)%" + $mqtt: "@App\\Service\\MQTTClient" + tags: + - name: 'doctrine.orm.entity_listener' + event: 'postUpdate' + entity: 'App\Entity\JobOrder' + - name: 'doctrine.orm.entity_listener' + event: 'postRemove' + entity: 'App\Entity\JobOrder' + - name: 'doctrine.orm.entity_listener' + event: 'postPersist' + entity: 'App\Entity\JobOrder' diff --git a/src/EventListener/JobOrderActiveCache.php b/src/EventListener/JobOrderActiveCache.php new file mode 100644 index 00000000..fb82b22c --- /dev/null +++ b/src/EventListener/JobOrderActiveCache.php @@ -0,0 +1,77 @@ +redis = $redis->getRedisClient(); + $this->key = $key; + $this->mqtt = $mqtt; + } + + // when a new job order comes in + public function postPersist(JobOrder $jo, LifecycleEventArgs $args) + { + $status = $jo->getStatus(); + + switch ($status) + { + // active + case JOStatus::PENDING: + case JOStatus::RIDER_ASSIGN: + case JOStatus::ASSIGNED: + case JOStatus::IN_TRANSIT: + case JOStatus::IN_PROGRESS: + $this->processActiveJO($jo); + break; + // inactive + case JOStatus::CANCELLED: + case JOStatus::FULFILLED: + $this->processInactiveJO($jo); + break; + } + } + + // when a job order is updated + public function postUpdate(JobOrder $jo, LifecycleEventArgs $args) + { + } + + // when a job order is deleted + public function postRemove(JobOrder $jo, LifecycleEventArgs $args) + { + } + + protected function processActiveJO($jo) + { + $coords = $jo->getCoordinates(); + + // put in redis cache + error_log('add ' . $this->key . ' - (' . $coords->getLongitude() . ', ' . $coords->getLatitude() . ') - ' . $jo->getID()); + $this->redis->geoadd($this->key, $coords->getLongitude(), $coords->getLatitude(), $jo->getID()); + + // TODO: publish to mqtt + } + + protected function processInactiveJO($jo) + { + // TODO: remove from redis cache + + // TODO: publich to mqtt + } +} + diff --git a/translations/messages.en.yaml b/translations/messages.en.yaml index 04ebdc42..992f8de9 100644 --- a/translations/messages.en.yaml +++ b/translations/messages.en.yaml @@ -26,4 +26,4 @@ default_lat: 14.6091 default_long: 121.0223 #default_lat: 3.084216 #default_long: 101.6129996 -default_region: my +default_region: ph From 66be351bd35c120921acc6bc862bc3d46b7d7053 Mon Sep 17 00:00:00 2001 From: Kendrick Chan Date: Fri, 24 Jan 2020 20:46:25 +0800 Subject: [PATCH 04/30] Make MQTT key configurable #299 --- config/services.yaml | 1 + src/Service/MQTTClient.php | 7 ++++--- utils/mqtt_sender/mqtt_sender.py | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/config/services.yaml b/config/services.yaml index e7cec9ea..69b75878 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -75,6 +75,7 @@ services: App\Service\MQTTClient: arguments: $redis_client: "@App\\Service\\RedisClientProvider" + $key: "mqtt_events" App\Service\APNSClient: arguments: diff --git a/src/Service/MQTTClient.php b/src/Service/MQTTClient.php index aa0932a3..c9c7a4f2 100644 --- a/src/Service/MQTTClient.php +++ b/src/Service/MQTTClient.php @@ -9,14 +9,15 @@ class MQTTClient { const PREFIX = 'motolite.control.'; const RIDER_PREFIX = 'motorider_'; - const REDIS_KEY = 'events'; // protected $mclient; protected $redis; + protected $key; - public function __construct(RedisClientProvider $redis_client) + public function __construct(RedisClientProvider $redis_client, $key) { $this->redis = $redis_client->getRedisClient(); + $this->key = $key; } public function __destruct() @@ -29,7 +30,7 @@ class MQTTClient // $this->mclient->publish($channel, $message); $data = $channel . '|' . $message; - $this->redis->lpush(self::REDIS_KEY, $data); + $this->redis->lpush($this->key, $data); } public function sendEvent(JobOrder $job_order, $payload) diff --git a/utils/mqtt_sender/mqtt_sender.py b/utils/mqtt_sender/mqtt_sender.py index a2d0ea8c..f610b3db 100644 --- a/utils/mqtt_sender/mqtt_sender.py +++ b/utils/mqtt_sender/mqtt_sender.py @@ -25,7 +25,7 @@ def redis_listen(client, logger): r = redis.StrictRedis(host='localhost', port=6379, db=0) while 1: time.sleep(0) - data = r.brpop("events", 10) + data = r.brpop("mqtt_events", 10) if data: info = data[1].split('|') logger.info("Channel: " + info[0] + " message: " + info[1]) From c753be54cb9a1f6a4259ccbb519307678370c807 Mon Sep 17 00:00:00 2001 From: Kendrick Chan Date: Sat, 25 Jan 2020 01:57:15 +0800 Subject: [PATCH 05/30] Refactor dashboard map js to use classes #299 --- composer.json | 1 + composer.lock | 60 +++++++++++- public/assets/js/dashboard_map.js | 114 ++++++++++++++++++++++ public/assets/js/map_mqtt.js | 81 ++++++++++++++++ symfony.lock | 3 + templates/home.html.twig | 151 +++++++++++------------------ templates/map/initOpenStreetMap.js | 139 -------------------------- 7 files changed, 315 insertions(+), 234 deletions(-) create mode 100644 public/assets/js/dashboard_map.js create mode 100644 public/assets/js/map_mqtt.js diff --git a/composer.json b/composer.json index e118abfc..7219d070 100644 --- a/composer.json +++ b/composer.json @@ -17,6 +17,7 @@ "predis/predis": "^1.1", "sensio/framework-extra-bundle": "^5.1", "setasign/fpdf": "^1.8", + "symfony/asset": "^4.0", "symfony/console": "^4.0", "symfony/debug": "^4.0", "symfony/filesystem": "^4.0", diff --git a/composer.lock b/composer.lock index b3630199..a1172379 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "4873ae3fd18db755bc9bf395bbbfb141", + "content-hash": "b101ecfbc1f6f2270f0e8ad326035b7e", "packages": [ { "name": "catalyst/auth-bundle", @@ -2411,6 +2411,62 @@ ], "time": "2016-01-01T17:47:15+00:00" }, + { + "name": "symfony/asset", + "version": "v4.4.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/asset.git", + "reference": "2c67c89d064bfb689ea6bc41217c87100bb94c17" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/asset/zipball/2c67c89d064bfb689ea6bc41217c87100bb94c17", + "reference": "2c67c89d064bfb689ea6bc41217c87100bb94c17", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "require-dev": { + "symfony/http-foundation": "^3.4|^4.0|^5.0", + "symfony/http-kernel": "^3.4|^4.0|^5.0" + }, + "suggest": { + "symfony/http-foundation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Asset\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Asset Component", + "homepage": "https://symfony.com", + "time": "2020-01-04T13:00:46+00:00" + }, { "name": "symfony/cache", "version": "v4.3.1", @@ -5344,6 +5400,7 @@ "code", "zf2" ], + "abandoned": "laminas/laminas-code", "time": "2018-08-13T20:36:59+00:00" }, { @@ -5398,6 +5455,7 @@ "events", "zf2" ], + "abandoned": "laminas/laminas-eventmanager", "time": "2018-04-25T15:33:34+00:00" } ], diff --git a/public/assets/js/dashboard_map.js b/public/assets/js/dashboard_map.js new file mode 100644 index 00000000..f50c7520 --- /dev/null +++ b/public/assets/js/dashboard_map.js @@ -0,0 +1,114 @@ +class DashboardMap { + constructor(options, rider_markers, cust_markers) { + this.options = options; + this.rider_markers = rider_markers; + this.cust_markers = cust_markers; + + // layer groups + this.lg_avail_rider = L.layerGroup(); + this.lg_jo_rider = L.layerGroup(); + this.lg_cust = L.layerGroup(); + } + + initialize() { + // main map + this.map = L.map(this.options.div_id).setView( + [this.options.center_lat, this.options.center_lng], + this.options.zoom + ); + + // add tile layer + var streets = L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}', { + attribution: 'Map data © OpenStreetMap contributors, CC-BY-SA, Imagery © Mapbox', + maxZoom: 18, + id: 'mapbox/streets-v11', + accessToken: this.options.access_token + }).addTo(this.map); + + // layer groups + this.lg_avail_rider.addTo(this.map); + this.lg_jo_rider.addTo(this.map); + this.lg_cust.addTo(this.map); + + // base layer + var baseMaps = { + 'Streets': streets + }; + + // overlay layer + var overlayMaps = { + 'Available Riders' : this.lg_avail_rider, + 'JO Riders' : this.lg_jo_rider, + 'Customers' : this.lg_cust + } + + L.control.layers(baseMaps, overlayMaps).addTo(this.map); + + return this.map; + } + + loadLocations(location_url) { + console.log(this.rider_markers); + var my = this; + $.ajax({ + url: location_url, + }).done(function(response) { + // clear all markers + my.lg_avail_rider.clearLayers(); + my.lg_jo_rider.clearLayers(); + my.lg_cust.clearLayers(); + // get riders and mark + var riders = response.riders; + + $.each(riders, function(rider_id, rider_data) { + // rider location + var point = rider_data['loc']; + var lat = point[0]; + var long = point[1]; + + // customer location + var cloc = rider_data['cust_loc']; + var clat = cloc[0]; + var clong = cloc[1]; + + // create rider markers + if (rider_data['has_jo']) { + var jo_data = rider_data['jo']; + + my.rider_markers[rider_id] = L.marker([lat, long], { icon: my.options.icons.rider_active_jo }).bindPopup('Loading...'); + my.cust_markers[jo_data['id']] = L.marker([clat, clong], { icon: my.options.icons.customer }).bindPopup('Loading...'); + + my.lg_cust.addLayer(my.cust_markers[jo_data['id']]); + my.lg_jo_rider.addLayer(my.rider_markers[rider_id]); + + // customer popup ajax + my.cust_markers[jo_data['id']].on('click', function(e) { + var popup = e.target.getPopup(); + var url = my.options.cust_popup_url.replace('[id]', jo_data['id']); + console.log(url); + $.get(url).done(function(data) { + popup.setContent(data); + popup.update(); + }); + }); + } else { + my.rider_markers[rider_id]= L.marker([lat, long], { icon: my.options.icons.rider_available }).bindPopup('Loading...'); + my.lg_avail_rider.addLayer(my.rider_markers[rider_id]); + } + + // ajax loading of rider popup + my.rider_markers[rider_id].on('click', function(e) { + var popup = e.target.getPopup(); + var url = my.options.rider_popup_url.replace('[id]', rider_id); + console.log(url); + $.get(url).done(function(data) { + popup.setContent(data); + popup.update(); + }); + }); + }); + + // console.log(rider_markers); + }); + } +} diff --git a/public/assets/js/map_mqtt.js b/public/assets/js/map_mqtt.js new file mode 100644 index 00000000..39272c4f --- /dev/null +++ b/public/assets/js/map_mqtt.js @@ -0,0 +1,81 @@ +class MapEventHandler { + constructor(options, rider_markers, cust_markers) { + this.options = options; + this.rider_markers = rider_markers; + this.cust_markers = cust_markers; + } + + connect(user_id, host, port) { + var d = new Date(); + var client_id = "dash-" + user_id + "-" + d.getMonth() + "-" + d.getDate() + "-" + d.getHours() + "-" + d.getMinutes() + "-" + d.getSeconds() + "-" + d.getMilliseconds(); + console.log(client_id); + + this.mqtt = new Paho.MQTT.Client(host, port, client_id); + var options = { + useSSL: true, + timeout: 3, + onSuccess: this.onConnect, + }; + + this.mqtt.onMessageArrived = this.onMessage; + + console.log('connecting to mqtt server...'); + this.mqtt.connect(options); + + return this.mqtt; + } + + onConnect() { + console.log('connected!'); + + this.mqtt.subscribe(this.options.channels.rider_location); + } + + onMessage(msg) { + // console.log(msg); + console.log('received message'); + + var channel = msg.destinationName; + var chan_split = channel.split('/'); + var payload = msg.payloadString; + + // handle different channels + switch (chan_split[0]) { + case "rider": + this.handleRider(chan_split, payload); + break; + } + } + + handleRider(chan_split, payload) { + console.log("rider message"); + switch (chan_split[2]) { + case "location": + console.log("got location for rider " + chan_split[1] + " - " + payload); + pl_split = payload.split(':'); + console.log(pl_split); + + // check for correct format + if (pl_split.length != 2) + break; + + var lat = parseFloat(pl_split[0]); + var lng = parseFloat(pl_split[1]); + + // move marker + console.log(this.rider_markers[chan_split[1]]); + + // check if marker exists + if (this.rider_markers.hasOwnProperty(chan_split[1])) { + // marker's there, move it + this.rider_markers[chan_split[1]].setLatLng(L.latLng(lat, lng)); + } else { + // no marker, make one + console.log('creating marker'); + // TODO: make it add to the correct map layer + this.rider_markers[chan_split[1]]= L.marker([lat, lng], { icon: this.options.icons.rider_available }).addTo(map);; + } + break; + } + } +} diff --git a/symfony.lock b/symfony.lock index 88d10d33..576e49fd 100644 --- a/symfony.lock +++ b/symfony.lock @@ -152,6 +152,9 @@ "setasign/fpdf": { "version": "1.8.1" }, + "symfony/asset": { + "version": "v4.4.3" + }, "symfony/cache": { "version": "v4.0.2" }, diff --git a/templates/home.html.twig b/templates/home.html.twig index f55fe863..e0b77426 100644 --- a/templates/home.html.twig +++ b/templates/home.html.twig @@ -13,111 +13,74 @@ {% block scripts %} + + +{{ include('map/' ~ map_js_file) }} - -{{ include('map/' ~ map_js_file) }} - - - {% endblock %} diff --git a/templates/map/initOpenStreetMap.js b/templates/map/initOpenStreetMap.js index 358b91d4..8eab44bd 100644 --- a/templates/map/initOpenStreetMap.js +++ b/templates/map/initOpenStreetMap.js @@ -2,142 +2,3 @@ integrity="sha512-gZwIG9x3wUXg2hdXF6+rVkLF/0Vi9U8D2Ntg4Ga5I5BZpVkVxlJWbSQtXPSiUTtC0TjtGOmxa1AJPuV0CPthew==" crossorigin=""> - - From 9e00c3f115656cf3388d60a9a234c6310b67ddf7 Mon Sep 17 00:00:00 2001 From: Kendrick Chan Date: Sat, 25 Jan 2020 05:11:53 +0800 Subject: [PATCH 06/30] Fix publishing and subscribing for new / updated JOs #299 --- public/assets/js/dashboard_map.js | 125 ++++++++++++------ public/assets/js/map_mqtt.js | 68 ++++++---- src/EventListener/JobOrderActiveCache.php | 31 ++++- .../JobOrderHandler/CMBJobOrderHandler.php | 2 +- templates/home.html.twig | 7 +- 5 files changed, 159 insertions(+), 74 deletions(-) diff --git a/public/assets/js/dashboard_map.js b/public/assets/js/dashboard_map.js index f50c7520..a0857a47 100644 --- a/public/assets/js/dashboard_map.js +++ b/public/assets/js/dashboard_map.js @@ -5,9 +5,11 @@ class DashboardMap { this.cust_markers = cust_markers; // layer groups - this.lg_avail_rider = L.layerGroup(); - this.lg_jo_rider = L.layerGroup(); - this.lg_cust = L.layerGroup(); + this.layer_groups = { + 'rider_available': L.layerGroup(), + 'rider_active_jo': L.layerGroup(), + 'customer': L.layerGroup() + }; } initialize() { @@ -26,9 +28,9 @@ class DashboardMap { }).addTo(this.map); // layer groups - this.lg_avail_rider.addTo(this.map); - this.lg_jo_rider.addTo(this.map); - this.lg_cust.addTo(this.map); + this.layer_groups.rider_available.addTo(this.map); + this.layer_groups.rider_active_jo.addTo(this.map); + this.layer_groups.customer.addTo(this.map); // base layer var baseMaps = { @@ -37,9 +39,9 @@ class DashboardMap { // overlay layer var overlayMaps = { - 'Available Riders' : this.lg_avail_rider, - 'JO Riders' : this.lg_jo_rider, - 'Customers' : this.lg_cust + 'Available Riders' : this.layer_groups.rider_available, + 'JO Riders' : this.layer_groups.rider_active_jo, + 'Customers' : this.layer_groups.customer } L.control.layers(baseMaps, overlayMaps).addTo(this.map); @@ -47,6 +49,69 @@ class DashboardMap { return this.map; } + putMarker(id, lat, lng, markers, icon, layer_group, popup_url) { + var my = this; + // existing marker + if (markers.hasOwnProperty(id)) { + markers[id].setLatLng(L.latLng(lat, lng)); + return; + } + + // new marker + markers[id] = L.marker( + [lat, lng], + { icon: icon } + ).bindPopup('Loading...') + .addTo(layer_group); + + // bind ajax for popup + markers[id].on('click', function(e) { + var popup = e.target.getPopup(); + var url = popup_url.replace('[id]', id); + console.log(url); + $.get(url).done(function(data) { + popup.setContent(data); + popup.update(); + }); + }); + } + + putCustomerMarker(id, lat, lng) { + this.putMarker( + id, + lat, + lng, + this.cust_markers, + this.options.icons.customer, + this.layer_groups.customer, + this.options.cust_popup_url + ); + } + + putRiderAvailableMarker(id, lat, lng) { + this.putMarker( + id, + lat, + lng, + this.rider_markers, + this.options.icons.rider_available, + this.layer_groups.rider_available, + this.options.rider_popup_url + ); + } + + putRiderActiveJOMarker(id, lat, lng) { + this.putMarker( + id, + lat, + lng, + this.rider_markers, + this.options.icons.rider_active_jo, + this.layer_groups.rider_active_jo, + this.options.rider_popup_url + ); + } + loadLocations(location_url) { console.log(this.rider_markers); var my = this; @@ -54,9 +119,9 @@ class DashboardMap { url: location_url, }).done(function(response) { // clear all markers - my.lg_avail_rider.clearLayers(); - my.lg_jo_rider.clearLayers(); - my.lg_cust.clearLayers(); + my.layer_groups.rider_available.clearLayers(); + my.layer_groups.rider_active_jo.clearLayers(); + my.layer_groups.customer.clearLayers(); // get riders and mark var riders = response.riders; @@ -64,48 +129,22 @@ class DashboardMap { // rider location var point = rider_data['loc']; var lat = point[0]; - var long = point[1]; + var lng = point[1]; // customer location var cloc = rider_data['cust_loc']; var clat = cloc[0]; - var clong = cloc[1]; + var clng = cloc[1]; // create rider markers if (rider_data['has_jo']) { var jo_data = rider_data['jo']; - my.rider_markers[rider_id] = L.marker([lat, long], { icon: my.options.icons.rider_active_jo }).bindPopup('Loading...'); - my.cust_markers[jo_data['id']] = L.marker([clat, clong], { icon: my.options.icons.customer }).bindPopup('Loading...'); - - my.lg_cust.addLayer(my.cust_markers[jo_data['id']]); - my.lg_jo_rider.addLayer(my.rider_markers[rider_id]); - - // customer popup ajax - my.cust_markers[jo_data['id']].on('click', function(e) { - var popup = e.target.getPopup(); - var url = my.options.cust_popup_url.replace('[id]', jo_data['id']); - console.log(url); - $.get(url).done(function(data) { - popup.setContent(data); - popup.update(); - }); - }); + my.putCustomerMarker(jo_data['id'], clat, clng); + my.putRiderActiveJOMarker(rider_id, lat, lng); } else { - my.rider_markers[rider_id]= L.marker([lat, long], { icon: my.options.icons.rider_available }).bindPopup('Loading...'); - my.lg_avail_rider.addLayer(my.rider_markers[rider_id]); + my.putRiderAvailableMarker(rider_id, lat, lng); } - - // ajax loading of rider popup - my.rider_markers[rider_id].on('click', function(e) { - var popup = e.target.getPopup(); - var url = my.options.rider_popup_url.replace('[id]', rider_id); - console.log(url); - $.get(url).done(function(data) { - popup.setContent(data); - popup.update(); - }); - }); }); // console.log(rider_markers); diff --git a/public/assets/js/map_mqtt.js b/public/assets/js/map_mqtt.js index 39272c4f..8af023df 100644 --- a/public/assets/js/map_mqtt.js +++ b/public/assets/js/map_mqtt.js @@ -1,8 +1,7 @@ class MapEventHandler { - constructor(options, rider_markers, cust_markers) { + constructor(options, dashmap) { this.options = options; - this.rider_markers = rider_markers; - this.cust_markers = cust_markers; + this.dashmap = dashmap; } connect(user_id, host, port) { @@ -12,23 +11,29 @@ class MapEventHandler { this.mqtt = new Paho.MQTT.Client(host, port, client_id); var options = { - useSSL: true, + // useSSL: true, timeout: 3, - onSuccess: this.onConnect, + invocationContext: this, + onSuccess: this.onConnect.bind(this), }; - this.mqtt.onMessageArrived = this.onMessage; + this.mqtt.onMessageArrived = this.onMessage.bind(this); console.log('connecting to mqtt server...'); this.mqtt.connect(options); - - return this.mqtt; } - onConnect() { - console.log('connected!'); + onConnect(icontext) { + console.log('mqtt connected!'); + var my = icontext.invocationContext; - this.mqtt.subscribe(this.options.channels.rider_location); + // subscribe to rider locations + console.log('subscribing to ' + my.options.channels.rider_location); + my.mqtt.subscribe(my.options.channels.rider_location); + + // subscribe to jo locations + console.log('subscribing to ' + my.options.channels.jo_location); + my.mqtt.subscribe(my.options.channels.jo_location); } onMessage(msg) { @@ -44,6 +49,9 @@ class MapEventHandler { case "rider": this.handleRider(chan_split, payload); break; + case "jo": + this.handleJobOrder(chan_split, payload); + break; } } @@ -52,7 +60,7 @@ class MapEventHandler { switch (chan_split[2]) { case "location": console.log("got location for rider " + chan_split[1] + " - " + payload); - pl_split = payload.split(':'); + var pl_split = payload.split(':'); console.log(pl_split); // check for correct format @@ -62,19 +70,31 @@ class MapEventHandler { var lat = parseFloat(pl_split[0]); var lng = parseFloat(pl_split[1]); - // move marker - console.log(this.rider_markers[chan_split[1]]); + this.dashmap.putRiderAvaialbleMarker(chan_split[1], lat, lng); + break; + } + } - // check if marker exists - if (this.rider_markers.hasOwnProperty(chan_split[1])) { - // marker's there, move it - this.rider_markers[chan_split[1]].setLatLng(L.latLng(lat, lng)); - } else { - // no marker, make one - console.log('creating marker'); - // TODO: make it add to the correct map layer - this.rider_markers[chan_split[1]]= L.marker([lat, lng], { icon: this.options.icons.rider_available }).addTo(map);; - } + handleJobOrder(chan_split, payload) { + console.log("jo message"); + switch (chan_split[2]) { + case "location": + var my = this; + var id = chan_split[1]; + console.log("got location for jo " + id + " - " + payload); + var pl_split = payload.split(':'); + + // check for correct format + if (pl_split.length != 2) + break; + + var lat = parseFloat(pl_split[0]); + var lng = parseFloat(pl_split[1]); + + // move marker + console.log(lat + ' - ' + lng); + + this.dashmap.putCustomerMarker(id, lat, lng); break; } } diff --git a/src/EventListener/JobOrderActiveCache.php b/src/EventListener/JobOrderActiveCache.php index fb82b22c..3ce0502a 100644 --- a/src/EventListener/JobOrderActiveCache.php +++ b/src/EventListener/JobOrderActiveCache.php @@ -49,6 +49,24 @@ class JobOrderActiveCache // when a job order is updated public function postUpdate(JobOrder $jo, LifecycleEventArgs $args) { + $status = $jo->getStatus(); + + switch ($status) + { + // active + case JOStatus::PENDING: + case JOStatus::RIDER_ASSIGN: + case JOStatus::ASSIGNED: + case JOStatus::IN_TRANSIT: + case JOStatus::IN_PROGRESS: + $this->processActiveJO($jo); + break; + // inactive + case JOStatus::CANCELLED: + case JOStatus::FULFILLED: + $this->processInactiveJO($jo); + break; + } } // when a job order is deleted @@ -62,9 +80,18 @@ class JobOrderActiveCache // put in redis cache error_log('add ' . $this->key . ' - (' . $coords->getLongitude() . ', ' . $coords->getLatitude() . ') - ' . $jo->getID()); - $this->redis->geoadd($this->key, $coords->getLongitude(), $coords->getLatitude(), $jo->getID()); + $this->redis->geoadd( + $this->key, + $coords->getLongitude(), + $coords->getLatitude(), + $jo->getID() + ); - // TODO: publish to mqtt + // publish to mqtt + $this->mqtt->publish( + 'jo/' . $jo->getID() . '/location', + $coords->getLatitude() . ':' . $coords->getLongitude() + ); } protected function processInactiveJO($jo) diff --git a/src/Service/JobOrderHandler/CMBJobOrderHandler.php b/src/Service/JobOrderHandler/CMBJobOrderHandler.php index 4803e1ec..50c504fc 100644 --- a/src/Service/JobOrderHandler/CMBJobOrderHandler.php +++ b/src/Service/JobOrderHandler/CMBJobOrderHandler.php @@ -164,7 +164,7 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface $row['delivery_address'] = $orow->getDeliveryAddress(); $row['date_schedule'] = $orow->getDateSchedule()->format("d M Y g:i A"); $row['type'] = $orow->isAdvanceOrder() ? 'Advanced Order' : 'Immediate'; - $row['service_type'] = $service_types[$orow->getServiceType()]; + $row['service_type'] = $service_types[$orow->getServiceType()] ?? 'Unknown'; $row['status'] = $statuses[$orow->getStatus()]; $row['flag_advance'] = $orow->isAdvanceOrder(); $row['plate_number'] = $orow->getCustomerVehicle()->getPlateNumber(); diff --git a/templates/home.html.twig b/templates/home.html.twig index e0b77426..50f5a177 100644 --- a/templates/home.html.twig +++ b/templates/home.html.twig @@ -43,14 +43,13 @@ function initMap(r_markers, c_markers, icons) { function initEventHandler(dashmap) { var options = { - 'map': dashmap.map, 'channels': { - 'rider_location': 'rider/+/location' + 'rider_location': 'rider/+/location', + 'jo_location': 'jo/+/location' }, - 'icons': icons }; - var event_handler = new MapEventHandler(options, dashmap.rider_markers, dashmap.cust_markers); + var event_handler = new MapEventHandler(options, dashmap); event_handler.connect('{{ app.user.getID }}', '{{ mqtt_host }}', {{ mqtt_port }}); } From 3eb7c2c42693b91bbc0a1eac34e0fbca672c2f7e Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Sat, 25 Jan 2020 01:31:18 +0000 Subject: [PATCH 07/30] Update RAPIController.php --- src/Controller/RAPIController.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Controller/RAPIController.php b/src/Controller/RAPIController.php index 51033004..2e16f58d 100644 --- a/src/Controller/RAPIController.php +++ b/src/Controller/RAPIController.php @@ -934,7 +934,8 @@ class RAPIController extends Controller protected function updateVehicleBattery(JobOrder $jo) { // check if new battery - if ($jo->getServiceType() != ServiceType::BATTERY_REPLACEMENT_NEW) + if (($jo->getServiceType() != ServiceType::BATTERY_REPLACEMENT_NEW) || + ($jo->getServiceType() != CMBServiceType::BATTERY_REPLACEMENT_NEW)) return; // customer vehicle From cba80ddd902bbaea5da5000f49671fbddee3e2dd Mon Sep 17 00:00:00 2001 From: Kendrick Chan Date: Sun, 26 Jan 2020 00:58:00 +0800 Subject: [PATCH 08/30] Have active job orders loaded from cache in dashboard map #299 --- config/services.yaml | 10 +++- public/assets/js/dashboard_map.js | 10 +++- src/Controller/HomeController.php | 20 ++++++- ...he.php => JobOrderActiveCacheListener.php} | 25 +++----- src/Service/JobOrderCache.php | 58 +++++++++++++++++++ src/Service/RedisClientProvider.php | 5 ++ 6 files changed, 103 insertions(+), 25 deletions(-) rename src/EventListener/{JobOrderActiveCache.php => JobOrderActiveCacheListener.php} (77%) create mode 100644 src/Service/JobOrderCache.php diff --git a/config/services.yaml b/config/services.yaml index 69b75878..cb7b5197 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -204,10 +204,9 @@ services: App\Service\GISManagerInterface: "@App\\Service\\GISManager\\OpenStreet" #App\Service\GISManagerInterface: "@App\\Service\\GISManager\\Google" - App\EventListener\JobOrderActiveCache: + App\EventListener\JobOrderActiveCacheListener: arguments: - $redis: "@App\\Service\\RedisClientProvider" - $key: "%env(JO_ACTIVE_CACHE_KEY)%" + $jo_cache: "@App\\Service\\JobOrderCache" $mqtt: "@App\\Service\\MQTTClient" tags: - name: 'doctrine.orm.entity_listener' @@ -219,3 +218,8 @@ services: - name: 'doctrine.orm.entity_listener' event: 'postPersist' entity: 'App\Entity\JobOrder' + + App\Service\JobOrderCache: + arguments: + $redis_prov: "@App\\Service\\RedisClientProvider" + $active_jo_key: "%env(JO_ACTIVE_CACHE_KEY)%" diff --git a/public/assets/js/dashboard_map.js b/public/assets/js/dashboard_map.js index a0857a47..81230953 100644 --- a/public/assets/js/dashboard_map.js +++ b/public/assets/js/dashboard_map.js @@ -124,6 +124,14 @@ class DashboardMap { my.layer_groups.customer.clearLayers(); // get riders and mark var riders = response.riders; + var jos = response.jos; + + $.each(jos, function(id, data) { + var lat = data.latitude; + var lng = data.longitude; + + my.putCustomerMarker(id, lat, lng); + }); $.each(riders, function(rider_id, rider_data) { // rider location @@ -140,7 +148,7 @@ class DashboardMap { if (rider_data['has_jo']) { var jo_data = rider_data['jo']; - my.putCustomerMarker(jo_data['id'], clat, clng); + // my.putCustomerMarker(jo_data['id'], clat, clng); my.putRiderActiveJOMarker(rider_id, lat, lng); } else { my.putRiderAvailableMarker(rider_id, lat, lng); diff --git a/src/Controller/HomeController.php b/src/Controller/HomeController.php index 3a3e5173..abdeb6fa 100644 --- a/src/Controller/HomeController.php +++ b/src/Controller/HomeController.php @@ -9,6 +9,7 @@ use Doctrine\ORM\EntityManagerInterface; use App\Service\RiderTracker; use App\Service\GISManagerInterface; +use App\Service\JobOrderCache; use App\Entity\Rider; @@ -18,8 +19,11 @@ class HomeController extends Controller /** * @Menu(selected="home") */ - public function index(EntityManagerInterface $em, RiderTracker $rider_tracker, - GISManagerInterface $gis_manager) + public function index( + EntityManagerInterface $em, + RiderTracker $rider_tracker, + GISManagerInterface $gis_manager + ) { // get map $params['map_js_file'] = $gis_manager->getJSInitFile(); @@ -27,9 +31,18 @@ class HomeController extends Controller return $this->render('home.html.twig', $params); } - public function getRiderLocations(EntityManagerInterface $em, RiderTracker $rider_tracker) + public function getMapLocations(JobOrderCache $jo_cache) + { + $active_jos = $jo_cache->getAllActiveJobOrders(); + + // get active JOs from cache + } + + public function getRiderLocations(JobOrderCache $jo_cache, EntityManagerInterface $em, RiderTracker $rider_tracker) { // TODO: get active riders from cache + $active_jos = $jo_cache->getAllActiveJobOrders(); + // TODO: get active JOs from cache // get all riders $riders = $em->getRepository(Rider::class)->findAll(); @@ -83,6 +96,7 @@ class HomeController extends Controller } return $this->json([ + 'jos' => $active_jos, 'riders' => $locations, ]); diff --git a/src/EventListener/JobOrderActiveCache.php b/src/EventListener/JobOrderActiveCacheListener.php similarity index 77% rename from src/EventListener/JobOrderActiveCache.php rename to src/EventListener/JobOrderActiveCacheListener.php index 3ce0502a..7b4c552a 100644 --- a/src/EventListener/JobOrderActiveCache.php +++ b/src/EventListener/JobOrderActiveCacheListener.php @@ -4,22 +4,18 @@ namespace App\EventListener; use Doctrine\Common\Persistence\Event\LifecycleEventArgs; -use App\Service\RedisClientProvider; - +use App\Service\JobOrderCache; use App\Ramcar\JOStatus; - use App\Entity\JobOrder; -class JobOrderActiveCache +class JobOrderActiveCacheListener { - protected $redis; protected $key; protected $mqtt; - public function __construct(RedisClientProvider $redis, $key, $mqtt) + public function __construct(JobOrderCache $jo_cache, $mqtt) { - $this->redis = $redis->getRedisClient(); - $this->key = $key; + $this->jo_cache = $jo_cache; $this->mqtt = $mqtt; } @@ -76,18 +72,11 @@ class JobOrderActiveCache protected function processActiveJO($jo) { - $coords = $jo->getCoordinates(); - - // put in redis cache - error_log('add ' . $this->key . ' - (' . $coords->getLongitude() . ', ' . $coords->getLatitude() . ') - ' . $jo->getID()); - $this->redis->geoadd( - $this->key, - $coords->getLongitude(), - $coords->getLatitude(), - $jo->getID() - ); + // save in cache + $jo_cache->addActiveJobOrder($jo); // publish to mqtt + // TODO: do we put the key in config? $this->mqtt->publish( 'jo/' . $jo->getID() . '/location', $coords->getLatitude() . ':' . $coords->getLongitude() diff --git a/src/Service/JobOrderCache.php b/src/Service/JobOrderCache.php new file mode 100644 index 00000000..5e3a04f5 --- /dev/null +++ b/src/Service/JobOrderCache.php @@ -0,0 +1,58 @@ +redis = $redis_prov->getRedisClient(); + $this->active_jo_key = $active_jo_key; + } + + public function addActiveJobOrder(JobOrder $jo) + { + $coords = $jo->getCoordinates(); + + $this->redis->geoadd( + $this->active_jo_key, + $coords->getLongitude(), + $coords->getLatitude(), + $jo->getID() + ); + } + + public function getAllActiveJobOrders() + { + $all_jo = $this->redis->georadius( + $this->active_jo_key, + 0, + 0, + 22000, + 'km', + ['WITHCOORD' => true] + ); + + $jo_locs = []; + foreach ($all_jo as $jo_data) + { + $id = $jo_data[0]; + $lng = $jo_data[1][0]; + $lat = $jo_data[1][1]; + + $jo_locs[$id] = [ + 'longitude' => $lng, + 'latitude' => $lat, + ]; + } + + // error_log(print_r($all_jo, true)); + return $jo_locs; + } +} diff --git a/src/Service/RedisClientProvider.php b/src/Service/RedisClientProvider.php index 2572370e..80dd06fa 100644 --- a/src/Service/RedisClientProvider.php +++ b/src/Service/RedisClientProvider.php @@ -18,12 +18,17 @@ class RedisClientProvider $this->host = $host; $this->port = $port; $this->password = $password; + $this->redis = null; $this->connect(); } protected function connect() { + // already connected + if ($this->redis != null) + return $this->redis; + // if password is specified attempt connection if (strlen($this->password) > 0) { From f350b3ebb8408cbd4428c5055fd1cdf86897b237 Mon Sep 17 00:00:00 2001 From: Kendrick Chan Date: Sun, 26 Jan 2020 02:06:30 +0800 Subject: [PATCH 09/30] Make command to load active job order cache #299 --- src/Command/RefreshJobOrderCacheCommand.php | 68 +++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 src/Command/RefreshJobOrderCacheCommand.php diff --git a/src/Command/RefreshJobOrderCacheCommand.php b/src/Command/RefreshJobOrderCacheCommand.php new file mode 100644 index 00000000..cdf904d2 --- /dev/null +++ b/src/Command/RefreshJobOrderCacheCommand.php @@ -0,0 +1,68 @@ +em = $om; + $this->jo_cache = $jo_cache; + + parent::__construct(); + } + + protected function configure() + { + $this->setName('joborder:refresh_cache') + ->setDescription('Refresh active job order cache from database.') + ->setHelp('Refresh active job order cache from database.'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $date = new DateTime(); + $date->modify('-3 day'); + + $status_list = [ + JOStatus::PENDING, + JOStatus::RIDER_ASSIGN, + JOStatus::ASSIGNED, + JOStatus::IN_TRANSIT, + JOStatus::IN_PROGRESS, + ]; + + $qb = $this->em->getRepository(JobOrder::class) + ->createQueryBuilder('jo'); + $res = $qb->select('jo') + ->where('jo.status IN (:statuses)') + ->andWhere('jo.date_schedule >= :date') + ->setParameter('statuses', $status_list, Connection::PARAM_STR_ARRAY) + ->setParameter('date', $date) + ->getQuery() + ->execute(); + + // fulfill each + foreach ($res as $jo) + { + $this->jo_cache->addActiveJobOrder($jo); + } + } +} From a5a95a45d0620c2acb931b4186ea226ea4f82b57 Mon Sep 17 00:00:00 2001 From: Kendrick Chan Date: Mon, 27 Jan 2020 04:03:53 +0800 Subject: [PATCH 10/30] Create new mqtt_rider utility script for rider location caching #299 --- utils/mqtt_rider/rider_location_cache.py | 49 ++++++++++++++++++++++ utils/mqtt_rider/rider_location_cache.pyc | Bin 0 -> 2234 bytes utils/mqtt_rider/riderloc.py | 23 ++++++++++ utils/mqtt_rider/riderloc.service | 12 ++++++ utils/rider_location_cache/riderloc.py | 6 +-- 5 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 utils/mqtt_rider/rider_location_cache.py create mode 100644 utils/mqtt_rider/rider_location_cache.pyc create mode 100644 utils/mqtt_rider/riderloc.py create mode 100644 utils/mqtt_rider/riderloc.service diff --git a/utils/mqtt_rider/rider_location_cache.py b/utils/mqtt_rider/rider_location_cache.py new file mode 100644 index 00000000..b7f59977 --- /dev/null +++ b/utils/mqtt_rider/rider_location_cache.py @@ -0,0 +1,49 @@ +import paho.mqtt.client as mqtt +import ssl +import redis +import time +import signal +import sys +import os +import json + +class RiderLocationCache(object): + + def run(self, client): + print "running loop..." + client.loop_forever() + +# TODO: fix this and put these guys back under the class +def init_subscriptions(client): + print "subscribing to rider/+/location" + client.subscribe('rider/+/location') + +def on_connect(client, userdata, flags, rc): + init_subscriptions(client) + #print("Connected with result code "+str(rc)) + # client.subscribe("$SYS/#") + +def user_data_set(userdata): + conn = redis.StrictRedis(host='localhost', port=6379, db=0) + return conn + +def on_publish(client, userdata, mid): + pass + +def on_message(client, userdata, message): + # TODO: persist redis connection + redis_conn = user_data_set(userdata) + #print("message topic=", message.topic[0:10]) + + topic_split = message.topic.split('/') + if topic_split[0] != 'rider': + return; + payload_split = message.payload.split(':') + + #print repr(message) + rider_long = str(res['longitude']) + rider_lat = str(res['latitude']) + + # set the location + redis_conn.geoadd('rider_active', rider_long, rider_lat, topic_split[1]) + diff --git a/utils/mqtt_rider/rider_location_cache.pyc b/utils/mqtt_rider/rider_location_cache.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a0fd917a82bed349888ad782c4f6f6dfb8c8d0fe GIT binary patch literal 2234 zcmb_d&u`;Y5FW?**==`2ffdVPS&&u=T5Te6;D8W9^Z*BxgL6RmfaTb)=_`(%^?PYm zDSKi0clgUV@&~~8jqP^DwN3h_GtZOx_05dKe@>2G{_^^_OxjO~|KH=dzd&;Q1yYDy z26EYxIOuLdiNlWXOWg1Hfy9H3A4)vz_>sh;jvq@r?)Zts6UlpGj^rH1L-|wWoyf28 zBgy*|;$z7N%A80(ROYedBW0dQK2~PRC|9!yBDVkHv-GE6Bb%H0=PFCRt;(}ByD+y& z26+4fj{5+_z>6kEcRh(#Gd|9GAUqV#aNLI=@blwO2NXlY3{aodj7m%Q1hn3lr7brv zimKYp=X15=kMV#P$$3?qD^sh!@8i~);@l6xWQ8@QpMeu4?rU(1tF&HRUtce3iQWR+lXrnM|v51I#rouRau(Me+)^nl0dPdrxA`~ z^#vG&juimJfE1=Q$G~IGhnUsyZ6Fwk)r|T}?dyxmc~1z4RG|`c+f}u=vHe;b#Il zr@cW9lIT<^U`>G3-`f0P;%o8Q?bn5M7tdk3!-sCzFI*gFy#E7)trf7f-iC7Mwd)#i z@un{az}1sy0Pgot>aR2rYK2_tz>-=6Rv66&T%S=McJi>1M{2f=yH3YITTh)NjRUqw z>!7_MC{36}RcEhewjr1MycO1#n;Q0M-@9AjLpA2xq{{UJb-`M~F literal 0 HcmV?d00001 diff --git a/utils/mqtt_rider/riderloc.py b/utils/mqtt_rider/riderloc.py new file mode 100644 index 00000000..e2503f8c --- /dev/null +++ b/utils/mqtt_rider/riderloc.py @@ -0,0 +1,23 @@ +import paho.mqtt.client as mqtt +import rider_location_cache as rlc +import ssl +import logging + + +client = mqtt.Client() +client.on_connect = rlc.on_connect +# client.on_publish = on_publish +client.on_message = rlc.on_message + +#client.tls_set( +# "/etc/letsencrypt/live/resqaws.jankstudio.com/fullchain.pem", cert_reqs=ssl.CERT_NONE, +# tls_version=ssl.PROTOCOL_TLSv1) +#client.tls_set( +# "/root/aws_ssl_keys/fullchain.pem", cert_reqs=ssl.CERT_NONE, +# tls_version=ssl.PROTOCOL_TLSv1) +#client.connect("resqaws.jankstudio.com", 8883, 60) +client.connect("localhost", 1883, 60) + + +rider_location = rlc.RiderLocationCache() +rider_location.run(client) diff --git a/utils/mqtt_rider/riderloc.service b/utils/mqtt_rider/riderloc.service new file mode 100644 index 00000000..f3d43264 --- /dev/null +++ b/utils/mqtt_rider/riderloc.service @@ -0,0 +1,12 @@ +[Unit] +Description=Rider Location Cache Service +After=mosquitto.service redis.service + +[Service] +Type=simple +ExecStart=/usr/bin/python /root/www/resq/utils/rider_location_cache/riderloc.py +StandardInput=tty-force +Restart=always + +[Install] +WantedBy=multi-user.target diff --git a/utils/rider_location_cache/riderloc.py b/utils/rider_location_cache/riderloc.py index 26a1a92f..ca7776b1 100644 --- a/utils/rider_location_cache/riderloc.py +++ b/utils/rider_location_cache/riderloc.py @@ -12,9 +12,9 @@ client.on_message = rlc.on_message #client.tls_set( # "/etc/letsencrypt/live/resqaws.jankstudio.com/fullchain.pem", cert_reqs=ssl.CERT_NONE, # tls_version=ssl.PROTOCOL_TLSv1) -client.tls_set( - "/root/aws_ssl_keys/fullchain.pem", cert_reqs=ssl.CERT_NONE, - tls_version=ssl.PROTOCOL_TLSv1) +#client.tls_set( +# "/root/aws_ssl_keys/fullchain.pem", cert_reqs=ssl.CERT_NONE, +# tls_version=ssl.PROTOCOL_TLSv1) #client.connect("resqaws.jankstudio.com", 8883, 60) client.connect("localhost", 8883, 60) From 2d6193318a64d1c5241b4fa126ff0b10c45e1a43 Mon Sep 17 00:00:00 2001 From: Kendrick Chan Date: Mon, 27 Jan 2020 04:04:30 +0800 Subject: [PATCH 11/30] Add ability to remove customer markers via status publish #299 --- public/assets/js/dashboard_map.js | 14 ++++++++++++ public/assets/js/map_mqtt.js | 13 +++++++++-- .../JobOrderActiveCacheListener.php | 22 ++++++++++++++----- src/Service/JobOrderCache.php | 8 +++++++ templates/home.html.twig | 3 ++- 5 files changed, 52 insertions(+), 8 deletions(-) diff --git a/public/assets/js/dashboard_map.js b/public/assets/js/dashboard_map.js index 81230953..b3e03735 100644 --- a/public/assets/js/dashboard_map.js +++ b/public/assets/js/dashboard_map.js @@ -88,6 +88,20 @@ class DashboardMap { ); } + removeCustomerMarker(id) { + console.log('removing customer marker for ' + id); + var layer_group = this.layer_groups.customer; + var markers = this.cust_markers; + + // no customer marker with that id + if (!markers.hasOwnProperty(id)) { + console.log('no such marker to remove'); + return; + } + + layer_group.removeLayer(markers[id]); + } + putRiderAvailableMarker(id, lat, lng) { this.putMarker( id, diff --git a/public/assets/js/map_mqtt.js b/public/assets/js/map_mqtt.js index 8af023df..7b6c9331 100644 --- a/public/assets/js/map_mqtt.js +++ b/public/assets/js/map_mqtt.js @@ -34,6 +34,7 @@ class MapEventHandler { // subscribe to jo locations console.log('subscribing to ' + my.options.channels.jo_location); my.mqtt.subscribe(my.options.channels.jo_location); + my.mqtt.subscribe(my.options.channels.jo_status); } onMessage(msg) { @@ -77,10 +78,10 @@ class MapEventHandler { handleJobOrder(chan_split, payload) { console.log("jo message"); + var id = chan_split[1]; switch (chan_split[2]) { case "location": - var my = this; - var id = chan_split[1]; + // var my = this; console.log("got location for jo " + id + " - " + payload); var pl_split = payload.split(':'); @@ -96,6 +97,14 @@ class MapEventHandler { this.dashmap.putCustomerMarker(id, lat, lng); break; + case "status": + switch (payload) { + case 'cancel': + case 'fulfill': + case 'delete': + this.dashmap.removeCustomerMarker(id); + break; + } } } } diff --git a/src/EventListener/JobOrderActiveCacheListener.php b/src/EventListener/JobOrderActiveCacheListener.php index 7b4c552a..541ce158 100644 --- a/src/EventListener/JobOrderActiveCacheListener.php +++ b/src/EventListener/JobOrderActiveCacheListener.php @@ -36,8 +36,10 @@ class JobOrderActiveCacheListener break; // inactive case JOStatus::CANCELLED: + $this->processInactiveJO($jo, 'cancel'); + break; case JOStatus::FULFILLED: - $this->processInactiveJO($jo); + $this->processInactiveJO($jo, 'fulfill'); break; } } @@ -59,8 +61,10 @@ class JobOrderActiveCacheListener break; // inactive case JOStatus::CANCELLED: + $this->processInactiveJO($jo, 'cancel'); + break; case JOStatus::FULFILLED: - $this->processInactiveJO($jo); + $this->processInactiveJO($jo, 'fulfill'); break; } } @@ -68,14 +72,17 @@ class JobOrderActiveCacheListener // when a job order is deleted public function postRemove(JobOrder $jo, LifecycleEventArgs $args) { + $this->processInactiveJO($jo, 'delete'); } protected function processActiveJO($jo) { // save in cache - $jo_cache->addActiveJobOrder($jo); + $this->jo_cache->addActiveJobOrder($jo); // publish to mqtt + $coords = $jo->getCoordinates(); + // TODO: do we put the key in config? $this->mqtt->publish( 'jo/' . $jo->getID() . '/location', @@ -83,11 +90,16 @@ class JobOrderActiveCacheListener ); } - protected function processInactiveJO($jo) + protected function processInactiveJO($jo, $status = 'cancel') { - // TODO: remove from redis cache + // remove from redis cache + $this->jo_cache->removeActiveJobOrder($jo); // TODO: publich to mqtt + $this->mqtt->publish( + 'jo/' . $jo->getID() . '/status', + $status + ); } } diff --git a/src/Service/JobOrderCache.php b/src/Service/JobOrderCache.php index 5e3a04f5..32d55e68 100644 --- a/src/Service/JobOrderCache.php +++ b/src/Service/JobOrderCache.php @@ -55,4 +55,12 @@ class JobOrderCache // error_log(print_r($all_jo, true)); return $jo_locs; } + + public function removeActiveJobOrder(JobOrder $jo) + { + $this->redis->zrem( + $this->active_jo_key, + $jo->getID() + ); + } } diff --git a/templates/home.html.twig b/templates/home.html.twig index 50f5a177..3bcb6c3b 100644 --- a/templates/home.html.twig +++ b/templates/home.html.twig @@ -45,7 +45,8 @@ function initEventHandler(dashmap) { var options = { 'channels': { 'rider_location': 'rider/+/location', - 'jo_location': 'jo/+/location' + 'jo_location': 'jo/+/location', + 'jo_status': 'jo/+/status' }, }; From 655ead7739ed0b7ba76719676cd7fe626086bec5 Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Mon, 27 Jan 2020 04:30:31 +0000 Subject: [PATCH 12/30] Change color of hubs and riders. #312 --- templates/job-order/cmb.form.onestep.html.twig | 2 +- templates/job-order/form.onestep.html.twig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/job-order/cmb.form.onestep.html.twig b/templates/job-order/cmb.form.onestep.html.twig index 5fff6343..cfa2e393 100644 --- a/templates/job-order/cmb.form.onestep.html.twig +++ b/templates/job-order/cmb.form.onestep.html.twig @@ -626,7 +626,7 @@ $(function() { }); var icon_hub = L.divIcon({ className: 'map-div-icon', - html: "
", + html: "
", iconSize: [39, 42], iconAnchor: [15, 42] }); diff --git a/templates/job-order/form.onestep.html.twig b/templates/job-order/form.onestep.html.twig index aee7ca87..7b834830 100644 --- a/templates/job-order/form.onestep.html.twig +++ b/templates/job-order/form.onestep.html.twig @@ -622,7 +622,7 @@ $(function() { }); var icon_hub = L.divIcon({ className: 'map-div-icon', - html: "
", + html: "
", iconSize: [39, 42], iconAnchor: [15, 42] }); From 79b758f547b8885c7524fe97d8ebb5876340255c Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Mon, 27 Jan 2020 06:33:03 +0000 Subject: [PATCH 13/30] Fix for rider icons not displaying in map. #312 --- .../job-order/cmb.form.onestep.html.twig | 39 ++++++++++--------- templates/job-order/form.onestep.html.twig | 36 ++++++++++------- 2 files changed, 42 insertions(+), 33 deletions(-) diff --git a/templates/job-order/cmb.form.onestep.html.twig b/templates/job-order/cmb.form.onestep.html.twig index cfa2e393..8fa8498c 100644 --- a/templates/job-order/cmb.form.onestep.html.twig +++ b/templates/job-order/cmb.form.onestep.html.twig @@ -743,14 +743,6 @@ $(function() { }); $(function() { - {% if mode in ['onestep-edit'] %} - selected_hub = '{{ obj.getHub ? obj.getHub.getID: "" }}'; - $('#hub-field').val(selected_hub); - {% endif %} - {% if mode in ['onestep'] %} - selected_hub = ''; - {% endif %} - $('#hubs-table').on('click', 'tr', function() { var id = $(this).data('id'); @@ -806,13 +798,6 @@ $(function() { }); $(function() { - {% if mode in ['onestep-edit'] %} - selected_rider = '{{ obj.getRider ? obj.getRider.getID: "" }}'; - $('#rider-field').val(selected_rider); - {% endif %} - {% if mode in ['onestep'] %} - selected_rider = ''; - {% endif %} $('#rider-table').on('click', 'tr', function() { var id = $(this).data('id'); @@ -830,12 +815,30 @@ $(function() { {% if mode in ['onestep-edit'] %} var lat = {{ obj.getCoordinates.getLatitude }}; var lng = {{ obj.getCoordinates.getLongitude }}; - var hub = {{ obj.getHub.getID }}; - var rider = {{ obj.getRider.getID }}; + + selected_hub = '{{ obj.getHub ? obj.getHub.getID: "" }}'; + $('#hub-field').val(selected_hub); + + selected_rider = '{{ obj.getRider ? obj.getRider.getID: "" }}'; + $('#rider-field').val(selected_rider); selectPoint(lat, lng); - // TODO: find a way to highlight the set hub + // need to put selected rider on map. selected_hub is already on map because of selectPoint + riderLayerGroup.clearLayers(); + + $.getJSON("{{ url('hub_riders') }}?id=" + selected_hub, function(data) { + var riders = data['riders']; + for (i in riders) { + var rider = riders[i]; + if (selected_rider == rider['id']) { + var rider_lat = rider['location'][0]; + var rider_lng = rider['location'][1]; + var rider_marker = L.marker([rider_lat, rider_lng], { icon: icon_rider_available }); + riderLayerGroup.addLayer(rider_marker); + } + } + }); {% endif %} {% if mode in ['update-processing', 'update-reassign-hub'] %} diff --git a/templates/job-order/form.onestep.html.twig b/templates/job-order/form.onestep.html.twig index 7b834830..40fef7d4 100644 --- a/templates/job-order/form.onestep.html.twig +++ b/templates/job-order/form.onestep.html.twig @@ -739,13 +739,6 @@ $(function() { }); $(function() { - {% if mode in ['onestep-edit'] %} - selected_hub = '{{ obj.getHub ? obj.getHub.getID: "" }}'; - $('#hub-field').val(selected_hub); - {% endif %} - {% if mode in ['onestep'] %} - selected_hub = ''; - {% endif %} $('#hubs-table').on('click', 'tr', function() { var id = $(this).data('id'); @@ -801,13 +794,6 @@ $(function() { }); $(function() { - {% if mode in ['onestep-edit'] %} - selected_rider = '{{ obj.getRider ? obj.getRider.getID: "" }}'; - $('#rider-field').val(selected_rider); - {% endif %} - {% if mode in ['onestep'] %} - selected_rider = ''; - {% endif %} $('#rider-table').on('click', 'tr', function() { var id = $(this).data('id'); @@ -826,9 +812,29 @@ $(function() { var lat = {{ obj.getCoordinates.getLatitude }}; var lng = {{ obj.getCoordinates.getLongitude }}; + selected_hub = '{{ obj.getHub ? obj.getHub.getID: "" }}'; + $('#hub-field').val(selected_hub); + + selected_rider = '{{ obj.getRider ? obj.getRider.getID: "" }}'; + $('#rider-field').val(selected_rider); + selectPoint(lat, lng); - // TODO: find a way to highlight the set hub + // need to put selected rider on map. selected_hub is already on map because of selectPoint + riderLayerGroup.clearLayers(); + + $.getJSON("{{ url('hub_riders') }}?id=" + selected_hub, function(data) { + var riders = data['riders']; + for (i in riders) { + var rider = riders[i]; + if (selected_rider == rider['id']) { + var rider_lat = rider['location'][0]; + var rider_lng = rider['location'][1]; + var rider_marker = L.marker([rider_lat, rider_lng], { icon: icon_rider_available }); + riderLayerGroup.addLayer(rider_marker); + } + } + }); {% endif %} {% if mode in ['update-processing', 'update-reassign-hub'] %} From 95964bf3294555ef48a8abffbd969f4537a9cd6d Mon Sep 17 00:00:00 2001 From: Kendrick Chan Date: Mon, 27 Jan 2020 17:40:39 +0000 Subject: [PATCH 14/30] Add README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 00000000..0fc05d72 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +MQTT format for location updates: +: \ No newline at end of file From cc2dd8c7ff1959703175614a7e7fa1ccf8f121a2 Mon Sep 17 00:00:00 2001 From: Kendrick Chan Date: Mon, 27 Jan 2020 17:41:07 +0000 Subject: [PATCH 15/30] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0fc05d72..998e0dba 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ MQTT format for location updates: -: \ No newline at end of file +`:` \ No newline at end of file From bdb306e5e1459ee7960eb07c505cf3385745effb Mon Sep 17 00:00:00 2001 From: Kendrick Chan Date: Tue, 28 Jan 2020 04:31:41 +0800 Subject: [PATCH 16/30] Fix bug in mqtt_rider script #299 --- utils/mqtt_rider/rider_location_cache.py | 16 ++++------------ utils/mqtt_rider/rider_location_cache.pyc | Bin 2234 -> 1929 bytes utils/mqtt_rider/riderloc.py | 5 ++++- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/utils/mqtt_rider/rider_location_cache.py b/utils/mqtt_rider/rider_location_cache.py index b7f59977..b07d4acc 100644 --- a/utils/mqtt_rider/rider_location_cache.py +++ b/utils/mqtt_rider/rider_location_cache.py @@ -23,27 +23,19 @@ def on_connect(client, userdata, flags, rc): #print("Connected with result code "+str(rc)) # client.subscribe("$SYS/#") -def user_data_set(userdata): - conn = redis.StrictRedis(host='localhost', port=6379, db=0) - return conn - def on_publish(client, userdata, mid): pass def on_message(client, userdata, message): - # TODO: persist redis connection - redis_conn = user_data_set(userdata) - #print("message topic=", message.topic[0:10]) + redis_conn = userdata['redis'] topic_split = message.topic.split('/') if topic_split[0] != 'rider': return; payload_split = message.payload.split(':') - #print repr(message) - rider_long = str(res['longitude']) - rider_lat = str(res['latitude']) + rider_long = str(payload_split[1]) + rider_lat = str(payload_split[0]) # set the location - redis_conn.geoadd('rider_active', rider_long, rider_lat, topic_split[1]) - + redis_conn.geoadd('loc_rider_active', rider_long, rider_lat, topic_split[1]) diff --git a/utils/mqtt_rider/rider_location_cache.pyc b/utils/mqtt_rider/rider_location_cache.pyc index a0fd917a82bed349888ad782c4f6f6dfb8c8d0fe..e8968436ff784ec3bb1aca3ba18a64dba399a0d8 100644 GIT binary patch delta 326 zcmXv|Jxjzu6r7j+NI1}2uQ_v3#4d$!VlN0{V{w943I@rhNEC!5SS2W2WhJt;jocrw z6aRpft-rzEKj2%@WoP!ydoz!HTe}}7;(KLqwmm)1@plBj2aIOYAq0upO?adP87pDG8vsN+Jg)JL*qLYo1Shj}g>N?Nr z(iU}gk+e!AuMqm0(w IQ#2v*14alqNB{r; delta 547 zcmXw0&ubGw6n?WayPKG#-Gm?}(H0AZ2o|9ykAf&Dm_u?XR4{CJmbz(3%I>764Uxu+ z7a`2advB$OUOjm7;Q!*$Kfr!(lP>=J`)0mv|Ezc9zuJTSqk~ToZ9a+rD_C~3 z>RfG==qu52Kw8kCTfY7MMlJ-34$OzW=EgmMAsPwp(hg}!T9I~1d$c4u7nJj9?(Z1| z{5;K!DZ?30qi|eAoIanXCi(jaWK6G55+HTiH!dKJb&{FNQ@8_@CXwlLqNw+ote}pj zlZoB+-ae@zGt1(1s6!JDv)Hte$aoBi9qX$V@P~T5xI_ZWW5cDOxuo1-AtR_*XB6eo z;!q2!&LpLWq%ohnWYMLHb1qNEmJr=y9`8Q(*0E1H+C9>6Jj#I}EumIZB$%+v3VQaP z+j+UUnaOElW;!+{6vm-h2UP@Vq9H4cOmYz?{20Z}_M7&L``qW(OXJL5yMH=-Q&|8Y vrz#sF5K1&;AOhK~+wa~BQMbRm_mw8<{f7+C?0@mWe)JonZ6|(Ps Date: Tue, 28 Jan 2020 04:32:05 +0800 Subject: [PATCH 17/30] Add rider cache service #299 --- config/services.yaml | 8 +++- public/assets/js/dashboard_map.js | 17 ++++++- public/assets/js/map_mqtt.js | 2 +- src/Controller/HomeController.php | 24 ++++++++-- src/Controller/RAPIController.php | 13 +++++- src/Service/JobOrderCache.php | 2 +- src/Service/RiderCache.php | 78 +++++++++++++++++++++++++++++++ 7 files changed, 134 insertions(+), 10 deletions(-) create mode 100644 src/Service/RiderCache.php diff --git a/config/services.yaml b/config/services.yaml index cb7b5197..0b7669fd 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -222,4 +222,10 @@ services: App\Service\JobOrderCache: arguments: $redis_prov: "@App\\Service\\RedisClientProvider" - $active_jo_key: "%env(JO_ACTIVE_CACHE_KEY)%" + $active_jo_key: "%env(LOCATION_JO_ACTIVE_KEY)%" + + App\Service\RiderCache: + arguments: + $redis_prov: "@App\\Service\\RedisClientProvider" + $loc_key: "%env(LOCATION_RIDER_ACTIVE_KEY)%" + $status_key: "%env(STATUS_RIDER_KEY)%" diff --git a/public/assets/js/dashboard_map.js b/public/assets/js/dashboard_map.js index b3e03735..57d3c7a3 100644 --- a/public/assets/js/dashboard_map.js +++ b/public/assets/js/dashboard_map.js @@ -136,10 +136,12 @@ class DashboardMap { my.layer_groups.rider_available.clearLayers(); my.layer_groups.rider_active_jo.clearLayers(); my.layer_groups.customer.clearLayers(); - // get riders and mark + + // get riders and job orders var riders = response.riders; var jos = response.jos; + // job orders $.each(jos, function(id, data) { var lat = data.latitude; var lng = data.longitude; @@ -147,6 +149,18 @@ class DashboardMap { my.putCustomerMarker(id, lat, lng); }); + // riders + $.each(riders, function(id, data) { + var lat = data.latitude; + var lng = data.longitude; + + if (data.has_jo) + my.putRiderActiveJOMarker(id, lat, lng); + else + my.putRiderAvailableMarker(id, lat, lng); + }); + + /* $.each(riders, function(rider_id, rider_data) { // rider location var point = rider_data['loc']; @@ -168,6 +182,7 @@ class DashboardMap { my.putRiderAvailableMarker(rider_id, lat, lng); } }); + */ // console.log(rider_markers); }); diff --git a/public/assets/js/map_mqtt.js b/public/assets/js/map_mqtt.js index 7b6c9331..ebd401bb 100644 --- a/public/assets/js/map_mqtt.js +++ b/public/assets/js/map_mqtt.js @@ -71,7 +71,7 @@ class MapEventHandler { var lat = parseFloat(pl_split[0]); var lng = parseFloat(pl_split[1]); - this.dashmap.putRiderAvaialbleMarker(chan_split[1], lat, lng); + this.dashmap.putRiderAvailableMarker(chan_split[1], lat, lng); break; } } diff --git a/src/Controller/HomeController.php b/src/Controller/HomeController.php index abdeb6fa..ca5654b4 100644 --- a/src/Controller/HomeController.php +++ b/src/Controller/HomeController.php @@ -10,6 +10,7 @@ use Doctrine\ORM\EntityManagerInterface; use App\Service\RiderTracker; use App\Service\GISManagerInterface; use App\Service\JobOrderCache; +use App\Service\RiderCache; use App\Entity\Rider; @@ -38,13 +39,27 @@ class HomeController extends Controller // get active JOs from cache } - public function getRiderLocations(JobOrderCache $jo_cache, EntityManagerInterface $em, RiderTracker $rider_tracker) + public function getRiderLocations(JobOrderCache $jo_cache, RiderCache $rider_cache, EntityManagerInterface $em, RiderTracker $rider_tracker) { - // TODO: get active riders from cache + // get active JOs from cache $active_jos = $jo_cache->getAllActiveJobOrders(); + $riders = $rider_cache->getAllActiveRiders(); - // TODO: get active JOs from cache + // TODO: optimize this + // get all riders and figure out if they have active jos + foreach ($riders as $rider_id => $rider_data) + { + $rider = $em->getRepository(Rider::class)->find($rider_id); + $jo = $rider->getActiveJobOrder(); + if ($jo == null) + $riders[$rider_id]['has_jo'] = false; + else + $riders[$rider_id]['has_jo'] = true; + } + + // get active riders from cache // get all riders + /* $riders = $em->getRepository(Rider::class)->findAll(); $locations = []; @@ -94,10 +109,11 @@ class HomeController extends Controller ]; } + */ return $this->json([ 'jos' => $active_jos, - 'riders' => $locations, + 'riders' => $riders, ]); } diff --git a/src/Controller/RAPIController.php b/src/Controller/RAPIController.php index c5d629c8..ce8631fd 100644 --- a/src/Controller/RAPIController.php +++ b/src/Controller/RAPIController.php @@ -28,6 +28,7 @@ use App\Ramcar\JOEventType; use App\Service\InvoiceGeneratorInterface; use App\Service\MQTTClient; use App\Service\RedisClientProvider; +use App\Service\RiderCache; use App\Entity\RiderSession; use App\Entity\Customer; @@ -209,7 +210,7 @@ class RAPIController extends Controller return $res->getReturnResponse(); } - public function login(Request $req, EncoderFactoryInterface $ef, RedisClientProvider $redis) + public function login(Request $req, EncoderFactoryInterface $ef, RedisClientProvider $redis, RiderCache $rcache) { $required_params = [ 'user', @@ -251,6 +252,11 @@ class RAPIController extends Controller $rider->setAvailable(true); + $rider_id = $rider->getID(); + // cache rider location (default to hub) + // TODO: figure out longitude / latitude default + $rcache->addActiveRider($rider_id, 0, 0); + // TODO: log rider logging in $em->flush(); @@ -289,7 +295,7 @@ class RAPIController extends Controller return $res->getReturnResponse(); } - public function logout(Request $req) + public function logout(Request $req, RiderCache $rcache) { $required_params = []; $em = $this->getDoctrine()->getManager(); @@ -301,6 +307,9 @@ class RAPIController extends Controller $rider = $this->session->getRider(); $rider->setAvailable(false); + // remove from cache + $rcache->removeActiveRider($rider->getID()); + // remove rider from session $this->session->setRider(null); diff --git a/src/Service/JobOrderCache.php b/src/Service/JobOrderCache.php index 32d55e68..488db29d 100644 --- a/src/Service/JobOrderCache.php +++ b/src/Service/JobOrderCache.php @@ -34,7 +34,7 @@ class JobOrderCache $this->active_jo_key, 0, 0, - 22000, + 41000, 'km', ['WITHCOORD' => true] ); diff --git a/src/Service/RiderCache.php b/src/Service/RiderCache.php new file mode 100644 index 00000000..399a63f2 --- /dev/null +++ b/src/Service/RiderCache.php @@ -0,0 +1,78 @@ +redis = $redis_prov->getRedisClient(); + $this->loc_key = $loc_key; + $this->status_key = $status_key; + } + + public function addActiveRider($id, $lat, $lng) + { + $coords = $jo->getCoordinates(); + + $this->redis->geoadd( + $this->loc_key, + $lng, + $lat, + $id + ); + } + + public function getAllActiveRiders() + { + $all_riders = $this->redis->georadius( + $this->loc_key, + 0, + 0, + 41000, + 'km', + ['WITHCOORD' => true] + ); + + $locs = []; + foreach ($all_riders as $data) + { + $id = $data[0]; + $lng = $data[1][0]; + $lat = $data[1][1]; + + $locs[$id] = [ + 'longitude' => $lng, + 'latitude' => $lat, + ]; + } + + // error_log(print_r($all_riders, true)); + return $locs; + } + + public function removeActiveRider($id) + { + $this->redis->zrem( + $this->loc_key, + $id + ); + } + + public function incJobOrderCount($id, $status) + { + $this->redis->hincrby($this->status_key, $id, 1); + } + + public function decJobOrderCount($id, $status) + { + $this->redis->hincrby($this->status_key, $id, -1); + } +} From 86b4601bb8f49299e48be78d15ad4e5e34396eed Mon Sep 17 00:00:00 2001 From: Kendrick Chan Date: Tue, 28 Jan 2020 05:37:41 +0800 Subject: [PATCH 18/30] Fix bug in rider cache #270 --- src/Service/RiderCache.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Service/RiderCache.php b/src/Service/RiderCache.php index 399a63f2..cdd18864 100644 --- a/src/Service/RiderCache.php +++ b/src/Service/RiderCache.php @@ -20,8 +20,6 @@ class RiderCache public function addActiveRider($id, $lat, $lng) { - $coords = $jo->getCoordinates(); - $this->redis->geoadd( $this->loc_key, $lng, From 9563bd8cb2d68abcbda328d6f6d6051284ccbdc7 Mon Sep 17 00:00:00 2001 From: Kendrick Chan Date: Tue, 28 Jan 2020 05:53:52 +0800 Subject: [PATCH 19/30] Fix dashboard bug for active riders not in the database #270 --- src/Controller/HomeController.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Controller/HomeController.php b/src/Controller/HomeController.php index ca5654b4..2ecfcd09 100644 --- a/src/Controller/HomeController.php +++ b/src/Controller/HomeController.php @@ -50,6 +50,12 @@ class HomeController extends Controller foreach ($riders as $rider_id => $rider_data) { $rider = $em->getRepository(Rider::class)->find($rider_id); + if ($rider == null) + { + unset $riders[$rider_id]; + continue; + } + $jo = $rider->getActiveJobOrder(); if ($jo == null) $riders[$rider_id]['has_jo'] = false; From a82cfb2691762441007ce3bb68e27b19dbe59135 Mon Sep 17 00:00:00 2001 From: Kendrick Chan Date: Tue, 28 Jan 2020 05:55:22 +0800 Subject: [PATCH 20/30] Fix bug for dashboard rider #270 --- src/Controller/HomeController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Controller/HomeController.php b/src/Controller/HomeController.php index 2ecfcd09..5ba473af 100644 --- a/src/Controller/HomeController.php +++ b/src/Controller/HomeController.php @@ -52,7 +52,7 @@ class HomeController extends Controller $rider = $em->getRepository(Rider::class)->find($rider_id); if ($rider == null) { - unset $riders[$rider_id]; + unset($riders[$rider_id]); continue; } From ffb4551fb1bcd3c53553cbe86542e7e68e7ecaad Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Tue, 28 Jan 2020 05:03:20 +0000 Subject: [PATCH 21/30] Update composer lock after running composer require. #312 --- composer.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.lock b/composer.lock index a1172379..2aa7742d 100644 --- a/composer.lock +++ b/composer.lock @@ -1,7 +1,7 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], "content-hash": "b101ecfbc1f6f2270f0e8ad326035b7e", From 555bd5ae9a1e402d597f1d91f78824682e88dc99 Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Tue, 28 Jan 2020 05:20:11 +0000 Subject: [PATCH 22/30] Add Cancel Job Order button to onestep form. #313 --- templates/job-order/cmb.form.onestep.html.twig | 3 +++ templates/job-order/form.onestep.html.twig | 3 +++ 2 files changed, 6 insertions(+) diff --git a/templates/job-order/cmb.form.onestep.html.twig b/templates/job-order/cmb.form.onestep.html.twig index 8fa8498c..8eced3cc 100644 --- a/templates/job-order/cmb.form.onestep.html.twig +++ b/templates/job-order/cmb.form.onestep.html.twig @@ -582,6 +582,9 @@
+ {% if ftags.set_map_coordinate and is_granted('joborder.cancel') and not obj.isCancelled %} + Cancel Job Order + {% endif %} Back
diff --git a/templates/job-order/form.onestep.html.twig b/templates/job-order/form.onestep.html.twig index 40fef7d4..120943ad 100644 --- a/templates/job-order/form.onestep.html.twig +++ b/templates/job-order/form.onestep.html.twig @@ -578,6 +578,9 @@
+ {% if ftags.set_map_coordinate and is_granted('joborder.cancel') and not obj.isCancelled %} + Cancel Job Order + {% endif %} Back
From bc8ce40b1339020e1b5b2854603ca64d4602b171 Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Tue, 28 Jan 2020 07:24:14 +0000 Subject: [PATCH 23/30] Add distance from customer to hub to hub table. #314 --- src/Controller/HubController.php | 1 + src/Service/MapTools.php | 29 +++++++++- .../job-order/cmb.form.onestep.html.twig | 25 ++++++++- templates/job-order/form.onestep.html.twig | 53 ++++++++++++++----- 4 files changed, 91 insertions(+), 17 deletions(-) diff --git a/src/Controller/HubController.php b/src/Controller/HubController.php index ffce16f6..aeaaec7a 100644 --- a/src/Controller/HubController.php +++ b/src/Controller/HubController.php @@ -314,6 +314,7 @@ class HubController extends Controller 'name' => $hub->getName(), 'branch' => $hub->getBranch(), 'cnum' => $hub->getContactNumbers(), + 'distance' => $hub_res['distance'], ]; } diff --git a/src/Service/MapTools.php b/src/Service/MapTools.php index f02618ba..6aa5848c 100644 --- a/src/Service/MapTools.php +++ b/src/Service/MapTools.php @@ -84,10 +84,23 @@ class MapTools { //error_log($row[0]->getName() . ' - ' . $row['dist']); $hubs[] = $row[0]; + + // get coordinates of hub + $hub_coordinates = $row[0]->getCoordinates(); + + $cust_lat = $point->getLatitude(); + $cust_lng = $point->getLongitude(); + + $hub_lat = $hub_coordinates->getLatitude(); + $hub_lng = $hub_coordinates->getLongitude(); + + // get distance in kilometers from customer point to hub point + $dist = $this->distance($cust_lat, $cust_lng, $hub_lat, $hub_lng); + $final_data[] = [ 'hub' => $row[0], 'db_distance' => $row['dist'], - 'distance' => 0, + 'distance' => $dist, 'duration' => 0, ]; } @@ -135,4 +148,18 @@ class MapTools return $final_data; */ } + + protected function distance($lat1, $lon1, $lat2, $lon2) + { + if (($lat1 == $lat2) && ($lon1 == $lon2)) + return 0; + + $theta = $lon1 - $lon2; + $dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta)); + $dist = acos($dist); + $dist = rad2deg($dist); + $miles = $dist * 60 * 1.1515; + + return round(($miles * 1.609344), 1); + } } diff --git a/templates/job-order/cmb.form.onestep.html.twig b/templates/job-order/cmb.form.onestep.html.twig index 8eced3cc..8e4ac45d 100644 --- a/templates/job-order/cmb.form.onestep.html.twig +++ b/templates/job-order/cmb.form.onestep.html.twig @@ -381,18 +381,21 @@ Hub Branch Contact Numbers + Distance in KM Action - {% if mode in ['onestep-edit'] %} + @@ -662,6 +665,7 @@ $(function() { hub_table += '' + hub['name'] + ''; hub_table += '' + hub['branch'] + ''; hub_table += '' + hub['cnum'] + ''; + hub_table += '' + hub['distance'] + ''; hub_table += ''; hub_table += ''; } @@ -673,13 +677,30 @@ $(function() { {% if mode in ['onestep-edit'] %} // get nearest hubs ajax + var hub_table = ''; $.getJSON("{{ url('hub_nearest') }}?lat=" + lat + "&long=" + lng, function(data) { var hubs = data['hubs']; for (i in hubs) { var hub = hubs[i]; var hub_marker = L.marker([hub['lat'], hub['long']], { icon: icon_hub }); hubLayerGroup.addLayer(hub_marker); + + if (selected_hub == hub['id']) { + hub_table += ''; + } + else { + hub_table += ''; + } + hub_table += '' + hub['name'] + ''; + hub_table += '' + hub['branch'] + ''; + hub_table += '' + hub['cnum'] + ''; + hub_table += '' + hub['distance'] + ''; + hub_table += ''; + hub_table += ''; + } + + $('#nearest_hubs').html(hub_table); }); {% endif %} diff --git a/templates/job-order/form.onestep.html.twig b/templates/job-order/form.onestep.html.twig index 120943ad..8e4ac45d 100644 --- a/templates/job-order/form.onestep.html.twig +++ b/templates/job-order/form.onestep.html.twig @@ -36,7 +36,9 @@
- +
@@ -326,7 +328,9 @@
- +
@@ -338,7 +342,9 @@
- + @@ -363,7 +369,9 @@
- +
@@ -372,25 +380,22 @@ Hub Branch - Contact Numbers + Distance in KM Action - {% if mode in ['onestep-edit'] %} +
@@ -405,7 +410,9 @@
- +
@@ -658,24 +665,42 @@ $(function() { hub_table += '' + hub['name'] + ''; hub_table += '' + hub['branch'] + ''; hub_table += '' + hub['cnum'] + ''; + hub_table += '' + hub['distance'] + ''; hub_table += ''; hub_table += ''; } $('#nearest_hubs').html(hub_table); - + }); {% endif %} {% if mode in ['onestep-edit'] %} // get nearest hubs ajax + var hub_table = ''; $.getJSON("{{ url('hub_nearest') }}?lat=" + lat + "&long=" + lng, function(data) { var hubs = data['hubs']; for (i in hubs) { var hub = hubs[i]; var hub_marker = L.marker([hub['lat'], hub['long']], { icon: icon_hub }); hubLayerGroup.addLayer(hub_marker); + + if (selected_hub == hub['id']) { + hub_table += ''; + } + else { + hub_table += ''; + } + hub_table += '' + hub['name'] + ''; + hub_table += '' + hub['branch'] + ''; + hub_table += '' + hub['cnum'] + ''; + hub_table += '' + hub['distance'] + ''; + hub_table += ''; + hub_table += ''; + } + + $('#nearest_hubs').html(hub_table); }); {% endif %} From e914c6d2e1e044bb35008edd8ebe08091b8aa03a Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Tue, 28 Jan 2020 21:51:32 +0800 Subject: [PATCH 24/30] Add customer jo tracking view #315 --- config/packages/security.yaml | 5 + config/routes/job_order.yaml | 5 + public/assets/css/style.css | 27 ++++ public/assets/js/tracker_map.js | 182 ++++++++++++++++++++++++++ src/Controller/JobOrderController.php | 26 ++++ templates/base_minimal.html.twig | 70 ++++++++++ templates/job-order/tracker.html.twig | 95 ++++++++++++++ 7 files changed, 410 insertions(+) create mode 100644 public/assets/js/tracker_map.js create mode 100644 templates/base_minimal.html.twig create mode 100644 templates/job-order/tracker.html.twig diff --git a/config/packages/security.yaml b/config/packages/security.yaml index c8deb304..dc9aa78d 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -21,6 +21,11 @@ security: methods: [GET] security: false + tracker: + pattern: ^\/track\/ + methods: [GET] + security: false + api: pattern: ^\/api\/ security: false diff --git a/config/routes/job_order.yaml b/config/routes/job_order.yaml index 139b208f..bfd41df9 100644 --- a/config/routes/job_order.yaml +++ b/config/routes/job_order.yaml @@ -201,3 +201,8 @@ jo_ajax_popup: controller: App\Controller\JobOrderController::popupInfo methods: [GET] +jo_tracker: + path: /track/{id} + controller: App\Controller\JobOrderController::tracker + methods: [GET] + diff --git a/public/assets/css/style.css b/public/assets/css/style.css index 051d2143..140966da 100644 --- a/public/assets/css/style.css +++ b/public/assets/css/style.css @@ -331,4 +331,31 @@ span.has-danger, .map-div-icon i.awesome { margin: 12px auto; font-size: 17px; +} +.map-info { + position: absolute; + bottom: 0; + left: 50%; + transform: translateX(-50%); + z-index: 9999; + padding: 1.5em; + width: 100%; +} + +.map-info > .m-portlet { + margin-bottom: 0; +} + +.map-info .m-portlet__body { + padding: 1.5rem; +} + +.map-info .rider-image { + width: 4.8rem; + border-radius: 50%; +} + +.map-info .m-badge { + border-radius: 0; +} \ No newline at end of file diff --git a/public/assets/js/tracker_map.js b/public/assets/js/tracker_map.js new file mode 100644 index 00000000..ecf3df41 --- /dev/null +++ b/public/assets/js/tracker_map.js @@ -0,0 +1,182 @@ +class DashboardMap { + constructor(options, rider_markers, cust_markers) { + this.options = options; + this.rider_markers = rider_markers; + this.cust_markers = cust_markers; + + // layer groups + this.layer_groups = { + 'rider_available': L.layerGroup(), + 'rider_active_jo': L.layerGroup(), + 'customer': L.layerGroup() + }; + } + + initialize() { + // main map + this.map = L.map(this.options.div_id).setView( + [this.options.center_lat, this.options.center_lng], + this.options.zoom + ); + + // add tile layer + var streets = L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}', { + attribution: 'Map data © OpenStreetMap contributors, CC-BY-SA, Imagery © Mapbox', + maxZoom: 18, + id: 'mapbox/streets-v11', + accessToken: this.options.access_token + }).addTo(this.map); + + // layer groups + this.layer_groups.rider_available.addTo(this.map); + this.layer_groups.rider_active_jo.addTo(this.map); + this.layer_groups.customer.addTo(this.map); + + // base layer + var baseMaps = { + 'Streets': streets + }; + + // overlay layer + return this.map; + } + + putMarker(id, lat, lng, markers, icon, layer_group, popup_url) { + var my = this; + // existing marker + if (markers.hasOwnProperty(id)) { + markers[id].setLatLng(L.latLng(lat, lng)); + return; + } + + // new marker + markers[id] = L.marker( + [lat, lng], + { icon: icon } + ).bindPopup('Loading...') + .addTo(layer_group); + + // bind ajax for popup + markers[id].on('click', function(e) { + var popup = e.target.getPopup(); + var url = popup_url.replace('[id]', id); + console.log(url); + $.get(url).done(function(data) { + popup.setContent(data); + popup.update(); + }); + }); + } + + putCustomerMarker(id, lat, lng) { + this.putMarker( + id, + lat, + lng, + this.cust_markers, + this.options.icons.customer, + this.layer_groups.customer, + this.options.cust_popup_url + ); + } + + removeCustomerMarker(id) { + console.log('removing customer marker for ' + id); + var layer_group = this.layer_groups.customer; + var markers = this.cust_markers; + + // no customer marker with that id + if (!markers.hasOwnProperty(id)) { + console.log('no such marker to remove'); + return; + } + + layer_group.removeLayer(markers[id]); + } + + putRiderAvailableMarker(id, lat, lng) { + this.putMarker( + id, + lat, + lng, + this.rider_markers, + this.options.icons.rider_available, + this.layer_groups.rider_available, + this.options.rider_popup_url + ); + } + + putRiderActiveJOMarker(id, lat, lng) { + this.putMarker( + id, + lat, + lng, + this.rider_markers, + this.options.icons.rider_active_jo, + this.layer_groups.rider_active_jo, + this.options.rider_popup_url + ); + } + + loadLocations(location_url) { + console.log(this.rider_markers); + var my = this; + $.ajax({ + url: location_url, + }).done(function(response) { + // clear all markers + my.layer_groups.rider_available.clearLayers(); + my.layer_groups.rider_active_jo.clearLayers(); + my.layer_groups.customer.clearLayers(); + + // get riders and job orders + var riders = response.riders; + var jos = response.jos; + + // job orders + $.each(jos, function(id, data) { + var lat = data.latitude; + var lng = data.longitude; + + my.putCustomerMarker(id, lat, lng); + }); + + // riders + $.each(riders, function(id, data) { + var lat = data.latitude; + var lng = data.longitude; + + if (data.has_jo) + my.putRiderActiveJOMarker(id, lat, lng); + else + my.putRiderAvailableMarker(id, lat, lng); + }); + + /* + $.each(riders, function(rider_id, rider_data) { + // rider location + var point = rider_data['loc']; + var lat = point[0]; + var lng = point[1]; + + // customer location + var cloc = rider_data['cust_loc']; + var clat = cloc[0]; + var clng = cloc[1]; + + // create rider markers + if (rider_data['has_jo']) { + var jo_data = rider_data['jo']; + + // my.putCustomerMarker(jo_data['id'], clat, clng); + my.putRiderActiveJOMarker(rider_id, lat, lng); + } else { + my.putRiderAvailableMarker(rider_id, lat, lng); + } + }); + */ + + // console.log(rider_markers); + }); + } +} diff --git a/src/Controller/JobOrderController.php b/src/Controller/JobOrderController.php index 039ea37d..c92638a4 100644 --- a/src/Controller/JobOrderController.php +++ b/src/Controller/JobOrderController.php @@ -4,6 +4,7 @@ namespace App\Controller; use App\Ramcar\JOStatus; use App\Ramcar\InvoiceCriteria; +use App\Ramcar\CMBServiceType; use App\Entity\CustomerVehicle; use App\Entity\Promo; @@ -23,6 +24,9 @@ use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; +use Doctrine\ORM\EntityManagerInterface; +use App\Service\RiderTracker; + use Catalyst\MenuBundle\Annotation\Menu; class JobOrderController extends Controller @@ -922,4 +926,26 @@ class JobOrderController extends Controller return $this->render('job-order/popup.html.twig', [ 'jo' => $jo ]); } + + /** + * @ParamConverter("jo", class="App\Entity\JobOrder") + */ + public function tracker( + EntityManagerInterface $em, + RiderTracker $rider_tracker, + GISManagerInterface $gis_manager, + JobOrder $jo + ) + { + if ($jo === null) + return new Response('No job order data'); + + // get map + $params['jo'] = $jo; + $params['rider'] = $jo->getRider(); + $params['service_type'] = CMBServiceType::getName($jo->getServiceType()); + $params['map_js_file'] = $gis_manager->getJSInitFile(); + + return $this->render('job-order/tracker.html.twig', $params); + } } diff --git a/templates/base_minimal.html.twig b/templates/base_minimal.html.twig new file mode 100644 index 00000000..8a51bcc6 --- /dev/null +++ b/templates/base_minimal.html.twig @@ -0,0 +1,70 @@ +{% import 'menu.html.twig' as menu %} + + + + + + + {% block title %}{% trans %}block_title{% endtrans %}{% endblock %} + + + + + + + + + + + + + + + + + + + + + + + + + {% block stylesheets %} + + {% endblock %} + + + + + + + {% block body %}{% endblock %} + + + + + + + + + + + + + + + + {% block scripts %}{% endblock %} + + + diff --git a/templates/job-order/tracker.html.twig b/templates/job-order/tracker.html.twig new file mode 100644 index 00000000..42f18746 --- /dev/null +++ b/templates/job-order/tracker.html.twig @@ -0,0 +1,95 @@ +{% extends 'base_minimal.html.twig' %} + +{% block body %} +
+
+
+
+
+
+ +
+
Order #{{ jo.getID }}
+
{{ rider.getFullName }}
+
{{ service_type }}
+
+
+
+
+
+
+{% endblock %} + +{% block scripts %} + + + +{{ include('map/' ~ map_js_file) }} + +{% endblock %} \ No newline at end of file From c8157b950948b436a7a93aae9958207ad6c76974 Mon Sep 17 00:00:00 2001 From: Kendrick Chan Date: Wed, 29 Jan 2020 01:13:47 +0800 Subject: [PATCH 25/30] Add functionality to job order tracking page #270 --- public/assets/js/dashboard_map.js | 65 ++++----- public/assets/js/map_mqtt.js | 14 +- public/assets/js/tracker_map.js | 182 -------------------------- src/Controller/JobOrderController.php | 5 +- templates/home.html.twig | 4 + templates/job-order/tracker.html.twig | 35 ++++- 6 files changed, 69 insertions(+), 236 deletions(-) delete mode 100644 public/assets/js/tracker_map.js diff --git a/public/assets/js/dashboard_map.js b/public/assets/js/dashboard_map.js index 57d3c7a3..4691a11d 100644 --- a/public/assets/js/dashboard_map.js +++ b/public/assets/js/dashboard_map.js @@ -37,14 +37,16 @@ class DashboardMap { 'Streets': streets }; - // overlay layer - var overlayMaps = { - 'Available Riders' : this.layer_groups.rider_available, - 'JO Riders' : this.layer_groups.rider_active_jo, - 'Customers' : this.layer_groups.customer - } + if (this.options.display_overlay) { + // overlay layer + var overlayMaps = { + 'Available Riders' : this.layer_groups.rider_available, + 'JO Riders' : this.layer_groups.rider_active_jo, + 'Customers' : this.layer_groups.customer + } - L.control.layers(baseMaps, overlayMaps).addTo(this.map); + L.control.layers(baseMaps, overlayMaps).addTo(this.map); + } return this.map; } @@ -61,19 +63,22 @@ class DashboardMap { markers[id] = L.marker( [lat, lng], { icon: icon } - ).bindPopup('Loading...') - .addTo(layer_group); + ).addTo(layer_group); - // bind ajax for popup - markers[id].on('click', function(e) { - var popup = e.target.getPopup(); - var url = popup_url.replace('[id]', id); - console.log(url); - $.get(url).done(function(data) { - popup.setContent(data); - popup.update(); + if (my.options.enable_popup) { + markers[id].bindPopup('Loading...'); + + // bind ajax for popup + markers[id].on('click', function(e) { + var popup = e.target.getPopup(); + var url = popup_url.replace('[id]', id); + console.log(url); + $.get(url).done(function(data) { + popup.setContent(data); + popup.update(); + }); }); - }); + } } putCustomerMarker(id, lat, lng) { @@ -160,30 +165,6 @@ class DashboardMap { my.putRiderAvailableMarker(id, lat, lng); }); - /* - $.each(riders, function(rider_id, rider_data) { - // rider location - var point = rider_data['loc']; - var lat = point[0]; - var lng = point[1]; - - // customer location - var cloc = rider_data['cust_loc']; - var clat = cloc[0]; - var clng = cloc[1]; - - // create rider markers - if (rider_data['has_jo']) { - var jo_data = rider_data['jo']; - - // my.putCustomerMarker(jo_data['id'], clat, clng); - my.putRiderActiveJOMarker(rider_id, lat, lng); - } else { - my.putRiderAvailableMarker(rider_id, lat, lng); - } - }); - */ - // console.log(rider_markers); }); } diff --git a/public/assets/js/map_mqtt.js b/public/assets/js/map_mqtt.js index ebd401bb..6eac1f65 100644 --- a/public/assets/js/map_mqtt.js +++ b/public/assets/js/map_mqtt.js @@ -28,13 +28,17 @@ class MapEventHandler { var my = icontext.invocationContext; // subscribe to rider locations - console.log('subscribing to ' + my.options.channels.rider_location); - my.mqtt.subscribe(my.options.channels.rider_location); + if (my.options.track_rider) { + console.log('subscribing to ' + my.options.channels.rider_location); + my.mqtt.subscribe(my.options.channels.rider_location); + } // subscribe to jo locations - console.log('subscribing to ' + my.options.channels.jo_location); - my.mqtt.subscribe(my.options.channels.jo_location); - my.mqtt.subscribe(my.options.channels.jo_status); + if (my.options.track_jo) { + console.log('subscribing to ' + my.options.channels.jo_location); + my.mqtt.subscribe(my.options.channels.jo_location); + my.mqtt.subscribe(my.options.channels.jo_status); + } } onMessage(msg) { diff --git a/public/assets/js/tracker_map.js b/public/assets/js/tracker_map.js deleted file mode 100644 index ecf3df41..00000000 --- a/public/assets/js/tracker_map.js +++ /dev/null @@ -1,182 +0,0 @@ -class DashboardMap { - constructor(options, rider_markers, cust_markers) { - this.options = options; - this.rider_markers = rider_markers; - this.cust_markers = cust_markers; - - // layer groups - this.layer_groups = { - 'rider_available': L.layerGroup(), - 'rider_active_jo': L.layerGroup(), - 'customer': L.layerGroup() - }; - } - - initialize() { - // main map - this.map = L.map(this.options.div_id).setView( - [this.options.center_lat, this.options.center_lng], - this.options.zoom - ); - - // add tile layer - var streets = L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}', { - attribution: 'Map data © OpenStreetMap contributors, CC-BY-SA, Imagery © Mapbox', - maxZoom: 18, - id: 'mapbox/streets-v11', - accessToken: this.options.access_token - }).addTo(this.map); - - // layer groups - this.layer_groups.rider_available.addTo(this.map); - this.layer_groups.rider_active_jo.addTo(this.map); - this.layer_groups.customer.addTo(this.map); - - // base layer - var baseMaps = { - 'Streets': streets - }; - - // overlay layer - return this.map; - } - - putMarker(id, lat, lng, markers, icon, layer_group, popup_url) { - var my = this; - // existing marker - if (markers.hasOwnProperty(id)) { - markers[id].setLatLng(L.latLng(lat, lng)); - return; - } - - // new marker - markers[id] = L.marker( - [lat, lng], - { icon: icon } - ).bindPopup('Loading...') - .addTo(layer_group); - - // bind ajax for popup - markers[id].on('click', function(e) { - var popup = e.target.getPopup(); - var url = popup_url.replace('[id]', id); - console.log(url); - $.get(url).done(function(data) { - popup.setContent(data); - popup.update(); - }); - }); - } - - putCustomerMarker(id, lat, lng) { - this.putMarker( - id, - lat, - lng, - this.cust_markers, - this.options.icons.customer, - this.layer_groups.customer, - this.options.cust_popup_url - ); - } - - removeCustomerMarker(id) { - console.log('removing customer marker for ' + id); - var layer_group = this.layer_groups.customer; - var markers = this.cust_markers; - - // no customer marker with that id - if (!markers.hasOwnProperty(id)) { - console.log('no such marker to remove'); - return; - } - - layer_group.removeLayer(markers[id]); - } - - putRiderAvailableMarker(id, lat, lng) { - this.putMarker( - id, - lat, - lng, - this.rider_markers, - this.options.icons.rider_available, - this.layer_groups.rider_available, - this.options.rider_popup_url - ); - } - - putRiderActiveJOMarker(id, lat, lng) { - this.putMarker( - id, - lat, - lng, - this.rider_markers, - this.options.icons.rider_active_jo, - this.layer_groups.rider_active_jo, - this.options.rider_popup_url - ); - } - - loadLocations(location_url) { - console.log(this.rider_markers); - var my = this; - $.ajax({ - url: location_url, - }).done(function(response) { - // clear all markers - my.layer_groups.rider_available.clearLayers(); - my.layer_groups.rider_active_jo.clearLayers(); - my.layer_groups.customer.clearLayers(); - - // get riders and job orders - var riders = response.riders; - var jos = response.jos; - - // job orders - $.each(jos, function(id, data) { - var lat = data.latitude; - var lng = data.longitude; - - my.putCustomerMarker(id, lat, lng); - }); - - // riders - $.each(riders, function(id, data) { - var lat = data.latitude; - var lng = data.longitude; - - if (data.has_jo) - my.putRiderActiveJOMarker(id, lat, lng); - else - my.putRiderAvailableMarker(id, lat, lng); - }); - - /* - $.each(riders, function(rider_id, rider_data) { - // rider location - var point = rider_data['loc']; - var lat = point[0]; - var lng = point[1]; - - // customer location - var cloc = rider_data['cust_loc']; - var clat = cloc[0]; - var clng = cloc[1]; - - // create rider markers - if (rider_data['has_jo']) { - var jo_data = rider_data['jo']; - - // my.putCustomerMarker(jo_data['id'], clat, clng); - my.putRiderActiveJOMarker(rider_id, lat, lng); - } else { - my.putRiderAvailableMarker(rider_id, lat, lng); - } - }); - */ - - // console.log(rider_markers); - }); - } -} diff --git a/src/Controller/JobOrderController.php b/src/Controller/JobOrderController.php index c92638a4..5d6127e3 100644 --- a/src/Controller/JobOrderController.php +++ b/src/Controller/JobOrderController.php @@ -940,9 +940,12 @@ class JobOrderController extends Controller if ($jo === null) return new Response('No job order data'); + $rider = $jo->getRider(); + // get map $params['jo'] = $jo; - $params['rider'] = $jo->getRider(); + $params['rider'] = $rider; + $params['rider_pos'] = $rider_tracker->getRiderLocation($rider->getID()); $params['service_type'] = CMBServiceType::getName($jo->getServiceType()); $params['map_js_file'] = $gis_manager->getJSInitFile(); diff --git a/templates/home.html.twig b/templates/home.html.twig index 3bcb6c3b..975b0ea5 100644 --- a/templates/home.html.twig +++ b/templates/home.html.twig @@ -23,6 +23,8 @@ function initMap(r_markers, c_markers, icons) { var default_lng = {% trans %}default_long{% endtrans %}; var options = { + 'display_overlay': true, + 'enable_popup': true, 'access_token': 'pk.eyJ1Ijoia2NvcmRlcm8iLCJhIjoiY2szbzA3ZHdsMDZxdTNsbGl4ZGNnN2VxaSJ9.LRzAe3RlV8sIP1N1x0chdw', 'div_id': 'dashboard_map', 'center_lat': default_lat, @@ -43,6 +45,8 @@ function initMap(r_markers, c_markers, icons) { function initEventHandler(dashmap) { var options = { + 'track_jo': true, + 'track_rider': true, 'channels': { 'rider_location': 'rider/+/location', 'jo_location': 'jo/+/location', diff --git a/templates/job-order/tracker.html.twig b/templates/job-order/tracker.html.twig index 42f18746..ca08d381 100644 --- a/templates/job-order/tracker.html.twig +++ b/templates/job-order/tracker.html.twig @@ -22,7 +22,7 @@ {% block scripts %} - + {{ include('map/' ~ map_js_file) }} -{% endblock %} \ No newline at end of file +{% endblock %} From 41a6801c1975096bd3e7110484fc70f2fc6f0840 Mon Sep 17 00:00:00 2001 From: Kendrick Chan Date: Wed, 29 Jan 2020 01:20:40 +0800 Subject: [PATCH 26/30] Fix string concat bug in jo tracking page #270 --- templates/job-order/tracker.html.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/job-order/tracker.html.twig b/templates/job-order/tracker.html.twig index ca08d381..661bcc64 100644 --- a/templates/job-order/tracker.html.twig +++ b/templates/job-order/tracker.html.twig @@ -7,7 +7,7 @@
- +
Order #{{ jo.getID }}
{{ rider.getFullName }}
From 234d993d8f4529ebbfa29942ce4311ad3b0ba709 Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Wed, 29 Jan 2020 09:03:56 +0000 Subject: [PATCH 27/30] Add deletion of compatible battery from vehicle. #317 --- src/Controller/VehicleController.php | 39 ++++++++++++++++++++++- templates/vehicle/form.html.twig | 47 ++++++++++++++++++++++++++-- 2 files changed, 83 insertions(+), 3 deletions(-) diff --git a/src/Controller/VehicleController.php b/src/Controller/VehicleController.php index db82b18b..4c6a5484 100644 --- a/src/Controller/VehicleController.php +++ b/src/Controller/VehicleController.php @@ -241,6 +241,10 @@ class VehicleController extends Controller if (empty($row)) throw $this->createNotFoundException('The item does not exist'); + // get current batteries of vehicle to be updated + $current_batteries = $row->getBatteries(); + error_log('count of current batteries ' . count($current_batteries)); + // set and save values $row->setMake($req->request->get('make')) ->setModelYearFrom($req->request->get('model_year_from')) @@ -251,7 +255,6 @@ class VehicleController extends Controller else $row->setDisplayMobile(false); - // validate $errors = $validator->validate($row); @@ -272,6 +275,40 @@ class VehicleController extends Controller else $row->setManufacturer($manufacturer); + // custom validation for batteries + $batteries = $req->request->get('batteries'); + if (!empty($batteries)) + { + // need to check if a battery has been removed + if (count($current_batteries) > count($batteries)) + { + error_log('A battery has been removed'); + } + foreach ($batteries as $battery_id) + { + $battery = $em->getRepository(Battery::class)->find($battery_id); + + if (!empty($battery)) + { + error_log('battery id ' . $battery->getID()); + } + } + } + else + { + // no more battery compatible with vehicle + foreach ($current_batteries as $c_battery) + { + $cbatt_id = $c_battery->getID(); + $battery = $em->getRepository(Battery::class)->find($cbatt_id); + + if (!empty($battery)) + { + $battery->removeVehicle($row); + } + } + } + // check if any errors were found if (!empty($error_array)) { // return validation failure response diff --git a/templates/vehicle/form.html.twig b/templates/vehicle/form.html.twig index bd6fa482..4669d1c2 100644 --- a/templates/vehicle/form.html.twig +++ b/templates/vehicle/form.html.twig @@ -135,13 +135,23 @@ $(function() { $("#row-form").submit(function(e) { var form = $(this); + var formdata = form.serialize(); e.preventDefault(); + // add battery data + bdata = ''; + $.each(battRows, function(index, battery) { + bdata += "&batteries%5B%5D=" + battery.id; + }); + + // append to form data + formdata += bdata; + $.ajax({ method: "POST", url: form.prop('action'), - data: form.serialize() + data: formdata }).done(function(response) { // remove all error classes removeErrors(); @@ -192,6 +202,7 @@ $(function() { }); var battRows = []; + var batteryIds = []; {% for batt in obj.getBatteries %} trow = { @@ -202,8 +213,30 @@ $(function() { }; battRows.push(trow); + batteryIds.push({{ batt.getID }}); {% endfor %} + // remove battery from table + $(document).on('click', '.btn-delete', function(e) { + var btn = $(this); + var id = $(this).data('id'); + + $.each(battRows, function(index, battery) { + if (battery.id == id) { + battRows.splice(index, 1); + return false; + } + }); + + // remove from battery ids + batteryIds.splice(batteryIds.indexOf(id), 1); + + // reload table + battTable.row(btn.parents('tr')).remove(); + battTable.originalDataSet = battRows; + battTable.reload(); + }); + // battery data table var battOptions = { data: { @@ -235,7 +268,17 @@ $(function() { { field: 'sell_price', title: 'Price' - } + }, + { + field: 'Actions', + width: 70, + title: 'Actions', + sortable: false, + overflow: 'visible', + template: function (row, index, datatable) { + return ''; + }, + } ], pagination: false }; From e8e97bcd6b54b307fe43b56bb07cd5f95d0984d8 Mon Sep 17 00:00:00 2001 From: Kendrick Chan Date: Wed, 29 Jan 2020 19:13:49 +0800 Subject: [PATCH 28/30] Fix jo link in rider and customer popup in dashboard #270 --- templates/job-order/popup.html.twig | 9 +++++++-- templates/rider/popup.html.twig | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/templates/job-order/popup.html.twig b/templates/job-order/popup.html.twig index 509c7296..d4e67d48 100644 --- a/templates/job-order/popup.html.twig +++ b/templates/job-order/popup.html.twig @@ -2,7 +2,12 @@ {% set cv = jo.getCustomerVehicle %} {{ cust.getNameDisplay }}
{{ cv.getPlateNumber }}
-Job Order #{{ jo.getID }}
+Job Order #{{ jo.getID }}
{{ jo.getServiceTypeName }}
{{ jo.getStatusText }} - +{% if jo.getRider != null %} +

+{% set rider = jo.getRider %} +{{ rider.getFullName }}
+{{ rider.getPlateNumber }} +{% endif %} diff --git a/templates/rider/popup.html.twig b/templates/rider/popup.html.twig index 8807d6c7..13b201d0 100644 --- a/templates/rider/popup.html.twig +++ b/templates/rider/popup.html.twig @@ -4,7 +4,7 @@ {% set cust = jo.getCustomer %} {% set cv = jo.getCustomerVehicle %}
-Job Order #{{ jo.getID }}
+Job Order #{{ jo.getID }}
{{ jo.getServiceTypeName }}
{{ jo.getStatusText }}

{{ cust.getNameDisplay }}
From 33fdc857f23670524cf0b466a40faa8fd826b644 Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Wed, 29 Jan 2020 11:16:07 +0000 Subject: [PATCH 29/30] Add deletion of battery from vehicle when vehicle has more than one compatible battery. #317 --- src/Controller/VehicleController.php | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/Controller/VehicleController.php b/src/Controller/VehicleController.php index 4c6a5484..9fb93544 100644 --- a/src/Controller/VehicleController.php +++ b/src/Controller/VehicleController.php @@ -282,15 +282,25 @@ class VehicleController extends Controller // need to check if a battery has been removed if (count($current_batteries) > count($batteries)) { - error_log('A battery has been removed'); - } - foreach ($batteries as $battery_id) - { - $battery = $em->getRepository(Battery::class)->find($battery_id); - - if (!empty($battery)) + // battery/batteries have been removed + foreach ($current_batteries as $cbatt) { - error_log('battery id ' . $battery->getID()); + $cbatt_id = $cbatt->getID(); + if (in_array($cbatt_id, $batteries)) + { + // do nothing, move to next element + continue; + } + else + { + // cbatt_id has been deleted + $battery = $em->getRepository(Battery::class)->find($cbatt_id); + + if (!empty($battery)) + { + $battery->removeVehicle($row); + } + } } } } From dab8986ad1d7eb26dec2e8ce395f8706bec896a0 Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Thu, 30 Jan 2020 00:31:01 +0000 Subject: [PATCH 30/30] Remove or comment out error_logs meant for debugging purposes. #318 --- src/Command/CreateCustomerFromWarrantyCommand.php | 3 --- src/Controller/HubController.php | 2 +- src/Controller/StaticContentController.php | 2 -- src/Controller/VehicleController.php | 1 - src/Controller/WarrantyController.php | 4 ++-- src/Service/InvoiceCreator.php | 4 ++-- src/Service/WarrantyHandler.php | 8 ++++---- 7 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/Command/CreateCustomerFromWarrantyCommand.php b/src/Command/CreateCustomerFromWarrantyCommand.php index e644fc06..91684217 100644 --- a/src/Command/CreateCustomerFromWarrantyCommand.php +++ b/src/Command/CreateCustomerFromWarrantyCommand.php @@ -313,17 +313,14 @@ class CreateCustomerFromWarrantyCommand extends Command protected function loadCustomers() { - error_log('starting query...'); // get all customers $customers = $this->em->getRepository(Customer::class)->findAll(); $cust_q = $this->em->createQuery('select c from App\Entity\Customer c'); $cust_iter = $q->iterate(); - error_log('looping through query...'); $this->cust_index = []; foreach ($cust_iter as $customer) { - error_log('here'); $mobile = trim($customer->getPhoneMobile()); if (!empty($mobile)) { diff --git a/src/Controller/HubController.php b/src/Controller/HubController.php index eb1faa73..115b7ef8 100644 --- a/src/Controller/HubController.php +++ b/src/Controller/HubController.php @@ -163,7 +163,7 @@ class HubController extends Controller { $this->denyAccessUnlessGranted('hub.add', null, 'No access.'); - error_log($req->request->get('time_open')); + //error_log($req->request->get('time_open')); // create new object $em = $this->getDoctrine()->getManager(); diff --git a/src/Controller/StaticContentController.php b/src/Controller/StaticContentController.php index 395736d4..44c77595 100644 --- a/src/Controller/StaticContentController.php +++ b/src/Controller/StaticContentController.php @@ -148,7 +148,6 @@ class StaticContentController extends Controller $result = $em->getRepository(StaticContent::class)->find($id); if ($result != null) { - error_log($id); $error_array['id'] = 'Duplicate ID exists.'; } @@ -226,7 +225,6 @@ class StaticContentController extends Controller $result = $em->getRepository(StaticContent::class)->find($id); if ($result != null) { - error_log($id); $error_array['id'] = 'Duplicate ID exists.'; } diff --git a/src/Controller/VehicleController.php b/src/Controller/VehicleController.php index 9fb93544..2df6ec74 100644 --- a/src/Controller/VehicleController.php +++ b/src/Controller/VehicleController.php @@ -243,7 +243,6 @@ class VehicleController extends Controller // get current batteries of vehicle to be updated $current_batteries = $row->getBatteries(); - error_log('count of current batteries ' . count($current_batteries)); // set and save values $row->setMake($req->request->get('make')) diff --git a/src/Controller/WarrantyController.php b/src/Controller/WarrantyController.php index 40afec1a..ae6da43f 100644 --- a/src/Controller/WarrantyController.php +++ b/src/Controller/WarrantyController.php @@ -567,7 +567,7 @@ class WarrantyController extends Controller { // call service to check if warranty details is incomplete and then update warranty // using details from csv file - error_log('Updating warranty for ' . $warr->getID()); + // error_log('Updating warranty for ' . $warr->getID()); $wh->updateWarranty($warr, $first_name, $last_name, $mobile_number, $batt_list, $date_purchase); } } @@ -582,7 +582,7 @@ class WarrantyController extends Controller continue; } - error_log('Creating warranty for serial ' . $serial . ' and plate number ' . $plate_number); + // error_log('Creating warranty for serial ' . $serial . ' and plate number ' . $plate_number); $wh->createWarranty($serial, $plate_number, $first_name, $last_name, $mobile_number, $batt_list, $date_purchase, $warranty_class); } diff --git a/src/Service/InvoiceCreator.php b/src/Service/InvoiceCreator.php index 1c6a74a7..4576b531 100644 --- a/src/Service/InvoiceCreator.php +++ b/src/Service/InvoiceCreator.php @@ -355,7 +355,7 @@ class InvoiceCreator $diesel_price = 220; $fuel = new InvoiceItem(); - error_log('fuel type - ' . $ftype); + //error_log('fuel type - ' . $ftype); switch ($ftype) { case FuelType::GAS: @@ -443,7 +443,7 @@ class InvoiceCreator $this->processOverheat($total, $invoice, $cv, $has_coolant); break; case ServiceType::EMERGENCY_REFUEL: - error_log('processing refuel'); + //error_log('processing refuel'); $ftype = $criteria->getCustomerVehicle()->getFuelType(); $this->processRefuel($total, $invoice, $cv); break; diff --git a/src/Service/WarrantyHandler.php b/src/Service/WarrantyHandler.php index 641658d7..281464bf 100644 --- a/src/Service/WarrantyHandler.php +++ b/src/Service/WarrantyHandler.php @@ -93,7 +93,7 @@ class WarrantyHandler public function updateCustomerVehicle($serial, $batteries, $plate_number, $date_expire) { // find customer vehicle using plate number - error_log('Finding customer vehicle with plate number ' . $plate_number); + // error_log('Finding customer vehicle with plate number ' . $plate_number); $cv_q = $this->em->createQuery('select count(cv) from App\Entity\CustomerVehicle cv where cv.plate_number = :plate_number') ->setParameter('plate_number', $plate_number); $cv_result = $cv_q->getSingleScalarResult(); @@ -288,7 +288,7 @@ class WarrantyHandler if (empty($warranty_class)) { - error_log('Warranty class is empty for warranty id ' . $warr->getID()); + //error_log('Warranty class is empty for warranty id ' . $warr->getID()); return null; } @@ -301,12 +301,12 @@ class WarrantyHandler { if ($batt_model == null) { - error_log('Battery model is null for warranty id ' . $warr->getID()); + //error_log('Battery model is null for warranty id ' . $warr->getID()); return null; } if ($batt_size == null) { - error_log('Battery size is null for warranty id ' . $warr->getID()); + //error_log('Battery size is null for warranty id ' . $warr->getID()); return null; }