You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

576 lines
30 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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
{
/// <summary>
/// 视觉系统业务类
/// </summary>
public class VisionBusiness
{
private ILogger<VisionBusiness> _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<VisionBusiness> 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<VisionBusiness> 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;
}
/// <summary>
/// AMR就绪请求视觉开始工作
/// </summary>
public void RequestVisionStartWork(StackState state, int count, string id)
{
_tcpServer.SendAMRRequestVisionStartWork(new byte[2] { (byte)state, (byte)count }, id);
}
/// <summary>
/// 码垛结束,请求视觉系统复位
/// </summary>
public void RequestVisionReplace(string ip)
{
_tcpServer.SendStackOverRequestVisionSysReplace(ip);
VDCount = 1;
VACount = 0;
TotalJudge = false;
}
/// <summary>
/// 上位机调度系统每隔固定时间,请求视觉系统状态
/// </summary>
public void VisionStateRequest(TcpVisionEntity entity, string id)
{
if (entity.DataLength == 2)
{
}
//Thread.Sleep(1000);
//SendRequestVisionSysState(id);
}
/// <summary>
/// 接受开始工作状态
/// </summary>
/// <param name="entity"></param>
/// <param name="id"></param>
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}");
}
}
/// <summary>
/// 收到堆筐请求
/// </summary>
/// <param name="entity"></param>
/// <param name="id"></param>
/// <exception cref="Exception"></exception>
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;
/// <summary>
/// 接受码垛结果,进行下一次码垛判断
/// </summary>
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($"视觉系统一次码垛结束,返回数据格式错误");
}
}
/// <summary>
/// 收到视觉系统复位结果
/// </summary>
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($"视觉系统码垛结束,返回数据格式错误");
}
}
/// <summary>
/// 人工异常处理完成并更新计数
/// </summary>
public void GetManualExceptionDealDone(TcpVisionEntity entity, string id)
{
_tcpServer.SendReplyGetManualException(id);
}
}
}