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.

547 lines
24 KiB
C#

7 months ago
using Masuit.Tools;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Newtonsoft.Json;
using SlnMesnac.Config;
using SlnMesnac.Extensions;
using SlnMesnac.Model.domain;
using SlnMesnac.Model.dto.taskType;
using SlnMesnac.Model.dto.webapi;
using SlnMesnac.Plc;
using SlnMesnac.Rfid;
using SlnMesnac.Serilog;
using SlnMesnac.WCS.Global;
using SlnMesnac.WCS.Library;
using SlnMesnac.WCS.WCS;
using SqlSugar;
using System.Timers;
namespace SlnMesnac.WCS
{
public class MainCentralControl
{
private IHost host;
//删除日志定时器
private System.Timers.Timer deleteLogTimer;
//plc心跳交互定时器
private System.Timers.Timer plcHeartTimer;
// 新增一个标志位,用于表示方法是否正在执行
private bool isExecuting = false;
private readonly SerilogHelper _logger;
private IServiceProvider serviceProvider;
private ISqlSugarClient SqlSugarClient;
private BearAgv bearAgv;
7 months ago
#region 变量定义
public List<PlcAbsractFactory> plcList;
//容器里面的读写器集合
public List<RfidAbsractFactory> rfidList;
#endregion 变量定义
7 months ago
/// <summary>
/// 构造函数
/// </summary>
/// <param name="host"></param>
public MainCentralControl(IHost _host)
{
host = _host;
SqlSugarClient = host.Services.GetRequiredService<ISqlSugarClient>();
_logger = host.Services.GetRequiredService<SerilogHelper>();
plcList = host.Services.GetRequiredService<List<PlcAbsractFactory>>();
rfidList = host.Services.GetRequiredService<List<RfidAbsractFactory>>();
InitCacheData();
}
/// <summary>
/// 加载缓存数据
/// </summary>
private void InitCacheData()
{
//加载plc地址信息
StaticData.BaseConfigList = SqlSugarClient.Queryable<BaseConfigInfo>().ToList();
StaticData.WmsMachineInfos = SqlSugarClient.Queryable<WmsMachineInfo>().ToList();
StaticData.WcsAgvCodeMaps = SqlSugarClient.Queryable<WcsAgvCodeMap>().ToList();
#region 定时删除日志
//DeleteLogsLogic();
//deleteLogTimer = new System.Timers.Timer(24 * 60 * 60 * 1000);
//deleteLogTimer.Elapsed += OnTimedEvent;
//deleteLogTimer.AutoReset = true;
//deleteLogTimer.Enabled = true;
#endregion 定时删除日志
#region 定时写入心跳
plcHeartTimer = new System.Timers.Timer(1000);
plcHeartTimer.Elapsed += OnTimedPlcHeartEvent;
plcHeartTimer.AutoReset = true;
plcHeartTimer.Enabled = true;
#endregion 定时写入心跳
}
/// <summary>
/// 启动程序
/// </summary>
public void Start()
{
StartCheckStatus();
GetAgvStatus();
7 months ago
DeleteTaskLogic();
//根据条件创建任务
CreateTaskByRecord createTaskByRecord = new CreateTaskByRecord(host.Services);
createTaskByRecord.StartPoint();
_logger.Agv("WCS调度程序启动成功!");
//背负式agv执行任务
bearAgv = new BearAgv(host.Services);
7 months ago
bearAgv.StartPoint();
}
#region
/// <summary>
/// 检查设备状态自动重连
/// </summary>
private void StartCheckStatus()
{
//数据库里面的读写器集合,仅用来更新状态
List<BaseRfidInfo> rfidDbList = SqlSugarClient.Queryable<BaseRfidInfo>().ToList();
List<WcsBaseEquip> agvEquipList = SqlSugarClient.Queryable<WcsBaseEquip>().Where(it => it.EquipType == 1).ToList();
Task.Run(async () =>
{
while (true)
{
try
{
#region PLC状态
foreach (var plc in plcList)
{
PlcConfig? plcConfig = host.Services.GetService<AppConfig>().plcConfig.FirstOrDefault(x => x.plcKey == plc.ConfigKey);
if (plc != null && plc.IsConnected)
{
plc.IsConnected = plc.readHeartByAddress("M100"); //DB100.DBX0.0
if (!plc.IsConnected)
{
plc.IsConnected = await plc.ConnectAsync(plcConfig.plcIp, plcConfig.plcPort);
}
}
else
{
if (plcConfig != null)
{
bool result = await plc.ConnectAsync(plcConfig.plcIp, plcConfig.plcPort);
plc.IsConnected = result;
}
}
}
#endregion PLC状态
#region RFID状态
foreach (var rfid in rfidList)
{
if (rfid.ConfigKey == "2#Transplant")
{
}
bool status = rfid.GetOnlineStatus();
if (!status)
{
status = await rfid.ConnectAsync(rfid.ip, rfid.port);
}
//状态同步数据库
BaseRfidInfo? rfidDb = rfidDbList.FirstOrDefault(x => x.EquipIp == rfid.ip);
if (rfidDb != null)
{
rfidDb.IsOnline = status ? 1 : 0;
}
}
SqlSugarClient.Updateable(rfidDbList).UpdateColumns(it => new { it.IsOnline }).ExecuteCommand();
7 months ago
#endregion RFID状态
//色粉库位有未取走色粉声光报警
7 months ago
ListeningTonerLocation();
}
catch (Exception ex)
{
_logger.Error($"监听设备状态异常:{ex.Message}");
}
await Task.Delay(1000 * 10);
}
});
}
/// <summary>
/// 获取agv状态
/// </summary>
private void GetAgvStatus()
{
List<WcsBaseEquip> agvEquipList = SqlSugarClient.Queryable<WcsBaseEquip>().Where(it => it.EquipType == 1).ToList();
Task.Run(async () =>
{
while (true)
{
try
{
//实时获取agv状态
MessageSynchronousLogic(agvEquipList);
}
catch (Exception ex)
{
_logger.Error($"获取agv状态异常:{ex.Message}");
}
await Task.Delay(1000 * 30);
}
});
}
7 months ago
#endregion
/// <summary>
/// 监听色粉库位是否色粉未从料想取出有的话通知plc声光报警
/// </summary>
private void ListeningTonerLocation()
{
try
{
PlcAbsractFactory? workShop3Plc = plcList.FirstOrDefault(x => x.ConfigKey == "workShop3Plc");
// 构建查询并执行 Any 方法
bool hasRecords = SqlSugarClient.Queryable<WcsBaseEquip>()
.InnerJoin<WmsPalletInfo>((wbe, wpi) => wbe.ContainerCode == wpi.PalletInfoCode
&& !string.IsNullOrEmpty(wbe.ContainerCode)
&& wpi.TonerFlag == 1 && wbe.EquipType == 7).Any();
if (workShop3Plc != null && workShop3Plc.IsConnected)
{
if (hasRecords)
{
workShop3Plc.writeBoolByAddress(StaticData.GetPlcAddress("3#异常色粉未处理提示"), true);
}
else
{
workShop3Plc.writeBoolByAddress(StaticData.GetPlcAddress("3#异常色粉未处理提示"), false);
}
}
}
catch (Exception ex)
{
_logger.Error($"监听色粉库位异常:{ex.Message}");
}
}
/// <summary>
/// agv状态实时刷新
/// </summary>
private void MessageSynchronousLogic(List<WcsBaseEquip> agvEquipList)
{
try
{
List<WcsAgvStatus> agvList = SqlSugarClient.Queryable<WcsAgvStatus>().ToList();
var data = new
{
reqCode = StaticData.SnowId.NextId(),
mapCode = "AA",
};
string result = HttpHelper.SendPostMessage(agvEquipList.First().ServerIp, agvEquipList.First().ServerPort, "rcms-dps/rest/queryAgvStatus", data.ToJsonString());
var reponse = JsonConvert.DeserializeObject<AgvStatusDto>(result);
if (reponse != null && reponse.code == "0")
{
foreach (var agv in agvList)
{
CardStatus? agvCard = reponse.data.FirstOrDefault(x => x.robotCode == agv.RobotCode);
if (agvCard != null)
{
agv.Battery = agvCard.battery;
agv.ExclType = agvCard.exclType;
agv.Online = agvCard.online;
agv.PosX = agvCard.posX;
agv.PosY = agvCard.posY;
agv.RobotDir = agvCard.robotDir;
agv.Speed = agvCard.speed;
agv.Status = agvCard.status;
WcsAgvCodeMap? wcsAgvCodeMap = StaticData.WcsAgvCodeMaps.FirstOrDefault(x => x.AgvCode == long.Parse(agv.Status));
if (wcsAgvCodeMap != null)
{
agv.StatusDetail = wcsAgvCodeMap.agv_display_content;
}
agv.Stop = agvCard.stop;
agv.Timestamp = DateTime.Now;
}
}
//TaskHandleByAgv(agvList);
7 months ago
SqlSugarClient.Updateable(agvList).ExecuteCommand();
}
}
catch (Exception ex)
{
7 months ago
_logger.Info($"同步agv状态信息异常:{ex.Message},{ex.StackTrace.ToJsonString()}");
7 months ago
}
}
public void TaskHandleByAgv(List<WcsAgvStatus> agvInfos)
{
if (agvInfos == null || agvInfos.Count == 0)
{
return;
}
var targetPoint = new { X = 142430, Y = 108540 };
var agvInfo = agvInfos.OrderBy(agv => CalculateDistance(double.Parse(agv.PosX), double.Parse(agv.PosY), targetPoint.X, targetPoint.Y)).First();
_logger.Agv($"=====>>>>离接驳位最近的设备为:{agvInfo.RobotCode}");
//判断当前小车有没有送空箱任务,下发PLC触发取料信号
if (agvInfo.StatusDetail.Contains("待机模式") || agvInfo.StatusDetail.Contains("空闲"))
{
_logger.Agv($"当前空闲车辆{agvInfo.RobotCode};离取料点最近优先进行取料锁定线体下发PLC触发取料信号");
}
else
{
_logger.Agv($"当前没有空闲小车离取料点最近,等线体空闲后进行取料");
var flag = bearAgv.workShop3Plc.readInt16ByAddress(StaticData.GetPlcAddress("3#线体忙碌状态"));
if (flag == 0)
{
//线体空闲了,可以锁定进行取料
_logger.Agv($"线体空闲了,可以锁定使用远处小车进行取料");
}
}
}
private static double CalculateDistance(double x1, double y1, double x2, double y2)
{
return Math.Sqrt(Math.Pow(x2 - x1, 2) + Math.Pow(y2 - y1, 2));
}
7 months ago
#region 删除任务 -- 已经下发的任务需要通知agv取消
/// <summary>
/// 定时监测是否有待删除任务,完成删除
/// </summary>
private void DeleteTaskLogic()
{
Task.Run(() =>
{
List<WcsBaseEquip> agvEquipList = SqlSugarClient.Queryable<WcsBaseEquip>().Where(it => it.EquipType == 1).ToList();
while (true)
{
try
{
List<WcsTask> wcsTasks = SqlSugarClient.Queryable<WcsTask>().Where(t => t.IsDelete == 1 || t.IsDelete == 2).ToList();
foreach (var item in wcsTasks)
{
WcsBaseEquip agvEquip = agvEquipList.First();
if (!string.IsNullOrEmpty(item.TaskCode))
{
var cancelTask = new
{
reqCode = StaticData.SnowId.NextId().ToString(),
taskCode = item.TaskCode
};
string reponse = HttpHelper.SendPostMessage(agvEquip.ServerIp, agvEquip.ServerPort, "rcms/services/rest/hikRpcService/cancelTask", cancelTask.ToJsonString());
var result = JsonConvert.DeserializeObject<ReponseMessage>(reponse);
if (result != null && result.code == "0")
{
#region 解锁起点库位与终点库位
//根据任务类型,如果是大料箱解锁wmsbaseLocation
List<int> bigType = new List<int>() { 1, 3, 8, 10 };
if (bigType.Contains(item.TaskType))
{
List<WmsBaseLocation>? wmsBaseLocations = SqlSugarClient.Queryable<WmsBaseLocation>().Where(x => x.LocationCode == item.CurrPointNo || x.LocationCode == item.EndPointNo).ToList();
if (wmsBaseLocations != null && wmsBaseLocations.Count > 0)
{
wmsBaseLocations.ForEach(x => x.LocationStatus = 0);
SqlSugarClient.Updateable(wmsBaseLocations).ExecuteCommand();
}
}
else //如果是小料箱解锁wcsbaseEquip
{
List<WcsBaseEquip> equips = SqlSugarClient.Queryable<WcsBaseEquip>().Where(x => x.AgvPositionCode == item.CurrPointNo || x.AgvPositionCode == item.EndPointNo).ToList();
if (equips != null && equips.Count > 0)
{
equips.ForEach(x => x.EquipStatus = 0);
SqlSugarClient.Updateable(equips).ExecuteCommand();
}
}
#endregion
SqlSugarClient.Deleteable<WcsTask>(item).ExecuteCommand();
_logger.Agv($"任务id:{item.Id},任务名:{item.TaskName}已删除");
//
if (!string.IsNullOrEmpty(item.NextPointNo) && item.TaskStatus >= 2 && item.IsDelete == 2) //有执行任务的agv编号,并且需要去异常库位
{
WmsBaseLocation? endLocation = SqlSugarClient.Queryable<WmsBaseLocation>().First(x => x.LocationCode == "3066" && x.LocationStatus == 0 && string.IsNullOrEmpty(x.ContainerCode));
if (endLocation != null)
{
WcsTask task = new WcsTask();
task.TaskType = StaticTaskType.ExceptionTask;
task.CurrPointNo = "ExceptionStart";
task.NextPointNo = item.NextPointNo; //指定agv
task.EndPointNo = endLocation.AgvPositionCode;
task.TaskStatus = 0;
task.CreatedTime = DateTime.Now;
task.CreatedBy = "wcs";
task.TaskName = "前往异常库位移库任务";
task.PalletInfoCode = item.PalletInfoCode;
SqlSugarClient.AsTenant().BeginTran();
try
{
int id = SqlSugarClient.Insertable(task).ExecuteReturnIdentity();
WcsTaskLog wcsTaskLog = CoreMapper.Map<WcsTaskLog>(task);
wcsTaskLog.Id = id;
//锁定库位
endLocation.LocationStatus = 1;
SqlSugarClient.Updateable(endLocation).ExecuteCommand();
SqlSugarClient.Insertable(wcsTaskLog).ExecuteCommand();
SqlSugarClient.AsTenant().CommitTran();
_logger.Agv($"生成{task.TaskName},起点:{task.CurrPointNo},终点:{task.EndPointNo}");
}
catch (Exception ex)
{
_logger.Error($"生成移库任务提交事务异常:{ex.Message}");
SqlSugarClient.AsTenant().RollbackTran();
}
}
else
{
_logger.Error("想要创建异常库位任务,但是异常库位不可用或已经有托盘");
}
}
}
else
{
_logger.Error($"任务id:{item.Id},任务名:{item.TaskName}删除失败agv接口回复:{result.message}");
}
}
else
{
SqlSugarClient.Deleteable<WcsTask>(item).ExecuteCommand();
_logger.Agv($"任务id:{item.Id},任务名:{item.TaskName}已删除");
}
}
}
catch (Exception ex)
{
_logger.Error($"任务删除异常:{ex.Message}");
}
Thread.Sleep(2000);
}
});
}
#endregion 删除任务 -- 已经下发的任务需要通知agv
#region 定时删除日志处理
/// <summary>
/// 定时器触发的事件处理方法
/// </summary>
/// <param name="source"></param>
/// <param name="e"></param>
private void OnTimedEvent(Object source, ElapsedEventArgs e)
{
DeleteLogsLogic();
}
/// <summary>
/// 清除日志文件
/// </summary>
private void DeleteLogsLogic()
{
try
{
// 获取当前程序运行目录下的 Log 文件夹
string logPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs");
// 检查 Log 文件夹是否存在
if (!Directory.Exists(logPath))
{
// 如果 Log 文件夹不存在,可以选择记录日志或者直接返回
Console.WriteLine($"Log 文件夹 {logPath} 不存在。");
return;
}
// 获取 Log 文件夹下的所有子文件夹
string[] logDirectories = Directory.GetDirectories(logPath);
foreach (string directoryPath in logDirectories)
{
string directoryName = Path.GetFileName(directoryPath);
if (!string.IsNullOrEmpty(directoryName) && DateTime.TryParse(directoryName, out DateTime directoryDate))
{
// 检查文件夹日期是否早于当前日期三天之前
if (directoryDate < DateTime.Now.AddDays(-3))
{
try
{
// 删除符合条件的文件夹
Directory.Delete(directoryPath, true);
Console.WriteLine($"已删除文件夹: {directoryPath}");
}
catch (Exception ex)
{
// 记录删除文件夹时的异常信息
Console.WriteLine($"删除文件夹 {directoryPath} 时出错: {ex.Message}");
}
}
}
}
}
catch (Exception ex)
{
// 记录整体操作的异常信息
Console.WriteLine($"执行删除日志逻辑时出错: {ex.Message}");
}
}
#endregion
#region 定时plc发送心跳脉冲
private bool heartFlag = true;
/// <summary>
/// 定时器触发的事件处理方法
/// </summary>
/// <param name="source"></param>
/// <param name="e"></param>
private void OnTimedPlcHeartEvent(Object source, ElapsedEventArgs e)
{
// 检查方法是否正在执行
if (isExecuting)
{
return;
}
try
{
// 设置标志位为 true表示方法正在执行
isExecuting = true;
PlcAbsractFactory? plc3 = plcList.FirstOrDefault(x => x.ConfigKey == "workShop3Plc");
PlcAbsractFactory? plc2 = plcList.FirstOrDefault(x => x.ConfigKey == "workShop2Plc");
if (plc3 != null && plc3.IsConnected)
{
plc3.writeBoolByAddress("DB100.DBX84.0", heartFlag);
heartFlag = !heartFlag;
}
if (plc2 != null && plc2.IsConnected)
{
plc2.writeBoolByAddress("DB100.DBX24.0", heartFlag);
heartFlag = !heartFlag;
}
}
finally
{
isExecuting = false;
}
}
#endregion
}
}