@fleetbase/fleetops-engine 0.6.12 → 0.6.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/addon/components/customer/orders.js +6 -8
- package/addon/components/layout/fleet-ops-sidebar.js +8 -0
- package/addon/components/live-map.js +6 -8
- package/addon/components/order-config/fields-editor.js +1 -1
- package/addon/components/route-optimization-engine-select-button.hbs +25 -0
- package/addon/components/route-optimization-engine-select-button.js +13 -0
- package/addon/controllers/operations/orders/index/new.js +52 -150
- package/addon/controllers/operations/orders/index/view.js +8 -9
- package/addon/controllers/settings/routing.js +47 -0
- package/addon/engine.js +27 -0
- package/addon/routes/application.js +8 -0
- package/addon/routes/operations/orders/index/view.js +12 -3
- package/addon/routes/settings/routing.js +3 -0
- package/addon/routes.js +1 -0
- package/addon/services/leaflet-router-control.js +64 -0
- package/addon/services/osrm.js +46 -0
- package/addon/services/route-optimization-interface.js +9 -0
- package/addon/services/route-optimization.js +67 -0
- package/addon/templates/operations/orders/index/new.hbs +21 -16
- package/addon/templates/settings/routing.hbs +32 -0
- package/app/components/route-optimization-engine-select-button.js +1 -0
- package/app/controllers/settings/routing.js +1 -0
- package/app/routes/settings/routing.js +1 -0
- package/app/services/leaflet-router-control.js +1 -0
- package/app/services/osrm.js +1 -0
- package/app/services/route-optimization-interface.js +1 -0
- package/app/services/route-optimization.js +1 -0
- package/app/templates/settings/routing.js +1 -0
- package/composer.json +1 -1
- package/extension.json +1 -1
- package/package.json +2 -2
- package/server/config/api.php +10 -1
- package/server/src/Auth/Schemas/FleetOps.php +5 -0
- package/server/src/Events/EntityActivityChanged.php +118 -0
- package/server/src/Events/EntityCompleted.php +118 -0
- package/server/src/Events/OrderCanceled.php +2 -2
- package/server/src/Events/OrderCompleted.php +2 -2
- package/server/src/Events/OrderDispatched.php +2 -2
- package/server/src/Events/OrderDriverAssigned.php +1 -1
- package/server/src/Events/OrderFailed.php +2 -2
- package/server/src/Events/OrderReady.php +1 -1
- package/server/src/Events/OrderStarted.php +1 -1
- package/server/src/Events/WaypointActivityChanged.php +2 -2
- package/server/src/Events/WaypointCompleted.php +2 -2
- package/server/src/Http/Controllers/Internal/v1/OrderController.php +1 -1
- package/server/src/Http/Controllers/Internal/v1/SettingController.php +30 -0
- package/server/src/Listeners/NotifyOrderEvent.php +1 -0
- package/server/src/Models/Order.php +7 -1
- package/server/src/Models/Payload.php +19 -8
- package/server/src/Models/Place.php +49 -13
- package/server/src/routes.php +2 -0
- package/translations/en-us.yaml +5 -0
|
@@ -6,8 +6,7 @@ import { isArray } from '@ember/array';
|
|
|
6
6
|
import { later } from '@ember/runloop';
|
|
7
7
|
import { debug } from '@ember/debug';
|
|
8
8
|
import { task, timeout } from 'ember-concurrency';
|
|
9
|
-
import {
|
|
10
|
-
import getRoutingHost from '@fleetbase/ember-core/utils/get-routing-host';
|
|
9
|
+
import { Control as RoutingControl } from '@fleetbase/leaflet-routing-machine';
|
|
11
10
|
import engineService from '@fleetbase/ember-core/decorators/engine-service';
|
|
12
11
|
import registerComponent from '@fleetbase/ember-core/utils/register-component';
|
|
13
12
|
import OrderProgressCardComponent from '../order-progress-card';
|
|
@@ -26,6 +25,7 @@ export default class CustomerOrdersComponent extends Component {
|
|
|
26
25
|
@service modalsManager;
|
|
27
26
|
@service customerSession;
|
|
28
27
|
@service hostRouter;
|
|
28
|
+
@service leafletRouterControl;
|
|
29
29
|
@engineService('@fleetbase/fleetops-engine') movementTracker;
|
|
30
30
|
@engineService('@fleetbase/fleetops-engine') location;
|
|
31
31
|
@tracked orders = [];
|
|
@@ -226,7 +226,6 @@ export default class CustomerOrdersComponent extends Component {
|
|
|
226
226
|
|
|
227
227
|
@action displayOrderRoute() {
|
|
228
228
|
const waypoints = this.getRouteCoordinatesFromOrder(this.selectedOrder);
|
|
229
|
-
const routingHost = getRoutingHost();
|
|
230
229
|
if (this.cannotRouteWaypoints(waypoints)) {
|
|
231
230
|
return;
|
|
232
231
|
}
|
|
@@ -240,15 +239,14 @@ export default class CustomerOrdersComponent extends Component {
|
|
|
240
239
|
debug(`Leaflet Map Error: ${error.message}`);
|
|
241
240
|
}
|
|
242
241
|
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
profile: 'driving',
|
|
246
|
-
});
|
|
242
|
+
const routingService = this.currentUser.getOption('routing', { router: 'osrm' }).router;
|
|
243
|
+
const { router, formatter } = this.leafletRouterControl.get(routingService);
|
|
247
244
|
|
|
248
245
|
this.routeControl = new RoutingControl({
|
|
249
|
-
fitSelectedRoutes: false,
|
|
250
246
|
router,
|
|
247
|
+
formatter,
|
|
251
248
|
waypoints,
|
|
249
|
+
fitSelectedRoutes: false,
|
|
252
250
|
alternativeClassName: 'hidden',
|
|
253
251
|
addWaypoints: false,
|
|
254
252
|
markerOptions: {
|
|
@@ -162,6 +162,14 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
|
|
|
162
162
|
permission: 'fleet-ops view notification-settings',
|
|
163
163
|
visible: this.abilities.can('fleet-ops see notification-settings'),
|
|
164
164
|
},
|
|
165
|
+
{
|
|
166
|
+
intl: 'fleet-ops.component.layout.fleet-ops-sidebar.routing',
|
|
167
|
+
title: this.intl.t('fleet-ops.component.layout.fleet-ops-sidebar.routing'),
|
|
168
|
+
icon: 'route',
|
|
169
|
+
route: 'settings.routing',
|
|
170
|
+
permission: 'fleet-ops view routing-settings',
|
|
171
|
+
visible: this.abilities.can('fleet-ops see routing-settings'),
|
|
172
|
+
},
|
|
165
173
|
];
|
|
166
174
|
|
|
167
175
|
const createPanel = (intl, routePrefix, items = []) => ({
|
|
@@ -9,8 +9,7 @@ import { later } from '@ember/runloop';
|
|
|
9
9
|
import { debug } from '@ember/debug';
|
|
10
10
|
import { allSettled } from 'rsvp';
|
|
11
11
|
import { task } from 'ember-concurrency';
|
|
12
|
-
import {
|
|
13
|
-
import getRoutingHost from '@fleetbase/ember-core/utils/get-routing-host';
|
|
12
|
+
import { Control as RoutingControl } from '@fleetbase/leaflet-routing-machine';
|
|
14
13
|
import getWithDefault from '@fleetbase/ember-core/utils/get-with-default';
|
|
15
14
|
|
|
16
15
|
/**
|
|
@@ -37,6 +36,7 @@ export default class LiveMapComponent extends Component {
|
|
|
37
36
|
@service contextPanel;
|
|
38
37
|
@service leafletMapManager;
|
|
39
38
|
@service leafletContextmenuManager;
|
|
39
|
+
@service leafletRouterControl;
|
|
40
40
|
@service theme;
|
|
41
41
|
|
|
42
42
|
/**
|
|
@@ -1126,7 +1126,6 @@ export default class LiveMapComponent extends Component {
|
|
|
1126
1126
|
|
|
1127
1127
|
// create order route preview
|
|
1128
1128
|
const waypoints = this.getRouteCoordinatesFromOrder(order);
|
|
1129
|
-
const routingHost = getRoutingHost();
|
|
1130
1129
|
if (this.cannotRouteWaypoints(waypoints)) {
|
|
1131
1130
|
return;
|
|
1132
1131
|
}
|
|
@@ -1140,15 +1139,14 @@ export default class LiveMapComponent extends Component {
|
|
|
1140
1139
|
debug(`Leaflet Map Error: ${error.message}`);
|
|
1141
1140
|
}
|
|
1142
1141
|
|
|
1143
|
-
const
|
|
1144
|
-
|
|
1145
|
-
profile: 'driving',
|
|
1146
|
-
});
|
|
1142
|
+
const routingService = this.currentUser.getOption('routing', { router: 'osrm' }).router;
|
|
1143
|
+
const { router, formatter } = this.leafletRouterControl.get(routingService);
|
|
1147
1144
|
|
|
1148
1145
|
this.routeControl = new RoutingControl({
|
|
1149
|
-
fitSelectedRoutes: false,
|
|
1150
1146
|
router,
|
|
1147
|
+
formatter,
|
|
1151
1148
|
waypoints,
|
|
1149
|
+
fitSelectedRoutes: false,
|
|
1152
1150
|
alternativeClassName: 'hidden',
|
|
1153
1151
|
addWaypoints: false,
|
|
1154
1152
|
markerOptions: {
|
|
@@ -197,7 +197,7 @@ export default class OrderConfigFieldsEditorComponent extends Component {
|
|
|
197
197
|
}
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
-
/* eslint no-unused-vars
|
|
200
|
+
/* eslint-disable no-unused-vars */
|
|
201
201
|
@action sortMetaFieldOptions(metaField, el, target) {
|
|
202
202
|
// const { fields } = this;
|
|
203
203
|
// const { index } = el.dataset;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<DropdownButton
|
|
2
|
+
@text="Optimize Route"
|
|
3
|
+
@icon="caret-down"
|
|
4
|
+
@type="magic"
|
|
5
|
+
@size="sm"
|
|
6
|
+
@iconPrefix="fas"
|
|
7
|
+
@triggerClass="hidden md:flex"
|
|
8
|
+
@helpText="Optimize route using an integrated optimization engine"
|
|
9
|
+
@disabled={{@disabled}}
|
|
10
|
+
@isLoading={{@isLoading}}
|
|
11
|
+
@renderInPlace={{true}}
|
|
12
|
+
as |dd|
|
|
13
|
+
>
|
|
14
|
+
<div class="next-dd-menu" aria-labelledby="user-menu">
|
|
15
|
+
<div class="px-1">
|
|
16
|
+
{{#each this.routeOptimization.availableServices as |service|}}
|
|
17
|
+
<a href="javascript:;" class="next-dd-item" {{on "click" (dropdown-fn dd this.handleClick service)}}>
|
|
18
|
+
<div >
|
|
19
|
+
<div class="text-sm">{{service.name}}</div>
|
|
20
|
+
</div>
|
|
21
|
+
</a>
|
|
22
|
+
{{/each}}
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
</DropdownButton>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import Component from '@glimmer/component';
|
|
2
|
+
import { inject as service } from '@ember/service';
|
|
3
|
+
import { action } from '@ember/object';
|
|
4
|
+
|
|
5
|
+
export default class RouteOptimizationEngineSelectButtonComponent extends Component {
|
|
6
|
+
@service routeOptimization;
|
|
7
|
+
|
|
8
|
+
@action handleClick({ key }) {
|
|
9
|
+
if (typeof this.args.onClick === 'function') {
|
|
10
|
+
this.args.onClick(key);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -9,11 +9,9 @@ import { isBlank } from '@ember/utils';
|
|
|
9
9
|
import { dasherize } from '@ember/string';
|
|
10
10
|
import { next } from '@ember/runloop';
|
|
11
11
|
import { task } from 'ember-concurrency';
|
|
12
|
-
import {
|
|
12
|
+
import { Control as RoutingControl } from '@fleetbase/leaflet-routing-machine';
|
|
13
13
|
import { debug } from '@ember/debug';
|
|
14
|
-
import polyline from '@fleetbase/ember-core/utils/polyline';
|
|
15
14
|
import isNotEmpty from '@fleetbase/ember-core/utils/is-not-empty';
|
|
16
|
-
import getRoutingHost from '@fleetbase/ember-core/utils/get-routing-host';
|
|
17
15
|
import getWithDefault from '@fleetbase/ember-core/utils/get-with-default';
|
|
18
16
|
import isModel from '@fleetbase/ember-core/utils/is-model';
|
|
19
17
|
|
|
@@ -31,95 +29,22 @@ L.Bounds.prototype.intersects = function (bounds) {
|
|
|
31
29
|
export default class OperationsOrdersIndexNewController extends BaseController {
|
|
32
30
|
@controller('operations.orders.index') ordersController;
|
|
33
31
|
|
|
34
|
-
/**
|
|
35
|
-
* Inject the `modalsManager` service
|
|
36
|
-
*
|
|
37
|
-
* @var {Service}
|
|
38
|
-
*/
|
|
39
32
|
@service modalsManager;
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Inject the `notifications` service
|
|
43
|
-
*
|
|
44
|
-
* @var {Service}
|
|
45
|
-
*/
|
|
46
33
|
@service notifications;
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Inject the `loader` service
|
|
50
|
-
*
|
|
51
|
-
* @var {Service}
|
|
52
|
-
*/
|
|
53
34
|
@service loader;
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Inject the `currentUser` service
|
|
57
|
-
*
|
|
58
|
-
* @var {Service}
|
|
59
|
-
*/
|
|
60
35
|
@service currentUser;
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Inject the `hostRouter` service
|
|
64
|
-
*
|
|
65
|
-
* @var {Service}
|
|
66
|
-
*/
|
|
67
36
|
@service hostRouter;
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Inject the `fileQueue` service
|
|
71
|
-
*
|
|
72
|
-
* @var {Service}
|
|
73
|
-
*/
|
|
74
37
|
@service fileQueue;
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Inject the `intl` service
|
|
78
|
-
*
|
|
79
|
-
* @var {Service}
|
|
80
|
-
*/
|
|
81
38
|
@service intl;
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Inject the `fetch` service
|
|
85
|
-
*
|
|
86
|
-
* @var {Service}
|
|
87
|
-
*/
|
|
88
39
|
@service fetch;
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Inject the `store` service
|
|
92
|
-
*
|
|
93
|
-
* @var {Service}
|
|
94
|
-
*/
|
|
95
40
|
@service store;
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Inject the `contextPanel` service
|
|
99
|
-
*
|
|
100
|
-
* @var {Service}
|
|
101
|
-
*/
|
|
102
41
|
@service contextPanel;
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Inject the `universe` service
|
|
106
|
-
*
|
|
107
|
-
* @var {Service}
|
|
108
|
-
*/
|
|
109
42
|
@service universe;
|
|
43
|
+
@service routeOptimization;
|
|
44
|
+
@service leafletRouterControl;
|
|
45
|
+
@service osrm;
|
|
110
46
|
|
|
111
|
-
/**
|
|
112
|
-
* Create an OrderModel instance.
|
|
113
|
-
*
|
|
114
|
-
* @var {OrderModel}
|
|
115
|
-
*/
|
|
116
47
|
@tracked order = this.store.createRecord('order', { meta: [] });
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Create an PayloadModel instance.
|
|
120
|
-
*
|
|
121
|
-
* @var {OrderModel}
|
|
122
|
-
*/
|
|
123
48
|
@tracked payload = this.store.createRecord('payload');
|
|
124
49
|
@tracked driversQuery = {};
|
|
125
50
|
@tracked vehiclesQuery = {};
|
|
@@ -139,7 +64,6 @@ export default class OperationsOrdersIndexNewController extends BaseController {
|
|
|
139
64
|
@tracked isCreatingOrder = false;
|
|
140
65
|
@tracked isMultipleDropoffOrder = false;
|
|
141
66
|
@tracked isViewingRoutePreview = false;
|
|
142
|
-
@tracked isOptimizingRoute = false;
|
|
143
67
|
@tracked optimizedRouteMarkers = [];
|
|
144
68
|
@tracked optimizedRoutePolyline;
|
|
145
69
|
@tracked isFetchingQuotes = false;
|
|
@@ -191,14 +115,8 @@ export default class OperationsOrdersIndexNewController extends BaseController {
|
|
|
191
115
|
'application/x-tar',
|
|
192
116
|
];
|
|
193
117
|
|
|
194
|
-
get
|
|
195
|
-
|
|
196
|
-
return renderableComponents;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
get renderableEntityInputComponents() {
|
|
200
|
-
const renderableComponents = this.universe.getRenderableComponentsFromRegistry('fleet-ops:template:operations:orders:new:entities-input');
|
|
201
|
-
return renderableComponents;
|
|
118
|
+
get hasRouteOptimizationEngines() {
|
|
119
|
+
return this.routeOptimization.availableEngines.length > 1;
|
|
202
120
|
}
|
|
203
121
|
|
|
204
122
|
@not('isServicable') isNotServicable;
|
|
@@ -873,13 +791,12 @@ export default class OperationsOrdersIndexNewController extends BaseController {
|
|
|
873
791
|
const canPreviewRoute = this.routePreviewArray.length > 0;
|
|
874
792
|
|
|
875
793
|
if (canPreviewRoute) {
|
|
876
|
-
const
|
|
877
|
-
const router =
|
|
878
|
-
serviceUrl: `${routingHost}/route/v1`,
|
|
879
|
-
profile: 'driving',
|
|
880
|
-
});
|
|
794
|
+
const routingService = this.currentUser.getOption('routing', { router: 'osrm' }).router;
|
|
795
|
+
const { router, formatter } = this.leafletRouterControl.get(routingService);
|
|
881
796
|
|
|
882
797
|
this.previewRouteControl = new RoutingControl({
|
|
798
|
+
router,
|
|
799
|
+
formatter,
|
|
883
800
|
waypoints: this.routePreviewCoordinates,
|
|
884
801
|
alternativeClassName: 'hidden',
|
|
885
802
|
addWaypoints: false,
|
|
@@ -892,7 +809,6 @@ export default class OperationsOrdersIndexNewController extends BaseController {
|
|
|
892
809
|
iconAnchor: [12, 41],
|
|
893
810
|
}),
|
|
894
811
|
},
|
|
895
|
-
router,
|
|
896
812
|
}).addTo(leafletMap);
|
|
897
813
|
|
|
898
814
|
this.previewRouteControl.on('routesfound', (event) => {
|
|
@@ -939,68 +855,54 @@ export default class OperationsOrdersIndexNewController extends BaseController {
|
|
|
939
855
|
}
|
|
940
856
|
}
|
|
941
857
|
|
|
942
|
-
@task *
|
|
943
|
-
this.isOptimizingRoute = true;
|
|
944
|
-
|
|
945
|
-
// Build the coordinate list we’ll send to OSRM
|
|
946
|
-
const driverAssigned = this.order.driver_assigned;
|
|
947
|
-
const driverPosition = driverAssigned?.location?.coordinates; // [lon,lat] | undefined
|
|
948
|
-
const originalCoords = this.getCoordinatesFromPayload(); // [[lon,lat], …]
|
|
949
|
-
const coordinates = driverPosition ? [driverPosition, ...originalCoords] : [...originalCoords];
|
|
950
|
-
const hasDriverStart = Boolean(driverPosition);
|
|
951
|
-
const source = 'first';
|
|
952
|
-
const destination = 'any';
|
|
953
|
-
const roundtrip = false; // don’t loop back
|
|
954
|
-
const routingHost = getRoutingHost(this.payload, this.waypoints);
|
|
955
|
-
|
|
956
|
-
// Call the OSRM /trip service
|
|
858
|
+
@task *optimizeRouteWithService(service) {
|
|
957
859
|
try {
|
|
958
|
-
const
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
}));
|
|
971
|
-
|
|
972
|
-
// Drop the driver start if present
|
|
973
|
-
const payloadPairs = hasDriverStart ? pairs.slice(1) : pairs;
|
|
974
|
-
|
|
975
|
-
// Sort by the optimised order
|
|
976
|
-
payloadPairs.sort((a, b) => a.wp.waypoint_index - b.wp.waypoint_index);
|
|
977
|
-
|
|
978
|
-
// Extract the Ember models (null-safe)
|
|
979
|
-
const sortedWaypoints = payloadPairs.map((p) => p.model).filter(Boolean);
|
|
860
|
+
const result = yield this.routeOptimization.optimize(service, {
|
|
861
|
+
context: 'create_order',
|
|
862
|
+
order: this.order,
|
|
863
|
+
payload: this.payload,
|
|
864
|
+
waypoints: this.waypoints,
|
|
865
|
+
coordinates: this.getCoordinatesFromPayload(),
|
|
866
|
+
});
|
|
867
|
+
this.handleRouteOptimization(result);
|
|
868
|
+
} catch (err) {
|
|
869
|
+
this.notifications.error(err.message ?? this.intl.t('fleet-ops.operations.orders.index.new.route-error'));
|
|
870
|
+
}
|
|
871
|
+
}
|
|
980
872
|
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
this.
|
|
984
|
-
|
|
873
|
+
@task *optimizeRoute() {
|
|
874
|
+
try {
|
|
875
|
+
const result = yield this.osrm.optimize({
|
|
876
|
+
context: 'create_order',
|
|
877
|
+
order: this.order,
|
|
878
|
+
payload: this.payload,
|
|
879
|
+
waypoints: this.waypoints,
|
|
880
|
+
coordinates: this.getCoordinatesFromPayload(),
|
|
881
|
+
});
|
|
985
882
|
|
|
986
|
-
|
|
987
|
-
|
|
883
|
+
this.handleRouteOptimization(result);
|
|
884
|
+
} catch (err) {
|
|
885
|
+
this.notifications.error(this.intl.t('fleet-ops.operations.orders.index.new.route-error'));
|
|
886
|
+
}
|
|
887
|
+
}
|
|
988
888
|
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
889
|
+
handleRouteOptimization({ sortedWaypoints, route, trip, result }) {
|
|
890
|
+
// Update map layers & UI
|
|
891
|
+
this.removeRoutingControlPreview();
|
|
892
|
+
this.removeOptimizedRoute(this.leafletMap);
|
|
893
|
+
this.clearLayers();
|
|
993
894
|
|
|
994
|
-
|
|
895
|
+
// Update controller state
|
|
896
|
+
this.waypoints = sortedWaypoints;
|
|
897
|
+
if (route) {
|
|
898
|
+
this.setOptimizedRoute(route, trip, result.waypoints);
|
|
899
|
+
}
|
|
900
|
+
this.previewDraftOrderRoute(this.payload, this.waypoints, this.isMultipleDropoffOrder);
|
|
901
|
+
this.updatePayloadCoordinates();
|
|
995
902
|
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
} catch (err) {
|
|
1000
|
-
debug('Error optimizing route', err);
|
|
1001
|
-
this.notifications.error(this.intl.t('fleet-ops.operations.orders.index.new.route-error'));
|
|
1002
|
-
} finally {
|
|
1003
|
-
this.isOptimizingRoute = false;
|
|
903
|
+
this.order.set('is_route_optimized', true);
|
|
904
|
+
if (this.isUsingIntegratedVendor) {
|
|
905
|
+
this.getQuotes();
|
|
1004
906
|
}
|
|
1005
907
|
}
|
|
1006
908
|
|
|
@@ -6,8 +6,7 @@ import { action, computed } from '@ember/object';
|
|
|
6
6
|
import { later } from '@ember/runloop';
|
|
7
7
|
import { not, notEmpty, alias } from '@ember/object/computed';
|
|
8
8
|
import { task } from 'ember-concurrency';
|
|
9
|
-
import {
|
|
10
|
-
import getRoutingHost from '@fleetbase/ember-core/utils/get-routing-host';
|
|
9
|
+
import { Control as RoutingControl } from '@fleetbase/leaflet-routing-machine';
|
|
11
10
|
|
|
12
11
|
export default class OperationsOrdersIndexViewController extends BaseController {
|
|
13
12
|
@controller('operations.orders.index') ordersController;
|
|
@@ -24,6 +23,7 @@ export default class OperationsOrdersIndexViewController extends BaseController
|
|
|
24
23
|
@service socket;
|
|
25
24
|
@service universe;
|
|
26
25
|
@service contextPanel;
|
|
26
|
+
@service leafletRouterControl;
|
|
27
27
|
|
|
28
28
|
@tracked isLoadingAdditionalData = false;
|
|
29
29
|
@tracked isWaypointsCollapsed;
|
|
@@ -276,6 +276,7 @@ export default class OperationsOrdersIndexViewController extends BaseController
|
|
|
276
276
|
}
|
|
277
277
|
|
|
278
278
|
this.forceRemoveRoutePreview();
|
|
279
|
+
this.routeControl = null;
|
|
279
280
|
resolve(true);
|
|
280
281
|
});
|
|
281
282
|
}
|
|
@@ -372,9 +373,8 @@ export default class OperationsOrdersIndexViewController extends BaseController
|
|
|
372
373
|
const leafletMap = this.leafletMap;
|
|
373
374
|
const payload = this.model.payload;
|
|
374
375
|
const waypoints = this.getPayloadCoordinates(payload);
|
|
375
|
-
const routingHost = getRoutingHost(payload, this.getPayloadWaypointsAsArray());
|
|
376
376
|
|
|
377
|
-
if (!waypoints || waypoints.length < 2 || !leafletMap) {
|
|
377
|
+
if (!waypoints || waypoints.length < 2 || !leafletMap || this.routeControl) {
|
|
378
378
|
return;
|
|
379
379
|
}
|
|
380
380
|
|
|
@@ -382,12 +382,12 @@ export default class OperationsOrdersIndexViewController extends BaseController
|
|
|
382
382
|
leafletMap.stop();
|
|
383
383
|
leafletMap.flyTo(waypoints.firstObject);
|
|
384
384
|
|
|
385
|
-
const
|
|
386
|
-
|
|
387
|
-
profile: 'driving',
|
|
388
|
-
});
|
|
385
|
+
const routingService = this.currentUser.getOption('routing', { router: 'osrm' }).router;
|
|
386
|
+
const { router, formatter } = this.leafletRouterControl.get(routingService);
|
|
389
387
|
|
|
390
388
|
this.routeControl = new RoutingControl({
|
|
389
|
+
router,
|
|
390
|
+
formatter,
|
|
391
391
|
waypoints,
|
|
392
392
|
markerOptions: {
|
|
393
393
|
icon: L.icon({
|
|
@@ -400,7 +400,6 @@ export default class OperationsOrdersIndexViewController extends BaseController
|
|
|
400
400
|
},
|
|
401
401
|
alternativeClassName: 'hidden',
|
|
402
402
|
addWaypoints: false,
|
|
403
|
-
router,
|
|
404
403
|
}).addTo(leafletMap);
|
|
405
404
|
|
|
406
405
|
this.routeControl.on('routesfound', (event) => {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import Controller from '@ember/controller';
|
|
2
|
+
import { tracked } from '@glimmer/tracking';
|
|
3
|
+
import { inject as service } from '@ember/service';
|
|
4
|
+
import { task } from 'ember-concurrency';
|
|
5
|
+
|
|
6
|
+
export default class SettingsRoutingController extends Controller {
|
|
7
|
+
@service fetch;
|
|
8
|
+
@service notifications;
|
|
9
|
+
@service currentUser;
|
|
10
|
+
@service leafletRouterControl;
|
|
11
|
+
@tracked routerService = 'osrm';
|
|
12
|
+
|
|
13
|
+
constructor() {
|
|
14
|
+
super(...arguments);
|
|
15
|
+
this.getSettings.perform();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Save routing settings.
|
|
20
|
+
*
|
|
21
|
+
* @memberof SettingsRoutingController
|
|
22
|
+
*/
|
|
23
|
+
@task *saveSettings() {
|
|
24
|
+
try {
|
|
25
|
+
yield this.fetch.post('fleet-ops/settings/routing-settings', { router: this.routerService });
|
|
26
|
+
// Save in local memory too
|
|
27
|
+
this.currentUser.setOption('routing', { router: this.routerService });
|
|
28
|
+
this.notifications.success('Routing setting saved.');
|
|
29
|
+
} catch (error) {
|
|
30
|
+
this.notifications.serverError(error);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Get routing settings.
|
|
36
|
+
*
|
|
37
|
+
* @memberof SettingsRoutingController
|
|
38
|
+
*/
|
|
39
|
+
@task *getSettings() {
|
|
40
|
+
try {
|
|
41
|
+
const { router } = yield this.fetch.get('fleet-ops/settings/routing-settings');
|
|
42
|
+
this.routerService = router;
|
|
43
|
+
} catch (error) {
|
|
44
|
+
this.notifications.serverError(error);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
package/addon/engine.js
CHANGED
|
@@ -9,6 +9,9 @@ import AdminAvatarManagementComponent from './components/admin/avatar-management
|
|
|
9
9
|
import CustomerOrdersComponent from './components/customer/orders';
|
|
10
10
|
import CustomerAdminSettingsComponent from './components/customer/admin-settings';
|
|
11
11
|
import OrderTrackingLookupComponent from './components/order-tracking-lookup';
|
|
12
|
+
import { RouterControl } from './services/leaflet-router-control';
|
|
13
|
+
import { OSRMv1 } from '@fleetbase/leaflet-routing-machine';
|
|
14
|
+
import getRoutingHost from '@fleetbase/ember-core/utils/get-routing-host';
|
|
12
15
|
|
|
13
16
|
const { modulePrefix } = config;
|
|
14
17
|
const externalRoutes = ['console', 'extensions'];
|
|
@@ -57,6 +60,29 @@ export default class FleetOpsEngine extends Engine {
|
|
|
57
60
|
},
|
|
58
61
|
});
|
|
59
62
|
|
|
63
|
+
// Register OSRM as route optimization service
|
|
64
|
+
const routeOptimization = app.lookup('service:route-optimization');
|
|
65
|
+
const osrm = app.lookup('service:osrm');
|
|
66
|
+
if (routeOptimization && osrm) {
|
|
67
|
+
routeOptimization.register('osrm', osrm);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Register OSRM as Routing Controler
|
|
71
|
+
const leafletRouterControl = app.lookup('service:leaflet-router-control');
|
|
72
|
+
if (leafletRouterControl) {
|
|
73
|
+
const routingHost = getRoutingHost();
|
|
74
|
+
leafletRouterControl.register(
|
|
75
|
+
'osrm',
|
|
76
|
+
new RouterControl({
|
|
77
|
+
name: 'OSRM',
|
|
78
|
+
router: new OSRMv1({
|
|
79
|
+
serviceUrl: `${routingHost}/route/v1`,
|
|
80
|
+
profile: 'driving',
|
|
81
|
+
}),
|
|
82
|
+
})
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
60
86
|
// widgets for registry
|
|
61
87
|
const KeyMetricsWidgetDefinition = {
|
|
62
88
|
widgetId: 'fleet-ops-key-metrics-widget',
|
|
@@ -106,6 +132,7 @@ export default class FleetOpsEngine extends Engine {
|
|
|
106
132
|
'fleet-ops:template:operations:orders:view',
|
|
107
133
|
'fleet-ops:template:operations:orders:new',
|
|
108
134
|
'fleet-ops:template:operations:orders:new:entities-input',
|
|
135
|
+
'fleet-ops:template:operations:orders:new:entities-input:entity',
|
|
109
136
|
]);
|
|
110
137
|
|
|
111
138
|
universe.afterBoot(function (universe) {
|
|
@@ -10,6 +10,8 @@ export default class ApplicationRoute extends Route {
|
|
|
10
10
|
@service abilities;
|
|
11
11
|
@service hostRouter;
|
|
12
12
|
@service notifications;
|
|
13
|
+
@service fetch;
|
|
14
|
+
@service currentUser;
|
|
13
15
|
|
|
14
16
|
@action loading(transition) {
|
|
15
17
|
const resourceName = getResourceNameFromTransition(transition, { humanize: true });
|
|
@@ -25,5 +27,11 @@ export default class ApplicationRoute extends Route {
|
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
this.location.getUserLocation();
|
|
30
|
+
this.#loadRoutingSettings();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async #loadRoutingSettings() {
|
|
34
|
+
const routingSetting = await this.fetch.get('fleet-ops/settings/routing-settings');
|
|
35
|
+
this.currentUser.setOption('routing', routingSetting);
|
|
28
36
|
}
|
|
29
37
|
}
|
|
@@ -12,10 +12,18 @@ export default class OperationsOrdersIndexViewRoute extends Route {
|
|
|
12
12
|
@service abilities;
|
|
13
13
|
@service intl;
|
|
14
14
|
|
|
15
|
-
@action willTransition() {
|
|
16
|
-
if
|
|
15
|
+
@action willTransition(transition) {
|
|
16
|
+
// if we're coming _from_ this route and going _to_ a different one, then reset
|
|
17
|
+
let fromName = transition.from?.name;
|
|
18
|
+
let toName = transition.to.name;
|
|
19
|
+
|
|
20
|
+
// transition.from is null on initial page load, so guard that
|
|
21
|
+
if (fromName && fromName !== toName) {
|
|
17
22
|
this.controller.resetView();
|
|
18
23
|
}
|
|
24
|
+
|
|
25
|
+
// bubble the event on up so Ember can finish the transition
|
|
26
|
+
return true;
|
|
19
27
|
}
|
|
20
28
|
|
|
21
29
|
@action error(error) {
|
|
@@ -75,13 +83,14 @@ export default class OperationsOrdersIndexViewRoute extends Route {
|
|
|
75
83
|
// Only reload if the order has a status change stemming from an updated event OR
|
|
76
84
|
// if a waypoint has been completed which will trigger `order.completed`
|
|
77
85
|
const statusChanged = event === 'order.updated' && data.status !== model.status;
|
|
78
|
-
const shouldReload = ['order.completed', 'waypoint.activity', 'order.created'].includes(event);
|
|
86
|
+
const shouldReload = ['order.completed', 'waypoint.activity', 'entity.activity', 'order.created'].includes(event);
|
|
79
87
|
if (statusChanged || shouldReload) {
|
|
80
88
|
this.refresh();
|
|
81
89
|
|
|
82
90
|
// reload the controller stuff as well
|
|
83
91
|
if (this.controller) {
|
|
84
92
|
this.controller.loadOrderRelations.perform(model);
|
|
93
|
+
this.controller.displayOrderRoute();
|
|
85
94
|
}
|
|
86
95
|
}
|
|
87
96
|
|
package/addon/routes.js
CHANGED