From fb52b59ad9eb08e34f1f5e639cca91b519f53511 Mon Sep 17 00:00:00 2001 From: Kendrick Chan Date: Fri, 24 Jan 2020 03:17:42 +0800 Subject: [PATCH 01/16] 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/16] 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/16] 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/16] 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/16] 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/16] 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/16] 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/16] 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/16] 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/16] 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/16] 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 bdb306e5e1459ee7960eb07c505cf3385745effb Mon Sep 17 00:00:00 2001 From: Kendrick Chan Date: Tue, 28 Jan 2020 04:31:41 +0800 Subject: [PATCH 12/16] 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 13/16] 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 14/16] 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 15/16] 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 16/16] 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; }