ForeignHaiRoboticsService.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. <?php
  2. namespace App\Services;
  3. use App\Exceptions\ErrorException;
  4. use App\Exceptions\Exception;
  5. use App\MaterialBox;
  6. use App\Station;
  7. use App\StationTask;
  8. use App\StationTaskMaterialBox;
  9. use Carbon\Carbon;
  10. use Illuminate\Support\Collection;
  11. use Illuminate\Support\Facades\DB;
  12. use Illuminate\Support\Facades\Http;
  13. use App\Traits\ServiceAppAop;
  14. class ForeignHaiRoboticsService
  15. {
  16. use ServiceAppAop;
  17. // protected $modelClass=ForeignHaiRobotics::class;
  18. /** @var $stationTaskMaterialBoxService StationTaskMaterialBoxService */
  19. private $stationTaskMaterialBoxService;
  20. /** @var $stationTaskBatchService StationTaskBatchService */
  21. private $stationTaskBatchService;
  22. /** @var $stationTaskCommoditiesService StationTaskCommodityService */
  23. private $stationTaskCommoditiesService;
  24. /** @var $materialBoxService MaterialBoxService */
  25. private $materialBoxService;
  26. /** @var $stationTaskService StationTaskService */
  27. private $stationTaskService;
  28. /** @var $stationService StationService */
  29. private $stationService;
  30. public function __construct()
  31. {
  32. $this->stationTaskMaterialBoxService=null;
  33. $this->materialBoxService=null;
  34. $this->stationService=null;
  35. }
  36. /**
  37. * @param string $modeName '输送线入立架'|'立架出至输送线'|'移动立架内位置'|'缓存架入立架'|'立架出至缓存架'
  38. * @param string $fromLocation
  39. * @param string $toLocation
  40. * @param Collection $taskMaterialBoxes
  41. * @param string $groupId
  42. * @param int $priority
  43. * @param int $isSequenced
  44. * @return array
  45. */
  46. private function makeJson_move(
  47. Collection $taskMaterialBoxes,
  48. string $modeName,
  49. string $fromLocation='',
  50. string $toLocation='',
  51. $groupId=''
  52. , $priority=10
  53. , $isSequenced=1
  54. ): array
  55. {
  56. $timestampSuffix = microtime(true);
  57. $taskMode=$this->getTaskMode($modeName);
  58. $bins=$taskMaterialBoxes->map(function (StationTaskMaterialBox $taskMaterialBox)use($timestampSuffix,$fromLocation,$toLocation){
  59. return [
  60. "taskCode" =>$taskMaterialBox['id'].'_'.$timestampSuffix,
  61. "binCode" => $taskMaterialBox['materialBox']['code'],
  62. "fromLocCode" => $fromLocation??'',
  63. "toLocCode" => $toLocation??'',
  64. ];
  65. });
  66. return [[
  67. "taskMode" =>$taskMode,
  68. "bins"=>$bins,
  69. "groupCode"=>$groupId.'_'.$timestampSuffix,
  70. "priority"=>$priority,
  71. "sequenceFlag"=>$isSequenced,
  72. ]];
  73. }
  74. private function makeJson_move_multi(
  75. Collection $taskMaterialBoxes,
  76. string $modeName,
  77. string $fromLocation='',
  78. Collection $toLocations=null,
  79. $groupId=''
  80. , $priority=10
  81. , $isSequenced=1
  82. ): array
  83. {
  84. if(!$toLocations||($toLocations->count()!=$taskMaterialBoxes->count()))throw new Exception('toLocation中的元素数量必须和料箱任务一致');
  85. $timestampSuffix = microtime(true);
  86. $taskMode=$this->getTaskMode($modeName);
  87. $bins=$taskMaterialBoxes->map(function (StationTaskMaterialBox $taskMaterialBox,$i)use($timestampSuffix,$fromLocation,$toLocations){
  88. return [
  89. "taskCode" =>$taskMaterialBox['id'].'_'.$timestampSuffix,
  90. "binCode" => $taskMaterialBox['materialBox']['code'],
  91. "fromLocCode" => $fromLocation??'',
  92. "toLocCode" => $toLocations[$i],
  93. ];
  94. });
  95. return [[
  96. "taskMode" =>$taskMode,
  97. "bins"=>$bins,
  98. "groupCode"=>$groupId.'_'.$timestampSuffix,
  99. "priority"=>$priority,
  100. "sequenceFlag"=>$isSequenced,
  101. ]];
  102. }
  103. private function getTaskMode($modeName){
  104. switch ($modeName){
  105. case '输送线入立架': return 1;
  106. case '立架出至输送线': return 2;
  107. case '移动立架内位置': return 3;
  108. case '立架出至缓存架':
  109. case '缓存架入立架':return 6;
  110. default: throw new \Exception('发至海柔的移料箱请求,模式不存在');
  111. }
  112. }
  113. public function fetchGroup($toLocation, Collection $taskMaterialBoxes, $groupIdPrefix='',$mode='立架出至输送线'): bool
  114. {
  115. $dataToPost=$this->makeJson_move(
  116. $taskMaterialBoxes,
  117. $mode,
  118. '',
  119. $toLocation??'',
  120. $groupIdPrefix
  121. );
  122. return $this->controlHaiRobot($dataToPost,$taskMaterialBoxes,$mode);
  123. }
  124. /**
  125. * @param Collection $toLocations //库位和料箱任务一一对应,一个库位对应一个料箱
  126. * @param Collection $taskMaterialBoxes
  127. * @param string $groupIdPrefix
  128. * @param string $mode
  129. * @return bool
  130. * @throws ErrorException
  131. */
  132. public function fetchGroup_multiLocation(Collection $toLocations, Collection $taskMaterialBoxes, $groupIdPrefix='',$mode='立架出至输送线'): bool
  133. {
  134. $dataToPost=$this->makeJson_move_multi(
  135. $taskMaterialBoxes,
  136. $mode,
  137. '',
  138. $toLocations,
  139. $groupIdPrefix
  140. );
  141. return $this->controlHaiRobot($dataToPost,$taskMaterialBoxes,$mode);
  142. }
  143. public function moveBin(){
  144. }
  145. public function markBinProcessed(
  146. $workStation,
  147. $binCode,
  148. $success,
  149. $created_at,
  150. $exception,
  151. $is_in_plan
  152. ): bool
  153. {
  154. LogService::log('海柔请求','markBinProcessed1.1',
  155. $binCode.'|'.$success.'|'.$exception.'|'.$is_in_plan);
  156. $this->instant($this->stationService,'StationService');
  157. $this->instant($this->materialBoxService,'MaterialBoxService');
  158. $this->instant($this->stationTaskMaterialBoxService,'StationTaskMaterialBoxService');
  159. $this->instant($this->stationTaskCommoditiesService,'StationTaskCommodityService');
  160. $this->instant($this->stationTaskBatchService,'StationTaskBatchService');
  161. try{
  162. LogService::log('海柔请求','markBinProcessed1.2',
  163. json_encode([$binCode,$success,$exception,$is_in_plan]));
  164. if($failed
  165. =!$success)
  166. throw new ErrorException('海柔任务失败:'.$exception);
  167. LogService::log('海柔请求','markBinProcessed1.3',
  168. $failed);
  169. // if($NotInPlan
  170. // =!$is_in_plan)
  171. // throw new ErrorException('海柔认为是计划外的料箱:'.$exception);
  172. LogService::log('海柔请求','markBinProcessed1.4',
  173. '$NotInPlan');
  174. $materialBox=
  175. $this->materialBoxService->get(['code'=>$binCode])->first();
  176. LogService::log('海柔请求','markBinProcessed1.5',
  177. json_encode($materialBox));
  178. /** @var StationTaskMaterialBox $stationTaskMaterialBox */
  179. $stationTaskMaterialBox
  180. =(function()use($materialBox){
  181. return $stationTaskMaterialBox=
  182. StationTaskMaterialBox::query()
  183. ->where('material_box_id',$materialBox['id'])
  184. ->where('created_at','>',Carbon::now()->subDay())
  185. ->whereIn('status',['处理中','待处理','异常','处理队列'])
  186. ->orderBy('id','desc')
  187. ->first();
  188. })();
  189. LogService::log('海柔请求','markBinProcessed1.6',
  190. json_encode($stationTaskMaterialBox).'|'.$binCode);
  191. if(!$stationTaskMaterialBox){
  192. throw new ErrorException($binCode.'该料箱没有安排在处理队列中.');
  193. }
  194. LogService::log('海柔请求','markBinProcessed1.7',
  195. json_encode($stationTaskMaterialBox).'|'.$binCode);
  196. DB::transaction(function ()use($stationTaskMaterialBox,$binCode){
  197. $stationTaskMaterialBox_next=
  198. $this->stationTaskMaterialBoxService
  199. ->processNextQueued($stationTaskMaterialBox); //找到队列中下一个料箱,并标记为处理中
  200. $this->stationTaskCommoditiesService
  201. ->markProcessed($stationTaskMaterialBox['stationTaskCommodities']);
  202. LogService::log('海柔请求','markBinProcessed1.8',
  203. json_encode($stationTaskMaterialBox).'|'.$binCode);
  204. if($stationTaskMaterialBox_next)
  205. $this->stationTaskCommoditiesService
  206. ->markProcessing($stationTaskMaterialBox_next['stationTaskCommodities']);//因为上边商品任务被标记完成了,所以这里要将队列中找出正在处理的料箱对应的标记为“处理中”
  207. $this->stationTaskMaterialBoxService
  208. ->markProcessed($stationTaskMaterialBox);
  209. $notProcessedBoxTasks = $this->stationTaskMaterialBoxService->getNotProcessedSiblings($stationTaskMaterialBox);
  210. if($notProcessedBoxTasks->isEmpty()){
  211. $this->instant($this->stationTaskService,'StationTaskService');
  212. LogService::log('海柔请求','markBinProcessed1.81',
  213. json_encode($stationTaskMaterialBox['stationTaskBatch']).'|'.$binCode);
  214. $stationTaskMaterialBox->loadMissing('stationTaskBatch');
  215. $this->stationTaskBatchService->markProcessed($stationTaskMaterialBox['stationTaskBatch']);
  216. LogService::log('海柔请求','markBinProcessed1.82',
  217. json_encode($stationTaskMaterialBox['stationTaskBatch']).'|'.$binCode);
  218. $this->stationTaskService->markProcessed($stationTaskMaterialBox['stationTask']);
  219. }
  220. $this->storeBox($stationTaskMaterialBox)
  221. ?true
  222. :(function(){throw new ErrorException('呼叫机器人回收U型线料箱失败');})();
  223. LogService::log('海柔请求','markBinProcessed1.9',
  224. json_encode($stationTaskMaterialBox).'|'.$binCode);
  225. $this->stationService->broadcastBinMonitor($stationTaskMaterialBox['station_id'],$stationTaskMaterialBox['stationTask']);
  226. LogService::log('海柔请求','markBinProcessed1.99',
  227. json_encode($stationTaskMaterialBox).'|'.$binCode);
  228. });
  229. return true;
  230. }catch (\Exception $e){
  231. LogService::log('海柔请求','markBinProcessed E1',
  232. $binCode);
  233. $this->instant($this->stationTaskMaterialBoxService,'StationTaskMaterialBoxService');
  234. $this->instant($this->materialBoxService,'MaterialBoxService');
  235. $box=$this->materialBoxService->firstOrCreate(['code'=>$binCode]);
  236. $stationTaskMaterialBox_toStore=
  237. $this->stationTaskMaterialBoxService->create([
  238. 'station_id' => $this->stationService->getStation_byType('立库')['id'],
  239. 'material_box_id' => $box['id'],
  240. 'status' => '处理中'
  241. ] );
  242. $dataToPost=$this->makeJson_move(
  243. collect([$stationTaskMaterialBox_toStore]),
  244. '输送线入立架',
  245. 'BIN-IN1',//TODO:这里应该是动态取得,参考出立架getULineExit()方法,不然不能从站获得对应的出口,而且要改Station的child为children
  246. '',
  247. $stationTaskMaterialBox_toStore['stationTaskBatch']['id']
  248. );
  249. $this->controlHaiRobot($dataToPost,collect([$stationTaskMaterialBox_toStore]),'输送线入立架');
  250. LogService::log('海柔请求','markBinProcessed E2',
  251. $binCode);
  252. $stationTaskMaterialBox = $stationTaskMaterialBox_toStore??$materialBox??null;
  253. if($stationTaskMaterialBox && get_class($stationTaskMaterialBox)==MaterialBox::class){
  254. $stationTaskMaterialBox = StationTaskMaterialBox::query()
  255. ->where('material_box_id',$stationTaskMaterialBox['id'])
  256. ->where('status','<>','完成')
  257. ->where('created_at','>',now()->format('Y-m-d'))
  258. ->first();
  259. }
  260. if($stationTaskMaterialBox)
  261. $this->stationTaskMaterialBoxService
  262. ->excepted($stationTaskMaterialBox);
  263. return $e->getMessage();
  264. }
  265. }
  266. public function storeBox(?StationTaskMaterialBox $stationTaskMaterialBox): bool
  267. {
  268. LogService::log('海柔请求','putBinToStore1',
  269. '');
  270. $this->instant($this->stationService,'StationService');
  271. $this->instant($this->stationTaskMaterialBoxService,'StationTaskMaterialBoxService');
  272. $stationTaskMaterialBox_toStore=
  273. $this->stationTaskMaterialBoxService->firstOrCreate([
  274. 'station_id' => $this->stationService->getStation_byType('立库')['id'],
  275. 'material_box_id' => $stationTaskMaterialBox['materialBox']['id'],
  276. 'status' => '处理中',
  277. 'type' => '放',
  278. ]);
  279. LogService::log('海柔请求','putBinToStore2',
  280. json_encode($stationTaskMaterialBox));
  281. $dataToPost=$this->makeJson_move(
  282. collect([$stationTaskMaterialBox_toStore]),
  283. '输送线入立架',
  284. 'BIN-IN1',//TODO:这里应该是动态取得,参考出立架getULineExit()方法,不然不能从站获得对应的出口,而且要改Station的child为children
  285. '',
  286. $stationTaskMaterialBox['stationTaskBatch']['id']
  287. );
  288. LogService::log('海柔请求','putBinToStore3',
  289. json_encode($dataToPost));
  290. $controlSuccess = $this->controlHaiRobot($dataToPost,collect([$stationTaskMaterialBox_toStore]),'输送线入立架');
  291. return $controlSuccess;
  292. }
  293. /** 缓存架入立架 料箱 任务
  294. * @param StationTaskMaterialBox|null $stationTaskMaterialBox
  295. * @param string|null $formLocation
  296. * @return bool
  297. * @throws ErrorException
  298. */
  299. public function putBinToStore_fromCacheShelf(?StationTaskMaterialBox $stationTaskMaterialBox,
  300. string $formLocation): bool
  301. {
  302. $dataToPost=$this->makeJson_move(collect([$stationTaskMaterialBox]), '缓存架入立架', $formLocation, '');
  303. $controlSuccess = $this->controlHaiRobot($dataToPost,collect([$stationTaskMaterialBox]),'缓存架入立架');
  304. if($controlSuccess){
  305. $this->instant($this->stationTaskMaterialBoxService,'StationTaskMaterialBoxService');
  306. $this->stationTaskMaterialBoxService->set($stationTaskMaterialBox,['status' => '处理中']);
  307. }
  308. return $controlSuccess;
  309. }
  310. public function taskUpdate(
  311. // $groupCode,
  312. $stationTaskMaterialBox_id, //实际对应传入的字段 taskCode, 这里用料箱任务号做taskCode
  313. $updateEventType, //0:task_begin(取货)1:task_end(放货)
  314. $status, //0:任务成功1:任务失败
  315. $binCode
  316. ):bool{
  317. LogService::log('海柔请求','taskUpdateIn',
  318. json_encode([
  319. $stationTaskMaterialBox_id,
  320. $updateEventType,
  321. $status,
  322. $binCode
  323. ]));
  324. $this->instant($this->stationTaskMaterialBoxService,'StationTaskMaterialBoxService');
  325. try{
  326. if(($failed
  327. =$status)==1){
  328. throw new ErrorException('海柔任务失败');
  329. }
  330. if($料箱不匹配=
  331. !$stationTaskMaterialBox
  332. =(function()use($stationTaskMaterialBox_id,$binCode){
  333. $stationTaskMaterialBox=StationTaskMaterialBox::query()->find($id=$stationTaskMaterialBox_id);
  334. if($stationTaskMaterialBox['materialBox']['code']==$binCode)return $stationTaskMaterialBox;
  335. return null;
  336. })()){
  337. throw new ErrorException('发回的料箱和任务号(ID)不匹配:$stationTaskMaterialBox_id:'
  338. .$stationTaskMaterialBox_id.' $binCode:'.$binCode. ' '.
  339. StationTaskMaterialBox::query()
  340. ->where('id', $id=$stationTaskMaterialBox_id)
  341. ->get()
  342. ->toJson());
  343. }
  344. if(($标记已放置=
  345. function()use($updateEventType,$stationTaskMaterialBox){
  346. if(($isPut
  347. =$updateEventType)==1){
  348. $this->stationTaskMaterialBoxService->markHasPut($stationTaskMaterialBox);
  349. return true;
  350. }return false;
  351. })())
  352. return true;
  353. ($标记已取出=
  354. function()use($updateEventType,$stationTaskMaterialBox){
  355. if(($isGet
  356. =$updateEventType)==0){
  357. $this->stationTaskMaterialBoxService->markHasTaken($stationTaskMaterialBox);
  358. }
  359. })();
  360. }catch (\Exception $e){
  361. $this->excepted($stationTaskMaterialBox_id, $binCode, $e->getMessage());
  362. return false;
  363. }
  364. return true;
  365. }
  366. // public function markHasPut($taskCode,$binCode):bool{
  367. // try{
  368. // //标记料箱进入位置
  369. //// $taskMaterialBoxesService->markDone();//
  370. // }catch (\Exception $e){
  371. // switch ($e->getCode()){
  372. // case 'taskBinNotMatch';
  373. // case 'taskGetFailed';
  374. // }
  375. // }
  376. //
  377. // }
  378. public function excepted($taskCode='',$binCode='', $msg=''):bool{
  379. try{
  380. throw new ErrorException(
  381. "taskCode任务号:$taskCode , binCode箱号:$binCode 海柔运行报错: $msg"
  382. );
  383. }catch (\Exception $e){
  384. return true;
  385. }
  386. }
  387. /**
  388. * @param array $dataToPost
  389. * @return bool
  390. */
  391. public function controlHaiRobot(array $dataToPost,Collection $taskMaterialBoxes,$modeName): bool
  392. {
  393. LogService::log('海柔请求','runMany','波次任务分配6.r5f2c1:'.json_encode($dataToPost));
  394. try{
  395. LogService::log('海柔请求','runMany','波次任务分配6.r5f2c1.51:');
  396. $response = Http::post(config('api.haiq.storage.moveBin'), $dataToPost);
  397. if(isset($response->json()['code'])&&$response->json()['code']==500)
  398. throw new ErrorException('机器人500错误:'.json_encode($response->json()));
  399. LogService::log('海柔请求','runMany','波次任务分配6.r5f2c1.52:');
  400. LogService::log(__METHOD__,'runMany','波次任务分配6.r5f2c1.53:'.json_encode($response->json()));
  401. }catch (\Exception $e){
  402. LogService::log('海柔请求','runMany','波次任务分配6.r5f2c1.54:'.json_encode($dataToPost).$e->getMessage());
  403. throw new ErrorException('海柔机器人任务执行失败:'.json_encode($dataToPost).$e->getMessage());
  404. }
  405. LogService::log('海柔请求','runMany','波次任务分配6.r5f2c2:'.json_encode($dataToPost));
  406. $errMsg = (function () use ($response) {
  407. if ($response->ok()) return '';
  408. $errMsg = '错误: ';
  409. if (!$response) {
  410. return $errMsg . '没有返回内容,检查连接或目标服务器';
  411. }
  412. switch (((string)$response["code"])[0]) {
  413. case 5:
  414. $errMsg .= '目标服务器代码错误,请联系对方';
  415. break;
  416. case 4:
  417. $errMsg .= '权限不足以请求资源,请检查对方服务器规范';
  418. break;
  419. default:
  420. $errMsg .= '出现未知请求错误';
  421. break;
  422. }
  423. $responseDetails = ' code:' . $response["code"]
  424. . ' header:' . $response->body()
  425. . ' response:' . json_encode($response->headers());
  426. return $errMsg . $responseDetails;
  427. })();
  428. LogService::log('海柔请求','runMany','波次任务分配6.r5f2c3:'.json_encode($errMsg));
  429. LogService::log(__METHOD__, __FUNCTION__,
  430. $errMsg ?? ''
  431. . '请求:' . json_encode($dataToPost)
  432. . '调用堆栈c:' . json_encode(array_slice(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS), 0, 3))
  433. );
  434. $isSuccess = !$errMsg;
  435. $标记料箱状态=(function() use ($taskMaterialBoxes,$modeName){
  436. foreach ($taskMaterialBoxes as $taskMaterialBox){
  437. switch ($modeName){
  438. case '缓存架入立架':
  439. case '输送线入立架':
  440. case '移动立架内位置':
  441. $taskMaterialBox->materialBox['status']='在入库中';break;
  442. case '立架出至输送线':
  443. case '立架出至缓存架':
  444. $taskMaterialBox->materialBox['status']='在出库中';break;
  445. default:
  446. $taskMaterialBox->materialBox['status']='未知';break;
  447. }
  448. $taskMaterialBox->materialBox->update();
  449. }
  450. })();
  451. return $isSuccess;
  452. }
  453. /**
  454. * 填充缓存架
  455. *
  456. * @param \Illuminate\Database\Eloquent\Collection $stations
  457. *
  458. * @return bool
  459. *
  460. * @throws
  461. */
  462. public function paddingCacheShelf($stations)
  463. {
  464. $collection = new Collection();
  465. $stationCollection = new Collection();
  466. $blacklist = [];
  467. foreach ($stations as $station){
  468. $box = app("MaterialBoxService")->getAnEmptyBox($blacklist);
  469. if (!$box)break;
  470. $task = StationTask::query()->create([
  471. 'status' => "待处理",
  472. 'station_id' => $station->id,
  473. ]);
  474. $collection->add(StationTaskMaterialBox::query()->create([
  475. 'station_id' => $station->id,
  476. 'material_box_id'=>$box->id,
  477. 'status'=>"待处理",
  478. 'type' => '取',
  479. 'station_task_id' => $task->id,
  480. ]));
  481. $stationCollection->add($station->code);
  482. $blacklist[] = $box->id;
  483. }
  484. if ($stationCollection->count()>0){
  485. if (!$this->fetchGroup_multiLocation($stationCollection,$collection,'','立架出至缓存架')) return null;
  486. }
  487. return $stations->count()==$stationCollection->count();
  488. }
  489. }