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/config/services.yaml b/config/services.yaml
index 4249102e..0b7669fd 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:
@@ -86,7 +87,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 +204,28 @@ services:
App\Service\GISManagerInterface: "@App\\Service\\GISManager\\OpenStreet"
#App\Service\GISManagerInterface: "@App\\Service\\GISManager\\Google"
+ App\EventListener\JobOrderActiveCacheListener:
+ arguments:
+ $jo_cache: "@App\\Service\\JobOrderCache"
+ $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'
+
+ App\Service\JobOrderCache:
+ arguments:
+ $redis_prov: "@App\\Service\\RedisClientProvider"
+ $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
new file mode 100644
index 00000000..57d3c7a3
--- /dev/null
+++ b/public/assets/js/dashboard_map.js
@@ -0,0 +1,190 @@
+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
+ 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);
+
+ 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/public/assets/js/map_mqtt.js b/public/assets/js/map_mqtt.js
new file mode 100644
index 00000000..ebd401bb
--- /dev/null
+++ b/public/assets/js/map_mqtt.js
@@ -0,0 +1,110 @@
+class MapEventHandler {
+ constructor(options, dashmap) {
+ this.options = options;
+ this.dashmap = dashmap;
+ }
+
+ 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,
+ invocationContext: this,
+ onSuccess: this.onConnect.bind(this),
+ };
+
+ this.mqtt.onMessageArrived = this.onMessage.bind(this);
+
+ console.log('connecting to mqtt server...');
+ this.mqtt.connect(options);
+ }
+
+ onConnect(icontext) {
+ console.log('mqtt connected!');
+ 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);
+
+ // 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) {
+ // 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;
+ case "jo":
+ this.handleJobOrder(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);
+ var 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]);
+
+ this.dashmap.putRiderAvailableMarker(chan_split[1], lat, lng);
+ break;
+ }
+ }
+
+ handleJobOrder(chan_split, payload) {
+ console.log("jo message");
+ var id = chan_split[1];
+ switch (chan_split[2]) {
+ case "location":
+ // var my = this;
+ 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;
+ case "status":
+ switch (payload) {
+ case 'cancel':
+ case 'fulfill':
+ case 'delete':
+ this.dashmap.removeCustomerMarker(id);
+ break;
+ }
+ }
+ }
+}
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);
+ }
+ }
+}
diff --git a/src/Controller/HomeController.php b/src/Controller/HomeController.php
index 3a3e5173..ca5654b4 100644
--- a/src/Controller/HomeController.php
+++ b/src/Controller/HomeController.php
@@ -9,6 +9,8 @@ use Doctrine\ORM\EntityManagerInterface;
use App\Service\RiderTracker;
use App\Service\GISManagerInterface;
+use App\Service\JobOrderCache;
+use App\Service\RiderCache;
use App\Entity\Rider;
@@ -18,8 +20,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,11 +32,34 @@ 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)
{
- // TODO: get active riders from cache
- // TODO: get active JOs from cache
+ $active_jos = $jo_cache->getAllActiveJobOrders();
+
+ // get active JOs from cache
+ }
+
+ public function getRiderLocations(JobOrderCache $jo_cache, RiderCache $rider_cache, EntityManagerInterface $em, RiderTracker $rider_tracker)
+ {
+ // get active JOs from cache
+ $active_jos = $jo_cache->getAllActiveJobOrders();
+ $riders = $rider_cache->getAllActiveRiders();
+
+ // 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 = [];
@@ -81,9 +109,11 @@ class HomeController extends Controller
];
}
+ */
return $this->json([
- 'riders' => $locations,
+ 'jos' => $active_jos,
+ '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/EventListener/JobOrderActiveCacheListener.php b/src/EventListener/JobOrderActiveCacheListener.php
new file mode 100644
index 00000000..541ce158
--- /dev/null
+++ b/src/EventListener/JobOrderActiveCacheListener.php
@@ -0,0 +1,105 @@
+jo_cache = $jo_cache;
+ $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:
+ $this->processInactiveJO($jo, 'cancel');
+ break;
+ case JOStatus::FULFILLED:
+ $this->processInactiveJO($jo, 'fulfill');
+ break;
+ }
+ }
+
+ // 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:
+ $this->processInactiveJO($jo, 'cancel');
+ break;
+ case JOStatus::FULFILLED:
+ $this->processInactiveJO($jo, 'fulfill');
+ break;
+ }
+ }
+
+ // when a job order is deleted
+ public function postRemove(JobOrder $jo, LifecycleEventArgs $args)
+ {
+ $this->processInactiveJO($jo, 'delete');
+ }
+
+ protected function processActiveJO($jo)
+ {
+ // save in cache
+ $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',
+ $coords->getLatitude() . ':' . $coords->getLongitude()
+ );
+ }
+
+ protected function processInactiveJO($jo, $status = 'cancel')
+ {
+ // 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
new file mode 100644
index 00000000..488db29d
--- /dev/null
+++ b/src/Service/JobOrderCache.php
@@ -0,0 +1,66 @@
+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,
+ 41000,
+ '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;
+ }
+
+ public function removeActiveJobOrder(JobOrder $jo)
+ {
+ $this->redis->zrem(
+ $this->active_jo_key,
+ $jo->getID()
+ );
+ }
+}
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/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/src/Service/RedisClientProvider.php b/src/Service/RedisClientProvider.php
index 04ac1f80..80dd06fa 100644
--- a/src/Service/RedisClientProvider.php
+++ b/src/Service/RedisClientProvider.php
@@ -11,34 +11,46 @@ 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->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)
+ {
+ $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;
}
}
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);
+ }
+}
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..3bcb6c3b 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="">
-
-
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
diff --git a/utils/mqtt_rider/rider_location_cache.py b/utils/mqtt_rider/rider_location_cache.py
new file mode 100644
index 00000000..b07d4acc
--- /dev/null
+++ b/utils/mqtt_rider/rider_location_cache.py
@@ -0,0 +1,41 @@
+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 on_publish(client, userdata, mid):
+ pass
+
+def on_message(client, userdata, message):
+ redis_conn = userdata['redis']
+
+ topic_split = message.topic.split('/')
+ if topic_split[0] != 'rider':
+ return;
+ payload_split = message.payload.split(':')
+
+ rider_long = str(payload_split[1])
+ rider_lat = str(payload_split[0])
+
+ # set the location
+ 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
new file mode 100644
index 00000000..e8968436
Binary files /dev/null and b/utils/mqtt_rider/rider_location_cache.pyc differ
diff --git a/utils/mqtt_rider/riderloc.py b/utils/mqtt_rider/riderloc.py
new file mode 100644
index 00000000..2b810cca
--- /dev/null
+++ b/utils/mqtt_rider/riderloc.py
@@ -0,0 +1,26 @@
+import paho.mqtt.client as mqtt
+import rider_location_cache as rlc
+import ssl
+import redis
+import logging
+
+redis_client = redis.StrictRedis(host='localhost', port=6379, db=0)
+userdata = {'redis': redis_client}
+
+client = mqtt.Client("", True, userdata)
+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/mqtt_sender/mqtt_sender.py b/utils/mqtt_sender/mqtt_sender.py
index 54349755..f610b3db 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,29 +9,23 @@ 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:
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])
@@ -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()
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)