diff --git a/app/Http/Controllers/APIS/CustomerApi/CustomerDeviceInfoController.php b/app/Http/Controllers/APIS/CustomerApi/CustomerDeviceInfoController.php index 63074dd..174616d 100644 --- a/app/Http/Controllers/APIS/CustomerApi/CustomerDeviceInfoController.php +++ b/app/Http/Controllers/APIS/CustomerApi/CustomerDeviceInfoController.php @@ -7,6 +7,8 @@ use App\Models\Customer; use App\Models\User; use Illuminate\Http\Request; use App\Services\CustomerInfoService; +use Exception; +use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Log; use Tymon\JWTAuth\Facades\JWTAuth; @@ -21,6 +23,8 @@ class CustomerDeviceInfoController extends Controller $this->customerInfoService = $customerInfoService; } + + diff --git a/app/Http/Controllers/APIS/CustomerApi/TelemetryController.php b/app/Http/Controllers/APIS/CustomerApi/TelemetryController.php index d2ab681..6ebee49 100644 --- a/app/Http/Controllers/APIS/CustomerApi/TelemetryController.php +++ b/app/Http/Controllers/APIS/CustomerApi/TelemetryController.php @@ -873,4 +873,4 @@ class TelemetryController extends Controller // return response()->json(['telemetry' => $response]); // } -} +} \ No newline at end of file diff --git a/app/Http/Controllers/APIS/CustomerApi/TimeseriesAlertMessageController.php b/app/Http/Controllers/APIS/CustomerApi/TimeseriesAlertMessageController.php new file mode 100644 index 0000000..f528ee2 --- /dev/null +++ b/app/Http/Controllers/APIS/CustomerApi/TimeseriesAlertMessageController.php @@ -0,0 +1,333 @@ +customerInfoService = $customerInfoService; + } + // public function alertMessage(Request $request){ + // $token = readHeaderToken(); + // if (!$token) { + // return response()->json([ + // 'success' => false, + // 'error' => 'Authorization token required' + // ], 401); + // } + + // $validator = Validator::make($request->all(), [ + // 'device_id' => 'required|string', + // 'startTs' => 'nullable|string', + // 'endTs' => 'nullable|string', + // ]); + + // if ($validator->fails()) { + // return response()->json([ + // 'success' => false, + // 'error' => $validator->errors()->first() + // ], 400); + // } + + + // $deviceId = $request->input('device_id'); + + // // $deviceWithTelemetry = Device::with([ + // // 'deviceProfile', + // // 'timeseriesKeys' => function ($query) { + // // $query->where(function ($q) { + // // $q->where('display_on_dashboard', true) + // // ->orWhere('display_on_popup', true); + // // }) + // // ->select('id','device_profile_xid', 'display_name') + // // ->with(['timeseriesAlert' => function($query) { // Use singular name to match model + // // $query->select('id', 'timeseries_key_master_xid', 'min_value', 'max_value', 'color_code', 'alert_msg'); + // // }]); + // // } + // // ]) + // // ->where('id', $deviceId) + // // ->firstOrFail(); + + + // } + + // public function alertMessage(Request $request) + // { + // try { + // // Authentication + // $token = readHeaderToken(); + // if (!$token) { + // return response()->json([ + // 'success' => false, + // 'message' => 'Authorization token required' + // ], 401); + // } + + // // Validation + // $validator = Validator::make($request->all(), [ + // 'device_id' => 'required|string|exists:devices,id', + // 'startTs' => 'nullable|numeric', + // 'endTs' => 'nullable|numeric|gte:startTs', + // ]); + + // if ($validator->fails()) { + // return response()->json([ + // 'success' => false, + // 'message' => $validator->errors()->first() + // ], 400); + // } + + // // Fetch device with relationships + // $device = Device::with([ + // 'deviceProfile', + // 'timeseriesKeys' => function ($query) { + // $query->where(function ($q) { + // $q->where('display_on_dashboard', true) + // ->orWhere('display_on_popup', true); + // }) + // ->select('id', 'device_profile_xid', 'key_name', 'display_name') + // ->with(['timeseriesAlert' => function ($query) { + // $query->select('id', 'timeseries_key_master_xid', 'min_value', 'max_value', 'color_code', 'alert_msg'); + // }]); + // } + // ])->findOrFail($request->device_id); + + // // Get telemetry data + // $telemetryData = $this->customerInfoService->getTelemetryDataDevice( + // $device, + // $device->timeseriesKeys->pluck('key_name')->toArray(), + // $request->startTs, + // $request->endTs + // ); + + // if (isset($telemetryData['error'])) { + // throw new \Exception($telemetryData['error']); + // } + + // // Process data + // $result = [ + // 'alerts' => [], + // 'normal_telemetry' => [] + // ]; + + // foreach ($device->timeseriesKeys as $key) { + // if (!isset($telemetryData[$key->key_name])) continue; + + // $dataPoint = $telemetryData[$key->key_name][0] ?? null; + // if (!$dataPoint || !isset($dataPoint['value'])) continue; + + // $item = [ + // 'key' => $key->key_name, + // 'display_name' => $key->display_name, + // 'value' => $dataPoint['value'], + // 'timestamp' => $dataPoint['ts'] ?? null + // ]; + + // // Check alerts + // // Inside your alertMessage controller method, modify the alert check: + // foreach ($key->timeseriesAlert as $alert) { + // $isBelowMin = $alert->min_value !== null && $dataPoint['value'] < $alert->min_value; + // $isAboveMax = $alert->max_value !== null && $dataPoint['value'] > $alert->max_value; + + // if ($isBelowMin || $isAboveMax) { + // Log::warning("Alert triggered", [ + // 'device' => $device->id, + // 'key' => $key->key_name, + // 'value' => $dataPoint['value'], + // 'threshold_min' => $alert->min_value, + // 'threshold_max' => $alert->max_value, + // 'condition' => $isBelowMin ? 'below_min' : 'above_max' + // ]); + + // $item['alert'] = [ + // 'message' => $alert->alert_msg, + // 'color_code' => $alert->color_code, + // 'thresholds' => [ + // 'min' => $alert->min_value, + // 'max' => $alert->max_value + // ] + // ]; + // $result['alerts'][] = $item; + // continue 2; + // } + // } + + // $result['normal_telemetry'][] = $item; + // } + + // return response()->json([ + // 'success' => true, + // 'device' => [ + // 'id' => $device->id, + // 'name' => $device->name, + // 'profile' => $device->deviceProfile + // ], + // 'data' => $result + // ]); + // } catch (\Exception $e) { + // Log::error("Alert processing failed: {$e->getMessage()}", [ + // 'device_id' => $request->device_id ?? null, + // 'trace' => $e->getTraceAsString() + // ]); + + // return response()->json([ + // 'success' => false, + // 'message' => 'Failed to process alerts: ' . $e->getMessage() + // ], 500); + // } + // } + + public function alertMessage(Request $request) + { + try { + // Authentication + $token = readHeaderToken(); + if (!$token) { + return response()->json([ + 'success' => false, + 'message' => 'Authorization token required' + ], 401); + } + + // Validation + $validator = Validator::make($request->all(), [ + 'device_id' => 'required|string|exists:devices,id', + 'startTs' => 'nullable|numeric', + 'endTs' => 'nullable|numeric|gte:startTs', + ]); + + if ($validator->fails()) { + return response()->json([ + 'success' => false, + 'message' => $validator->errors()->first() + ], 400); + } + + // Fetch device with relationships + $device = Device::with([ + 'deviceProfile', + 'timeseriesKeys' => function ($query) { + $query->where(function ($q) { + $q->where('display_on_dashboard', true) + ->orWhere('display_on_popup', true); + }) + ->select('id', 'device_profile_xid', 'key_name', 'display_name') + ->with(['timeseriesAlert' => function ($query) { + $query->select('id', 'timeseries_key_master_xid', 'min_value', 'max_value', 'color_code', 'alert_msg'); + }]); + } + ])->findOrFail($request->device_id); + + // Get telemetry data FIRST + $telemetryData = $this->customerInfoService->getTelemetryDataDevice( + $device, + $device->timeseriesKeys->pluck('key_name')->toArray(), + $request->startTs, + $request->endTs + ); + + if (isset($telemetryData['error'])) { + throw new \Exception($telemetryData['error']); + } + + // Get only the keys that actually exist in ThingsBoard response + $existingKeys = array_keys($telemetryData); + + // Process data + $result = [ + 'alerts' => [], + 'normal_telemetry' => [] + ]; + + foreach ($device->timeseriesKeys as $key) { + // Skip if this key doesn't exist in ThingsBoard response + if (!in_array($key->key_name, $existingKeys)) { + continue; + } + + $dataPoint = $telemetryData[$key->key_name][0] ?? null; + if (!$dataPoint || !isset($dataPoint['value'])) { + continue; + } + + // Convert values to float for proper comparison + $currentValue = (float)$dataPoint['value']; + $item = [ + 'key' => $key->key_name, + 'display_name' => $key->display_name, + 'value' => $currentValue, + 'timestamp' => $dataPoint['ts'] ?? null + ]; + + $alertTriggered = false; + + foreach ($key->timeseriesAlert as $alert) { + // Convert thresholds to float + $minValue = $alert->min_value !== null ? (float)$alert->min_value : null; + $maxValue = $alert->max_value !== null ? (float)$alert->max_value : null; + + $isBelowMin = $minValue !== null && $currentValue < $minValue; + $isAboveMax = $maxValue !== null && $currentValue > $maxValue; + + if ($isBelowMin || $isAboveMax) { + Log::warning("Alert triggered", [ + 'device' => $device->id, + 'key' => $key->key_name, + 'value' => $currentValue, + 'threshold_min' => $minValue, + 'threshold_max' => $maxValue, + 'condition' => $isBelowMin ? 'below_min' : 'above_max' + ]); + + $item['alert'] = [ + 'message' => $alert->alert_msg, + 'color_code' => $alert->color_code, + 'thresholds' => [ + 'min' => $minValue, + 'max' => $maxValue + ] + ]; + $result['alerts'][] = $item; + $alertTriggered = true; + break; + } + } + + if (!$alertTriggered) { + $result['normal_telemetry'][] = $item; + } + } + + return response()->json([ + 'success' => true, + 'device' => [ + 'id' => $device->id, + 'name' => $device->name, + 'profile' => $device->deviceProfile + ], + 'data' => $result + ]); + + } catch (\Exception $e) { + Log::error("Alert processing failed: {$e->getMessage()}", [ + 'device_id' => $request->device_id ?? null, + 'trace' => $e->getTraceAsString() + ]); + + return response()->json([ + 'success' => false, + 'message' => 'Failed to process alerts: ' . $e->getMessage() + ], 500); + } + } +} diff --git a/app/Models/TimeseriesAlertMessage.php b/app/Models/TimeseriesAlertMessage.php new file mode 100644 index 0000000..46b9c11 --- /dev/null +++ b/app/Models/TimeseriesAlertMessage.php @@ -0,0 +1,31 @@ +belongsTo(TimeseriesKeyMaster::class, 'timeseries_key_master_xid', 'id'); + } +} \ No newline at end of file diff --git a/app/Models/TimeseriesKeyMaster.php b/app/Models/TimeseriesKeyMaster.php index 85eed34..31d0491 100644 --- a/app/Models/TimeseriesKeyMaster.php +++ b/app/Models/TimeseriesKeyMaster.php @@ -28,4 +28,9 @@ class TimeseriesKeyMaster extends Model { return $this->belongsTo(Device::class, 'device_profile_xid', 'device_profile_id'); } -} \ No newline at end of file + + public function timeseriesAlert() + { + return $this->hasMany(TimeseriesAlertMessage::class, 'timeseries_key_master_xid', 'id'); + } +} diff --git a/app/Services/CustomerInfoService.php b/app/Services/CustomerInfoService.php index bd40b46..1b1260d 100644 --- a/app/Services/CustomerInfoService.php +++ b/app/Services/CustomerInfoService.php @@ -184,6 +184,7 @@ class CustomerInfoService return $telemetry; } + public function getTelemetryDataDeviceDiagonostic($device, $keyNames, $startTs, $endTs) { @@ -389,4 +390,4 @@ class CustomerInfoService } } -} +} \ No newline at end of file diff --git a/database/migrations/2025_04_01_063542_create_timeseries_alert_messages_table.php b/database/migrations/2025_04_01_063542_create_timeseries_alert_messages_table.php new file mode 100644 index 0000000..407680d --- /dev/null +++ b/database/migrations/2025_04_01_063542_create_timeseries_alert_messages_table.php @@ -0,0 +1,36 @@ +uuid('id')->primary(); + $table->unsignedBigInteger('timeseries_key_master_xid'); + $table->decimal('min_value', 10, 2)->nullable(); + $table->decimal('max_value', 10, 2)->nullable(); + $table->string('color_code', 7)->nullable(); + $table->text('alert_msg')->nullable(); + $table->timestamps(); + $table->softDeletes(); + + // $table->foreign('timeseries_key_master_xid')->references('id')->on('timeseries_key_master')->onDelete('cascade'); + + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('timeseries_alert_messages'); + } +}; diff --git a/routes/customer_api.php b/routes/customer_api.php index c011629..0a3e31f 100644 --- a/routes/customer_api.php +++ b/routes/customer_api.php @@ -10,7 +10,7 @@ use App\Http\Controllers\APIS\CustomerApi\AuthController; use App\Http\Controllers\APIS\CustomerApi\CustomerDeviceInfoController; use App\Http\Controllers\APIS\CustomerApi\TelemetryController; use App\Http\Controllers\APIS\CustomerApi\DownloadsController as CustomerApiDownloadsController; - +use App\Http\Controllers\APIS\CustomerApi\TimeseriesAlertMessageController; Route::get('/customerapi', function () { return ('Welcome to admin api routes.'); @@ -31,6 +31,12 @@ Route::get('/customer-device-info',[CustomerDeviceInfoController::class,'custome Route::post('/telemetry-data-asset',[TelemetryController::class,'telemetryDataAsset']); Route::post('/telemetry-data-device',[TelemetryController::class,'telemetryDataDevice']); +Route::get('/user-assets', [UserAssetLinkController::class, 'index']); +Route::post('/store/report', [CustomerApiDownloadsController::class, 'storePdfData'])->name('store-report'); +Route::post('/fetch/report', [CustomerApiDownloadsController::class, 'fetchReport'])->name('fetch-report'); +Route::post('/destroy/report', [CustomerApiDownloadsController::class, 'destroyReport'])->name('destroy-report'); + +Route::post('/alert-message',[TimeseriesAlertMessageController::class, 'alertMessage']); Route::get('/user-assets', [UserAssetLinkController::class, 'index']); Route::post('/store/report', [CustomerApiDownloadsController::class, 'storePdfData'])->name('store-report'); Route::post('/fetch/report', [CustomerApiDownloadsController::class, 'fetchReport'])->name('fetch-report'); @@ -38,4 +44,4 @@ Route::post('/telemetry-data-device',[TelemetryController::class,'telemetryDataD Route::post('/alarm', [AlarmControllerCommon::class, 'getDeviceAlarms'])->name('alarm.device'); -}); +}); \ No newline at end of file