sgerp-frontend-lib 0.1.4 → 0.2.0
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/dist/constants/timezones.d.ts +7 -2
- package/dist/constants/timezones.d.ts.map +1 -1
- package/dist/constants/timezones.js +17 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/locales/locale-server.d.ts +1 -1
- package/dist/locales/locale-server.d.ts.map +1 -1
- package/dist/locales/locale-server.js +5 -0
- package/dist/locales/locale.d.ts +1 -1
- package/dist/locales/locale.d.ts.map +1 -1
- package/dist/locales/locale.js +6 -2
- package/dist/locales/locale_en.d.ts.map +1 -1
- package/dist/locales/locale_en.js +236 -1
- package/dist/locales/locale_id.d.ts +2 -0
- package/dist/locales/locale_id.d.ts.map +1 -0
- package/dist/locales/locale_id.js +2473 -0
- package/dist/locales/locale_ja.d.ts.map +1 -1
- package/dist/locales/locale_ja.js +188 -1
- package/dist/locales/locale_ms.d.ts.map +1 -1
- package/dist/locales/locale_ms.js +60 -1
- package/dist/locales/locale_tl.d.ts +2 -0
- package/dist/locales/locale_tl.d.ts.map +1 -0
- package/dist/locales/locale_tl.js +1122 -0
- package/dist/sgerp/collection.d.ts.map +1 -1
- package/dist/sgerp/collection.js +18 -4
- package/dist/sgerp/domains.d.ts +16 -2
- package/dist/sgerp/domains.d.ts.map +1 -1
- package/dist/sgerp/domains.js +47 -1
- package/dist/sgerp/hooks/use-selection-state.d.ts +37 -0
- package/dist/sgerp/hooks/use-selection-state.d.ts.map +1 -0
- package/dist/sgerp/hooks/use-selection-state.js +121 -0
- package/dist/sgerp/hooks/use-simulation-data.d.ts +105 -0
- package/dist/sgerp/hooks/use-simulation-data.d.ts.map +1 -0
- package/dist/sgerp/hooks/use-simulation-data.js +146 -0
- package/dist/sgerp/hooks/use-simulation-state.d.ts +80 -0
- package/dist/sgerp/hooks/use-simulation-state.d.ts.map +1 -0
- package/dist/sgerp/hooks/use-simulation-state.js +161 -0
- package/dist/sgerp/index.d.ts +10 -3
- package/dist/sgerp/index.d.ts.map +1 -1
- package/dist/sgerp/index.js +30 -2
- package/dist/sgerp/simulation-logic/fetchUtils.d.ts +18 -0
- package/dist/sgerp/simulation-logic/fetchUtils.d.ts.map +1 -1
- package/dist/sgerp/simulation-logic/fetchUtils.js +196 -0
- package/dist/sgerp/simulation-logic/index.d.ts +7 -2
- package/dist/sgerp/simulation-logic/index.d.ts.map +1 -1
- package/dist/sgerp/simulation-logic/index.js +18 -1
- package/dist/sgerp/simulation-logic/liveUpdates.d.ts +102 -0
- package/dist/sgerp/simulation-logic/liveUpdates.d.ts.map +1 -0
- package/dist/sgerp/simulation-logic/liveUpdates.js +87 -0
- package/dist/sgerp/simulation-logic/prepareSimulationData.d.ts +36 -0
- package/dist/sgerp/simulation-logic/prepareSimulationData.d.ts.map +1 -0
- package/dist/sgerp/simulation-logic/prepareSimulationData.js +34 -0
- package/dist/sgerp/simulation-logic/routeCalculationUtils.d.ts +7 -1
- package/dist/sgerp/simulation-logic/routeCalculationUtils.d.ts.map +1 -1
- package/dist/sgerp/simulation-logic/routeCalculationUtils.js +81 -0
- package/dist/sgerp/simulation-logic/vrpStatsCalculator.d.ts +52 -0
- package/dist/sgerp/simulation-logic/vrpStatsCalculator.d.ts.map +1 -0
- package/dist/sgerp/simulation-logic/vrpStatsCalculator.js +247 -0
- package/dist/sgerp/simulation-logic/vrpStatsUtils.d.ts +17 -0
- package/dist/sgerp/simulation-logic/vrpStatsUtils.d.ts.map +1 -0
- package/dist/sgerp/simulation-logic/vrpStatsUtils.js +48 -0
- package/dist/sgerp/utils/timezone.d.ts +15 -0
- package/dist/sgerp/utils/timezone.d.ts.map +1 -0
- package/dist/sgerp/utils/timezone.js +19 -0
- package/dist/sgerp/vrptoolbox-analyzer.d.ts +39 -0
- package/dist/sgerp/vrptoolbox-analyzer.d.ts.map +1 -0
- package/dist/sgerp/vrptoolbox-analyzer.js +210 -0
- package/dist/vrptoolbox/collection.d.ts +16 -0
- package/dist/vrptoolbox/collection.d.ts.map +1 -0
- package/dist/vrptoolbox/collection.js +62 -0
- package/dist/vrptoolbox/index.d.ts +7 -0
- package/dist/vrptoolbox/index.d.ts.map +1 -0
- package/dist/vrptoolbox/index.js +24 -0
- package/dist/vrptoolbox/types/job.d.ts +46 -0
- package/dist/vrptoolbox/types/job.d.ts.map +1 -0
- package/dist/vrptoolbox/types/job.js +5 -0
- package/dist/vrptoolbox/vrptoolbox-utils.d.ts +133 -0
- package/dist/vrptoolbox/vrptoolbox-utils.d.ts.map +1 -0
- package/dist/vrptoolbox/vrptoolbox-utils.js +207 -0
- package/package.json +9 -4
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* VRPToolbox Statistics Calculator
|
|
4
|
+
* Processes VRP payload and result to calculate fleet statistics, timeline data, and utilization
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.calculateFleetStats = calculateFleetStats;
|
|
8
|
+
exports.processVRPStatistics = processVRPStatistics;
|
|
9
|
+
const vrpStatsUtils_1 = require("./vrpStatsUtils");
|
|
10
|
+
const VEHICLE_COLORS = [
|
|
11
|
+
'red',
|
|
12
|
+
'blue',
|
|
13
|
+
'green',
|
|
14
|
+
'orange',
|
|
15
|
+
'purple',
|
|
16
|
+
'brown',
|
|
17
|
+
'magenta',
|
|
18
|
+
'teal',
|
|
19
|
+
];
|
|
20
|
+
/**
|
|
21
|
+
* Calculate fleet-wide statistics from processed vehicles
|
|
22
|
+
*/
|
|
23
|
+
function calculateFleetStats(vehicles) {
|
|
24
|
+
let totalSlack = 0;
|
|
25
|
+
let totalDistance = 0;
|
|
26
|
+
let totalDrivingTime = 0;
|
|
27
|
+
let totalBreakTime = 0;
|
|
28
|
+
vehicles.forEach((vehicle) => {
|
|
29
|
+
totalDistance += parseFloat(vehicle.totalDistance || '0');
|
|
30
|
+
vehicle.timelineWaypoints.forEach((wp) => {
|
|
31
|
+
totalSlack += wp.adjustedSlack || 0;
|
|
32
|
+
totalDrivingTime += wp.travel_time_from_prev || 0;
|
|
33
|
+
totalBreakTime += wp.nodeBreakDuration || 0;
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
return {
|
|
37
|
+
totalSlack: (0, vrpStatsUtils_1.formatSeconds)(totalSlack),
|
|
38
|
+
totalDistance: totalDistance.toFixed(2),
|
|
39
|
+
totalDrivingTime: (0, vrpStatsUtils_1.formatSeconds)(totalDrivingTime),
|
|
40
|
+
totalBreakTime: (0, vrpStatsUtils_1.formatSeconds)(totalBreakTime),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Process VRP result and payload to generate vehicle statistics
|
|
45
|
+
*/
|
|
46
|
+
async function processVRPStatistics(result, payload) {
|
|
47
|
+
if (!result || !result.vehicles) {
|
|
48
|
+
console.warn('No vehicles in result', result);
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
const inputVehicles = (payload === null || payload === void 0 ? void 0 : payload.vehicles) || [];
|
|
52
|
+
const vehicleList = Array.isArray(result.vehicles)
|
|
53
|
+
? result.vehicles
|
|
54
|
+
.filter((v) => v.nodes && v.nodes.length > 0) // Only vehicles with nodes
|
|
55
|
+
.map((v) => ({
|
|
56
|
+
agent_id: v.agent_id,
|
|
57
|
+
nodes: v.nodes,
|
|
58
|
+
}))
|
|
59
|
+
: Object.entries(result.vehicles)
|
|
60
|
+
.filter(([id, nodes]) => nodes && nodes.length > 0) // Only vehicles with nodes
|
|
61
|
+
.map(([id, nodes]) => ({
|
|
62
|
+
agent_id: id,
|
|
63
|
+
nodes: nodes,
|
|
64
|
+
}));
|
|
65
|
+
const vehicles = await Promise.all(vehicleList.map(async (vehicleData, index) => {
|
|
66
|
+
var _a, _b, _c, _d;
|
|
67
|
+
const inputVehicle = inputVehicles.find((v) => v.agent_id === vehicleData.agent_id);
|
|
68
|
+
let vehicleBreakDuration = 0;
|
|
69
|
+
if (inputVehicle &&
|
|
70
|
+
typeof inputVehicle.dynamic_break_duration === 'number') {
|
|
71
|
+
vehicleBreakDuration = inputVehicle.dynamic_break_duration;
|
|
72
|
+
}
|
|
73
|
+
let sortedNodes = JSON.parse(JSON.stringify(vehicleData.nodes));
|
|
74
|
+
sortedNodes.sort((a, b) => new Date(a.scheduled_ts).getTime() - new Date(b.scheduled_ts).getTime());
|
|
75
|
+
// Add start/end nodes if vehicle has start/end times
|
|
76
|
+
if (inputVehicle) {
|
|
77
|
+
if (inputVehicle.start_time) {
|
|
78
|
+
const startNode = ((payload === null || payload === void 0 ? void 0 : payload.nodes) || []).find((n) => { var _a; return n.uid === ((_a = inputVehicle.partial_route) === null || _a === void 0 ? void 0 : _a[0]); }) || vehicleData.nodes[0];
|
|
79
|
+
if (startNode) {
|
|
80
|
+
sortedNodes.unshift({
|
|
81
|
+
lat: startNode.lat,
|
|
82
|
+
lon: startNode.lon,
|
|
83
|
+
scheduled_ts: inputVehicle.start_time,
|
|
84
|
+
location_name: 'Start',
|
|
85
|
+
node_type: 'vehicle_start',
|
|
86
|
+
service_time: 0,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (inputVehicle.end_time) {
|
|
91
|
+
const lastRouteUid = (_a = inputVehicle.partial_route) === null || _a === void 0 ? void 0 : _a[((_b = inputVehicle.partial_route) === null || _b === void 0 ? void 0 : _b.length) - 1];
|
|
92
|
+
const endNode = ((payload === null || payload === void 0 ? void 0 : payload.nodes) || []).find((n) => n.uid === lastRouteUid) ||
|
|
93
|
+
vehicleData.nodes[vehicleData.nodes.length - 1];
|
|
94
|
+
if (endNode) {
|
|
95
|
+
sortedNodes.push({
|
|
96
|
+
lat: endNode.lat,
|
|
97
|
+
lon: endNode.lon,
|
|
98
|
+
scheduled_ts: inputVehicle.end_time,
|
|
99
|
+
location_name: 'End',
|
|
100
|
+
node_type: 'vehicle_end',
|
|
101
|
+
service_time: 0,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Process nodes with travel times, breaks, and slack
|
|
107
|
+
const processedNodes = sortedNodes.map((node, idx, array) => {
|
|
108
|
+
const prevNode = array[idx - 1];
|
|
109
|
+
let prevNodeDepartureTime;
|
|
110
|
+
if (prevNode) {
|
|
111
|
+
let prevNodeActualBreakDuration = 0;
|
|
112
|
+
let prevNodeEffectiveSlack = 0;
|
|
113
|
+
if (typeof prevNode.dynamic_break_time === 'number') {
|
|
114
|
+
prevNodeActualBreakDuration = prevNode.dynamic_break_time;
|
|
115
|
+
prevNodeEffectiveSlack = prevNode.slack || 0;
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
const prevNodeBreakCount = prevNode.dynamic_break || 0;
|
|
119
|
+
prevNodeActualBreakDuration =
|
|
120
|
+
prevNodeBreakCount * vehicleBreakDuration;
|
|
121
|
+
prevNodeEffectiveSlack = Math.max(0, (prevNode.slack || 0) - prevNodeActualBreakDuration);
|
|
122
|
+
}
|
|
123
|
+
const prevScheduled = new Date(prevNode.scheduled_ts);
|
|
124
|
+
const departureMs = prevScheduled.getTime() +
|
|
125
|
+
((prevNode.service_time || 0) +
|
|
126
|
+
prevNodeEffectiveSlack +
|
|
127
|
+
prevNodeActualBreakDuration) *
|
|
128
|
+
1000;
|
|
129
|
+
prevNodeDepartureTime = new Date(departureMs);
|
|
130
|
+
}
|
|
131
|
+
let travel_time_from_prev = 0;
|
|
132
|
+
if (prevNode && node.node_type !== 'vehicle_end') {
|
|
133
|
+
const nodeScheduled = new Date(node.scheduled_ts);
|
|
134
|
+
if (prevNodeDepartureTime) {
|
|
135
|
+
travel_time_from_prev =
|
|
136
|
+
(nodeScheduled.getTime() - prevNodeDepartureTime.getTime()) /
|
|
137
|
+
1000;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
let nodeBreakDuration = 0;
|
|
141
|
+
let adjustedSlack = 0;
|
|
142
|
+
if (typeof node.dynamic_break_time === 'number') {
|
|
143
|
+
nodeBreakDuration = node.dynamic_break_time;
|
|
144
|
+
adjustedSlack = node.slack || 0;
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
const nodeBreakCount = node.dynamic_break || 0;
|
|
148
|
+
nodeBreakDuration = nodeBreakCount * vehicleBreakDuration;
|
|
149
|
+
adjustedSlack = Math.max(0, (node.slack || 0) - nodeBreakDuration);
|
|
150
|
+
}
|
|
151
|
+
return Object.assign(Object.assign({}, node), { travel_time_from_prev,
|
|
152
|
+
nodeBreakDuration,
|
|
153
|
+
adjustedSlack, service_time: node.service_time || 0 });
|
|
154
|
+
});
|
|
155
|
+
// Calculate total distance (try multiple methods)
|
|
156
|
+
let totalDistance = 0;
|
|
157
|
+
const lastNode = processedNodes[processedNodes.length - 1];
|
|
158
|
+
// Method 1: Use scheduled_cumulative_dist if available
|
|
159
|
+
if (lastNode &&
|
|
160
|
+
typeof lastNode.scheduled_cumulative_dist === 'number') {
|
|
161
|
+
totalDistance = lastNode.scheduled_cumulative_dist / 1000;
|
|
162
|
+
}
|
|
163
|
+
// Method 2: Fallback to Euclidean distance
|
|
164
|
+
else if (processedNodes.length > 1) {
|
|
165
|
+
for (let i = 1; i < processedNodes.length; i++) {
|
|
166
|
+
const prev = processedNodes[i - 1];
|
|
167
|
+
const curr = processedNodes[i];
|
|
168
|
+
totalDistance += (0, vrpStatsUtils_1.getDistanceFromLatLonInKm)(prev.lat, prev.lon, curr.lat, curr.lon);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// Calculate total time
|
|
172
|
+
let totalTime = 0;
|
|
173
|
+
if (processedNodes.length > 0 && lastNode) {
|
|
174
|
+
const startTime = new Date(processedNodes[0].scheduled_ts);
|
|
175
|
+
const endTime = new Date(lastNode.scheduled_ts);
|
|
176
|
+
totalTime = (endTime.getTime() - startTime.getTime()) / 1000;
|
|
177
|
+
}
|
|
178
|
+
// Calculate utilization over time
|
|
179
|
+
const utilizationData = [];
|
|
180
|
+
const vehicleCapacity = (inputVehicle === null || inputVehicle === void 0 ? void 0 : inputVehicle.capacity) || (inputVehicle === null || inputVehicle === void 0 ? void 0 : inputVehicle.limits) || {};
|
|
181
|
+
const totalCapacity = Object.values(vehicleCapacity).reduce((sum, val) => sum + val, 0);
|
|
182
|
+
const currentLoad = {};
|
|
183
|
+
// Initialize load for prebook_cvrptw mode
|
|
184
|
+
if (((_d = (_c = payload.engine_settings) === null || _c === void 0 ? void 0 : _c.calculation_parameters) === null || _d === void 0 ? void 0 : _d.scheduling_mode) ===
|
|
185
|
+
'prebook_cvrptw') {
|
|
186
|
+
const inputNodes = payload.nodes || [];
|
|
187
|
+
const allDemands = inputNodes
|
|
188
|
+
.filter((n) => n.node_type === 'dropoff' &&
|
|
189
|
+
n.demand &&
|
|
190
|
+
vehicleData.nodes.some((vn) => vn.booking_uid && vn.booking_uid === n.booking_uid))
|
|
191
|
+
.flatMap((n) => Object.entries(n.demand));
|
|
192
|
+
for (const [key, value] of allDemands) {
|
|
193
|
+
currentLoad[key] = (currentLoad[key] || 0) + Math.abs(value);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
Object.keys(vehicleCapacity).forEach((key) => (currentLoad[key] = 0));
|
|
198
|
+
}
|
|
199
|
+
// Track utilization at each node
|
|
200
|
+
processedNodes.forEach((node) => {
|
|
201
|
+
const originalNode = (payload.nodes || []).find((n) => n.uid === node.uid || n.id === node.uid);
|
|
202
|
+
if (originalNode === null || originalNode === void 0 ? void 0 : originalNode.demand) {
|
|
203
|
+
if (node.node_type === 'dropoff') {
|
|
204
|
+
for (const [key, value] of Object.entries(originalNode.demand)) {
|
|
205
|
+
currentLoad[key] =
|
|
206
|
+
(currentLoad[key] || 0) - Math.abs(value);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
else if (node.node_type === 'pickup') {
|
|
210
|
+
for (const [key, value] of Object.entries(originalNode.demand)) {
|
|
211
|
+
currentLoad[key] =
|
|
212
|
+
(currentLoad[key] || 0) + Math.abs(value);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
let utilizationAfter = 0;
|
|
217
|
+
if (totalCapacity > 0) {
|
|
218
|
+
const totalLoad = Object.values(currentLoad).reduce((sum, val) => sum + val, 0);
|
|
219
|
+
utilizationAfter = (totalLoad / totalCapacity) * 100;
|
|
220
|
+
}
|
|
221
|
+
utilizationData.push({
|
|
222
|
+
time: node.scheduled_ts,
|
|
223
|
+
utilization: Math.max(0, Math.min(100, utilizationAfter)),
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
// Calculate vehicle-specific stats
|
|
227
|
+
const vehicleStats = processedNodes.reduce((acc, node) => {
|
|
228
|
+
acc.slack += node.adjustedSlack || 0;
|
|
229
|
+
acc.breakTime += node.nodeBreakDuration || 0;
|
|
230
|
+
acc.drivingTime += node.travel_time_from_prev || 0;
|
|
231
|
+
return acc;
|
|
232
|
+
}, { slack: 0, breakTime: 0, drivingTime: 0 });
|
|
233
|
+
return {
|
|
234
|
+
vehicleId: vehicleData.agent_id,
|
|
235
|
+
vehicleName: (inputVehicle === null || inputVehicle === void 0 ? void 0 : inputVehicle.service_number) || vehicleData.agent_id,
|
|
236
|
+
timelineWaypoints: processedNodes,
|
|
237
|
+
utilizationData: utilizationData,
|
|
238
|
+
color: VEHICLE_COLORS[index % VEHICLE_COLORS.length],
|
|
239
|
+
totalDistance: totalDistance.toFixed(2),
|
|
240
|
+
totalTime: (0, vrpStatsUtils_1.formatSeconds)(totalTime),
|
|
241
|
+
totalSlack: (0, vrpStatsUtils_1.formatSeconds)(vehicleStats.slack),
|
|
242
|
+
totalBreakTime: (0, vrpStatsUtils_1.formatSeconds)(vehicleStats.breakTime),
|
|
243
|
+
totalDrivingTime: (0, vrpStatsUtils_1.formatSeconds)(vehicleStats.drivingTime),
|
|
244
|
+
};
|
|
245
|
+
}));
|
|
246
|
+
return vehicles;
|
|
247
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VRPToolbox Statistics Utilities
|
|
3
|
+
* Helper functions for calculating and formatting VRP statistics
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Format seconds into HH:MM:SS format
|
|
7
|
+
*/
|
|
8
|
+
export declare function formatSeconds(seconds: number): string;
|
|
9
|
+
/**
|
|
10
|
+
* Calculate distance between two lat/lon coordinates in kilometers (Haversine formula)
|
|
11
|
+
*/
|
|
12
|
+
export declare function getDistanceFromLatLonInKm(lat1: number, lon1: number, lat2: number, lon2: number): number;
|
|
13
|
+
/**
|
|
14
|
+
* Convert timestamp to display Date (handles timezone conversions)
|
|
15
|
+
*/
|
|
16
|
+
export declare function toDisplayDate(ts: string): Date;
|
|
17
|
+
//# sourceMappingURL=vrpStatsUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vrpStatsUtils.d.ts","sourceRoot":"","sources":["../../../sgerplib/sgerp/simulation-logic/vrpStatsUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAUrD;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX,MAAM,CAaR;AAMD;;GAEG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAc9C"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* VRPToolbox Statistics Utilities
|
|
4
|
+
* Helper functions for calculating and formatting VRP statistics
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.formatSeconds = formatSeconds;
|
|
8
|
+
exports.getDistanceFromLatLonInKm = getDistanceFromLatLonInKm;
|
|
9
|
+
exports.toDisplayDate = toDisplayDate;
|
|
10
|
+
/**
|
|
11
|
+
* Format seconds into HH:MM:SS format
|
|
12
|
+
*/
|
|
13
|
+
function formatSeconds(seconds) {
|
|
14
|
+
const hours = Math.floor(seconds / 3600);
|
|
15
|
+
const minutes = Math.floor((seconds % 3600) / 60);
|
|
16
|
+
const remainingSeconds = Math.floor(seconds % 60);
|
|
17
|
+
const formattedHours = String(hours).padStart(2, '0');
|
|
18
|
+
const formattedMinutes = String(minutes).padStart(2, '0');
|
|
19
|
+
const formattedSeconds = String(remainingSeconds).padStart(2, '0');
|
|
20
|
+
return `${formattedHours}:${formattedMinutes}:${formattedSeconds}`;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Calculate distance between two lat/lon coordinates in kilometers (Haversine formula)
|
|
24
|
+
*/
|
|
25
|
+
function getDistanceFromLatLonInKm(lat1, lon1, lat2, lon2) {
|
|
26
|
+
const R = 6371; // Earth's radius in km
|
|
27
|
+
const dLat = deg2rad(lat2 - lat1);
|
|
28
|
+
const dLon = deg2rad(lon2 - lon1);
|
|
29
|
+
const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
|
30
|
+
Math.cos(deg2rad(lat1)) *
|
|
31
|
+
Math.cos(deg2rad(lat2)) *
|
|
32
|
+
Math.sin(dLon / 2) *
|
|
33
|
+
Math.sin(dLon / 2);
|
|
34
|
+
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
|
35
|
+
const d = R * c;
|
|
36
|
+
return d;
|
|
37
|
+
}
|
|
38
|
+
function deg2rad(deg) {
|
|
39
|
+
return deg * (Math.PI / 180);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Convert timestamp to display Date (handles timezone conversions)
|
|
43
|
+
*/
|
|
44
|
+
function toDisplayDate(ts) {
|
|
45
|
+
// Parse as UTC and return as local Date object
|
|
46
|
+
const date = new Date(ts);
|
|
47
|
+
return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds(), date.getUTCMilliseconds()));
|
|
48
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timezone utility functions
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Get the effective timezone based on the current mode
|
|
6
|
+
* In VRPToolbox mode, uses the VRP timezone selector
|
|
7
|
+
* Otherwise, uses the project timezone (or falls back to UTC)
|
|
8
|
+
*
|
|
9
|
+
* @param mode - The current effective mode ('vrptoolbox' or other)
|
|
10
|
+
* @param vrpTimezone - The timezone selected in VRPToolbox mode
|
|
11
|
+
* @param projectTimezone - The project's configured timezone
|
|
12
|
+
* @returns The effective timezone string (e.g., 'UTC', 'Asia/Singapore')
|
|
13
|
+
*/
|
|
14
|
+
export declare function getEffectiveTimezone(mode: string, vrpTimezone: string, projectTimezone?: string): string;
|
|
15
|
+
//# sourceMappingURL=timezone.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timezone.d.ts","sourceRoot":"","sources":["../../../sgerplib/sgerp/utils/timezone.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EACnB,eAAe,CAAC,EAAE,MAAM,GACvB,MAAM,CAER"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Timezone utility functions
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getEffectiveTimezone = getEffectiveTimezone;
|
|
7
|
+
/**
|
|
8
|
+
* Get the effective timezone based on the current mode
|
|
9
|
+
* In VRPToolbox mode, uses the VRP timezone selector
|
|
10
|
+
* Otherwise, uses the project timezone (or falls back to UTC)
|
|
11
|
+
*
|
|
12
|
+
* @param mode - The current effective mode ('vrptoolbox' or other)
|
|
13
|
+
* @param vrpTimezone - The timezone selected in VRPToolbox mode
|
|
14
|
+
* @param projectTimezone - The project's configured timezone
|
|
15
|
+
* @returns The effective timezone string (e.g., 'UTC', 'Asia/Singapore')
|
|
16
|
+
*/
|
|
17
|
+
function getEffectiveTimezone(mode, vrpTimezone, projectTimezone) {
|
|
18
|
+
return mode === 'vrptoolbox' ? vrpTimezone : (projectTimezone || 'UTC');
|
|
19
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
interface Vehicle {
|
|
2
|
+
agent_id: string;
|
|
3
|
+
characteristics?: Record<string, any>;
|
|
4
|
+
start_time?: number;
|
|
5
|
+
end_time?: number;
|
|
6
|
+
geofence_ids: any[];
|
|
7
|
+
assigned_nodes: any[];
|
|
8
|
+
labels?: string[];
|
|
9
|
+
capacity: number | Record<string, number>;
|
|
10
|
+
}
|
|
11
|
+
interface Node {
|
|
12
|
+
node_type: string;
|
|
13
|
+
vehicle_characteristics: Record<string, any>;
|
|
14
|
+
open_time_ts: number;
|
|
15
|
+
close_time_ts: number;
|
|
16
|
+
geofence_id: any[];
|
|
17
|
+
geofence_ids?: any[];
|
|
18
|
+
demand: number | Record<string, number>;
|
|
19
|
+
vehicle_labels?: {
|
|
20
|
+
or?: string[];
|
|
21
|
+
and?: string[];
|
|
22
|
+
};
|
|
23
|
+
booking_uid: string;
|
|
24
|
+
}
|
|
25
|
+
interface PipelineObject {
|
|
26
|
+
request: {
|
|
27
|
+
vehicles: Vehicle[];
|
|
28
|
+
nodes: Node[];
|
|
29
|
+
};
|
|
30
|
+
result: {
|
|
31
|
+
rejected_bookings: {
|
|
32
|
+
uid: string;
|
|
33
|
+
}[];
|
|
34
|
+
vehicles: Record<string, any>;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export declare function analyzePipelineObject(pipelineObject: PipelineObject): Record<string, any>;
|
|
38
|
+
export type { PipelineObject, Vehicle, Node };
|
|
39
|
+
//# sourceMappingURL=vrptoolbox-analyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vrptoolbox-analyzer.d.ts","sourceRoot":"","sources":["../../sgerplib/sgerp/vrptoolbox-analyzer.ts"],"names":[],"mappings":"AAAA,UAAU,OAAO;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,GAAG,EAAE,CAAC;IACpB,cAAc,EAAE,GAAG,EAAE,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC3C;AAED,UAAU,IAAI;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,uBAAuB,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC7C,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,GAAG,EAAE,CAAC;IACnB,YAAY,CAAC,EAAE,GAAG,EAAE,CAAC;IACrB,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,cAAc,CAAC,EAAE;QACf,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC;QACd,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;KAChB,CAAC;IACF,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,UAAU,cAAc;IACtB,OAAO,EAAE;QACP,QAAQ,EAAE,OAAO,EAAE,CAAC;QACpB,KAAK,EAAE,IAAI,EAAE,CAAC;KACf,CAAC;IACF,MAAM,EAAE;QACN,iBAAiB,EAAE;YAAE,GAAG,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;QACrC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;KAC/B,CAAC;CACH;AAwQD,wBAAgB,qBAAqB,CACnC,cAAc,EAAE,cAAc,GAC7B,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAkCrB;AAED,YAAY,EAAE,cAAc,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC"}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.analyzePipelineObject = analyzePipelineObject;
|
|
4
|
+
const DEFAULT_SEAT_TYPE = "passenger";
|
|
5
|
+
function getCapacity(value, defaultSeatType = DEFAULT_SEAT_TYPE) {
|
|
6
|
+
return typeof value === "object" ? value : { [DEFAULT_SEAT_TYPE]: value };
|
|
7
|
+
}
|
|
8
|
+
function checkCharacteristics(node, vehicle) {
|
|
9
|
+
const reasons = [];
|
|
10
|
+
const messages = [];
|
|
11
|
+
if (!vehicle.characteristics) {
|
|
12
|
+
return [reasons, messages];
|
|
13
|
+
}
|
|
14
|
+
const vehicleChars = vehicle.characteristics;
|
|
15
|
+
const agentId = vehicle.agent_id;
|
|
16
|
+
const nodeType = node.node_type;
|
|
17
|
+
Object.entries(node.vehicle_characteristics).forEach(([char, value]) => {
|
|
18
|
+
const vehicleVar = vehicleChars[char];
|
|
19
|
+
if (vehicleVar === undefined) {
|
|
20
|
+
reasons.push({
|
|
21
|
+
violation: `${char}_availability_violation`,
|
|
22
|
+
vehicle_value: vehicleVar,
|
|
23
|
+
char: char,
|
|
24
|
+
vehicle_characteristics: vehicleChars,
|
|
25
|
+
});
|
|
26
|
+
messages.push(`${nodeType} can not be assigned to vehicle ${agentId} because characteristic ${char} is not available in vehicle ${JSON.stringify(vehicleChars)}`);
|
|
27
|
+
}
|
|
28
|
+
else if (typeof value === "object") {
|
|
29
|
+
const minVal = value.min;
|
|
30
|
+
const maxVal = value.max;
|
|
31
|
+
if (minVal !== undefined && vehicleVar < minVal) {
|
|
32
|
+
reasons.push({
|
|
33
|
+
violation: `${char}_min_violation`,
|
|
34
|
+
vehicle_value: vehicleVar,
|
|
35
|
+
required_min: minVal,
|
|
36
|
+
vehicle: agentId,
|
|
37
|
+
});
|
|
38
|
+
messages.push(`${nodeType} can not be assigned to vehicle ${agentId} because of ${char}.min violation: ${vehicleVar} < ${minVal}`);
|
|
39
|
+
}
|
|
40
|
+
if (maxVal !== undefined && vehicleVar > maxVal) {
|
|
41
|
+
reasons.push({
|
|
42
|
+
violation: `${char}_max_violation`,
|
|
43
|
+
vehicle_value: vehicleVar,
|
|
44
|
+
required_max: maxVal,
|
|
45
|
+
vehicle: agentId,
|
|
46
|
+
});
|
|
47
|
+
messages.push(`${nodeType} can not be assigned to vehicle ${agentId} because of ${char}.max violation: ${vehicleVar} > ${maxVal}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
else if (value !== vehicleVar) {
|
|
51
|
+
reasons.push({
|
|
52
|
+
violation: `${char}_value_violation`,
|
|
53
|
+
required_value: value,
|
|
54
|
+
vehicle_value: vehicleVar,
|
|
55
|
+
});
|
|
56
|
+
messages.push(`can not be assigned to vehicle ${agentId} - ${char}_value_violation`);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
return [reasons, messages];
|
|
60
|
+
}
|
|
61
|
+
function checkStartTimeEndTimeConstraints(node, vehicle) {
|
|
62
|
+
const reasons = [];
|
|
63
|
+
const messages = [];
|
|
64
|
+
const agentId = vehicle.agent_id;
|
|
65
|
+
const nodeType = node.node_type;
|
|
66
|
+
if (vehicle.start_time !== undefined) {
|
|
67
|
+
if (vehicle.start_time > node.close_time_ts) {
|
|
68
|
+
reasons.push({
|
|
69
|
+
violation: "start_time_violation",
|
|
70
|
+
node_close_time: node.close_time_ts,
|
|
71
|
+
vehicle_start_time: vehicle.start_time,
|
|
72
|
+
});
|
|
73
|
+
messages.push(`${nodeType} can not be assigned to vehicle ${agentId} - start_time_violation: ${vehicle.start_time} > ${node.close_time_ts}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (vehicle.end_time !== undefined) {
|
|
77
|
+
if (vehicle.end_time < node.open_time_ts) {
|
|
78
|
+
reasons.push({
|
|
79
|
+
violation: "end_time_violation",
|
|
80
|
+
node_open_time: node.open_time_ts,
|
|
81
|
+
vehicle_end_time: vehicle.end_time,
|
|
82
|
+
});
|
|
83
|
+
messages.push(`${nodeType} can not be assigned to vehicle ${agentId} - end_time_violation: ${vehicle.end_time} < ${node.open_time_ts}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return [reasons, messages];
|
|
87
|
+
}
|
|
88
|
+
function checkGeofenceConstraints(node, vehicle) {
|
|
89
|
+
const reasons = [];
|
|
90
|
+
const messages = [];
|
|
91
|
+
const agentId = vehicle.agent_id;
|
|
92
|
+
if (node.geofence_ids === undefined && node.geofence_id) {
|
|
93
|
+
node.geofence_ids = [node.geofence_id];
|
|
94
|
+
}
|
|
95
|
+
if (node.geofence_ids && node.geofence_ids.length > 0 && node.geofence_ids[0] !== null) {
|
|
96
|
+
const vehicleGeofenceIds = new Set(vehicle.geofence_ids);
|
|
97
|
+
const nodeGeofenceIds = new Set(node.geofence_ids);
|
|
98
|
+
if (vehicleGeofenceIds.size > 0 &&
|
|
99
|
+
!Array.from(nodeGeofenceIds).some((id) => vehicleGeofenceIds.has(id))) {
|
|
100
|
+
reasons.push({
|
|
101
|
+
violation: "geofence_violation",
|
|
102
|
+
node_geofence_ids: node.geofence_ids,
|
|
103
|
+
vehicle_geofence_ids: vehicle.geofence_ids,
|
|
104
|
+
});
|
|
105
|
+
messages.push(`can not be assigned to vehicle ${agentId} - geofence violations: ${JSON.stringify([...vehicleGeofenceIds])} vs ${JSON.stringify([...nodeGeofenceIds])}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return [reasons, messages];
|
|
109
|
+
}
|
|
110
|
+
function checkVehicleAssignedNodes(node, vehicle) {
|
|
111
|
+
const reasons = [];
|
|
112
|
+
const messages = [];
|
|
113
|
+
const agentId = vehicle.agent_id;
|
|
114
|
+
if (vehicle.assigned_nodes.length > 0) {
|
|
115
|
+
reasons.push({
|
|
116
|
+
violation: "vehicle_has_assigned_nodes",
|
|
117
|
+
});
|
|
118
|
+
messages.push(`vehicle ${agentId} has assigned nodes. Possibly change the first solution strategy to 16 or clear vehicles`);
|
|
119
|
+
}
|
|
120
|
+
return [reasons, messages];
|
|
121
|
+
}
|
|
122
|
+
function checkVehicleLabels(node, vehicle) {
|
|
123
|
+
const reasons = [];
|
|
124
|
+
const messages = [];
|
|
125
|
+
const agentId = vehicle.agent_id;
|
|
126
|
+
const nodeLabels = node.vehicle_labels;
|
|
127
|
+
if (!nodeLabels) {
|
|
128
|
+
return [reasons, messages];
|
|
129
|
+
}
|
|
130
|
+
const labelOrValues = new Set(nodeLabels.or || []);
|
|
131
|
+
const labelAndValues = new Set(nodeLabels.and || []);
|
|
132
|
+
const vehicleLabels = new Set(vehicle.labels || []);
|
|
133
|
+
if (labelOrValues.size > 0) {
|
|
134
|
+
if (!Array.from(labelOrValues).some((label) => vehicleLabels.has(label))) {
|
|
135
|
+
reasons.push({
|
|
136
|
+
violation: "vehicle and booking labels do not satisfy each other",
|
|
137
|
+
});
|
|
138
|
+
messages.push(`vehicle ${agentId} has labels ${JSON.stringify([...vehicleLabels])}. Booking: ${JSON.stringify(nodeLabels)}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (labelAndValues.size > 0) {
|
|
142
|
+
if (!Array.from(labelAndValues).every((label) => vehicleLabels.has(label))) {
|
|
143
|
+
reasons.push({
|
|
144
|
+
violation: "vehicle and booking labels do not satisfy each other",
|
|
145
|
+
});
|
|
146
|
+
messages.push(`vehicle ${agentId} has labels ${JSON.stringify([...vehicleLabels])}. Booking: ${JSON.stringify(nodeLabels)}`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return [reasons, messages];
|
|
150
|
+
}
|
|
151
|
+
function analyzeVehicles(nodeGroup, vehicles) {
|
|
152
|
+
const possibleReasons = {
|
|
153
|
+
messages: [],
|
|
154
|
+
impossibleVehicles: new Set(),
|
|
155
|
+
comments: [],
|
|
156
|
+
};
|
|
157
|
+
nodeGroup.forEach((node) => {
|
|
158
|
+
vehicles.forEach((vehicle) => {
|
|
159
|
+
const [reasons1, messages1] = checkCharacteristics(node, vehicle);
|
|
160
|
+
const [reasons2, messages2] = checkStartTimeEndTimeConstraints(node, vehicle);
|
|
161
|
+
const [reasons3, messages3] = checkGeofenceConstraints(node, vehicle);
|
|
162
|
+
const [reasons4, messages4] = checkVehicleAssignedNodes(node, vehicle);
|
|
163
|
+
const [reasons5, messages5] = checkVehicleLabels(node, vehicle);
|
|
164
|
+
if (reasons1.length > 0 ||
|
|
165
|
+
reasons2.length > 0 ||
|
|
166
|
+
reasons3.length > 0 ||
|
|
167
|
+
reasons4.length > 0 ||
|
|
168
|
+
reasons5.length > 0) {
|
|
169
|
+
possibleReasons.messages.push(...messages1, ...messages2, ...messages3, ...messages4, ...messages5);
|
|
170
|
+
possibleReasons.impossibleVehicles.add(vehicle.agent_id);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
possibleReasons.impossibleVehicles = Array.from(possibleReasons.impossibleVehicles);
|
|
175
|
+
possibleReasons.impossibleVehiclesCount =
|
|
176
|
+
possibleReasons.impossibleVehicles.length;
|
|
177
|
+
if (possibleReasons.impossibleVehiclesCount === vehicles.length) {
|
|
178
|
+
possibleReasons.comments.push("all vehicles are impossible");
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
const possibleVehicles = vehicles
|
|
182
|
+
.filter((vehicle) => !possibleReasons.impossibleVehicles.includes(vehicle.agent_id))
|
|
183
|
+
.map((vehicle) => vehicle.agent_id);
|
|
184
|
+
possibleReasons.comments.push({ possibleVehicles });
|
|
185
|
+
}
|
|
186
|
+
return possibleReasons;
|
|
187
|
+
}
|
|
188
|
+
function analyzePipelineObject(pipelineObject) {
|
|
189
|
+
const { vehicles, nodes } = pipelineObject.request;
|
|
190
|
+
const { rejected_bookings, vehicles: resultedVehicles } = pipelineObject.result;
|
|
191
|
+
const rejectedUids = new Set(rejected_bookings.map((b) => b.uid));
|
|
192
|
+
const rejectedNodes = nodes.filter((node) => rejectedUids.has(node.booking_uid));
|
|
193
|
+
const emptyVehicleUids = new Set(Object.entries(resultedVehicles)
|
|
194
|
+
.filter(([_, item]) => !item)
|
|
195
|
+
.map(([key, _]) => key));
|
|
196
|
+
const emptyVehicles = vehicles; // .filter(v => emptyVehicleUids.has(v.agent_id));
|
|
197
|
+
const possibleReasons = {};
|
|
198
|
+
const bookingNodes = {};
|
|
199
|
+
rejectedNodes.forEach((node) => {
|
|
200
|
+
const { booking_uid } = node;
|
|
201
|
+
if (!bookingNodes[booking_uid]) {
|
|
202
|
+
bookingNodes[booking_uid] = [];
|
|
203
|
+
}
|
|
204
|
+
bookingNodes[booking_uid].push(node);
|
|
205
|
+
});
|
|
206
|
+
Object.entries(bookingNodes).forEach(([booking_uid, node_group]) => {
|
|
207
|
+
possibleReasons[booking_uid] = analyzeVehicles(node_group, emptyVehicles);
|
|
208
|
+
});
|
|
209
|
+
return possibleReasons;
|
|
210
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { OptimizationJob } from './types/job';
|
|
2
|
+
import { Collection } from '../sgerp/collection';
|
|
3
|
+
/**
|
|
4
|
+
* Collection class for VRP Toolbox Optimization Jobs
|
|
5
|
+
*/
|
|
6
|
+
export declare class OptimizationJobCollection extends Collection<OptimizationJob> {
|
|
7
|
+
private baseUrl;
|
|
8
|
+
private username?;
|
|
9
|
+
private password?;
|
|
10
|
+
constructor(baseUrl: string, username?: string, password?: string);
|
|
11
|
+
/**
|
|
12
|
+
* Set authentication credentials
|
|
13
|
+
*/
|
|
14
|
+
setCredentials(username: string, password: string): void;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=collection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../../sgerplib/vrptoolbox/collection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAA0B,MAAM,aAAa,CAAC;AAE3E,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAqBjD;;GAEG;AACH,qBAAa,yBAA0B,SAAQ,UAAU,CAAC,eAAe,CAAC;IACxE,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAC,CAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,CAAS;gBAEd,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM;IAqCjE;;OAEG;IACH,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;CAIzD"}
|