Skip to content
Snippets Groups Projects
SmartAPI.php 20.2 KiB
Newer Older
root's avatar
root committed
<?php
namespace SmartData\SmartAPI
{
root's avatar
root committed
    require_once( __DIR__ . '/Logger.php');
    require_once( __DIR__ . '/Backend.php');
    require_once( __DIR__ . '/Packer.php');
    require_once( __DIR__ . '/Description.php');
    require_once( __DIR__ . '/MultiSmartData.php');
root's avatar
root committed

    use SmartData\SmartAPI\Internals\{JsonAPI, BinaryAPI};
    use SmartData\Exception\{BadRequestException, RequestFailedException, InsertionFailedException, CreationFailedException};
    use SmartData\{Series, SmartData, Backend, Credentials, Logger, MultiSmartData, Unit, Config_Common};
root's avatar
root committed
    use SmartData\Utility\{Pack,Unpack};

    function health_check()
    {
        return Backend::health_check();
    }

    {
        $json = json_decode($content, false, 512, JSON_BIGINT_AS_STRING);
        if (json_last_error() === JSON_ERROR_NONE) {
            list($credentials,$series,$parameter) = JsonAPI::parse_search($json);

            if ($series instanceof Series) { // is this verification required?
                $backend = new Backend($credentials);

                switch($series->version) {
                case SmartData::STATIC_VERSION:
                case SmartData::MOBILE_VERSION:
                    $return = $backend->search($series,$parameter);
                    break;
                default:
                    throw new \Exception("Unsupported SmartData version [{$ver}]");
                    return null;
                }

                if($return != NULL){
                    $return = json_encode( $return, JSON_NUMERIC_CHECK );
                } else {
                    throw new RequestFailedException("Error processing request: null return");
                }
            }
        } else {
            throw new BadRequestException("Error parsing content request: invalid series");
        }

        return $return;

    }

root's avatar
root committed
    function get($content)
    {
        $json = json_decode($content, false, 512, JSON_BIGINT_AS_STRING);
        if (json_last_error() === JSON_ERROR_NONE)
            list($credentials,$series,$aggregator,$options) = JsonAPI::parse_get($json);
        else
            list($credentials,$series,$aggregator,$options) = BinaryAPI::parse_get($content);

        if ($series instanceof Series) {
            $backend = new Backend($credentials);

            switch($series->version) {
            case SmartData::STATIC_VERSION:
                if ($series->dev== -999999)
                     $return = $backend->get_activity($series,$aggregator,$options);
                else
                     $return = $backend->query($series,$aggregator,$options);
root's avatar
root committed
                break;
root's avatar
root committed
	    case SmartData::MOBILE_VERSION:
root's avatar
root committed
                $return = $backend->track($series,$aggregator,$options);
                break;
            default:
                throw new \Exception("Unsupported SmartData version [{$ver}]");
                return null;
            }
root's avatar
root committed
                $isBigQuery = false;
                $bigUnits = [0x02240003, 0x02230000, 0x03020000]; //Image, WAV, LIDAR
root's avatar
root committed
                    //Check if the series are a "big unit"
		    $isBigQuery = in_array( $sd['unit'] & 0x0FFF0000 , $bigUnits ); 

root's avatar
root committed
                    $ver = intval( $sd['version'] );
                    $return['series'][$pos]['version'] = ( $ver >> 4 ) . "." . ( $ver & 0x0F );
                    if ( array_key_exists('value', $sd) ) {
                        $u = Unit::interpret( $sd['unit'] );
                        if ( $u->is_digital() && ( $sd['unit'] & 0x0FFF0000 ) == 0x02230000 ) {
                            $aux = base64_encode($sd['value']);
                            $return['series'][$pos]['value'] = $aux;
                // The following lines dumps the response to a file and uses X-SendFile to notify Apache to send the response.
                // this saves a lot of memory.
root's avatar
root committed
		if ( $isBigQuery ) {
                $file_path_for_digital_series = '/smartdata/tmp/' . microtime(true) . '.gets';
                file_put_contents($file_path_for_digital_series, '{"series": [' , FILE_APPEND);
                foreach ( $return['series'] as $key => $sd  ) {
                        $prefix = isset( $prefix ) ? ',' : '' ;
                        file_put_contents($file_path_for_digital_series, $prefix . json_encode($sd, JSON_NUMERIC_CHECK ) , FILE_APPEND);
                        unset($return['series'][$key]);
                }
                file_put_contents( $file_path_for_digital_series, ']}', FILE_APPEND );
                $return = [];
                $return['headers'][] = 'X-Sendfile: ' . $file_path_for_digital_series ;
                $return['headers'][] = 'Content-type: application/json' ;
                $return['headers'][] = 'Content-Length: ' . (filesize( $file_path_for_digital_series ) + 1) ;
root's avatar
root committed
		} else {
			$return = json_encode( $return, JSON_NUMERIC_CHECK );
	        }
root's avatar
root committed
            } else {
                throw new RequestFailedException("Error processing request: null return");
            }
        } else {
            throw new BadRequestException("Error parsing content request: invalid series");
        }
        return $return;
    }
    function put($content)
    {
        $values = json_decode($content, false, 512, JSON_BIGINT_AS_STRING);
        if (json_last_error() === JSON_ERROR_NONE) {
           if (property_exists( $values, "credentials") ) {
              $credentials = Credentials::fromJson( $values->credentials );
              $backend = new Backend($credentials);
           } else {
              $backend = new Backend();
           }
           if (property_exists( $values, "MultiValueSmartData")) {
              $data = MultiSmartData::multi_values($values);
              if (!$backend->insert_multiple(...$data)) {
                 throw new InsertionFailedException("JSON MultiValue SmartData Insertion Failed");
              }
           } else if (property_exists( $values, "MultiDeviceSmartData")) {
              $data = MultiSmartData::multi_devices($values);
              if (!$backend->insert_multiple(...$data)) {
                 throw new InsertionFailedException("JSON MultiDevice SmartData Insertion Failed");
              }
           } else if (property_exists( $values, "MultiUnitSmartData")) {
              $data = MultiSmartData::multi_units($values);
              $keys = array_keys($data);
              foreach ($keys as $k) {
                 $lst = $data[$k];
                 if (!$backend->insert_multiple(...$lst)) {
                    throw new InsertionFailedException("MultiUnit SmartData Insertion Failed");
root's avatar
root committed
              }
           } else if ( property_exists($values,"smartdata" ) ) {
              list($credentials,$smartdata_array,$params) = JsonAPI::parse_put($values);
              foreach($smartdata_array as $smartdata) {
                 $u = Unit::interpret($smartdata->unit);
                 if ( $u->is_digital() && ( $smartdata->unit & 0x0FFF0000 ) == 0x02230000 ) {
                    $wav_name = Config_Common::TEMP_DIR . "/arq". mt_rand(100000000000, 999999999999) . ".wav";
                    $flac_name = Config_Common::TEMP_DIR . "/arq". mt_rand(100000000000, 999999999999) . ".flac";
                    $arq = fopen($wav_name,"wb");
                    fwrite($arq, $smartdata->value);
                    fclose($arq);
                    $ret = shell_exec("ffmpeg -v 0 -loglevel 0 -y -i " . $wav_name . " -af aformat=s16 " . $flac_name);
                    $arq = fopen($flac_name,"rb");
                    $aux = fread($arq, filesize($flac_name));
                    $smartdata->value = $aux;
                    fclose($arq);
                    try { unlink($wav_name); } finally  { }
                    try { unlink($flac_name);  } finally { }
                 }
                 if(!$backend->insert($smartdata, ...$params)) {
                    throw new InsertionFailedException("Insertion Failed");
                 }
              }
           } else {
              throw new InsertionFailedException("SmartData type not identified...");
           $backend = new Backend();
           list($credentials,$smartdata_array,$params) = BinaryAPI::parse_put($content);
           foreach($smartdata_array as $smartdata) {
               if(!$backend->insert($smartdata, ...$params)) {
                   throw new InsertionFailedException("Insertion Failed");
               }
root's avatar
root committed

root's avatar
root committed
    {
        $json = json_decode($content, false, 512, JSON_BIGINT_AS_STRING);
        $last_error = json_last_error();
        if ($last_error === JSON_ERROR_NONE) {
root's avatar
root committed
            list($credentials,$series,$params) = JsonAPI::parse_create($json);
root's avatar
root committed
            list($credentials,$series,$params) = BinaryAPI::parse_create($content);
root's avatar
root committed
        if ($series instanceof Series) {
            $backend = new Backend($credentials);
            if(!$backend->finish($series, ...$params)) {
                throw new CreationFailedException("Finish Failed");
            } 
        } else {
            throw new BadRequestException("Error parsing content request: invalid series");
        }
    }
root's avatar
root committed

    function create($content)
    {
        $json = json_decode($content, false, 512, JSON_BIGINT_AS_STRING);
        $last_error = json_last_error();
        if ($last_error === JSON_ERROR_NONE) {
            list($credentials,$series,$params) = JsonAPI::parse_create($json);
        } else {
            list($credentials,$series,$params) = BinaryAPI::parse_create($content);
        }
        if ($series instanceof Series) {
root's avatar
root committed
            $backend = new Backend($credentials);
            if(!$backend->create($series, ...$params)) {
root's avatar
root committed
                throw new CreationFailedException("Creation Failed");
root's avatar
root committed
        } else {
            throw new BadRequestException("Error parsing content request: invalid series");
        }
    }

    function attach($content)
    {
        $json = json_decode($content, false, 512, JSON_BIGINT_AS_STRING);
        if (json_last_error() === JSON_ERROR_NONE)
            list($credentials,$series,$params) = JsonAPI::parse_attach($json);
        else
            list($credentials,$series,$params) = BinaryAPI::parse_attach($content);

        if ($series instanceof Series) {
            $backend = new Backend($credentials);
            if(!$backend->attach($series, ...$params)){
                throw new CreationFailedException("Attachment Failed");
            }
        } else {
            throw new BadRequestException("Error parsing content request: invalid series");
        }
    }

    function batch($content)
    {
        $json = json_decode($content, false, 512, JSON_BIGINT_AS_STRING);
        if (json_last_error() === JSON_ERROR_NONE)
            throw new Exception\NotImplementedException("JSON-based import in batch has not been implemented yet");
        else{
            //list($credentials,$smartdata_array,$params) = BinaryAPI::parse_put($content);
            $binary = $content;
            $credentials = null;
root's avatar
root committed
                $credentials = Credentials::unpack($binary);
                if(!$credentials){
                    throw new BadRequestException("!CERT > Credentials cannot be null");
                }
            }
            $series = Series::unpack($binary);
            $csdf = array();
            $smartdata_array = array();
            while($binary) {
                $smartdata = SmartData::unpack($binary, $csdf);
                if($smartdata instanceof SmartData)
                    array_push($smartdata_array, $smartdata);
            }
            $params = array();

            $backend = new Backend($credentials);
            if(!$backend->insertBatch($series, ...$smartdata_array)) {
                throw new Exception\InsertionFailedException("Insertion Failed");
            }
        }
    }
    
    function describe($content)
    {
        $json = json_decode($content, false, 512, JSON_BIGINT_AS_STRING);
        if (json_last_error() !== JSON_ERROR_NONE)
            throw new Exception\InvalidJSONException("JSON description is incorrect");
        else{
           list($credentials, $description) = JsonAPI::parse_description($json);
           $backend = new Backend($credentials);
           if(!$backend->describe($description)) {
               throw new Exception\InsertionFailedException("Description insertion failed");
           }
        }
    }
    
    function list_devices($content) {
        $json = json_decode($content, false, 512, JSON_BIGINT_AS_STRING);
        if (json_last_error() !== JSON_ERROR_NONE)
            throw new Exception\InvalidJSONException("JSON series for listing is incorrect");
        else{
           list($credentials, $series) = JsonAPI::parse_get($json, false);
           $backend = new Backend($credentials);
           $descs = $backend->list_devices($series);
           return json_encode($descs);
        }
    }        
root's avatar
root committed
}

namespace SmartData\SmartAPI\Internals
{
    use SmartData\{Series,SmartData,Credentials,Logger,Description};
root's avatar
root committed

    trait JsonAPI
    {
        public function parse_description($json)
        {
            $credentials = null;
            $description = null;            
            if (isset($json->credentials))
                $credentials = Credentials::fromJson($json->credentials);
            if (isset($json->series_descriptions)) {
                $description = array();
                foreach ($json->series_descriptions as $d) {
                    $d = Description::fromJson($d);
                    array_push($description, $d);
                }
            }
            if (isset($json->series_description)) {
                $d = Description::fromJson($json->series_description);
                $description = array();
                array_push($description, $d);
            }
            return array($credentials, $description);
        }
    
        public function parse_get($json, $verify = true)
root's avatar
root committed
        {
            $credentials = null;
            if(isset($json->credentials))
                $credentials = Credentials::fromJson($json->credentials);
            $series      = Series::fromJsonNew($json->series, $verify);
root's avatar
root committed
            $aggregator  = $json->aggregator ?? null;
            $options     = $json->options?? null;
            return array($credentials, $series, $aggregator, $options);
        }

        public function parse_put($json)
        {
            $credentials = null;
            $params = $smartdata_array = array();
            if(isset($json->credentials))
                $credentials = Credentials::fromJson($json->credentials);
            $smartdata_set = $json->smartdata;
            foreach($smartdata_set as $smartdata_fields) {
                $smartdata = SmartData::fromJson($smartdata_fields);
                if($smartdata instanceof SmartData)
                    array_push($smartdata_array, $smartdata);
            }
            return array($credentials, $smartdata_array, $params);
        }

        public function parse_create($json)
        {
            $credentials = null;
            $params = array();
            if(isset($json->credentials))
                $credentials = Credentials::fromJson($json->credentials);
            if (isset($json->series->type)) {
                $series = Series::fromJsonNew($json->series);
            } else {    
                $series = Series::fromJson($json->series);
            }
            $aggregator = array($json->aggregator??null);
root's avatar
root committed
            return array($credentials, $series, $params);
        }

        public function parse_attach($json)
        {
            $credentials = null;
            $params = array();
            if(isset($json->credentials))
                $credentials = Credentials::fromJson($json->credentials);
            $series      = Series::fromJson($json->series);
            $aggregator  = array($json->aggregator??null);
            return array($credentials, $series, $params);
        }

        public function parse_search($json)
        {
            $credentials = null;
            if(isset($json->credentials))
                $credentials = Credentials::fromJson($json->credentials);
            $series      = Series::fromJson($json->series);
            return array($credentials, $series, $parameter);
root's avatar
root committed
    }
}

namespace SmartData\SmartAPI\Internals
{
    use SmartData\Exception\{BadRequestException};
    use SmartData\{Series,SmartData,Credentials,Logger};
    use SmartData\Utility\{Unpack};

    trait BinaryAPI
    {
        public function parse_get($binary)
        {
            $credentials = null;
            $series = Series::unpack($binary);
            $params = array();
            $options = array();
            return array($credentials, $series, $params, $options);
        }

        public function parse_put($binary)
        {
            $credentials = null;
            if(!REQUEST_CERT){
                $credentials = Credentials::unpack($binary);
                if(!$credentials){
                    throw new BadRequestException("!CERT > Credentials cannot be null");
                }
            }

            // Common SmartData Fields Bitmap
            // ------7-----------6-----------5-----------4-----------3-----------2-----------1-----------0-------
            // |  version  |    unit   |   error   | confidence |    x     |     y     |     z     |    dev     |
            // --< 1 bit >---< 1 bit >---< 1 bit >---< 1 bit >---< 1 bit >---< 1 bit >---< 1 bit >---< 1 bit >---
            $csdf = array();
            if(isset($_GET['csdf'])){
                $csdf_bitmap = intval($_GET['csdf']);
                if(($csdf_bitmap & (0b1 << 7)) != 0) $csdf = array_merge($csdf, array('version'    => Unpack::uInt8($binary,  true)));
                if(($csdf_bitmap & (0b1 << 6)) != 0) $csdf = array_merge($csdf, array('unit'       => Unpack::uInt32($binary, true)));
                if(($csdf_bitmap & (0b1 << 5)) != 0) $csdf = array_merge($csdf, array('error'      => Unpack::uInt8($binary,  true)));
                if(($csdf_bitmap & (0b1 << 4)) != 0) $csdf = array_merge($csdf, array('confidence' => Unpack::uInt8($binary,  true)));
                if(($csdf_bitmap & (0b1 << 3)) != 0) $csdf = array_merge($csdf, array('x'          => Unpack::int32($binary,  true)));
                if(($csdf_bitmap & (0b1 << 2)) != 0) $csdf = array_merge($csdf, array('y'          => Unpack::int32($binary,  true)));
                if(($csdf_bitmap & (0b1 << 1)) != 0) $csdf = array_merge($csdf, array('z'          => Unpack::int32($binary,  true)));
                if(($csdf_bitmap & (0b1 << 0)) != 0) $csdf = array_merge($csdf, array('dev'        => Unpack::uint32($binary, true)));
            }

            $smartdata_array = array();
            while($binary) {
                $smartdata = SmartData::unpack($binary, $csdf);
                if($smartdata instanceof SmartData)
                    array_push($smartdata_array, $smartdata);
            }
            $params = array();
            return array($credentials, $smartdata_array, $params);
        }

        public function parse_create($binary)
        {
            $credentials = null;
            if(!REQUEST_CERT){
                $credentials = Credentials::unpack($binary);
                if(!$credentials){
                    throw new BadRequestException("!CERT > Credentials cannot be null");
                }
            }
            $series = Series::unpack($binary);
            $params = array();
            return array($credentials, $series, $params);
        }

        public function parse_attach($binary)
        {
            $credentials = null;
            if(!REQUEST_CERT){
                $credentials = Credentials::unpack($binary);
                if(!$credentials){
                    throw new BadRequestException("!CERT > Credentials cannot be null");
                }
            }
            $series = Series::unpack($binary);
            $params = array();
            return array($credentials, $series, $params);
        }
    }
}