Merge pull request 'sneha' (#56) from sneha into main
Reviewed-on: Nikhil.Kadam/vib360#56
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -873,4 +873,4 @@ class TelemetryController extends Controller
|
||||
// return response()->json(['telemetry' => $response]);
|
||||
// }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,333 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\APIS\CustomerApi;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Device;
|
||||
use App\Services\CustomerInfoService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
class TimeseriesAlertMessageController extends Controller
|
||||
{
|
||||
protected $customerInfoService;
|
||||
|
||||
public function __construct(CustomerInfoService $customerInfoService)
|
||||
{
|
||||
$this->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);
|
||||
}
|
||||
}
|
||||
}
|
||||
31
app/Models/TimeseriesAlertMessage.php
Normal file
31
app/Models/TimeseriesAlertMessage.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Concerns\HasUuids;
|
||||
|
||||
class TimeseriesAlertMessage extends Model
|
||||
{
|
||||
use HasFactory, HasUuids;
|
||||
|
||||
protected $table = 'timeseries_alert_messages';
|
||||
|
||||
protected $primaryKey = 'id';
|
||||
public $incrementing = false;
|
||||
protected $keyType = 'string';
|
||||
|
||||
protected $fillable = [
|
||||
'timeseries_key_master_xid',
|
||||
'min_value',
|
||||
'max_value',
|
||||
'color_code',
|
||||
'alert_msg',
|
||||
];
|
||||
|
||||
public function timeseriesKeyMaster()
|
||||
{
|
||||
return $this->belongsTo(TimeseriesKeyMaster::class, 'timeseries_key_master_xid', 'id');
|
||||
}
|
||||
}
|
||||
@@ -28,4 +28,9 @@ class TimeseriesKeyMaster extends Model
|
||||
{
|
||||
return $this->belongsTo(Device::class, 'device_profile_xid', 'device_profile_id');
|
||||
}
|
||||
}
|
||||
|
||||
public function timeseriesAlert()
|
||||
{
|
||||
return $this->hasMany(TimeseriesAlertMessage::class, 'timeseries_key_master_xid', 'id');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,6 +184,7 @@ class CustomerInfoService
|
||||
return $telemetry;
|
||||
}
|
||||
|
||||
|
||||
public function getTelemetryDataDeviceDiagonostic($device, $keyNames, $startTs, $endTs)
|
||||
{
|
||||
|
||||
@@ -389,4 +390,4 @@ class CustomerInfoService
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('timeseries_alert_messages', function (Blueprint $table) {
|
||||
$table->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');
|
||||
}
|
||||
};
|
||||
@@ -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');
|
||||
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user