@fleetbase/fleetops-engine 0.6.20 → 0.6.21
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/custom-entity/form.hbs +14 -14
- package/addon/components/device/details.hbs +92 -43
- package/addon/components/device/form.hbs +108 -60
- package/addon/components/device/form.js +36 -8
- package/addon/components/device/panel-header.hbs +32 -0
- package/addon/components/device/panel-header.js +3 -0
- package/addon/components/driver/form.hbs +1 -1
- package/addon/components/driver/form.js +49 -47
- package/addon/components/entity/form.hbs +7 -5
- package/addon/components/layout/fleet-ops-sidebar.js +12 -12
- package/addon/components/map/drawer/device-event-listing.hbs +58 -0
- package/addon/components/map/drawer/device-event-listing.js +181 -0
- package/addon/components/map/drawer/position-listing.hbs +84 -0
- package/addon/components/map/drawer/position-listing.js +289 -0
- package/addon/components/map/drawer.js +2 -0
- package/addon/components/map/leaflet-live-map.hbs +7 -2
- package/addon/components/order/details/payload.hbs +6 -4
- package/addon/components/order/details/payload.js +2 -0
- package/addon/components/order-config-manager/custom-fields.js +1 -1
- package/addon/components/positions-replay.hbs +333 -0
- package/addon/components/positions-replay.js +372 -0
- package/addon/components/sensor/details.hbs +64 -38
- package/addon/components/sensor/form.hbs +112 -63
- package/addon/components/sensor/form.js +36 -24
- package/addon/components/sensor/panel-header.hbs +32 -0
- package/addon/components/sensor/panel-header.js +3 -0
- package/addon/components/telematic/details.hbs +40 -16
- package/addon/components/telematic/form.hbs +63 -64
- package/addon/components/telematic/form.js +73 -4
- package/addon/components/vehicle/card.hbs +1 -1
- package/addon/controllers/analytics/reports/index/edit.js +1 -1
- package/addon/controllers/connectivity/devices/index/details.js +22 -1
- package/addon/controllers/connectivity/devices/index/edit.js +66 -1
- package/addon/controllers/connectivity/devices/index.js +51 -9
- package/addon/controllers/connectivity/events/index.js +65 -16
- package/addon/controllers/connectivity/sensors/index/details.js +22 -1
- package/addon/controllers/connectivity/sensors/index/edit.js +66 -1
- package/addon/controllers/connectivity/sensors/index.js +66 -6
- package/addon/controllers/connectivity/telematics/index/details.js +22 -1
- package/addon/controllers/connectivity/telematics/index/edit.js +66 -1
- package/addon/controllers/connectivity/telematics/index.js +20 -11
- package/addon/controllers/management/fleets/index/details.js +26 -21
- package/addon/controllers/management/fleets/index/edit.js +9 -6
- package/addon/controllers/management/vehicles/index/details.js +21 -13
- package/addon/controllers/settings/custom-fields.js +6 -0
- package/addon/helpers/get-fleet-ops-option-label.js +11 -0
- package/addon/routes/connectivity/devices/index/details.js +27 -1
- package/addon/routes/connectivity/devices/index/edit.js +27 -1
- package/addon/routes/connectivity/sensors/index/details.js +27 -1
- package/addon/routes/connectivity/sensors/index/edit.js +27 -1
- package/addon/routes/connectivity/telematics/index/details.js +27 -1
- package/addon/routes/connectivity/telematics/index/edit.js +27 -1
- package/addon/routes/management/vehicles/index/details/positions.js +3 -0
- package/addon/routes.js +1 -0
- package/addon/services/movement-tracker.js +81 -30
- package/addon/styles/fleetops-engine.css +157 -0
- package/addon/templates/connectivity/devices/index/details/index.hbs +2 -2
- package/addon/templates/connectivity/devices/index/details.hbs +15 -2
- package/addon/templates/connectivity/devices/index/edit.hbs +1 -1
- package/addon/templates/connectivity/events/index.hbs +1 -1
- package/addon/templates/connectivity/sensors/index/details/index.hbs +2 -2
- package/addon/templates/connectivity/sensors/index/details.hbs +15 -2
- package/addon/templates/connectivity/sensors/index/edit.hbs +1 -1
- package/addon/templates/connectivity/telematics/index/details/index.hbs +2 -2
- package/addon/templates/connectivity/telematics/index/details.hbs +14 -2
- package/addon/templates/connectivity/telematics/index/edit.hbs +1 -1
- package/addon/templates/management/vehicles/index/details/positions.hbs +1 -0
- package/addon/utils/fleet-ops-options.js +95 -0
- package/app/components/device/panel-header.js +1 -0
- package/app/components/map/drawer/device-event-listing.js +1 -0
- package/app/components/map/drawer/position-listing.js +1 -0
- package/app/components/positions-replay.js +1 -0
- package/app/components/sensor/panel-header.js +1 -0
- package/app/helpers/get-fleet-ops-option-label.js +1 -0
- package/app/routes/management/vehicles/index/details/positions.js +1 -0
- package/app/templates/management/vehicles/index/details/positions.js +1 -0
- package/composer.json +1 -1
- package/extension.json +1 -1
- package/package.json +4 -4
- package/server/config/telematics.php +111 -0
- package/server/migrations/2025_10_27_000001_add_telematics_integration_fields.php +70 -0
- package/server/migrations/2025_10_27_171322_fix_device_column_names.php +107 -0
- package/server/migrations/2025_10_27_203023_add_company_uuid_to_device_events_table.php +28 -0
- package/server/src/Console/Commands/ReplayVehicleLocations.php +225 -0
- package/server/src/Contracts/TelematicProviderDescriptor.php +72 -0
- package/server/src/Contracts/TelematicProviderInterface.php +119 -0
- package/server/src/Exceptions/TelematicProviderException.php +14 -0
- package/server/src/Exceptions/TelematicRateLimitExceededException.php +12 -0
- package/server/src/Http/Controllers/Api/v1/DriverController.php +24 -14
- package/server/src/Http/Controllers/Api/v1/VehicleController.php +27 -7
- package/server/src/Http/Controllers/Internal/v1/DeviceController.php +22 -0
- package/server/src/Http/Controllers/Internal/v1/PositionController.php +240 -0
- package/server/src/Http/Controllers/Internal/v1/SensorController.php +11 -0
- package/server/src/Http/Controllers/Internal/v1/TelematicController.php +141 -0
- package/server/src/Http/Controllers/Internal/v1/TelematicWebhookController.php +170 -0
- package/server/src/Http/Filter/DeviceEventFilter.php +68 -0
- package/server/src/Http/Filter/PositionFilter.php +35 -0
- package/server/src/Http/Resources/v1/Position.php +44 -0
- package/server/src/Jobs/ReplayPositions.php +64 -0
- package/server/src/Jobs/SendPositionReplay.php +65 -0
- package/server/src/Jobs/SyncTelematicDevicesJob.php +106 -0
- package/server/src/Jobs/TestTelematicConnectionJob.php +102 -0
- package/server/src/Models/Device.php +72 -10
- package/server/src/Models/DeviceEvent.php +7 -0
- package/server/src/Models/Driver.php +28 -1
- package/server/src/Models/Payload.php +0 -1
- package/server/src/Models/Place.php +4 -1
- package/server/src/Models/Position.php +17 -17
- package/server/src/Models/Sensor.php +78 -13
- package/server/src/Models/Telematic.php +116 -6
- package/server/src/Models/Vehicle.php +8 -11
- package/server/src/Providers/FleetOpsServiceProvider.php +2 -0
- package/server/src/Support/Telematics/Providers/AbstractProvider.php +151 -0
- package/server/src/Support/Telematics/Providers/FlespiProvider.php +182 -0
- package/server/src/Support/Telematics/Providers/GeotabProvider.php +181 -0
- package/server/src/Support/Telematics/Providers/SamsaraProvider.php +177 -0
- package/server/src/Support/Telematics/TelematicProviderRegistry.php +147 -0
- package/server/src/Support/Telematics/TelematicService.php +223 -0
- package/server/src/Support/Utils.php +1 -1
- package/server/src/routes.php +12 -1
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Fleetbase\FleetOps\Support\Telematics;
|
|
4
|
+
|
|
5
|
+
use Fleetbase\FleetOps\Jobs\SyncDevicesJob;
|
|
6
|
+
use Fleetbase\FleetOps\Jobs\TestConnectionJob;
|
|
7
|
+
use Fleetbase\FleetOps\Models\Device;
|
|
8
|
+
use Fleetbase\FleetOps\Models\Telematic;
|
|
9
|
+
use Illuminate\Support\Facades\Crypt;
|
|
10
|
+
use Illuminate\Support\Facades\Validator;
|
|
11
|
+
use Illuminate\Validation\ValidationException;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Class TelematicService.
|
|
15
|
+
*
|
|
16
|
+
* Business logic for telematics management.
|
|
17
|
+
* Handles CRUD operations, connection testing, and device discovery.
|
|
18
|
+
*/
|
|
19
|
+
class TelematicService
|
|
20
|
+
{
|
|
21
|
+
protected TelematicProviderRegistry $registry;
|
|
22
|
+
|
|
23
|
+
public function __construct(TelematicProviderRegistry $registry)
|
|
24
|
+
{
|
|
25
|
+
$this->registry = $registry;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Create a new telematic integration.
|
|
30
|
+
*
|
|
31
|
+
* @throws ValidationException
|
|
32
|
+
*/
|
|
33
|
+
public function create(array $data): Telematic
|
|
34
|
+
{
|
|
35
|
+
// Validate provider exists
|
|
36
|
+
$providerKey = $data['provider_key'];
|
|
37
|
+
$descriptor = $this->registry->findByKey($providerKey);
|
|
38
|
+
|
|
39
|
+
if (!$descriptor) {
|
|
40
|
+
throw ValidationException::withMessages(['provider_key' => ['Invalid provider key.']]);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Validate credentials against provider schema
|
|
44
|
+
$this->validateCredentials($data['credentials'] ?? [], $descriptor->requiredFields);
|
|
45
|
+
|
|
46
|
+
// Test connection if requested
|
|
47
|
+
if ($data['test_connection'] ?? false) {
|
|
48
|
+
$provider = $this->registry->resolve($providerKey);
|
|
49
|
+
$result = $provider->testConnection($data['credentials']);
|
|
50
|
+
|
|
51
|
+
if (!$result['success']) {
|
|
52
|
+
throw ValidationException::withMessages(['credentials' => [$result['message']]]);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Create telematic record
|
|
57
|
+
$telematic = new Telematic();
|
|
58
|
+
$telematic->company_uuid = session('company');
|
|
59
|
+
$telematic->name = $data['name'];
|
|
60
|
+
$telematic->provider = $providerKey;
|
|
61
|
+
$telematic->credentials = Crypt::encryptString(json_encode($data['credentials']));
|
|
62
|
+
$telematic->status = 'active';
|
|
63
|
+
$telematic->meta = $data['meta'] ?? [];
|
|
64
|
+
$telematic->save();
|
|
65
|
+
|
|
66
|
+
return $telematic;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Update a telematic integration.
|
|
71
|
+
*/
|
|
72
|
+
public function update(Telematic $telematic, array $data): Telematic
|
|
73
|
+
{
|
|
74
|
+
if (isset($data['name'])) {
|
|
75
|
+
$telematic->name = $data['name'];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (isset($data['credentials'])) {
|
|
79
|
+
$descriptor = $this->registry->findByKey($telematic->provider);
|
|
80
|
+
$this->validateCredentials($data['credentials'], $descriptor->requiredFields);
|
|
81
|
+
$telematic->credentials = Crypt::encryptString(json_encode($data['credentials']));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (isset($data['status'])) {
|
|
85
|
+
$telematic->status = $data['status'];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (isset($data['meta'])) {
|
|
89
|
+
$telematic->meta = array_merge($telematic->meta ?? [], $data['meta']);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
$telematic->save();
|
|
93
|
+
|
|
94
|
+
return $telematic;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Delete a telematic integration.
|
|
99
|
+
*/
|
|
100
|
+
public function delete(Telematic $telematic): bool
|
|
101
|
+
{
|
|
102
|
+
return $telematic->delete();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Test connection to a provider.
|
|
107
|
+
*
|
|
108
|
+
* @return array|string
|
|
109
|
+
*/
|
|
110
|
+
public function testConnection(Telematic $telematic, bool $async = false)
|
|
111
|
+
{
|
|
112
|
+
if ($async) {
|
|
113
|
+
$job = new TestConnectionJob($telematic);
|
|
114
|
+
dispatch($job);
|
|
115
|
+
|
|
116
|
+
return ['job_id' => $job->getJobId(), 'message' => 'Connection test queued'];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
$provider = $this->registry->resolve($telematic->provider);
|
|
120
|
+
$provider->connect($telematic);
|
|
121
|
+
|
|
122
|
+
$credentials = json_decode(Crypt::decryptString($telematic->credentials), true);
|
|
123
|
+
|
|
124
|
+
return $provider->testConnection($credentials);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Discover devices from a provider.
|
|
129
|
+
*
|
|
130
|
+
* @return string Job ID
|
|
131
|
+
*/
|
|
132
|
+
public function discoverDevices(Telematic $telematic, array $options = []): string
|
|
133
|
+
{
|
|
134
|
+
$job = new SyncDevicesJob($telematic, $options);
|
|
135
|
+
dispatch($job);
|
|
136
|
+
|
|
137
|
+
return $job->getJobId();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Link a device to a telematic.
|
|
142
|
+
*/
|
|
143
|
+
public function linkDevice(Telematic $telematic, array $deviceData): Device
|
|
144
|
+
{
|
|
145
|
+
$device = Device::firstOrNew([
|
|
146
|
+
'telematic_uuid' => $telematic->uuid,
|
|
147
|
+
'external_id' => $deviceData['external_id'],
|
|
148
|
+
]);
|
|
149
|
+
|
|
150
|
+
$device->device_name = $deviceData['device_name'] ?? 'Unknown Device';
|
|
151
|
+
$device->device_model = $deviceData['device_model'] ?? null;
|
|
152
|
+
$device->device_provider = $telematic->provider;
|
|
153
|
+
$device->status = $deviceData['status'] ?? 'active';
|
|
154
|
+
$device->meta = array_merge($device->meta ?? [], $deviceData['meta'] ?? []);
|
|
155
|
+
|
|
156
|
+
$device->save();
|
|
157
|
+
|
|
158
|
+
return $device;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Get devices for a telematic.
|
|
163
|
+
*
|
|
164
|
+
* @return \Illuminate\Database\Eloquent\Collection
|
|
165
|
+
*/
|
|
166
|
+
public function getDevices(Telematic $telematic, array $filters = [])
|
|
167
|
+
{
|
|
168
|
+
$query = Device::where('telematic_uuid', $telematic->uuid);
|
|
169
|
+
|
|
170
|
+
if (isset($filters['status'])) {
|
|
171
|
+
$query->where('status', $filters['status']);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (isset($filters['search'])) {
|
|
175
|
+
$query->where(function ($q) use ($filters) {
|
|
176
|
+
$q->where('device_name', 'like', "%{$filters['search']}%")
|
|
177
|
+
->orWhere('external_id', 'like', "%{$filters['search']}%");
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return $query->get();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Validate credentials against provider schema.
|
|
186
|
+
*
|
|
187
|
+
* @throws ValidationException
|
|
188
|
+
*/
|
|
189
|
+
protected function validateCredentials(array $credentials, array $schema): void
|
|
190
|
+
{
|
|
191
|
+
$rules = [];
|
|
192
|
+
|
|
193
|
+
foreach ($schema as $field) {
|
|
194
|
+
$fieldRules = [];
|
|
195
|
+
|
|
196
|
+
if ($field['required'] ?? true) {
|
|
197
|
+
$fieldRules[] = 'required';
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (isset($field['validation'])) {
|
|
201
|
+
$fieldRules[] = $field['validation'];
|
|
202
|
+
} else {
|
|
203
|
+
$fieldRules[] = 'string';
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
$rules[$field['name']] = implode('|', $fieldRules);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
$validator = Validator::make($credentials, $rules);
|
|
210
|
+
|
|
211
|
+
if ($validator->fails()) {
|
|
212
|
+
throw new ValidationException($validator);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Get decrypted credentials for a telematic.
|
|
218
|
+
*/
|
|
219
|
+
public function getCredentials(Telematic $telematic): array
|
|
220
|
+
{
|
|
221
|
+
return json_decode(Crypt::decryptString($telematic->credentials), true);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
@@ -908,7 +908,7 @@ class Utils extends FleetbaseUtils
|
|
|
908
908
|
|
|
909
909
|
/**
|
|
910
910
|
* Calculates the great-circle distance between two points, with
|
|
911
|
-
* the Vincenty formula. (Using over haversine
|
|
911
|
+
* the Vincenty formula. (Using over haversine due to antipodal point issues).
|
|
912
912
|
*
|
|
913
913
|
* https://en.wikipedia.org/wiki/Great-circle_distance#Formulas
|
|
914
914
|
* https://en.wikipedia.org/wiki/Antipodal_point
|
package/server/src/routes.php
CHANGED
|
@@ -345,6 +345,10 @@ Route::prefix(config('fleetops.api.routing.prefix', null))->namespace('Fleetbase
|
|
|
345
345
|
$router->fleetbaseRoutes('proofs');
|
|
346
346
|
$router->fleetbaseRoutes('purchase-rates');
|
|
347
347
|
$router->fleetbaseRoutes('routes');
|
|
348
|
+
$router->fleetbaseRoutes('positions', function ($router, $controller) {
|
|
349
|
+
$router->post('replay', $controller('replay'));
|
|
350
|
+
$router->post('metrics', $controller('metrics'));
|
|
351
|
+
});
|
|
348
352
|
$router->fleetbaseRoutes(
|
|
349
353
|
'service-areas',
|
|
350
354
|
function ($router, $controller) {
|
|
@@ -399,7 +403,14 @@ Route::prefix(config('fleetops.api.routing.prefix', null))->namespace('Fleetbase
|
|
|
399
403
|
$router->fleetbaseRoutes('devices');
|
|
400
404
|
$router->fleetbaseRoutes('device-events');
|
|
401
405
|
$router->fleetbaseRoutes('sensors');
|
|
402
|
-
$router->fleetbaseRoutes('telematics')
|
|
406
|
+
$router->fleetbaseRoutes('telematics', function ($router, $controller) {
|
|
407
|
+
$router->get('providers', $controller('providers'));
|
|
408
|
+
$router->get('devices', $controller('devices'));
|
|
409
|
+
$router->post('link-device', $controller('linkDevice'));
|
|
410
|
+
$router->post('discover', $controller('discover'));
|
|
411
|
+
$router->post('{id}/test-connection', $controller('testConnection'));
|
|
412
|
+
$router->post('{key}/test-credentials', $controller('testCredentials'));
|
|
413
|
+
});
|
|
403
414
|
$router->fleetbaseRoutes('work-orders');
|
|
404
415
|
$router->fleetbaseRoutes('maintenance');
|
|
405
416
|
$router->fleetbaseRoutes('equipment');
|