using Microsoft.Extensions.Logging; using SlnMesnac.Model.domain; using SlnMesnac.Model.Enum; using SlnMesnac.Repository; using SlnMesnac.Repository.service; using SlnMesnac.TouchSocket; using SlnMesnac.TouchSocket.Entity; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Transactions; namespace SlnMesnac.Business.@base { /// /// 视觉系统业务类 /// public class VisionBusiness { private ILogger _logger; private static VisionBusiness instance; private TcpServer _tcpServer; private IAirportTaskService _airportTaskService; private IVisionSettingService _visionSettingService; private IAGVStateService _aGVStateService; private IAGVJobService _AGVJobService; private BaseAGVBusiness _baseAGVBusiness; public VisionBusiness(ILogger logger, TcpServer tcpServer, IAirportTaskService airportTaskService, IVisionSettingService visionSettingService, IAGVStateService aGVStateService, IAGVJobService aGVJobService) { _logger = logger; _tcpServer = tcpServer; _airportTaskService = airportTaskService; _visionSettingService = visionSettingService; _aGVStateService = aGVStateService; _AGVJobService = aGVJobService; //视觉系统回复状态 _tcpServer.ReceiveVisionSysStateEvent += VisionStateRequest; //视觉系统回复开始码垛 _tcpServer.ReceiveVisionStartWorkEvent += GetVisionWorkState; //视觉系统通知一次码垛完成结果 _tcpServer.ReceiveStackWorkDoneEvent += AutoStackResultSend; //_tcpServer.ReceiveStackWorkDoneEvent += AutoStackResultSend; //视觉系统通知人工异常处理完成 _tcpServer.ReceiveManualExceptionDealDoneEvent += GetManualExceptionDealDone; //视觉系统回复复位结果 _tcpServer.ReceiveStackRobotResetEvent += GetResetResult; _AGVJobService = aGVJobService; } public static VisionBusiness GetInstance(ILogger logger, TcpServer tcpServer, IAirportTaskService airportTaskService, IVisionSettingService visionSettingService, IAGVStateService aGVStateService, IAGVJobService aGVJobService) { if (instance == null) { instance = new VisionBusiness(logger, tcpServer, airportTaskService, visionSettingService, aGVStateService, aGVJobService); } return instance; } /// /// AMR就绪,请求视觉开始工作 /// public void RequestVisionStartWork(StackState state, int count, string id) { _tcpServer.SendAMRRequestVisionStartWork(new byte[2] { (byte)state, (byte)count }, id); } /// /// 码垛结束,请求视觉系统复位 /// public void RequestVisionReplace(string ip) { _tcpServer.SendStackOverRequestVisionSysReplace(ip); VDCount = 1; VACount = 0; TotalJudge = false; } /// /// 上位机调度系统每隔固定时间,请求视觉系统状态 /// public void VisionStateRequest(TcpVisionEntity entity, string id) { if (entity.DataLength == 2) { } //Thread.Sleep(1000); //SendRequestVisionSysState(id); } /// /// 接受开始工作状态 /// /// /// private void GetVisionWorkState(TcpVisionEntity entity, string id) { try { if (entity.DataLength == 3) { //正常开始工作 if (entity.DataBytes[0] == 0x00) { _logger.LogInformation($"视觉系统收到开始工作状态:正常开始工作"); VisionSetting record = _visionSettingService.GetList(x => x.VisionID == id).ToList().First(); //大端字节序 Array.Reverse(entity.SN); AirportTask airportTask = _airportTaskService.GetTaskInfos(x => x.amragvno == record.AMRGUID && x.visiontaskno == (BitConverter.ToUInt16(entity.SN, 0) - 1).ToString() && x.taskstate == "抓取中").First(); //判断视觉计数和上位机技术是否一致,如果不一致,向视觉计数统一 //如果这次码垛是AMR if (entity.DataBytes[2] == 00) { AGVState AMR = _aGVStateService.GetSingleAGVState(airportTask.amragvno); //如果不一样 if (entity.DataBytes[1] != AMR.stackcount) { AMR.stackcount = entity.DataBytes[1]; _aGVStateService.UpdateAsync(AMR); } } //如果这次码垛是Deliver else if (entity.DataBytes[2] == 01) { AGVState Deliver = _aGVStateService.GetSingleAGVState(airportTask.deliveragvno); //如果不一样 if (entity.DataBytes[1] != Deliver.stackcount) { Deliver.stackcount = entity.DataBytes[1]; _aGVStateService.UpdateAsync(Deliver); } } //内容 else { _logger.LogError($"视觉系统收到开始工作状态:返回未知代码"); } } else if (entity.DataBytes[0] == 0x01) { _logger.LogError($"视觉系统收到开始工作状态:异常不能开始工作"); } else { _logger.LogError($"视觉系统收到开始工作状态:返回未知代码"); } } else { _logger.LogError($"视觉系统收到开始工作状态:数据返回格式错误"); } } catch (Exception ex) { RequestVisionReplace(id); _logger.LogError($"接受开始工作状态发生错误 {ex.Message}"); } } /// /// 收到堆筐请求 /// /// /// /// public void AutoStackResultSend(TcpVisionEntity entity, string id) { try { if (entity.DataLength == 2) { if (entity.DataBytes[0] == 0x00) { _logger.LogInformation($"视觉系统一次码垛结束,码垛完成 Deliver:{VDCount} AMR:{VACount}"); //上位机回复收到码垛结果 _tcpServer.SendReplayStackResult(id); VisionSetting record = _visionSettingService.GetList(x => x.VisionID == id).ToList().First(); //大端字节序 Array.Reverse(entity.SN); AirportTask airportTask = _airportTaskService.GetTaskInfos(x => x.amragvno == record.AMRGUID && x.visiontaskno == (BitConverter.ToUInt16(entity.SN, 0) - 1).ToString() && x.taskstate == "抓取中").First(); //成功码垛先码垛计数 StackCount(airportTask, entity.DataBytes[1]); //取出两辆小车的计数 int AMRCount = _aGVStateService.GetSingleAGVState(airportTask.amragvno).stackcount; int DeliverCount = 0; if (!string.IsNullOrEmpty(airportTask.deliveragvno)) { DeliverCount = _aGVStateService.GetSingleAGVState(airportTask.deliveragvno).stackcount; } //////////////下面是下一次码垛的综合判断条件////////////// //先判断总数是否需要继续工作 if (airportTask.totalcount > airportTask.loadcount) { //如上一次是AMR 如有Deliver 判断是否到达 到达向deliver发送开始工作,没就判断amr是否装满 继续向amr //上一次是AMR if (entity.DataBytes[1] == 0x00) { //妹有Deliver的任务(小车号和是否已到达都没有)amr肯定能抓完 继续amr抓取 if (string.IsNullOrEmpty(airportTask.deliveragvno) && string.IsNullOrEmpty(airportTask.deliveragvisarrive)) { //一直向amr抓,直到抓满 RequestVisionStartWork(StackState.NXAMRNoPositioning, AMRCount, id); } //有Delievr任务(有任务号且状态为未到达)继续amr抓取 else if (!string.IsNullOrEmpty(airportTask.deliveragvno) && airportTask.deliveragvisarrive == "未到达") { //判断AMR小车是否装满,没满就开装,满了就结束,开始循环判断deliver是否到达 //amr没满 继续装amr if (_aGVStateService.GetSingleAGVState(airportTask.amragvno).stackcount < BaseTaskInfoBusiness.AMRStackNumber) { RequestVisionStartWork(StackState.NXAMRNoPositioning, AMRCount, id); } //AMR满了 else { AirportTask task; //结束抓取、循环判断deliver是否到达,到了就向新的Deliver开始抓取 Task.Run(() => { //请求复位 RequestVisionReplace(id); while (true) { //循环判断该任务的deliver是否到达 Task.Delay(1000); task = _airportTaskService.GetTaskInfos(x => x.amragvno == record.AMRGUID && x.taskstate == "抓取中").First(); if (task.deliveragvisarrive == "已到达") { RequestVisionStartWork(StackState.NXAGVNeedPositioning, DeliverCount, id); break; } } }); } } //有Deliver任务(有任务号且已到达)向Deliver发送开始抓取 else if (!string.IsNullOrEmpty(airportTask.deliveragvno) && airportTask.deliveragvisarrive == "已到达") { RequestVisionStartWork(StackState.NXAGVNeedPositioning, DeliverCount, id); } //异常情况 else { throw new Exception($"任务 {airportTask.taskno} 状态异常!请清除任务并重新下发任务!"); } } //如上一次是deliver-->则判断deliver是否装满-->装满则deliver入库-->判断剩下的数量用不用调新的deliver-->用就调新的deliver然后发向amr抓取(还得判断AMR是否抓满)-->不用就直接向amr抓取 //上一次是Deliver else if (entity.DataBytes[1] == 0x01) { //判断Deliver是否装满,装满就直接入库,未装满继续装 //deliver没满,继续装 if (_aGVStateService.GetSingleAGVState(airportTask.deliveragvno).stackcount < BaseTaskInfoBusiness.DeliverStackNumber) { RequestVisionStartWork(StackState.NXAGVNoPositioning, DeliverCount, id); } //deliver满了,入库然后判断 else { //入库代码 _baseAGVBusiness.EndTaskAndClearErrorAndDownloadTask(airportTask.deliveragvno, _AGVJobService.GetAGVJobListByTypeAndConveyorNo("800入库", airportTask.conveyorno).JobName); airportTask.deliveragvno = null; airportTask.deliveragvisarrive = null; _airportTaskService.UpdateTaskAsync(airportTask); //判断剩下的数量用不用调用新的deliver //剩下的数量不用 直接向amr抓取 int amrleft = BaseTaskInfoBusiness.AMRStackNumber - _aGVStateService.GetSingleAGVState(airportTask.amragvno).stackcount; if (airportTask.totalcount - airportTask.loadcount < amrleft) { RequestVisionStartWork(StackState.NXAMRNeedPositioning, AMRCount, id); } //剩下的数量需要调用,并判断向AMR抓取 else { //调新的deliver AGVState newagv = _baseAGVBusiness.GetBestAGV(AgvType.Deliver); _baseAGVBusiness.EndTaskAndClearErrorAndDownloadTask(newagv.agvno, _AGVJobService.GetAGVJobListByTypeAndConveyorNo("800入位", airportTask.conveyorno).JobName); airportTask.deliveragvno = newagv.agvno; airportTask.deliveragvisarrive = "未到达"; _airportTaskService.UpdateTaskAsync(airportTask); //amr无容量,结束抓取,等待Deliver到达 if (amrleft == 0) { AirportTask task; //结束抓取、循环判断deliver是否到达,到了就向新的Deliver开始抓取 Task.Run(() => { //请求复位 RequestVisionReplace(id); while (true) { //循环判断该任务的deliver是否到达 Task.Delay(1000); task = _airportTaskService.GetTaskInfos(x => x.amragvno == record.AMRGUID && x.taskstate == "抓取中").First(); if (task.deliveragvisarrive == "已到达") { RequestVisionStartWork(StackState.NXAGVNeedPositioning, DeliverCount, id); break; } } }); } //amr有容量,抓 else { RequestVisionStartWork(StackState.NXAMRNeedPositioning, AMRCount, id); } } } } //未知代码 else { throw new Exception($"任务 {airportTask.taskno} 未知上次码垛代码!请检查视觉系统状态!"); } } //总数达标,结束任务,复位机械臂,调回小车,任务状态调整 else { _baseAGVBusiness.EndTaskAndClearErrorAndDownloadTask(airportTask.amragvno, _AGVJobService.GetAGVJobListByTypeAndConveyorNo("1000入库", airportTask.conveyorno).JobName); airportTask.amragvno = null; airportTask.amragvisarrive = null; airportTask.visiontaskno = null; if (!string.IsNullOrEmpty(airportTask.deliveragvno)) { _baseAGVBusiness.EndTaskAndClearErrorAndDownloadTask(airportTask.deliveragvno, _AGVJobService.GetAGVJobListByTypeAndConveyorNo("800入库", airportTask.conveyorno).JobName); airportTask.deliveragvno = null; airportTask.deliveragvisarrive = null; } airportTask.finishtime = DateTime.Now; airportTask.taskstate = "已完成"; _airportTaskService.UpdateTaskAsync(airportTask); } } //码垛过程失败 else if (entity.DataBytes[0] == 0x01) { VisionSetting record = _visionSettingService.GetList(x => x.VisionID == id).ToList().First(); //大端字节序 Array.Reverse(entity.SN); AirportTask airportTask = _airportTaskService.GetTaskInfos(x => x.amragvno == record.AMRGUID && x.visiontaskno == (BitConverter.ToUInt16(entity.SN, 0) - 1).ToString() && x.taskstate == "抓取中").First(); //取出两辆小车的计数 int AMRCount = _aGVStateService.GetSingleAGVState(airportTask.amragvno).stackcount; int DeliverCount = 0; if (!string.IsNullOrEmpty(airportTask.deliveragvno)) { DeliverCount = _aGVStateService.GetSingleAGVState(airportTask.deliveragvno).stackcount; } _logger.LogError($"视觉系统一次码垛结束,码垛过程失败,重新发送码垛信号"); if (entity.DataBytes[1] == 0x00) { //发送向复合机器人(AMR)码垛的信号 RequestVisionStartWork(StackState.NXAMRNoPositioning, AMRCount, id); _logger.LogInformation("下一次码垛开始,方向:复合机器人"); } else if (entity.DataBytes[1] == 0x01) { //发送向搬运机器人(AGVDeliver)码垛的信号 RequestVisionStartWork(StackState.NXAGVNoPositioning, DeliverCount, id); _logger.LogInformation("下一次码垛开始,方向:搬运机器人"); } else { _logger.LogError($"码垛失败返回未知码垛结果代码,请手动重新发送码垛信号"); } } //码垛检查失败 else if (entity.DataBytes[0] == 0x02) { _logger.LogError($"视觉系统一次码垛结束,码垛检查失败,请手动矫正并手动开始下一次码垛"); //仍然要码垛计数,只是筐码的不准 VisionSetting record = _visionSettingService.GetList(x => x.VisionID == id).ToList().First(); AirportTask airportTask = _airportTaskService.GetTaskInfos(x => x.amragvno == record.AMRGUID && x.visiontaskno == (BitConverter.ToInt16(entity.SN, 0) - 1).ToString() && x.taskstate == "抓取中").First(); StackCount(airportTask, entity.DataBytes[1]); } //未知代码 else { _logger.LogError($"视觉系统一次码垛结束,未知返回代码!请检查视觉系统状态!"); throw new Exception($"视觉系统 {id}一次码垛结束,未知返回代码!请检查视觉系统状态!"); } } //数据格式错误 else { _logger.LogError($"视觉系统一次码垛结束,返回数据格式错误,请检查视觉系统状态!"); } } catch(Exception ex) { RequestVisionReplace(id); _logger.LogError($"接受工作结束再分配状态发生错误 {ex.Message}"); } } public void StackCount(AirportTask airportTask, byte lastCount) { //AMR码垛计数 if (lastCount == 0x00) { airportTask.loadcount++; AGVState aGVState = _aGVStateService.GetSingleAGVState(airportTask.amragvno); aGVState.stackcount++; //判断返回结果 if (_aGVStateService.UpdateAsync(aGVState).Result || _airportTaskService.UpdateTaskAsync(airportTask).Result) { _logger.LogInformation($"计数更新成功 {airportTask.amragvno} AMR: {aGVState.stackcount} Total: {airportTask.loadcount}"); } else { _logger.LogError("计数更新失败!"); throw new Exception("计数更新失败!"); } } //Deliver码垛计数 else if (lastCount == 0x01) { airportTask.loadcount++; AGVState aGVState = _aGVStateService.GetSingleAGVState(airportTask.deliveragvno); aGVState.stackcount++; //判断返回结果 if (_aGVStateService.UpdateAsync(aGVState).Result || _airportTaskService.UpdateTaskAsync(airportTask).Result) { _logger.LogInformation($"计数更新成功 {airportTask.amragvno} AMR: {aGVState.stackcount} Total: {airportTask.loadcount}"); } else { _logger.LogError("计数更新失败!"); throw new Exception("计数更新失败!"); } } else { throw new Exception($"任务 {airportTask.taskno} 未知上次码垛代码!请检查视觉系统状态!"); } } int VDCount = 1; int VACount = 0; int VDTotal = 8; int VATotal = 6; bool Vjudge = true; bool VNow = true; public bool TotalJudge = false; /// /// 接受码垛结果,进行下一次码垛判断 /// public void StackResultSend(TcpVisionEntity entity, string id) { if (entity.DataLength == 1) { if (entity.DataBytes[0] == 0x00) { _logger.LogInformation($"视觉系统一次码垛结束,码垛完成 Deliver:{VDCount} AMR:{VACount}"); //上位机回复收到码垛结果 _tcpServer.SendReplayStackResult(id); //这里写是否继续下一次码垛的判断条件 //判断总数是否继续工作 if ((VDCount < VDTotal && Vjudge == true && TotalJudge == true) || (VACount < VATotal && Vjudge == false && TotalJudge == true)) { //如果码垛没结束继续发下一次的码垛信号 if (Vjudge) //这里写向哪个机器人码垛的判断条件 { //发送向搬运机器人(AGVDeliver)码垛的信号 RequestVisionStartWork(StackState.NXAGVNoPositioning, VDCount, id); _logger.LogInformation("下一次码垛开始,方向:搬运机器人"); VDCount++; VNow = true; if (VDCount >= VDTotal) { Vjudge = false; } } else { //发送向复合机器人(AMR)码垛的信号 RequestVisionStartWork(StackState.NXAMRNoPositioning, VACount, id); _logger.LogInformation("下一次码垛开始,方向:复合机器人"); VACount++; VNow = false; if (VACount >= VATotal) { Vjudge = true; } } } else { //如果码垛结束就请求复位 RequestVisionReplace(id); _logger.LogInformation("本次码垛结束,请求复位"); } } else if (entity.DataBytes[0] == 0x01) { _logger.LogError($"视觉系统一次码垛结束,码垛过程失败 Deliver:{VDCount} AMR:{VACount}"); //如果码垛没结束继续发下一次的码垛信号 if (VNow) //这里写向哪个机器人码垛的判断条件 { //发送向搬运机器人(AGVDeliver)码垛的信号 RequestVisionStartWork(StackState.NXAGVNoPositioning, VDCount, id); _logger.LogInformation("下一次码垛开始,方向:搬运机器人"); } else { //发送向复合机器人(AMR)码垛的信号 RequestVisionStartWork(StackState.NXAMRNoPositioning, VACount, id); _logger.LogInformation("下一次码垛开始,方向:复合机器人"); } } else if (entity.DataBytes[0] == 0x02) { _logger.LogError($"视觉系统一次码垛结束,码垛检查失败"); } else { _logger.LogError($"视觉系统一次码垛结束,未知返回代码"); } } else { _logger.LogError($"视觉系统一次码垛结束,返回数据格式错误"); } } /// /// 收到视觉系统复位结果 /// public void GetResetResult(TcpVisionEntity entity, string id) { if (entity.DataLength == 0x01) { if (entity.DataBytes[0] == 0x00) { _logger.LogInformation($"视觉系统码垛结束,复位正常"); } else if (entity.DataBytes[0] == 0x01) { _logger.LogError($"视觉系统码垛结束,复位异常"); } else { _logger.LogError($"视觉系统码垛结束,未知返回代码"); } } else { _logger.LogError($"视觉系统码垛结束,返回数据格式错误"); } } /// /// 人工异常处理完成并更新计数 /// public void GetManualExceptionDealDone(TcpVisionEntity entity, string id) { _tcpServer.SendReplyGetManualException(id); } } }