admin.aprsto/htdocs/includes/repositories/stationrepository.class.php
Steve White ae9ece5266 *** Initial Commit of Files for APRS.TO Backend Administration Panel ***
This code is non-functional at this point.
2025-02-02 15:53:34 -05:00

629 lines
22 KiB
PHP

<?php
class StationRepository extends ModelRepository
{
private static $_singletonInstance = null;
private $_config;
public function __construct()
{
parent::__construct('Station');
}
/**
* Returnes an initiated StationRepository
*
* @return StationRepository
*/
public static function getInstance()
{
if (self::$_singletonInstance === null) {
self::$_singletonInstance = new StationRepository();
}
return self::$_singletonInstance;
}
/**
* Get object by id
*
* @param int $id
* @param int $source_id (optional, 1 = APRS, 2 = CWOP, 3 = CBAPRS, 5 = OGN) used by query planner to optimize the query)
* @return Station
*/
public function getObjectById($id, $source_id = null)
{
if (!isInt($id) || (!is_null($source_id) && !isInt($source_id)))
{
return new Station(0);
}
static $cache = array();
$key = $id;
if (!isset($cache[$key]))
{
if (is_null($source_id))
$cache[$key] = $this->getObjectFromSql('select * from station where id = ?', [$id]);
else
$cache[$key] = $this->getObjectFromSql('select * from station where id = ? AND source_id = ?', [$id, $source_id]);
}
return $cache[$key];
}
/**
* Get object by name
*
* @param string $name
* @param int $source_id (optional, 1 = APRS, 2 = CWOP, 3 = CBAPRS, 5 = OGN) used by query planner to optimize the query)
* @return Station
*/
public function getObjectByName($name, $source_id = null)
{
if (!is_string($name) || (!is_null($source_id) && !isInt($source_id)))
{
return new Station(0);
}
static $cache = array();
$key = $name;
if (!isset($cache[$key]))
{
if (is_null($source_id))
$cache[$key] = $this->getObjectFromSql('select * from station where name = ? order by latest_location_packet_timestamp desc nulls last limit 1', [$name]);
else
$cache[$key] = $this->getObjectFromSql('select * from station where name = ? AND source_id = ? order by latest_location_packet_timestamp desc nulls last limit 1', [$name, $source_id]);
}
return $cache[$key];
}
/**
* Get list of objects by name (call)
*
* @param string $name
* @param int $source_id (optional, 1 = APRS, 2 = CWOP, 3 = CBAPRS, 5 = OGN) used by query planner to optimize the query)
* @return Station
*/
public function getObjectListByName($name, $station_type_id = null, $source_id = null, $min_timestamp = 0)
{
if (!is_string($name) || (!is_null($source_id) && !isInt($source_id)) || (!is_null($station_type_id) && !isInt($station_type_id)) || !isInt($station_type_id) || !isInt($min_timestamp))
{
return new Station(0);
}
static $cache = array();
$key = $name;
if (!isset($cache[$key]))
{
if (isInt($station_type_id) && isInt($source_id))
{
$cache[$key] = $this->getObjectListFromSql('select *
from station
where station_type_id = ?
and name = ?
and (source_id = ? or source_id is null)
and latest_confirmed_packet_timestamp > ?', [$station_type_id, $name, $source_id, $min_timestamp]);
}
else if (isInt($station_type_id))
{
$cache[$key] = $this->getObjectListFromSql('select *
from station
where station_type_id = ?
and name = ?
and latest_confirmed_packet_timestamp > ?', [$station_type_id, $name, $min_timestamp]);
}
else
{
$cache[$key] = $this->getObjectListFromSql('select *
from station
where name = ?
and latest_confirmed_packet_timestamp > ?', [$name, $min_timestamp]);
}
}
return $cache[$key];
}
/**
* Get sender station object by sender id
*
* @param string $name
* @param int $senderId
* @param int $source_id (optional, 1 = APRS, 2 = CWOP, 3 = CBAPRS, 5 = OGN) used by query planner to optimize the query)
* @return Station
*/
public function getSenderStationObjectBySenderId($senderId, $source_id = null)
{
if (!isInt($senderId) || (!is_null($source_id) && !isInt($source_id)))
{
return new Station(0);
}
static $cache = array();
$key = $senderId;
if (!isset($cache[$key]))
{
if (is_null($source_id))
$cache[$key] = $this->getObjectFromSql('select station.* from station, sender where station.latest_sender_id = sender.id and sender.name = station.name and sender.id = ? and station.station_type_id = 1', [$senderId]);
else
$cache[$key] = $this->getObjectFromSql('select station.* from station, sender where station.source_id = ? station.latest_sender_id = sender.id and sender.name = station.name and sender.id = ? and station.station_type_id = 1', [$source_id, $senderId]);
}
return $cache[$key];
}
/**
* Get object by name and sender id
*
* @param string $name
* @param int $senderId
* @param int $source_id (optional, 1 = APRS, 2 = CWOP, 3 = CBAPRS, 5 = OGN) used by query planner to optimize the query)
* @return Station
*/
public function getObjectByNameAndSenderId($name, $senderId, $source_id = null)
{
if (!is_string($name) || !isInt($senderId) || (!is_null($source_id) && !isInt($source_id)))
{
return new Station(0);
}
static $cache = array();
$key = $name . ';' . $senderId;
if (!isset($cache[$key]))
{
if (is_null($source_id))
$cache[$key] = $this->getObjectFromSql('select * from station where name = ? and latest_sender_id = ?', [$name, $senderId]);
else
$cache[$key] = $this->getObjectFromSql('select * from station where name = ? and source_id = ? and latest_sender_id = ?', [$name, $source_id, $senderId]);
}
return $cache[$key];
}
/**
* Get object list
*
* @param int $activeDuringLatestNumberOfSeconds
* @param int $limit
* @param int $offset
* @param mixed $filter_source - ID of the data_source (APRS, CWOP) or an array of data source IDs.
* @return array
*/
public function getObjectList($activeDuringLatestNumberOfSeconds = (24*60*60), $limit=50, $offset=0, $filter_source = 0)
{
if ($activeDuringLatestNumberOfSeconds == 0)
{
$activeDuringLatestNumberOfSeconds = time();
}
// Filter sources
$filter_query = null;
$filter_data = null;
if (is_array($filter_source))
{
if (count($filter_source) > 0 && count($filter_source) < 4)
{
$filter_query = 'and source_id IN (' . implode(',', $filter_source) . ')';
}
}
else if (isInt($filter_source) && $filter_source > 0)
{
$filter_query = 'and source_id = :fs';
$filter_data = intval($filter_source);
}
$pdo = PDOConnection::getInstance();
$stmt = $pdo->prepare(
'select * from station
where latest_confirmed_packet_timestamp is not null
and latest_confirmed_packet_timestamp > :ts1
and (source_id != 5 or latest_confirmed_packet_timestamp > :ts2)
' . $filter_query . '
order by latest_confirmed_packet_timestamp desc
limit :limit offset :offset'
);
$stmt->bindValue(':ts1', (time() - $activeDuringLatestNumberOfSeconds));
$stmt->bindValue(':ts2', (time() - (60*60*24))); // OGN data should be deleted after 24h, but just to be safe we avoid including older data when searching
$stmt->bindValue(':limit', $limit);
$stmt->bindValue(':offset', $offset);
if (!is_null($filter_data)) {
$stmt->bindValue(':fs', $filter_data);
}
$stmt->execute();
$records = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (is_array($records) && !empty($records)) {
return $this->_getObjectListFromRecords($records);
}
// No object found, return empty array
return [];
}
/**
* Get number of stations
*
* @param int $activeDuringLatestNumberOfSeconds
* @return int
*/
public function getNumberOfStations($activeDuringLatestNumberOfSeconds = (24*60*60))
{
if ($activeDuringLatestNumberOfSeconds == 0) {
$activeDuringLatestNumberOfSeconds = time();
}
$sql = 'select count(*) c from station
where latest_confirmed_packet_timestamp is not null
and latest_confirmed_packet_timestamp > ?
and (source_id != 5 or latest_confirmed_packet_timestamp > ?)';
$parameters = [(time() - $activeDuringLatestNumberOfSeconds), (time() - (60*60*24))];
$pdo = PDOConnection::getInstance();
$stmt = $pdo->prepareAndExec($sql, $parameters);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
$sum = 0;
foreach($rows as $row) {
$sum += $row['c'];
}
return $sum;
}
/**
* Get object list by query string
*
* @param int $q
* @param int $activeDuringLatestNumberOfSeconds
* @param int $limit
* @param int $offset
* @return array
*/
public function getObjectListByQueryString($q, $activeDuringLatestNumberOfSeconds = (24*60*60), $limit=50, $offset=0)
{
if (!is_string($q) || !isInt($limit)) {
return [];
}
if ($activeDuringLatestNumberOfSeconds == 0) {
$activeDuringLatestNumberOfSeconds = time();
}
$pdo = PDOConnection::getInstance();
$stmt = $pdo->prepare('
select s.*
from station s
left outer join ogn_device d on d.device_id = s.latest_ogn_sender_address
where s.latest_confirmed_packet_timestamp is not null
and s.latest_confirmed_packet_timestamp > ?
and (s.source_id != 5 or s.latest_confirmed_packet_timestamp > ?)
and (s.name ilike ? or d.registration ilike ? or d.cn ilike ?)
order by name
limit ? offset ?'
);
$stmt->bindValue(1, (time() - $activeDuringLatestNumberOfSeconds));
$stmt->bindValue(2, (time() - (60*60*24))); // OGN data should be deleted after 24h, but just to be safe we avoid including older data when searching
$stmt->bindValue(3, "$q%");
$stmt->bindValue(4, "$q%");
$stmt->bindValue(5, "$q%");
$stmt->bindValue(6, $limit);
$stmt->bindValue(7, $offset);
$stmt->execute();
$records = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (is_array($records) && !empty($records)) {
return $this->_getObjectListFromRecords($records);
}
// No object found, return empty array
return [];
}
/**
* Get number of stations by query string
*
* @param int $q
* @param int $activeDuringLatestNumberOfSeconds
* @return int
*/
public function getNumberOfStationsByQueryString($q, $activeDuringLatestNumberOfSeconds = (24*60*60))
{
if (!is_string($q)) {
return 0;
}
if ($activeDuringLatestNumberOfSeconds == 0) {
$activeDuringLatestNumberOfSeconds = time();
}
$sql = 'select count(*) c from station
where latest_confirmed_packet_timestamp is not null
and latest_confirmed_packet_timestamp > ?
and (source_id != 5 or latest_confirmed_packet_timestamp > ?)
and name ilike ?';
$parameters = [(time() - $activeDuringLatestNumberOfSeconds), (time() - (60*60*24)), "$q%"];
$pdo = PDOConnection::getInstance();
$stmt = $pdo->prepareAndExec($sql, $parameters);
return $stmt->fetchColumn();
}
/**
* Get object list by sender id (close to specified lat/lng)
*
* @param int $senderId
* @param int $limit
* @param float $latitude
* @param float $longitude
* @return array
*/
public function getObjectListBySenderId($senderId, $limit, $latitude = null, $longitude = null)
{
if (!isInt($senderId) || !isFloat($latitude) || !isFloat($longitude) || !isInt($limit)) {
return [];
}
$pdo = PDOConnection::getInstance();
if ($latitude === null || $longitude === null) {
$stmt = $pdo->prepare(
'select * from station
where latest_confirmed_packet_timestamp is not null
and latest_confirmed_packet_timestamp > ?
and latest_sender_id = ?
limit ?'
);
$stmt->bindValue(1, (time() - 60*60*24));
$stmt->bindValue(2, $senderId);
$stmt->bindValue(3, $limit);
} else {
$stmt = $pdo->prepare(
'select * from station
where latest_confirmed_packet_timestamp is not null
and latest_confirmed_packet_timestamp > ?
and latest_sender_id = ?
order by (abs(latest_confirmed_latitude - ?) + abs(latest_confirmed_longitude - ?))
limit ?'
);
$stmt->bindValue(1, (time() - 60*60*24));
$stmt->bindValue(2, $senderId);
$stmt->bindValue(3, $latitude);
$stmt->bindValue(4, $longitude);
$stmt->bindValue(5, $limit);
}
$stmt->execute();
$records = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (is_array($records) && !empty($records)) {
return $this->_getObjectListFromRecords($records);
}
// No object found, return empty array
return [];
}
/**
* Get related object list by station id (stations with same name but different SSID)
*
* @param int $stationId
* @param int $limit
* @param int $durationHours (Number of hours to consider as active, default 24)
* @return array
*/
public function getRelatedObjectListByStationId($stationId, $limit, $durationHours = 24)
{
if (!isInt($stationId) || !isInt($limit)) {
return [];
}
$station = $this->getObjectById($stationId);
$name = $station->name;
$pos = strrpos($name, '-');
if ($pos) {
$call = substr($name, 0, $pos);
} else {
// Primary stations are supposed to use the -0 suffix.
// Some APRS software will drop the -0 and transmit just the callsign.
$call = $name;
}
if ($station->latestPacketTimestamp !== null) {
$time = $station->latestPacketTimestamp;
} else {
$time = time();
}
$pdo = PDOConnection::getInstance();
if ($station->latestConfirmedLatitude === null || $station->latestConfirmedLongitude === null) {
$stmt = $pdo->prepare(
'select * from station
where latest_confirmed_packet_timestamp is not null
and latest_confirmed_packet_timestamp > ?
and name like ?
limit ?'
);
$stmt->bindValue(1, $time - 60*60*$durationHours, PDO::PARAM_STR);
$stmt->bindValue(2, "$call%", PDO::PARAM_STR);
$stmt->bindValue(3, $limit);
} else {
$stmt = $pdo->prepare(
'select * from station
where latest_confirmed_packet_timestamp is not null
and latest_confirmed_packet_timestamp > ?
and name like ?
order by (abs(latest_confirmed_latitude - ?) + abs(latest_confirmed_longitude - ?))
limit ?'
);
$stmt->bindValue(1, $station->latestPacketTimestamp - 60*60*$durationHours, PDO::PARAM_STR);
$stmt->bindValue(2, "$call%", PDO::PARAM_STR);
$stmt->bindValue(3, $station->latestConfirmedLatitude);
$stmt->bindValue(4, $station->latestConfirmedLongitude);
$stmt->bindValue(5, $limit);
}
$stmt->execute();
$records = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (is_array($records) && !empty($records)) {
return $this->_getObjectListFromRecords($records);
}
// No object found, return empty array
return [];
}
/**
* Get the most recently used station (-SSID) from station call (no SSID)
*
* @param int $stationName
* @param int $limit
* @param int $durationHours (Number of hours to consider as active, default 60 days)
* @return array
*/
public function getMostRecentStationByName($stationName, $limit = 1, $durationHours = 24*60)
{
if (!isInt($limit)) {
return [];
}
$call = trim($stationName);
$pdo = PDOConnection::getInstance();
$stmt = $pdo->prepare(
'select * from station
where latest_confirmed_packet_timestamp is not null
and latest_confirmed_packet_timestamp > ?
and name like ?
order by latest_confirmed_packet_timestamp desc
limit ?'
);
$stmt->bindValue(1, time() - 60*60*$durationHours, PDO::PARAM_STR);
$stmt->bindValue(2, "$call%", PDO::PARAM_STR);
$stmt->bindValue(3, $limit);
$stmt->execute();
$records = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (is_array($records) && !empty($records)) {
return $this->_getObjectListFromRecords($records);
}
// No object found, return empty array
return [];
}
/**
* Get object list of stations near specified position
*
* @param int $latitude
* @param int $longitude
* @param int $maxDistanceInKm
* @param int $limit
* @param string $sourceFilter
* @return array
*/
public function getCloseByObjectListByPosition($latitude, $longitude, $maxDistanceInKm = 100, $limit = 10000, $sourceFilter = null)
{
if (!isFloat($latitude) || !isFloat($longitude) || !isInt($maxDistanceInKm) || !isInt($limit)) {
return [];
}
if ($maxDistanceInKm <= 10) {
// Plus 0.1 should be about 11km
$minLatitude = $latitude - 0.1;
$maxLatitude = $latitude + 0.1;
$minLongitude = $longitude - 0.1;
$maxLongitude = $longitude + 0.1;
} else {
// Plus 1 should be about 111km
$minLatitude = $latitude - 1;
$maxLatitude = $latitude + 1;
$minLongitude = $longitude - 1;
$maxLongitude = $longitude + 1;
}
$minTimestamp = time() - 60*60; // Latest 1h is pretty fast
$sourceFilterSQL = '';
if ($sourceFilter != null && strlen($sourceFilter) > 0)
{
$sourceFilterSQL = " and source_id IN($sourceFilter) ";
}
// The order by used here is not 100% accurate, but it's fast :-)
$list = $this->getObjectListFromSql(
'select * from station
where latest_confirmed_latitude > ?
and latest_confirmed_latitude < ?
and latest_confirmed_longitude > ?
and latest_confirmed_longitude < ?
and latest_confirmed_packet_timestamp > ?
' . $sourceFilterSQL . '
order by (abs(latest_confirmed_latitude - ?) + abs(latest_confirmed_longitude - ?))
limit ?', [$minLatitude, $maxLatitude, $minLongitude, $maxLongitude, $minTimestamp, $latitude, $longitude, $limit]
);
$orderedList = [];
foreach ($list as $closeByStation) {
$distance = $closeByStation->getDistance($latitude, $longitude);
if ($distance !== null && $distance <= ($maxDistanceInKm*1000)) {
$key = intval($distance);
while (isset($orderedList[$key])) {
$key++;
}
$orderedList[$key] = $closeByStation;
}
}
ksort($orderedList);
return array_values($orderedList);
}
/**
* Get object list of stations near specified station
*
* @param int $stationId
* @param int $limit
* @param string $filterSource
* @return array
*/
public function getCloseByObjectListByStationId($stationId, $limit, $filterSource)
{
if (!isInt($stationId) || !isInt($limit)) {
return [];
}
$station = $this->getObjectById($stationId);
if ($station->latestConfirmedLatitude === null || $station->latestConfirmedLongitude === null) {
return [];
}
$closeByStationList = self::getCloseByObjectListByPosition($station->latestConfirmedLatitude, $station->latestConfirmedLongitude, 100, $limit + 1, $filterSource);
$result = Array();
foreach ($closeByStationList as $key => $closeByStation) {
if ($station->getId() == $closeByStation->getId()) {
break;
}
}
if (isset($key)) {
unset($closeByStationList[$key]);
}
return $closeByStationList;
}
/**
* Super-basic utility function to get object list from raw SQL query
*
* @param int $sqlQuery
* @param array $queryParams
* @return array
*/
public function getObjectListWithByRawSQLQuery($sqlQuery, $queryParams=[])
{
if (empty($sqlQuery)) {
return [];
}
return $this->getObjectListFromSql($sqlQuery, $queryParams);
}
}