621 lines
20 KiB
PHP
621 lines
20 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Models\TimeseriesKeyMaster;
|
|
use Illuminate\Support\Facades\Http;
|
|
use Illuminate\Support\Facades\Log;
|
|
use App\Services\AdminService;
|
|
use Exception;
|
|
use Illuminate\Support\Facades\Request;
|
|
use Illuminate\Support\Facades\Cache;
|
|
|
|
|
|
class CustomerInfoService
|
|
{
|
|
protected $adminService;
|
|
|
|
public function __construct(AdminService $adminService)
|
|
{
|
|
$this->adminService = $adminService;
|
|
}
|
|
|
|
// public function getThingsBoardDevices($customerId)
|
|
// {
|
|
// try {
|
|
// $token = $this->adminService->getToken();
|
|
|
|
// if (!$token) {
|
|
// Log::error("Failed to authenticate with ThingsBoard.");
|
|
// return response()->json([
|
|
// 'error' => 'thingsboard_auth_failed',
|
|
// 'message' => 'Failed to authenticate with ThingsBoard'
|
|
// ], 401);
|
|
// }
|
|
|
|
// $response = Http::withHeaders([
|
|
// 'Authorization' => "Bearer $token",
|
|
// 'Accept' => 'application/json',
|
|
// ])->get("http://65.0.131.117:8080/api/customer/{$customerId}/deviceInfos?pageSize=100&page=0");
|
|
|
|
// if (!$response->successful()) {
|
|
// Log::error("Failed to fetch ThingsBoard devices: " . $response->body());
|
|
// return response()->json([
|
|
// 'error' => 'thingsboard_fetch_failed',
|
|
// 'message' => 'Failed to fetch devices from ThingsBoard',
|
|
// 'details' => $response->body()
|
|
// ], $response->status());
|
|
// }
|
|
|
|
// $data = $response->json();
|
|
// return $data['data'] ?? [];
|
|
|
|
// } catch (\Exception $e) {
|
|
// Log::error("Error fetching ThingsBoard devices: " . $e->getMessage());
|
|
// return response()->json([
|
|
// 'error' => 'thingsboard_error',
|
|
// 'message' => 'Error fetching devices from ThingsBoard',
|
|
// 'details' => $e->getMessage()
|
|
// ], 500);
|
|
// }
|
|
// }
|
|
|
|
|
|
|
|
public function getTelemetryData($device, $keyNames, $startTs, $endTs)
|
|
{
|
|
$token = $this->adminService->getToken();
|
|
|
|
if (!$token) {
|
|
return ['error' => 'Failed to fetch ThingsBoard token'];
|
|
}
|
|
|
|
$baseUrl = env('THINGSBOARD_URL');
|
|
$deviceId = $device->id;
|
|
|
|
$keys = implode(',', $keyNames);
|
|
|
|
// Build query parameters dynamically
|
|
$queryParams = [
|
|
'keys' => $keys,
|
|
];
|
|
|
|
if ($startTs) {
|
|
$queryParams['startTs'] = $startTs;
|
|
}
|
|
|
|
if ($endTs) {
|
|
$queryParams['interval'] = $endTs;
|
|
}
|
|
|
|
// Make the HTTP request
|
|
$response = Http::withHeaders([
|
|
'Authorization' => "Bearer $token",
|
|
'Accept' => 'application/json',
|
|
])->get("{$baseUrl}api/plugins/telemetry/DEVICE/{$deviceId}/values/timeseries", $queryParams);
|
|
|
|
// Check if the response was successful
|
|
if (!$response->successful()) {
|
|
Log::error("Failed to fetch telemetry for device: {$device->name} (ID: {$deviceId})", [
|
|
'http_code' => $response->status(),
|
|
'url' => $response->effectiveUri(),
|
|
'response' => $response->body(),
|
|
]);
|
|
|
|
return ['error' => "Failed to fetch telemetry. HTTP Code: " . $response->status()];
|
|
}
|
|
|
|
// Decode the telemetry response
|
|
$telemetry = $response->json();
|
|
Log::info("Telemetry Data", $telemetry);
|
|
|
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
|
Log::error("Failed to decode telemetry response for device: {$device->name}", [
|
|
'response' => $response->body(),
|
|
]);
|
|
|
|
return ['error' => 'Invalid telemetry response format'];
|
|
}
|
|
|
|
return $telemetry;
|
|
}
|
|
|
|
|
|
|
|
public function getTelemetryDataDevice($device, $keyNames, $startTs, $endTs)
|
|
{
|
|
|
|
$token = $this->adminService->getToken();
|
|
|
|
if (!$token) {
|
|
Log::error('Failed to fetch ThingsBoard token');
|
|
return ['error' => 'Failed to fetch ThingsBoard token'];
|
|
}
|
|
|
|
$baseUrl = env('THINGSBOARD_URL');
|
|
$deviceId = $device->id;
|
|
|
|
$keys = implode(',', $keyNames);
|
|
|
|
|
|
$queryParams = [
|
|
'keys' => $keys,
|
|
];
|
|
|
|
if ($startTs) {
|
|
$queryParams['startTs'] = $startTs;
|
|
}
|
|
|
|
if ($endTs) {
|
|
$queryParams['endTs'] = $endTs;
|
|
}
|
|
|
|
$response = Http::withHeaders([
|
|
'Authorization' => "Bearer $token",
|
|
'Accept' => 'application/json',
|
|
])->get("{$baseUrl}api/plugins/telemetry/DEVICE/{$deviceId}/values/timeseries", $queryParams);
|
|
|
|
|
|
|
|
|
|
// Check if the response was successful
|
|
if (!$response->successful()) {
|
|
Log::error("Failed to fetch telemetry for device: {$device->name} (ID: {$deviceId})", [
|
|
'http_code' => $response->status(),
|
|
'url' => $response->effectiveUri(),
|
|
'response' => $response->body()
|
|
]);
|
|
|
|
return ['error' => "Failed to fetch telemetry. HTTP Code: " . $response->status()];
|
|
}
|
|
|
|
// Decode the telemetry response
|
|
$telemetry = $response->json();
|
|
|
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
|
Log::error("Failed to decode telemetry response for device: {$device->name}", [
|
|
'response' => $response->body()
|
|
]);
|
|
|
|
return ['error' => 'Invalid telemetry response format'];
|
|
}
|
|
|
|
return $telemetry;
|
|
}
|
|
|
|
|
|
public function getTelemetryDataDeviceDiagonostic($device, $keyNames, $startTs, $endTs)
|
|
{
|
|
|
|
$token = $this->adminService->getToken();
|
|
|
|
if (!$token) {
|
|
Log::error('Failed to fetch ThingsBoard token');
|
|
return ['error' => 'Failed to fetch ThingsBoard token'];
|
|
}
|
|
|
|
$baseUrl = env('THINGSBOARD_URL');
|
|
$deviceId = $device->id;
|
|
|
|
$keys = implode(',', $keyNames);
|
|
|
|
|
|
|
|
$response = Http::withHeaders([
|
|
'Authorization' => "Bearer $token",
|
|
'Accept' => 'application/json'
|
|
// ])->get("$baseUrl/api/plugins/telemetry/DEVICE/58bf81a0-0619-11f0-a9dc-45dd276e4cd5/values/timeseries", [
|
|
])->get("$baseUrl/api/plugins/telemetry/DEVICE/{$deviceId}/values/timeseries", [
|
|
|
|
'keys' => $keys,
|
|
// 'keys' => 'MechanicalHealth_valueInPercent',
|
|
'startTs' => $startTs,
|
|
'interval' => $endTs,
|
|
'limit' => 100,
|
|
'useStrictDataTypes' => false
|
|
]);
|
|
|
|
|
|
|
|
// Check if the response was successful
|
|
if (!$response->successful()) {
|
|
Log::error("Failed to fetch telemetry for device: {$device->name} (ID: {$deviceId})", [
|
|
'http_code' => $response->status(),
|
|
'url' => $response->effectiveUri(),
|
|
'response' => $response->body()
|
|
]);
|
|
|
|
return ['error' => "Failed to fetch telemetry. HTTP Code: " . $response->status()];
|
|
}
|
|
|
|
// Decode the telemetry response
|
|
$telemetry = $response->json();
|
|
|
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
|
Log::error("Failed to decode telemetry response for device: {$device->name}", [
|
|
'response' => $response->body()
|
|
]);
|
|
|
|
return ['error' => 'Invalid telemetry response format'];
|
|
}
|
|
|
|
return $telemetry;
|
|
}
|
|
|
|
|
|
|
|
public function getCustomerDevicesAndAlarms($customerId)
|
|
{
|
|
$result = [
|
|
'devices' => [],
|
|
'alarms' => [],
|
|
'errors' => []
|
|
];
|
|
|
|
try {
|
|
// Step 1: Get devices
|
|
$devicesResponse = $this->getThingsBoardDevices($customerId);
|
|
|
|
if (isset($devicesResponse['error'])) {
|
|
$result['errors']['devices'] = $devicesResponse;
|
|
} else {
|
|
$result['devices'] = $devicesResponse;
|
|
|
|
// Step 2: Get alarms if devices were fetched successfully
|
|
$deviceIds = array_column($devicesResponse, 'id.id');
|
|
if (!empty($deviceIds)) {
|
|
$alarmsResponse = $this->getDeviceAlarms($deviceIds);
|
|
|
|
if (isset($alarmsResponse['error'])) {
|
|
$result['errors']['alarms'] = $alarmsResponse;
|
|
} else {
|
|
$result['alarms'] = $alarmsResponse['data'] ?? [];
|
|
}
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
} catch (\Exception $e) {
|
|
Log::error("ThingsBoard service error: " . $e->getMessage());
|
|
return [
|
|
'error' => 'service_error',
|
|
'message' => 'Failed to complete ThingsBoard operation',
|
|
'details' => $e->getMessage()
|
|
];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get devices for a customer
|
|
*/
|
|
private function getThingsBoardDevices($customerId)
|
|
{
|
|
try {
|
|
$token = $this->adminService->getToken();
|
|
if (!$token) {
|
|
throw new \Exception("Failed to authenticate with ThingsBoard");
|
|
}
|
|
|
|
$response = Http::withHeaders([
|
|
'Authorization' => "Bearer $token",
|
|
'Accept' => 'application/json',
|
|
])->get("http://65.0.131.117:8080/api/customer/{$customerId}/deviceInfos?pageSize=100&page=0");
|
|
|
|
if (!$response->successful()) {
|
|
throw new \Exception("Failed to fetch devices: " . $response->body());
|
|
}
|
|
|
|
$data = $response->json();
|
|
return $data['data'] ?? [];
|
|
} catch (\Exception $e) {
|
|
Log::error("Device fetch error: " . $e->getMessage());
|
|
return [
|
|
'error' => 'device_fetch_failed',
|
|
'message' => 'Failed to fetch devices',
|
|
'details' => $e->getMessage()
|
|
];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get alarms for specific devices (last 24 hours)
|
|
*/
|
|
private function getDeviceAlarms(array $deviceIds)
|
|
{
|
|
try {
|
|
$token = $this->adminService->getToken();
|
|
if (!$token) {
|
|
throw new \Exception("Failed to authenticate with ThingsBoard");
|
|
}
|
|
|
|
$startTime = (time() - 86400) * 1000; // 24 hours ago in milliseconds
|
|
$deviceIdsParam = implode(',', $deviceIds);
|
|
|
|
$response = Http::withHeaders([
|
|
'Authorization' => "Bearer $token",
|
|
'Accept' => 'application/json',
|
|
])->get("http://65.0.131.117:8080/api/v2/alarms", [
|
|
'pageSize' => 100,
|
|
'page' => 0,
|
|
'startTime' => $startTime,
|
|
'deviceIds' => $deviceIdsParam
|
|
]);
|
|
|
|
if (!$response->successful()) {
|
|
throw new \Exception("Failed to fetch alarms: " . $response->body());
|
|
}
|
|
|
|
return $response->json();
|
|
} catch (\Exception $e) {
|
|
Log::error("Alarm fetch error: " . $e->getMessage());
|
|
return [
|
|
'error' => 'alarm_fetch_failed',
|
|
'message' => 'Failed to fetch alarms',
|
|
'details' => $e->getMessage()
|
|
];
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public function getToken()
|
|
{
|
|
$baseUrl = env('THINGSBOARD_URL', 'http://65.0.131.117:8080');
|
|
$username = env('THINGSBOARD_USERNAME', 'tenant1@thingsboard.org');
|
|
$password = env('THINGSBOARD_PASSWORD', 'tenant1');
|
|
|
|
if (Cache::has('thingsboard_token')) {
|
|
return Cache::get('thingsboard_token');
|
|
}
|
|
|
|
$response = Http::withHeaders([
|
|
// 'accept' => 'application/json',
|
|
'Content-Type' => 'application/json',
|
|
])
|
|
->post("{$baseUrl}api/auth/login", [
|
|
'username' => $username,
|
|
'password' => $password,
|
|
]);
|
|
|
|
|
|
if ($response->successful()) {
|
|
$token = $response->json('token');
|
|
Cache::put('thingsboard_token', $token, now()->addMinutes(1440));
|
|
return $token;
|
|
} else {
|
|
Log::error("ThingsBoard Authentication Failed: " . $response->body());
|
|
throw new Exception('Unable to authenticate with ThingsBoard: ' . $response->body());
|
|
}
|
|
}
|
|
|
|
public function fetchEntityQuery($deviceIds, $timeseriesKeys){
|
|
try{
|
|
$token = $this->adminService->getToken();
|
|
if (!$token) {
|
|
throw new \Exception("Failed to authenticate with ThingsBoard");
|
|
}
|
|
|
|
$url = env('THINGSBOARD_URL').'api/entitiesQuery/find';
|
|
|
|
$timeSeries = array_map(function($key) {
|
|
return [
|
|
'type' => 'TIME_SERIES',
|
|
'key' => $key
|
|
];
|
|
}, $timeseriesKeys);
|
|
|
|
$payload = [
|
|
"entityFilter" => [
|
|
"type" => "entityList",
|
|
"entityType" => "DEVICE",
|
|
"entityList" => $deviceIds
|
|
],
|
|
"pageLink" => [
|
|
"pageSize" => 100,
|
|
"page" => 0,
|
|
"textSearch" => "",
|
|
"dynamic" => true
|
|
],
|
|
"entityFields" => [
|
|
["type" => "ENTITY_FIELD", "key" => "name"],
|
|
["type" => "ENTITY_FIELD", "key" => "type"],
|
|
],
|
|
"latestValues" => $timeSeries
|
|
];
|
|
|
|
$response = Http::withHeaders([
|
|
'accept' => 'application/json',
|
|
'Content-Type' => 'application/json',
|
|
'X-Authorization' => 'Bearer ' . $token,
|
|
])->post($url, $payload);
|
|
|
|
// Return JSON response
|
|
return response()->json($response->json(), $response->status());
|
|
|
|
} catch(Exception $e){
|
|
return response()->json(['success' => false, 'message' => $e->getMessage()], 500);
|
|
}
|
|
}
|
|
|
|
public function fetchTelemetryData($deviceId,$keys,$ts=[])
|
|
{
|
|
$url = env('THINGSBOARD_URL') ."api/plugins/telemetry/DEVICE/{$deviceId}/values/timeseries?useStrictDataTypes=false";
|
|
|
|
$token = $this->adminService->getToken();
|
|
if (!$token) {
|
|
throw new \Exception("Failed to authenticate with ThingsBoard");
|
|
}
|
|
|
|
try {
|
|
$response = Http::withToken($token)->asForm()->get($url, [
|
|
'keys' => $keys,
|
|
'startTs' => $ts['startTs'] ?? null,
|
|
'endTs' => $ts['endTs'] ?? null,
|
|
'limit' => $ts ? 25000 : null
|
|
]);
|
|
|
|
if ($response->successful()) {
|
|
return $response->json();
|
|
} else {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Failed to fetch data',
|
|
'status' => $response->status(),
|
|
], $response->status());
|
|
}
|
|
} catch (\Exception $e) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'An error occurred: ' . $e->getMessage(),
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
public function getDevicesCount($deviceIds){
|
|
try {
|
|
$token = $this->adminService->getToken();
|
|
if (!$token) {
|
|
throw new \Exception("Failed to authenticate with ThingsBoard");
|
|
}
|
|
|
|
$url = env('THINGSBOARD_URL') . 'api/entitiesQuery/count';
|
|
|
|
// ---------- 1. Total count ----------
|
|
$totalPayload = [
|
|
"entityFilter" => [
|
|
"type" => "entityList",
|
|
"entityType" => "DEVICE",
|
|
"entityList" => $deviceIds
|
|
]
|
|
];
|
|
|
|
$totalResponse = Http::withHeaders([
|
|
'accept' => 'application/json',
|
|
'Content-Type' => 'application/json',
|
|
'X-Authorization' => 'Bearer ' . $token,
|
|
])->post($url, $totalPayload);
|
|
|
|
$totalCount = $totalResponse->json() ?? 0;
|
|
|
|
// ---------- 2. active count (active = false) ----------
|
|
$activePayload = [
|
|
"entityFilter" => [
|
|
"type" => "entityList",
|
|
"entityType" => "DEVICE",
|
|
"entityList" => $deviceIds
|
|
],
|
|
"keyFilters" => [
|
|
[
|
|
"key" => [
|
|
"type" => "ATTRIBUTE",
|
|
"key" => "active"
|
|
],
|
|
"valueType" => "BOOLEAN",
|
|
"predicate" => [
|
|
"operation" => "EQUAL",
|
|
"value" => [
|
|
"defaultValue" => true,
|
|
"dynamicValue" => null
|
|
],
|
|
"type" => "BOOLEAN"
|
|
]
|
|
]
|
|
]
|
|
];
|
|
|
|
$activeResponse = Http::withHeaders([
|
|
'accept' => 'application/json',
|
|
'Content-Type' => 'application/json',
|
|
'X-Authorization' => 'Bearer ' . $token,
|
|
])->post($url, $activePayload);
|
|
|
|
$activeCount = $activeResponse->json() ?? 0;
|
|
|
|
// ---------- Return both counts ----------
|
|
return [
|
|
'totalDevices' => $totalCount,
|
|
'activeDevices' => $activeCount
|
|
];
|
|
|
|
|
|
} catch(Exception $e){
|
|
return response()->json(['success' => false, 'message' => $e->getMessage()], 500);
|
|
}
|
|
}
|
|
|
|
public function fetchDeviceAlarms(array $deviceIds, $data=[])
|
|
{
|
|
try {
|
|
$token = $this->adminService->getToken();
|
|
if (!$token) {
|
|
throw new \Exception("Failed to authenticate with ThingsBoard");
|
|
}
|
|
|
|
$url = env('THINGSBOARD_URL') . 'api/alarmsQuery/find';
|
|
if (isset($data['startTs']) && isset($data['endTs'])) {
|
|
$startDate = \DateTime::createFromFormat('d/m/Y', $data['startTs']);
|
|
$endDate = \DateTime::createFromFormat('d/m/Y', $data['endTs']);
|
|
|
|
if ($startDate && $endDate) {
|
|
$startTimestamp = $startDate->getTimestamp() * 1000;
|
|
$endTimestamp = $endDate->getTimestamp() * 1000;
|
|
}
|
|
}
|
|
// Timestamp range (modify as needed)
|
|
$startTs = $startTimestamp ?? now()->subHours(24)->timestamp * 1000;
|
|
$endTs = $endTimestamp ?? now()->timestamp * 1000;
|
|
$entityList = $data['deviceIds'] ?? $deviceIds;
|
|
|
|
$payload = [
|
|
"entityFilter" => [
|
|
"type" => "entityList",
|
|
"entityType" => "DEVICE",
|
|
"entityList" => $entityList
|
|
],
|
|
"pageLink" => [
|
|
"startTs" => $startTs,
|
|
"endTs" => $endTs,
|
|
"pageSize" => 100,
|
|
"page" => 0,
|
|
"textSearch" => "",
|
|
"statusList" => $data['statusList'] ?? [],
|
|
"severityList" => $data['severityList'] ?? [],
|
|
"sortOrder" => [
|
|
"key" => [
|
|
"type" => "ALARM_FIELD",
|
|
"key" => "createdTime"
|
|
],
|
|
"direction" => "DESC"
|
|
]
|
|
],
|
|
"alarmFields" => []
|
|
];
|
|
|
|
$response = Http::withHeaders([
|
|
'accept' => 'application/json',
|
|
'Content-Type' => 'application/json',
|
|
'X-Authorization' => 'Bearer ' . $token,
|
|
])->post($url, $payload);
|
|
|
|
if (!$response->successful()) {
|
|
throw new \Exception("Failed to fetch alarms: " . $response->body());
|
|
}
|
|
|
|
$alarmData = $response->json();
|
|
|
|
return [
|
|
'data' => $alarmData['data'] ?? [],
|
|
'count' => $alarmData['totalElements'] ?? 0
|
|
];
|
|
|
|
} catch (\Exception $e) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Error: ' . $e->getMessage()
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
|
|
}
|