@gpe-sistemas/tripero-node 0.1.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/LICENSE +21 -0
- package/README.md +674 -0
- package/dist/client/TriperoClient.d.ts +137 -0
- package/dist/client/TriperoClient.d.ts.map +1 -0
- package/dist/client/TriperoClient.js +386 -0
- package/dist/client/TriperoClient.js.map +1 -0
- package/dist/client/TriperoHttpClient.d.ts +161 -0
- package/dist/client/TriperoHttpClient.d.ts.map +1 -0
- package/dist/client/TriperoHttpClient.js +121 -0
- package/dist/client/TriperoHttpClient.js.map +1 -0
- package/dist/client/constants.d.ts +51 -0
- package/dist/client/constants.d.ts.map +1 -0
- package/dist/client/constants.js +53 -0
- package/dist/client/constants.js.map +1 -0
- package/dist/client/index.d.ts +5 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +15 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/logger.d.ts +17 -0
- package/dist/client/logger.d.ts.map +1 -0
- package/dist/client/logger.js +51 -0
- package/dist/client/logger.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/interfaces/config.interface.d.ts +120 -0
- package/dist/interfaces/config.interface.d.ts.map +1 -0
- package/dist/interfaces/config.interface.js +3 -0
- package/dist/interfaces/config.interface.js.map +1 -0
- package/dist/interfaces/events.interface.d.ts +199 -0
- package/dist/interfaces/events.interface.d.ts.map +1 -0
- package/dist/interfaces/events.interface.js +7 -0
- package/dist/interfaces/events.interface.js.map +1 -0
- package/dist/interfaces/index.d.ts +3 -0
- package/dist/interfaces/index.d.ts.map +1 -0
- package/dist/interfaces/index.js +19 -0
- package/dist/interfaces/index.js.map +1 -0
- package/dist/nestjs/decorators.d.ts +53 -0
- package/dist/nestjs/decorators.d.ts.map +1 -0
- package/dist/nestjs/decorators.js +90 -0
- package/dist/nestjs/decorators.js.map +1 -0
- package/dist/nestjs/index.d.ts +3 -0
- package/dist/nestjs/index.d.ts.map +1 -0
- package/dist/nestjs/index.js +19 -0
- package/dist/nestjs/index.js.map +1 -0
- package/dist/nestjs/tripero.module.d.ts +84 -0
- package/dist/nestjs/tripero.module.d.ts.map +1 -0
- package/dist/nestjs/tripero.module.js +146 -0
- package/dist/nestjs/tripero.module.js.map +1 -0
- package/package.json +92 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 GPE Sistemas
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,674 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/GPE-Sistemas/tripero-node/main/docs/logo.png" width="200" alt="Tripero Node Logo" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">tripero-node</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<strong>Node.js/TypeScript SDK for Tripero GPS Trip Detection Service</strong>
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://www.npmjs.com/package/@gpe-sistemas/tripero-node"><img src="https://img.shields.io/npm/v/@gpe-sistemas/tripero-node.svg" alt="npm version"></a>
|
|
13
|
+
<a href="https://www.npmjs.com/package/@gpe-sistemas/tripero-node"><img src="https://img.shields.io/npm/dm/@gpe-sistemas/tripero-node.svg" alt="npm downloads"></a>
|
|
14
|
+
<a href="https://github.com/GPE-Sistemas/tripero-node/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License"></a>
|
|
15
|
+
<a href="https://www.typescriptlang.org/"><img src="https://img.shields.io/badge/TypeScript-5.0-blue.svg" alt="TypeScript"></a>
|
|
16
|
+
<a href="https://nodejs.org/"><img src="https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen.svg" alt="Node.js"></a>
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
<p align="center">
|
|
20
|
+
<a href="#-features">Features</a> •
|
|
21
|
+
<a href="#-installation">Installation</a> •
|
|
22
|
+
<a href="#-quick-start">Quick Start</a> •
|
|
23
|
+
<a href="#-api-reference">API</a> •
|
|
24
|
+
<a href="#-nestjs-integration">NestJS</a> •
|
|
25
|
+
<a href="#-events">Events</a> •
|
|
26
|
+
<a href="#-contributing">Contributing</a>
|
|
27
|
+
</p>
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## 📋 Overview
|
|
32
|
+
|
|
33
|
+
**tripero-node** is the official Node.js/TypeScript SDK for [Tripero](https://github.com/GPE-Sistemas/tripero), an intelligent GPS trip detection and stop analysis microservice. This SDK simplifies integration with Tripero by providing:
|
|
34
|
+
|
|
35
|
+
- **Type-safe API** - Full TypeScript support with complete type definitions
|
|
36
|
+
- **Redis PubSub abstraction** - Simple methods for publishing positions and subscribing to events
|
|
37
|
+
- **HTTP client** - Access Tripero's REST API for queries and configuration
|
|
38
|
+
- **NestJS integration** - Dedicated module with dependency injection support
|
|
39
|
+
- **Fire-and-forget** - Non-blocking async operations by default
|
|
40
|
+
|
|
41
|
+
## ✨ Features
|
|
42
|
+
|
|
43
|
+
| Feature | Description |
|
|
44
|
+
|---------|-------------|
|
|
45
|
+
| 🔌 **Easy Integration** | Simple API to publish GPS positions and receive trip events |
|
|
46
|
+
| 📡 **Real-time Events** | Subscribe to trip starts, completions, stops, and state changes |
|
|
47
|
+
| 🔒 **Type Safety** | Full TypeScript definitions for all events and configurations |
|
|
48
|
+
| ⚡ **High Performance** | Fire-and-forget publishing with Redis pipelines for batch operations |
|
|
49
|
+
| 🏗️ **NestJS Ready** | First-class NestJS support with module, service, and decorators |
|
|
50
|
+
| 🔧 **Configurable** | Flexible configuration with sensible defaults |
|
|
51
|
+
| 📊 **HTTP API Access** | Query tracker status, trips, stops, and configure odometers |
|
|
52
|
+
| 🏷️ **Metadata Support** | Multi-tenancy support with custom metadata propagation |
|
|
53
|
+
|
|
54
|
+
## 📦 Installation
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# npm
|
|
58
|
+
npm install @gpe-sistemas/tripero-node
|
|
59
|
+
|
|
60
|
+
# yarn
|
|
61
|
+
yarn add @gpe-sistemas/tripero-node
|
|
62
|
+
|
|
63
|
+
# pnpm
|
|
64
|
+
pnpm add @gpe-sistemas/tripero-node
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Peer Dependencies
|
|
68
|
+
|
|
69
|
+
For NestJS integration (optional):
|
|
70
|
+
```bash
|
|
71
|
+
npm install @nestjs/common @nestjs/core
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## 🚀 Quick Start
|
|
75
|
+
|
|
76
|
+
### Basic Usage (Node.js)
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
import { TriperoClient } from '@gpe-sistemas/tripero-node';
|
|
80
|
+
|
|
81
|
+
// Create client
|
|
82
|
+
const tripero = new TriperoClient({
|
|
83
|
+
redis: {
|
|
84
|
+
host: 'localhost',
|
|
85
|
+
port: 6379,
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Connect to Redis
|
|
90
|
+
await tripero.connect();
|
|
91
|
+
|
|
92
|
+
// Publish a GPS position
|
|
93
|
+
await tripero.publishPosition({
|
|
94
|
+
deviceId: 'VEHICLE-001',
|
|
95
|
+
timestamp: Date.now(),
|
|
96
|
+
latitude: -34.6037,
|
|
97
|
+
longitude: -58.3816,
|
|
98
|
+
speed: 45,
|
|
99
|
+
ignition: true,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// Subscribe to trip events
|
|
103
|
+
tripero.on('trip:started', (event) => {
|
|
104
|
+
console.log(`🚗 Trip started: ${event.tripId}`);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
tripero.on('trip:completed', (event) => {
|
|
108
|
+
console.log(`🏁 Trip completed: ${event.tripId}`);
|
|
109
|
+
console.log(` Distance: ${event.distance}m`);
|
|
110
|
+
console.log(` Duration: ${event.duration}s`);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
await tripero.subscribe();
|
|
114
|
+
|
|
115
|
+
// Cleanup on shutdown
|
|
116
|
+
process.on('SIGINT', async () => {
|
|
117
|
+
await tripero.disconnect();
|
|
118
|
+
process.exit(0);
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### With HTTP API
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
const tripero = new TriperoClient({
|
|
126
|
+
redis: {
|
|
127
|
+
host: 'redis-tripero-service',
|
|
128
|
+
port: 6379,
|
|
129
|
+
},
|
|
130
|
+
http: {
|
|
131
|
+
baseUrl: 'http://tripero-service:3001',
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
await tripero.connect();
|
|
136
|
+
|
|
137
|
+
// Get tracker status
|
|
138
|
+
const status = await tripero.getTrackerStatus('VEHICLE-001');
|
|
139
|
+
console.log(`State: ${status.data.currentState.state}`);
|
|
140
|
+
console.log(`Odometer: ${status.data.odometer.totalKm} km`);
|
|
141
|
+
|
|
142
|
+
// Get trip history
|
|
143
|
+
const trips = await tripero.getTrips({
|
|
144
|
+
deviceId: 'VEHICLE-001',
|
|
145
|
+
from: new Date('2024-01-01'),
|
|
146
|
+
to: new Date(),
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// Set odometer (sync with vehicle's real odometer)
|
|
150
|
+
await tripero.setOdometer('VEHICLE-001', 125000000, 'vehicle_sync');
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## 📚 API Reference
|
|
154
|
+
|
|
155
|
+
### TriperoClient
|
|
156
|
+
|
|
157
|
+
The main class for interacting with Tripero.
|
|
158
|
+
|
|
159
|
+
#### Constructor
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
new TriperoClient(options: TriperoClientOptions)
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
#### Configuration Options
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
interface TriperoClientOptions {
|
|
169
|
+
redis: {
|
|
170
|
+
host?: string; // Default: 'localhost'
|
|
171
|
+
port?: number; // Default: 6379
|
|
172
|
+
db?: number; // Default: 0
|
|
173
|
+
password?: string; // Optional
|
|
174
|
+
username?: string; // Optional (Redis 6+ ACL)
|
|
175
|
+
keyPrefix?: string; // Default: 'tripero:'
|
|
176
|
+
};
|
|
177
|
+
http?: {
|
|
178
|
+
baseUrl: string; // Tripero HTTP API URL
|
|
179
|
+
timeout?: number; // Default: 10000ms
|
|
180
|
+
headers?: Record<string, string>;
|
|
181
|
+
};
|
|
182
|
+
options?: {
|
|
183
|
+
enableRetry?: boolean; // Default: false
|
|
184
|
+
enableOfflineQueue?: boolean; // Default: false
|
|
185
|
+
throwOnError?: boolean; // Default: false
|
|
186
|
+
logLevel?: 'debug' | 'info' | 'warn' | 'error' | 'silent';
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Connection Methods
|
|
192
|
+
|
|
193
|
+
| Method | Description |
|
|
194
|
+
|--------|-------------|
|
|
195
|
+
| `connect()` | Connect to Redis server |
|
|
196
|
+
| `disconnect()` | Disconnect from Redis server |
|
|
197
|
+
| `health()` | Check Redis connection health |
|
|
198
|
+
| `healthHttp()` | Check Tripero HTTP API health (requires `http` config) |
|
|
199
|
+
|
|
200
|
+
### Publishing Methods
|
|
201
|
+
|
|
202
|
+
#### `publishPosition(position: PositionEvent): Promise<void>`
|
|
203
|
+
|
|
204
|
+
Publish a GPS position to Tripero.
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
await tripero.publishPosition({
|
|
208
|
+
deviceId: 'VEHICLE-001', // Required: unique device identifier
|
|
209
|
+
timestamp: Date.now(), // Required: Unix timestamp in ms
|
|
210
|
+
latitude: -34.6037, // Required: -90 to 90
|
|
211
|
+
longitude: -58.3816, // Required: -180 to 180
|
|
212
|
+
speed: 45, // Required: km/h
|
|
213
|
+
ignition: true, // Optional: engine state
|
|
214
|
+
heading: 180, // Optional: degrees 0-360
|
|
215
|
+
altitude: 25, // Optional: meters
|
|
216
|
+
accuracy: 5, // Optional: GPS accuracy in meters
|
|
217
|
+
satellites: 12, // Optional: number of satellites
|
|
218
|
+
metadata: { // Optional: custom data
|
|
219
|
+
tenant_id: 'acme-corp',
|
|
220
|
+
fleet_id: 'delivery',
|
|
221
|
+
driver_id: 'driver-123',
|
|
222
|
+
},
|
|
223
|
+
});
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
#### `publishPositions(positions: PositionEvent[]): Promise<void>`
|
|
227
|
+
|
|
228
|
+
Publish multiple positions in a single batch (uses Redis pipeline for performance).
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
await tripero.publishPositions([
|
|
232
|
+
{ deviceId: 'V-001', timestamp: Date.now(), latitude: -34.60, longitude: -58.38, speed: 45 },
|
|
233
|
+
{ deviceId: 'V-002', timestamp: Date.now(), latitude: -34.61, longitude: -58.39, speed: 30 },
|
|
234
|
+
{ deviceId: 'V-003', timestamp: Date.now(), latitude: -34.62, longitude: -58.40, speed: 55 },
|
|
235
|
+
]);
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
#### `publishIgnitionEvent(event: IgnitionEvent): Promise<void>`
|
|
239
|
+
|
|
240
|
+
Publish an ignition state change.
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
await tripero.publishIgnitionEvent({
|
|
244
|
+
deviceId: 'VEHICLE-001',
|
|
245
|
+
timestamp: Date.now(),
|
|
246
|
+
ignition: true, // true = ON, false = OFF
|
|
247
|
+
latitude: -34.6037,
|
|
248
|
+
longitude: -58.3816,
|
|
249
|
+
});
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Subscription Methods
|
|
253
|
+
|
|
254
|
+
#### `on(eventType, handler): void`
|
|
255
|
+
|
|
256
|
+
Register an event handler.
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
tripero.on('trip:started', (event: TripStartedEvent) => {
|
|
260
|
+
console.log(`Trip ${event.tripId} started at ${event.startTime}`);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
tripero.on('trip:completed', (event: TripCompletedEvent) => {
|
|
264
|
+
console.log(`Trip ${event.tripId}: ${event.distance}m in ${event.duration}s`);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
tripero.on('stop:started', (event: StopStartedEvent) => {
|
|
268
|
+
console.log(`Stop detected: ${event.reason}`);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
tripero.on('tracker:state:changed', (event: TrackerStateChangedEvent) => {
|
|
272
|
+
console.log(`${event.previousState} → ${event.currentState}`);
|
|
273
|
+
});
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
#### `off(eventType, handler): void`
|
|
277
|
+
|
|
278
|
+
Remove an event handler.
|
|
279
|
+
|
|
280
|
+
#### `subscribe(): Promise<void>`
|
|
281
|
+
|
|
282
|
+
Start listening to all registered events.
|
|
283
|
+
|
|
284
|
+
#### `unsubscribe(): Promise<void>`
|
|
285
|
+
|
|
286
|
+
Stop listening to events.
|
|
287
|
+
|
|
288
|
+
### HTTP API Methods
|
|
289
|
+
|
|
290
|
+
> **Note:** These methods require the `http` configuration option.
|
|
291
|
+
|
|
292
|
+
#### `getTrackerStatus(trackerId: string): Promise<TrackerStatusResponse>`
|
|
293
|
+
|
|
294
|
+
Get real-time status of a tracker including state, odometer, position, and statistics.
|
|
295
|
+
|
|
296
|
+
#### `setOdometer(trackerId: string, meters: number, reason?: string): Promise<OdometerSetResponse>`
|
|
297
|
+
|
|
298
|
+
Set the odometer offset to sync with the vehicle's real odometer.
|
|
299
|
+
|
|
300
|
+
#### `getTrips(options: ReportQueryOptions): Promise<TripReport[]>`
|
|
301
|
+
|
|
302
|
+
Query historical trips.
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
const trips = await tripero.getTrips({
|
|
306
|
+
deviceId: 'VEHICLE-001', // or ['V-001', 'V-002'] or 'all'
|
|
307
|
+
from: new Date('2024-01-01'),
|
|
308
|
+
to: new Date(),
|
|
309
|
+
tenantId: 'acme-corp', // Optional: filter by tenant
|
|
310
|
+
fleetId: 'delivery', // Optional: filter by fleet
|
|
311
|
+
});
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
#### `getStops(options: ReportQueryOptions): Promise<StopReport[]>`
|
|
315
|
+
|
|
316
|
+
Query historical stops with the same options as `getTrips()`.
|
|
317
|
+
|
|
318
|
+
## 🏗️ NestJS Integration
|
|
319
|
+
|
|
320
|
+
### Module Setup
|
|
321
|
+
|
|
322
|
+
```typescript
|
|
323
|
+
// app.module.ts
|
|
324
|
+
import { Module } from '@nestjs/common';
|
|
325
|
+
import { TriperoModule } from '@gpe-sistemas/tripero-node/nestjs';
|
|
326
|
+
|
|
327
|
+
@Module({
|
|
328
|
+
imports: [
|
|
329
|
+
// Static configuration
|
|
330
|
+
TriperoModule.forRoot({
|
|
331
|
+
redis: {
|
|
332
|
+
host: 'redis-tripero-service',
|
|
333
|
+
port: 6379,
|
|
334
|
+
},
|
|
335
|
+
http: {
|
|
336
|
+
baseUrl: 'http://tripero-service:3001',
|
|
337
|
+
},
|
|
338
|
+
}),
|
|
339
|
+
],
|
|
340
|
+
})
|
|
341
|
+
export class AppModule {}
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### Async Configuration
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
// app.module.ts
|
|
348
|
+
import { Module } from '@nestjs/common';
|
|
349
|
+
import { ConfigModule, ConfigService } from '@nestjs/config';
|
|
350
|
+
import { TriperoModule } from '@gpe-sistemas/tripero-node/nestjs';
|
|
351
|
+
|
|
352
|
+
@Module({
|
|
353
|
+
imports: [
|
|
354
|
+
ConfigModule.forRoot(),
|
|
355
|
+
TriperoModule.forRootAsync({
|
|
356
|
+
imports: [ConfigModule],
|
|
357
|
+
inject: [ConfigService],
|
|
358
|
+
useFactory: (config: ConfigService) => ({
|
|
359
|
+
redis: {
|
|
360
|
+
host: config.get('TRIPERO_REDIS_HOST', 'localhost'),
|
|
361
|
+
port: config.get('TRIPERO_REDIS_PORT', 6379),
|
|
362
|
+
keyPrefix: config.get('TRIPERO_KEY_PREFIX', 'tripero:'),
|
|
363
|
+
},
|
|
364
|
+
http: {
|
|
365
|
+
baseUrl: config.get('TRIPERO_HTTP_URL'),
|
|
366
|
+
},
|
|
367
|
+
}),
|
|
368
|
+
}),
|
|
369
|
+
],
|
|
370
|
+
})
|
|
371
|
+
export class AppModule {}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### Using the Service
|
|
375
|
+
|
|
376
|
+
```typescript
|
|
377
|
+
// position.service.ts
|
|
378
|
+
import { Injectable } from '@nestjs/common';
|
|
379
|
+
import { TriperoService } from '@gpe-sistemas/tripero-node/nestjs';
|
|
380
|
+
|
|
381
|
+
@Injectable()
|
|
382
|
+
export class PositionService {
|
|
383
|
+
constructor(private readonly tripero: TriperoService) {}
|
|
384
|
+
|
|
385
|
+
async handleGpsData(data: GpsData) {
|
|
386
|
+
await this.tripero.publishPosition({
|
|
387
|
+
deviceId: data.imei,
|
|
388
|
+
timestamp: Date.now(),
|
|
389
|
+
latitude: data.lat,
|
|
390
|
+
longitude: data.lon,
|
|
391
|
+
speed: data.speed,
|
|
392
|
+
ignition: data.acc,
|
|
393
|
+
metadata: {
|
|
394
|
+
tenant_id: data.tenantId,
|
|
395
|
+
},
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
async getVehicleStatus(deviceId: string) {
|
|
400
|
+
return this.tripero.getTrackerStatus(deviceId);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
### Event Decorators
|
|
406
|
+
|
|
407
|
+
```typescript
|
|
408
|
+
// trip-listener.service.ts
|
|
409
|
+
import { Injectable, OnModuleInit } from '@nestjs/common';
|
|
410
|
+
import { TriperoService } from '@gpe-sistemas/tripero-node/nestjs';
|
|
411
|
+
import type { TripStartedEvent, TripCompletedEvent } from '@gpe-sistemas/tripero-node';
|
|
412
|
+
|
|
413
|
+
@Injectable()
|
|
414
|
+
export class TripListenerService implements OnModuleInit {
|
|
415
|
+
constructor(private readonly tripero: TriperoService) {}
|
|
416
|
+
|
|
417
|
+
async onModuleInit() {
|
|
418
|
+
// Register event handlers
|
|
419
|
+
this.tripero.on('trip:started', this.handleTripStarted.bind(this));
|
|
420
|
+
this.tripero.on('trip:completed', this.handleTripCompleted.bind(this));
|
|
421
|
+
|
|
422
|
+
// Start listening
|
|
423
|
+
await this.tripero.subscribe();
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
private handleTripStarted(event: TripStartedEvent) {
|
|
427
|
+
console.log(`🚗 Trip started: ${event.tripId}`);
|
|
428
|
+
// Send notification, update dashboard, etc.
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
private handleTripCompleted(event: TripCompletedEvent) {
|
|
432
|
+
console.log(`🏁 Trip completed: ${event.tripId}`);
|
|
433
|
+
console.log(` Distance: ${event.distance}m`);
|
|
434
|
+
console.log(` Duration: ${event.duration}s`);
|
|
435
|
+
// Generate report, calculate fuel, etc.
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
## 📡 Events
|
|
441
|
+
|
|
442
|
+
### Input Events (publish to Tripero)
|
|
443
|
+
|
|
444
|
+
| Channel | Method | Description |
|
|
445
|
+
|---------|--------|-------------|
|
|
446
|
+
| `position:new` | `publishPosition()` | GPS position data |
|
|
447
|
+
| `ignition:changed` | `publishIgnitionEvent()` | Ignition state changes |
|
|
448
|
+
|
|
449
|
+
### Output Events (receive from Tripero)
|
|
450
|
+
|
|
451
|
+
| Event | Description | Key Fields |
|
|
452
|
+
|-------|-------------|------------|
|
|
453
|
+
| `tracker:state:changed` | Tracker state transition | `previousState`, `currentState`, `odometer` |
|
|
454
|
+
| `trip:started` | Trip has started | `tripId`, `deviceId`, `startTime`, `startLocation` |
|
|
455
|
+
| `trip:completed` | Trip has ended | `tripId`, `distance`, `duration`, `avgSpeed`, `maxSpeed` |
|
|
456
|
+
| `stop:started` | Stop detected during trip | `stopId`, `tripId`, `location`, `reason` |
|
|
457
|
+
| `stop:completed` | Stop has ended | `stopId`, `duration` |
|
|
458
|
+
| `position:rejected` | Position failed validation | `deviceId`, `reason` |
|
|
459
|
+
|
|
460
|
+
### Tracker States
|
|
461
|
+
|
|
462
|
+
```
|
|
463
|
+
STOPPED ←→ IDLE ←→ MOVING
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
| State | Description |
|
|
467
|
+
|-------|-------------|
|
|
468
|
+
| `STOPPED` | Vehicle completely stopped (ignition OFF) |
|
|
469
|
+
| `IDLE` | Engine running but not moving (ignition ON, speed ≈ 0) |
|
|
470
|
+
| `MOVING` | Vehicle in motion (ignition ON, speed > threshold) |
|
|
471
|
+
|
|
472
|
+
## 🏷️ Metadata Support
|
|
473
|
+
|
|
474
|
+
Tripero supports custom metadata that propagates automatically from positions to trips and stops:
|
|
475
|
+
|
|
476
|
+
```typescript
|
|
477
|
+
await tripero.publishPosition({
|
|
478
|
+
deviceId: 'VEHICLE-001',
|
|
479
|
+
timestamp: Date.now(),
|
|
480
|
+
latitude: -34.6037,
|
|
481
|
+
longitude: -58.3816,
|
|
482
|
+
speed: 45,
|
|
483
|
+
metadata: {
|
|
484
|
+
// Optimized fields (B-tree index, ~1-2ms queries)
|
|
485
|
+
tenant_id: 'acme-corp',
|
|
486
|
+
client_id: 'client-123',
|
|
487
|
+
fleet_id: 'delivery-trucks',
|
|
488
|
+
|
|
489
|
+
// Custom fields (GIN index, ~5-10ms queries)
|
|
490
|
+
driver_id: 'driver-456',
|
|
491
|
+
route_number: 'R42',
|
|
492
|
+
delivery_id: 'DEL-789',
|
|
493
|
+
},
|
|
494
|
+
});
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
Query trips/stops by metadata:
|
|
498
|
+
|
|
499
|
+
```typescript
|
|
500
|
+
const trips = await tripero.getTrips({
|
|
501
|
+
deviceId: 'all',
|
|
502
|
+
from: new Date('2024-01-01'),
|
|
503
|
+
to: new Date(),
|
|
504
|
+
tenantId: 'acme-corp', // Fast query (~1-2ms)
|
|
505
|
+
fleetId: 'delivery-trucks', // Fast query (~1-2ms)
|
|
506
|
+
});
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
## ⚙️ Configuration
|
|
510
|
+
|
|
511
|
+
### Environment Variables
|
|
512
|
+
|
|
513
|
+
When using with NestJS ConfigService:
|
|
514
|
+
|
|
515
|
+
| Variable | Description | Default |
|
|
516
|
+
|----------|-------------|---------|
|
|
517
|
+
| `TRIPERO_REDIS_HOST` | Redis server host | `localhost` |
|
|
518
|
+
| `TRIPERO_REDIS_PORT` | Redis server port | `6379` |
|
|
519
|
+
| `TRIPERO_REDIS_DB` | Redis database number | `0` |
|
|
520
|
+
| `TRIPERO_REDIS_PASSWORD` | Redis password | - |
|
|
521
|
+
| `TRIPERO_KEY_PREFIX` | Redis key/channel prefix | `tripero:` |
|
|
522
|
+
| `TRIPERO_HTTP_URL` | Tripero HTTP API URL | - |
|
|
523
|
+
| `TRIPERO_LOG_LEVEL` | Log level | `info` |
|
|
524
|
+
|
|
525
|
+
### Redis Key Prefix
|
|
526
|
+
|
|
527
|
+
The SDK uses `tripero:` as the default key prefix to match Tripero server defaults. This allows sharing a Redis instance with other applications:
|
|
528
|
+
|
|
529
|
+
```typescript
|
|
530
|
+
const tripero = new TriperoClient({
|
|
531
|
+
redis: {
|
|
532
|
+
host: 'shared-redis',
|
|
533
|
+
keyPrefix: 'myapp:tripero:', // Custom prefix
|
|
534
|
+
},
|
|
535
|
+
});
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
> **Important:** The `keyPrefix` must match the `REDIS_KEY_PREFIX` configured in Tripero server.
|
|
539
|
+
|
|
540
|
+
## 🔧 Advanced Usage
|
|
541
|
+
|
|
542
|
+
### Custom Logger
|
|
543
|
+
|
|
544
|
+
```typescript
|
|
545
|
+
import { TriperoClient, TriperoLogger } from '@gpe-sistemas/tripero-node';
|
|
546
|
+
|
|
547
|
+
const customLogger: TriperoLogger = {
|
|
548
|
+
debug: (msg, ...args) => myLogger.debug(msg, ...args),
|
|
549
|
+
info: (msg, ...args) => myLogger.info(msg, ...args),
|
|
550
|
+
warn: (msg, ...args) => myLogger.warn(msg, ...args),
|
|
551
|
+
error: (msg, ...args) => myLogger.error(msg, ...args),
|
|
552
|
+
};
|
|
553
|
+
|
|
554
|
+
const tripero = new TriperoClient({
|
|
555
|
+
redis: { host: 'localhost' },
|
|
556
|
+
options: {
|
|
557
|
+
logger: customLogger,
|
|
558
|
+
},
|
|
559
|
+
});
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
### Error Handling
|
|
563
|
+
|
|
564
|
+
By default, the SDK logs errors but doesn't throw (fire-and-forget). To enable exceptions:
|
|
565
|
+
|
|
566
|
+
```typescript
|
|
567
|
+
const tripero = new TriperoClient({
|
|
568
|
+
redis: { host: 'localhost' },
|
|
569
|
+
options: {
|
|
570
|
+
throwOnError: true,
|
|
571
|
+
},
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
try {
|
|
575
|
+
await tripero.publishPosition(position);
|
|
576
|
+
} catch (error) {
|
|
577
|
+
console.error('Failed to publish:', error);
|
|
578
|
+
}
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
### Connection Retry
|
|
582
|
+
|
|
583
|
+
```typescript
|
|
584
|
+
const tripero = new TriperoClient({
|
|
585
|
+
redis: {
|
|
586
|
+
host: 'localhost',
|
|
587
|
+
redisOptions: {
|
|
588
|
+
retryStrategy: (times) => Math.min(times * 1000, 30000),
|
|
589
|
+
maxRetriesPerRequest: 3,
|
|
590
|
+
},
|
|
591
|
+
},
|
|
592
|
+
options: {
|
|
593
|
+
enableRetry: true,
|
|
594
|
+
enableOfflineQueue: true,
|
|
595
|
+
},
|
|
596
|
+
});
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
## 🤝 Contributing
|
|
600
|
+
|
|
601
|
+
Contributions are welcome! Here's how you can help:
|
|
602
|
+
|
|
603
|
+
1. **Fork** the repository
|
|
604
|
+
2. **Create** a feature branch (`git checkout -b feature/amazing-feature`)
|
|
605
|
+
3. **Commit** your changes (`git commit -m 'Add amazing feature'`)
|
|
606
|
+
4. **Push** to the branch (`git push origin feature/amazing-feature`)
|
|
607
|
+
5. **Open** a Pull Request
|
|
608
|
+
|
|
609
|
+
### Development Setup
|
|
610
|
+
|
|
611
|
+
```bash
|
|
612
|
+
# Clone the repository
|
|
613
|
+
git clone https://github.com/GPE-Sistemas/tripero-node.git
|
|
614
|
+
cd tripero-node
|
|
615
|
+
|
|
616
|
+
# Install dependencies
|
|
617
|
+
npm install
|
|
618
|
+
|
|
619
|
+
# Build
|
|
620
|
+
npm run build
|
|
621
|
+
|
|
622
|
+
# Run tests
|
|
623
|
+
npm test
|
|
624
|
+
|
|
625
|
+
# Lint
|
|
626
|
+
npm run lint
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
### Code Style
|
|
630
|
+
|
|
631
|
+
- Follow TypeScript best practices
|
|
632
|
+
- Use meaningful variable and function names
|
|
633
|
+
- Add JSDoc comments for public APIs
|
|
634
|
+
- Write tests for new features
|
|
635
|
+
|
|
636
|
+
## 📄 License
|
|
637
|
+
|
|
638
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
639
|
+
|
|
640
|
+
```
|
|
641
|
+
MIT License
|
|
642
|
+
|
|
643
|
+
Copyright (c) 2024 GPE Sistemas
|
|
644
|
+
|
|
645
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
646
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
647
|
+
in the Software without restriction, including without limitation the rights
|
|
648
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
649
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
650
|
+
furnished to do so, subject to the following conditions:
|
|
651
|
+
|
|
652
|
+
The above copyright notice and this permission notice shall be included in all
|
|
653
|
+
copies or substantial portions of the Software.
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
## 🔗 Related Projects
|
|
657
|
+
|
|
658
|
+
- **[Tripero](https://github.com/GPE-Sistemas/tripero)** - GPS trip detection microservice
|
|
659
|
+
- **[IRIX](https://github.com/GPE-Sistemas)** - Fleet management platform
|
|
660
|
+
|
|
661
|
+
## 📞 Support
|
|
662
|
+
|
|
663
|
+
- **Issues**: [GitHub Issues](https://github.com/GPE-Sistemas/tripero-node/issues)
|
|
664
|
+
- **Discussions**: [GitHub Discussions](https://github.com/GPE-Sistemas/tripero-node/discussions)
|
|
665
|
+
|
|
666
|
+
---
|
|
667
|
+
|
|
668
|
+
<p align="center">
|
|
669
|
+
Made with ❤️ by <a href="https://gpesistemas.com">GPE Sistemas</a>
|
|
670
|
+
</p>
|
|
671
|
+
|
|
672
|
+
<p align="center">
|
|
673
|
+
<sub>If you find this project useful, please consider giving it a ⭐</sub>
|
|
674
|
+
</p>
|