/**
* BeNomad BeMap JavaScript API - EvSmartRouting service (v1.5)
*
* Promise-based wrapper around `POST service/2.0/evsmartrouting` (REST v2.0.0).
*
* **What it does.** Given a start, a stop, and a vehicle key, the service
* plans an EV journey that respects the battery's state of charge — adding
* charging stops where needed using the configured CSP(s). The response
* carries the journey summary (distance, duration, charging time, CO2)
* plus an ordered list of `EvStepPoint`s with energy budgets and the
* route polyline.
*
* Auth: requires `ROLE_EVSMARTROUTING`. The CSP(s) must be in the user's
* allowed list (see `bemap.helpers.listChargingStationProviders`).
*
* Typical usage:
*
* var router = new bemap.EvSmartRouting(ctx);
* var req = new bemap.EvSmartRoutingRequest({
* start: new bemap.Coordinate(2.35, 48.85),
* stop: new bemap.Coordinate(4.83, 45.75),
* vehicle: 'renault-megane-e-tech-ev60',
* chargingStationProviders: ['ecoMovement'],
* initBatteryLevel: 80,
* minBatteryLevel: 10,
* minArrivalBatteryLevel: 20
* });
* router.calculate(req).then(function(response) {
* drawPolyline(response.getPolyline());
* response.getStepPoints().forEach(addChargingMarker);
* summary(response.getDistance(), response.getDuration(), response.getChargingTime());
* });
*
* @since 1.5.0
* @public
* @constructor
* @extends {bemap.ServiceV2}
* @param {bemap.Context} context BeMap-JS-API Context. Mandatory.
* @param {Object} [options]
*/
bemap.EvSmartRouting = function(context, options) {
bemap.ServiceV2.call(this, context, options);
};
bemap.inherits(bemap.EvSmartRouting, bemap.ServiceV2);
/**
* Plan an EV journey with optional charging stops.
*
* @public
* @param {bemap.EvSmartRoutingRequest} request
* @param {Object} [options]
* @param {AbortSignal} [options.signal] AbortSignal for cancellation.
* @return {Promise<bemap.EvSmartRoutingResponse>}
*/
bemap.EvSmartRouting.prototype.calculate = function(request, options) {
if (!(request instanceof bemap.EvSmartRoutingRequest)) {
return Promise.reject(new bemap.Error({
code: bemap.Error.INVALID_ARGUMENT,
message: 'EvSmartRouting.calculate requires a bemap.EvSmartRoutingRequest'
}));
}
var opts = options || {};
var body = request.toJsonV2();
if (!body.geoserver) body.geoserver = this.ctx.getGeoserver();
// Pre-fill the CSP from the Context when the request did not specify one.
if ((!body.csps || !body.csps.length) && this.ctx.chargingStationProvider) {
body.csps = [this.ctx.chargingStationProvider];
}
// v2 `start` / `stop` are structured places with a mandatory `lon`/`lat`
// (@JsonProperty(required = true) in v2_0_0 EvSmartRoutingRequest / PlaceFront).
// Trap the missing-coordinate case client-side so consumers get a typed error
// rather than an opaque 400. `== null` catches null/undefined while letting a
// legitimate `0` (Prime-Meridian / Equator) through.
var s = body.start, e = body.stop;
if (!s || s.lon == null || s.lat == null || !e || e.lon == null || e.lat == null) {
return Promise.reject(new bemap.Error({
code: bemap.Error.INVALID_ARGUMENT,
message: 'EvSmartRouting.calculate: start and stop coordinates are required'
}));
}
return this._post('service/2.0/evsmartrouting', body, { signal: opts.signal })
.then(function(json) {
var resp = bemap.EvSmartRoutingResponse.fromJson(json);
// A journey was returned if the v2 `journeys[]` has an entry (or, for a
// v1-shaped payload, `journey` is present). Gating on the summary-derived
// shim alone would falsely throw when a journey omits its summary.
var journeys = resp.getJourneys();
var hasJourney = (journeys && journeys.length) || !!resp.getJourney();
if (!hasJourney) {
throw new bemap.Error({
code: bemap.Error.EV_NO_JOURNEY,
message: 'No EV journey could be planned between the supplied points'
});
}
return resp;
});
};
/**
* Fetch the list of EV brands the current user has access to. Wraps
* `GET service/evsmartrouting/vehiclebrands/list/1.0`.
*
* @public
* @param {Object} [options]
* @param {AbortSignal} [options.signal]
* @return {Promise<Array.<Object>>} Raw `brands[]` array (each entry has `id` and `name`).
*/
bemap.EvSmartRouting.prototype.getBrands = function(options) {
var opts = options || {};
return this._get('service/evsmartrouting/vehiclebrands/list/1.0', { signal: opts.signal })
.then(function(json) {
return (json && Array.isArray(json.brands)) ? json.brands : [];
});
};
/**
* Fetch the list of EV vehicle models. Wraps
* `GET service/evsmartrouting/vehiclemodels/list/1.0`.
*
* @public
* @param {Object} [options]
* @param {String} [options.vehicle] Filter by vehicle key.
* @param {String} [options.brandId] Filter by brand id.
* @param {AbortSignal} [options.signal]
* @return {Promise<Array.<Object>>} Raw `vehicles[]` array.
*/
bemap.EvSmartRouting.prototype.getVehicles = function(options) {
var opts = options || {};
var qs = [];
if (opts.vehicle) qs.push('vehicle=' + encodeURIComponent(opts.vehicle));
if (opts.brandId) qs.push('brandId=' + encodeURIComponent(opts.brandId));
var path = 'service/evsmartrouting/vehiclemodels/list/1.0' + (qs.length ? '?' + qs.join('&') : '');
return this._get(path, { signal: opts.signal })
.then(function(json) {
return (json && Array.isArray(json.vehicles)) ? json.vehicles : [];
});
};