Web Interfaces of PROSUME

EprosumeModel.php 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  1. <?php
  2. namespace App\Utility;
  3. use Cake\Datasource\ConnectionManager;
  4. use \App\Utility\WalletEprosumeModel;
  5. class EprosumeModel
  6. {
  7. protected $connection = null;
  8. protected $conf = null;
  9. public function __construct()
  10. {
  11. $this->connection = ConnectionManager::get('default');
  12. $conf_rows = $this->connection->execute('select conf_key, conf_value from conf')->fetchAll(\PDO::FETCH_ASSOC);
  13. foreach($conf_rows as $v) {
  14. $this->conf[$v['conf_key']] = $v['conf_value'];
  15. }
  16. }
  17. public function blocksList($filters = [], $where = 'true', $values = [], $ascending=true)
  18. {
  19. $acceptedRequest = [
  20. 'id' => ' AND id = :id ',
  21. 'blocknumber' => ' AND blocknumber = :blocknumber ',
  22. 'to' => ' AND wallet_to = :to ',
  23. 'from' => ' AND wallet_from = :from ',
  24. 'wallet' => ' AND ( wallet_to = :wallet or wallet_from = :wallet ) ',
  25. 'meter' => ' AND wallet_from = :meter ',
  26. 'date_from' => ' AND timestamp >= :date_from ',
  27. 'date_to' => ' AND timestamp <= :date_to ',
  28. 'asset' => ' AND asset = :asset ',
  29. ];
  30. $order = ' ORDER BY timestamp '.($ascending ? "ASC" : "DESC");
  31. foreach ($acceptedRequest as $request => $sqlrequest) {
  32. if (!empty($filters[$request])) {
  33. $where .= $sqlrequest;
  34. $values[$request] = $filters[$request];
  35. }
  36. }
  37. $selectItem = ' *, DATE_FORMAT(timestamp, "%Y-%m-%d %H:%i") AS timestamp ';
  38. $groupby = '';
  39. $getMemo = true;
  40. if (isset($filters['daily'])) {
  41. $selectItem = ' wallet_from,wallet_to,asset,sum(amount) AS amount,DATE(timestamp) AS timestamp ';
  42. $groupby = ' GROUP BY DATE(timestamp),asset,wallet_from,wallet_to ';
  43. $getMemo = false;
  44. } else if (isset($filters['monthly'])) {
  45. $selectItem = ' wallet_from,wallet_to,asset,sum(amount) AS amount,DATE(timestamp) AS timestamp ';
  46. $groupby = ' GROUP BY MONTH(timestamp),asset,wallet_from,wallet_to ';
  47. $getMemo = false;
  48. }
  49. $blocks = $this->connection->execute("SELECT $selectItem FROM blocks WHERE $where $groupby $order", $values)->fetchAll(\PDO::FETCH_ASSOC);
  50. foreach ($blocks as $block) {
  51. $block['memo'] = $getMemo ? $this->memo($block['id']) : [];
  52. yield $block;
  53. }
  54. }
  55. public function memo($block_id)
  56. {
  57. $memo = [];
  58. if ($block_id > 0) {
  59. foreach ($this->connection->execute("SELECT * FROM memo WHERE block_id=:block_id", compact('block_id')) as $info) {
  60. $memo[$info['memo_key']] = $info['memo_value'];
  61. }
  62. }
  63. return $memo;
  64. }
  65. public function meterList($meter_id = null)
  66. {
  67. $meterList = [];
  68. $and = $meter_id ? " WHERE h.id = ".((int)$meter_id) : "";
  69. $sql = <<<SQL
  70. SELECT h.id, h.prs AS PRS, h.tft AS TFT, h.timestamp, h.name, h.buy, h.sell, h.location FROM meter h
  71. SQL;
  72. $sql.= $and;
  73. $data = ($this->connection->execute($sql))->fetchAll(\PDO::FETCH_ASSOC);
  74. foreach ($data as $v) {
  75. $meterList[$v['name']] = [
  76. 'meter' => $v['name'],
  77. 'name' => $v['name'],
  78. 'id' => $v['id'],
  79. 'location' => $v['location'],
  80. 'balance' => [
  81. 'PRS' => $v['PRS'],
  82. 'TFT' => $v['TFT'],
  83. 'timestamp' => $v['timestamp']
  84. ]
  85. ];
  86. }
  87. if(!empty($and)) {
  88. $result = null;
  89. foreach($meterList as $k=>$v) {
  90. $result = $meterList[$k];
  91. break;
  92. }
  93. $meterList = $result;
  94. }
  95. return $meterList;
  96. }
  97. public function balanceMeter($meter_id)
  98. {
  99. $sql = <<<SQL
  100. select h.name, h.id, h.location, (select b.timestamp from balances b where b.meter_id = h.id order by b.timestamp desc limit 1) as timestamp, (select b.prs from balances b where b.meter_id = h.id order by b.timestamp desc limit 1) as PRS, (select b.tft from balances b where b.meter_id = h.id order by b.timestamp desc limit 1) as TFT from meter h where h.name = :meter_name;
  101. SQL;
  102. return ($this->connection->execute($sql, [ "meter_name" => $meter_id ] ))->fetch(\PDO::FETCH_ASSOC);
  103. }
  104. public function balanceHistory($date_from, $date_to, $meter)
  105. {
  106. $sql = <<<SQL
  107. SELECT h.id, h.subid, h.name, h.location, b.TFT, b.PRS, DATE_FORMAT(timestamp, "%%Y-%%m-%%d %%H:%%i") AS timestamp FROM meter h
  108. LEFT JOIN balances b ON b.meter_id = h.id
  109. WHERE 1
  110. %s
  111. AND h.status = 'ACTIVE'
  112. AND b.timestamp BETWEEN :date_from AND :date_to
  113. ORDER BY timestamp ASC
  114. SQL;
  115. $values = [
  116. "date_from" => $date_from,
  117. "date_to" => $date_to,
  118. ];
  119. $meter && $values['meter'] = $meter;
  120. return ($this->connection->execute(
  121. sprintf($sql, ($meter ? "AND h.name = :meter" : ""))
  122. ,
  123. $values
  124. ))->fetchAll(\PDO::FETCH_ASSOC);
  125. }
  126. public function blockID($block_id)
  127. {
  128. $this->blocksList(['id' => $block_id]);
  129. }
  130. public function actualPrice($meter_id = null)
  131. {
  132. $prices = [];
  133. $sql = <<<SQL
  134. SELECT * FROM meter
  135. SQL;
  136. if($meter_id) {
  137. $sql.= " WHERE id = ".$meter_id;
  138. }
  139. $meters = $this->connection->execute($sql)->fetchAll(\PDO::FETCH_ASSOC);
  140. foreach($meters as $meter) {
  141. $prices[$meter['name']] = [
  142. 'buyPrice' => $meter['buy'],
  143. 'sellPrice' => $meter['sell'],
  144. ];
  145. }
  146. return $prices;
  147. }
  148. public function paymentsList($meter_id = null)
  149. {
  150. $where = 'true';
  151. $values = [];
  152. if ($meter_id) {
  153. $where .= ' AND ( wallet_from=:meter_id or wallet_to=:meter_id )';
  154. $values['meter_id'] = $meter_id;
  155. }
  156. $order = ' ORDER BY timestamp DESC ';
  157. $blocks = $this->connection->execute("SELECT b.* FROM blocks b LEFT JOIN memo on memo.block_id = b.id WHERE $where $order", $values);
  158. foreach ($blocks as $block) {
  159. yield $block;
  160. }
  161. }
  162. public function dataTableSSP($dataset, $user_meter_id, $draw, $columns, array $order, $start, $length, array $search = null, $meter = null, $txid=null, $from=null, $to=null, $period=null) {
  163. $filterString = "";
  164. $groupString = "";
  165. $params = [];
  166. $response = [
  167. "data" => [],
  168. "draw" => $draw,
  169. "recordsFiltered"=> 0,
  170. "recordsTotal"=> 0
  171. ];
  172. switch($dataset) {
  173. case 'history':
  174. case 'historyTransfer':
  175. if($period) {
  176. if($period == 1) {
  177. $timeString = "b.day";
  178. $groupTimeString = "b.day";
  179. } else {
  180. $timeString = "CONCAT(b.yea, '-', b.mon)";
  181. $groupTimeString = "b.yea, b.mon";
  182. }
  183. $selectString = <<<SQL
  184. SELECT $timeString AS timestamp,
  185. null AS txID,
  186. b.wallet_from, b.wallet_to, b.asset, b.type,
  187. SUM(b.amount+0) AS amount,
  188. SUM(b.purchased_sold+0) AS energyDiff,
  189. NULL as mv
  190. FROM blocks b
  191. SQL;
  192. $groupString = <<<SQL
  193. GROUP BY $groupTimeString, b.wallet_from, b.wallet_to, b.asset, b.type
  194. SQL;
  195. } else {
  196. $derivedSelect = $dataset == "historyTransfer" ? "m3.memo_value AS mv" : "NULL as mv";
  197. $selectString = <<<SQL
  198. SELECT b.id, b.timestamp, b.wallet_from, b.wallet_to, b.asset, b.type,
  199. b.tx_id AS txID,
  200. b.amount,
  201. b.purchased_sold AS energyDiff,
  202. $derivedSelect
  203. FROM blocks b
  204. SQL;
  205. }
  206. $joinString = <<<SQL
  207. SQL;
  208. $countString = <<<SQL
  209. SELECT COUNT(*)
  210. FROM blocks b
  211. SQL;
  212. $clauseString = <<<SQL
  213. WHERE (b.type = 'measure' OR b.type = 'payment')
  214. SQL;
  215. $utility_account = $this->conf['utility_account'] ?? null;
  216. if($dataset == "historyTransfer") {
  217. $clauseString = ' WHERE b.type = "transfer"';
  218. $joinString = " LEFT JOIN memo m3 ON m3.block_id = b.id AND m3.memo_key = 'Memo'";
  219. }
  220. if($txid) {
  221. $filterString.= <<<SQL
  222. AND b.tx_id = :txid
  223. SQL;
  224. $params["txid"] = $txid;
  225. }
  226. break;
  227. case 'payment':
  228. if($period) {
  229. if($period == 1) {
  230. $timeString = "b.day";
  231. $groupTimeString = "b.day";
  232. } else {
  233. $timeString = "CONCAT(b.yea, '-', b.mon)";
  234. $groupTimeString = "b.yea, b.mon";
  235. }
  236. $selectString = <<<SQL
  237. SELECT $timeString AS timestamp, b.wallet_from, b.wallet_to,
  238. SUM(b.amount+0) AS amount, b.asset
  239. FROM blocks b
  240. SQL;
  241. $groupString = <<<SQL
  242. GROUP BY $groupTimeString, b.wallet_from, b.wallet_to, b.asset
  243. SQL;
  244. } else {
  245. $selectString = <<<SQL
  246. SELECT b.id, b.timestamp, b.wallet_from, b.wallet_to, b.amount, b.asset FROM blocks b
  247. SQL;
  248. }
  249. $joinString = <<<SQL
  250. SQL;
  251. $countString = <<<SQL
  252. SELECT COUNT(b.id) FROM blocks b
  253. SQL;
  254. $clauseString = <<<SQL
  255. WHERE b.asset = "PRS" and b.type = "payment"
  256. SQL;
  257. break;
  258. case 'priceHistory':
  259. $selectString = <<<SQL
  260. SELECT b.id AS id, b.timestamp, b.wallet_from, b.wallet_to, b.buy_price AS buyPrice, b.sell_price AS sellPrice FROM blocks b
  261. SQL;
  262. $joinString = <<<SQL
  263. SQL;
  264. $clauseString = <<<SQL
  265. WHERE 1
  266. AND b.asset = "TFT"
  267. AND b.type = "update_price"
  268. SQL;
  269. $countString = <<<SQL
  270. SELECT COUNT(t1.id) AS id FROM ($selectString $joinString $clauseString) AS t1 WHERE 1
  271. SQL;
  272. $countStringAlone = true;
  273. break;
  274. default:
  275. return $response;
  276. }
  277. if($user_meter_id) {
  278. $user_meter_id_clause = <<<SQL
  279. AND (b.wallet_from = '$user_meter_id' OR b.wallet_to = '$user_meter_id')
  280. SQL;
  281. $clauseString.= $user_meter_id_clause;
  282. }
  283. if($meter) {
  284. $filterString.= <<<SQL
  285. AND (wallet_from = :meter OR wallet_to = :meter)
  286. SQL;
  287. $params["meter"] = $meter;
  288. }
  289. if(!$txid) {
  290. if($from && $to) {
  291. $params["from"] = $from;
  292. $params["to"] = $to;
  293. $filterString.= <<<SQL
  294. AND timestamp BETWEEN :from AND :to
  295. SQL;
  296. } else {
  297. /*
  298. $now = new \DateTime();
  299. $params["to"] = $now->format('Y-m-d H:i:s');
  300. $now->sub(new \DateInterval('P30D'));
  301. $params["from"] = $now->format('Y-m-d H:i:s');
  302. */
  303. }
  304. }
  305. $orderString = empty($order)
  306. ? ""
  307. : "ORDER BY ".
  308. (
  309. function($order, $columns) {
  310. return implode(
  311. ", ",
  312. array_filter(
  313. array_map(
  314. function($v) use($columns) {
  315. return isset($columns[$v["column"]]["data"])
  316. ? sprintf("`%s` %s", $columns[$v["column"]]["data"], $v["dir"])
  317. : null
  318. ;
  319. },
  320. $order
  321. )
  322. )
  323. );
  324. }
  325. )
  326. ($order, $columns)
  327. ;
  328. $limitString = "";
  329. $limitString.= is_null($length) ? "" : sprintf("LIMIT %d", $length);
  330. $limitString.= is_null($start) ? "" : sprintf(" OFFSET %d", $start);
  331. $recordsQuery = implode(PHP_EOL, [
  332. $selectString,
  333. $joinString,
  334. $clauseString,
  335. ($filterString ?? null),
  336. $groupString,
  337. $orderString,
  338. $limitString
  339. ]);
  340. $records = $this->connection->execute($recordsQuery, $params);
  341. $response["data"] = (function($records) {
  342. foreach($records as $key => $record) {
  343. array_walk(
  344. $record,
  345. function(&$v,$k) use(&$record) {
  346. if(!is_string($k)) {
  347. unset($record[$k]);
  348. }
  349. }
  350. );
  351. yield $record;
  352. }
  353. })($records);
  354. if(isset($countStringAlone)) {
  355. $queryComposition = [
  356. $countString
  357. ];
  358. } else {
  359. $queryComposition = [
  360. $countString,
  361. $joinString,
  362. $clauseString
  363. ];
  364. }
  365. /*
  366. if(empty($groupString)) {
  367. $recordsTotalQuery = implode(PHP_EOL, $queryComposition);
  368. $recordsTotalResults = $this->connection->execute($recordsTotalQuery)->fetch();
  369. isset($recordsTotalResults[0]) && $response["recordsTotal"] = $recordsTotalResults[0];
  370. } else {
  371. $queryComposition[] = $groupString;
  372. $recordsTotalQuery = implode(PHP_EOL, $queryComposition);
  373. $recordsTotalResults = $this->connection->execute($recordsTotalQuery)->fetchAll();
  374. $response["recordsTotal"] = count($recordsTotalResults);
  375. array_pop($queryComposition);
  376. }
  377. */
  378. if($filterString) {
  379. $queryComposition[] = $filterString;
  380. }
  381. if(empty($groupString)) {
  382. $recordsFilteredQuery = implode(PHP_EOL, $queryComposition);
  383. $recordsFilteredResults = $this->connection->execute($recordsFilteredQuery, $params)->fetch();
  384. isset($recordsFilteredResults[0]) && $response["recordsFiltered"] = $recordsFilteredResults[0];
  385. } else {
  386. $queryComposition[] = $groupString;
  387. $recordsFilteredQuery = implode(PHP_EOL, $queryComposition);
  388. $recordsFilteredResults = $this->connection->execute($recordsFilteredQuery, $params)->fetchAll();
  389. $response["recordsFiltered"] = count($recordsFilteredResults);
  390. }
  391. $response["recordsTotal"] = $response["recordsFiltered"];
  392. return $response;
  393. }
  394. public function getAssetsType() {
  395. $assetsType = [
  396. "PRS",
  397. "TFT",
  398. ];
  399. return $assetsType;
  400. }
  401. public function getUtilityAccount() {
  402. return $this->conf['utility_account'];
  403. }
  404. public function charts($type, $meter_name, $from, $to, $period) {
  405. $sql = null;
  406. switch($type) {
  407. case 'payments':
  408. break;
  409. }
  410. if($period > 0) {
  411. $clause = '';
  412. $amount = '(SUM(amount))';
  413. $groupby = ' GROUP BY mon';
  414. if($period == 1) {
  415. $groupby.= ', day';
  416. }
  417. } else if($from && $to) {
  418. $amount = '(amount)';
  419. $clause = " AND timestamp BETWEEN '$from' AND '$to'";
  420. $groupby = '';
  421. } else {
  422. $clause = null;
  423. $groupby = null;
  424. }
  425. if($clause == null && $groupby == null) {
  426. return [];
  427. }
  428. $sql = <<<SQL
  429. SELECT timestamp, CONCAT('[',UNIX_TIMESTAMP(timestamp), ',', $amount, ']') AS data FROM blocks WHERE (wallet_from = :meter_name AND wallet_to = :utility_account) AND type = 'payment' $clause $groupby
  430. UNION
  431. SELECT timestamp, CONCAT('[',UNIX_TIMESTAMP(timestamp), ',', ($amount*-1), ']') AS data FROM blocks WHERE (wallet_from = :utility_account AND wallet_to = :meter_name) AND type = 'payment' $clause $groupby
  432. ORDER BY timestamp ASC
  433. SQL;
  434. $values = [
  435. 'meter_name' => $meter_name,
  436. 'utility_account' => $this->conf['utility_account']
  437. ];
  438. if($sql) {
  439. $data = [];
  440. $rows = ($this->connection->execute($sql,$values))->fetchAll(\PDO::FETCH_ASSOC);
  441. foreach($rows as $row) {
  442. $data[] = json_decode($row['data']);
  443. }
  444. return $data;
  445. } else {
  446. return [];
  447. }
  448. }
  449. public function setPrice($meter_name, $buyPrice, $sellPrice) {
  450. $sql = <<<SQL
  451. UPDATE meter SET buy = :buyPrice, sell = :sellPrice WHERE name = :meter_name;
  452. SQL;
  453. $values = [
  454. 'meter_name' => $meter_name,
  455. 'buyPrice' => $buyPrice,
  456. 'sellPrice' => $sellPrice
  457. ];
  458. return $this->connection->execute($sql,$values);
  459. }
  460. }