Compare commits
1 Commits
ai/task-4
...
ai/issue-2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d0d6f1bfc5 |
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 Luca Haid
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
require_once(APPDIR."/Measurement/Measurement.php");
|
|
||||||
require_once(APPDIR."/Measurement/MeasurementModel.php");
|
|
||||||
|
|
||||||
class MeasurementsApicontroller extends mfBaseApicontroller
|
|
||||||
{
|
|
||||||
protected function init()
|
|
||||||
{
|
|
||||||
// Initialize database connection if needed
|
|
||||||
$this->addRoute("/measurements/latest", "getLatestMeasurements", "GET");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the latest measurements grouped by unique device_id and sensor_id combinations
|
|
||||||
* Returns one measurement per combination, limited to 50 results
|
|
||||||
*/
|
|
||||||
protected function getLatestMeasurements()
|
|
||||||
{
|
|
||||||
// Get limit from query parameters, default to 50, max 100
|
|
||||||
$limit = 50;
|
|
||||||
if (isset($this->get['limit']) && is_numeric($this->get['limit'])) {
|
|
||||||
$limit = min(100, max(1, intval($this->get['limit'])));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the latest measurements grouped by device_id and sensor_id
|
|
||||||
$measurements = MeasurementModel::getLatestGroupedByDeviceAndSensor($limit);
|
|
||||||
|
|
||||||
$result = [];
|
|
||||||
foreach ($measurements as $measurement) {
|
|
||||||
$result[] = [
|
|
||||||
'id' => $measurement->id,
|
|
||||||
'device_id' => $measurement->device_id,
|
|
||||||
'sensor_id' => $measurement->sensor_id,
|
|
||||||
'value' => (float)$measurement->value,
|
|
||||||
'unit' => $measurement->unit,
|
|
||||||
'timestamp' => $measurement->create,
|
|
||||||
'created_at' => date('Y-m-d H:i:s', $measurement->create)
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return mfResponse::Ok([
|
|
||||||
'count' => count($result),
|
|
||||||
'measurements' => $result
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
class Measurement extends mfBaseModel
|
|
||||||
{
|
|
||||||
private $device;
|
|
||||||
private $sensor;
|
|
||||||
|
|
||||||
public function getProperty($name)
|
|
||||||
{
|
|
||||||
if ($this->$name == null) {
|
|
||||||
if (!$this->id) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if($name == "device") {
|
|
||||||
$this->device = mfValuecache::singleton()->get("Device-id-".$this->device_id);
|
|
||||||
if($this->device === null) {
|
|
||||||
$this->device = new Device($this->device_id);
|
|
||||||
if($this->device->id) {
|
|
||||||
mfValuecache::singleton()->set("Device-id-".$this->device_id, $this->device);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $this->device;
|
|
||||||
}
|
|
||||||
|
|
||||||
if($name == "sensor") {
|
|
||||||
// Sensor would be a separate entity if it exists
|
|
||||||
// For now, just return the sensor_id
|
|
||||||
return $this->sensor_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
$classname = ucfirst($name);
|
|
||||||
$idfield = $name."_id";
|
|
||||||
if(property_exists($this->data, $idfield)) {
|
|
||||||
$this->$name = mfValuecache::singleton()->get("mfObjectmodel-$name-".$this->$idfield);
|
|
||||||
if(!$this->$name) {
|
|
||||||
$this->$name = new $classname($this->$idfield);
|
|
||||||
}
|
|
||||||
|
|
||||||
if($this->$name->id) {
|
|
||||||
mfValuecache::singleton()->set("mfObjectmodel-$name-".$this->$name->id, $this->$name);
|
|
||||||
return $this->$name;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->$name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
class MeasurementModel extends mfBaseModel
|
|
||||||
{
|
|
||||||
public static function search($filter = [])
|
|
||||||
{
|
|
||||||
$db = FronkDB::singleton();
|
|
||||||
$where = [];
|
|
||||||
$params = [];
|
|
||||||
|
|
||||||
if (isset($filter['device_id'])) {
|
|
||||||
if (is_array($filter['device_id'])) {
|
|
||||||
$where[] = "device_id IN (" . implode(',', array_map('intval', $filter['device_id'])) . ")";
|
|
||||||
} else {
|
|
||||||
$where[] = "device_id = " . intval($filter['device_id']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($filter['sensor_id'])) {
|
|
||||||
if (is_array($filter['sensor_id'])) {
|
|
||||||
$where[] = "sensor_id IN (" . implode(',', array_map('intval', $filter['sensor_id'])) . ")";
|
|
||||||
} else {
|
|
||||||
$where[] = "sensor_id = " . intval($filter['sensor_id']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$whereClause = '';
|
|
||||||
if (count($where)) {
|
|
||||||
$whereClause = ' WHERE ' . implode(' AND ', $where);
|
|
||||||
}
|
|
||||||
|
|
||||||
$order = ' ORDER BY `create` DESC';
|
|
||||||
if (isset($filter['order'])) {
|
|
||||||
$order = ' ORDER BY ' . $db->escape($filter['order']);
|
|
||||||
}
|
|
||||||
|
|
||||||
$limit = '';
|
|
||||||
if (isset($filter['limit'])) {
|
|
||||||
$limit = ' LIMIT ' . intval($filter['limit']);
|
|
||||||
}
|
|
||||||
|
|
||||||
$sql = "SELECT * FROM Measurement" . $whereClause . $order . $limit;
|
|
||||||
$res = $db->query($sql);
|
|
||||||
|
|
||||||
$measurements = [];
|
|
||||||
while ($row = $db->fetch_object($res)) {
|
|
||||||
$measurement = new Measurement();
|
|
||||||
$measurement->load($row);
|
|
||||||
$measurements[] = $measurement;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $measurements;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getLatestGroupedByDeviceAndSensor($limit = 50)
|
|
||||||
{
|
|
||||||
$db = FronkDB::singleton();
|
|
||||||
|
|
||||||
// Get the most recent measurements, grouped by unique device_id and sensor_id combinations
|
|
||||||
// This query gets the latest measurement for each device_id/sensor_id combination
|
|
||||||
$sql = "
|
|
||||||
SELECT m.*
|
|
||||||
FROM Measurement m
|
|
||||||
INNER JOIN (
|
|
||||||
SELECT device_id, sensor_id, MAX(`create`) as max_create
|
|
||||||
FROM (
|
|
||||||
SELECT device_id, sensor_id, `create`
|
|
||||||
FROM Measurement
|
|
||||||
ORDER BY `create` DESC
|
|
||||||
LIMIT 1000
|
|
||||||
) AS recent
|
|
||||||
GROUP BY device_id, sensor_id
|
|
||||||
) AS latest
|
|
||||||
ON m.device_id = latest.device_id
|
|
||||||
AND m.sensor_id = latest.sensor_id
|
|
||||||
AND m.`create` = latest.max_create
|
|
||||||
ORDER BY m.`create` DESC
|
|
||||||
LIMIT " . intval($limit);
|
|
||||||
|
|
||||||
$res = $db->query($sql);
|
|
||||||
|
|
||||||
$measurements = [];
|
|
||||||
while ($row = $db->fetch_object($res)) {
|
|
||||||
$measurement = new Measurement();
|
|
||||||
$measurement->load($row);
|
|
||||||
$measurements[] = $measurement;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $measurements;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user