stationTaskMaterialBoxService=null; $this->materialBoxService=null; $this->stationService=null; } /** * @param string $modeName '输送线入立架'|'立架出至输送线'|'移动立架内位置'|'缓存架入立架'|'立架出至缓存架' * @param string $fromLocation * @param string $toLocation * @param Collection $taskMaterialBoxes * @param string $groupId * @param int $priority * @param int $isSequenced * @return array */ private function makeJson_move( Collection $taskMaterialBoxes, string $modeName, string $fromLocation='', string $toLocation='', $groupId='' , $priority=10 , $isSequenced=1 ): array { $timestampSuffix = microtime(true); $taskMode=$this->getTaskMode($modeName); $bins=$taskMaterialBoxes->map(function (StationTaskMaterialBox $taskMaterialBox)use($timestampSuffix,$fromLocation,$toLocation){ return [ "taskCode" =>$taskMaterialBox['id'].'_'.$timestampSuffix, "binCode" => $taskMaterialBox['materialBox']['code'], "fromLocCode" => $fromLocation??'', "toLocCode" => $toLocation??'', ]; }); return [[ "taskMode" =>$taskMode, "bins"=>$bins, "groupCode"=>$groupId.'_'.$timestampSuffix, "priority"=>$priority, "sequenceFlag"=>$isSequenced, ]]; } public function makeJson_move_multi( Collection $taskMaterialBoxes, string $modeName, Collection $fromLocation=null, Collection $toLocations=null, $groupId='' , $priority=10 , $isSequenced=1 ): array { if((!$toLocations||($toLocations->count()!=$taskMaterialBoxes->count())) && (!$fromLocation || ($fromLocation->count()!=$taskMaterialBoxes->count())))throw new Exception('元素数量必须和料箱任务不一致'); $timestampSuffix = microtime(true); $taskMode=$this->getTaskMode($modeName); $bins=$taskMaterialBoxes->map(function (StationTaskMaterialBox $taskMaterialBox,$i)use($timestampSuffix,$fromLocation,$toLocations){ return [ "taskCode" =>$taskMaterialBox['id'].'_'.$timestampSuffix, "binCode" => $taskMaterialBox['materialBox']['code'], "fromLocCode" => $fromLocation ? $fromLocation[$i] : '', "toLocCode" => $toLocations ? $toLocations[$i] : '', ]; }); return [[ "taskMode" =>$taskMode, "bins"=>$bins, "groupCode"=>$groupId.'_'.$timestampSuffix, "priority"=>$priority, "sequenceFlag"=>$isSequenced, ]]; } private function getTaskMode($modeName){ switch ($modeName){ case '输送线入立架': return 1; case '立架出至输送线': return 2; case '移动立架内位置': return 3; case '立架出至缓存架': case '缓存架入立架':return 6; default: throw new \Exception('发至海柔的移料箱请求,模式不存在'); } } public function fetchGroup($toLocation, Collection $taskMaterialBoxes, $groupIdPrefix='',$mode='立架出至输送线'): bool { $dataToPost=$this->makeJson_move( $taskMaterialBoxes, $mode, '', $toLocation??'', $groupIdPrefix ); return $this->controlHaiRobot($dataToPost,$taskMaterialBoxes,$mode); } /** * @param Collection $toLocations //库位和料箱任务一一对应,一个库位对应一个料箱 * @param Collection $taskMaterialBoxes * @param string $groupIdPrefix * @param string $mode * @param int $priority * @param bool $isSequenced * @return bool * @throws ErrorException|Exception */ public function fetchGroup_multiLocation(Collection $toLocations, Collection $taskMaterialBoxes,string $groupIdPrefix='',string $mode='立架出至输送线', int $priority = 10, bool $isSequenced = true): bool { $dataToPost=$this->makeJson_move_multi( $taskMaterialBoxes, $mode, null, $toLocations, $groupIdPrefix, $priority, $isSequenced ? 1 : 0 ); return $this->controlHaiRobot($dataToPost,$taskMaterialBoxes,$mode); } public function markBinProcessed( $workStation, $binCode, $success, $created_at, $exception, $is_in_plan ): bool { LogService::log('海柔请求','markBinProcessed1.1', $binCode.'|'.$success.'|'.$exception.'|'.$is_in_plan); $this->instant($this->stationService,'StationService'); $this->instant($this->materialBoxService,'MaterialBoxService'); $this->instant($this->stationTaskMaterialBoxService,'StationTaskMaterialBoxService'); $this->instant($this->stationTaskCommoditiesService,'StationTaskCommodityService'); $this->instant($this->stationTaskBatchService,'StationTaskBatchService'); try{ LogService::log('海柔请求','markBinProcessed1.2', json_encode([$binCode,$success,$exception,$is_in_plan])); if($failed =!$success) throw new ErrorException('海柔任务失败:'.$exception); LogService::log('海柔请求','markBinProcessed1.3', $failed); $materialBox= $this->materialBoxService->get(['code'=>$binCode])->first(); /** @var StationTaskMaterialBox $stationTaskMaterialBox */ $stationTaskMaterialBox =(function()use($materialBox){ return $stationTaskMaterialBox= StationTaskMaterialBox::query() ->where('material_box_id',$materialBox['id']) ->where('created_at','>',Carbon::now()->subDay()) ->whereIn('status',['处理中','待处理','异常','处理队列']) ->orderBy('id','desc') ->first(); })(); if(!$stationTaskMaterialBox){ throw new ErrorException($binCode.'该料箱没有安排在处理队列中.'); } DB::transaction(function ()use($stationTaskMaterialBox,$binCode){ $stationTaskMaterialBox_next= $this->stationTaskMaterialBoxService ->processNextQueued($stationTaskMaterialBox); //找到队列中下一个料箱,并标记为处理中 $this->stationTaskCommoditiesService ->markProcessed($stationTaskMaterialBox['stationTaskCommodities']); LogService::log('海柔请求','markBinProcessed1.8', json_encode($stationTaskMaterialBox).'|'.$binCode); if($stationTaskMaterialBox_next) $this->stationTaskCommoditiesService ->markProcessing($stationTaskMaterialBox_next['stationTaskCommodities']);//因为上边商品任务被标记完成了,所以这里要将队列中找出正在处理的料箱对应的标记为“处理中” $this->stationTaskMaterialBoxService ->markProcessed($stationTaskMaterialBox); $notProcessedBoxTasks = $this->stationTaskMaterialBoxService->getNotProcessedSiblings($stationTaskMaterialBox); if($notProcessedBoxTasks->isEmpty()){ $this->instant($this->stationTaskService,'StationTaskService'); LogService::log('海柔请求','markBinProcessed1.81', json_encode($stationTaskMaterialBox['stationTaskBatch']).'|'.$binCode); $stationTaskMaterialBox->loadMissing('stationTaskBatch'); $this->stationTaskBatchService->markProcessed($stationTaskMaterialBox['stationTaskBatch']); $this->stationTaskService->markProcessed($stationTaskMaterialBox['stationTask']); } $this->storeBox($stationTaskMaterialBox) ?true :(function(){throw new ErrorException('呼叫机器人回收U型线料箱失败');})(); LogService::log('海柔请求','markBinProcessed1.9', json_encode($stationTaskMaterialBox).'|'.$binCode); $this->stationService->broadcastBinMonitor($stationTaskMaterialBox['station_id'],$stationTaskMaterialBox['stationTask']); LogService::log('海柔请求','markBinProcessed1.99', json_encode($stationTaskMaterialBox).'|'.$binCode); }); return true; }catch (\Exception $e){ LogService::log('海柔请求','markBinProcessed E1', $binCode); $this->instant($this->stationTaskMaterialBoxService,'StationTaskMaterialBoxService'); $this->instant($this->materialBoxService,'MaterialBoxService'); $box=$this->materialBoxService->firstOrCreate(['code'=>$binCode]); $stationTaskMaterialBox_toStore= $this->stationTaskMaterialBoxService->create([ 'station_id' => $this->stationService->getStation_byType('立库')['id'], 'material_box_id' => $box['id'], 'status' => '处理中' ] ); $dataToPost=$this->makeJson_move( collect([$stationTaskMaterialBox_toStore]), '输送线入立架', 'BIN-IN1',//TODO:这里应该是动态取得,参考出立架getULineExit()方法,不然不能从站获得对应的出口,而且要改Station的child为children '', $stationTaskMaterialBox_toStore['stationTaskBatch']['id'] ); $this->controlHaiRobot($dataToPost,collect([$stationTaskMaterialBox_toStore]),'输送线入立架'); LogService::log('海柔请求','markBinProcessed E2', $binCode); $stationTaskMaterialBox = $stationTaskMaterialBox_toStore??$materialBox??null; if($stationTaskMaterialBox && get_class($stationTaskMaterialBox)==MaterialBox::class){ $stationTaskMaterialBox = StationTaskMaterialBox::query() ->where('material_box_id',$stationTaskMaterialBox['id']) ->where('status','<>','完成') ->where('created_at','>',now()->format('Y-m-d')) ->first(); } if($stationTaskMaterialBox) $this->stationTaskMaterialBoxService ->excepted($stationTaskMaterialBox); return $e->getMessage(); } } public function storeBox(?StationTaskMaterialBox $stationTaskMaterialBox): bool { LogService::log('海柔请求','putBinToStore1', ''); $this->instant($this->stationService,'StationService'); $this->instant($this->stationTaskMaterialBoxService,'StationTaskMaterialBoxService'); $stationTaskMaterialBox_toStore= $this->stationTaskMaterialBoxService->firstOrCreate([ 'station_id' => $this->stationService->getStation_byType('立库')['id'], 'material_box_id' => $stationTaskMaterialBox['materialBox']['id'], 'status' => '处理中', 'type' => '放', ]); LogService::log('海柔请求','putBinToStore2', json_encode($stationTaskMaterialBox)); $dataToPost=$this->makeJson_move( collect([$stationTaskMaterialBox_toStore]), '输送线入立架', 'BIN-IN1',//TODO:这里应该是动态取得,参考出立架getULineExit()方法,不然不能从站获得对应的出口,而且要改Station的child为children '', $stationTaskMaterialBox['stationTaskBatch']['id'] ); LogService::log('海柔请求','putBinToStore3', json_encode($dataToPost)); $controlSuccess = $this->controlHaiRobot($dataToPost,collect([$stationTaskMaterialBox_toStore]),'输送线入立架'); return $controlSuccess; } /** 缓存架入立架 料箱 任务 * @param StationTaskMaterialBox|null $stationTaskMaterialBox * @param Station|\stdClass|null $station * @return bool * @throws ErrorException */ public function putBinToStore_fromCacheShelf(?StationTaskMaterialBox $stationTaskMaterialBox, $station): bool { $formLocation = $station->code; if ($station && $station->parent_id){ //缓存标记 推入延时队列 等待三秒决定是否执行 $key = "cacheShelfTask_".$station->parent_id; if (Cache::has($key)){ list($task,$location) = Cache::get($key); $task->add($stationTaskMaterialBox); $location->add($formLocation); }else{ $task = collect([$stationTaskMaterialBox]); $location = collect([$formLocation]); } Cache::forever($key,array($task,$location)); CacheShelfTaskJob::dispatch($key,$task->count())->delay(now()->addSeconds(config("haiRou.cacheShelf.callAwait"))); return true; } $dataToPost=$this->makeJson_move(collect([$stationTaskMaterialBox]), '缓存架入立架', $formLocation, ''); $controlSuccess = $this->controlHaiRobot($dataToPost,collect([$stationTaskMaterialBox]),'缓存架入立架'); if($controlSuccess){ $this->instant($this->stationTaskMaterialBoxService,'StationTaskMaterialBoxService'); $this->stationTaskMaterialBoxService->set($stationTaskMaterialBox,['status' => '处理中']); } return $controlSuccess; } /** * 任务变更时的动作 * * @param $stationTaskMaterialBox_id * @param $updateEventType * @param $status * @param $binCode * @return bool */ public function taskUpdate( $stationTaskMaterialBox_id, //实际对应传入的字段 taskCode, 这里用料箱任务号做taskCode $updateEventType, //0:task_begin(取货)1:task_end(放货) $status, //0:任务成功1:任务失败 $binCode ):bool{ $this->instant($this->stationTaskMaterialBoxService,'StationTaskMaterialBoxService'); //获取下达任务 /** @var StationTaskMaterialBox|\stdClass $stationTaskMaterialBox */ $stationTaskMaterialBox = $stationTaskMaterialBox_id ? StationTaskMaterialBox::query()->with("materialBox:id,code") ->find($stationTaskMaterialBox_id) : null; if (!$stationTaskMaterialBox || !$stationTaskMaterialBox->materialBox){ $this->push(__METHOD__."->".__LINE__,"海柔任务不存在",json_encode(request()->input())); return false; } if ($status==1){ //海柔失败 $this->push(__METHOD__."->".__LINE__,"海柔任务异常",json_encode(request()->input())); $stationTaskMaterialBox->update(['status'=>"异常"]); return false; } if ($stationTaskMaterialBox->materialBox->code != $binCode){ $this->push(__METHOD__."->".__LINE__,"海柔任务料箱号不匹配",json_encode(request()->input())); return false; } try { switch ($updateEventType){ case 0://已取出 $this->stationTaskMaterialBoxService->markHasTaken($stationTaskMaterialBox); break; case 1://已放置 $this->stationTaskMaterialBoxService->markHasPut($stationTaskMaterialBox); break; default: throw new \Exception("任务进行状态错误,未捕获"); } }catch (\Exception $err){ $this->push(__METHOD__."->".__LINE__,"海柔任务通知失败",json_encode(request()->input())." | ".$err->getMessage()); return false; } return true; } public function excepted($taskCode='',$binCode='', $msg=''):bool{ try{ throw new ErrorException( "taskCode任务号:$taskCode , binCode箱号:$binCode 海柔运行报错: $msg" ); }catch (\Exception $e){ return true; } } /** * @param array $dataToPost * @param Collection $taskMaterialBoxes * @param string|null $modeName * @return bool */ public function controlHaiRobot(array $dataToPost,Collection $taskMaterialBoxes, string $modeName): bool { LogService::log('海柔请求','runMany','波次任务分配6.r5f2c1:'.json_encode($dataToPost)); try{ LogService::log('海柔请求','runMany','波次任务分配6.r5f2c1.51:'); $response = Http::post(config('api.haiq.storage.moveBin'), $dataToPost); if(isset($response->json()['code'])&&$response->json()['code']==500) throw new ErrorException('机器人500错误:'.json_encode($response->json())); LogService::log(__METHOD__,'runMany','波次任务分配6.r5f2c1.53:'.json_encode($response->json())); }catch (\Exception $e){ LogService::log('海柔请求','runMany','波次任务分配6.r5f2c1.54:'.json_encode($dataToPost).$e->getMessage()); throw new ErrorException('海柔机器人任务执行失败:'.json_encode($dataToPost).$e->getMessage()); } LogService::log('海柔请求','runMany','波次任务分配6.r5f2c2:'.json_encode($dataToPost)); $errMsg = (function () use ($response) { if ($response->ok()) return ''; $errMsg = '错误: '; if (!$response) { return $errMsg . '没有返回内容,检查连接或目标服务器'; } switch (((string)$response["code"])[0]) { case 5: $errMsg .= '目标服务器代码错误,请联系对方'; break; case 4: $errMsg .= '权限不足以请求资源,请检查对方服务器规范'; break; default: $errMsg .= '出现未知请求错误'; break; } $responseDetails = ' code:' . $response["code"] . ' header:' . $response->body() . ' response:' . json_encode($response->headers()); return $errMsg . $responseDetails; })(); LogService::log('海柔请求','runMany','波次任务分配6.r5f2c3:'.json_encode($errMsg)); LogService::log(__METHOD__, __FUNCTION__, $errMsg ?? '' . '请求:' . json_encode($dataToPost) . '调用堆栈c:' . json_encode(array_slice(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS), 0, 3)) ); $isSuccess = !$errMsg; $标记料箱状态=(function() use ($taskMaterialBoxes,$modeName){ foreach ($taskMaterialBoxes as $taskMaterialBox){ switch ($modeName){ case '缓存架入立架': case '输送线入立架': case '移动立架内位置': $taskMaterialBox->materialBox['status']='在入库中';break; case '立架出至输送线': case '立架出至缓存架': $taskMaterialBox->materialBox['status']='在出库中';break; default: $taskMaterialBox->materialBox['status']='未知';break; } $taskMaterialBox->materialBox->update(); } })(); return $isSuccess; } /** * 填充缓存架 * * @param \Illuminate\Database\Eloquent\Collection $stations * * @return bool * * @throws */ public function paddingCacheShelf(Collection $stations):?bool { $collection = new Collection(); $stationCollection = new Collection(); $blacklist = []; foreach ($stations as $station){ $box = app("MaterialBoxService")->getAnEmptyBoxSortedByOwner(null,$blacklist); if (!$box)break; $task = StationTask::query()->create([ 'status' => "待处理", 'station_id' => $station->id, ]); $collection->add(StationTaskMaterialBox::query()->create([ 'station_id' => $station->id, 'material_box_id'=>$box->id, 'status'=>"待处理", 'type' => '放', 'station_task_id' => $task->id, ])); $stationCollection->add($station->code); $blacklist[] = $box->id; } if ($stationCollection->count()>0){ if (!$this->fetchGroup_multiLocation($stationCollection,$collection,'','立架出至缓存架',20,false)) return null; app("StationService")->locationOccupyMulti($stationCollection->toArray()); $stationCollection->each(function ($code){ app("CacheShelfService")->lightUp($code,'3','0',["title"=>"机器人取箱中,禁止操作"]); }); } return $stations->count()==$stationCollection->count(); } }