code.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. <?php
  2. $hexSerial = 0x6FFFFF;
  3. // 修复前先关闭设备上报
  4. $switch = sqlite("system_set")->where(["key"=>"light_state_switch"])->value("val");
  5. if(!$switch || $switch == "on"){
  6. // 先关闭
  7. $switchCmdStr = jsonCmdStr("LIGHT_STATE_SWITCH", ["state" => "off"]);
  8. $socket = sendSocketMsg($switchCmdStr, 0, 1, "LIGHT_STATE_SWITCH");
  9. $socketStatus = returnCmdDeal($socket, $switchCmdStr, function ($code,$msg) {
  10. writeLog("关闭网关设备信息上报失败,错误码:".$code."-错误消息:".$msg);
  11. });
  12. }
  13. // 修复zone的日程表
  14. $zoneList = sqlite("zones")->field("id,zone_title,holidays")->select();
  15. foreach ($zoneList as $zone){
  16. if (!$zone["holidays"]) continue;
  17. $holidaysList = json_decode($zone["holidays"], true);
  18. foreach ($holidaysList as $k => $v){
  19. // 开始修复
  20. $params = [];
  21. $params[] = replenStr(dechex($v["table_id"]), 4, 1);
  22. $start = $v["num"];
  23. if ($start >= 38) { // 特殊日
  24. $startDate = explode("-", $v["start_time"]);
  25. $params[] = $startDate[2] . $startDate[1];
  26. } else {
  27. $startDate = explode("-", $v["start_time"]);
  28. $endDate = explode("-", $v["end_time"]);
  29. $params[] = $startDate[2] . $startDate[1];
  30. $params[] = $endDate[2] . $endDate[1];
  31. }
  32. $loc = replenStr(dechex($start), 2);
  33. $socket = writeACTCmd(dechex($zone["id"] + 20480), $loc, "01", $params, "76");
  34. $socketStatus = returnCmdDeal($socket["result"], $socket["cmdStr"], function($code,$msg) use($zone) {
  35. writeLog("重写Zone:".$zone["zone_title"]."的日程失败,错误码:".$code."-错误消息:".$msg);
  36. });
  37. }
  38. }
  39. // 修复传感器群组的日程表
  40. $groupList = sqlite("groups")->field("id,group_title,holidays")->where(["type"=>["in",[3,4,5,6,8,9,12,13,14]]])->select();
  41. foreach ($groupList as $group){
  42. if (!$group["holidays"]) continue;
  43. $holidaysList = json_decode($group["holidays"], true);
  44. $gwid = sqlite("group_device")->where(["gid"=>$group["id"]])->value("gwid");
  45. if(!$gwid){
  46. writeLog("传感器群组:".$group["group_title"]."未找到网关id");
  47. continue;
  48. }
  49. $ip = sqlite("gateways")->where(["id"=>$gwid])->value("eth_ip");
  50. if(!$ip){
  51. writeLog("传感器群组:".$group["group_title"]."未找到对应的网关IP");
  52. continue;
  53. }
  54. foreach ($holidaysList as $k => $v){
  55. // 开始修复
  56. $params = [];
  57. $params[] = replenStr(dechex($v["table_id"]), 4, 1);
  58. $start = $v["num"];
  59. if ($start >= 38) { // 特殊日
  60. $startDate = explode("-", $v["start_time"]);
  61. $params[] = $startDate[2] . $startDate[1];
  62. } else {
  63. $startDate = explode("-", $v["start_time"]);
  64. $endDate = explode("-", $v["end_time"]);
  65. $params[] = $startDate[2] . $startDate[1];
  66. $params[] = $endDate[2] . $endDate[1];
  67. }
  68. $loc = replenStr(dechex($start), 2);
  69. $cmdStr = jsonCmdStr("SET_SENSOR_PERIOD", ["addr" => dechex($group["id"]), "byte7" => $loc, "byte8"=>"01", "ip"=>$ip, "param"=>implode("", $params)]);
  70. $res = sendSocketMsg($cmdStr, 0, 1, "SET_SENSOR_PERIOD");
  71. $socketStatus = returnCmdDeal($res, $cmdStr, function ($code,$msg) use ($group){
  72. writeLog("重写传感器群组:".$group["group_title"]."的日程失败,错误码:".$code."-错误消息:".$msg);
  73. });
  74. }
  75. }
  76. if(!$switch || $switch == "on"){
  77. // 原来是打开的重新打开
  78. $switchCmdStr = jsonCmdStr("LIGHT_STATE_SWITCH", ["state" => "on"]);
  79. $socket = sendSocketMsg($switchCmdStr, 0, 1, "LIGHT_STATE_SWITCH");
  80. $socketStatus = returnCmdDeal($socket, $switchCmdStr, function ($code,$msg) {
  81. writeLog("打开网关设备信息上报失败,错误码:".$code."-错误消息:".$msg);
  82. });
  83. }
  84. // 写入动作/配置等
  85. function writeACTCmd($addr="",$byte7,$byte8,$data=[], $byte4="71"){
  86. if(!$addr || !$byte7 || !$byte8) return false;
  87. $addr = swapHilo($addr);
  88. $serial = getSerial();
  89. $byte = [];
  90. $byte[0] = "E0";
  91. $byte[1] = $serial[0];
  92. $byte[2] = $serial[1];
  93. $byte[3] = $serial[2];
  94. $byte[4] = $byte4;
  95. $byte[5] = $addr["lbyte"];
  96. $byte[6] = $addr["hbyte"];
  97. $byte[7] = $byte7;
  98. $byte[8] = $byte8;
  99. if(!empty($data)){
  100. foreach ($data as $k => $v) {
  101. $byte[9+$k] = $v;
  102. }
  103. }
  104. $cmdStr = implode("",$byte);
  105. $crc = crc16($cmdStr);
  106. $cmdStr .= $crc;
  107. $result = sendSocketMsg($cmdStr);
  108. if(!$result){
  109. }
  110. return ["cmdStr"=>$cmdStr,"result"=>$result];
  111. }
  112. function sendSocketMsg($data,$hex=1,$back=1, $cmd=""){
  113. $host = "127.0.0.1";
  114. $port = "2347";
  115. $socket = socket_create(AF_INET,SOCK_STREAM,0);
  116. if ($socket < 0) return false;
  117. $result = @socket_connect($socket,$host,$port);
  118. if (!$result)return false;
  119. // 转成二级制流发送
  120. if(is_array($data)){ // 是数组,批量发送
  121. foreach ($data as $k => $v) {
  122. if($hex) $v = hex2bin(preg_replace("# #", "", $v));
  123. socket_write($socket,$v,strlen($v));
  124. }
  125. }else{
  126. if($hex) $data = hex2bin(preg_replace("# #", "", $data));
  127. $res = socket_write($socket,$data,strlen($data));
  128. }
  129. if($back!=0){
  130. do{
  131. $is_fail = false;
  132. $input = socket_read($socket, 1024);
  133. $is = json_decode($input, true);
  134. if($is && $cmd && $is["cmd"] != $cmd && $is["cmd"] != "GLOBAL"){
  135. $is_fail = true;
  136. }else if(strpos($input, $cmd)){
  137. break;
  138. }
  139. }while($is_fail);
  140. // $input = bin2hex($input); // 将2进制数据转换成16进制
  141. socket_close ($socket);
  142. return $input;
  143. }else{
  144. socket_close ($socket);
  145. return true;
  146. }
  147. }
  148. function crc16($str)
  149. {
  150. $string = pack("H*", $str);
  151. $crc = 0xFFFF;
  152. for ($x = 0; $x < strlen($string); $x++) {
  153. $crc = $crc ^ ord($string[$x]);
  154. for ($y = 0; $y < 8; $y++) {
  155. if (($crc & 0x0001) == 0x0001) {
  156. $crc = (($crc >> 1) ^ 0xA001);
  157. } else {
  158. $crc = $crc >> 1;
  159. }
  160. }
  161. }
  162. return crcSwapHilo($crc);
  163. }
  164. function crcSwapHilo($crc)
  165. {
  166. $hbyte = dechex(floor($crc / 256));
  167. $lbyte = dechex($crc % 256);
  168. if (strlen($hbyte) <= 1) $hbyte = "0" . $hbyte;
  169. if (strlen($lbyte) <= 1) $lbyte = "0" . $lbyte;
  170. return $lbyte . $hbyte;
  171. }
  172. function returnCmdDeal($returnCmd,$cmdStr, $failCall=null, $successCall=null){
  173. if(!$returnCmd){
  174. call_user_func($failCall, "00", "");
  175. return false;
  176. }
  177. /*
  178. * 过滤粘包
  179. * 防止出现:{"code":"00","cmd":"","data":[]}{"code":"01","cmd":"","data":[]}
  180. */
  181. if(!preg_match("/(\{.*\}\})/", $returnCmd, $matches) && !preg_match("/(\{.*\})/", $returnCmd, $matches)) return false;
  182. $data = json_decode($matches[1],true);
  183. if(is_null($data)){
  184. $successCall && call_user_func($successCall, true);
  185. return true;
  186. }
  187. if($data["cmd"] == "75"){
  188. $is = call_user_func($failCall, "00", "操作失败,网关ip:".$data["data"]);
  189. if($is === false) return false;
  190. }
  191. if($data["code"] == "40"){ // 网关不在线
  192. $is = call_user_func($failCall, $data["code"], "网关不在线");
  193. if($is === false) return false;
  194. }elseif($data["code"] == "0a"){ // 设备不在线
  195. $is = call_user_func($failCall, $data["code"], "设备不在线");
  196. if($is === false) return false;
  197. }elseif($data["code"] == "07") {
  198. $is = call_user_func($failCall, $data["code"], "设备超时");
  199. if($is === false) return false;
  200. }elseif($data["code"] == "08") {
  201. $is = call_user_func($failCall, $data["code"], "未设置meshId和密码");
  202. if($is === false) return false;
  203. }elseif($data["code"] == "09") {
  204. $is = call_user_func($failCall, $data["code"], "设备类型错误");
  205. if($is === false) return false;
  206. }elseif($data["code"] == "13") {
  207. $is = call_user_func($failCall, $data["code"], "操作无效,系统正在升级中");
  208. if($is === false) return false;
  209. }elseif($data["code"] == "02") {
  210. $is = call_user_func($failCall, $data["code"], "设备地址错误");
  211. if($is === false) return false;
  212. }elseif($data["code"] == "-1") {
  213. $is = call_user_func($failCall, $data["code"], "失败");
  214. if($is === false) return false;
  215. }elseif($data["code"] == "-2") {
  216. $is = call_user_func($failCall, $data["code"], "自动备份中,请勿操作");
  217. if($is === false) return false;
  218. }elseif($data["code"] == "-3") {
  219. $is = call_user_func($failCall, $data["code"], "预设导入中,请误操作");
  220. if($is === false) return false;
  221. }elseif($data["code"] == "-4") {
  222. $is = call_user_func($failCall, $data["code"], "正在恢复数据中,请误操作");
  223. if($is === false) return false;
  224. }elseif($data["code"] == "-5") {
  225. $is = call_user_func($failCall, $data["code"], "系统很拥挤");
  226. if($is === false) return false;
  227. }elseif($data["code"] == "-6") {
  228. $is = call_user_func($failCall, $data["code"], "mcu正在matching");
  229. if($is === false) return false;
  230. }elseif($data["code"] == "03") {
  231. $is = call_user_func($failCall, $data["code"], "数据地址错误");
  232. if($is === false) return false;
  233. }elseif($data["code"] == "05") {
  234. $is = call_user_func($failCall, $data["code"], "数据超出范围");
  235. if($is === false) return false;
  236. }elseif($data["code"] == "04") {
  237. $is = call_user_func($failCall, $data["code"], "数据长度错误");
  238. if($is === false) return false;
  239. }elseif($data["code"] == "0d") {
  240. $is = call_user_func($failCall, $data["code"], "mcu忙碌着");
  241. if($is === false) return false;
  242. }elseif($data["code"] == "00"){
  243. if(empty($data["data"])) {
  244. $successCall && call_user_func($successCall, true);
  245. return true;
  246. } else {
  247. $successCall &&call_user_func($successCall, $data["data"]);
  248. return $data["data"];
  249. }
  250. }elseif($data["code"] == "0b"){
  251. $is = call_user_func($failCall, $data["code"], "当前网关设备地址254个已用完");
  252. if($is === false) return false;
  253. }elseif($data["code"] == "01"){// 需要重发,重发次数5
  254. $count = 4;
  255. while ($count > 0) {
  256. $socket = sendSocketMsg($cmdStr);
  257. if(strcasecmp($socket,"FAD20001")==0){
  258. $count--;
  259. }elseif(strcasecmp($socket,"FAD20000")==0){
  260. return true;
  261. break;
  262. }else{
  263. $is = call_user_func($failCall, "0", "错误");
  264. if($is === false) return false;
  265. }
  266. }
  267. $is = call_user_func($failCall, "01", "重发次数超出");
  268. if($is === false) return false;
  269. }
  270. return false;
  271. }
  272. // 长度补充
  273. function replenStr($str, $len, $swap = 0, $replenField = "0")
  274. {
  275. $str = str_pad($str, $len, $replenField, STR_PAD_LEFT);
  276. if ($swap == 1) {
  277. // 交换高低位
  278. $lhbyte = swapHilo($str);
  279. $str = $lhbyte["lbyte"] . $lhbyte["hbyte"];
  280. }
  281. return $str;
  282. }
  283. // 交换高低位
  284. function swapHilo($num)
  285. {
  286. // 先转换为10进制数
  287. $num = hexdec($num);
  288. $hbyte = dechex(floor($num / 256));
  289. $lbyte = dechex($num % 256);
  290. if (strlen($hbyte) <= 1) $hbyte = "0" . $hbyte;
  291. if (strlen($lbyte) <= 1) $lbyte = "0" . $lbyte;
  292. return ["hbyte" => $hbyte, "lbyte" => $lbyte];
  293. }
  294. function jsonCmdStr($cmd = "", $data = [])
  295. {
  296. return json_encode(["cmd" => $cmd, "data" => $data]);
  297. }
  298. function getSerial()
  299. {
  300. global $hexSerial;
  301. $lowerLimit = 0x6FFFFF;
  302. $upperLimit = 0xAFFFFF;
  303. $hexSerial++;
  304. // 检查是否超出上限,如果超出则重新从下限开始
  305. if ($hexSerial > $upperLimit) {
  306. $hexSerial = $lowerLimit;
  307. }
  308. // 将整数转换为 24 位无符号整数的二进制表示
  309. $binaryData = pack('N', $hexSerial);
  310. // 取后 3 个字节(24 位)的数据
  311. $binaryData = substr($binaryData, 1);
  312. // 将二进制数据反转(高低位交换)
  313. $reversedData = strrev($binaryData);
  314. // 将反转后的二进制数据转换为十六进制字符串
  315. $hexSerialNumber = bin2hex($reversedData);
  316. return str_split($hexSerialNumber, 2);
  317. }
  318. function writeLog($msg){
  319. $dir = __DIR__ . "/../logs/patch/";
  320. // 日志目录不存在
  321. if(!file_exists($dir)) mkdir($dir, 0777, true);
  322. $filename = $dir ."rewriteTheSchedule.log";
  323. // 日志文件不存在
  324. if(!file_exists($filename)){
  325. file_put_contents($filename, "");
  326. chmod($filename, 0777);
  327. }
  328. // 写入日志
  329. file_put_contents($filename, $msg . "\n", FILE_APPEND);
  330. }