2024-09-03 21:04:34 -05:00
< ? php
/**
* Sensor . php
*
* Collects discovered sensors and allows the deletion of non - discovered sensors
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < https :// www . gnu . org / licenses />.
*
* @ link https :// www . librenms . org
*
* @ copyright 2024 Tony Murray
* @ author Tony Murray < murraytony @ gmail . com >
*/
namespace App\Discovery ;
use App\Models\Device ;
2024-09-14 19:13:11 -05:00
use App\Models\Eventlog ;
use App\Models\SensorToStateIndex ;
use App\Models\StateIndex ;
use App\Models\StateTranslation ;
use Illuminate\Database\UniqueConstraintViolationException ;
2024-09-03 21:04:34 -05:00
use Illuminate\Support\Collection ;
2024-09-13 10:23:17 -05:00
use Illuminate\Support\Facades\Log ;
use LibreNMS\Config ;
2024-09-03 21:04:34 -05:00
use LibreNMS\DB\SyncsModels ;
2024-09-14 19:13:11 -05:00
use LibreNMS\Enum\Severity ;
2024-09-03 21:04:34 -05:00
class Sensor
{
use SyncsModels ;
private Collection $models ;
/** @var bool[] */
private array $discovered = [];
private string $relationship = 'sensors' ;
private Device $device ;
2024-09-14 19:13:11 -05:00
/** @var array<string, Collection<StateTranslation>> */
private array $states = [];
2024-09-03 21:04:34 -05:00
public function __construct ( Device $device )
{
$this -> device = $device ;
$this -> models = new Collection ;
}
public function discover ( \App\Models\Sensor $sensor ) : static
{
2024-09-13 10:23:17 -05:00
if ( $this -> canSkip ( $sensor )) {
Log :: info ( '~' );
Log :: debug ( " Skipped Sensor: $sensor " );
return $this ;
}
2024-09-12 11:20:50 -05:00
$sensor -> device_id ? ? = \DeviceCache :: getPrimary () -> device_id ;
2024-09-03 21:04:34 -05:00
$this -> models -> push ( $sensor );
$this -> discovered [ $sensor -> syncGroup ()] = false ;
2024-09-13 10:23:17 -05:00
Log :: debug ( " Discovered Sensor: $sensor " );
Log :: info ( " $sensor->sensor_descr : Cur $sensor->sensor_current , Low: $sensor->sensor_limit_low , Low Warn: $sensor->sensor_limit_low_warn , Warn: $sensor->sensor_limit_warn , High: $sensor->sensor_limit " );
2024-09-03 21:04:34 -05:00
return $this ;
}
2024-09-14 19:13:11 -05:00
/**
* @ param string $stateName
* @ param StateTranslation [] | Collection < StateTranslation > $states
* @ return $this
*/
public function withStateTranslations ( string $stateName , array | Collection $states ) : static
{
$this -> states [ $stateName ] = new Collection ( $states );
return $this ;
}
2024-09-03 21:04:34 -05:00
public function isDiscovered ( string $type ) : bool
{
return $this -> discovered [ $type ] ? ? false ;
}
public function sync ( ... $params ) : Collection
{
$type = implode ( '-' , $params );
if ( ! $this -> isDiscovered ( $type )) {
$synced = $this -> syncModelsByGroup ( $this -> device , 'sensors' , $this -> getModels (), $params );
$this -> discovered [ $type ] = true ;
2024-09-14 19:13:11 -05:00
$this -> syncStates ( $synced );
2024-09-03 21:04:34 -05:00
return $synced ;
}
return new Collection ;
}
public function getModels () : Collection
{
return $this -> models ;
}
2024-09-13 10:23:17 -05:00
public function canSkip ( \App\Models\Sensor $sensor ) : bool
{
if ( ! empty ( $sensor -> sensor_class ) && ( Config :: getOsSetting ( $this -> device -> os , " disabled_sensors. $sensor->sensor_class " ) || Config :: get ( " disabled_sensors. $sensor->sensor_class " ))) {
return true ;
}
foreach ( Config :: getCombined ( $this -> device -> os , 'disabled_sensors_regex' ) as $skipRegex ) {
if ( ! empty ( $sensor -> sensor_descr ) && preg_match ( $skipRegex , $sensor -> sensor_descr )) {
return true ;
}
}
foreach ( Config :: getCombined ( $this -> device -> os , " disabled_sensors_regex. $sensor->sensor_class " ) as $skipRegex ) {
if ( ! empty ( $sensor -> sensor_descr ) && preg_match ( $skipRegex , $sensor -> sensor_descr )) {
return true ;
}
}
return false ;
}
2024-09-14 19:13:11 -05:00
private function syncStates ( Collection $sensors ) : void
{
$stateSensors = $sensors -> where ( 'sensor_class' , 'state' );
if ( $stateSensors -> isEmpty ()) {
return ;
}
$usedStates = $stateSensors -> pluck ( 'sensor_type' );
$existingStateIndexes = StateIndex :: whereIn ( 'state_name' , $usedStates ) -> get () -> keyBy ( 'state_name' );
foreach ( $usedStates as $stateName ) {
// make sure the state translations were given for this state name
if ( ! isset ( $this -> states [ $stateName ])) {
Log :: error ( " Non existent state name ( $stateName ) set by sensor: " . $stateSensors -> where ( 'sensor_type' , $stateName ) -> first () ? -> sensor_descr );
continue ;
}
$stateIndex = $existingStateIndexes -> get ( $stateName );
// create new state indexes
if ( $stateIndex == null ) {
try {
$stateIndex = StateIndex :: create ([ 'state_name' => $stateName ]);
$existingStateIndexes -> put ( $stateName , $stateIndex );
} catch ( UniqueConstraintViolationException ) {
Eventlog :: log ( " Duplicate state name $stateName (with case mismatch) " , $this -> device , 'sensor' , Severity :: Error , $stateSensors -> where ( 'sensor_type' , $stateName ) -> first () ? -> sensor_id );
continue ;
}
}
// set state_index_id
$stateTranslations = $this -> states [ $stateName ];
foreach ( $stateTranslations as $translation ) {
$translation -> state_index_id = $stateIndex -> state_index_id ;
}
// sync the translations to make sure they are up to date
$this -> syncModels ( $stateIndex , 'translations' , $stateTranslations );
}
// update sensor to state indexes
foreach ( $stateSensors as $stateSensor ) {
$state_index_id = $existingStateIndexes -> get ( $stateSensor -> sensor_type ) ? -> state_index_id ;
// only map if sensor gave a valid state name
if ( $state_index_id ) {
SensorToStateIndex :: updateOrCreate (
[ 'sensor_id' => $stateSensor -> sensor_id ],
[ 'state_index_id' => $state_index_id ],
);
}
}
}
2024-09-03 21:04:34 -05:00
}