<?php namespace SmartData\SmartAPI { require_once( __DIR__ . '/Config.php'); 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'); require_once( __DIR__ . '/Unit.php'); use SmartData\SmartAPI\Internals\{JsonAPI, BinaryAPI}; use SmartData\Exception\{BadRequestException, RequestFailedException, InsertionFailedException, CreationFailedException}; use SmartData\{Series, SmartData, Backend, Credentials, Logger, MultiSmartData, Unit, Config_Common}; use SmartData\Description; use SmartData\Utility\{Pack,Unpack}; function health_check() { return Backend::health_check(); } function search($content) { $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; } 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); break; case SmartData::MOBILE_VERSION: $return = $backend->track($series,$aggregator,$options); break; default: throw new \Exception("Unsupported SmartData version [{$ver}]"); return null; } if($return != NULL) { $pos = 0; $isBigQuery = false; $bigUnits = [0x02240003, 0x02230000, 0x03020000]; //Image, WAV, LIDAR foreach ( $return['series'] as $key => $sd ) { //Check if the series are a "big unit" $isBigQuery = in_array( $sd['unit'] & 0x0FFF0000 , $bigUnits ); $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; } } $pos = $pos + 1; } // 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. 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) ; } else { $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; } 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"); } } } 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..."); } } else { // Is Binary $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"); } } } } function finish($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) { $backend = new Backend($credentials); if(!$backend->finish($series, ...$params)) { throw new CreationFailedException("Finish Failed"); } } else { throw new BadRequestException("Error parsing content request: invalid series"); } } 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) { $backend = new Backend($credentials); if(!$backend->create($series, ...$params)) { throw new CreationFailedException("Creation Failed"); } } 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; if(!REQUEST_CERT) { $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); } } } namespace SmartData\SmartAPI\Internals { use SmartData\{Series,SmartData,Credentials,Logger,Description}; 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) { $credentials = null; if(isset($json->credentials)) $credentials = Credentials::fromJson($json->credentials); $series = Series::fromJsonNew($json->series, $verify); $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); 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); $parameter = $json->parameter ?? null; return array($credentials, $series, $parameter); } } } 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); } } }