127 lines
4.3 KiB
PHP
127 lines
4.3 KiB
PHP
<?php
|
|
|
|
namespace App\Jobs\Machine;
|
|
|
|
use App\Models\Machine\Machine;
|
|
use Illuminate\Bus\Queueable;
|
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
|
use Illuminate\Foundation\Bus\Dispatchable;
|
|
use Illuminate\Queue\InteractsWithQueue;
|
|
use Illuminate\Queue\SerializesModels;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
class ProcessHeartbeat implements ShouldQueue
|
|
{
|
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
|
|
|
protected $serialNo;
|
|
protected $payload;
|
|
|
|
/**
|
|
* Create a new job instance.
|
|
*/
|
|
public function __construct(string $serialNo, $payload)
|
|
{
|
|
$this->serialNo = $serialNo;
|
|
$this->payload = (array) $payload;
|
|
}
|
|
|
|
/**
|
|
* Execute the job.
|
|
*/
|
|
public function handle(): void
|
|
{
|
|
$machine = Machine::withoutGlobalScopes()->where('serial_no', $this->serialNo)->first();
|
|
|
|
if (!$machine) {
|
|
Log::warning("MQTT Heartbeat: Machine not found", ['serial_no' => $this->serialNo]);
|
|
return;
|
|
}
|
|
|
|
// 更新機台狀態
|
|
Log::debug("ProcessHeartbeat: Attempting to set status to online for {$this->serialNo}");
|
|
$updateData = [
|
|
'last_heartbeat_at' => now(),
|
|
'status' => 'online',
|
|
];
|
|
|
|
// === 狀態異動觸發 (Redis 快取,讓 MQTT 也支援日誌) ===
|
|
$cacheKey = "machine:{$this->serialNo}:state";
|
|
$oldState = \Illuminate\Support\Facades\Cache::get($cacheKey);
|
|
// 1. 偵測狀態由離線轉在線 (寫入日誌)
|
|
if ($machine->status === 'offline') {
|
|
\App\Jobs\Machine\ProcessStateLog::dispatch(
|
|
$machine->id,
|
|
$machine->company_id,
|
|
"Machine is online",
|
|
'info',
|
|
[]
|
|
);
|
|
}
|
|
|
|
$newState = $oldState ?? [];
|
|
|
|
// 2. 韌體版本比對 (有改才傳,有改才記)
|
|
if (isset($this->payload['firmware_version'])) {
|
|
$fv = (string) $this->payload['firmware_version'];
|
|
if (!isset($oldState['firmware_version']) || (string)$oldState['firmware_version'] !== $fv) {
|
|
\App\Jobs\Machine\ProcessStateLog::dispatch(
|
|
$machine->id,
|
|
$machine->company_id,
|
|
"Firmware updated to :version",
|
|
'info',
|
|
['version' => $fv, 'old' => $oldState['firmware_version'] ?? 'Unknown']
|
|
);
|
|
}
|
|
$updateData['firmware_version'] = $fv;
|
|
$newState['firmware_version'] = $fv;
|
|
}
|
|
|
|
// 2. 溫度更新 (只要有變動就紀錄日誌)
|
|
if (isset($this->payload['temperature'])) {
|
|
$temp = (int) $this->payload['temperature'];
|
|
Log::debug("ProcessHeartbeat: Temperature reported for {$this->serialNo}: {$temp}");
|
|
$updateData['temperature'] = $temp;
|
|
|
|
// 讀取最後一次記錄日誌的溫度與時間
|
|
$tempLogCacheKey = "machine:{$this->serialNo}:last_temp_log";
|
|
$lastLogEntry = \Illuminate\Support\Facades\Cache::get($tempLogCacheKey);
|
|
|
|
$shouldLog = false;
|
|
if (!$lastLogEntry) {
|
|
$shouldLog = true;
|
|
} else {
|
|
$lastValue = (int) $lastLogEntry['value'];
|
|
$lastAt = \Carbon\Carbon::parse($lastLogEntry['at']);
|
|
|
|
// 條件 A: 只要溫度與上次記錄不同就記錄 (整數比對)
|
|
if ($temp !== $lastValue) {
|
|
Log::debug("ProcessHeartbeat: Temperature changed from {$lastValue} to {$temp}. Triggering log.");
|
|
$shouldLog = true;
|
|
}
|
|
}
|
|
|
|
if ($shouldLog) {
|
|
\App\Jobs\Machine\ProcessStateLog::dispatch(
|
|
$machine->id,
|
|
$machine->company_id,
|
|
"Temperature reported: :temp°C",
|
|
'info',
|
|
['temp' => $temp]
|
|
);
|
|
|
|
// 更新快取,保留 7 天
|
|
\Illuminate\Support\Facades\Cache::put($tempLogCacheKey, [
|
|
'value' => $temp,
|
|
'at' => now()->toDateTimeString()
|
|
], 604800);
|
|
}
|
|
}
|
|
|
|
// 更新快取
|
|
\Illuminate\Support\Facades\Cache::put($cacheKey, $newState, 86400);
|
|
|
|
$machine->update($updateData);
|
|
}
|
|
}
|