123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591 |
- using HslCommunication;
- using ProjectManagementSystem.Common.Config;
- using ProjectManagementSystem.Common.Extenions;
- using ProjectManagementSystem.Common.Logger;
- using ProjectManagementSystem.Common.Models;
- using ProjectManagementSystem.Common.WebApi;
- using System;
- using System.Collections.Concurrent;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading;
- using System.Threading.Tasks;
- namespace ProjectManagementSystem.Common.Core
- {
- public class PackControl
- {
- private static PackControl m_instance;
- private bool checkOnline = true;
- private bool firstStart = true;
- public ConcurrentDictionary<string, PackStation> StationDictionary { get; set; } = new ConcurrentDictionary<string, PackStation>();
- public static PackControl Instance
- {
- get
- {
- if (m_instance == null)
- {
- m_instance = new PackControl();
- if (!m_instance.Initialize())
- {
- m_instance = null;
- }
- }
- return m_instance;
- }
- }
- private PackControl()
- {
- }
- private bool Initialize()
- {
- try
- {
- var configList = ExcelConfig.Instance.RouteConfigList;
- for (int i = 0; i < configList.Count; i++)
- {
- var config = configList[i];
- PackStation data = StationDictionary.ContainsKey(config.LocationCode) ? StationDictionary[config.LocationCode] : new PackStation();
- data.Id = config.LocationCode;
- data.routeConfig = config;
- data.请求进入导航点 = config.LocationCode.GetLocationMember("AGV请求进入导航点").ToValue<int>();
- data.EnteringEdges = config.LocationCode.GetLocationMember("AGV正在进入路段").ToValueArray<int>();
- data.EnteringVertexs = config.LocationCode.GetLocationMember("AGV正在进入导航点").ToValueArray<int>();
- data.工位导航点 = config.GraphVertex;
- data.LeavingEdges = config.LocationCode.GetLocationMember("AGV正在离开路段").ToValueArray<int>();
- data.LeavingVertexs = config.LocationCode.GetLocationMember("AGV正在离开导航点").ToValueArray<int>();
- StationDictionary.AddOrUpdate(data.Id, data, (key, value) => data);
- }
- Task.Factory.StartNew(UserThread);
- Task.Factory.StartNew(UserThread2);
- return true;
- }
- catch (Exception ex)
- {
- CLog.Instance.SystemLog.WriteException("PackControl Initialize", ex);
- }
- return false;
- }
- private void TaskCustomProc()
- {
- //return;
- //AGV状态
- //0:空闲,
- //1:配送中,
- //3:完成
- //4:充电中
- //5:发生异常
- //默认值:65535
- //return;
- var agvDataList1 = PmsApi.GetAllCarrier();
- if (agvDataList1 == null) return;
- var agvDataList = checkOnline ? agvDataList1.Where(d => d.Online) : agvDataList1.Where(d => true);
- var configList = ExcelConfig.Instance.RouteConfigList;
- for (int i = 0; i < configList.Count; i++)
- {
- var config = configList[i];
- if (!config.PackTaskAddCooled)
- {
- //CLog.Instance.TaskLog.WriteInfo($"{config.LocationCode} 限制添加任务(未冷却)");
- continue;
- }
- //已到位的AGV可以出发后任务模板
- var agvData = agvDataList?.FirstOrDefault(d => /*d.Status == 0 && */d.GraphVertex == config.GraphVertex);
- if (agvData != null)
- {
- var taskList = PmsApi.GetTaskList();
- if (taskList != null
- && taskList.FirstOrDefault(d => d.Carrier == agvData.AgvID || d.BindingAGVNumber == agvData.AgvID) == null)
- {
- //添加任务
- string nextLocation = GetPlcNextLocation(config, agvData.AgvID, out string templateName);
- if (!string.IsNullOrEmpty(nextLocation)
- && !string.IsNullOrEmpty(templateName))
- {
- AddPackTask(templateName, agvData.AgvID, config.LocationCode, nextLocation);
- }
- }
- }
- //工位前的任务导航点:可根据前任务导航点,匹配不一样的任务模板
- var vertexArray = config.LocationCode.GetLocationMember("前任务导航点").ToValueArray<int>();
- for (int index = 0; index < vertexArray.Length; index++)
- {
- int vertex = vertexArray[index];
- agvData = agvDataList?.FirstOrDefault(d => d.Status == 0 && vertex == d.GraphVertex);
- if (agvData != null)
- {
- //判断是否直通
- var passToNextLocation = GetPlcAllowToPassNextLocation(config, agvData.AgvID);
- if (!string.IsNullOrEmpty(passToNextLocation))
- {
- string preTemplateName = config.LocationCode.GetLocationMember("直通任务模板").GetArrayValue<string>(index);
- AddPackTask(preTemplateName, agvData.AgvID, config.LocationCode, passToNextLocation);
- }
- else
- {
- string preTemplateName = config.LocationCode.GetLocationMember("前任务模板").GetArrayValue<string>(index);
- AddPackTask(preTemplateName, agvData.AgvID, config.LocationCode);
- }
- }
- }
- }
- }
- private string GetPlcAllowToPassNextLocation(RouteConfig config, int agvId)
- {
- string plcAllowToPass = config.LocationCode.GetLocationMember("PLC允许通过");
- if (string.IsNullOrEmpty(plcAllowToPass)) return null;
- var plc = config.PlcReadWrite;
- var read = plc.ReadBool(plcAllowToPass);
- if (!read.IsSuccess && !read.Content)
- {
- return null;
- }
- string nextLocation = config.LocationCode.GetLocationMember("下个工位");
- if (string.IsNullOrEmpty(nextLocation))
- {
- CLog.Instance.GetAgvLog(agvId).WriteInfo($"{config.LocationCode} 没配置下个工位");
- return null;
- }
- return nextLocation.ToValueArray<string>()[0];
- }
- private string GetPlcNextLocation(RouteConfig config, int agvId, out string templateName)
- {
- if (!checkOnline)
- {
- //模拟测试
- templateName = config.LocationCode.GetLocationMember("后任务模板");
- return config.LocationCode.GetLocationMember("下个工位").ToValueArray<string>()[0];
- }
- templateName = null;
- if (string.IsNullOrEmpty(config.PlcIpAddr))
- {
- return null;
- }
- //var plc放行 = config.LocationCode.GetLocationMember("PLC放行");
- //产生任务的地址改成允许上升地址
- var plc放行 = config.LocationCode.GetLocationMember("PLC允许上升");
- if (string.IsNullOrEmpty(plc放行))
- {
- plc放行 = config.LocationCode.GetLocationMember("PLC放行");
- if (string.IsNullOrEmpty(plc放行))
- {
- return null;
- }
- }
- var plc = config.PlcReadWrite;
- var read = plc.ReadBool(plc放行);
- if (!read.IsSuccess || !read.Content)
- {
- return null;
- }
- //已有放行信号
- string nextLocation = config.LocationCode.GetLocationMember("下个工位");
- if (string.IsNullOrEmpty(nextLocation))
- {
- CLog.Instance.GetAgvLog(agvId).WriteInfo($"{config.LocationCode} 没配置下个工位");
- return null;
- }
- templateName = config.LocationCode.GetLocationMember("后任务模板");
- nextLocation = nextLocation.ToValueArray<string>()[0];
- var plc返修 = config.LocationCode.GetLocationMember("PLC返修");
- if (string.IsNullOrEmpty(plc返修))
- {
- //没有配置返修地址,直接去下个工位
- CLog.Instance.GetAgvLog(agvId).WriteInfo($"{config.LocationCode} 没配置返修地址,去默认下个工位 {nextLocation} (读取{plc放行} = {read.Content})");
- return nextLocation;
- }
- read = plc.ReadBool(plc返修);
- if (!read.IsSuccess)
- {
- return null;
- }
- if (read.Content == false)
- {
- //不需要返修去下个工位
- CLog.Instance.GetAgvLog(agvId).WriteInfo($"{config.LocationCode} 不需要返修,去默认下个工位 {nextLocation} (读取{plc返修} = {read.Content})");
- return nextLocation;
- }
- //已有需要返修的信号
- templateName = config.LocationCode.GetLocationMember("返修任务模板");
- if (string.IsNullOrEmpty(templateName))
- {
- CLog.Instance.GetAgvLog(agvId).WriteInfo($"{config.LocationCode} 需要返修,但没配置返修任务模板");
- return null;
- }
- //需要返修去固定返修位置
- string ngLocation = config.LocationCode.GetLocationMember("返修工位");
- if (string.IsNullOrEmpty(ngLocation))
- {
- CLog.Instance.GetAgvLog(agvId).WriteInfo($"{config.LocationCode} 需要返修,但没配置返修工位");
- return null;
- }
- CLog.Instance.GetAgvLog(agvId).WriteInfo($"{config.LocationCode} 需要返修,去返修工位 {ngLocation} (读取{plc返修} = {read.Content})");
- return ngLocation;
- }
- private bool AddPackTask(string templateName, int agvId, params string[] locations)
- {
- //添加任务
- if (string.IsNullOrEmpty(templateName)) return false;
- var taskList = PmsApi.GetTaskList();
- if (taskList == null) return false;
- var taskData = taskList.FirstOrDefault(d => d.Carrier == agvId || d.BindingAGVNumber == agvId);
- if (taskData != null)
- {
- //指定AGV已经有任务,就不添任务
- return false;
- }
- MidTaskInfoEx data = new MidTaskInfoEx();
- data.TemplateName = templateName;
- for (int i = 0; i < locations.Length; i++)
- {
- data.ParametersDic.Add($"U{i + 1}", locations[i]);
- }
- data.AgvType = "0";
- data.AgvId = agvId;
- data.Priority = 0;
- data.CreateTime = DateTime.Now;
- bool result = PmsApi.TaskAdd(data);
- if (result)
- {
- for (int i = 0; i < locations.Length; i++)
- {
- var location = locations[i];
- var config = ExcelConfig.Instance.GetRouteConfig(location);
- if (config != null)
- {
- config.LastPackTaskAddTime = DateTime.Now;
- }
- }
- }
- return result;
- }
- private void UpdateStationDict(bool[] forceWrite)
- {
- var agvDataList1 = PmsApi.GetAllCarrier();
- if (agvDataList1 == null) return;
- var agvDataList = checkOnline ? agvDataList1.Where(d => d.Online) : agvDataList1.Where(d => true);
- DateTime start = DateTime.Now;
- Parallel.ForEach(StationDictionary.Values, delegate (PackStation data, ParallelLoopState loopState)
- {
- try
- {
- var agvData = agvDataList.FirstOrDefault(d => data.请求进入导航点 > 0 && d.GraphVertex == data.请求进入导航点);
- bool result = agvData != null;
- data.请求进入AGV信息 = result ? $"({agvData.AgvID}# 路段:{agvData.GraphEdge} 导航点:{agvData.GraphVertex})" : null;
- if (data.请求进入 != result || forceWrite[0])
- {
- data.请求进入 = result;
- HandleAgvToPlc(data.routeConfig, "AGV请求进入", data.请求进入, data.请求进入AGV信息);
- }
- var agvDataAtStation = agvDataList.FirstOrDefault(d => data.工位导航点 > 0 && d.GraphVertex == data.工位导航点);
- result = agvDataAtStation != null;
- data.到位AGV信息 = result ? $"({agvDataAtStation.AgvID}# 路段:{agvDataAtStation.GraphEdge} 导航点:{agvDataAtStation.GraphVertex})" : null;
- if (data.到位 != result || forceWrite[1])
- {
- data.到位 = result;
- HandleAgvToPlc(data.routeConfig, "AGV到位", data.到位, data.到位AGV信息);
- }
- var agvDataEntering = agvDataList.FirstOrDefault(d =>
- (d.GraphEdge > 0 && data.EnteringEdges.Length > 0 && data.EnteringEdges.Contains(d.GraphEdge)) ||
- (d.GraphVertex > 0 && data.EnteringVertexs.Length > 0 && data.EnteringVertexs.Contains(d.GraphVertex)));
- result = agvDataEntering != null;
- data.正在进入AGV信息 = result ? $"({agvDataEntering.AgvID}# 路段:{agvDataEntering.GraphEdge} 导航点:{agvDataEntering.GraphVertex})" : null;
- if (data.正在进入 != result || firstStart)
- {
- data.正在进入 = result;
- HandleAgvToPlc(data.routeConfig, "AGV正在进入", data.正在进入, data.正在进入AGV信息);
- }
- if (forceWrite[2])
- {
- data.正在进入 = result;
- if (data.正在进入)
- {
- HandleAgvToPlc(data.routeConfig, "AGV正在进入", data.正在进入, data.正在进入AGV信息);
- }
- else
- {
- //清零信号需要已经离开前面的导航点
- if (data.请求进入 == false)
- {
- HandleAgvToPlc(data.routeConfig, "AGV正在进入", data.正在进入, data.正在进入AGV信息);
- }
- }
- }
- var agvDataLeaving = agvDataList.FirstOrDefault(d =>
- (d.GraphEdge > 0 && data.LeavingEdges.Length > 0 && data.LeavingEdges.Contains(d.GraphEdge)) ||
- (d.GraphVertex > 0 && data.LeavingVertexs.Length > 0 && data.LeavingVertexs.Contains(d.GraphVertex)));
- result = agvDataLeaving != null;
- data.正在离开AGV信息 = result ? $"({agvDataLeaving.AgvID}# 路段:{agvDataLeaving.GraphEdge} 导航点:{agvDataLeaving.GraphVertex})" : null;
- if (data.正在离开 != result || firstStart)
- {
- data.正在离开 = result;
- HandleAgvToPlc(data.routeConfig, "AGV正在离开", data.正在离开, data.正在离开AGV信息);
- }
- if (forceWrite[3])
- {
- data.正在离开 = result;
- if (data.正在离开)
- {
- HandleAgvToPlc(data.routeConfig, "AGV正在离开", data.正在离开, data.正在离开AGV信息);
- }
- else
- {
- //清零信号需要已经离开前面的导航点
- if (data.到位 == false)
- {
- HandleAgvToPlc(data.routeConfig, "AGV正在离开", data.正在离开, data.正在离开AGV信息);
- }
- }
- }
- //LogicBit 9 上升到位状态 512
- //LogicBit 10 下降到位状态 1024
- result = agvDataAtStation != null ? agvDataAtStation.LogicBits.GetBitVaule(9) : false;
- data.上升到位AGV信息 = result ? $"({agvDataAtStation.AgvID}# 路段:{agvDataAtStation.GraphEdge} 导航点:{agvDataAtStation.GraphVertex}) LogicBits:{agvDataAtStation.LogicBits}" : null;
- if (data.AGV上升到位 != result || forceWrite[4])
- {
- data.AGV上升到位 = result;
- HandleAgvToPlc(data.routeConfig, nameof(data.AGV上升到位), data.AGV上升到位, data.上升到位AGV信息);
- }
- result = agvDataAtStation != null ? agvDataAtStation.LogicBits.GetBitVaule(10) : false;
- data.下降到位AGV信息 = result ? $"({agvDataAtStation.AgvID}# 路段:{agvDataAtStation.GraphEdge} 导航点:{agvDataAtStation.GraphVertex}) LogicBits:{agvDataAtStation.LogicBits}" : null;
- if (data.AGV下降到位 != result || forceWrite[5])
- {
- data.AGV下降到位 = result;
- HandleAgvToPlc(data.routeConfig, nameof(data.AGV下降到位), data.AGV下降到位, data.下降到位AGV信息);
- }
- var agvId = agvDataAtStation == null ? 0 : agvDataAtStation.AgvID;
- if (data.AGV编号 != agvId || forceWrite[1])
- {
- data.AGV编号 = agvId;
- HandleAgvToPlc(data.routeConfig, nameof(data.AGV编号), (ushort)data.AGV编号, data.到位AGV信息);
- }
- var alarmId = agvDataAtStation == null || agvDataAtStation.AlarmList == null || agvDataAtStation.AlarmList.Count == 0 ?
- 0 : agvDataAtStation.AlarmList.OrderByDescending(d => d.alarmLevel).FirstOrDefault().alarmId;
- if (data.AGV报警代码 != alarmId || forceWrite[1])
- {
- data.AGV报警代码 = alarmId;
- HandleAgvToPlc(data.routeConfig, nameof(data.AGV报警代码), (ushort)data.AGV报警代码, data.到位AGV信息);
- }
- data.异常消息 = null;
- }
- catch (Exception ex)
- {
- data.异常消息 = ex.Message;
- }
- data.耗时 = Math.Round((DateTime.Now - start).TotalMilliseconds);
- });
- }
- //private void HandleAgvToPlc(RouteConfig routeConfig, string plcAddrDesc, bool agvValue, string agvInfo)
- //{
- // string plcAddr = routeConfig.LocationCode.GetLocationMember(plcAddrDesc);
- // if (!string.IsNullOrEmpty(plcAddr)
- // && routeConfig.PlcReadWrite != null
- // && routeConfig.PlcConnected)
- // {
- // var readResult = routeConfig.PlcReadWrite.ReadBool(plcAddr);
- // if (readResult.IsSuccess
- // && readResult.Content != agvValue)
- // {
- // var result = routeConfig.PlcReadWrite.Write(plcAddr, agvValue);
- // string strLog = $"{routeConfig.LocationCode} {routeConfig.PlcIpAddr} {plcAddrDesc}:{plcAddr} = {agvValue} 写入{result.IsSuccess.ToChineseString()} {agvInfo}";
- // CLog.Instance.TaskLog.WriteInfo(strLog);
- // }
- // }
- //}
- private void HandleAgvToPlc<T>(RouteConfig routeConfig, string plcAddrDesc, T agvValue, string agvInfo)
- {
- string plcAddr = routeConfig.LocationCode.GetLocationMember(plcAddrDesc);
- if (!string.IsNullOrEmpty(plcAddr)
- && routeConfig.PlcReadWrite != null
- && routeConfig.PlcConnected)
- {
- var readResult = routeConfig.PlcReadWrite.ReadValue(plcAddr, agvValue, out OperateResult<T> operateResult);
- if (!readResult)
- {
- var result = routeConfig.PlcReadWrite.WriteValue(plcAddr, agvValue);
- string strLog = $"{routeConfig.LocationCode} {routeConfig.PlcIpAddr} {plcAddrDesc}:{plcAddr} = {agvValue} 写入{result.IsSuccess.ToChineseString()} {agvInfo}";
- CLog.Instance.TaskLog.WriteInfo(strLog);
- }
- }
- }
- private void UserThread()
- {
- CLog.Instance.SystemLog.WriteDebug($"PackControl已启动");
- uint couter = 0;
- bool[] forces = new bool[6];
- while (true)
- {
- try
- {
- //错开刷新
- var mod = couter % 10;
- if (mod >= 0 && mod <= forces.Length)
- {
- for (int i = 0; i < forces.Length; i++)
- {
- forces[i] = (mod == i);
- }
- }
- DateTime start = DateTime.Now;
- UpdateStationDict(forces);
- System.Diagnostics.Trace.WriteLine($"UpdateStationDict {string.Join(" ", forces.Select(d => { return d ? "1" : "0"; }))} 耗时:{(DateTime.Now - start).TotalMilliseconds} ms");
- }
- catch (Exception ex)
- {
- CLog.Instance.SystemLog.WriteException("PackControl", ex);
- Thread.Sleep(5000);
- }
- firstStart = false;
- couter++;
- Thread.Sleep(1000);
- }
- }
- private void UserThread2()
- {
- CLog.Instance.SystemLog.WriteDebug($"PackControl2已启动");
- while (true)
- {
- try
- {
- TaskCustomProc();
- }
- catch (Exception ex)
- {
- CLog.Instance.SystemLog.WriteException("PackControl2", ex);
- Thread.Sleep(5000);
- }
- Thread.Sleep(500);
- }
- }
- }
- public class PackStation
- {
- public string Id { get; set; }
- internal RouteConfig routeConfig { get; set; }
- public int 工位导航点 { get; set; }
- public int 请求进入导航点 { get; set; }
- public int[] EnteringVertexs { get; set; }
- public int[] EnteringEdges { get; set; }
- public int[] LeavingVertexs { get; set; }
- public int[] LeavingEdges { get; set; }
- public bool 请求进入 { get; set; }
- public bool 正在进入 { get; set; }
- public bool 到位 { get; set; }
- public bool 正在离开 { get; set; }
- public bool 区域有Agv { get { return 正在进入 || 到位 || 正在离开; } }
- public bool AGV上升到位 { get; set; }
- public bool AGV下降到位 { get; set; }
- public int AGV编号 { get; set; }
- public int AGV报警代码 { get; set; }
- public string 请求进入AGV信息 { get; set; }
- public string 正在进入AGV信息 { get; set; }
- public string 到位AGV信息 { get; set; }
- public string 正在离开AGV信息 { get; set; }
- public string 上升到位AGV信息 { get; set; }
- public string 下降到位AGV信息 { get; set; }
- public double 耗时 { get; set; }
- public string 异常消息 { get; set; }
- }
- }
|