Compare commits
19 Commits
79580e16b7
...
main
Author | SHA256 | Date | |
---|---|---|---|
c0ac71ebba | |||
10e8e14cd6 | |||
a9c8525e6e | |||
39c07fcef0 | |||
52d521a6ad | |||
d3848ac1aa | |||
93f0c71a6c | |||
5e4fd59148 | |||
62a2dc2c4a | |||
59dc57a618 | |||
452589d11d | |||
d04bdb3ac1 | |||
a745aaf9d0 | |||
51112b5870 | |||
262b4718fc | |||
4d0e84b84a | |||
f969b0a4c0 | |||
49fe1b4ce3 | |||
34167c4d99 |
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
docker-compose.yml
|
10
README.md
10
README.md
@ -11,10 +11,11 @@ We recommend using [The Things Network](https://www.thethingsnetwork.org/) (TTN)
|
|||||||
- [Database](#database)
|
- [Database](#database)
|
||||||
- [Configuration](#configuration)
|
- [Configuration](#configuration)
|
||||||
2. [TTN Integration](#ttn-integration)
|
2. [TTN Integration](#ttn-integration)
|
||||||
3. [Add a Location Tracker](#add-a-location-tracker)
|
3. [Prometheus Metrics](#prometheus-metrics)
|
||||||
|
4. [Add a Location Tracker](#add-a-location-tracker)
|
||||||
- [Onboard SenseCAP T1000-B](#onboard-sensecap-t1000-b)
|
- [Onboard SenseCAP T1000-B](#onboard-sensecap-t1000-b)
|
||||||
- [Register SenseCAP T1000-B](#register-sensecap-t1000-b)
|
- [Register SenseCAP T1000-B](#register-sensecap-t1000-b)
|
||||||
4. [Testing](#testing)
|
5. [Testing](#testing)
|
||||||
- [Testing Webhook](#testing-webhook)
|
- [Testing Webhook](#testing-webhook)
|
||||||
- [Emulating Wigle API](#emulating-wigle-api)
|
- [Emulating Wigle API](#emulating-wigle-api)
|
||||||
|
|
||||||
@ -49,6 +50,9 @@ Add a addidtional header:
|
|||||||
- Type: `authorization`
|
- Type: `authorization`
|
||||||
- Value: `Bearer your-very-secure-token-from-the-env-file`
|
- Value: `Bearer your-very-secure-token-from-the-env-file`
|
||||||
|
|
||||||
|
### Prometheus Metrics
|
||||||
|
Use `https://your.domain.tld/api/metrics` to retrieve useful insides for monitoring.
|
||||||
|
|
||||||
## Add a Location Tracker
|
## Add a Location Tracker
|
||||||
We use the [SenseCAP T1000-B](https://www.seeedstudio.com/SenseCAP-Card-Tracker-T1000-B-p-5698.html) from seeedstudio because of the fair price and multiple location providers. However, you can use any LoRaWAN-enabled tracker that is compatible with TTN and supports the required payload fields.
|
We use the [SenseCAP T1000-B](https://www.seeedstudio.com/SenseCAP-Card-Tracker-T1000-B-p-5698.html) from seeedstudio because of the fair price and multiple location providers. However, you can use any LoRaWAN-enabled tracker that is compatible with TTN and supports the required payload fields.
|
||||||
|
|
||||||
@ -60,7 +64,7 @@ We use the [SenseCAP T1000-B](https://www.seeedstudio.com/SenseCAP-Card-Tracker-
|
|||||||
5. In the `Settings` tab select `The Things Network` as Platform under `LoRa`
|
5. In the `Settings` tab select `The Things Network` as Platform under `LoRa`
|
||||||
6. Copy `Device EUI`, `AppEUI` and `AppKey`
|
6. Copy `Device EUI`, `AppEUI` and `AppKey`
|
||||||
7. Save LoRa settings
|
7. Save LoRa settings
|
||||||
8. Under `Geolocation` select Geolocation Strategy as `Bluetooth+Wi-Fi+GNSS`
|
8. Under `Geolocation` select Geolocation Strategy as `GNSS+Wi-Fi`
|
||||||
9. Set `GNSS Max Scan Time (s)` to 120
|
9. Set `GNSS Max Scan Time (s)` to 120
|
||||||
10. Save Geolocation settings
|
10. Save Geolocation settings
|
||||||
11. Exit App with return arrow to trigger re-init of SenseCAP T1000-B
|
11. Exit App with return arrow to trigger re-init of SenseCAP T1000-B
|
||||||
|
3
server/.gitignore
vendored
3
server/.gitignore
vendored
@ -309,6 +309,3 @@ cython_debug/
|
|||||||
*.vsix
|
*.vsix
|
||||||
|
|
||||||
config.py
|
config.py
|
||||||
|
|
||||||
#docker
|
|
||||||
docker-compose.yml
|
|
@ -5,7 +5,7 @@ CREATE TABLE IF NOT EXISTS lp_ttn_end_device_uplinks (
|
|||||||
dev_eui VARCHAR(255),
|
dev_eui VARCHAR(255),
|
||||||
join_eui VARCHAR(255),
|
join_eui VARCHAR(255),
|
||||||
dev_addr VARCHAR(255),
|
dev_addr VARCHAR(255),
|
||||||
received_at_utc DATE,
|
received_at_utc DATE NOT NULL,
|
||||||
battery NUMERIC,
|
battery NUMERIC,
|
||||||
created_at_utc TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
created_at_utc TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
updated_at_utc TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
updated_at_utc TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||||
@ -16,6 +16,7 @@ CREATE TABLE IF NOT EXISTS wifi_scan (
|
|||||||
lp_ttn_end_device_uplinks_id UUID,
|
lp_ttn_end_device_uplinks_id UUID,
|
||||||
mac VARCHAR(255),
|
mac VARCHAR(255),
|
||||||
rssi NUMERIC,
|
rssi NUMERIC,
|
||||||
|
scanned_at_utc TIMESTAMP NOT NULL,
|
||||||
created_at_utc TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
created_at_utc TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
updated_at_utc TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
updated_at_utc TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
FOREIGN KEY (lp_ttn_end_device_uplinks_id) REFERENCES lp_ttn_end_device_uplinks(lp_ttn_end_device_uplinks_id)
|
FOREIGN KEY (lp_ttn_end_device_uplinks_id) REFERENCES lp_ttn_end_device_uplinks(lp_ttn_end_device_uplinks_id)
|
||||||
@ -63,6 +64,7 @@ CREATE TABLE IF NOT EXISTS location (
|
|||||||
gnss_longitude DOUBLE,
|
gnss_longitude DOUBLE,
|
||||||
ttn_gw_latitude DOUBLE,
|
ttn_gw_latitude DOUBLE,
|
||||||
ttn_gw_longitude DOUBLE,
|
ttn_gw_longitude DOUBLE,
|
||||||
|
gnss_location_at_utc TIMESTAMP,
|
||||||
created_at_utc TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
created_at_utc TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
updated_at_utc TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
updated_at_utc TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
FOREIGN KEY (lp_ttn_end_device_uplinks_id) REFERENCES lp_ttn_end_device_uplinks(lp_ttn_end_device_uplinks_id)
|
FOREIGN KEY (lp_ttn_end_device_uplinks_id) REFERENCES lp_ttn_end_device_uplinks(lp_ttn_end_device_uplinks_id)
|
||||||
|
@ -4,64 +4,47 @@ import { Counter, Gauge, collectDefaultMetrics, register } from "prom-client";
|
|||||||
import { LocationService } from "../services/locationService";
|
import { LocationService } from "../services/locationService";
|
||||||
import { WifiLocationService } from "../services/wifiLocationService";
|
import { WifiLocationService } from "../services/wifiLocationService";
|
||||||
import { TtnGatewayReceptionService } from "../services/ttnGatewayReceptionService";
|
import { TtnGatewayReceptionService } from "../services/ttnGatewayReceptionService";
|
||||||
|
import { MetricsService } from "../services/metricsService";
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const locationService = container.resolve(LocationService);
|
const locationService = container.resolve(LocationService);
|
||||||
const wifiLocationService = container.resolve(WifiLocationService);
|
const wifiLocationService = container.resolve(WifiLocationService);
|
||||||
const ttnGatewayReceptionService = container.resolve(TtnGatewayReceptionService);
|
const ttnGatewayReceptionService = container.resolve(TtnGatewayReceptionService);
|
||||||
|
const metricsService = container.resolve(MetricsService);
|
||||||
|
|
||||||
// Collect default system metrics (e.g., CPU, memory usage)
|
const requestCounter = metricsService.getRequestCounter();
|
||||||
const prefix = 'locationhub_';
|
const locationsTotal = metricsService.getLocationsTotal();
|
||||||
collectDefaultMetrics({ prefix });
|
const gnssLocationsTotal = metricsService.getGnssLocationsTotal()
|
||||||
|
const wifiLocationTotal = metricsService.getWifiLocationTotal();
|
||||||
// Define a custom Counter metric
|
const wifiLocationNotResolvable = metricsService.getWifiLocationResolvable();
|
||||||
const requestCounter = new Counter({
|
const wifiLocationRequestLimitExceeded = metricsService.getLWifiLocationRequestLimitExceeded();
|
||||||
name: `${prefix}http_requests_total`,
|
const ttnGatewayReceptions = metricsService.getTnnGatewayReceptions();
|
||||||
help: "Total number of HTTP requests",
|
|
||||||
labelNames: ["method", "route", "status"],
|
|
||||||
});
|
|
||||||
|
|
||||||
const locationsTotal = new Gauge({
|
|
||||||
name: `${prefix}locations_total`,
|
|
||||||
help: "Total number of location entries in database",
|
|
||||||
labelNames: ["database"],
|
|
||||||
});
|
|
||||||
|
|
||||||
const wifiLocationTotal = new Gauge({
|
|
||||||
name: `${prefix}wifi_locations_total`,
|
|
||||||
help: "Total number of wifi location entries in database",
|
|
||||||
labelNames: ["database"],
|
|
||||||
});
|
|
||||||
|
|
||||||
const wifiLocationNotResolvable = new Gauge({
|
|
||||||
name: `${prefix}wifi_locations_not_resolvable`,
|
|
||||||
help: "Unresolved number of wifi location entries in database",
|
|
||||||
labelNames: ["database"],
|
|
||||||
});
|
|
||||||
|
|
||||||
const wifiLocationRequestLimitExceeded = new Gauge({
|
|
||||||
name: `${prefix}wifi_locations_request_limit_exceeded`,
|
|
||||||
help: "Unresolved number of wifi location because request limit exceeded entries in database",
|
|
||||||
labelNames: ["database"],
|
|
||||||
});
|
|
||||||
|
|
||||||
const ttnGatewayReceptions = new Gauge({
|
|
||||||
name: `${prefix}ttn_gateway_receptions`,
|
|
||||||
help: "Total number of TTN Gateway receptions entries in database",
|
|
||||||
labelNames: ["database"],
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// Define the metrics endpoint
|
// Define the metrics endpoint
|
||||||
router.get("/", async (req: Request, res: Response) => {
|
router.get("/", async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
console.log("Metric Endpoint triggered");
|
const [
|
||||||
|
allLocations,
|
||||||
|
gnssLocations,
|
||||||
|
allWifiLocations,
|
||||||
|
wifiLocationsNotResolvable,
|
||||||
|
wifiLocationsRequestLimitExceeded,
|
||||||
|
allTtnGatewayReceptions
|
||||||
|
] = await Promise.all([
|
||||||
|
locationService.getAllLocations(),
|
||||||
|
locationService.getAllGnssLocations(),
|
||||||
|
wifiLocationService.getAllWifiLocations(),
|
||||||
|
wifiLocationService.getAllWifiLocationsByNotResolvable(),
|
||||||
|
wifiLocationService.getAllWifiLocationsByRequestLimitExceeded(),
|
||||||
|
ttnGatewayReceptionService.getAllGatewayReceptions()
|
||||||
|
]);
|
||||||
|
|
||||||
locationsTotal.set((await locationService.getAllLocations()).length);
|
locationsTotal.set(allLocations.length);
|
||||||
wifiLocationTotal.set((await wifiLocationService.getAllWifiLocations()).length);
|
gnssLocationsTotal.set(gnssLocations.length);
|
||||||
wifiLocationNotResolvable.set((await wifiLocationService.getAllWifiLocationsByNotResolvable()).length);
|
wifiLocationTotal.set(allWifiLocations.length);
|
||||||
wifiLocationRequestLimitExceeded.set((await wifiLocationService.getAllWifiLocationsByRequestLimitExceeded()).length);
|
wifiLocationNotResolvable.set(wifiLocationsNotResolvable.length);
|
||||||
ttnGatewayReceptions.set((await ttnGatewayReceptionService.getAllGatewayReceptions()).length);
|
wifiLocationRequestLimitExceeded.set(wifiLocationsRequestLimitExceeded.length);
|
||||||
|
ttnGatewayReceptions.set(allTtnGatewayReceptions.length);
|
||||||
|
|
||||||
// Increment the counter with labels
|
// Increment the counter with labels
|
||||||
requestCounter.inc({ method: req.method, route: req.route.path, status: 200 });
|
requestCounter.inc({ method: req.method, route: req.route.path, status: 200 });
|
||||||
@ -72,7 +55,6 @@ router.get("/", async (req: Request, res: Response) => {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Increment the counter for errors
|
// Increment the counter for errors
|
||||||
requestCounter.inc({ method: req.method, route: req.route.path, status: 500 });
|
requestCounter.inc({ method: req.method, route: req.route.path, status: 500 });
|
||||||
|
|
||||||
console.error("Error running metrics endpoint:", error);
|
console.error("Error running metrics endpoint:", error);
|
||||||
res.status(500).json({ error: "Error running metrics endpoint" });
|
res.status(500).json({ error: "Error running metrics endpoint" });
|
||||||
}
|
}
|
||||||
|
@ -42,22 +42,37 @@ router.post(
|
|||||||
)?.measurementValue,
|
)?.measurementValue,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const messageData = message.uplink_message.decoded_payload?.messages[0];
|
||||||
|
|
||||||
|
const latitudeData = messageData?.find((e) => e.type === "Latitude");
|
||||||
|
const longitudeData = messageData?.find((e) => e.type === "Longitude");
|
||||||
|
|
||||||
const gnnsLocation = {
|
const gnnsLocation = {
|
||||||
latitude: message.uplink_message.decoded_payload?.messages[0].find(
|
latitude: latitudeData?.measurementValue,
|
||||||
(e) => e.type === "Latitude"
|
longitude: longitudeData?.measurementValue,
|
||||||
)?.measurementValue,
|
|
||||||
longitude: message.uplink_message.decoded_payload?.messages[0].find(
|
|
||||||
(e) => e.type === "Longitude"
|
|
||||||
)?.measurementValue,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const gnssTimestamp = {
|
||||||
|
timestamp: latitudeData?.timestamp
|
||||||
|
? new Date(latitudeData.timestamp)
|
||||||
|
: longitudeData?.timestamp
|
||||||
|
? new Date(longitudeData.timestamp)
|
||||||
|
: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
const wifiMessage =
|
||||||
|
message.uplink_message.decoded_payload?.messages[0].find(
|
||||||
|
(e) => e.type === "Wi-Fi Scan"
|
||||||
|
);
|
||||||
|
|
||||||
const wifiScans =
|
const wifiScans =
|
||||||
message.uplink_message.decoded_payload?.messages[0]
|
wifiMessage?.measurementValue?.map((w) => ({
|
||||||
.find((e) => e.type === "Wi-Fi Scan")
|
|
||||||
?.measurementValue?.map((w) => ({
|
|
||||||
lp_ttn_end_device_uplinks_id,
|
lp_ttn_end_device_uplinks_id,
|
||||||
mac: w.mac,
|
mac: w.mac,
|
||||||
rssi: w.rssi,
|
rssi: w.rssi,
|
||||||
|
scanned_at_utc: wifiMessage?.timestamp
|
||||||
|
? new Date(wifiMessage.timestamp)
|
||||||
|
: undefined,
|
||||||
})) ?? [];
|
})) ?? [];
|
||||||
|
|
||||||
const ttnGatewayReceptions = message.uplink_message.rx_metadata.map(
|
const ttnGatewayReceptions = message.uplink_message.rx_metadata.map(
|
||||||
@ -98,6 +113,7 @@ router.post(
|
|||||||
longitude: gnnsLocation.longitude,
|
longitude: gnnsLocation.longitude,
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
|
gnss_timestamp: gnssTimestamp.timestamp,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
createDatabaseEntries().then();
|
createDatabaseEntries().then();
|
||||||
|
@ -10,6 +10,7 @@ export class Location extends Model {
|
|||||||
public gnss_longitude!: number;
|
public gnss_longitude!: number;
|
||||||
public ttn_gw_latitude!: number;
|
public ttn_gw_latitude!: number;
|
||||||
public ttn_gw_longitude!: number;
|
public ttn_gw_longitude!: number;
|
||||||
|
public gnss_location_at_utc!: Date;
|
||||||
public created_at_utc!: Date;
|
public created_at_utc!: Date;
|
||||||
public updated_at_utc!: Date;
|
public updated_at_utc!: Date;
|
||||||
}
|
}
|
||||||
@ -42,6 +43,9 @@ Location.init(
|
|||||||
ttn_gw_longitude: {
|
ttn_gw_longitude: {
|
||||||
type: DataTypes.NUMBER,
|
type: DataTypes.NUMBER,
|
||||||
},
|
},
|
||||||
|
gnss_location_at_utc: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
},
|
||||||
created_at_utc: {
|
created_at_utc: {
|
||||||
type: DataTypes.DATE,
|
type: DataTypes.DATE,
|
||||||
defaultValue: DataTypes.NOW,
|
defaultValue: DataTypes.NOW,
|
||||||
|
@ -6,6 +6,7 @@ export class WifiScan extends Model {
|
|||||||
public wifi_scan_id!: string;
|
public wifi_scan_id!: string;
|
||||||
public mac!: string;
|
public mac!: string;
|
||||||
public rssi!: number;
|
public rssi!: number;
|
||||||
|
public scanned_at_utc!: Date;
|
||||||
public created_at_utc!: Date;
|
public created_at_utc!: Date;
|
||||||
public updated_at_utc!: Date;
|
public updated_at_utc!: Date;
|
||||||
}
|
}
|
||||||
@ -30,6 +31,11 @@ WifiScan.init(
|
|||||||
type: DataTypes.NUMBER,
|
type: DataTypes.NUMBER,
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
},
|
},
|
||||||
|
scanned_at_utc: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
defaultValue: DataTypes.NOW,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
created_at_utc: {
|
created_at_utc: {
|
||||||
type: DataTypes.DATE,
|
type: DataTypes.DATE,
|
||||||
defaultValue: DataTypes.NOW,
|
defaultValue: DataTypes.NOW,
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
|
import { Attributes, FindOptions } from "sequelize";
|
||||||
import { injectable } from "tsyringe";
|
import { injectable } from "tsyringe";
|
||||||
import { Location } from "../models/location";
|
import { Location } from "../models/location";
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class LocationRepository {
|
export class LocationRepository {
|
||||||
public async findAll() {
|
public async findAll(options?: FindOptions<Attributes<Location>>) {
|
||||||
return await Location.findAll();
|
return await Location.findAll(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async findById(id: string) {
|
public async findById(id: string) {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { inject, injectable } from "tsyringe";
|
import { inject, injectable } from "tsyringe";
|
||||||
|
import { Op } from 'sequelize';
|
||||||
import { Location } from "../models/location";
|
import { Location } from "../models/location";
|
||||||
import { LocationRepository } from "../repositories/locationRepository";
|
import { LocationRepository } from "../repositories/locationRepository";
|
||||||
import { WifiLocationService } from "./wifiLocationService";
|
import { WifiLocationService } from "./wifiLocationService";
|
||||||
@ -8,6 +9,7 @@ interface CreateLocationParams {
|
|||||||
wifi?: Coordinates;
|
wifi?: Coordinates;
|
||||||
gnss?: Coordinates;
|
gnss?: Coordinates;
|
||||||
ttn_gw?: Coordinates;
|
ttn_gw?: Coordinates;
|
||||||
|
gnss_timestamp?: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CreateLocationTriangulationParams {
|
interface CreateLocationTriangulationParams {
|
||||||
@ -18,6 +20,7 @@ interface CreateLocationTriangulationParams {
|
|||||||
}[];
|
}[];
|
||||||
ttn_gw: LocationSignal[];
|
ttn_gw: LocationSignal[];
|
||||||
gnss?: Coordinates;
|
gnss?: Coordinates;
|
||||||
|
gnss_timestamp?: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LocationSignal extends Coordinates {
|
interface LocationSignal extends Coordinates {
|
||||||
@ -52,6 +55,15 @@ export class LocationService {
|
|||||||
return this.repository.findAll();
|
return this.repository.findAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getAllGnssLocations() {
|
||||||
|
return this.repository.findAll({
|
||||||
|
where: {
|
||||||
|
gnss_latitude: { [Op.ne]: null },
|
||||||
|
gnss_longitude: { [Op.ne]: null }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public async getLocationById(id: string) {
|
public async getLocationById(id: string) {
|
||||||
return this.repository.findById(id);
|
return this.repository.findById(id);
|
||||||
}
|
}
|
||||||
@ -65,6 +77,7 @@ export class LocationService {
|
|||||||
ttn_gw_longitude: data.ttn_gw?.longitude,
|
ttn_gw_longitude: data.ttn_gw?.longitude,
|
||||||
gnss_latitude: data.gnss?.latitude,
|
gnss_latitude: data.gnss?.latitude,
|
||||||
gnss_longitude: data.gnss?.longitude,
|
gnss_longitude: data.gnss?.longitude,
|
||||||
|
gnss_location_at_utc: data.gnss_timestamp,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,6 +94,7 @@ export class LocationService {
|
|||||||
wifi: wifi_location,
|
wifi: wifi_location,
|
||||||
ttn_gw: gateway_location,
|
ttn_gw: gateway_location,
|
||||||
gnss: data.gnss,
|
gnss: data.gnss,
|
||||||
|
gnss_timestamp: data.gnss_timestamp,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
70
server/src/services/metricsService.ts
Normal file
70
server/src/services/metricsService.ts
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import {injectable } from "tsyringe";
|
||||||
|
import { Counter, Gauge, collectDefaultMetrics} from "prom-client";
|
||||||
|
|
||||||
|
// Collect default system metrics (e.g., CPU, memory usage)
|
||||||
|
const prefix = 'locationhub_';
|
||||||
|
collectDefaultMetrics({ prefix });
|
||||||
|
|
||||||
|
@injectable()
|
||||||
|
export class MetricsService {
|
||||||
|
constructor(
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public getRequestCounter() {
|
||||||
|
const requestCounter = new Counter({
|
||||||
|
name: `${prefix}http_requests_total`,
|
||||||
|
help: "Total number of HTTP requests",
|
||||||
|
labelNames: ["method", "route", "status"],
|
||||||
|
});
|
||||||
|
return requestCounter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getLocationsTotal() {
|
||||||
|
const locationsTotal = new Gauge({
|
||||||
|
name: `${prefix}locations_total`,
|
||||||
|
help: "Total number of location entries in database",
|
||||||
|
labelNames: ["database"],
|
||||||
|
});
|
||||||
|
return locationsTotal;
|
||||||
|
}
|
||||||
|
public getGnssLocationsTotal() {
|
||||||
|
const gnssLocationsTotal = new Gauge({
|
||||||
|
name: `${prefix}gnss_locations_total`,
|
||||||
|
help: "Total number of location entries with GNSS in database",
|
||||||
|
labelNames: ["database"],
|
||||||
|
});
|
||||||
|
return gnssLocationsTotal;
|
||||||
|
}
|
||||||
|
public getWifiLocationTotal() {
|
||||||
|
const wifiLocationTotal = new Gauge({
|
||||||
|
name: `${prefix}wifi_locations_total`,
|
||||||
|
help: "Total number of wifi location entries in database",
|
||||||
|
labelNames: ["database"],
|
||||||
|
});
|
||||||
|
return wifiLocationTotal;
|
||||||
|
}
|
||||||
|
public getWifiLocationResolvable() {
|
||||||
|
const wifiLocationNotResolvable = new Gauge({
|
||||||
|
name: `${prefix}wifi_locations_not_resolvable`,
|
||||||
|
help: "Unresolved number of wifi location entries in database",
|
||||||
|
labelNames: ["database"],
|
||||||
|
});
|
||||||
|
return wifiLocationNotResolvable;
|
||||||
|
}
|
||||||
|
public getLWifiLocationRequestLimitExceeded() {
|
||||||
|
const wifiLocationRequestLimitExceeded = new Gauge({
|
||||||
|
name: `${prefix}wifi_locations_request_limit_exceeded`,
|
||||||
|
help: "Unresolved number of wifi location because request limit exceeded entries in database",
|
||||||
|
labelNames: ["database"],
|
||||||
|
});
|
||||||
|
return wifiLocationRequestLimitExceeded;
|
||||||
|
}
|
||||||
|
public getTnnGatewayReceptions() {
|
||||||
|
const ttnGatewayReceptions = new Gauge({
|
||||||
|
name: `${prefix}ttn_gateway_receptions`,
|
||||||
|
help: "Total number of TTN Gateway receptions entries in database",
|
||||||
|
labelNames: ["database"],
|
||||||
|
});
|
||||||
|
return ttnGatewayReceptions;
|
||||||
|
}
|
||||||
|
}
|
@ -5,12 +5,14 @@ interface CreateWifiScanParams {
|
|||||||
lp_ttn_end_device_uplinks_id: string;
|
lp_ttn_end_device_uplinks_id: string;
|
||||||
mac: string;
|
mac: string;
|
||||||
rssi: number;
|
rssi: number;
|
||||||
|
scanned_at_utc?: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UpdateWifiScanParams {
|
interface UpdateWifiScanParams {
|
||||||
wifi_scan_id: string;
|
wifi_scan_id: string;
|
||||||
mac?: string;
|
mac?: string;
|
||||||
rssi?: number;
|
rssi?: number;
|
||||||
|
scanned_at_utc?: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
|
Reference in New Issue
Block a user