1. 重新整理项目,按照A034模式,将设备异步操作修改为类同步操作。使用任务队列来存储和分配任务。
| | |
| | | </Compile> |
| | | <Compile Include="Helper\PropertyConvertHelper.cs" /> |
| | | <Compile Include="ProcessConfig.cs" /> |
| | | <Compile Include="ProcessControl_AGV.cs" /> |
| | | <Compile Include="ProcessControl_Calibration.cs" /> |
| | | <Compile Include="ProcessControl_Method.cs" /> |
| | | <Compile Include="ProcessControl.cs" /> |
| | | <Compile Include="ProcessControl_Robot.cs" /> |
| | | <Compile Include="ProcessControl_Task.cs" /> |
| | | <Compile Include="Properties\AssemblyInfo.cs" /> |
| | | </ItemGroup> |
| | | <ItemGroup> |
| | |
| | | |
| | | namespace A032.Process |
| | | { |
| | | public enum TaskStatus |
| | | public enum TaskState |
| | | { |
| | | Available = 1, |
| | | /// <summary> |
| | | /// 新任务 |
| | | /// </summary> |
| | | New = 0, |
| | | /// <summary> |
| | | /// 任务完成 |
| | | /// </summary> |
| | | Done = 1, |
| | | /// <summary> |
| | | /// 任务失败 |
| | | /// </summary> |
| | | Failed = 2, |
| | | /// <summary> |
| | | /// 任务已添加队列,等待执行 |
| | | /// </summary> |
| | | Queueing = 3, |
| | | /// <summary> |
| | | /// 任务已指派 |
| | | /// </summary> |
| | | Assigned = 4, |
| | | /// <summary> |
| | | /// 任务未指派,已取消 |
| | | /// </summary> |
| | | Cancelled = 5, |
| | | } |
| | | |
| | | public enum AGVState |
| | | { |
| | | /// <summary> |
| | | /// AGV状态不可知 |
| | | /// </summary> |
| | | Unknown = 0, |
| | | /// <summary> |
| | | /// 空闲状态,可接收任务 |
| | | /// </summary> |
| | | Idle = 1, |
| | | /// <summary> |
| | | /// 任务执行中 |
| | | /// </summary> |
| | | Running = 2, |
| | | /// <summary> |
| | | /// 报警中,不可执行任务 |
| | | /// </summary> |
| | | Warning = 3, |
| | | /// <summary> |
| | | /// 充电中,不可执行任务 |
| | | /// </summary> |
| | | InCharge = 4, |
| | | /// <summary> |
| | | /// 空闲充电状态,可接收任务 |
| | | /// </summary> |
| | | IdleCharge = 5, |
| | | } |
| | | |
| | | public enum TaskAvailableLevel |
| | | { |
| | | Robot = 1, |
| | | AGV = 2, |
| | | Both = 3, |
| | | } |
| | | //public class AGVTaskModel |
| | | //{ |
| | | // public TaskAvailableLevel Level { get; set; } = TaskAvailableLevel.Robot; |
| | | |
| | | public class AGVTaskModel |
| | | { |
| | | public TaskAvailableLevel Level { get; set; } = TaskAvailableLevel.Robot; |
| | | // public string MethodName { get; set; } |
| | | |
| | | public string MethodName { get; set; } |
| | | // public IOperationConfig OpConfig { get; set; } |
| | | |
| | | public IOperationConfig OpConfig { get; set; } |
| | | // public IDevice Device { get; set; } |
| | | |
| | | public IDevice Device { get; set; } |
| | | // //public int Priority { get; set; } = 10; |
| | | |
| | | //public int Priority { get; set; } = 10; |
| | | // public AGVTaskModel() { } |
| | | |
| | | public AGVTaskModel() { } |
| | | |
| | | public AGVTaskModel(TaskAvailableLevel level, string methodName, IOperationConfig opConfig = null, IDevice device = null) |
| | | { |
| | | Level = level; |
| | | MethodName = methodName; |
| | | OpConfig = opConfig ?? new OperationConfigBase(); |
| | | Device = device; |
| | | } |
| | | } |
| | | // public AGVTaskModel(TaskAvailableLevel level, string methodName, IOperationConfig opConfig = null, IDevice device = null) |
| | | // { |
| | | // Level = level; |
| | | // MethodName = methodName; |
| | | // OpConfig = opConfig ?? new OperationConfigBase(); |
| | | // Device = device; |
| | | // } |
| | | //} |
| | | |
| | | public class AGVBindUnit : IComplexDisplay |
| | | { |
| | |
| | | } |
| | | #endregion |
| | | |
| | | #region 后台属性 |
| | | #region 状态 |
| | | private AGVState unitState = AGVState.Idle; |
| | | [Browsable(false)] |
| | | [JsonIgnore] |
| | | public Action<AGVTaskModel> OnMethodInvoke { get; set; } |
| | | |
| | | public string AGVDest { get; set; } = ""; |
| | | |
| | | private TaskStatus agvStatus = TaskStatus.Available; |
| | | [Browsable(false)] |
| | | [JsonIgnore] |
| | | public TaskStatus AGVStatus |
| | | public AGVState UnitState |
| | | { |
| | | get => agvStatus; |
| | | get => unitState; |
| | | set |
| | | { |
| | | agvStatus = value; |
| | | //InvokeTaskCheck(); |
| | | } |
| | | } |
| | | if (value != unitState) |
| | | { |
| | | var preState = unitState; |
| | | unitState = value; |
| | | |
| | | private TaskStatus robotStatus = TaskStatus.Available; |
| | | [Browsable(false)] |
| | | [JsonIgnore] |
| | | public TaskStatus RobotStatus |
| | | { |
| | | get => robotStatus; |
| | | set |
| | | { |
| | | robotStatus = value; |
| | | //InvokeTaskCheck(); |
| | | OnUnitStateChanged?.Invoke(this.Id, preState, unitState); |
| | | } |
| | | } |
| | | } |
| | | |
| | | [Browsable(false)] |
| | | [JsonIgnore] |
| | | public TaskStatus UnitStatus |
| | | { |
| | | get |
| | | { |
| | | if (AGVStatus == TaskStatus.Warning || RobotStatus == TaskStatus.Warning) |
| | | { |
| | | return TaskStatus.Warning; |
| | | } |
| | | else |
| | | { |
| | | if (AGVStatus == TaskStatus.Available && RobotStatus == TaskStatus.Available) |
| | | { |
| | | return TaskStatus.Available; |
| | | } |
| | | } |
| | | public string WarningMsg { get; set; } = ""; |
| | | #endregion |
| | | |
| | | return TaskStatus.Running; |
| | | } |
| | | } |
| | | |
| | | #region 设备 |
| | | [Browsable(false)] |
| | | [JsonIgnore] |
| | | public SeerAGVDriver AGV { get; set; } = null; |
| | |
| | | [Browsable(false)] |
| | | [JsonIgnore] |
| | | public CameraBase Camera { get; set; } = null; |
| | | #endregion |
| | | |
| | | //[Browsable(false)] |
| | | //[JsonIgnore] |
| | | //public ObservableCollection<AGVTaskModel> TaskList { get; set; } = new ObservableCollection<AGVTaskModel>(); |
| | | #region 任务信息 |
| | | [JsonIgnore] |
| | | [Browsable(false)] |
| | | public string CurrentTaskId { get; set; } |
| | | |
| | | [Browsable(false)] |
| | | [JsonIgnore] |
| | | public int CurrentEmptyTray { get; set; } |
| | | [Browsable(false)] |
| | | [JsonIgnore] |
| | | public int CurrentFullTray { get; set; } |
| | | [Browsable(false)] |
| | | [JsonIgnore] |
| | | public bool IsFullTrayFull { get; set; } |
| | | [Browsable(false)] |
| | | [JsonIgnore] |
| | | public bool IsFullTrayEmpty { get; set; } |
| | | [Browsable(false)] |
| | | [JsonIgnore] |
| | | public bool IsEmptyTrayEmpty { get; set; } |
| | | public bool IsTaskCancelled { get; set; } = false; |
| | | |
| | | [Browsable(false)] |
| | | [JsonIgnore] |
| | | public bool IsFullTrayTaskAssigned { get; set; } |
| | | |
| | | [Browsable(false)] |
| | | public bool IsTaskCancelling { get; set; } = false; |
| | | #endregion |
| | | |
| | | #region 事件 |
| | | [JsonIgnore] |
| | | public bool IsEmptyTrayTaskAssigned { get; set; } |
| | | [Browsable(false)] |
| | | public Action<string, AGVState, AGVState> OnUnitStateChanged { get; set; } |
| | | #endregion |
| | | |
| | | //[Browsable(false)] |
| | | //[JsonIgnore] |
| | | //public ManualResetEvent FullTrayFullHandle { get; set; } = new ManualResetEvent(false); |
| | | #region 托盘信息 |
| | | [JsonIgnore] |
| | | [Browsable(false)] |
| | | public int FullTrayNum { get; set; } |
| | | |
| | | //[Browsable(false)] |
| | | //[JsonIgnore] |
| | | //public ManualResetEvent FullTrayEmptyHandle { get; set; } = new ManualResetEvent(false); |
| | | |
| | | public ManualResetEvent RobotIOHandle { get; set; } = new ManualResetEvent(false); |
| | | [JsonIgnore] |
| | | [Browsable(false)] |
| | | public int EmptyTrayNum { get; set; } |
| | | #endregion |
| | | #endregion |
| | | |
| | | public AGVBindUnit() |
| | | { |
| | | //TaskList.CollectionChanged += TaskList_CollectionChanged; |
| | | } |
| | | |
| | | object agvStatusLock = new object(); |
| | | public bool SetAGVStatus(TaskStatus status) |
| | | { |
| | | lock (agvStatusLock) |
| | | { |
| | | switch (status) |
| | | { |
| | | case TaskStatus.Available: |
| | | break; |
| | | case TaskStatus.Running: |
| | | if (AGVStatus == TaskStatus.Available) |
| | | { |
| | | AGVStatus = status; |
| | | return true; |
| | | } |
| | | else |
| | | { |
| | | return false; |
| | | } |
| | | case TaskStatus.Warning: |
| | | break; |
| | | default: |
| | | break; |
| | | } |
| | | |
| | | AGVStatus = status; |
| | | return true; |
| | | } |
| | | } |
| | | //private void TaskList_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) |
| | | //{ |
| | | // if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add) |
| | | // { |
| | | // InvokeTaskCheck(); |
| | | // } |
| | | //} |
| | | |
| | | //private void InvokeTaskCheck() |
| | | //{ |
| | | // lock (taskLock) |
| | | // { |
| | | // for (int i = 0; i < TaskList.Count; i++) |
| | | // { |
| | | // var task = TaskList[i]; |
| | | |
| | | // bool isAgvOK = false; |
| | | // bool isRobotOK = false; |
| | | |
| | | // if ((task.Level & TaskAvailableLevel.AGV) == TaskAvailableLevel.AGV) |
| | | // { |
| | | // isAgvOK = AGVStatus == TaskStatus.Available; |
| | | // } |
| | | // else |
| | | // { |
| | | // isAgvOK = true; |
| | | // } |
| | | |
| | | // if ((task.Level & TaskAvailableLevel.Robot) == TaskAvailableLevel.Robot) |
| | | // { |
| | | // isRobotOK = RobotStatus == TaskStatus.Available; |
| | | // } |
| | | // else |
| | | // { |
| | | // isRobotOK = true; |
| | | // } |
| | | |
| | | // if (isRobotOK && isAgvOK) |
| | | // { |
| | | // OnMethodInvoke?.Invoke(TaskList[i]); |
| | | // TaskList.RemoveAt(i); |
| | | // break; |
| | | // } |
| | | // } |
| | | // } |
| | | //} |
| | | |
| | | //object taskLock = new object(); |
| | | //public void AddTask(AGVTaskModel task) |
| | | //{ |
| | | // lock (taskLock) |
| | | // { |
| | | // TaskList.Add(task); |
| | | // } |
| | | //} |
| | | |
| | | //public void ClearTask() |
| | | //{ |
| | | // lock (taskLock) |
| | | // { |
| | | // TaskList.Clear(); |
| | | // } |
| | | //} |
| | | |
| | | //public AGVTaskModel GetTask(int index) |
| | | //{ |
| | | // lock (taskLock) |
| | | // { |
| | | // return TaskList[index]; |
| | | // } |
| | | //} |
| | | } |
| | | |
| | | public class AGVDeviceConverter : ComboBoxItemTypeConvert |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | public class AllDeviceIdConverter : ComboBoxItemTypeConvert |
| | | { |
| | | public override void GetConvertHash() |
| | | { |
| | | using (var scope = GlobalVar.Container.BeginLifetimeScope()) |
| | | { |
| | | _hash[""] = "未指定"; |
| | | |
| | | List<IDevice> deviceList = scope.Resolve<List<IDevice>>(); |
| | | |
| | | deviceList.ForEach(d => |
| | | { |
| | | _hash[d.Id] = d.Name; |
| | | }); |
| | | } |
| | | } |
| | | } |
| | | |
| | | public class AllDeviceNameConverter : ComboBoxItemTypeConvert |
| | | { |
| | | public override void GetConvertHash() |
| | | { |
| | | using (var scope = GlobalVar.Container.BeginLifetimeScope()) |
| | | { |
| | | _hash[""] = "未指定"; |
| | | |
| | | List<IDevice> deviceList = scope.Resolve<List<IDevice>>(); |
| | | |
| | | deviceList.ForEach(d => |
| | | { |
| | | _hash[d.Name] = d.Name; |
| | | }); |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | using Bro.Common.Model; |
| | | using Bro.Common.Model.Interface; |
| | | using Bro.Device.HikCamera; |
| | | using Newtonsoft.Json; |
| | | using System; |
| | | using System.Collections.Generic; |
| | | using System.ComponentModel; |
| | |
| | | [TypeConverter(typeof(PLCDeviceConverter))] |
| | | public string DeviceOwner { get; set; } |
| | | |
| | | [Browsable(false)] |
| | | [JsonIgnore] |
| | | public bool IsOccupied { get; set; } = false; |
| | | |
| | | public string GetDisplayText() |
| | | { |
| | | return $"{PositionCode}-{Description}"; |
| | |
| | | [Editor(typeof(PropertyObjectEditor), typeof(UITypeEditor))] |
| | | public HikCameraOperationConfig CameraOpConfig { get; set; } = new HikCameraOperationConfig(); |
| | | |
| | | [Category("示教配置")] |
| | | [Description("机器人拍照位置")] |
| | | [TypeConverter(typeof(ComplexObjectConvert))] |
| | | [Editor(typeof(PropertyObjectEditor), typeof(UITypeEditor))] |
| | | public RobotPoint RobotSnapshotPoint { get; set; } = new RobotPoint(); |
| | | |
| | | [Category("示教配置")] |
| | | [Description("机器人示教位置到实际抓取位置的偏移")] |
| | | [TypeConverter(typeof(ComplexObjectConvert))] |
| | | [Editor(typeof(PropertyObjectEditor), typeof(UITypeEditor))] |
| | | public RobotPoint RobotShift { get; set; } = new RobotPoint(); |
| | | |
| | | public string GetDisplayText() |
| | | { |
| | | return $"{PositionCode}:曝光:{CameraOpConfig.Exposure};标定矩阵:{string.Join(",", Matrix)}"; |
| | |
| | | { |
| | | [Category("关联配置")] |
| | | [Description("位置序号")] |
| | | [TypeConverter(typeof(PositionNoConverter))] |
| | | public int PositionNo { get; set; } |
| | | [TypeConverter(typeof(PositionCodeConverter))] |
| | | public string PositionCode { get; set; } |
| | | |
| | | [Category("关联配置")] |
| | | [Description("适用相机编号")] |
| | |
| | | |
| | | public string GetDisplayText() |
| | | { |
| | | return $"PositionNo:{PositionNo}; Configs:{Configs.Count}"; |
| | | return $"PositionNo:{PositionCode}; Configs:{Configs.Count}"; |
| | | } |
| | | |
| | | public List<string> GetHalconToolPathList() |
| | |
| | | [Editor(typeof(PropertyObjectEditor), typeof(UITypeEditor))] |
| | | public HalconRelatedCameraOprerationConfigBase CameraOpConfig { get; set; } = new HalconRelatedCameraOprerationConfigBase(); |
| | | |
| | | //[Category("运动平台设置")] |
| | | //[Description("平台当前坐标")] |
| | | //[TypeConverter(typeof(ComplexObjectConvert))] |
| | | //[Editor(typeof(PropertyObjectEditor), typeof(UITypeEditor))] |
| | | //public CustomizedPoint CurrentPlatPoint { get; set; } = new CustomizedPoint(); |
| | | |
| | | [Category("运动平台设置")] |
| | | [Description("平台当前坐标")] |
| | | [Description("机器人运动坐标")] |
| | | [TypeConverter(typeof(ComplexObjectConvert))] |
| | | [Editor(typeof(PropertyObjectEditor), typeof(UITypeEditor))] |
| | | public CustomizedPoint CurrentPlatPoint { get; set; } = new CustomizedPoint(); |
| | | public RobotPoint PlatPoint { get; set; } = new RobotPoint(); |
| | | |
| | | public event PropertyChangedEventHandler PropertyChanged; |
| | | |
| | |
| | | InitializeComponent(); |
| | | } |
| | | |
| | | public CtrlCalib9PDynamic(ProcessControl process, CalibrationConfigCollection calibConfig, AGVBindUnit bind, PathPosition position, Action<AGVBindUnit, int, int> commuAction, Action<CalibrationConfigCollection, AGVBindUnit, PathPosition> finalCalculation) |
| | | public CtrlCalib9PDynamic(ProcessControl process, CalibrationConfigCollection calibConfig, AGVBindUnit bind, PathPosition position, Action<CalibrationConfigCollection, AGVBindUnit, PathPosition> finalCalculation) |
| | | { |
| | | InitializeComponent(); |
| | | |
| | |
| | | |
| | | Bind = bind; |
| | | Position = position; |
| | | CommuAction = commuAction; |
| | | FinalCalculation = finalCalculation; |
| | | } |
| | | |
| | |
| | | |
| | | _canvas.LoadImage(Config.Configs[index].Image); |
| | | _canvas.Elements.Clear(); |
| | | CrossHair ch = new CrossHair(new CalibrationPoint(Config.Configs[index].ImageMarkPoint, Config.Configs[index].CurrentPlatPoint)); |
| | | var config = Config.Configs[index]; |
| | | CrossHair ch = new CrossHair(new CalibrationPoint(new CustomizedPoint(config.ImageMarkPoint.X, config.ImageMarkPoint.Y), new CustomizedPoint(config.PlatPoint.X, config.PlatPoint.Y))); |
| | | _canvas.Elements.Add(ch); |
| | | |
| | | tsslInfo.Text = $"步骤{index + 1}完成"; |
| | |
| | | { |
| | | _canvas.LoadImage(stepConfig.Image); |
| | | _canvas.Elements.Clear(); |
| | | _canvas.Elements.Add(new CrossHair(new CalibrationPoint(stepConfig.ImageMarkPoint, stepConfig.CurrentPlatPoint))); |
| | | |
| | | CrossHair ch = new CrossHair(new CalibrationPoint(new CustomizedPoint(stepConfig.ImageMarkPoint.X, stepConfig.ImageMarkPoint.Y), new CustomizedPoint(stepConfig.PlatPoint.X, stepConfig.PlatPoint.Y))); |
| | | _canvas.Elements.Add(ch); |
| | | } |
| | | } |
| | | |
| | |
| | | // //ProcessControl.SendCalibStartSignal(Config.TriggerAddress); |
| | | //} |
| | | |
| | | ProcessControl.MultipleStepsProcess(Config, Bind, CommuAction); |
| | | ProcessControl.MultipleStepsProcess(Config, Bind); |
| | | } |
| | | |
| | | private void btnLoadOfflineImages_Click(object sender, EventArgs e) |
| | |
| | | |
| | | //tsslInfo.Text = $"单步运算完成。标记点坐标:{config.ImageMarkPoint.X},{config.ImageMarkPoint.Y}"; |
| | | |
| | | ProcessControl.SingleStepProcess(config, CommuAction, Bind, Position.PositionNo, _selectedStepIndex); |
| | | ProcessControl.SingleStepProcess(config, Bind, _selectedStepIndex); |
| | | tsslInfo.Text = $"单步运算完成。标记点坐标:{config.ImageMarkPoint.X},{config.ImageMarkPoint.Y}"; |
| | | } |
| | | |
| | |
| | | // CtrlCalib9PDynamic = new CtrlCalib9PDynamic(process, device, config, finalCalculation); |
| | | //} |
| | | |
| | | public FrmCalib9PDynamic(ProcessControl process, CalibrationConfigCollection calibConfig, AGVBindUnit bind, PathPosition position, Action<AGVBindUnit, int, int> commuAction, Action<CalibrationConfigCollection, AGVBindUnit, PathPosition> finalCalculation) |
| | | public FrmCalib9PDynamic(ProcessControl process, CalibrationConfigCollection calibConfig, AGVBindUnit bind, PathPosition position, Action<CalibrationConfigCollection, AGVBindUnit, PathPosition> finalCalculation) |
| | | { |
| | | InitializeComponent(); |
| | | |
| | |
| | | //CommuAction = commuAction; |
| | | //FinalCalculation = finalCalculation; |
| | | |
| | | CtrlCalib9PDynamic = new CtrlCalib9PDynamic(process, calibConfig, bind, position, commuAction, finalCalculation); |
| | | CtrlCalib9PDynamic = new CtrlCalib9PDynamic(process, calibConfig, bind, position, finalCalculation); |
| | | } |
| | | |
| | | //AGVBindUnit Bind { get; set; } |
| | |
| | | using Bro.Common.Helper; |
| | | using Bro.Common.Interface; |
| | | using Bro.Common.Model; |
| | | using Bro.Common.Model.Interface; |
| | | using Bro.Device.AuboRobot; |
| | | using Bro.Device.HikCamera; |
| | | using Bro.Device.OmronFins; |
| | |
| | | |
| | | namespace A032.Process |
| | | { |
| | | public class ProcessConfig : IStationConfig |
| | | public class ProcessConfig : IStationConfig, IHalconToolPath |
| | | { |
| | | #region 设备配置 |
| | | [Category("设备配置")] |
| | |
| | | [TypeConverter(typeof(CollectionCountConvert))] |
| | | [Editor(typeof(OperationConfigBindEditor), typeof(UITypeEditor))] |
| | | public Dictionary<string, IOperationConfig> ProcessOpConfigDict { get; set; } = new Dictionary<string, IOperationConfig>(); |
| | | |
| | | [Category("操作配置")] |
| | | [Description("操作超时设置,单位min")] |
| | | public int OperationTimeout { get; set; } = 10; |
| | | |
| | | //[Category("监听和操作配置")] |
| | | //[Description("监听操作配置集合")] |
| | |
| | | //[Description("是否采用外部算子。true:采用外部算子,false:使用内部算法")] |
| | | //public bool IsUsingExternalAlgorithem { get; set; } = true; |
| | | |
| | | #region A032 |
| | | [Category("路径相关")] |
| | | [Description("路径节点配置")] |
| | | [TypeConverter(typeof(CollectionCountConvert))] |
| | |
| | | [Description("是否启用视觉引导")] |
| | | public bool IsEnableVisionGuide { get; set; } = false; |
| | | |
| | | /// <summary> |
| | | /// 空Tray上料阈值,AGV上的空tray数量不大于该数值时,AGV可以执行空Tray上料任务 |
| | | /// </summary> |
| | | [Category("阈值设置")] |
| | | [Description("空Tray上料阈值,AGV上的空tray数量不大于该数值时,AGV可以执行空Tray上料任务")] |
| | | public int AGV_EmptyTrayThreshold { get; set; } = 0; |
| | | [Category("视觉配置")] |
| | | [Description("视觉引导次数")] |
| | | public int VisionGuideTimes { get; set; } = 2; |
| | | |
| | | /// <summary> |
| | | /// 满Tray下料阈值,AGV上的满tray数量不小于该数值时,AGV可以执行满Tray下料任务 |
| | | /// </summary> |
| | | [Category("阈值设置")] |
| | | [Description("满Tray下料阈值,AGV上的满tray数量不小于该数值时,AGV可以执行满Tray下料任务")] |
| | | public int AGV_FullTrayThreshold { get; set; } = 10; |
| | | [Category("设备参数配置")] |
| | | [Description("光源开关索引配置")] |
| | | public int LightOutputIndex { get; set; } |
| | | |
| | | [Category("设备参数配置")] |
| | | [Description("AGV满载满Tray/空Tray数量")] |
| | | public int AGVAvailableTrayNums { get; set; } = 6; |
| | | |
| | | [Category("设备参数配置")] |
| | | [Description("默认等待任务轮数。当某些条件不满足当前任务执行前提,当前任务会等待若干任务数后执行")] |
| | | public int DefaultWaitShift { get; set; } = 3; |
| | | |
| | | /// <summary> |
| | | /// 产线忙时拍照确认等待间隔,以秒为单位 |
| | |
| | | |
| | | [Category("阈值设置")] |
| | | [Description("机台压机满Tray数量")] |
| | | public int Machine_FullTrayNum { get; set; } |
| | | public int Machine_FullTrayNum { get; set; } = 6; |
| | | |
| | | [Category("阈值设置")] |
| | | [Description("机台压机空Tray数量")] |
| | | public int Machine_EmptyTrayNum { get; set; } |
| | | public int Machine_EmptyTrayNum { get; set; } = 6; |
| | | #endregion |
| | | |
| | | #region IHalconToolPath |
| | | public List<string> GetHalconToolPathList() |
| | | { |
| | | List<string> list = new List<string>(); |
| | | |
| | | ProcessOpConfigDict.Values.ToList().ForEach(c => |
| | | { |
| | | if (c is IHalconToolPath) |
| | | { |
| | | list.AddRange((c as IHalconToolPath).GetHalconToolPathList()); |
| | | } |
| | | }); |
| | | |
| | | this.GetType().GetProperties().ToList().ForEach(p => |
| | | { |
| | | var pValue = p.GetValue(this); |
| | | |
| | | if (pValue is IHalconToolPath) |
| | | { |
| | | list.AddRange((pValue as IHalconToolPath).GetHalconToolPathList()); |
| | | } |
| | | else if (pValue is IEnumerable<IHalconToolPath>) |
| | | { |
| | | list.AddRange((pValue as IEnumerable<IHalconToolPath>).SelectMany(u => u.GetHalconToolPathList())); |
| | | } |
| | | }); |
| | | |
| | | return list.Distinct().ToList(); |
| | | } |
| | | #endregion |
| | | |
| | | #region Ignore |
| | | [Browsable(false)] |
| | |
| | | public virtual bool IsDBSave { get; set; } = false; |
| | | #endregion |
| | | } |
| | | |
| | | [Device("OperationTest", "测试方法配置", DeviceAttributeType.OperationConfig)] |
| | | public class OperationTestConfig : OperationConfigBase |
| | | { |
| | | //[Category("测试配置")] |
| | | //[Description("方法类型")] |
| | | //public TaskType TaskType { get; set; } = TaskType.LoadEmptyTrayToAGV; |
| | | |
| | | [Category("测试配置")] |
| | | [Description("方法信息")] |
| | | [TypeConverter(typeof(ComplexObjectConvert))] |
| | | [Editor(typeof(PropertyObjectEditor), typeof(UITypeEditor))] |
| | | public TrayTask TaskInfo { get; set; } = new TrayTask(); |
| | | |
| | | [Category("测试配置")] |
| | | [Description("执行AGV设备")] |
| | | [TypeConverter(typeof(AGVDeviceConverter))] |
| | | public string AGVId { get; set; } |
| | | } |
| | | } |
| | |
| | | using Bro.Common.Model; |
| | | using Bro.Common.Model.Interface; |
| | | using Bro.Common.PubSub; |
| | | using Bro.Common.UI; |
| | | using Bro.Device.AuboRobot; |
| | | using Bro.Device.OmronFins; |
| | | using Bro.Device.SeerAGV; |
| | |
| | | using Newtonsoft.Json; |
| | | using Newtonsoft.Json.Linq; |
| | | using System; |
| | | using System.Collections.Concurrent; |
| | | using System.Collections.Generic; |
| | | using System.Collections.ObjectModel; |
| | | using System.Collections.Specialized; |
| | | using System.Configuration; |
| | | using System.Diagnostics; |
| | | using System.Drawing; |
| | | using System.Drawing.Imaging; |
| | | using System.IO; |
| | | using System.Linq; |
| | | using System.Reflection; |
| | | using System.Runtime.ExceptionServices; |
| | | using System.Threading; |
| | | using System.Threading.Tasks; |
| | | using static Bro.Common.Helper.EnumHelper; |
| | |
| | | if (ProcessState == DeviceState.DSClose) |
| | | return; |
| | | |
| | | List<string> currentTaskIds = Config.AGVBindCollection.Select(u => u.CurrentTaskId).Where(s => !string.IsNullOrWhiteSpace(s)).ToList(); |
| | | TrayTaskCollection.RemoveAll(t => !currentTaskIds.Contains(t.TaskId)); |
| | | |
| | | _bindTaskDoneHandleDict.Values.ToList().ForEach(h => h.WaitOne()); |
| | | |
| | | CloseDevice(PLCDict.Values.ToList()); |
| | | CloseDevice(RobotDict.Values.ToList()); |
| | | CloseDevice(AGVDict.Values.ToList()); |
| | |
| | | return; |
| | | |
| | | InitialProcessMethods(); |
| | | TrayTaskCollection.OnItemChangedWithItemInfo = OnTaskListChanged; |
| | | |
| | | OpenDevices(RobotDict.Values.ToList()); |
| | | OpenDevices(AGVDict.Values.ToList()); |
| | |
| | | |
| | | OpenDevices(PLCDict.Values.ToList()); |
| | | |
| | | //加载AGVUnit状态事件 |
| | | Config.AGVBindCollection.ForEach(b => |
| | | { |
| | | b.OnUnitStateChanged = OnUnitStateChanged; |
| | | |
| | | _bindTaskDoneHandleDict[b.Id] = new AutoResetEvent(true); |
| | | }); |
| | | |
| | | ProcessState = DeviceState.DSOpen; |
| | | |
| | | //QueryRobotIO(); |
| | | |
| | | //Task.Run(() => |
| | | //{ |
| | | // //PLCMonitor(); |
| | | //}); |
| | | |
| | | LogAsync(DateTime.Now, "Process Opened", ""); |
| | | } |
| | |
| | | #region 个别配置的特别处理 |
| | | #endregion |
| | | |
| | | _warningRemains.CollectionChanged -= _warningRemains_CollectionChanged; |
| | | _warningRemains.CollectionChanged += _warningRemains_CollectionChanged; |
| | | WarningRemains.CollectionChanged -= _warningRemains_CollectionChanged; |
| | | WarningRemains.CollectionChanged += _warningRemains_CollectionChanged; |
| | | |
| | | InitialPLCs(); |
| | | InitialAGVs(); |
| | | InitialRobots(); |
| | | InitialCameras(); |
| | | InitialAGVBindUnit(); |
| | | InitialMachineTrayNums(); |
| | | //InitialMachineTrayNums(); |
| | | |
| | | AutoFacRegister(); |
| | | |
| | | LogAsync(DateTime.Now, "Process Initialized", ""); |
| | | } |
| | | |
| | | private void InitialMachineTrayNums() |
| | | { |
| | | machineEmptyTrayDict = Config.PositionCollection.Where(u => u.Description == PathPositionDefinition.UnloadEmptyTray).ToDictionary(p => p.PositionNo, p => 0); |
| | | //private void InitialMachineTrayNums() |
| | | //{ |
| | | // machineEmptyTrayDict = Config.PositionCollection.Where(u => u.Description == PathPositionDefinition.UnloadEmptyTray).ToDictionary(p => p.PositionNo, p => 0); |
| | | |
| | | machineFullTrayDict = Config.PositionCollection.Where(u => u.Description == PathPositionDefinition.LoadFullTray).ToDictionary(p => p.PositionNo, p => 0); |
| | | } |
| | | // machineFullTrayDict = Config.PositionCollection.Where(u => u.Description == PathPositionDefinition.LoadFullTray).ToDictionary(p => p.PositionNo, p => 0); |
| | | //} |
| | | |
| | | private void InitialAGVBindUnit() |
| | | { |
| | |
| | | agv.InitialConfig = c; |
| | | AGVDict[agv.InitialConfig.ID] = agv; |
| | | |
| | | agv.OnMonitorAlarm -= OnMonitorAlarm; |
| | | agv.OnMonitorInvoke -= OnMonitorInvoke; |
| | | |
| | | agv.OnLog = OnDeviceLog; |
| | | agv.OnAGVPositoinChanged = OnAGVPositionChanged; |
| | | agv.OnAGVTaskStatusChanged = OnAGVTaskStatusChanged; |
| | |
| | | |
| | | ProcessConfig pConfig = config as ProcessConfig; |
| | | if (pConfig == null) |
| | | throw new ProcessException("目前只支持ProcessConfig类型的非空内容保存", null); |
| | | throw new ProcessException("目前只支持ProcessConfig类型的非空内容保存"); |
| | | |
| | | string newConfig = JsonConvert.SerializeObject(pConfig, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.Auto }); |
| | | using (StreamWriter writer = new StreamWriter(CONFIG_PATH, false, System.Text.Encoding.UTF8)) |
| | |
| | | if (attr != null) |
| | | { |
| | | _processMethodDict[attr.MethodCode] = m; |
| | | |
| | | #region 初始化HalconTool 根据processMethod的特性来配置 |
| | | //if (attr.DeviceType.EndsWith("Camera")) |
| | | //{ |
| | | // if (StationConfig.ProcessOpConfigDict.Keys.Contains(attr.MethodCode)) |
| | | // { |
| | | // var opConfig = StationConfig.ProcessOpConfigDict[attr.MethodCode] as HalconRelatedCameraOprerationConfigBase; |
| | | |
| | | // if (opConfig != null) |
| | | // { |
| | | // if (!string.IsNullOrWhiteSpace(opConfig.AlgorithemPath)) |
| | | // { |
| | | // string directoryPath = Path.GetDirectoryName(opConfig.AlgorithemPath); |
| | | // string fileName = Path.GetFileNameWithoutExtension(opConfig.AlgorithemPath); |
| | | |
| | | // HDevEngineTool tool = new HDevEngineTool(directoryPath); |
| | | // tool.LoadProcedure(fileName); |
| | | |
| | | // _halconToolDict[attr.MethodCode] = tool; |
| | | // } |
| | | // } |
| | | // } |
| | | //} |
| | | #endregion |
| | | } |
| | | }); |
| | | |
| | | #region 初始化HalconTool 根据配置的接口类型来配置 |
| | | _halconToolDict = new Dictionary<string, HDevEngineTool>(); |
| | | Config.PLCConfigCollection.SelectMany(plcConfig => plcConfig.MonitorSetCollection).Select(ms => ms.OpConfig).ToList().ForEach(c => |
| | | { |
| | | InitialHalconTool(c as IHalconToolPath); |
| | | }); |
| | | |
| | | Config.VisionConfigCollection.ForEach(c => |
| | | { |
| | | InitialHalconTool(c as IHalconToolPath); |
| | | }); |
| | | |
| | | Config.ProcessOpConfigDict.Values.ToList().ForEach(c => |
| | | { |
| | | InitialHalconTool(c as IHalconToolPath); |
| | | }); |
| | | InitialHalconTool(); |
| | | #endregion |
| | | } |
| | | |
| | | private void InitialHalconTool(IHalconToolPath toolPath) |
| | | private void InitialHalconTool() |
| | | { |
| | | //IHalconToolPath toolPath = c as IHalconToolPath; |
| | | if (toolPath != null) |
| | | foreach (HDevEngineTool tool in _halconToolDict.Values) |
| | | { |
| | | toolPath.GetHalconToolPathList().ForEach(path => |
| | | tool?.Dispose(); |
| | | } |
| | | |
| | | _halconToolDict = new Dictionary<string, HDevEngineTool>(); |
| | | |
| | | Config.GetHalconToolPathList().ForEach(path => |
| | | { |
| | | if (!string.IsNullOrWhiteSpace(path) && !_halconToolDict.ContainsKey(path)) |
| | | { |
| | |
| | | _halconToolDict[path] = tool; |
| | | } |
| | | }); |
| | | } |
| | | } |
| | | |
| | | public List<IDevice> GetDeviceList() |
| | |
| | | res = new ProcessResponse((int)ReturnValue.EXCEPTIONVALUE); |
| | | } |
| | | |
| | | var newEx = new ProcessException("函数" + methodCode + "执行异常", ex); |
| | | var newEx = new ProcessException("函数" + methodCode + "执行异常", ExceptionLevel.Warning, ex); |
| | | } |
| | | else |
| | | { |
| | |
| | | { |
| | | monitorSet.Response.ResultValue = (int)ReturnValue.OKVALUE; |
| | | } |
| | | #endregion |
| | | |
| | | //sw.Stop(); |
| | | //LogAsync(DateTime.Now, methodCode + " 调用耗时: " + sw.ElapsedMilliseconds.ToString() + "ms", ""); |
| | | //TimeRecordCSV(DateTime.Now, methodCode + "调用", (int)sw.ElapsedMilliseconds); |
| | | //sw.Start(); |
| | | |
| | | #region 原有PLC写入结果操作,现转到异步调用后回调去执行 |
| | | //ProcessResponse resValues = res as ProcessResponse; |
| | | |
| | | //if (resValues.ResultValue == (int)PLCReplyValue.IGNORE) |
| | | //{ |
| | | // return; |
| | | //} |
| | | |
| | | //if (monitorSet.ReplyDataAddress != -1 && resValues.DataList.Count > 0) |
| | | //{ |
| | | // PLC_ITEM item = new PLC_ITEM(); |
| | | // item.OP_TYPE = 2; |
| | | // item.ITEM_LENGTH = resValues.DataList.Count; |
| | | // item.ADDRESS = monitorSet.ReplyDataAddress.ToString(); |
| | | // item.ITEM_VALUE = String.Join(",", resValues.DataList); |
| | | // PLC.WriteItem(item, false); |
| | | //} |
| | | |
| | | //if (monitorSet.NoticeAddress != -1) |
| | | //{ |
| | | // //测试模式下始终反馈OK信号 |
| | | // if (StationConfig.IsDemoMode && resValues.ResultValue <= 0) |
| | | // { |
| | | // resValues.ResultValue = (int)ReturnValue.OKVALUE; |
| | | // } |
| | | |
| | | // int repeatTime = 5; |
| | | |
| | | // //LogAsync(DateTime.Now, methodCode + "开始反馈", ""); |
| | | // do |
| | | // { |
| | | // try |
| | | // { |
| | | // PLC.WriteSingleAddress(set.NoticeAddress, resValues.ResultValue, false); |
| | | // repeatTime = 0; |
| | | // } |
| | | // catch (Exception ex) |
| | | // { |
| | | // repeatTime--; |
| | | |
| | | // if (repeatTime <= 0) |
| | | // { |
| | | // new ProcessException("PLC反馈写入异常", ex); |
| | | // } |
| | | // } |
| | | // } while (repeatTime > 0); |
| | | //} |
| | | #endregion |
| | | } |
| | | |
| | |
| | | else |
| | | { |
| | | //MessageBox.Show("未能获取离线图片!"); |
| | | throw new ProcessException("未能获取离线图片!", null); |
| | | throw new ProcessException("未能获取离线图片!"); |
| | | } |
| | | } |
| | | } |
| | |
| | | #endregion |
| | | |
| | | #region 报警和DownTime |
| | | public ObservableCollection<string> _warningRemains = new ObservableCollection<string>(); |
| | | public ObservableCollection<string> WarningRemains { get; set; } = new ObservableCollection<string>(); |
| | | |
| | | bool warningRemainFlag = false; |
| | | bool WarningRemainFlag |
New file |
| | |
| | | using Bro.Common.Helper; |
| | | using Bro.Device.SeerAGV; |
| | | using System; |
| | | using System.Collections.Generic; |
| | | using System.Linq; |
| | | using System.Text; |
| | | using System.Threading.Tasks; |
| | | |
| | | namespace A032.Process |
| | | { |
| | | public partial class ProcessControl |
| | | { |
| | | private async void OnAGVBatteryLvlChanged(SeerAGVDriver agv, float preBatteryLvl, float batteryLvl) |
| | | { |
| | | await Task.Run(() => |
| | | { |
| | | var bind = Config.AGVBindCollection.FirstOrDefault(u => u.AGVId == agv.Id); |
| | | SeerAGVInitialConfig iConfig = bind.AGV.InitialConfig as SeerAGVInitialConfig; |
| | | |
| | | if (bind.UnitState == AGVState.Idle) |
| | | { |
| | | if (batteryLvl <= iConfig.BatteryLvlToCharge) |
| | | { |
| | | bind.CurrentTaskId = "InCharge"; |
| | | bind.UnitState = AGVState.InCharge; |
| | | |
| | | var chargePosition = Config.PositionCollection.FirstOrDefault(u => !u.IsOccupied && u.Description == PathPositionDefinition.Charge); |
| | | |
| | | if (chargePosition == null) |
| | | { |
| | | bind.WarningMsg = $"{bind.AGV.Name}目前无可用充电地址"; |
| | | new ProcessException(bind.WarningMsg); |
| | | bind.UnitState = AGVState.Warning; |
| | | } |
| | | else |
| | | { |
| | | bind.AGV.TaskOrder(chargePosition.PositionCode, true); |
| | | } |
| | | } |
| | | else if (batteryLvl <= iConfig.BatteryLvlToCharge_Recommand) |
| | | { |
| | | ChargeWhenIdle(bind); |
| | | } |
| | | } |
| | | else if (bind.UnitState == AGVState.InCharge) |
| | | { |
| | | if (batteryLvl >= iConfig.BatteryLvlChargeDone) |
| | | { |
| | | bind.UnitState = AGVState.Idle; |
| | | } |
| | | else if (batteryLvl >= iConfig.BatteryLvlToCharge_Recommand) |
| | | { |
| | | bind.UnitState = AGVState.IdleCharge; |
| | | } |
| | | } |
| | | }); |
| | | } |
| | | |
| | | private async void OnAGVTaskStatusChanged(SeerAGVDriver agv, AGVTaskStatus taskStatus) |
| | | { |
| | | await Task.Run(() => |
| | | { |
| | | //如果成功到达充电地点,开始充电 |
| | | if (taskStatus == AGVTaskStatus.Completed && Config.PositionCollection.Any(u => u.Description == PathPositionDefinition.Charge && u.PositionCode == agv.CurrentPosition)) |
| | | { |
| | | var bind = Config.AGVBindCollection.FirstOrDefault(u => u.AGVId == agv.Id); |
| | | if (bind != null) |
| | | { |
| | | //表示充电动作任务完成 |
| | | bind.CurrentTaskId = ""; |
| | | } |
| | | } |
| | | }); |
| | | } |
| | | |
| | | private async void OnAGVPositionChanged(SeerAGVDriver agv, string positionCode) |
| | | { |
| | | await Task.Run(() => |
| | | { |
| | | |
| | | }); |
| | | } |
| | | |
| | | private void ChargeWhenIdle(AGVBindUnit bind) |
| | | { |
| | | bind.CurrentTaskId = "IdleCharge"; |
| | | bind.UnitState = AGVState.IdleCharge; |
| | | |
| | | //设置为无需等待是为了方便执行过程中可以取消当前操作,执行任务 |
| | | var chargePosition = Config.PositionCollection.FirstOrDefault(u => !u.IsOccupied && u.Description == PathPositionDefinition.Charge); |
| | | |
| | | if (chargePosition != null) |
| | | { |
| | | bind.AGV.TaskOrder(chargePosition.PositionCode, true); |
| | | } |
| | | else |
| | | { |
| | | new ProcessException($"{bind.AGV.Name}未能获取充电地址"); |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | using Bro.Common.Helper; |
| | | using Bro.Common.Interface; |
| | | using Bro.Common.Model; |
| | | using Bro.Device.AuboRobot; |
| | | using HalconDotNet; |
| | | using System; |
| | | using System.Collections.Generic; |
| | |
| | | { |
| | | public partial class ProcessControl |
| | | { |
| | | CalibReplyMsg _calibReply = new CalibReplyMsg(); |
| | | |
| | | [ProcessMethod("CalibrationCollection", "RobotCalibration", "机器人9点标定", true)] |
| | | public ProcessResponse RobotCalibration(IOperationConfig config, IDevice device) |
| | | { |
| | |
| | | throw new ProcessException("未能获取绑定设备信息"); |
| | | } |
| | | |
| | | PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.PositionNo == calibConfig.PositionNo); |
| | | PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.PositionCode == calibConfig.PositionCode); |
| | | if (position == null) |
| | | { |
| | | throw new ProcessException("未能获取正确位置信息"); |
| | |
| | | |
| | | if (calibConfig.IsStartedFromUI) |
| | | { |
| | | FrmCalib9PDynamic frm = new FrmCalib9PDynamic(this, calibConfig, bind, position, SendMessageToRobot_Calibration, CalculateMatrix); |
| | | FrmCalib9PDynamic frm = new FrmCalib9PDynamic(this, calibConfig, bind, position, CalculateMatrix); |
| | | frm.ShowDialog(); |
| | | } |
| | | else |
| | | { |
| | | MultipleStepsProcess(calibConfig, bind, SendMessageToRobot_Calibration); |
| | | MultipleStepsProcess(calibConfig, bind); |
| | | |
| | | CalculateMatrix(calibConfig, bind, position); |
| | | } |
| | | |
| | | //for (int i = 0; i < calibConfig.Configs.Count; i++) |
| | | //{ |
| | | // bind.Robot.SendMsg(Bro.Device.AuboRobot.RobotMsgAction.Calibration, Bro.Device.AuboRobot.RobotMsgParas.None, calibConfig.PositionNo, new List<float>() { i + 1 }); |
| | | |
| | | // _calibReply.CalibHandle.WaitOne(); |
| | | |
| | | // if (_calibReply.CalibIndex != (i + 1) || _calibReply.CalibPositionNo != calibConfig.PositionNo) |
| | | // { |
| | | // throw new ProcessException("标定反馈的索引或位置信息不一致"); |
| | | // } |
| | | |
| | | // calibConfig.Configs[i].CurrentPlatPoint = new CustomizedPoint(_calibReply.RobotPosition.X, _calibReply.RobotPosition.Y); |
| | | |
| | | // using (HObject hImage = CollectHImage(bind.Camera, calibConfig.Configs[i].CameraOpConfig, "RobotCalibration")) |
| | | // { |
| | | // var tool = _halconToolDict[calibConfig.Configs[i].CameraOpConfig.AlgorithemPath]; |
| | | |
| | | // tool.SetDictionary(new Dictionary<string, HTuple>() { { "OUTPUT_X", new HTuple() }, { "OUTPUT_Y", new HTuple() }, { "OUTPUT_Angle", new HTuple() } }, new Dictionary<string, HObject>() { { "INPUT_Image", hImage } }); |
| | | // tool.RunProcedure(); |
| | | |
| | | // float x = (float)tool.GetResultTuple("OUTPUT_X").D; |
| | | // float y = (float)tool.GetResultTuple("OUTPUT_Y").D; |
| | | // float angel = (float)tool.GetResultTuple("OUTPUT_Angle").D; |
| | | // if (x < 0 || y < 0) |
| | | // { |
| | | // throw new ProcessException("获取点位信息不正确"); |
| | | // } |
| | | |
| | | // calibConfig.Configs[i].ImageMarkPoint = new CustomizedPointWithAngle(x, y, angel); |
| | | // } |
| | | //} |
| | | |
| | | //HOperatorSet.VectorToHomMat2d(new HTuple(calibConfig.Configs.Select(u => u.ImageMarkPoint.X).ToArray()), new HTuple(calibConfig.Configs.Select(u => u.ImageMarkPoint.Y).ToArray()), new HTuple(calibConfig.Configs.Select(u => u.CurrentPlatPoint.X).ToArray()), new HTuple(calibConfig.Configs.Select(u => u.CurrentPlatPoint.Y).ToArray()), out HTuple matrix); |
| | | |
| | | //var visionConfig = Config.VisionConfigCollection.FirstOrDefault(u => u.CameraId == bind.CameraId && u.PositionCode == position.PositionCode); |
| | | //if (visionConfig != null) |
| | | //{ |
| | | // visionConfig.Matrix = new List<double>() { matrix[0], matrix[1], 0, matrix[3], matrix[4], 0 }; |
| | | //} |
| | | //else |
| | | //{ |
| | | // Config.VisionConfigCollection.Add(new PositionVisionConfig() |
| | | // { |
| | | // CameraId = bind.CameraId, |
| | | // PositionCode = position.PositionCode, |
| | | // Matrix = new List<double>() { matrix[0], matrix[1], 0, matrix[3], matrix[4], 0 }, |
| | | // }); |
| | | //} |
| | | |
| | | return new ProcessResponse(true); |
| | | } |
| | |
| | | throw new ProcessException("未能获取绑定设备信息"); |
| | | } |
| | | |
| | | PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.PositionNo == calibConfig.PositionNo); |
| | | PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.PositionCode == calibConfig.PositionCode); |
| | | if (position == null) |
| | | { |
| | | throw new ProcessException("未能获取正确位置信息"); |
| | |
| | | |
| | | if (calibConfig.IsStartedFromUI) |
| | | { |
| | | FrmCalib9PDynamic frm = new FrmCalib9PDynamic(this, calibConfig, bind, position, SendMessageToRobot_Standard, CalculateStandardPoint); |
| | | FrmCalib9PDynamic frm = new FrmCalib9PDynamic(this, calibConfig, bind, position, CalculateStandardPoint); |
| | | frm.ShowDialog(); |
| | | } |
| | | else |
| | | { |
| | | MultipleStepsProcess(calibConfig, bind, SendMessageToRobot_Standard); |
| | | MultipleStepsProcess(calibConfig, bind); |
| | | |
| | | CalculateStandardPoint(calibConfig, bind, position); |
| | | } |
| | | |
| | | //for (int i = 0; i < calibConfig.Configs.Count; i++) |
| | | //{ |
| | | // bind.Robot.SendMsg(Bro.Device.AuboRobot.RobotMsgAction.Calibration, Bro.Device.AuboRobot.RobotMsgParas.None, calibConfig.PositionNo, new List<float>() { i + 1 }); |
| | | |
| | | // _calibReply.CalibHandle.WaitOne(); |
| | | |
| | | // if (_calibReply.CalibPositionNo != calibConfig.PositionNo) |
| | | // { |
| | | // throw new ProcessException("标定反馈的位置信息不一致"); |
| | | // } |
| | | |
| | | // calibConfig.Configs[i].CurrentPlatPoint = new CustomizedPoint(_calibReply.RobotPosition.X, _calibReply.RobotPosition.Y); |
| | | |
| | | // using (HObject hImage = CollectHImage(bind.Camera, calibConfig.Configs[i].CameraOpConfig, "RobotCalibration")) |
| | | // { |
| | | // var tool = _halconToolDict[calibConfig.Configs[i].CameraOpConfig.AlgorithemPath]; |
| | | |
| | | // tool.SetDictionary(new Dictionary<string, HTuple>() { { "OUTPUT_X", new HTuple() }, { "OUTPUT_Y", new HTuple() }, { "OUTPUT_Angle", new HTuple() } }, new Dictionary<string, HObject>() { { "INPUT_Image", hImage } }); |
| | | // tool.RunProcedure(); |
| | | |
| | | // float x = (float)tool.GetResultTuple("OUTPUT_X").D; |
| | | // float y = (float)tool.GetResultTuple("OUTPUT_Y").D; |
| | | // float angel = (float)tool.GetResultTuple("OUTPUT_Angle").D; |
| | | // if (x < 0 || y < 0) |
| | | // { |
| | | // throw new ProcessException("获取点位信息不正确"); |
| | | // } |
| | | |
| | | // calibConfig.Configs[i].ImageMarkPoint = new CustomizedPointWithAngle(x, y, angel); |
| | | // } |
| | | //} |
| | | |
| | | //CustomizedPointWithAngle markPoint = calibConfig.Configs[0].ImageMarkPoint; |
| | | //var visionConfig = Config.VisionConfigCollection.FirstOrDefault(u => u.CameraId == bind.CameraId && u.PositionCode == position.PositionCode); |
| | | //if (visionConfig != null) |
| | | //{ |
| | | // visionConfig.StandardPoint = new CustomizedPointWithAngle(markPoint.X, markPoint.Y, markPoint.Angle); |
| | | //} |
| | | //else |
| | | //{ |
| | | // Config.VisionConfigCollection.Add(new PositionVisionConfig() |
| | | // { |
| | | // CameraId = bind.CameraId, |
| | | // PositionCode = position.PositionCode, |
| | | // StandardPoint = new CustomizedPointWithAngle(markPoint.X, markPoint.Y, markPoint.Angle), |
| | | // }); |
| | | //} |
| | | |
| | | return new ProcessResponse(true); |
| | | } |
| | | |
| | | public void CalculateMatrix(CalibrationConfigCollection calibConfig, AGVBindUnit bind, PathPosition position) |
| | | { |
| | | //HOperatorSet.VectorToHomMat2d(new HTuple(calibConfig.Configs.Select(u => u.ImageMarkPoint.X).ToArray()), new HTuple(calibConfig.Configs.Select(u => u.ImageMarkPoint.Y).ToArray()), new HTuple(calibConfig.Configs.Select(u => u.CurrentPlatPoint.X).ToArray()), new HTuple(calibConfig.Configs.Select(u => u.CurrentPlatPoint.Y).ToArray()), out HTuple matrix); |
| | | |
| | | List<double> matrix = GetMovementMatrix(calibConfig.Configs.Select(u => u.ImageMarkPoint as CustomizedPoint).ToList(), calibConfig.Configs.Select(u => u.CurrentPlatPoint).ToList(), out string msg); |
| | | List<double> matrix = GetMovementMatrix(calibConfig.Configs.Select(u => u.ImageMarkPoint as CustomizedPoint).ToList(), calibConfig.Configs.Select(u => new CustomizedPoint(u.PlatPoint.X, u.PlatPoint.Y)).ToList(), out string msg); |
| | | |
| | | var visionConfig = Config.VisionConfigCollection.FirstOrDefault(u => u.CameraId == bind.CameraId && u.PositionCode == position.PositionCode); |
| | | if (visionConfig != null) |
| | |
| | | sum += Math.Sqrt((Math.Pow((m.D - platPoints[i].X), 2) + Math.Pow((n.D - platPoints[i].Y), 2))); |
| | | } |
| | | |
| | | //sum = ((sum / (double)Config.LengthPulseRatio) * 100.0) / ((double)imagePoints.Count); |
| | | sum = sum / (double)imagePoints.Count; |
| | | |
| | | msg = $"标定点数量:{imagePoints.Count};单点误差:{sum.ToString()}脉冲"; |
| | |
| | | |
| | | public void CalculateStandardPoint(CalibrationConfigCollection calibConfig, AGVBindUnit bind, PathPosition position) |
| | | { |
| | | //var bind = Config.AGVBindCollection.FirstOrDefault(u => u.CameraId == calibConfig.CameraId); |
| | | //if (bind == null) |
| | | //{ |
| | | // throw new ProcessException("未能获取绑定设备信息"); |
| | | //} |
| | | |
| | | //PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.PositionNo == calibConfig.PositionNo); |
| | | //if (position == null) |
| | | //{ |
| | | // throw new ProcessException("未能获取正确位置信息"); |
| | | //} |
| | | |
| | | CustomizedPointWithAngle markPoint = calibConfig.Configs[0].ImageMarkPoint; |
| | | var visionConfig = Config.VisionConfigCollection.FirstOrDefault(u => u.CameraId == bind.CameraId && u.PositionCode == position.PositionCode); |
| | | if (visionConfig != null) |
| | |
| | | PubSubCenter.Publish(PubTag.CalibAllDone.ToString(), "", $"标定完成,标准点:{markPoint.GetDisplayText()}", true); |
| | | } |
| | | |
| | | public void MultipleStepsProcess(CalibrationConfigCollection calibConfig, AGVBindUnit bind, Action<AGVBindUnit, int, int> sendMessageToRobot) |
| | | public void MultipleStepsProcess(CalibrationConfigCollection calibConfig, AGVBindUnit bind) |
| | | { |
| | | for (int i = 0; i < calibConfig.Configs.Count; i++) |
| | | { |
| | | SingleStepProcess(calibConfig.Configs[i], sendMessageToRobot, bind, calibConfig.PositionNo, i); |
| | | SingleStepProcess(calibConfig.Configs[i], bind, i); |
| | | } |
| | | } |
| | | |
| | | public void SendMessageToRobot_Calibration(AGVBindUnit bind, int positionNo, int index) |
| | | public void SingleStepProcess(CalibrationConfig config, AGVBindUnit bind, int index) |
| | | { |
| | | bind.Robot.SendMsg(Bro.Device.AuboRobot.RobotMsgAction.Calibration, Bro.Device.AuboRobot.RobotMsgParas.None, positionNo, new List<float>() { index + 1 }); |
| | | |
| | | _calibReply.CalibHandle.WaitOne(); |
| | | |
| | | if (_calibReply.CalibIndex != (index + 1) || _calibReply.CalibPositionNo != positionNo) |
| | | { |
| | | throw new ProcessException("标定反馈的索引或位置信息不一致"); |
| | | } |
| | | } |
| | | |
| | | public void SendMessageToRobot_Standard(AGVBindUnit bind, int positionNo, int index) |
| | | { |
| | | bind.Robot.SendMsg(Bro.Device.AuboRobot.RobotMsgAction.StandardPoint, Bro.Device.AuboRobot.RobotMsgParas.None, positionNo); |
| | | |
| | | _calibReply.CalibHandle.WaitOne(); |
| | | |
| | | if (_calibReply.CalibPositionNo != positionNo) |
| | | { |
| | | throw new ProcessException("标定反馈的位置信息不一致"); |
| | | } |
| | | } |
| | | |
| | | //PubSubCenter.Subscribe(PubTag.CalibStepDone.ToString(), CalibStepDone); |
| | | //PubSubCenter.Subscribe(PubTag.CalibAllDone.ToString(), CalibAllDone); |
| | | public void SingleStepProcess(CalibrationConfig config, Action<AGVBindUnit, int, int> sendMessageToRobot, AGVBindUnit bind, int positionNo, int index) |
| | | { |
| | | sendMessageToRobot.Invoke(bind, positionNo, index); |
| | | |
| | | config.CurrentPlatPoint = new CustomizedPoint(_calibReply.RobotPosition.X, _calibReply.RobotPosition.Y); |
| | | bind.Robot.Move(config.PlatPoint, MoveType.AbsoluteMove, true); |
| | | |
| | | using (HObject hImage = CollectHImage(bind.Camera, config.CameraOpConfig, "RobotCalibration")) |
| | | { |
| | | var tool = _halconToolDict[config.CameraOpConfig.AlgorithemPath]; |
| | | |
| | | tool.SetDictionary(new Dictionary<string, HTuple>() { { "OUTPUT_X", new HTuple() }, { "OUTPUT_Y", new HTuple() }, { "OUTPUT_Angle", new HTuple() } }, new Dictionary<string, HObject>() { { "INPUT_Image", hImage } }); |
| | | tool.InputImageDic.Clear(); |
| | | tool.InputImageDic["INPUT_Image"] = hImage; |
| | | |
| | | tool.RunProcedure(); |
| | | |
| | | float x = (float)tool.GetResultTuple("OUTPUT_X").D; |
| | |
| | | |
| | | PubSubCenter.Publish(PubTag.CalibStepDone.ToString(), index, "", true); |
| | | } |
| | | } |
| | | |
| | | public class CalibReplyMsg |
| | | { |
| | | public AutoResetEvent CalibHandle { get; set; } = new AutoResetEvent(false); |
| | | |
| | | public int CalibIndex { get; set; } = 0; |
| | | |
| | | public int CalibPositionNo { get; set; } |
| | | |
| | | public CustomizedPoint RobotPosition { get; set; } = new CustomizedPoint(); |
| | | } |
| | | } |
| | |
| | | using HalconDotNet; |
| | | using System; |
| | | using System.Collections.Generic; |
| | | using System.Collections.ObjectModel; |
| | | using System.ComponentModel; |
| | | using System.Drawing; |
| | | using System.IO; |
| | |
| | | { |
| | | public partial class ProcessControl |
| | | { |
| | | const int WAITTIME = 5000; |
| | | |
| | | Dictionary<int, int> machineFullTrayDict = new Dictionary<int, int>(); |
| | | Dictionary<int, int> machineEmptyTrayDict = new Dictionary<int, int>(); |
| | | List<TaskAssignInfo> taskAssignedList = new List<TaskAssignInfo>(); |
| | | |
| | | #region AGV事件 |
| | | private void OnAGVBatteryLvlChanged(SeerAGVDriver agv, float preBatteryLvl, float batteryLvl) |
| | | { |
| | | var bind = Config.AGVBindCollection.FirstOrDefault(u => u.AGVId == agv.Id); |
| | | SeerAGVInitialConfig iConfig = agv.InitialConfig as SeerAGVInitialConfig; |
| | | if (batteryLvl <= iConfig.BatteryLvlToCharge && preBatteryLvl > iConfig.BatteryLvlToCharge) |
| | | { |
| | | Task.Run(() => |
| | | { |
| | | var position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.Charge); |
| | | |
| | | if (position == null) |
| | | { |
| | | throw new ProcessException("未找到充电地点"); |
| | | } |
| | | |
| | | while (bind.UnitStatus != TaskStatus.Available) |
| | | { |
| | | if (bind.SetAGVStatus(TaskStatus.Running)) |
| | | { |
| | | bind.AGV.TaskOrder(position.PositionCode); |
| | | break; |
| | | } |
| | | |
| | | Thread.Sleep(WAITTIME); |
| | | } |
| | | }); |
| | | |
| | | return; |
| | | } |
| | | |
| | | if (batteryLvl >= iConfig.BatteryLvlChargeDone && preBatteryLvl < iConfig.BatteryLvlChargeDone) |
| | | { |
| | | var position = Config.PositionCollection.FirstOrDefault(u => u.PositionCode == agv.CurrentPosition); |
| | | |
| | | if (position != null && position.Description == PathPositionDefinition.Charge) |
| | | { |
| | | bind.SetAGVStatus(TaskStatus.Available); |
| | | } |
| | | return; |
| | | } |
| | | } |
| | | |
| | | private void OnAGVTaskStatusChanged(SeerAGVDriver agv, AGVTaskStatus taskStatus) |
| | | { |
| | | var bind = Config.AGVBindCollection.FirstOrDefault(u => u.AGVId == agv.Id); |
| | | |
| | | if (bind == null) |
| | | { |
| | | throw new ProcessException("未能根据AGV信息获取绑定设备信息", null); |
| | | } |
| | | |
| | | if (bind.AGVDest == agv.CurrentPosition && taskStatus == AGVTaskStatus.Completed) |
| | | { |
| | | //PathPosition loadEmptyTrayPosition = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.LoadEmptyTray); |
| | | |
| | | //if (bind.AGVDest == loadEmptyTrayPosition.PositionCode) |
| | | //{ |
| | | // bind.RobotStatus = TaskStatus.Running; |
| | | //} |
| | | |
| | | bind.SetAGVStatus(TaskStatus.Available); |
| | | } |
| | | } |
| | | |
| | | private void OnAGVPositionChanged(SeerAGVDriver agv, string positionCode) |
| | | { |
| | | var bind = Config.AGVBindCollection.FirstOrDefault(u => u.AGVId == agv.Id); |
| | | |
| | | if (bind == null) |
| | | { |
| | | throw new ProcessException("未能根据AGV信息获取绑定设备信息", null); |
| | | } |
| | | |
| | | if (bind.AGVDest == positionCode && agv.TaskStatus == AGVTaskStatus.Completed) |
| | | { |
| | | //PathPosition loadEmptyTrayPosition = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.LoadEmptyTray); |
| | | |
| | | //if (bind.AGVDest == loadEmptyTrayPosition.PositionCode) |
| | | //{ |
| | | // bind.RobotStatus = TaskStatus.Running; |
| | | //} |
| | | |
| | | bind.SetAGVStatus(TaskStatus.Available); |
| | | LogAsync(DateTime.Now, $"AGV到位{positionCode}", ""); |
| | | |
| | | PathPosition position = Config.PositionCollection.FirstOrDefault(p => p.PositionCode == bind.AGVDest); |
| | | switch (position.Description) |
| | | { |
| | | case PathPositionDefinition.LoadEmptyTray: |
| | | LogAsync(DateTime.Now, $"AGV完成,准备上空Tray", ""); |
| | | Robot_LoadEmptyTray(bind.Id, position); |
| | | break; |
| | | case PathPositionDefinition.LoadFullTray: |
| | | LogAsync(DateTime.Now, $"AGV完成,准备上满Tray", ""); |
| | | Robot_LoadFullTraySnap(bind.Id, position); |
| | | break; |
| | | case PathPositionDefinition.UnloadEmptyTray: |
| | | LogAsync(DateTime.Now, $"AGV完成,准备下空Tray", ""); |
| | | Robot_UnloadEmptyTraySnap(bind.Id, position); |
| | | break; |
| | | case PathPositionDefinition.UnloadFullTray: |
| | | LogAsync(DateTime.Now, $"AGV完成,准备下满Tray", ""); |
| | | Robot_UnloadFullTraySnap(bind.Id, position); |
| | | break; |
| | | default: |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | #endregion |
| | | |
| | | private void OnRobotMsgReceived(DateTime dt, AuboRobotDriver robot, RobotMsg msg) |
| | | { |
| | | LogAsync(dt, robot.Name + "接收信息", msg.GetDisplayText()); |
| | | |
| | | var bind = Config.AGVBindCollection.FirstOrDefault(u => u.RobotId == robot.Id); |
| | | |
| | | if (bind == null) |
| | | { |
| | | throw new ProcessException("未能根据机器人信息获取绑定设备信息", null); |
| | | } |
| | | |
| | | //List<AGVTaskModel> models = new List<AGVTaskModel>(); |
| | | |
| | | switch (msg.Action) |
| | | { |
| | | case RobotMsgAction.Load: |
| | | { |
| | | switch (msg.Para1) |
| | | { |
| | | case RobotMsgParas.EmptyTray: |
| | | { |
| | | bind.RobotStatus = bind.AGVStatus = TaskStatus.Available; |
| | | } |
| | | break; |
| | | case RobotMsgParas.FullTray: |
| | | { |
| | | machineFullTrayDict[msg.Para2]--; |
| | | bind.CurrentFullTray = int.Parse(msg.Datas[1]); |
| | | |
| | | if (machineFullTrayDict[msg.Para2] > 0 && !bind.IsFullTrayFull) |
| | | { |
| | | RobotMsg_UnloadEmptyTray.Para2 = msg.Para2; |
| | | robot.SendMsg(RobotMsg_UnloadEmptyTray, true); |
| | | } |
| | | else |
| | | { |
| | | bind.RobotStatus = TaskStatus.Available; |
| | | } |
| | | } |
| | | break; |
| | | default: |
| | | break; |
| | | } |
| | | } |
| | | break; |
| | | case RobotMsgAction.Unload: |
| | | { |
| | | switch (msg.Para1) |
| | | { |
| | | case RobotMsgParas.EmptyTray: |
| | | { |
| | | machineEmptyTrayDict[msg.Para2]++; |
| | | bind.CurrentEmptyTray = int.Parse(msg.Datas[0]); |
| | | |
| | | if (machineEmptyTrayDict[msg.Para2] < Config.Machine_EmptyTrayNum) |
| | | { |
| | | //bind.RobotIOHandle.Reset(); |
| | | //bind.RobotIOHandle.WaitOne(); |
| | | bind.Robot.MonitorHandle.WaitOne(); |
| | | //bind.Robot.IOChangedHandle.WaitOne(); |
| | | Thread.Sleep((bind.Robot.InitialConfig as AuboRobotInitialConfig).ScanInterval); |
| | | |
| | | if (!bind.IsFullTrayFull) |
| | | { |
| | | RobotMsg_UnloadEmptyTray.Para2 = msg.Para2; |
| | | robot.SendMsg(RobotMsg_UnloadEmptyTray, true); |
| | | } |
| | | else |
| | | { |
| | | bind.RobotStatus = TaskStatus.Available; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | bind.RobotStatus = TaskStatus.Available; |
| | | } |
| | | } |
| | | break; |
| | | case RobotMsgParas.FullTray: |
| | | { |
| | | bind.CurrentFullTray = int.Parse(msg.Datas[1]); |
| | | |
| | | //bind.RobotIOHandle.Reset(); |
| | | //bind.RobotIOHandle.WaitOne(); |
| | | bind.Robot.MonitorHandle.WaitOne(); |
| | | //bind.Robot.IOChangedHandle.WaitOne(); |
| | | Thread.Sleep((bind.Robot.InitialConfig as AuboRobotInitialConfig).ScanInterval); |
| | | |
| | | if (!bind.IsFullTrayEmpty) |
| | | { |
| | | Camera_UnloadFullTray(robot.Id, msg.Para2); |
| | | } |
| | | else |
| | | { |
| | | bind.RobotStatus = TaskStatus.Available; |
| | | } |
| | | } |
| | | break; |
| | | default: |
| | | break; |
| | | } |
| | | } |
| | | break; |
| | | case RobotMsgAction.Move: |
| | | { |
| | | switch (msg.Para1) |
| | | { |
| | | case RobotMsgParas.LineSnap: |
| | | { |
| | | Camera_UnloadFullTray(robot.Id, msg.Para2); |
| | | } |
| | | break; |
| | | case RobotMsgParas.LoadFullTraySnap: |
| | | { |
| | | Camera_LoadFullTray(robot.Id, msg.Para2); |
| | | } |
| | | break; |
| | | case RobotMsgParas.UnloadEmptyTraySnap: |
| | | { |
| | | Camera_UnloadEmptyTray(robot.Id, msg.Para2); |
| | | } |
| | | break; |
| | | case RobotMsgParas.Home: |
| | | { |
| | | bind.RobotStatus = TaskStatus.Available; |
| | | } |
| | | break; |
| | | default: |
| | | break; |
| | | } |
| | | } |
| | | break; |
| | | case RobotMsgAction.Calibration: |
| | | { |
| | | _calibReply.CalibIndex = int.Parse(msg.Datas[4]); |
| | | _calibReply.CalibPositionNo = msg.Para2; |
| | | _calibReply.RobotPosition = new CustomizedPoint(float.Parse(msg.Datas[0]), float.Parse(msg.Datas[1])); |
| | | |
| | | _calibReply.CalibHandle.Set(); |
| | | } |
| | | break; |
| | | case RobotMsgAction.StandardPoint: |
| | | { |
| | | _calibReply.CalibPositionNo = msg.Para2; |
| | | _calibReply.CalibHandle.Set(); |
| | | } |
| | | break; |
| | | default: |
| | | break; |
| | | } |
| | | |
| | | //if (models.Count > 0) |
| | | //{ |
| | | // models.ForEach(model => |
| | | // { |
| | | // if (!bind.TaskList.Any(t => t.MethodName == model.MethodName)) |
| | | // { |
| | | // model.OpConfig = new AGVBindOpConfig(bind.Id); |
| | | // bind.AddTask(model); |
| | | // } |
| | | // }); |
| | | //} |
| | | } |
| | | |
| | | public void QueryRobotIO() |
| | | { |
| | | RobotDict.Values.ToList().ForEach(r => |
| | | { |
| | | r.SendMsg(RobotMsgAction.IO, RobotMsgParas.Query, 0); |
| | | }); |
| | | } |
| | | |
| | | //private void OnBindUnitTaskInvoke(AGVTaskModel task) |
| | | //{ |
| | | // InvokeMethodDict[task.MethodName].Invoke(this, new object[] { task.OpConfig, task.Device }); |
| | | |
| | | // //var response = task.MethodFunc.Invoke(task.OpConfig, task.Device); |
| | | //} |
| | | |
| | | #region Robot监听事件 |
| | | //private void AddNewTaskToBind(string robotId, List<AGVTaskModel> models) |
| | | //{ |
| | | // var bind = Config.AGVBindCollection.FirstOrDefault(u => u.RobotId == robotId); |
| | | |
| | | // if (bind == null) |
| | | // { |
| | | // throw new ProcessException("未能根据机器人信息获取绑定设备信息", null); |
| | | // } |
| | | |
| | | // AddNewTaskToBind(bind, models); |
| | | //} |
| | | |
| | | //private void AddNewTaskToBind(AGVBindUnit bind, List<AGVTaskModel> models) |
| | | //{ |
| | | // if (models.Count > 0) |
| | | // { |
| | | // models.ForEach(model => |
| | | // { |
| | | // if (!bind.TaskList.Any(t => t.MethodName == model.MethodName)) |
| | | // { |
| | | // model.OpConfig = new AGVBindOpConfig(bind.Id); |
| | | // bind.AddTask(model); |
| | | // } |
| | | // }); |
| | | // } |
| | | //} |
| | | |
| | | [ProcessMethod("", "Robot_Monitor_Alarm", "机器人监听事件-报警", true)] |
| | | public ProcessResponse Robot_Monitor_Alarm(IOperationConfig config, IDevice device) |
| | | { |
| | |
| | | |
| | | if (bind == null) |
| | | { |
| | | throw new ProcessException("未能根据机器人信息获取绑定设备信息", null); |
| | | throw new ProcessException($"未能获取{device.Name}的绑定设备信息"); |
| | | } |
| | | |
| | | bind.AGV.PauseTask(); |
| | | bind.RobotStatus = TaskStatus.Warning; |
| | | bind.AGV.CancelTask(); |
| | | bind.UnitState = AGVState.Warning; |
| | | |
| | | return new ProcessResponse(true); |
| | | } |
| | |
| | | [ProcessMethod("", "Robot_Monitor_EmptyTrayEmpty", "机器人监听事件-空Tray区域清空", true)] |
| | | public ProcessResponse Robot_Monitor_EmptyTrayEmpty(IOperationConfig config, IDevice device) |
| | | { |
| | | bool isEmptyTrayEmpty = config.InputPara[0] == 0; |
| | | var bind = Config.AGVBindCollection.FirstOrDefault(u => u.RobotId == device.Id); |
| | | if (isEmptyTrayEmpty) |
| | | { |
| | | bind.IsEmptyTrayEmpty = true; |
| | | |
| | | Task.Run(() => |
| | | if (bind == null) |
| | | { |
| | | //Func<IOperationConfig, IDevice, ProcessResponse> action = AGV_LoadEmptyTray; |
| | | while (bind.IsEmptyTrayEmpty && !bind.IsEmptyTrayTaskAssigned) |
| | | { |
| | | //if (bind.TaskList.Count == 0) |
| | | if (bind.UnitStatus == TaskStatus.Available) |
| | | { |
| | | //List<AGVTaskModel> models = new List<AGVTaskModel>(); |
| | | //models.Add(new AGVTaskModel(TaskAvailableLevel.Both, "AGV_LoadEmptyTray")); |
| | | //models.Add(new AGVTaskModel(TaskAvailableLevel.AGV, "AfterEmptyTrayPositionArrived")); |
| | | throw new ProcessException($"未能获取{device.Name}的绑定设备信息"); |
| | | } |
| | | |
| | | //AddNewTaskToBind(device.Id, models); |
| | | bind.EmptyTrayNum = 0; |
| | | |
| | | if (AGV_LoadEmptyTray(bind.Id)) |
| | | { |
| | | bind.IsEmptyTrayTaskAssigned = true; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | Thread.Sleep(WAITTIME); |
| | | } |
| | | } |
| | | }); |
| | | } |
| | | else |
| | | { |
| | | bind.IsEmptyTrayEmpty = false; |
| | | bind.IsEmptyTrayTaskAssigned = false; |
| | | } |
| | | TrayTask task = new TrayTask(); |
| | | task.TaskType = TaskType.LoadEmptyTrayToAGV; |
| | | //task.Priority = 10; |
| | | task.SourceDeviceId = device.Id; |
| | | //task.SourceDeviceName = device.Name; |
| | | |
| | | //如果目前地址被占用,地址有可能为空,需要在任务指派时再次确认 |
| | | task.Location = Config.PositionCollection.FirstOrDefault(u => !u.IsOccupied && u.Description == PathPositionDefinition.LoadEmptyTray); |
| | | |
| | | InsertTask(task); |
| | | |
| | | return new ProcessResponse(true); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 信号要求长信号触发,避免误触发 |
| | | /// </summary> |
| | | /// <param name="config"></param> |
| | | /// <param name="device"></param> |
| | | /// <returns></returns> |
| | | [ProcessMethod("", "Robot_Monitor_FullTrayFull", "机器人监听事件-满Tray区域放满", true)] |
| | | public ProcessResponse Robot_Monitor_FullTrayFull(IOperationConfig config, IDevice device) |
| | | { |
| | | //(device as AuboRobotDriver).IOChangedHandle.Reset(); |
| | | bool isFullTrayFull = config.InputPara[0] == 1; |
| | | var bind = Config.AGVBindCollection.FirstOrDefault(u => u.RobotId == device.Id); |
| | | if (isFullTrayFull) |
| | | { |
| | | bind.IsFullTrayFull = true; |
| | | |
| | | Task.Run(() => |
| | | if (bind == null) |
| | | { |
| | | //Func<IOperationConfig, IDevice, ProcessResponse> action = AGV_UnloadFullTray; |
| | | while (bind.IsFullTrayFull && !bind.IsFullTrayTaskAssigned) |
| | | { |
| | | //if (bind.TaskList.Count == 0) |
| | | if (bind.UnitStatus == TaskStatus.Available) |
| | | { |
| | | //List<AGVTaskModel> models = new List<AGVTaskModel>(); |
| | | //models.Add(new AGVTaskModel(TaskAvailableLevel.Both, "AGV_UnloadFullTray")); |
| | | //models.Add(new AGVTaskModel(TaskAvailableLevel.AGV, "Robot_UnloadFullTray")); |
| | | |
| | | //AddNewTaskToBind(device.Id, models); |
| | | |
| | | if (AGV_UnloadFullTray(bind.Id)) |
| | | { |
| | | bind.IsFullTrayTaskAssigned = true; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | Thread.Sleep(WAITTIME); |
| | | } |
| | | } |
| | | }); |
| | | } |
| | | else |
| | | { |
| | | bind.IsFullTrayFull = false; |
| | | bind.IsFullTrayTaskAssigned = false; |
| | | throw new ProcessException($"未能获取{device.Name}的绑定设备信息"); |
| | | } |
| | | |
| | | //(device as AuboRobotDriver).IOChangedHandle.Set(); |
| | | //if (bind.FullTrayNum >= Config.AGVAvailableTrayNums) |
| | | { |
| | | bind.FullTrayNum = Config.AGVAvailableTrayNums; |
| | | |
| | | //bind.RobotIOHandle.Set(); |
| | | TrayTask task = new TrayTask(); |
| | | task.TaskType = TaskType.UnloadFullTrayToLine; |
| | | task.SourceDeviceId = device.Id; |
| | | |
| | | //如果目前地址被占用,地址有可能为空,需要在任务指派时再次确认 |
| | | task.Location = Config.PositionCollection.FirstOrDefault(u => !u.IsOccupied && u.Description == PathPositionDefinition.UnloadFullTray); |
| | | |
| | | InsertTask(task); |
| | | } |
| | | |
| | | return new ProcessResponse(true); |
| | | } |
| | |
| | | [ProcessMethod("", "Robot_Monitor_FullTrayEmpty", "机器人监听事件-满Tray区域清空", true)] |
| | | public ProcessResponse Robot_Monitor_FullTrayEmpty(IOperationConfig config, IDevice device) |
| | | { |
| | | //(device as AuboRobotDriver).IOChangedHandle.Reset(); |
| | | bool isFullTrayEmpty = config.InputPara[0] == 0; |
| | | var bind = Config.AGVBindCollection.FirstOrDefault(u => u.RobotId == device.Id); |
| | | bind.IsFullTrayEmpty = isFullTrayEmpty; |
| | | |
| | | //(device as AuboRobotDriver).IOChangedHandle.Set(); |
| | | //bind.RobotIOHandle.Set(); |
| | | if (bind == null) |
| | | { |
| | | throw new ProcessException($"未能获取{device.Name}的绑定设备信息"); |
| | | } |
| | | |
| | | bind.FullTrayNum = 0; |
| | | |
| | | return new ProcessResponse(true); |
| | | } |
| | |
| | | |
| | | if (bind == null) |
| | | { |
| | | throw new ProcessException("未能根据机器人信息获取绑定设备信息", null); |
| | | throw new ProcessException($"未能获取{device.Name}的绑定设备信息"); |
| | | } |
| | | bind.AGV.CancelTask(); |
| | | |
| | | //isEmptyTrayTaskAssigned = false; |
| | | //isFullTrayTaskAssigned = false; |
| | | |
| | | taskAssignedList.RemoveAll(u => u.AgvId == device.Id); |
| | | |
| | | //bind.ClearTask(); |
| | | bind.RobotStatus = bind.AGVStatus = TaskStatus.Available; |
| | | Reset(bind.Id); |
| | | |
| | | return new ProcessResponse(true); |
| | | } |
| | | #endregion |
| | | |
| | | #region 空Tray上料 |
| | | //[ProcessMethod("", "AGV_LoadEmptyTray", "AGV去往空Tray上料", true)] |
| | | //public ProcessResponse AGV_LoadEmptyTray(IOperationConfig config, IDevice device) |
| | | #region PLC监听事件 |
| | | [ProcessMethod("", "PLC_NoticeEmptyTray", "PLC通知需要上空Tray", true)] |
| | | public ProcessResponse PLC_NoticeEmptyTray(IOperationConfig config, IDevice device) |
| | | { |
| | | TrayTask task = new TrayTask(); |
| | | task.TaskType = TaskType.UnloadEmptyTrayToMachine; |
| | | task.SourceDeviceId = device.Id; |
| | | //task.SourceDeviceName = device.Name; |
| | | task.Location = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.UnloadEmptyTray && u.DeviceOwner == device.Id); |
| | | |
| | | InsertTask(task); |
| | | |
| | | return new ProcessResponse(true); |
| | | } |
| | | |
| | | [ProcessMethod("", "PLC_NoticeFullTray", "PLC通知满Tray需要取走", true)] |
| | | public ProcessResponse PLC_NoticeFullTray(IOperationConfig config, IDevice device) |
| | | { |
| | | TrayTask task = new TrayTask(); |
| | | task.TaskType = TaskType.LoadFullTrayFromMachine; |
| | | task.SourceDeviceId = device.Id; |
| | | //task.SourceDeviceName = device.Name; |
| | | task.Location = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.LoadFullTray && u.DeviceOwner == device.Id); |
| | | |
| | | InsertTask(task); |
| | | |
| | | return new ProcessResponse(true); |
| | | } |
| | | #endregion |
| | | |
| | | [ProcessMethod("OperationTest", "OperationDemo", "单步测试方法", true)] |
| | | public ProcessResponse OperationDemo(IOperationConfig config, IDevice device) |
| | | { |
| | | string s = (-1).ToString("D2"); |
| | | string a = (1).ToString("D2"); |
| | | |
| | | OperationTestConfig opConfig = config as OperationTestConfig; |
| | | |
| | | var bind = Config.AGVBindCollection.FirstOrDefault(u => u.AGVId == opConfig.AGVId); |
| | | if (bind == null) |
| | | throw new ProcessException("未能获取指定AGV信息或AGV绑定的设备信息"); |
| | | |
| | | switch (opConfig.TaskInfo.TaskType) |
| | | { |
| | | case TaskType.LoadEmptyTrayToAGV: |
| | | LoadEmptyTrayToAGV(opConfig.TaskInfo, bind); |
| | | break; |
| | | case TaskType.LoadFullTrayFromMachine: |
| | | LoadFullTrayFromMachine(opConfig.TaskInfo, bind); |
| | | break; |
| | | case TaskType.UnloadEmptyTrayToMachine: |
| | | UnloadEmptyTrayToMachine(opConfig.TaskInfo, bind); |
| | | break; |
| | | case TaskType.UnloadFullTrayToLine: |
| | | UnloadFullTrayToLine(opConfig.TaskInfo, bind); |
| | | break; |
| | | default: |
| | | throw new ProcessException($"未能指定{opConfig.TaskInfo.TaskType.ToString()}的对应方法"); |
| | | } |
| | | |
| | | return new ProcessResponse(true); |
| | | } |
| | | |
| | | #region old |
| | | //#region 空Tray上料 |
| | | ////[ProcessMethod("", "AGV_LoadEmptyTray", "AGV去往空Tray上料", true)] |
| | | ////public ProcessResponse AGV_LoadEmptyTray(IOperationConfig config, IDevice device) |
| | | ////{ |
| | | //// var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == (config as AGVBindOpConfig).BindId); |
| | | //// PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.LoadEmptyTray); |
| | | |
| | | //// if (position == null) |
| | | //// { |
| | | //// throw new ProcessException("路径配置未设置空Tray上料点"); |
| | | //// } |
| | | |
| | | //// bind.AGVDest = position.PositionCode; |
| | | //// bind.AGV.TaskOrder(position.PositionCode); |
| | | |
| | | //// bind.AGVStatus = TaskStatus.Running; |
| | | |
| | | //// return new ProcessResponse(true); |
| | | ////} |
| | | |
| | | //public bool AGV_LoadEmptyTray(string bindId) |
| | | //{ |
| | | // var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == (config as AGVBindOpConfig).BindId); |
| | | // var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == bindId); |
| | | // PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.LoadEmptyTray); |
| | | |
| | | // if (position == null) |
| | |
| | | // throw new ProcessException("路径配置未设置空Tray上料点"); |
| | | // } |
| | | |
| | | // if (bind.SetAGVStatus(TaskStatus.Running)) |
| | | // { |
| | | // bind.AGVDest = position.PositionCode; |
| | | // bind.AGV.TaskOrder(position.PositionCode); |
| | | |
| | | // bind.AGVStatus = TaskStatus.Running; |
| | | |
| | | // return new ProcessResponse(true); |
| | | // return true; |
| | | // } |
| | | // else |
| | | // { |
| | | // return false; |
| | | // } |
| | | //} |
| | | |
| | | public bool AGV_LoadEmptyTray(string bindId) |
| | | { |
| | | var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == bindId); |
| | | PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.LoadEmptyTray); |
| | | ////[ProcessMethod("", "AfterEmptyTrayPositionArrived", "到达空Tray上料点", true)] |
| | | ////public ProcessResponse AfterEmptyTrayPositionArrived(IOperationConfig config, IDevice device) |
| | | ////{ |
| | | //// var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == (config as AGVBindOpConfig).BindId); |
| | | //// PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.LoadEmptyTray); |
| | | |
| | | if (position == null) |
| | | { |
| | | throw new ProcessException("路径配置未设置空Tray上料点"); |
| | | } |
| | | //// if (position == null) |
| | | //// { |
| | | //// throw new ProcessException("路径配置未设置空Tray上料点", null); |
| | | //// } |
| | | |
| | | if (bind.SetAGVStatus(TaskStatus.Running)) |
| | | { |
| | | bind.AGVDest = position.PositionCode; |
| | | bind.AGV.TaskOrder(position.PositionCode); |
| | | //// if (bind.AGV.CurrentPosition != position.PositionCode) |
| | | //// { |
| | | //// throw new ProcessException("AGV尚未到达空Tray上料点", null); |
| | | //// } |
| | | |
| | | return true; |
| | | } |
| | | else |
| | | { |
| | | return false; |
| | | } |
| | | } |
| | | //// bind.Robot.SendMsg(RobotMsgAction.Load, RobotMsgParas.EmptyTray, 0); |
| | | //// bind.RobotStatus = TaskStatus.Running; |
| | | |
| | | //[ProcessMethod("", "AfterEmptyTrayPositionArrived", "到达空Tray上料点", true)] |
| | | //public ProcessResponse AfterEmptyTrayPositionArrived(IOperationConfig config, IDevice device) |
| | | //// return new ProcessResponse(true); |
| | | ////} |
| | | |
| | | //public void Robot_LoadEmptyTray(string bindId, PathPosition position) |
| | | //{ |
| | | // var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == (config as AGVBindOpConfig).BindId); |
| | | // PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.LoadEmptyTray); |
| | | // var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == bindId); |
| | | // //PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.LoadEmptyTray); |
| | | |
| | | // if (position == null) |
| | | // { |
| | |
| | | |
| | | // bind.Robot.SendMsg(RobotMsgAction.Load, RobotMsgParas.EmptyTray, 0); |
| | | // bind.RobotStatus = TaskStatus.Running; |
| | | |
| | | // return new ProcessResponse(true); |
| | | //} |
| | | |
| | | public void Robot_LoadEmptyTray(string bindId, PathPosition position) |
| | | { |
| | | var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == bindId); |
| | | //PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.LoadEmptyTray); |
| | | ////[ProcessMethod("", "EmptyTrayReady", "空Tray上料完成", true)] |
| | | ////public ProcessResponse EmptyTrayReady(IOperationConfig config, IDevice device) |
| | | ////{ |
| | | //// var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == (config as AGVBindOpConfig).BindId); |
| | | //// bind.RobotStatus = bind.AGVStatus = TaskStatus.Available; |
| | | |
| | | if (position == null) |
| | | { |
| | | throw new ProcessException("路径配置未设置空Tray上料点", null); |
| | | } |
| | | //// return new ProcessResponse(true); |
| | | ////} |
| | | //#endregion |
| | | |
| | | if (bind.AGV.CurrentPosition != position.PositionCode) |
| | | { |
| | | throw new ProcessException("AGV尚未到达空Tray上料点", null); |
| | | } |
| | | //#region 空Tray往机台下料 |
| | | ////bool isEmptyTrayNeed = false; |
| | | ////bool isEmptyTrayTaskAssigned = false; |
| | | //RobotMsg RobotMsg_UnloadEmptyTray = new RobotMsg(); |
| | | |
| | | bind.Robot.SendMsg(RobotMsgAction.Load, RobotMsgParas.EmptyTray, 0); |
| | | bind.RobotStatus = TaskStatus.Running; |
| | | } |
| | | |
| | | //[ProcessMethod("", "EmptyTrayReady", "空Tray上料完成", true)] |
| | | //public ProcessResponse EmptyTrayReady(IOperationConfig config, IDevice device) |
| | | //private async void CheckUnloadEmptyTrayTask(int positionNo) |
| | | //{ |
| | | // var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == (config as AGVBindOpConfig).BindId); |
| | | // bind.RobotStatus = bind.AGVStatus = TaskStatus.Available; |
| | | // await Task.Run(() => |
| | | // { |
| | | // var taskStatus = taskAssignedList.FirstOrDefault(u => u.PositionNo == positionNo); |
| | | |
| | | // return new ProcessResponse(true); |
| | | // if (taskStatus == null) |
| | | // return; |
| | | |
| | | // while (taskStatus.IsTaskNeed && !taskStatus.IsTaskAssgined) |
| | | // { |
| | | // //Func<IOperationConfig, IDevice, ProcessResponse> action = AGV_UnloadEmptyTray; |
| | | |
| | | // //if (!Config.AGVBindCollection.Any(b => b.TaskList.Any(t => t.MethodName == "AGV_UnloadEmptyTray"))) |
| | | // { |
| | | // var bind = Config.AGVBindCollection.FirstOrDefault(u => u.UnitStatus == TaskStatus.Available); |
| | | // if (bind != null) |
| | | // { |
| | | // var position = Config.PositionCollection.FirstOrDefault(u => u.PositionNo == positionNo); |
| | | // //AGVTaskModel model_AGV = new AGVTaskModel(TaskAvailableLevel.Both, "AGV_UnloadEmptyTray", new AGVBindOpConfig(bind.Id, position)); |
| | | // //AGVTaskModel model_Robot = new AGVTaskModel(TaskAvailableLevel.AGV, "Robot_UnloadEmptyTray", new AGVBindOpConfig(bind.Id)); |
| | | |
| | | // //bind.AddTask(model_AGV); |
| | | // //bind.AddTask(model_Robot); |
| | | |
| | | // if (AGV_UnloadEmptyTray(bind.Id, position)) |
| | | // { |
| | | // taskStatus.IsTaskAssgined = true; |
| | | // taskStatus.AgvId = bind.AGVId; |
| | | //} |
| | | #endregion |
| | | // } |
| | | // } |
| | | |
| | | #region 空Tray往机台下料 |
| | | //bool isEmptyTrayNeed = false; |
| | | //bool isEmptyTrayTaskAssigned = false; |
| | | RobotMsg RobotMsg_UnloadEmptyTray = new RobotMsg(); |
| | | // Thread.Sleep(WAITTIME); |
| | | // } |
| | | // }); |
| | | //} |
| | | |
| | | [ProcessMethod("", "PLC_NoticeEmptyTray", "PLC通知需要上空Tray", true)] |
| | | public ProcessResponse PLC_NoticeEmptyTray(IOperationConfig config, IDevice device) |
| | | { |
| | | if (config.InputPara == null || config.InputPara.Count == 0) |
| | | { |
| | | throw new ProcessException("上空Tray方法未配置输入参数", null); |
| | | } |
| | | |
| | | bool isEmptyTrayNeed = config.InputPara[0] == 1; |
| | | |
| | | if (isEmptyTrayNeed) |
| | | { |
| | | var position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.UnloadEmptyTray && u.DeviceOwner == device.Id); |
| | | |
| | | machineEmptyTrayDict[position.PositionNo] = 0; |
| | | |
| | | if (!taskAssignedList.Any(u => u.PositionNo == position.PositionNo)) |
| | | { |
| | | taskAssignedList.Add(new TaskAssignInfo() |
| | | { |
| | | IsTaskNeed = true, |
| | | IsTaskAssgined = false, |
| | | PositionNo = position.PositionNo, |
| | | }); |
| | | } |
| | | |
| | | CheckUnloadEmptyTrayTask(position.PositionNo); |
| | | } |
| | | |
| | | return new ProcessResponse(true); |
| | | } |
| | | |
| | | private async void CheckUnloadEmptyTrayTask(int positionNo) |
| | | { |
| | | await Task.Run(() => |
| | | { |
| | | var taskStatus = taskAssignedList.FirstOrDefault(u => u.PositionNo == positionNo); |
| | | |
| | | if (taskStatus == null) |
| | | return; |
| | | |
| | | while (taskStatus.IsTaskNeed && !taskStatus.IsTaskAssgined) |
| | | { |
| | | //Func<IOperationConfig, IDevice, ProcessResponse> action = AGV_UnloadEmptyTray; |
| | | |
| | | //if (!Config.AGVBindCollection.Any(b => b.TaskList.Any(t => t.MethodName == "AGV_UnloadEmptyTray"))) |
| | | { |
| | | var bind = Config.AGVBindCollection.FirstOrDefault(u => u.UnitStatus == TaskStatus.Available); |
| | | if (bind != null) |
| | | { |
| | | var position = Config.PositionCollection.FirstOrDefault(u => u.PositionNo == positionNo); |
| | | //AGVTaskModel model_AGV = new AGVTaskModel(TaskAvailableLevel.Both, "AGV_UnloadEmptyTray", new AGVBindOpConfig(bind.Id, position)); |
| | | //AGVTaskModel model_Robot = new AGVTaskModel(TaskAvailableLevel.AGV, "Robot_UnloadEmptyTray", new AGVBindOpConfig(bind.Id)); |
| | | |
| | | //bind.AddTask(model_AGV); |
| | | //bind.AddTask(model_Robot); |
| | | |
| | | if (AGV_UnloadEmptyTray(bind.Id, position)) |
| | | { |
| | | taskStatus.IsTaskAssgined = true; |
| | | taskStatus.AgvId = bind.AGVId; |
| | | } |
| | | } |
| | | } |
| | | |
| | | Thread.Sleep(WAITTIME); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | //[ProcessMethod("", "AGV_UnloadEmptyTray", "AGV去往卸载空Tray料位置", true)] |
| | | public bool AGV_UnloadEmptyTray(string bindId, PathPosition position) |
| | | { |
| | | var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == bindId); |
| | | |
| | | if (position == null) |
| | | { |
| | | throw new ProcessException("路径配置未设置空Tray下料点"); |
| | | } |
| | | |
| | | if (bind.SetAGVStatus(TaskStatus.Running)) |
| | | { |
| | | bind.AGVDest = position.PositionCode; |
| | | bind.AGV.TaskOrder(position.PositionCode); |
| | | |
| | | return true; |
| | | } |
| | | else |
| | | { |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | //[ProcessMethod("", "Robot_UnloadEmptyTray", "机器人运动至空Tray拍照位置", true)] |
| | | public void Robot_UnloadEmptyTraySnap(string bindId, PathPosition position) |
| | | { |
| | | var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == bindId); |
| | | //PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.UnloadEmptyTray && u.PositionCode == bind.AGV.CurrentPosition); |
| | | |
| | | if (position == null) |
| | | { |
| | | throw new ProcessException("路径配置未设置空Tray下料点"); |
| | | } |
| | | |
| | | taskAssignedList.RemoveAll(u => u.AgvId == bind.AGVId && u.PositionNo == position.PositionNo); |
| | | |
| | | bind.RobotStatus = TaskStatus.Running; |
| | | bind.Robot.SendMsg(RobotMsgAction.Move, RobotMsgParas.UnloadEmptyTraySnap, position.PositionNo); |
| | | } |
| | | |
| | | //[ProcessMethod("", "Camera_UnloadEmptyTray", "相机确认空Tray卸载机器人位置调整", true)] |
| | | //public ProcessResponse Camera_UnloadEmptyTray(IOperationConfig config, IDevice device) |
| | | ////[ProcessMethod("", "AGV_UnloadEmptyTray", "AGV去往卸载空Tray料位置", true)] |
| | | //public bool AGV_UnloadEmptyTray(string bindId, PathPosition position) |
| | | //{ |
| | | // var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == (config as AGVBindOpConfig).BindId); |
| | | // var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == bindId); |
| | | |
| | | // PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.UnloadEmptyTray); |
| | | // if (position == null) |
| | | // { |
| | | // throw new ProcessException("路径配置未设置空Tray下料点"); |
| | | // } |
| | | |
| | | // if (bind.SetAGVStatus(TaskStatus.Running)) |
| | | // { |
| | | // bind.AGVDest = position.PositionCode; |
| | | // bind.AGV.TaskOrder(position.PositionCode); |
| | | |
| | | // return true; |
| | | // } |
| | | // else |
| | | // { |
| | | // return false; |
| | | // } |
| | | //} |
| | | |
| | | ////[ProcessMethod("", "Robot_UnloadEmptyTray", "机器人运动至空Tray拍照位置", true)] |
| | | //public void Robot_UnloadEmptyTraySnap(string bindId, PathPosition position) |
| | | //{ |
| | | // var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == bindId); |
| | | // //PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.UnloadEmptyTray && u.PositionCode == bind.AGV.CurrentPosition); |
| | | |
| | | // if (position == null) |
| | | // { |
| | | // throw new ProcessException("路径配置未设置空Tray下料点"); |
| | | // } |
| | | |
| | | // taskAssignedList.RemoveAll(u => u.AgvId == bind.AGVId && u.PositionNo == position.PositionNo); |
| | | |
| | | // bind.RobotStatus = TaskStatus.Running; |
| | | // bind.Robot.SendMsg(RobotMsgAction.Move, RobotMsgParas.UnloadEmptyTraySnap, position.PositionNo); |
| | | //} |
| | | |
| | | ////[ProcessMethod("", "Camera_UnloadEmptyTray", "相机确认空Tray卸载机器人位置调整", true)] |
| | | ////public ProcessResponse Camera_UnloadEmptyTray(IOperationConfig config, IDevice device) |
| | | ////{ |
| | | //// var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == (config as AGVBindOpConfig).BindId); |
| | | |
| | | //// PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.UnloadEmptyTray); |
| | | |
| | | //// if (position == null) |
| | | //// { |
| | | //// throw new ProcessException("路径配置未设置空Tray下料点"); |
| | | //// } |
| | | |
| | | //// if (bind.AGV.CurrentPosition != position.PositionCode) |
| | | //// { |
| | | //// throw new ProcessException("AGV当前未处于空Tray下料点"); |
| | | //// } |
| | | |
| | | //// PositionVisionConfig visionConfig = Config.VisionConfigCollection.FirstOrDefault(u => u.PositionCode == position.PositionCode && u.CameraId == bind.CameraId); |
| | | |
| | | //// if (visionConfig == null) |
| | | //// { |
| | | //// throw new ProcessException("未配置该相机的空Tray下料点的视觉操作配置"); |
| | | //// } |
| | | |
| | | //// float x = 0; |
| | | //// float y = 0; |
| | | //// float angle = 0; |
| | | |
| | | //// using (HObject hImage = CollectHImage(bind.Camera, visionConfig.CameraOpConfig, "Camera_UnloadEmptyTray")) |
| | | //// { |
| | | //// string toolPath = visionConfig.CameraOpConfig.AlgorithemPath; |
| | | //// if (!_halconToolDict.ContainsKey(toolPath)) |
| | | //// { |
| | | //// throw new ProcessException($"未配置Camera_UnloadEmptyTray的视觉算法路径"); |
| | | //// } |
| | | |
| | | //// var tool = _halconToolDict[toolPath]; |
| | | //// tool.SetDictionary(new Dictionary<string, HTuple>() { { "OUTPUT_X", new HTuple() }, { "OUTPUT_Y", new HTuple() }, { "OUTPUT_Angle", new HTuple() } }, new Dictionary<string, HObject>() { { "INPUT_Image", hImage } }); |
| | | //// tool.RunProcedure(); |
| | | |
| | | //// x = (float)tool.GetResultTuple("OUTPUT_X").D; |
| | | //// y = (float)tool.GetResultTuple("OUTPUT_Y").D; |
| | | //// angle = (float)tool.GetResultTuple("OUTPUT_Angle").D; |
| | | //// } |
| | | |
| | | //// if (x <= 0 || y <= 0) |
| | | //// { |
| | | //// throw new ProcessException("Camera_UnloadEmptyTray视觉计算获取点位不可小于0"); |
| | | //// } |
| | | |
| | | //// float dx = visionConfig.StandardPoint.X - x; |
| | | //// float dy = visionConfig.StandardPoint.Y - y; |
| | | |
| | | //// HOperatorSet.AffineTransPoint2d(new HTuple(visionConfig.Matrix[0], visionConfig.Matrix[1], 0, visionConfig.Matrix[3], visionConfig.Matrix[4], 0), dx, dy, out HTuple dx_Robot, out HTuple dy_Robot); |
| | | |
| | | //// bind.Robot.SendMsg(RobotMsgAction.Unload, RobotMsgParas.EmptyTray, position.PositionNo, new List<float>() { (float)dx_Robot.D, (float)dy_Robot.D, angle }); |
| | | |
| | | //// return new ProcessResponse(true); |
| | | ////} |
| | | |
| | | //public ProcessResponse Camera_UnloadEmptyTray(string robotId, int positionNum) |
| | | //{ |
| | | // var bind = Config.AGVBindCollection.FirstOrDefault(u => u.RobotId == robotId); |
| | | |
| | | // PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.PositionNo == positionNum); |
| | | |
| | | // if (position == null) |
| | | // { |
| | |
| | | // throw new ProcessException("AGV当前未处于空Tray下料点"); |
| | | // } |
| | | |
| | | // float adjust_X = 0.0f; |
| | | // float adjust_Y = 0.0f; |
| | | // float adjust_Angle = 0.0f; |
| | | |
| | | // if (Config.IsEnableVisionGuide) |
| | | // { |
| | | // PositionVisionConfig visionConfig = Config.VisionConfigCollection.FirstOrDefault(u => u.PositionCode == position.PositionCode && u.CameraId == bind.CameraId); |
| | | |
| | | // if (visionConfig == null) |
| | |
| | | |
| | | // HOperatorSet.AffineTransPoint2d(new HTuple(visionConfig.Matrix[0], visionConfig.Matrix[1], 0, visionConfig.Matrix[3], visionConfig.Matrix[4], 0), dx, dy, out HTuple dx_Robot, out HTuple dy_Robot); |
| | | |
| | | // bind.Robot.SendMsg(RobotMsgAction.Unload, RobotMsgParas.EmptyTray, position.PositionNo, new List<float>() { (float)dx_Robot.D, (float)dy_Robot.D, angle }); |
| | | // adjust_X = (float)dx_Robot.D; |
| | | // adjust_Y = (float)dy_Robot.D; |
| | | // adjust_Angle = visionConfig.StandardPoint.Angle - angle; |
| | | // } |
| | | |
| | | // //bind.Robot.SendMsg(RobotMsgAction.Unload, RobotMsgParas.EmptyTray, position.PositionNo, new List<float>() { (float)dx_Robot.D, (float)dy_Robot.D, angle }); |
| | | // RobotMsg_UnloadEmptyTray.Action = RobotMsgAction.Unload; |
| | | // RobotMsg_UnloadEmptyTray.Para1 = RobotMsgParas.EmptyTray; |
| | | // RobotMsg_UnloadEmptyTray.Para2 = position.PositionNo; |
| | | // RobotMsg_UnloadEmptyTray.Datas = new List<float>() { adjust_X, adjust_Y, 0, adjust_Angle }.ConvertAll(s => s.ToString()).ToList(); |
| | | // bind.Robot.SendMsg(RobotMsg_UnloadEmptyTray, true); |
| | | |
| | | // return new ProcessResponse(true); |
| | | //} |
| | | //#endregion |
| | | |
| | | public ProcessResponse Camera_UnloadEmptyTray(string robotId, int positionNum) |
| | | { |
| | | var bind = Config.AGVBindCollection.FirstOrDefault(u => u.RobotId == robotId); |
| | | //#region 从机台上满Tray |
| | | ////bool isFullTrayNeed = false; |
| | | ////bool isFullTrayTaskAssigned = false; |
| | | //RobotMsg RobotMsg_LoadFullTray = new RobotMsg(); |
| | | |
| | | PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.PositionNo == positionNum); |
| | | |
| | | if (position == null) |
| | | { |
| | | throw new ProcessException("路径配置未设置空Tray下料点"); |
| | | } |
| | | |
| | | if (bind.AGV.CurrentPosition != position.PositionCode) |
| | | { |
| | | throw new ProcessException("AGV当前未处于空Tray下料点"); |
| | | } |
| | | |
| | | float adjust_X = 0.0f; |
| | | float adjust_Y = 0.0f; |
| | | float adjust_Angle = 0.0f; |
| | | |
| | | if (Config.IsEnableVisionGuide) |
| | | { |
| | | PositionVisionConfig visionConfig = Config.VisionConfigCollection.FirstOrDefault(u => u.PositionCode == position.PositionCode && u.CameraId == bind.CameraId); |
| | | |
| | | if (visionConfig == null) |
| | | { |
| | | throw new ProcessException("未配置该相机的空Tray下料点的视觉操作配置"); |
| | | } |
| | | |
| | | float x = 0; |
| | | float y = 0; |
| | | float angle = 0; |
| | | |
| | | using (HObject hImage = CollectHImage(bind.Camera, visionConfig.CameraOpConfig, "Camera_UnloadEmptyTray")) |
| | | { |
| | | string toolPath = visionConfig.CameraOpConfig.AlgorithemPath; |
| | | if (!_halconToolDict.ContainsKey(toolPath)) |
| | | { |
| | | throw new ProcessException($"未配置Camera_UnloadEmptyTray的视觉算法路径"); |
| | | } |
| | | |
| | | var tool = _halconToolDict[toolPath]; |
| | | tool.SetDictionary(new Dictionary<string, HTuple>() { { "OUTPUT_X", new HTuple() }, { "OUTPUT_Y", new HTuple() }, { "OUTPUT_Angle", new HTuple() } }, new Dictionary<string, HObject>() { { "INPUT_Image", hImage } }); |
| | | tool.RunProcedure(); |
| | | |
| | | x = (float)tool.GetResultTuple("OUTPUT_X").D; |
| | | y = (float)tool.GetResultTuple("OUTPUT_Y").D; |
| | | angle = (float)tool.GetResultTuple("OUTPUT_Angle").D; |
| | | } |
| | | |
| | | if (x <= 0 || y <= 0) |
| | | { |
| | | throw new ProcessException("Camera_UnloadEmptyTray视觉计算获取点位不可小于0"); |
| | | } |
| | | |
| | | float dx = visionConfig.StandardPoint.X - x; |
| | | float dy = visionConfig.StandardPoint.Y - y; |
| | | |
| | | HOperatorSet.AffineTransPoint2d(new HTuple(visionConfig.Matrix[0], visionConfig.Matrix[1], 0, visionConfig.Matrix[3], visionConfig.Matrix[4], 0), dx, dy, out HTuple dx_Robot, out HTuple dy_Robot); |
| | | |
| | | adjust_X = (float)dx_Robot.D; |
| | | adjust_Y = (float)dy_Robot.D; |
| | | adjust_Angle = visionConfig.StandardPoint.Angle - angle; |
| | | } |
| | | |
| | | //bind.Robot.SendMsg(RobotMsgAction.Unload, RobotMsgParas.EmptyTray, position.PositionNo, new List<float>() { (float)dx_Robot.D, (float)dy_Robot.D, angle }); |
| | | RobotMsg_UnloadEmptyTray.Action = RobotMsgAction.Unload; |
| | | RobotMsg_UnloadEmptyTray.Para1 = RobotMsgParas.EmptyTray; |
| | | RobotMsg_UnloadEmptyTray.Para2 = position.PositionNo; |
| | | RobotMsg_UnloadEmptyTray.Datas = new List<float>() { adjust_X, adjust_Y, 0, adjust_Angle }.ConvertAll(s => s.ToString()).ToList(); |
| | | bind.Robot.SendMsg(RobotMsg_UnloadEmptyTray, true); |
| | | |
| | | return new ProcessResponse(true); |
| | | } |
| | | #endregion |
| | | |
| | | #region 从机台上满Tray |
| | | //bool isFullTrayNeed = false; |
| | | //bool isFullTrayTaskAssigned = false; |
| | | RobotMsg RobotMsg_LoadFullTray = new RobotMsg(); |
| | | |
| | | [ProcessMethod("", "PLC_NoticeFullTray", "PLC通知满Tray需要取走", true)] |
| | | public ProcessResponse PLC_NoticeFullTray(IOperationConfig config, IDevice device) |
| | | { |
| | | if (config.InputPara == null || config.InputPara.Count == 0) |
| | | { |
| | | throw new ProcessException("上空Tray方法未配置输入参数", null); |
| | | } |
| | | |
| | | bool isFullTrayNeed = config.InputPara[0] == 1; |
| | | if (isFullTrayNeed) |
| | | { |
| | | var position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.LoadFullTray && u.DeviceOwner == device.Id); |
| | | |
| | | machineFullTrayDict[position.PositionNo] = Config.Machine_FullTrayNum; |
| | | |
| | | if (!taskAssignedList.Any(u => u.PositionNo == position.PositionNo)) |
| | | { |
| | | taskAssignedList.Add(new TaskAssignInfo() |
| | | { |
| | | IsTaskNeed = true, |
| | | IsTaskAssgined = false, |
| | | PositionNo = position.PositionNo, |
| | | }); |
| | | } |
| | | |
| | | CheckFullTrayTask(position.PositionNo); |
| | | } |
| | | |
| | | return new ProcessResponse(true); |
| | | } |
| | | |
| | | private async void CheckFullTrayTask(int positionNo) |
| | | { |
| | | await Task.Run(() => |
| | | { |
| | | var taskStatus = taskAssignedList.FirstOrDefault(u => u.PositionNo == positionNo); |
| | | |
| | | if (taskStatus == null) |
| | | return; |
| | | |
| | | while (taskStatus.IsTaskNeed && !taskStatus.IsTaskAssgined) |
| | | { |
| | | //Func<IOperationConfig, IDevice, ProcessResponse> action = AGV_LoadFullTray; |
| | | |
| | | //if (!Config.AGVBindCollection.Any(b => b.TaskList.Any(t => t.MethodFunc.Method.Name == action.Method.Name))) |
| | | { |
| | | var bind = Config.AGVBindCollection.FirstOrDefault(u => u.UnitStatus == TaskStatus.Available); |
| | | if (bind != null) |
| | | { |
| | | var position = Config.PositionCollection.FirstOrDefault(u => u.PositionNo == positionNo); |
| | | //AGVTaskModel model_AGV = new AGVTaskModel(TaskAvailableLevel.Both, "AGV_LoadFullTray", new AGVBindOpConfig(bind.Id, position)); |
| | | //AGVTaskModel model_Robot = new AGVTaskModel(TaskAvailableLevel.AGV, "Robot_LoadFullTray", new AGVBindOpConfig(bind.Id)); |
| | | |
| | | //bind.AddTask(model_AGV); |
| | | //bind.AddTask(model_Robot); |
| | | |
| | | if (AGV_LoadFullTray(bind.Id, position)) |
| | | { |
| | | taskStatus.IsTaskAssgined = true; |
| | | taskStatus.AgvId = bind.AGVId; |
| | | } |
| | | } |
| | | } |
| | | |
| | | Thread.Sleep(300); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | //[ProcessMethod("", "AGV_LoadFullTray", "AGV去往满Tray上料位置", true)] |
| | | public bool AGV_LoadFullTray(string bindId, PathPosition position) |
| | | { |
| | | var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == bindId); |
| | | |
| | | //PathPosition position = (config as AGVBindOpConfig).Position; |
| | | |
| | | if (position == null) |
| | | { |
| | | throw new ProcessException("路径配置未设置满Tray上料点"); |
| | | } |
| | | |
| | | if (bind.SetAGVStatus(TaskStatus.Running)) |
| | | { |
| | | bind.AGVDest = position.PositionCode; |
| | | bind.AGV.TaskOrder(position.PositionCode); |
| | | |
| | | return true; |
| | | } |
| | | else |
| | | { |
| | | return false; |
| | | } |
| | | |
| | | //return new ProcessResponse(true); |
| | | } |
| | | |
| | | //[ProcessMethod("", "Robot_LoadFullTray", "机器人运动至满Tray拍照位置", true)] |
| | | //public ProcessResponse Robot_LoadFullTray(IOperationConfig config, IDevice device) |
| | | //private async void CheckFullTrayTask(int positionNo) |
| | | //{ |
| | | // var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == (config as AGVBindOpConfig).BindId); |
| | | // PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.LoadFullTray && u.PositionCode == bind.AGV.CurrentPosition); |
| | | // await Task.Run(() => |
| | | // { |
| | | // var taskStatus = taskAssignedList.FirstOrDefault(u => u.PositionNo == positionNo); |
| | | |
| | | // if (taskStatus == null) |
| | | // return; |
| | | |
| | | // while (taskStatus.IsTaskNeed && !taskStatus.IsTaskAssgined) |
| | | // { |
| | | // //Func<IOperationConfig, IDevice, ProcessResponse> action = AGV_LoadFullTray; |
| | | |
| | | // //if (!Config.AGVBindCollection.Any(b => b.TaskList.Any(t => t.MethodFunc.Method.Name == action.Method.Name))) |
| | | // { |
| | | // var bind = Config.AGVBindCollection.FirstOrDefault(u => u.UnitStatus == TaskStatus.Available); |
| | | // if (bind != null) |
| | | // { |
| | | // var position = Config.PositionCollection.FirstOrDefault(u => u.PositionNo == positionNo); |
| | | // //AGVTaskModel model_AGV = new AGVTaskModel(TaskAvailableLevel.Both, "AGV_LoadFullTray", new AGVBindOpConfig(bind.Id, position)); |
| | | // //AGVTaskModel model_Robot = new AGVTaskModel(TaskAvailableLevel.AGV, "Robot_LoadFullTray", new AGVBindOpConfig(bind.Id)); |
| | | |
| | | // //bind.AddTask(model_AGV); |
| | | // //bind.AddTask(model_Robot); |
| | | |
| | | // if (AGV_LoadFullTray(bind.Id, position)) |
| | | // { |
| | | // taskStatus.IsTaskAssgined = true; |
| | | // taskStatus.AgvId = bind.AGVId; |
| | | // } |
| | | // } |
| | | // } |
| | | |
| | | // Thread.Sleep(300); |
| | | // } |
| | | // }); |
| | | //} |
| | | |
| | | ////[ProcessMethod("", "AGV_LoadFullTray", "AGV去往满Tray上料位置", true)] |
| | | //public bool AGV_LoadFullTray(string bindId, PathPosition position) |
| | | //{ |
| | | // var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == bindId); |
| | | |
| | | // //PathPosition position = (config as AGVBindOpConfig).Position; |
| | | |
| | | // if (position == null) |
| | | // { |
| | | // throw new ProcessException("路径配置未设置满Tray上料点"); |
| | | // } |
| | | |
| | | // if (bind.SetAGVStatus(TaskStatus.Running)) |
| | | // { |
| | | // bind.AGVDest = position.PositionCode; |
| | | // bind.AGV.TaskOrder(position.PositionCode); |
| | | |
| | | // return true; |
| | | // } |
| | | // else |
| | | // { |
| | | // return false; |
| | | // } |
| | | |
| | | // //return new ProcessResponse(true); |
| | | //} |
| | | |
| | | ////[ProcessMethod("", "Robot_LoadFullTray", "机器人运动至满Tray拍照位置", true)] |
| | | ////public ProcessResponse Robot_LoadFullTray(IOperationConfig config, IDevice device) |
| | | ////{ |
| | | //// var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == (config as AGVBindOpConfig).BindId); |
| | | //// PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.LoadFullTray && u.PositionCode == bind.AGV.CurrentPosition); |
| | | |
| | | //// if (position == null) |
| | | //// { |
| | | //// throw new ProcessException("路径配置未设置满Tray上料点"); |
| | | //// } |
| | | |
| | | //// //if (bind.AGV.CurrentPosition != position.PositionCode) |
| | | //// //{ |
| | | //// // throw new ProcessException("AGV当前未处于满Tray上料点"); |
| | | //// //} |
| | | |
| | | //// bind.RobotStatus = TaskStatus.Running; |
| | | //// bind.Robot.SendMsg(RobotMsgAction.Move, RobotMsgParas.LoadFullTraySnap, position.PositionNo); |
| | | |
| | | //// return new ProcessResponse(true); |
| | | ////} |
| | | |
| | | //public void Robot_LoadFullTraySnap(string bindId, PathPosition position) |
| | | //{ |
| | | // var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == bindId); |
| | | // //PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.LoadFullTray && u.PositionCode == bind.AGV.CurrentPosition); |
| | | |
| | | // if (position == null) |
| | | // { |
| | |
| | | |
| | | // bind.RobotStatus = TaskStatus.Running; |
| | | // bind.Robot.SendMsg(RobotMsgAction.Move, RobotMsgParas.LoadFullTraySnap, position.PositionNo); |
| | | |
| | | // return new ProcessResponse(true); |
| | | //} |
| | | |
| | | public void Robot_LoadFullTraySnap(string bindId, PathPosition position) |
| | | { |
| | | var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == bindId); |
| | | //PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.LoadFullTray && u.PositionCode == bind.AGV.CurrentPosition); |
| | | ////[ProcessMethod("", "Camera_LoadFullTray", "相机确认满Tray上料机器人位置调整", true)] |
| | | ////public ProcessResponse Camera_LoadFullTray(IOperationConfig config, IDevice device) |
| | | ////{ |
| | | //// var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == (config as AGVBindOpConfig).BindId); |
| | | |
| | | if (position == null) |
| | | { |
| | | throw new ProcessException("路径配置未设置满Tray上料点"); |
| | | } |
| | | //// PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.LoadFullTray); |
| | | |
| | | //if (bind.AGV.CurrentPosition != position.PositionCode) |
| | | //// if (position == null) |
| | | //// { |
| | | //// throw new ProcessException("路径配置未设置满Tray上料点"); |
| | | //// } |
| | | |
| | | //// if (bind.AGV.CurrentPosition != position.PositionCode) |
| | | //// { |
| | | //// throw new ProcessException("AGV当前未处于满Tray上料点"); |
| | | //// } |
| | | |
| | | //// PositionVisionConfig visionConfig = Config.VisionConfigCollection.FirstOrDefault(u => u.PositionCode == position.PositionCode && u.CameraId == bind.CameraId); |
| | | |
| | | //// if (visionConfig == null) |
| | | //// { |
| | | //// throw new ProcessException("未配置该相机的满Tray上料点的视觉操作配置"); |
| | | //// } |
| | | |
| | | //// float x = 0; |
| | | //// float y = 0; |
| | | //// float angle = 0; |
| | | |
| | | //// using (HObject hImage = CollectHImage(bind.Camera, visionConfig.CameraOpConfig, "Camera_LoadFullTray")) |
| | | //// { |
| | | //// string toolPath = visionConfig.CameraOpConfig.AlgorithemPath; |
| | | //// if (!_halconToolDict.ContainsKey(toolPath)) |
| | | //// { |
| | | //// throw new ProcessException($"未配置Camera_LoadFullTray的视觉算法路径"); |
| | | //// } |
| | | |
| | | //// var tool = _halconToolDict[toolPath]; |
| | | //// tool.SetDictionary(new Dictionary<string, HTuple>() { { "OUTPUT_X", new HTuple() }, { "OUTPUT_Y", new HTuple() }, { "OUTPUT_Angle", new HTuple() } }, new Dictionary<string, HObject>() { { "INPUT_Image", hImage } }); |
| | | //// tool.RunProcedure(); |
| | | |
| | | //// x = (float)tool.GetResultTuple("OUTPUT_X").D; |
| | | //// y = (float)tool.GetResultTuple("OUTPUT_Y").D; |
| | | //// angle = (float)tool.GetResultTuple("OUTPUT_Angle").D; |
| | | //// } |
| | | |
| | | //// if (x <= 0 || y <= 0) |
| | | //// { |
| | | //// throw new ProcessException("Camera_LoadFullTray视觉计算获取点位不可小于0"); |
| | | //// } |
| | | |
| | | //// float dx = visionConfig.StandardPoint.X - x; |
| | | //// float dy = visionConfig.StandardPoint.Y - y; |
| | | |
| | | //// HOperatorSet.AffineTransPoint2d(new HTuple(visionConfig.Matrix[0], visionConfig.Matrix[1], 0, visionConfig.Matrix[3], visionConfig.Matrix[4], 0), dx, dy, out HTuple dx_Robot, out HTuple dy_Robot); |
| | | |
| | | //// bind.Robot.SendMsg(RobotMsgAction.Load, RobotMsgParas.FullTray, position.PositionNo, new List<float>() { (float)dx_Robot.D, (float)dy_Robot.D, angle }); |
| | | |
| | | //// return new ProcessResponse(true); |
| | | ////} |
| | | |
| | | //public ProcessResponse Camera_LoadFullTray(string robotId, int positionNum) |
| | | //{ |
| | | // throw new ProcessException("AGV当前未处于满Tray上料点"); |
| | | //} |
| | | // var bind = Config.AGVBindCollection.FirstOrDefault(u => u.RobotId == robotId); |
| | | |
| | | bind.RobotStatus = TaskStatus.Running; |
| | | bind.Robot.SendMsg(RobotMsgAction.Move, RobotMsgParas.LoadFullTraySnap, position.PositionNo); |
| | | } |
| | | |
| | | //[ProcessMethod("", "Camera_LoadFullTray", "相机确认满Tray上料机器人位置调整", true)] |
| | | //public ProcessResponse Camera_LoadFullTray(IOperationConfig config, IDevice device) |
| | | //{ |
| | | // var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == (config as AGVBindOpConfig).BindId); |
| | | |
| | | // PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.LoadFullTray); |
| | | // PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.PositionNo == positionNum); |
| | | |
| | | // if (position == null) |
| | | // { |
| | |
| | | // throw new ProcessException("AGV当前未处于满Tray上料点"); |
| | | // } |
| | | |
| | | // float adjust_X = 0.0f; |
| | | // float adjust_Y = 0.0f; |
| | | // float adjust_Angle = 0.0f; |
| | | |
| | | // if (Config.IsEnableVisionGuide) |
| | | // { |
| | | // PositionVisionConfig visionConfig = Config.VisionConfigCollection.FirstOrDefault(u => u.PositionCode == position.PositionCode && u.CameraId == bind.CameraId); |
| | | |
| | | // if (visionConfig == null) |
| | |
| | | |
| | | // HOperatorSet.AffineTransPoint2d(new HTuple(visionConfig.Matrix[0], visionConfig.Matrix[1], 0, visionConfig.Matrix[3], visionConfig.Matrix[4], 0), dx, dy, out HTuple dx_Robot, out HTuple dy_Robot); |
| | | |
| | | // bind.Robot.SendMsg(RobotMsgAction.Load, RobotMsgParas.FullTray, position.PositionNo, new List<float>() { (float)dx_Robot.D, (float)dy_Robot.D, angle }); |
| | | // adjust_X = (float)dx_Robot.D; |
| | | // adjust_Y = (float)dy_Robot.D; |
| | | // adjust_Angle = visionConfig.StandardPoint.Angle - angle; |
| | | // } |
| | | |
| | | // //bind.Robot.SendMsg(RobotMsgAction.Load, RobotMsgParas.FullTray, position.PositionNo, new List<float>() { (float)dx_Robot.D, (float)dy_Robot.D, angle }); |
| | | // RobotMsg_LoadFullTray.Action = RobotMsgAction.Load; |
| | | // RobotMsg_LoadFullTray.Para1 = RobotMsgParas.FullTray; |
| | | // RobotMsg_LoadFullTray.Para2 = position.PositionNo; |
| | | // RobotMsg_LoadFullTray.Datas = new List<float>() { adjust_X, adjust_Y, 0, adjust_Angle }.ConvertAll(s => s.ToString()).ToList(); |
| | | // bind.Robot.SendMsg(RobotMsg_LoadFullTray, true); |
| | | |
| | | // return new ProcessResponse(true); |
| | | //} |
| | | //#endregion |
| | | |
| | | public ProcessResponse Camera_LoadFullTray(string robotId, int positionNum) |
| | | { |
| | | var bind = Config.AGVBindCollection.FirstOrDefault(u => u.RobotId == robotId); |
| | | //#region 满Tray产线下料 |
| | | ////[ProcessMethod("", "AGV_UnloadFullTray", "AGV去往卸载满Tray料", true)] |
| | | ////public ProcessResponse AGV_UnloadFullTray(IOperationConfig config, IDevice device) |
| | | ////{ |
| | | //// var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == (config as AGVBindOpConfig).BindId); |
| | | //// PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.UnloadFullTray); |
| | | |
| | | PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.PositionNo == positionNum); |
| | | //// if (position == null) |
| | | //// { |
| | | //// throw new ProcessException("路径配置未设置满Tray下料点"); |
| | | //// } |
| | | |
| | | if (position == null) |
| | | { |
| | | throw new ProcessException("路径配置未设置满Tray上料点"); |
| | | } |
| | | //// bind.AGVDest = position.PositionCode; |
| | | //// bind.AGV.TaskOrder(position.PositionCode); |
| | | |
| | | if (bind.AGV.CurrentPosition != position.PositionCode) |
| | | { |
| | | throw new ProcessException("AGV当前未处于满Tray上料点"); |
| | | } |
| | | //// bind.AGVStatus = TaskStatus.Running; |
| | | |
| | | float adjust_X = 0.0f; |
| | | float adjust_Y = 0.0f; |
| | | float adjust_Angle = 0.0f; |
| | | //// return new ProcessResponse(true); |
| | | ////} |
| | | |
| | | if (Config.IsEnableVisionGuide) |
| | | { |
| | | PositionVisionConfig visionConfig = Config.VisionConfigCollection.FirstOrDefault(u => u.PositionCode == position.PositionCode && u.CameraId == bind.CameraId); |
| | | |
| | | if (visionConfig == null) |
| | | { |
| | | throw new ProcessException("未配置该相机的满Tray上料点的视觉操作配置"); |
| | | } |
| | | |
| | | float x = 0; |
| | | float y = 0; |
| | | float angle = 0; |
| | | |
| | | using (HObject hImage = CollectHImage(bind.Camera, visionConfig.CameraOpConfig, "Camera_LoadFullTray")) |
| | | { |
| | | string toolPath = visionConfig.CameraOpConfig.AlgorithemPath; |
| | | if (!_halconToolDict.ContainsKey(toolPath)) |
| | | { |
| | | throw new ProcessException($"未配置Camera_LoadFullTray的视觉算法路径"); |
| | | } |
| | | |
| | | var tool = _halconToolDict[toolPath]; |
| | | tool.SetDictionary(new Dictionary<string, HTuple>() { { "OUTPUT_X", new HTuple() }, { "OUTPUT_Y", new HTuple() }, { "OUTPUT_Angle", new HTuple() } }, new Dictionary<string, HObject>() { { "INPUT_Image", hImage } }); |
| | | tool.RunProcedure(); |
| | | |
| | | x = (float)tool.GetResultTuple("OUTPUT_X").D; |
| | | y = (float)tool.GetResultTuple("OUTPUT_Y").D; |
| | | angle = (float)tool.GetResultTuple("OUTPUT_Angle").D; |
| | | } |
| | | |
| | | if (x <= 0 || y <= 0) |
| | | { |
| | | throw new ProcessException("Camera_LoadFullTray视觉计算获取点位不可小于0"); |
| | | } |
| | | |
| | | float dx = visionConfig.StandardPoint.X - x; |
| | | float dy = visionConfig.StandardPoint.Y - y; |
| | | |
| | | HOperatorSet.AffineTransPoint2d(new HTuple(visionConfig.Matrix[0], visionConfig.Matrix[1], 0, visionConfig.Matrix[3], visionConfig.Matrix[4], 0), dx, dy, out HTuple dx_Robot, out HTuple dy_Robot); |
| | | |
| | | adjust_X = (float)dx_Robot.D; |
| | | adjust_Y = (float)dy_Robot.D; |
| | | adjust_Angle = visionConfig.StandardPoint.Angle - angle; |
| | | } |
| | | |
| | | //bind.Robot.SendMsg(RobotMsgAction.Load, RobotMsgParas.FullTray, position.PositionNo, new List<float>() { (float)dx_Robot.D, (float)dy_Robot.D, angle }); |
| | | RobotMsg_LoadFullTray.Action = RobotMsgAction.Load; |
| | | RobotMsg_LoadFullTray.Para1 = RobotMsgParas.FullTray; |
| | | RobotMsg_LoadFullTray.Para2 = position.PositionNo; |
| | | RobotMsg_LoadFullTray.Datas = new List<float>() { adjust_X, adjust_Y, 0, adjust_Angle }.ConvertAll(s => s.ToString()).ToList(); |
| | | bind.Robot.SendMsg(RobotMsg_LoadFullTray, true); |
| | | |
| | | return new ProcessResponse(true); |
| | | } |
| | | #endregion |
| | | |
| | | #region 满Tray产线下料 |
| | | //[ProcessMethod("", "AGV_UnloadFullTray", "AGV去往卸载满Tray料", true)] |
| | | //public ProcessResponse AGV_UnloadFullTray(IOperationConfig config, IDevice device) |
| | | //public bool AGV_UnloadFullTray(string bindId) |
| | | //{ |
| | | // var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == (config as AGVBindOpConfig).BindId); |
| | | // var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == bindId); |
| | | // PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.UnloadFullTray); |
| | | |
| | | // if (position == null) |
| | |
| | | // throw new ProcessException("路径配置未设置满Tray下料点"); |
| | | // } |
| | | |
| | | // if (bind.SetAGVStatus(TaskStatus.Running)) |
| | | // { |
| | | // bind.AGVDest = position.PositionCode; |
| | | // bind.AGV.TaskOrder(position.PositionCode); |
| | | |
| | | // bind.AGVStatus = TaskStatus.Running; |
| | | |
| | | // return new ProcessResponse(true); |
| | | // return true; |
| | | // } |
| | | // else |
| | | // { |
| | | // return false; |
| | | // } |
| | | //} |
| | | |
| | | public bool AGV_UnloadFullTray(string bindId) |
| | | { |
| | | var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == bindId); |
| | | PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.UnloadFullTray); |
| | | ////[ProcessMethod("", "Robot_UnloadFullTray", "机器人卸载满Tray", true)] |
| | | ////public ProcessResponse Robot_UnloadFullTray(IOperationConfig config, IDevice device) |
| | | ////{ |
| | | //// var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == (config as AGVBindOpConfig).BindId); |
| | | //// PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.UnloadFullTray); |
| | | |
| | | if (position == null) |
| | | { |
| | | throw new ProcessException("路径配置未设置满Tray下料点"); |
| | | } |
| | | //// if (position == null) |
| | | //// { |
| | | //// throw new ProcessException("路径配置未设置满Tray下料点"); |
| | | //// } |
| | | |
| | | if (bind.SetAGVStatus(TaskStatus.Running)) |
| | | { |
| | | bind.AGVDest = position.PositionCode; |
| | | bind.AGV.TaskOrder(position.PositionCode); |
| | | //// if (bind.AGV.CurrentPosition != position.PositionCode) |
| | | //// { |
| | | //// throw new ProcessException("AGV当前未处于满Tray下料点"); |
| | | //// } |
| | | |
| | | return true; |
| | | } |
| | | else |
| | | { |
| | | return false; |
| | | } |
| | | } |
| | | //// bind.RobotStatus = TaskStatus.Running; |
| | | //// bind.Robot.SendMsg(RobotMsgAction.Move, RobotMsgParas.LineSnap, position.PositionNo); |
| | | |
| | | //[ProcessMethod("", "Robot_UnloadFullTray", "机器人卸载满Tray", true)] |
| | | //public ProcessResponse Robot_UnloadFullTray(IOperationConfig config, IDevice device) |
| | | //// return new ProcessResponse(true); |
| | | ////} |
| | | |
| | | //public void Robot_UnloadFullTraySnap(string bindId, PathPosition position) |
| | | //{ |
| | | // var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == (config as AGVBindOpConfig).BindId); |
| | | // PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.UnloadFullTray); |
| | | // var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == bindId); |
| | | // //PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.UnloadFullTray); |
| | | |
| | | // if (position == null) |
| | | // { |
| | |
| | | |
| | | // bind.RobotStatus = TaskStatus.Running; |
| | | // bind.Robot.SendMsg(RobotMsgAction.Move, RobotMsgParas.LineSnap, position.PositionNo); |
| | | |
| | | // return new ProcessResponse(true); |
| | | // //LogAsync(DateTime.Now, "Robot运动至下满Tray拍照", ""); |
| | | //} |
| | | |
| | | public void Robot_UnloadFullTraySnap(string bindId, PathPosition position) |
| | | { |
| | | var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == bindId); |
| | | //PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.UnloadFullTray); |
| | | ////[ProcessMethod("", "Camera_UnloadFullTray", "相机操作卸载满Tray", true)] |
| | | ////public ProcessResponse Camera_UnloadFullTray(IOperationConfig config, IDevice device) |
| | | //public ProcessResponse Camera_UnloadFullTray(string robotId, int positionNum) |
| | | //{ |
| | | // var bind = Config.AGVBindCollection.FirstOrDefault(u => u.RobotId == robotId); |
| | | // PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.UnloadFullTray && u.PositionNo == positionNum); |
| | | |
| | | if (position == null) |
| | | { |
| | | throw new ProcessException("路径配置未设置满Tray下料点"); |
| | | } |
| | | // if (position == null) |
| | | // { |
| | | // throw new ProcessException("路径配置未设置满Tray下料点"); |
| | | // } |
| | | |
| | | if (bind.AGV.CurrentPosition != position.PositionCode) |
| | | { |
| | | throw new ProcessException("AGV当前未处于满Tray下料点"); |
| | | } |
| | | // if (bind.AGV.CurrentPosition != position.PositionCode) |
| | | // { |
| | | // throw new ProcessException("AGV当前未处于满Tray下料点"); |
| | | // } |
| | | |
| | | bind.RobotStatus = TaskStatus.Running; |
| | | bind.Robot.SendMsg(RobotMsgAction.Move, RobotMsgParas.LineSnap, position.PositionNo); |
| | | //LogAsync(DateTime.Now, "Robot运动至下满Tray拍照", ""); |
| | | } |
| | | // //float adjust_X = 0.0f; |
| | | // //float adjust_Y = 0.0f; |
| | | // //float adjust_Angle = 0.0f; |
| | | // bool isLineReady = false; |
| | | |
| | | //[ProcessMethod("", "Camera_UnloadFullTray", "相机操作卸载满Tray", true)] |
| | | //public ProcessResponse Camera_UnloadFullTray(IOperationConfig config, IDevice device) |
| | | public ProcessResponse Camera_UnloadFullTray(string robotId, int positionNum) |
| | | { |
| | | var bind = Config.AGVBindCollection.FirstOrDefault(u => u.RobotId == robotId); |
| | | PathPosition position = Config.PositionCollection.FirstOrDefault(u => u.Description == PathPositionDefinition.UnloadFullTray && u.PositionNo == positionNum); |
| | | // if (Config.IsEnableVisionGuide) |
| | | // { |
| | | // PositionVisionConfig visionConfig = Config.VisionConfigCollection.FirstOrDefault(u => u.PositionCode == position.PositionCode && u.CameraId == bind.CameraId); |
| | | |
| | | if (position == null) |
| | | { |
| | | throw new ProcessException("路径配置未设置满Tray下料点"); |
| | | } |
| | | |
| | | if (bind.AGV.CurrentPosition != position.PositionCode) |
| | | { |
| | | throw new ProcessException("AGV当前未处于满Tray下料点"); |
| | | } |
| | | |
| | | //float adjust_X = 0.0f; |
| | | //float adjust_Y = 0.0f; |
| | | //float adjust_Angle = 0.0f; |
| | | bool isLineReady = false; |
| | | |
| | | if (Config.IsEnableVisionGuide) |
| | | { |
| | | PositionVisionConfig visionConfig = Config.VisionConfigCollection.FirstOrDefault(u => u.PositionCode == position.PositionCode && u.CameraId == bind.CameraId); |
| | | |
| | | if (visionConfig == null) |
| | | { |
| | | throw new ProcessException("未配置该相机的满Tray下料点的视觉操作配置"); |
| | | } |
| | | // if (visionConfig == null) |
| | | // { |
| | | // throw new ProcessException("未配置该相机的满Tray下料点的视觉操作配置"); |
| | | // } |
| | | |
| | | |
| | | int reTryTime = Config.LineBusyRetryTimes; |
| | | // int reTryTime = Config.LineBusyRetryTimes; |
| | | |
| | | do |
| | | { |
| | | using (HObject hImage = CollectHImage(bind.Camera, visionConfig.CameraOpConfig, "Camera_UnloadFullTray")) |
| | | { |
| | | string toolPath = visionConfig.CameraOpConfig.AlgorithemPath; |
| | | if (!_halconToolDict.ContainsKey(toolPath)) |
| | | { |
| | | throw new ProcessException($"未配置Camera_UnloadFullTray的视觉算法路径"); |
| | | } |
| | | // do |
| | | // { |
| | | // using (HObject hImage = CollectHImage(bind.Camera, visionConfig.CameraOpConfig, "Camera_UnloadFullTray")) |
| | | // { |
| | | // string toolPath = visionConfig.CameraOpConfig.AlgorithemPath; |
| | | // if (!_halconToolDict.ContainsKey(toolPath)) |
| | | // { |
| | | // throw new ProcessException($"未配置Camera_UnloadFullTray的视觉算法路径"); |
| | | // } |
| | | |
| | | _halconToolDict[toolPath].SetDictionary(new Dictionary<string, HTuple>() { { "OUTPUT_Result", new HTuple() } }, new Dictionary<string, HObject>() { { "INPUT_Image", hImage } }); |
| | | _halconToolDict[toolPath].RunProcedure(); |
| | | // _halconToolDict[toolPath].SetDictionary(new Dictionary<string, HTuple>() { { "OUTPUT_Result", new HTuple() } }, new Dictionary<string, HObject>() { { "INPUT_Image", hImage } }); |
| | | // _halconToolDict[toolPath].RunProcedure(); |
| | | |
| | | isLineReady = _halconToolDict[toolPath].GetResultTuple("OUTPUT_Result").I == 1; |
| | | } |
| | | |
| | | if (!isLineReady) |
| | | { |
| | | Thread.Sleep(Config.LineBusyWaitInterval * 1000); |
| | | reTryTime--; |
| | | } |
| | | else |
| | | { |
| | | reTryTime = 0; |
| | | } |
| | | } while (reTryTime > 0); |
| | | // isLineReady = _halconToolDict[toolPath].GetResultTuple("OUTPUT_Result").I == 1; |
| | | // } |
| | | |
| | | //if (!isLineReady) |
| | | //{ |
| | | // bind.Robot.SendMsg(RobotMsgType.Send, -1, true, RobotMsgAction.State, RobotMsgParas.LineSnap, new List<string>() { "-1" }); |
| | | // throw new ProcessException("产线忙,等待超时"); |
| | | // Thread.Sleep(Config.LineBusyWaitInterval * 1000); |
| | | // reTryTime--; |
| | | //} |
| | | //else |
| | | //{ |
| | | // bind.Robot.SendMsg(RobotMsgType.Send, -1, true, RobotMsgAction.State, RobotMsgParas.LineSnap, new List<string>() { "1" }); |
| | | // reTryTime = 0; |
| | | //} |
| | | } |
| | | else |
| | | { |
| | | isLineReady = true; |
| | | } |
| | | // } while (reTryTime > 0); |
| | | |
| | | if (isLineReady) |
| | | { |
| | | bind.Robot.SendMsg(RobotMsgAction.Unload, RobotMsgParas.FullTray, position.PositionNo); |
| | | } |
| | | else |
| | | { |
| | | bind.Robot.SendMsg(RobotMsgAction.Move, RobotMsgParas.Home, position.PositionNo); |
| | | } |
| | | // //if (!isLineReady) |
| | | // //{ |
| | | // // bind.Robot.SendMsg(RobotMsgType.Send, -1, true, RobotMsgAction.State, RobotMsgParas.LineSnap, new List<string>() { "-1" }); |
| | | // // throw new ProcessException("产线忙,等待超时"); |
| | | // //} |
| | | // //else |
| | | // //{ |
| | | // // bind.Robot.SendMsg(RobotMsgType.Send, -1, true, RobotMsgAction.State, RobotMsgParas.LineSnap, new List<string>() { "1" }); |
| | | // //} |
| | | // } |
| | | // else |
| | | // { |
| | | // isLineReady = true; |
| | | // } |
| | | |
| | | return new ProcessResponse(true); |
| | | } |
| | | // if (isLineReady) |
| | | // { |
| | | // bind.Robot.SendMsg(RobotMsgAction.Unload, RobotMsgParas.FullTray, position.PositionNo); |
| | | // } |
| | | // else |
| | | // { |
| | | // bind.Robot.SendMsg(RobotMsgAction.Move, RobotMsgParas.Home, position.PositionNo); |
| | | // } |
| | | |
| | | // return new ProcessResponse(true); |
| | | //} |
| | | //#endregion |
| | | #endregion |
| | | } |
| | | |
| | | //[Device("AGVBind", "AGVBind", EnumHelper.DeviceAttributeType.OperationConfig)] |
| | | public class AGVBindOpConfig : OperationConfigBase |
| | | { |
| | | [Category("设备信息")] |
| | | [Description("操作相关的设备编号")] |
| | | public string BindId { get; set; } |
| | | |
| | | [Category("位置信息")] |
| | | [Description("操作相关的位置")] |
| | | public PathPosition Position { get; set; } |
| | | |
| | | public AGVBindOpConfig() { } |
| | | |
| | | public AGVBindOpConfig(string bindId, PathPosition position = null) |
| | | { |
| | | BindId = bindId; |
| | | Position = position; |
| | | } |
| | | } |
| | | |
| | | public class TaskAssignInfo |
| | | { |
| | | public int PositionNo { get; set; } |
| | | |
| | | public bool IsTaskNeed { get; set; } = false; |
| | | |
| | | public bool IsTaskAssgined { get; set; } = false; |
| | | |
| | | public string AgvId { get; set; } |
| | | } |
| | | } |
New file |
| | |
| | | using Bro.Device.AuboRobot; |
| | | using System; |
| | | using System.Collections.Generic; |
| | | using System.Linq; |
| | | using System.Text; |
| | | using System.Threading.Tasks; |
| | | |
| | | namespace A032.Process |
| | | { |
| | | public partial class ProcessControl |
| | | { |
| | | private void OnRobotMsgReceived(DateTime dt, AuboRobotDriver robot, RobotMsg msg) |
| | | { |
| | | } |
| | | |
| | | private void SwitchLight(AuboRobotDriver robot, bool isOn) |
| | | { |
| | | if (Config.LightOutputIndex > 0) |
| | | { |
| | | robot.SetIO(Config.LightOutputIndex, isOn); |
| | | } |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | using Autofac; |
| | | using Bro.Common.Base; |
| | | using Bro.Common.Helper; |
| | | using Bro.Common.Interface; |
| | | using Bro.Common.Model; |
| | | using Bro.Device.AuboRobot; |
| | | using Bro.Device.SeerAGV; |
| | | using HalconDotNet; |
| | | using Newtonsoft.Json; |
| | | using System; |
| | | using System.Collections.Generic; |
| | | using System.Collections.ObjectModel; |
| | | using System.Collections.Specialized; |
| | | using System.ComponentModel; |
| | | using System.Drawing.Design; |
| | | using System.Linq; |
| | | using System.Text; |
| | | using System.Threading; |
| | | using System.Threading.Tasks; |
| | | |
| | | namespace A032.Process |
| | | { |
| | | public class TrayTask : IComplexDisplay |
| | | { |
| | | [Browsable(false)] |
| | | public string TaskId { get; set; } = Guid.NewGuid().ToString(); |
| | | |
| | | /// <summary> |
| | | /// 优先级越高,越快执行 |
| | | /// </summary> |
| | | [Category("任务配置")] |
| | | [Description("优先级。优先级越高,越快执行")] |
| | | public int Priority { get; set; } |
| | | |
| | | private string locationCode = ""; |
| | | |
| | | [Category("任务配置")] |
| | | [Description("任务执行地址代码")] |
| | | [TypeConverter(typeof(PositionCodeConverter))] |
| | | public string LocationCode |
| | | { |
| | | get => locationCode; |
| | | set |
| | | { |
| | | if (locationCode != value) |
| | | { |
| | | locationCode = value; |
| | | |
| | | using (var scope = GlobalVar.Container.BeginLifetimeScope()) |
| | | { |
| | | ProcessConfig config = scope.Resolve<ProcessConfig>(); |
| | | |
| | | Location = config.PositionCollection.FirstOrDefault(u => u.PositionCode == locationCode); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | [Browsable(false)] |
| | | public PathPosition Location { get; set; } = new PathPosition(); |
| | | |
| | | private TaskType taskType = TaskType.LoadEmptyTrayToAGV; |
| | | [Category("任务配置")] |
| | | [Description("任务类型")] |
| | | public TaskType TaskType |
| | | { |
| | | get => taskType; |
| | | set |
| | | { |
| | | taskType = value; |
| | | |
| | | var attr = taskType.GetEnumAttribute<PriorityAttribute>(); |
| | | if (attr != null) |
| | | { |
| | | Priority = attr.Priority; |
| | | } |
| | | } |
| | | } |
| | | |
| | | private string sourceDeviceId = ""; |
| | | [Category("任务配置")] |
| | | [Description("任务来源设备")] |
| | | [TypeConverter(typeof(AllDeviceIdConverter))] |
| | | public string SourceDeviceId |
| | | { |
| | | get => sourceDeviceId; |
| | | set |
| | | { |
| | | if (sourceDeviceId != value) |
| | | { |
| | | sourceDeviceId = value; |
| | | |
| | | using (var scope = GlobalVar.Container.BeginLifetimeScope()) |
| | | { |
| | | List<IDevice> deviceList = scope.Resolve<List<IDevice>>(); |
| | | |
| | | var device = deviceList.FirstOrDefault(u => u.Id == value); |
| | | SourceDeviceName = device.Name; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | [Browsable(false)] |
| | | [JsonIgnore] |
| | | public string SourceDeviceName { get; set; } = ""; |
| | | |
| | | [Browsable(false)] |
| | | [JsonIgnore] |
| | | public string BindId { get; set; } = ""; |
| | | |
| | | [Browsable(false)] |
| | | [JsonIgnore] |
| | | public int WaitShift { get; set; } = 0; |
| | | |
| | | public string GetDisplayText() |
| | | { |
| | | return $"{SourceDeviceName}发起的{TaskType.ToString()}任务"; |
| | | } |
| | | } |
| | | |
| | | public enum TaskType |
| | | { |
| | | [Priority(30)] |
| | | UnloadEmptyTrayToMachine, |
| | | |
| | | [Priority(20)] |
| | | LoadEmptyTrayToAGV, |
| | | |
| | | [Priority(30)] |
| | | LoadFullTrayFromMachine, |
| | | |
| | | [Priority(20)] |
| | | UnloadFullTrayToLine, |
| | | |
| | | [Priority(50)] |
| | | Charge, |
| | | |
| | | [Priority(10)] |
| | | IdleCharge, |
| | | } |
| | | |
| | | public class PriorityAttribute : Attribute |
| | | { |
| | | public int Priority { get; set; } |
| | | |
| | | public PriorityAttribute(int priority) |
| | | { |
| | | Priority = priority; |
| | | } |
| | | } |
| | | |
| | | public partial class ProcessControl |
| | | { |
| | | private bool isEnableExecuteTask = true; |
| | | public bool IsEnableExecuteTask |
| | | { |
| | | get => isEnableExecuteTask; |
| | | set |
| | | { |
| | | isEnableExecuteTask = value; |
| | | |
| | | LogAsync(DateTime.Now, $"Set IsEnableExecuteTask {value.ToString()}", $"{(value ? "打开" : "关闭")}任务执行"); |
| | | } |
| | | } |
| | | List<AGVState> _disableStates = new List<AGVState>() { AGVState.InCharge, AGVState.Warning, AGVState.Unknown }; |
| | | |
| | | private void Reset(string bindId = "") |
| | | { |
| | | WarningRemains.Clear(); |
| | | IsEnableExecuteTask = true; |
| | | |
| | | if (string.IsNullOrWhiteSpace(bindId)) |
| | | { |
| | | Config.AGVBindCollection.ForEach(bind => |
| | | { |
| | | if (bind.UnitState == AGVState.Warning) |
| | | { |
| | | bind.WarningMsg = ""; |
| | | bind.UnitState = AGVState.Idle; |
| | | } |
| | | }); |
| | | |
| | | LogAsync(DateTime.Now, "Reset", "执行全体复位操作"); |
| | | } |
| | | else |
| | | { |
| | | var bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == bindId); |
| | | |
| | | if (bind != null && bind.UnitState == AGVState.Warning) |
| | | { |
| | | bind.WarningMsg = ""; |
| | | bind.UnitState = AGVState.Idle; |
| | | |
| | | LogAsync(DateTime.Now, "Reset", $"执行{bind.AGV.Name}复位操作"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | static object _taskLock = new object(); |
| | | NoticedList<TrayTask> TrayTaskCollection = new NoticedList<TrayTask>(); |
| | | |
| | | private void InsertTask(TrayTask task) |
| | | { |
| | | lock (_taskLock) |
| | | { |
| | | if (TrayTaskCollection.Any(u => u.TaskType == task.TaskType && u.SourceDeviceId == task.SourceDeviceId)) |
| | | { |
| | | LogAsync(DateTime.Now, "重复任务", $"{task.SourceDeviceName}发起的{task.TaskType.ToString()}任务已存在任务列表中"); |
| | | return; |
| | | } |
| | | |
| | | int insertIndex = TrayTaskCollection.Count; |
| | | var nextTask = TrayTaskCollection.FirstOrDefault(u => u.Priority < task.Priority && u.WaitShift == 0); |
| | | if (nextTask != null) |
| | | { |
| | | insertIndex = TrayTaskCollection.IndexOf(nextTask); |
| | | } |
| | | |
| | | TrayTaskCollection.Insert(insertIndex, task); |
| | | } |
| | | } |
| | | |
| | | #region 任务触发 |
| | | private async void OnTaskListChanged(NotifyCollectionChangedAction action, List<TrayTask> task) |
| | | { |
| | | await Task.Run(() => |
| | | { |
| | | if (action == NotifyCollectionChangedAction.Add) |
| | | { |
| | | ExecuteTask(); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | private async void OnUnitStateChanged(string unitId, AGVState preState, AGVState currentState) |
| | | { |
| | | await Task.Run(() => |
| | | { |
| | | if (currentState == AGVState.Idle || currentState == AGVState.IdleCharge) |
| | | { |
| | | ExecuteTask(unitId); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | private void ExecuteTask(string unitId = "") |
| | | { |
| | | TrayTask task = null; |
| | | |
| | | lock (_taskLock) |
| | | { |
| | | if (TrayTaskCollection.Count <= 0) |
| | | { |
| | | return; |
| | | } |
| | | |
| | | task = TrayTaskCollection.FirstOrDefault(u => string.IsNullOrWhiteSpace(u.BindId)); |
| | | } |
| | | |
| | | //任务是否已在执行中 |
| | | if (Config.AGVBindCollection.Any(u => u.CurrentTaskId == task.TaskId)) |
| | | { |
| | | LogAsync(DateTime.Now, "任务执行中", $"{task.GetDisplayText()}正在执行中"); |
| | | return; |
| | | } |
| | | |
| | | AGVBindUnit bind = null; |
| | | |
| | | if (string.IsNullOrWhiteSpace(unitId)) |
| | | { |
| | | var available = Config.AGVBindCollection.Where(u => u.UnitState == AGVState.Idle || u.UnitState == AGVState.IdleCharge); |
| | | |
| | | switch (task.TaskType) |
| | | { |
| | | case TaskType.LoadFullTrayFromMachine: |
| | | available = available.Where(u => u.FullTrayNum < Config.AGVAvailableTrayNums); |
| | | break; |
| | | case TaskType.UnloadEmptyTrayToMachine: |
| | | available = available.Where(u => u.EmptyTrayNum > 0); |
| | | break; |
| | | default: |
| | | break; |
| | | } |
| | | |
| | | bind = available.FirstOrDefault(); |
| | | } |
| | | else |
| | | { |
| | | bind = Config.AGVBindCollection.FirstOrDefault(u => u.Id == unitId); |
| | | } |
| | | |
| | | if (bind == null) |
| | | { |
| | | LogAsync(DateTime.Now, $"暂时没有可执行任务的单元", ""); |
| | | ReArrangeTask(task); |
| | | return; |
| | | } |
| | | |
| | | if (task.Location == null) |
| | | { |
| | | switch (task.TaskType) |
| | | { |
| | | case TaskType.LoadEmptyTrayToAGV: |
| | | task.Location = Config.PositionCollection.FirstOrDefault(u => !u.IsOccupied && u.Description == PathPositionDefinition.LoadEmptyTray); |
| | | break; |
| | | case TaskType.LoadFullTrayFromMachine: |
| | | task.Location = Config.PositionCollection.FirstOrDefault(u => !u.IsOccupied && u.Description == PathPositionDefinition.LoadFullTray && u.DeviceOwner == task.SourceDeviceId); |
| | | break; |
| | | case TaskType.UnloadEmptyTrayToMachine: |
| | | task.Location = Config.PositionCollection.FirstOrDefault(u => !u.IsOccupied && u.Description == PathPositionDefinition.UnloadEmptyTray && u.DeviceOwner == task.SourceDeviceId); |
| | | break; |
| | | case TaskType.UnloadFullTrayToLine: |
| | | task.Location = Config.PositionCollection.FirstOrDefault(u => !u.IsOccupied && u.Description == PathPositionDefinition.UnloadFullTray && u.DeviceOwner == task.SourceDeviceId); |
| | | break; |
| | | default: |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if (task.Location == null) |
| | | { |
| | | ReArrangeTask(task); |
| | | return; |
| | | } |
| | | else |
| | | { |
| | | task.Location.IsOccupied = true; |
| | | } |
| | | |
| | | task.BindId = bind.Id; |
| | | |
| | | if (!string.IsNullOrWhiteSpace(bind.CurrentTaskId)) |
| | | { |
| | | bind.AGV.CancelTask(); |
| | | } |
| | | |
| | | LogAsync(DateTime.Now, $"开始执行任务", task.GetDisplayText()); |
| | | |
| | | bind.IsTaskCancelled = false; |
| | | bind.IsTaskCancelling = false; |
| | | |
| | | bind.UnitState = AGVState.Running; |
| | | bind.CurrentTaskId = task.TaskId; |
| | | |
| | | try |
| | | { |
| | | switch (task.TaskType) |
| | | { |
| | | case TaskType.LoadEmptyTrayToAGV: |
| | | LoadEmptyTrayToAGV(task, bind); |
| | | break; |
| | | case TaskType.LoadFullTrayFromMachine: |
| | | LoadFullTrayFromMachine(task, bind); |
| | | break; |
| | | case TaskType.UnloadEmptyTrayToMachine: |
| | | UnloadEmptyTrayToMachine(task, bind); |
| | | break; |
| | | case TaskType.UnloadFullTrayToLine: |
| | | UnloadFullTrayToLine(task, bind); |
| | | break; |
| | | default: |
| | | break; |
| | | } |
| | | |
| | | ////任务完成后检查电量 |
| | | //bind.AGV.BatteryHandle.Reset(); |
| | | //bool isNotTimeout = bind.AGV.BatteryHandle.WaitOne((bind.AGV.InitialConfig as SeerAGVInitialConfig).ScanInterval * 10); |
| | | |
| | | //if (!isNotTimeout) |
| | | //{ |
| | | // throw new ProcessException($"{bind.AGV.Name}获取电池状态超时"); |
| | | //} |
| | | } |
| | | catch (TaskCanceledException ex) |
| | | { |
| | | try |
| | | { |
| | | CancelTask(bind); |
| | | } |
| | | catch (Exception) |
| | | { |
| | | ExceptionHandle(task, bind, ex, $"任务{task.TaskId}取消失败,"); |
| | | return; |
| | | } |
| | | finally |
| | | { |
| | | bind.IsTaskCancelled = false; |
| | | bind.IsTaskCancelling = false; |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | ExceptionHandle(task, bind, ex); |
| | | return; |
| | | } |
| | | finally |
| | | { |
| | | //lock (_taskListLock) |
| | | //{ |
| | | // _taskList.RemoveAt(0); |
| | | //} |
| | | } |
| | | |
| | | LogAsync(DateTime.Now, $"任务{task.TaskId}流程结束", ""); |
| | | |
| | | AfterTaskHandle(task, bind); |
| | | } |
| | | |
| | | private void ReArrangeTask(TrayTask task) |
| | | { |
| | | task.WaitShift++; |
| | | |
| | | TrayTaskCollection.Remove(task); |
| | | |
| | | if (task.WaitShift > Config.DefaultWaitShift) |
| | | { |
| | | LogAsync(DateTime.Now, "任务无法执行", $"{task.GetDisplayText()}等待{task.WaitShift}次无法执行,已移出任务队列"); |
| | | } |
| | | else |
| | | { |
| | | InsertTask(task); |
| | | } |
| | | } |
| | | |
| | | //AutoResetEvent _taskDoneHandle = new AutoResetEvent(true); |
| | | Dictionary<string, AutoResetEvent> _bindTaskDoneHandleDict = new Dictionary<string, AutoResetEvent>(); |
| | | private void AfterTaskHandle(TrayTask task, AGVBindUnit bind, string warningMsg = "", bool isWarningRaised = false) |
| | | { |
| | | lock (_taskLock) |
| | | { |
| | | task.Location.IsOccupied = false; |
| | | TrayTaskCollection.Remove(task); |
| | | } |
| | | |
| | | bind.CurrentTaskId = ""; |
| | | if (isWarningRaised) |
| | | { |
| | | bind.WarningMsg = warningMsg; |
| | | bind.UnitState = AGVState.Warning; |
| | | } |
| | | else |
| | | { |
| | | //任务完成后检查电量 |
| | | bind.AGV.BatteryHandle.Reset(); |
| | | bool isNotTimeout = bind.AGV.BatteryHandle.WaitOne((bind.AGV.InitialConfig as SeerAGVInitialConfig).ScanInterval * 10); |
| | | |
| | | if (!isNotTimeout) |
| | | { |
| | | bind.WarningMsg = $"{bind.AGV.Name}获取电池状态超时"; |
| | | new ProcessException(bind.WarningMsg); |
| | | bind.UnitState = AGVState.Warning; |
| | | } |
| | | else |
| | | { |
| | | SeerAGVInitialConfig iConfig = bind.AGV.InitialConfig as SeerAGVInitialConfig; |
| | | if (bind.AGV.BatteryLvl <= iConfig.BatteryLvlToCharge) |
| | | { |
| | | bind.UnitState = AGVState.InCharge; |
| | | |
| | | var chargePosition = Config.PositionCollection.FirstOrDefault(u => !u.IsOccupied && u.Description == PathPositionDefinition.Charge); |
| | | |
| | | if (chargePosition == null) |
| | | { |
| | | bind.WarningMsg = $"{bind.AGV.Name}目前无可用充电地址"; |
| | | new ProcessException(bind.WarningMsg); |
| | | bind.UnitState = AGVState.Warning; |
| | | } |
| | | else |
| | | { |
| | | bind.AGV.TaskOrder(chargePosition.PositionCode, true); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | bind.UnitState = AGVState.Idle; |
| | | } |
| | | } |
| | | } |
| | | |
| | | _bindTaskDoneHandleDict[bind.Id].Set(); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 取消当前任务,如果已取卷宗,归还到原处 |
| | | /// </summary> |
| | | /// <param name="bind"></param> |
| | | private void CancelTask(AGVBindUnit bind) |
| | | { |
| | | bind.IsTaskCancelling = true; |
| | | |
| | | bind.AGV.CancelTask(); |
| | | bind.Robot.Move(new RobotPoint(), MoveType.Origin, true); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 过程异常处理,反馈异常消息到RabbitMQ |
| | | /// </summary> |
| | | /// <param name="task"></param> |
| | | /// <param name="bind"></param> |
| | | /// <param name="ex"></param> |
| | | /// <param name="errorMsg"></param> |
| | | private void ExceptionHandle(TrayTask task, AGVBindUnit bind, Exception ex, string errorMsg = "") |
| | | { |
| | | bool isWarningEx = false; |
| | | if (ex is ProcessException) |
| | | { |
| | | isWarningEx = (ex as ProcessException).Level > 0; |
| | | errorMsg += (ex as ProcessException).ErrorCode; |
| | | } |
| | | else |
| | | { |
| | | isWarningEx = true; |
| | | errorMsg += ex.Message; |
| | | LogAsync(DateTime.Now, $"{task.TaskId}异常", ex.GetExceptionMessage()); |
| | | } |
| | | |
| | | try |
| | | { |
| | | SwitchLight(bind.Robot, false); |
| | | bind.Robot.Move(new RobotPoint(), MoveType.Origin, true); |
| | | } |
| | | catch (Exception) |
| | | { |
| | | } |
| | | |
| | | LogAsync(DateTime.Now, $"任务{task.GetDisplayText()}异常反馈", errorMsg); |
| | | |
| | | AfterTaskHandle(task, bind, errorMsg, isWarningEx); |
| | | } |
| | | #endregion |
| | | |
| | | #region 任务执行 |
| | | /// <summary> |
| | | /// 将满Tray放置到产线上 |
| | | /// </summary> |
| | | /// <param name="task"></param> |
| | | /// <param name="bind"></param> |
| | | private void UnloadFullTrayToLine(TrayTask task, AGVBindUnit bind) |
| | | { |
| | | string methodName = "UnloadFullTrayToLine"; |
| | | |
| | | bind.AGV.TaskOrder(task.Location.PositionCode, true); |
| | | |
| | | var visionConfig = Config.VisionConfigCollection.FirstOrDefault(u => u.CameraId == bind.CameraId && u.PositionCode == task.Location.PositionCode); |
| | | |
| | | if (visionConfig == null) |
| | | throw new ProcessException($"未能获取{bind.Camera.Name}在{task.Location.PositionCode}的视觉配置"); |
| | | |
| | | while (bind.FullTrayNum > 0) |
| | | { |
| | | bind.Robot.Move(visionConfig.RobotSnapshotPoint, MoveType.AbsoluteMove, true); |
| | | |
| | | bool isLineReady = false; |
| | | if (Config.IsEnableVisionGuide) |
| | | { |
| | | int reTryTime = Config.LineBusyRetryTimes; |
| | | |
| | | HDevEngineTool tool = null; |
| | | if (!_halconToolDict.ContainsKey(visionConfig.CameraOpConfig.AlgorithemPath)) |
| | | { |
| | | throw new ProcessException($"未配置{methodName}的算法工具"); |
| | | } |
| | | |
| | | tool = _halconToolDict[visionConfig.CameraOpConfig.AlgorithemPath]; |
| | | |
| | | SwitchLight(bind.Robot, true); |
| | | Thread.Sleep(500);//等待灯开 |
| | | |
| | | do |
| | | { |
| | | RobotPoint point = new RobotPoint(); |
| | | using (var hImage = CollectHImage(bind.Camera, visionConfig.CameraOpConfig, methodName)) |
| | | { |
| | | tool.InputImageDic.Clear(); |
| | | tool.InputImageDic["INPUT_Image"] = hImage; |
| | | |
| | | tool.RunProcedure(); |
| | | |
| | | if (!tool.IsSuccessful) |
| | | { |
| | | throw new ProcessException($"{visionConfig.CameraOpConfig.AlgorithemPath}算法运行失败"); |
| | | } |
| | | |
| | | isLineReady = tool.GetResultTuple("OUTPUT_Result").I == 1; |
| | | } |
| | | |
| | | if (!isLineReady) |
| | | { |
| | | LogAsync(DateTime.Now, $"{task.Location.PositionCode}位置线体繁忙", ""); |
| | | Thread.Sleep(Config.LineBusyWaitInterval * 1000); |
| | | reTryTime--; |
| | | } |
| | | else |
| | | { |
| | | reTryTime = 0; |
| | | } |
| | | } while (reTryTime > 0); |
| | | |
| | | SwitchLight(bind.Robot, false); |
| | | } |
| | | else |
| | | { |
| | | isLineReady = true; |
| | | } |
| | | |
| | | if (isLineReady) |
| | | { |
| | | bind.Robot.UnLoad(bind.Robot.CurrentPoint, TrayType.FullTray, true); |
| | | } |
| | | else |
| | | { |
| | | bind.Robot.Move(new RobotPoint(), MoveType.Origin, true); |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 将空Tray放置到压机上 |
| | | /// </summary> |
| | | /// <param name="task"></param> |
| | | /// <param name="bind"></param> |
| | | private void UnloadEmptyTrayToMachine(TrayTask task, AGVBindUnit bind) |
| | | { |
| | | string methodName = "UnloadEmptyTrayToMachine"; |
| | | |
| | | MachineRelatedOperation(task, bind, methodName); |
| | | |
| | | RobotPoint point = bind.Robot.CurrentPoint.DeepSerializeClone(); |
| | | |
| | | int emptyTrayNum = 0; |
| | | do |
| | | { |
| | | bind.Robot.UnLoad(point, TrayType.EmptyTray, true); |
| | | emptyTrayNum++; |
| | | |
| | | bind.Robot.MonitorHandle.Reset(); |
| | | var isNotTimeout = bind.Robot.MonitorHandle.WaitOne((bind.Robot.InitialConfig as AuboRobotInitialConfig).ReplyTimeout * 3); |
| | | if (!isNotTimeout) |
| | | throw new ProcessException($"{bind.Robot.Name}监听操作超时"); |
| | | |
| | | } while (emptyTrayNum < Config.Machine_EmptyTrayNum && bind.EmptyTrayNum > 0); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 从压机上取满Tray到AGV |
| | | /// </summary> |
| | | /// <param name="task"></param> |
| | | /// <param name="bind"></param> |
| | | private void LoadFullTrayFromMachine(TrayTask task, AGVBindUnit bind) |
| | | { |
| | | string methodName = "LoadFullTrayFromMachine"; |
| | | |
| | | MachineRelatedOperation(task, bind, methodName); |
| | | |
| | | RobotPoint point = bind.Robot.CurrentPoint.DeepSerializeClone(); |
| | | |
| | | int fullTrayNum = Config.Machine_FullTrayNum; |
| | | do |
| | | { |
| | | bind.Robot.Load(point, TrayType.FullTray, true); |
| | | fullTrayNum--; |
| | | bind.FullTrayNum++; |
| | | |
| | | bind.Robot.MonitorHandle.Reset(); |
| | | var isNotTimeout = bind.Robot.MonitorHandle.WaitOne((bind.Robot.InitialConfig as AuboRobotInitialConfig).ReplyTimeout * 3); |
| | | if (!isNotTimeout) |
| | | throw new ProcessException($"{bind.Robot.Name}监听操作超时"); |
| | | |
| | | } while (fullTrayNum > 0 && bind.FullTrayNum < Config.AGVAvailableTrayNums); |
| | | } |
| | | |
| | | private void MachineRelatedOperation(TrayTask task, AGVBindUnit bind, string methodName) |
| | | { |
| | | bind.AGV.TaskOrder(task.Location.PositionCode, true); |
| | | |
| | | var visionConfig = Config.VisionConfigCollection.FirstOrDefault(u => u.CameraId == bind.CameraId && u.PositionCode == task.Location.PositionCode); |
| | | |
| | | if (visionConfig == null) |
| | | throw new ProcessException($"未能获取{bind.Camera.Name}在{task.Location.PositionCode}的视觉配置"); |
| | | |
| | | bind.Robot.Move(visionConfig.RobotSnapshotPoint, MoveType.AbsoluteMove, true); |
| | | |
| | | if (Config.IsEnableVisionGuide) |
| | | { |
| | | int repeatTime = Config.VisionGuideTimes; |
| | | |
| | | HDevEngineTool tool = null; |
| | | if (!_halconToolDict.ContainsKey(visionConfig.CameraOpConfig.AlgorithemPath)) |
| | | { |
| | | throw new ProcessException($"未配置{methodName}的算法工具"); |
| | | } |
| | | |
| | | tool = _halconToolDict[visionConfig.CameraOpConfig.AlgorithemPath]; |
| | | |
| | | SwitchLight(bind.Robot, true); |
| | | Thread.Sleep(500);//等待灯开 |
| | | |
| | | do |
| | | { |
| | | RobotPoint point = new RobotPoint(); |
| | | using (var hImage = CollectHImage(bind.Camera, visionConfig.CameraOpConfig, methodName)) |
| | | { |
| | | tool.InputImageDic.Clear(); |
| | | tool.InputImageDic["INPUT_Image"] = hImage; |
| | | |
| | | tool.RunProcedure(); |
| | | |
| | | if (!tool.IsSuccessful) |
| | | { |
| | | throw new ProcessException($"{visionConfig.CameraOpConfig.AlgorithemPath}算法运行失败"); |
| | | } |
| | | |
| | | double du = tool.GetResultTuple("OUTPUT_X").D; |
| | | double dv = tool.GetResultTuple("OUTPUT_Y").D; |
| | | |
| | | if (du < 0 || dv < 0) |
| | | { |
| | | throw new ProcessException($"{visionConfig.CameraOpConfig.AlgorithemPath}算法结果异常,点位信息获取失败"); |
| | | } |
| | | |
| | | du -= visionConfig.StandardPoint.X; |
| | | dv -= visionConfig.StandardPoint.Y; |
| | | |
| | | HOperatorSet.AffineTransPoint2d(new HTuple(visionConfig.Matrix), du, dv, out HTuple dx, out HTuple dy); |
| | | |
| | | point.X = (float)dx.D; |
| | | point.Y = (float)dy.D; |
| | | point.A = (float)tool.GetResultTuple("OUTPUT_Angle").D; |
| | | } |
| | | |
| | | LogAsync(DateTime.Now, $"{methodName}引导坐标", point.GetDisplayText()); |
| | | |
| | | bind.Robot.Move(point, MoveType.RobotRelativeMove, true); |
| | | |
| | | repeatTime--; |
| | | } while (repeatTime > 0); |
| | | |
| | | SwitchLight(bind.Robot, false); |
| | | |
| | | //视觉导引后再做固定偏移 |
| | | bind.Robot.Move(visionConfig.RobotShift, MoveType.RobotRelativeMove, true); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 人工装空Tray到AGV |
| | | /// </summary> |
| | | /// <param name="task"></param> |
| | | /// <param name="bind"></param> |
| | | private void LoadEmptyTrayToAGV(TrayTask task, AGVBindUnit bind) |
| | | { |
| | | bind.AGV.TaskOrder(task.Location.PositionCode, true); |
| | | bind.Robot.Load(new RobotPoint(), TrayType.EmptyTray, true); |
| | | } |
| | | #endregion |
| | | } |
| | | } |
| | |
| | | <Compile Include="Interface\IStationProcess.cs" /> |
| | | <Compile Include="Model\CustomizedPoint.cs" /> |
| | | <Compile Include="Model\DeviceCmmd.cs" /> |
| | | <Compile Include="Model\IODefinition.cs" /> |
| | | <Compile Include="Model\ModbusFrame.cs" /> |
| | | <Compile Include="Model\MonitorSet.cs" /> |
| | | <Compile Include="Model\NGDesc.cs" /> |
| | |
| | | } |
| | | } |
| | | |
| | | public static T GetEnumAttribute<T>(this Enum enumObj) where T : Attribute |
| | | { |
| | | Type t = enumObj.GetType(); |
| | | FieldInfo f = t.GetField(enumObj.ToString()); |
| | | |
| | | T attr = f.GetCustomAttribute<T>(); |
| | | return attr; |
| | | } |
| | | |
| | | public static string GetPartSFCName(this PartType partType) |
| | | { |
| | | Type t = partType.GetType(); |
| | |
| | | |
| | | namespace Bro.Common.Helper |
| | | { |
| | | public enum ExceptionLevel |
| | | { |
| | | Info = 0, |
| | | Warning = 1, |
| | | Fatal = 2, |
| | | } |
| | | |
| | | public class ProcessException : Exception |
| | | { |
| | | public ExceptionLevel Level { get; set; } = ExceptionLevel.Warning; |
| | | |
| | | public static Action<DateTime, string, string> OnExceptionNotice; |
| | | |
| | | //PubSubCenter pubSub = PubSubCenter.GetInstance(); |
| | |
| | | { |
| | | } |
| | | |
| | | public ProcessException(Exception ex) |
| | | public ProcessException(Exception ex, ExceptionLevel lvl = ExceptionLevel.Warning) |
| | | { |
| | | OriginalException = ex; |
| | | Level = lvl; |
| | | |
| | | ExceptionNotice(); |
| | | } |
| | | |
| | |
| | | // PositionIndex = positionIndex; |
| | | //} |
| | | |
| | | public ProcessException(string error, Exception ex = null) : base(error, ex) |
| | | public ProcessException(string error, ExceptionLevel lvl = ExceptionLevel.Warning, Exception ex = null) : base(error, ex) |
| | | { |
| | | ErrorCode = error; |
| | | Level = lvl; |
| | | OriginalException = ex; |
| | | |
| | | ExceptionNotice(); |
| | |
| | | //pubSub.Publish(PubTag.ExceptionUpdate.ToString(), ErrorCode, OriginalException, true); |
| | | OnExceptionNotice?.Invoke(DateTime.Now, ErrorCode, OriginalException?.GetExceptionMessage()); |
| | | } |
| | | } |
| | | |
| | | public class Level3Exception : ProcessException |
| | | { |
| | | public Level3Exception() : base() { } |
| | | |
| | | public Level3Exception(string error, Exception ex) : base(error, ex) { } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 来料检测异常 |
| | | /// </summary> |
| | | public class IncomingMaterialException : ProcessException |
| | | { |
| | | public IncomingMaterialException() : base() { } |
| | | |
| | | public IncomingMaterialException(string error, Exception ex) : base(error, ex) { } |
| | | } |
| | | |
| | | public class BarcodeScanFailureException : ProcessException |
| | | { |
| | | public BarcodeScanFailureException() : base() { } |
| | | |
| | | public BarcodeScanFailureException(string msg, Exception ex) : base(msg, ex) { } |
| | | } |
| | | } |
| | |
| | | using System; |
| | | using System.Collections.Generic; |
| | | using System.Collections.Specialized; |
| | | using System.Linq; |
| | | using System.Text; |
| | | using System.Threading.Tasks; |
| | | |
| | | namespace Bro.Common.Helper |
| | | { |
| | | public class NoticedList<T> : List<T> where T : class |
| | | { |
| | | public Action OnItemChanged; |
| | | public Action<NotifyCollectionChangedAction> OnItemChanged; |
| | | |
| | | public Action<List<T>> OnItemChangedWithItemInfo; |
| | | public Action<NotifyCollectionChangedAction, List<T>> OnItemChangedWithItemInfo; |
| | | |
| | | public Action<NoticedList<T>> OnItemChangedWithSelf; |
| | | //public Action<NotifyCollectionChangedAction, NoticedList<T>> OnItemChangedWithSelf; |
| | | |
| | | public new T this[int index] |
| | | { |
| | |
| | | if (base[index] != value) |
| | | { |
| | | base[index] = value; |
| | | OnItemChanged?.Invoke(); |
| | | OnItemChangedWithItemInfo?.Invoke(new List<T>() { value }); |
| | | OnItemChangedWithSelf?.Invoke(this); |
| | | OnItemChanged?.Invoke(NotifyCollectionChangedAction.Replace); |
| | | OnItemChangedWithItemInfo?.Invoke(NotifyCollectionChangedAction.Replace, new List<T>() { value }); |
| | | } |
| | | } |
| | | } |
| | |
| | | public new void Add(T item) |
| | | { |
| | | base.Add(item); |
| | | OnItemChanged?.Invoke(); |
| | | OnItemChangedWithItemInfo?.Invoke(new List<T>() { item }); |
| | | OnItemChangedWithSelf?.Invoke(this); |
| | | OnItemChanged?.Invoke(NotifyCollectionChangedAction.Add); |
| | | OnItemChangedWithItemInfo?.Invoke(NotifyCollectionChangedAction.Add, new List<T>() { item }); |
| | | } |
| | | |
| | | public new void AddRange(IEnumerable<T> collection) |
| | | { |
| | | base.AddRange(collection); |
| | | OnItemChanged?.Invoke(); |
| | | OnItemChangedWithItemInfo?.Invoke(collection.ToList()); |
| | | OnItemChangedWithSelf?.Invoke(this); |
| | | OnItemChanged?.Invoke(NotifyCollectionChangedAction.Add); |
| | | OnItemChangedWithItemInfo?.Invoke(NotifyCollectionChangedAction.Add, collection.ToList()); |
| | | } |
| | | |
| | | public new void Clear() |
| | | { |
| | | base.Clear(); |
| | | OnItemChanged?.Invoke(); |
| | | OnItemChangedWithSelf?.Invoke(this); |
| | | OnItemChanged?.Invoke(NotifyCollectionChangedAction.Reset); |
| | | OnItemChangedWithItemInfo?.Invoke(NotifyCollectionChangedAction.Reset, this); |
| | | } |
| | | |
| | | public new void Insert(int index, T item) |
| | | { |
| | | base.Insert(index, item); |
| | | OnItemChanged?.Invoke(); |
| | | OnItemChangedWithItemInfo?.Invoke(new List<T>() { item }); |
| | | OnItemChangedWithSelf?.Invoke(this); |
| | | OnItemChanged?.Invoke(NotifyCollectionChangedAction.Add); |
| | | OnItemChangedWithItemInfo?.Invoke(NotifyCollectionChangedAction.Add, new List<T>() { item }); |
| | | } |
| | | |
| | | public new void InsertRange(int index, IEnumerable<T> collection) |
| | | { |
| | | base.InsertRange(index, collection); |
| | | OnItemChanged?.Invoke(); |
| | | OnItemChangedWithItemInfo?.Invoke(collection.ToList()); |
| | | OnItemChangedWithSelf?.Invoke(this); |
| | | OnItemChanged?.Invoke(NotifyCollectionChangedAction.Add); |
| | | OnItemChangedWithItemInfo?.Invoke(NotifyCollectionChangedAction.Add, collection.ToList()); |
| | | } |
| | | |
| | | public new bool Remove(T item) |
| | |
| | | |
| | | if (flag) |
| | | { |
| | | OnItemChanged?.Invoke(); |
| | | OnItemChangedWithItemInfo?.Invoke(new List<T>() { item }); |
| | | OnItemChangedWithSelf?.Invoke(this); |
| | | OnItemChanged?.Invoke(NotifyCollectionChangedAction.Remove); |
| | | OnItemChangedWithItemInfo?.Invoke(NotifyCollectionChangedAction.Remove, new List<T>() { item }); |
| | | } |
| | | |
| | | return flag; |
| | |
| | | |
| | | public new int RemoveAll(Predicate<T> match) |
| | | { |
| | | List<T> temp = this.Where(u => match.Invoke(u)).ToList(); |
| | | int i = base.RemoveAll(match); |
| | | if (i > 0) |
| | | { |
| | | OnItemChanged?.Invoke(); |
| | | OnItemChangedWithSelf?.Invoke(this); |
| | | OnItemChanged?.Invoke(NotifyCollectionChangedAction.Remove); |
| | | OnItemChangedWithItemInfo?.Invoke(NotifyCollectionChangedAction.Remove, temp); |
| | | } |
| | | |
| | | return i; |
| | |
| | | |
| | | public new void RemoveAt(int index) |
| | | { |
| | | var item = this[index]; |
| | | base.RemoveAt(index); |
| | | OnItemChanged?.Invoke(); |
| | | OnItemChangedWithSelf?.Invoke(this); |
| | | OnItemChanged?.Invoke(NotifyCollectionChangedAction.Remove); |
| | | OnItemChangedWithItemInfo?.Invoke(NotifyCollectionChangedAction.Remove, new List<T>() { item }); |
| | | } |
| | | |
| | | public new void RemoveRange(int index, int count) |
| | | { |
| | | List<T> temp = this.Skip(index).Take(count).ToList(); |
| | | base.RemoveRange(index, count); |
| | | OnItemChanged?.Invoke(); |
| | | OnItemChangedWithSelf?.Invoke(this); |
| | | OnItemChanged?.Invoke(NotifyCollectionChangedAction.Remove); |
| | | OnItemChangedWithItemInfo?.Invoke(NotifyCollectionChangedAction.Remove, temp); |
| | | } |
| | | } |
| | | } |
| | |
| | | /// <summary> |
| | | /// 设备初始配置 |
| | | /// </summary> |
| | | public interface IInitialConfig |
| | | public interface IInitialConfig : ILog |
| | | { |
| | | /// <summary> |
| | | /// 设备序号 GUID |
| | |
| | | string Name { get; set; } |
| | | |
| | | bool IsEnabled { get; set; } |
| | | |
| | | string DriverType { get; set; } |
| | | } |
| | | |
| | | public interface ILog |
| | | { |
| | | string LogPath { get; set; } |
| | | |
| | | bool IsEnableLog { get; set; } |
| | | } |
| | | } |
| | |
| | | public delegate void OnMonitorAlarmDelegate(DateTime dt, IDevice device, WarningSet warning, bool isAlarmRaised); |
| | | public interface IMonitor |
| | | { |
| | | List<int> GetMonitorValues(int startAddress, int length); |
| | | //List<int> GetMonitorValues(int startAddress, int length); |
| | | void Monitor(); |
| | | |
| | | event OnMonitorInvokeDelegate OnMonitorInvoke; |
| | |
| | | return data.TrimEnd(new char[] { ',' }); |
| | | } |
| | | } |
| | | |
| | | public class RobotPoint : IComplexDisplay |
| | | { |
| | | [Category("点位信息")] |
| | | [Description("坐标X")] |
| | | public float X { get; set; } |
| | | |
| | | [Category("点位信息")] |
| | | [Description("坐标Y")] |
| | | public float Y { get; set; } |
| | | |
| | | [Category("点位信息")] |
| | | [Description("坐标Z")] |
| | | public float Z { get; set; } |
| | | |
| | | [Category("点位信息")] |
| | | [Description("角度A")] |
| | | public float A { get; set; } |
| | | |
| | | [Category("点位信息")] |
| | | [Description("角度B")] |
| | | public float B { get; set; } |
| | | |
| | | [Category("点位信息")] |
| | | [Description("角度C")] |
| | | public float C { get; set; } |
| | | |
| | | public string GetDisplayText() |
| | | { |
| | | return $"X:{X.ToString()} Y:{Y.ToString()} Z:{Z.ToString()} A:{A.ToString()} B:{B.ToString()} C:{C.ToString()}"; |
| | | } |
| | | |
| | | public double[] GetArray() |
| | | { |
| | | return new double[] { X, Y, Z, A, B, C }; |
| | | } |
| | | |
| | | public static RobotPoint GetRobotPointByArray(double[] array) |
| | | { |
| | | if (array.Length != 6) |
| | | { |
| | | return null; |
| | | } |
| | | |
| | | RobotPoint point = new RobotPoint(); |
| | | point.X = (float)array[0]; |
| | | point.Y = (float)array[1]; |
| | | point.Z = (float)array[2]; |
| | | point.A = (float)array[3]; |
| | | point.B = (float)array[4]; |
| | | point.C = (float)array[5]; |
| | | |
| | | return point; |
| | | } |
| | | |
| | | public static float GetDistance(RobotPoint pointA, RobotPoint pointB) |
| | | { |
| | | return (float)Math.Sqrt(Math.Pow(pointA.X - pointB.X, 2) + Math.Pow(pointA.Y - pointB.Y, 2) + Math.Pow(pointA.Z - pointB.Z, 2)); |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | using Bro.Common.Helper; |
| | | using Newtonsoft.Json; |
| | | using System; |
| | | using System.ComponentModel; |
| | | |
| | | namespace Bro.Common.Model |
| | | { |
| | | public class IODefinition : IComplexDisplay |
| | | { |
| | | [Category("IO配置")] |
| | | [Description("IO索引,从0开始")] |
| | | public int IOIndex { get; set; } |
| | | |
| | | [Category("IO配置")] |
| | | [Description("IO描述")] |
| | | public string IODesc { get; set; } = ""; |
| | | |
| | | [Category("IO配置")] |
| | | [Description("true:接通触发 false:断开触发")] |
| | | public bool TriggerValue { get; set; } = true; |
| | | |
| | | [Browsable(false)] |
| | | [JsonIgnore] |
| | | public bool CurrentValue { get; set; } = true; |
| | | |
| | | [Category("IO配置")] |
| | | [Description("IO定义类型")] |
| | | public IODefinitionType IOType { get; set; } = IODefinitionType.Warning; |
| | | |
| | | public string GetDisplayText() |
| | | { |
| | | return $"{IOIndex}{(string.IsNullOrWhiteSpace(IODesc) ? "" : (":" + IODesc))}:{IOType.ToString()}"; |
| | | } |
| | | } |
| | | |
| | | public enum IODefinitionType |
| | | { |
| | | Start, |
| | | Stop, |
| | | Reset, |
| | | Warning, |
| | | } |
| | | } |
| | |
| | | [Description("触发值,设置为-999时变化即触发")] |
| | | public int TriggerValue { get; set; } = -1; |
| | | |
| | | [Browsable(false)] |
| | | [JsonIgnore] |
| | | public int CurrentValue { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 传入数据地址的索引 按照PLC监听地址从0开始的索引集合 |
| | | /// </summary> |
| | |
| | | [Description("通知地址 实际PLC寄存器的地址,10进制,例如 40012")] |
| | | public int NoticeAddress { get; set; } = -1; |
| | | |
| | | //[Browsable(false)] |
| | | //[JsonIgnore] |
| | | //public List<int> InputDatas { get; set; } = new List<int>(); |
| | | |
| | | [Browsable(false)] |
| | | [JsonIgnore] |
| | | public ProcessResponse Response { get; set; } = new ProcessResponse(); |
| | | |
| | | public MonitorSet() { } |
| | | |
| | | //public MonitorSet(int triggerIndex, int triggerValue, List<int> inputDataIndex, int dataIndex, int noticeIndex) |
| | | //{ |
| | | // TriggerIndex = triggerIndex; |
| | | // TriggerValue = triggerValue; |
| | | // InputDataIndex = inputDataIndex; |
| | | // ReplyDataAddress = dataIndex; |
| | | // NoticeAddress = noticeIndex; |
| | | //} |
| | | |
| | | public string GetDisplayText() |
| | | { |
| | | using (var scope = GlobalVar.Container.BeginLifetimeScope()) |
| | | { |
| | | List<ProcessMethodAttribute> methodList = scope.Resolve<List<ProcessMethodAttribute>>(); |
| | | //List<IDevice> deviceList = scope.Resolve<List<IDevice>>(); |
| | | |
| | | string desc = ""; |
| | | |
| | |
| | | using Bro.Common.Base; |
| | | using Bro.Common.Helper; |
| | | using Bro.Common.Model; |
| | | using System; |
| | | using System.Collections.Generic; |
| | | using System.ComponentModel; |
| | | using System.Drawing.Design; |
| | |
| | | public int ReplyTimeout { get; set; } = 1000; |
| | | |
| | | [Category("通信设置")] |
| | | [Description("反馈超时重试次数")] |
| | | public int TimeoutRetryTimes { get; set; } = 5; |
| | | |
| | | [Category("通信设置")] |
| | | [Description("协议文本结束字符")] |
| | | public string EndChar { get; set; } = "#"; |
| | | |
| | | [Category("通信设置")] |
| | | [Description("协议内容分隔字符")] |
| | | public string Seperator { get; set; } = ","; |
| | | |
| | | [Category("动作设置")] |
| | | [Description("动作超时设置,单位min")] |
| | | public float OperationTimeout { get; set; } = 1; |
| | | |
| | | [Category("IO监听设置")] |
| | | [Description("IO监听操作配置集合")] |
| | |
| | | |
| | | [Category("IO监听设置")] |
| | | [Description("IO监听间隔,以ms为单位")] |
| | | public int ScanInterval { get; set; } = 100; |
| | | public int ScanInterval { get; set; } = 9999; |
| | | |
| | | [Category("IO监听设置")] |
| | | [Description("是否启用IO监听,true:监听 false:不监听")] |
| | | public bool IsEnableMonitor { get; set; } = false; |
| | | |
| | | [Category("报警配置")] |
| | | [Description("报警代码配置")] |
| | | [TypeConverter(typeof(CollectionCountConvert))] |
| | | [Editor(typeof(ComplexCollectionEditor<RobotWarningCode>), typeof(UITypeEditor))] |
| | | public List<RobotWarningCode> RobotWarnings { get; set; } = new List<RobotWarningCode>(); |
| | | } |
| | | |
| | | public class RobotWarningCode |
| | | { |
| | | [Category("报警配置")] |
| | | [Description("报警代码")] |
| | | public int WarningCode { get; set; } |
| | | |
| | | [Category("报警配置")] |
| | | [Description("报警代码描述")] |
| | | public string WarningDescription { get; set; } |
| | | } |
| | | |
| | | [Device("AuboRobot", "奥博机器人", EnumHelper.DeviceAttributeType.OperationConfig)] |
| | | public class AuboRobotOperationConfig : OperationConfigBase |
| | | { |
| | | [Category("动作指令")] |
| | | [Description("动作指令")] |
| | | public RobotMsgAction Action { get; set; } = RobotMsgAction.Move; |
| | | |
| | | [Category("参数")] |
| | | [Description("参数1")] |
| | | public int Para1 { get; set; } = 0; |
| | | |
| | | [Category("参数")] |
| | | [Description("参数2")] |
| | | public int Para2 { get; set; } = 0; |
| | | |
| | | [Category("运动配置")] |
| | | [Description("机器人运动点位")] |
| | | [TypeConverter(typeof(ComplexObjectConvert))] |
| | | [Editor(typeof(PropertyObjectEditor), typeof(UITypeEditor))] |
| | | public RobotPoint Point { get; set; } = new RobotPoint(); |
| | | |
| | | [Category("操作配置")] |
| | | [Description("是否等待完成信号")] |
| | | public bool IsWaitFinished { get; set; } = true; |
| | | } |
| | | |
| | | public class RobotMsg : IComplexDisplay |
| | |
| | | |
| | | public RobotMsgAction Action { get; set; } = RobotMsgAction.Move; |
| | | |
| | | public RobotMsgParas Para1 { get; set; } = RobotMsgParas.None; |
| | | public int Para1 { get; set; } |
| | | |
| | | public int Para2 { get; set; } = 0; |
| | | |
| | | /// <summary> |
| | | /// Paras不包含Para1,Para2内容 |
| | | /// </summary> |
| | | public List<string> Datas { get; set; } = new List<string>(); |
| | | public RobotPoint Point { get; set; } = new RobotPoint(); |
| | | |
| | | public byte[] GetMsgBytes(string seperator, string endChar) |
| | | public byte[] GetMsgBytes(string seperator, string endChar, out string msg) |
| | | { |
| | | List<string> list = new List<string>() { ((int)Type).ToString("D2"), ID.ToString("D2") }; |
| | | |
| | |
| | | { |
| | | list.Add(((int)Action).ToString("D2")); |
| | | |
| | | list.Add(((int)Para1).ToString("D2")); |
| | | list.Add(Para1.ToString("D2")); |
| | | list.Add(Para2.ToString("D2")); |
| | | |
| | | if (Datas == null) |
| | | { |
| | | Datas = new List<string>(); |
| | | list.Add(GetFormatString(Point.X)); |
| | | list.Add(GetFormatString(Point.Y)); |
| | | list.Add(GetFormatString(Point.Z)); |
| | | list.Add(GetFormatString_Angle(Point.A)); |
| | | list.Add(GetFormatString_Angle(Point.B)); |
| | | list.Add(GetFormatString_Angle(Point.C)); |
| | | } |
| | | |
| | | while (Datas.Count < 5) |
| | | { |
| | | Datas.Add("0"); |
| | | msg = string.Join(seperator, list) + seperator + endChar; |
| | | |
| | | return System.Text.Encoding.ASCII.GetBytes(msg); |
| | | } |
| | | |
| | | list.AddRange(Datas.ConvertAll(s => |
| | | private string GetFormatString(float x) |
| | | { |
| | | string res = float.Parse(s).ToString("F7"); |
| | | string s = x.ToString("f10"); |
| | | |
| | | while (res.Length < 11) |
| | | s = s.TrimEnd(new char[] { '0' }); |
| | | |
| | | bool isNegative = x < 0; |
| | | |
| | | while (s.Length < 11) |
| | | { |
| | | res = "0" + res; |
| | | } |
| | | return res; |
| | | })); |
| | | s = s.Insert(isNegative ? 1 : 0, "0"); |
| | | } |
| | | |
| | | string msg = string.Join(seperator, list); |
| | | return s; |
| | | } |
| | | |
| | | return System.Text.Encoding.ASCII.GetBytes(msg + endChar); |
| | | /// <summary> |
| | | /// 将角度数值转换为11位字符串,输入角度区间0~360,输出角度区间-180~+180 |
| | | /// </summary> |
| | | /// <param name="x"></param> |
| | | /// <returns></returns> |
| | | private string GetFormatString_Angle(float x) |
| | | { |
| | | if (x > 180) |
| | | { |
| | | x = x - 360; |
| | | } |
| | | |
| | | return GetFormatString(x); |
| | | } |
| | | |
| | | public static RobotMsg GetRobotMsg(string data, string seperator) |
| | |
| | | { |
| | | msg.Action = (RobotMsgAction)int.Parse(datas[2]); |
| | | |
| | | msg.Para1 = (RobotMsgParas)int.Parse(datas[3]); |
| | | msg.Para1 = int.Parse(datas[3]); |
| | | msg.Para2 = int.Parse(datas[4]); |
| | | |
| | | msg.Datas = datas.Skip(5).ToList(); |
| | | msg.Point = new RobotPoint() |
| | | { |
| | | X = float.Parse(datas[5]), |
| | | Y = float.Parse(datas[6]), |
| | | Z = float.Parse(datas[7]), |
| | | A = float.Parse(datas[8]) < 0 ? float.Parse(datas[8]) + 360 : float.Parse(datas[8]), |
| | | B = float.Parse(datas[9]) < 0 ? float.Parse(datas[9]) + 360 : float.Parse(datas[9]), |
| | | C = float.Parse(datas[10]) < 0 ? float.Parse(datas[10]) + 360 : float.Parse(datas[10]), |
| | | }; |
| | | } |
| | | else |
| | | { |
| | | msg.Para1 = int.Parse(datas[2]); |
| | | } |
| | | |
| | | return msg; |
| | |
| | | |
| | | public string GetDisplayText() |
| | | { |
| | | string msg = $"序号:{ID},{Action.ToString()}_{Para1.ToString()}_{Para2.ToString()}_{(Datas.Count > 0 ? ("_" + string.Join(",", Datas)) : "")}"; |
| | | string msg = $"序号:{ID},{Action.ToString()}_{Para1.ToString()}_{Para2.ToString()}_{Point.GetDisplayText()}"; |
| | | return msg; |
| | | } |
| | | } |
| | |
| | | Move = 1, |
| | | Unload = 2, |
| | | Load = 3, |
| | | IO = 6, |
| | | GetPosition = 4, |
| | | /// <summary> |
| | | /// 指定当前点位为基准点 |
| | | /// </summary> |
| | | BasePoint = 5, |
| | | IOSet = 6, |
| | | IOQuery = 7, |
| | | Calibration = 9, |
| | | StandardPoint = 10, |
| | | } |
| | | |
| | | public enum RobotMsgParas |
| | | public enum MoveType |
| | | { |
| | | None = 0, |
| | | Home = 1, |
| | | LineSnap = 2, |
| | | EmptyTray = 3, |
| | | FullTray = 4, |
| | | UnloadEmptyTraySnap = 5, |
| | | LoadFullTraySnap = 6, |
| | | Query = 7, |
| | | [Description("绝对运动")] |
| | | AbsoluteMove = 1, |
| | | [Description("机器人坐标系相对运动")] |
| | | RobotRelativeMove = 2, |
| | | [Description("相对某个基准点位的相对运动")] |
| | | BasedPointRelativeMove = 3, |
| | | [Description("回原点")] |
| | | Origin = 4, |
| | | [Description("左侧姿势")] |
| | | LeftPose = 6, |
| | | [Description("右侧姿势")] |
| | | RightPose = 5, |
| | | [Description("前侧姿势")] |
| | | FrontPose = 7, |
| | | [Description("相机坐标系相对运动")] |
| | | CameraRelativeMove = 12, |
| | | } |
| | | |
| | | public enum TrayType |
| | | { |
| | | FullTray = 1, |
| | | EmptyTray = 2, |
| | | } |
| | | |
| | | //public enum RobotMsgAction |
| | | //{ |
| | | // Move = 1, |
| | | // Unload = 2, |
| | | // Load = 3, |
| | | // IO = 6, |
| | | // Calibration = 9, |
| | | // StandardPoint = 10, |
| | | //} |
| | | |
| | | //public enum RobotMsgParas |
| | | //{ |
| | | // None = 0, |
| | | // Home = 1, |
| | | // LineSnap = 2, |
| | | // EmptyTray = 3, |
| | | // FullTray = 4, |
| | | // UnloadEmptyTraySnap = 5, |
| | | // LoadFullTraySnap = 6, |
| | | // Query = 7, |
| | | //} |
| | | } |
| | |
| | | using System.Net.Sockets; |
| | | using System.Threading; |
| | | using System.Threading.Tasks; |
| | | using static Bro.Common.Helper.EnumHelper; |
| | | |
| | | namespace Bro.Device.AuboRobot |
| | | { |
| | |
| | | |
| | | if (string.IsNullOrWhiteSpace(IConfig.EndChar)) |
| | | { |
| | | throw new ProcessException("协议文本的结束字符不可为空,请检查配置", null); |
| | | throw new ProcessException("协议文本的结束字符不可为空,请检查配置"); |
| | | } |
| | | |
| | | client = new TcpClient(); |
| | |
| | | client.Client.Blocking = true; |
| | | client.NoDelay = true; |
| | | client.BeginConnect(IPAddress.Parse(IConfig.RobotIP), IConfig.RobotPort, OnConnect, null); |
| | | |
| | | replyHandleDict = new Dictionary<int, AutoResetEvent>(); |
| | | replyErrorCodeDict = new Dictionary<int, int>(); |
| | | taskHandleDict = new Dictionary<int, ManualResetEvent>(); |
| | | } |
| | | |
| | | protected override void Pause() |
| | |
| | | //Query Robot IOs |
| | | //SendMsg(RobotMsgType.Send, 0, true, RobotMsgAction.IO, RobotMsgParas.Query, new List<string>()); |
| | | |
| | | scanMsg = new RobotMsg(); |
| | | scanMsg.Action = RobotMsgAction.IO; |
| | | scanMsg.Para1 = RobotMsgParas.Query; |
| | | //scanMsg = new RobotMsg(); |
| | | //scanMsg.Action = RobotMsgAction.IO; |
| | | //scanMsg.Para1 = RobotMsgParas.Query; |
| | | |
| | | Task.Run(() => |
| | | { |
| | | Monitor(); |
| | | }); |
| | | //Task.Run(() => |
| | | //{ |
| | | // Monitor(); |
| | | //}); |
| | | } |
| | | |
| | | protected override void Stop() |
| | | { |
| | | replyHandleDict.Values.ToList().ForEach(h => h.Set()); |
| | | replyHandleDict.Clear(); |
| | | |
| | | taskHandleDict.Values.ToList().ForEach(h => h.Set()); |
| | | taskHandleDict.Clear(); |
| | | |
| | | if (client != null && client.Connected) |
| | | { |
| | | client.Close(); |
| | |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | OnLog?.Invoke(DateTime.Now, this, ex.GetExceptionMessage()); |
| | | //OnLog?.Invoke(DateTime.Now, this, ex.GetExceptionMessage()); |
| | | LogAsync(DateTime.Now, "连接异常", ex.GetExceptionMessage()); |
| | | |
| | | if (client != null && client.Connected) |
| | | { |
| | |
| | | try |
| | | { |
| | | int dataLength = stream.EndRead(ar); |
| | | |
| | | if (dataLength > 0) |
| | | { |
| | | byte[] data = buffer.Take(dataLength).ToArray(); |
| | | |
| | | string dataStr = System.Text.Encoding.ASCII.GetString(data).Trim(); |
| | | //OnLog?.BeginInvoke(DateTime.Now, this, $"{Name}接收数据:{dataStr}", null, null); |
| | | |
| | | AnalyzeData(dataStr); |
| | | |
| | | stream.BeginRead(buffer, 0, buffer.Length, OnDataReceived, null); |
| | |
| | | { |
| | | if (!client.Connected) |
| | | { |
| | | OnLog?.Invoke(DateTime.Now, this, "返回空数据,连接中断"); |
| | | //OnLog?.Invoke(DateTime.Now, this, "返回空数据,连接中断"); |
| | | LogAsync(DateTime.Now, "连接中断", "返回空数据,连接中断"); |
| | | client.BeginConnect(IPAddress.Parse(IConfig.RobotIP), IConfig.RobotPort, OnConnect, null); |
| | | } |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | OnLog?.Invoke(DateTime.Now, this, $"{Name}数据接收异常:{ex.GetExceptionMessage()}"); |
| | | //OnLog?.Invoke(DateTime.Now, this, $"{Name}:{ex.GetExceptionMessage()}"); |
| | | LogAsync(DateTime.Now, "数据接收异常", ex.GetExceptionMessage()); |
| | | } |
| | | } |
| | | |
| | | string msg = ""; |
| | | static object analyzeLock = new object(); |
| | | object analyzeLock = new object(); |
| | | private async void AnalyzeData(string data) |
| | | { |
| | | await Task.Run(() => |
| | |
| | | |
| | | if (msg.Type == RobotMsgType.Rec) |
| | | { |
| | | replyErrorCodeDict[msg.ID] = msg.Para1; |
| | | if (replyHandleDict.ContainsKey(msg.ID)) |
| | | { |
| | | replyHandleList.Remove(msg.ID); |
| | | |
| | | replyHandleDict[msg.ID].Set(); |
| | | replyHandleDict.Remove(msg.ID); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | canMonitor = true; |
| | | |
| | | if (msg.Action == RobotMsgAction.IO && msg.Para1 == RobotMsgParas.Query) |
| | | if (msg.Action == RobotMsgAction.IOQuery) |
| | | { |
| | | string resultStr = msg.Datas[0]; |
| | | newValues = new List<int>(); |
| | | |
| | | for (int i = resultStr.Length - 1; i >= 0; i--) |
| | | for (int i = msg.Para1.ToString().Length - 1; i >= 0; i--) |
| | | { |
| | | newValues.Add(int.Parse(resultStr[i].ToString())); |
| | | newValues.Add((msg.Para1 >> i) & 1); |
| | | } |
| | | |
| | | MonitorHandle.Set(); |
| | | } |
| | | else |
| | | { |
| | | canMonitor = true; |
| | | switch (msg.Action) |
| | | { |
| | | case RobotMsgAction.Move: |
| | | CurrentPoint = msg.Point; |
| | | break; |
| | | //case RobotMsgAction.Load: |
| | | // //SlotIndex = msg.Para1; |
| | | // break; |
| | | case RobotMsgAction.GetPosition: |
| | | CurrentPoint = msg.Point; |
| | | break; |
| | | default: |
| | | break; |
| | | } |
| | | |
| | | if (taskHandleDict.ContainsKey(msg.ID)) |
| | | { |
| | | taskHandleDict[msg.ID].Set(); |
| | | } |
| | | |
| | | LogAsync(DateTime.Now, "指令反馈", msg.GetDisplayText()); |
| | | OnMsgReceived?.BeginInvoke(DateTime.Now, this, msg, null, null); |
| | | } |
| | | } |
| | |
| | | } |
| | | } |
| | | |
| | | List<int> replyHandleList = new List<int>(); |
| | | Dictionary<int, AutoResetEvent> replyHandleDict = new Dictionary<int, AutoResetEvent>(); |
| | | |
| | | public void SendMsg(RobotMsgAction action, RobotMsgParas para1, int para2, List<float> paras = null) |
| | | { |
| | | RobotMsg msg = new RobotMsg(); |
| | | msg.Type = RobotMsgType.Send; |
| | | msg.ID = SID; |
| | | |
| | | msg.Action = action; |
| | | msg.Para1 = para1; |
| | | msg.Para2 = para2; |
| | | |
| | | msg.Datas = new List<string>((paras ?? new List<float>()).ConvertAll(i => i.ToString())); |
| | | |
| | | OnLog?.BeginInvoke(DateTime.Now, this, $"{Name}发送指令:{msg.GetDisplayText()}", null, null); |
| | | SendMsg(msg, true); |
| | | } |
| | | Dictionary<int, int> replyErrorCodeDict = new Dictionary<int, int>(); |
| | | Dictionary<int, ManualResetEvent> taskHandleDict = new Dictionary<int, ManualResetEvent>(); |
| | | |
| | | public void SendReplyMsg(int replyId) |
| | | { |
| | |
| | | object monitorLock = new object(); |
| | | public void SendMsg(RobotMsg msg, bool isWaitReply = true, bool isMonitorMsg = false) |
| | | { |
| | | replyErrorCodeDict[msg.ID] = 0; |
| | | |
| | | if (isWaitReply) |
| | | { |
| | | replyHandleList.Add(msg.ID); |
| | | replyHandleDict[msg.ID] = new AutoResetEvent(false); |
| | | } |
| | | |
| | | byte[] bytes = msg.GetMsgBytes(IConfig.Seperator, IConfig.EndChar); |
| | | byte[] bytes = msg.GetMsgBytes(IConfig.Seperator, IConfig.EndChar, out string dataStr); |
| | | if (!isMonitorMsg) |
| | | { |
| | | LogAsync(DateTime.Now, "指令发送", dataStr); |
| | | } |
| | | |
| | | bool isNotTimeout = true; |
| | | int reTryTime = IConfig.TimeoutRetryTimes; |
| | | do |
| | | { |
| | | lock (monitorLock) |
| | | { |
| | | if (!isMonitorMsg) |
| | |
| | | if (isMonitorMsg && !canMonitor) |
| | | return; |
| | | |
| | | //lock (this) |
| | | { |
| | | stream.Write(bytes, 0, bytes.Length); |
| | | |
| | | if (!isMonitorMsg) |
| | | { |
| | | LogAsync(DateTime.Now, "数据发送", BitConverter.ToString(bytes)); |
| | | } |
| | | } |
| | | |
| | | if (isWaitReply) |
| | | { |
| | | replyHandleDict[msg.ID].WaitOne(IConfig.ReplyTimeout); |
| | | int errorCode = replyErrorCodeDict[msg.ID]; |
| | | |
| | | if (replyHandleList.Contains(msg.ID)) |
| | | if (errorCode != 0) |
| | | { |
| | | throw new ProcessException("反馈数据超时\r\n" + msg.GetDisplayText(), null); |
| | | var desc = IConfig.RobotWarnings.FirstOrDefault(u => u.WarningCode == errorCode); |
| | | throw new ProcessException($"{Name}{msg.ID}任务反馈异常{errorCode},异常描述:{(desc == null ? "无" : desc.WarningDescription)}"); |
| | | } |
| | | |
| | | isNotTimeout = replyHandleDict[msg.ID].WaitOne(IConfig.ReplyTimeout); |
| | | |
| | | if (!isNotTimeout) |
| | | { |
| | | reTryTime--; |
| | | } |
| | | } |
| | | } while (reTryTime > 0 && !isNotTimeout); |
| | | |
| | | if (isWaitReply) |
| | | { |
| | | replyHandleDict.Remove(msg.ID); |
| | | } |
| | | |
| | | if (!isNotTimeout) |
| | | { |
| | | throw new ProcessException($"{Name}反馈数据超时\r\n" + msg.GetDisplayText()); |
| | | } |
| | | } |
| | | |
| | | #region 执行结果属性 |
| | | public RobotPoint CurrentPoint { get; set; } = null; |
| | | //public int SlotIndex { get; set; } = 0; |
| | | #endregion |
| | | |
| | | public void Move(RobotPoint point, MoveType isAbs, bool isWaitFinished) |
| | | { |
| | | CurrentPoint = null; |
| | | |
| | | RobotMsg msg = new RobotMsg(); |
| | | msg.Type = RobotMsgType.Send; |
| | | msg.ID = SID; |
| | | |
| | | msg.Action = RobotMsgAction.Move; |
| | | msg.Para1 = (int)isAbs; |
| | | |
| | | msg.Point = point ?? new RobotPoint(); |
| | | |
| | | SendMsg(msg, true); |
| | | |
| | | TaskTimeoutCheck(isWaitFinished, msg.ID, $"{Name}{(isAbs.GetEnumDescription())}至{point.GetDisplayText()}动作超时"); |
| | | } |
| | | |
| | | public void Load(RobotPoint point, TrayType trayType, bool isWaitFinished) |
| | | { |
| | | //SlotIndex = 0; |
| | | |
| | | RobotMsg msg = new RobotMsg(); |
| | | msg.Type = RobotMsgType.Send; |
| | | msg.ID = SID; |
| | | |
| | | msg.Action = RobotMsgAction.Load; |
| | | msg.Para1 = (int)trayType; |
| | | msg.Point = point; |
| | | |
| | | SendMsg(msg, true); |
| | | |
| | | TaskTimeoutCheck(isWaitFinished, msg.ID, $"{Name}取{trayType.ToString()}{point.GetDisplayText()}动作超时"); |
| | | |
| | | //if (isWaitFinished && SlotIndex <= 0) |
| | | //{ |
| | | // throw new ProcessException($"{Name}没有可用的空白格位,无法放置卷宗"); |
| | | //} |
| | | } |
| | | |
| | | public void UnLoad(RobotPoint point, TrayType trayType, bool isWaitFinished) |
| | | { |
| | | RobotMsg msg = new RobotMsg(); |
| | | msg.Type = RobotMsgType.Send; |
| | | msg.ID = SID; |
| | | |
| | | msg.Action = RobotMsgAction.Unload; |
| | | msg.Para1 = (int)trayType; |
| | | msg.Point = point; |
| | | |
| | | SendMsg(msg, true); |
| | | |
| | | TaskTimeoutCheck(isWaitFinished, msg.ID, $"{Name}卸{trayType.ToString()}{point.GetDisplayText()}动作超时"); |
| | | } |
| | | |
| | | public void GetPosition(bool isWaitFinished) |
| | | { |
| | | CurrentPoint = null; |
| | | |
| | | RobotMsg msg = new RobotMsg(); |
| | | msg.Type = RobotMsgType.Send; |
| | | msg.ID = SID; |
| | | |
| | | msg.Action = RobotMsgAction.GetPosition; |
| | | |
| | | SendMsg(msg, true); |
| | | |
| | | TaskTimeoutCheck(isWaitFinished, msg.ID, $"{Name}获取当前坐标超时"); |
| | | } |
| | | |
| | | public void SetBasePoint() |
| | | { |
| | | RobotMsg msg = new RobotMsg(); |
| | | msg.Type = RobotMsgType.Send; |
| | | msg.ID = SID; |
| | | |
| | | msg.Action = RobotMsgAction.BasePoint; |
| | | |
| | | SendMsg(msg, true); |
| | | } |
| | | |
| | | public void SetIO(int outputIndex, bool isOn) |
| | | { |
| | | RobotMsg msg = new RobotMsg(); |
| | | msg.Type = RobotMsgType.Send; |
| | | msg.ID = SID; |
| | | |
| | | msg.Action = RobotMsgAction.IOSet; |
| | | msg.Para1 = outputIndex; |
| | | msg.Para2 = isOn ? 1 : 0; |
| | | |
| | | SendMsg(msg, true); |
| | | } |
| | | |
| | | private void TaskTimeoutCheck(bool isWaitFinished, int msgId, string errorMsg) |
| | | { |
| | | if (isWaitFinished) |
| | | { |
| | | taskHandleDict[msgId] = new ManualResetEvent(false); |
| | | taskHandleDict[msgId].Reset(); |
| | | bool isNotTimeout = taskHandleDict[msgId].WaitOne((int)(IConfig.OperationTimeout * 60 * 1000)); |
| | | |
| | | taskHandleDict.Remove(msgId); |
| | | |
| | | if (!isNotTimeout) |
| | | { |
| | | throw new ProcessException(errorMsg); |
| | | } |
| | | } |
| | | } |
| | |
| | | protected List<int> oldValues = new List<int>(); |
| | | List<int> newValues = new List<int>(); |
| | | public ManualResetEvent MonitorHandle { get; set; } = new ManualResetEvent(false); |
| | | //public ManualResetEvent IOChangedHandle { get; set; } = new ManualResetEvent(true); |
| | | public List<int> GetMonitorValues(int startAddress, int length) |
| | | public List<int> GetIOValues() |
| | | { |
| | | MonitorHandle.Reset(); |
| | | scanMsg.ID = SID; |
| | | SendMsg(scanMsg, true, true); |
| | | |
| | | MonitorHandle.WaitOne(IConfig.ReplyTimeout); |
| | | var isNotTimeout = MonitorHandle.WaitOne(IConfig.ReplyTimeout * 3); |
| | | |
| | | if (!isNotTimeout) |
| | | throw new ProcessException($"{Name}监听操作超时"); |
| | | |
| | | return newValues; |
| | | } |
| | |
| | | { |
| | | try |
| | | { |
| | | List<int> newValues = GetMonitorValues(0, 0); |
| | | if (IConfig.IsEnableMonitor) |
| | | { |
| | | List<int> newValues = GetIOValues(); |
| | | |
| | | if (newValues == null || newValues.Count == 0) |
| | | continue; |
| | |
| | | MonitorCheckAndInvoke(tempNew, tempOld); |
| | | } |
| | | oldValues = new List<int>(newValues); |
| | | } |
| | | |
| | | Thread.Sleep(IConfig.ScanInterval); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | OnLog?.Invoke(DateTime.Now, this, $"{Name}监听异常:{ex.GetExceptionMessage()}"); |
| | | LogAsync(DateTime.Now, "监听异常", ex.GetExceptionMessage()); |
| | | } |
| | | } |
| | | } |
| | |
| | | //using BroCComn.PubSub; |
| | | using static Bro.Common.Helper.EnumHelper; |
| | | using System.ComponentModel; |
| | | using System.IO; |
| | | |
| | | namespace Bro.Common.Base |
| | | { |
| | |
| | | { |
| | | string currentStateStr = CurrentStateToBe.GetEnumDescription(); |
| | | string stateToBeStr = stateToBe.GetEnumDescription(); |
| | | throw new ProcessException($"{InitialConfig.Name}设备的当前状态为{currentStateStr},无法切换至{stateToBeStr}", null); |
| | | throw new ProcessException($"{InitialConfig.Name}设备的当前状态为{currentStateStr},无法切换至{stateToBeStr}"); |
| | | } |
| | | |
| | | CurrentState = EnumHelper.DeviceState.TBD; |
| | |
| | | } |
| | | else |
| | | { |
| | | throw new ProcessException($"{InitialConfig.Name}设备操作超时", null); |
| | | throw new ProcessException($"{InitialConfig.Name}设备操作超时"); |
| | | } |
| | | } |
| | | |
| | |
| | | //this.CurrentState = EnumHelper.DeviceState.DSExcept; |
| | | //this.CurrentStateToBe = EnumHelper.DeviceState.DSExcept; |
| | | |
| | | throw new ProcessException($"设备{InitialConfig.Name}的{CurrentStateToBe.GetEnumDescription()}操作重复3次失败", ex); |
| | | throw new ProcessException($"设备{InitialConfig.Name}的{CurrentStateToBe.GetEnumDescription()}操作重复3次失败", ExceptionLevel.Warning, ex); |
| | | } |
| | | } |
| | | } |
| | |
| | | CurrentUserId = obj; |
| | | } |
| | | #endregion |
| | | |
| | | #region 日志处理 |
| | | object logObject = new object(); |
| | | public virtual async void LogAsync(DateTime dt, string prefix, string msg) |
| | | { |
| | | await Task.Run(() => |
| | | { |
| | | if (InitialConfig.IsEnableLog) |
| | | { |
| | | OnLog?.Invoke(dt, this, prefix + "\t" + msg); |
| | | |
| | | if (string.IsNullOrWhiteSpace(InitialConfig.LogPath) || !Path.IsPathRooted(InitialConfig.LogPath)) |
| | | { |
| | | return; |
| | | } |
| | | |
| | | if (!Directory.Exists(InitialConfig.LogPath)) |
| | | { |
| | | Directory.CreateDirectory(InitialConfig.LogPath); |
| | | } |
| | | |
| | | string filePath = Path.Combine(InitialConfig.LogPath, $"Log_{Name}_{DateTime.Now.ToString("yyyyMMdd")}.txt"); |
| | | lock (logObject) |
| | | { |
| | | using (StreamWriter writer = new StreamWriter(filePath, true, Encoding.UTF8)) |
| | | { |
| | | writer.WriteLine($"{DateTime.Now.ToString("HH:mm:ss.fff")}\t{prefix}\t{msg}"); |
| | | writer.Flush(); |
| | | writer.Close(); |
| | | } |
| | | } |
| | | } |
| | | }); |
| | | } |
| | | #endregion |
| | | } |
| | | } |
| | |
| | | using Bro.Common.Interface; |
| | | using Bro.Common.Helper; |
| | | using Bro.Common.Interface; |
| | | using System; |
| | | using System.Collections.Generic; |
| | | using System.ComponentModel; |
| | | using System.Drawing.Design; |
| | | using System.Linq; |
| | | using System.Text; |
| | | using System.Threading.Tasks; |
| | |
| | | [Category("通用配置")] |
| | | [Description("设备是否启用")] |
| | | public bool IsEnabled { get; set; } = false; |
| | | |
| | | [Browsable(false)] |
| | | public virtual string DriverType { get; set; } = ""; |
| | | |
| | | [Category("日志配置")] |
| | | [Description("日志记录目录")] |
| | | [Editor(typeof(FoldDialogEditor), typeof(UITypeEditor))] |
| | | public string LogPath { get; set; } |
| | | |
| | | [Category("日志配置")] |
| | | [Description("true:启用日志记录 false:不启用日志记录")] |
| | | public bool IsEnableLog { get; set; } = false; |
| | | } |
| | | } |
| | |
| | | |
| | | namespace Bro.Common.Base |
| | | { |
| | | public class HDevEngineTool |
| | | public class HDevEngineTool : IDisposable |
| | | { |
| | | #region 常量 |
| | | |
| | |
| | | /// <summary> |
| | | /// 程序运行是否成功 |
| | | /// </summary> |
| | | private bool isSuccess = false; |
| | | public bool IsSuccessful { get; set; } = false; |
| | | |
| | | /// <summary> |
| | | /// 控制参数字典 |
| | |
| | | |
| | | procedureCall.Execute(); |
| | | |
| | | isSuccess = true; |
| | | IsSuccessful = true; |
| | | } |
| | | catch (HDevEngineException ex) |
| | | { |
| | | isSuccess = false; |
| | | IsSuccessful = false; |
| | | Trace.TraceInformation("HDevProgram {0} Run fail , Line number: {1}, Halcon error number : {2},ex:{3}", ex.ProcedureName, ex.LineNumber, ex.HalconError, ex.Message); |
| | | return; |
| | | } |
| | |
| | | |
| | | public HTuple GetResultTuple(string key) |
| | | { |
| | | if (isSuccess) |
| | | if (IsSuccessful) |
| | | { |
| | | return procedureCall.GetOutputCtrlParamTuple(key); |
| | | } |
| | |
| | | |
| | | public HObject GetResultObject(string key, bool ignoreError = false) |
| | | { |
| | | if (ignoreError || isSuccess) |
| | | if (ignoreError || IsSuccessful) |
| | | { |
| | | return procedureCall.GetOutputIconicParamObject(key); |
| | | } |
| | |
| | | return new HObject(); |
| | | } |
| | | } |
| | | |
| | | public void Dispose() |
| | | { |
| | | //foreach (HObject obj in InputImageDic.Values) |
| | | //{ |
| | | // obj.Dispose(); |
| | | //} |
| | | |
| | | procedureCall.Dispose(); |
| | | myEngine.Dispose(); |
| | | } |
| | | } |
| | | |
| | | public static class HalconHelper |
| | |
| | | }); |
| | | } |
| | | |
| | | private void OnMethodInvoked(IAsyncResult ar) |
| | | protected virtual void OnMethodInvoked(IAsyncResult ar) |
| | | { |
| | | MonitorSet monitorSet = ar.AsyncState as MonitorSet; |
| | | |
| | |
| | | |
| | | if (repeatTime <= 0) |
| | | { |
| | | new ProcessException("PLC反馈写入异常", ex); |
| | | new ProcessException("PLC反馈写入异常", ExceptionLevel.Info, ex); |
| | | } |
| | | } |
| | | } while (repeatTime > 0); |
| | |
| | | [Description("超时设置,单位:ms")] |
| | | public int Timeout { get; set; } = 500; |
| | | |
| | | [Category("输出设置")] |
| | | [Description("是否日志输出")] |
| | | public bool IsEnabelLog { get; set; } = false; |
| | | //[Category("输出设置")] |
| | | //[Description("是否日志输出")] |
| | | //public bool IsEnabelLog { get; set; } = false; |
| | | |
| | | [Category("输出设置")] |
| | | [Description("输出文件路径")] |
| | | public string LogPath { get; set; } = @"D:\PLCLog.txt"; |
| | | //[Category("输出设置")] |
| | | //[Description("输出文件路径")] |
| | | //public string LogPath { get; set; } = @"D:\PLCLog.txt"; |
| | | |
| | | #region 地址设置 |
| | | [Category("事件地址设置")] |
| | |
| | | IOperationConfig config = args.Arguments.FirstOrDefault() as IOperationConfig; |
| | | if (config == null) |
| | | { |
| | | throw new ProcessException("设备操作转入参数类型错误!", null); |
| | | throw new ProcessException("设备操作转入参数类型错误!"); |
| | | } |
| | | |
| | | if (device.DeviceMode == DeviceMode.Run) |
| | |
| | | List<byte> errorCode = bytes.Skip(4 + 4 + 4).Take(4).ToList(); |
| | | if (errorCode.Any(u => u != 0x00)) |
| | | { |
| | | throw new ProcessException($"返回异常,错误码{string.Join("", errorCode)}", null); |
| | | throw new ProcessException($"返回异常,错误码{string.Join("", errorCode)}"); |
| | | } |
| | | |
| | | bytes = bytes.Skip(4 + 4 + 4 + 4).ToArray(); |
| | |
| | | |
| | | if (beginIndex == -1) |
| | | { |
| | | throw new ProcessException("欧姆龙PLC通讯帧错误", null); |
| | | throw new ProcessException("欧姆龙PLC通讯帧错误"); |
| | | } |
| | | |
| | | List<byte> singleBytes = bytes.Skip(beginIndex).ToList(); |
| | |
| | | |
| | | if (wordAddress == 0 && byteAddress == 0) |
| | | { |
| | | throw new ProcessException("欧姆龙PLC监听地址设置错误", null); |
| | | throw new ProcessException("欧姆龙PLC监听地址设置错误"); |
| | | } |
| | | |
| | | //字地址 |
| | |
| | | using System.Text; |
| | | using System.Threading; |
| | | using System.Threading.Tasks; |
| | | using static Bro.Common.Helper.EnumHelper; |
| | | |
| | | namespace Bro.Device.OmronFins |
| | | { |
| | |
| | | } |
| | | |
| | | byte[] data = opFrame.GetSendReadFrameBytes(item, CurrentSid); |
| | | var stream = opClient.GetStream(); |
| | | stream.Write(data, 0, data.Length); |
| | | //var stream = opClient.GetStream(); |
| | | opStream.Write(data, 0, data.Length); |
| | | |
| | | byte[] buffer = new byte[2048]; |
| | | int readSize = stream.Read(buffer, 0, buffer.Length); |
| | | int readSize = opStream.Read(buffer, 0, buffer.Length); |
| | | |
| | | if (readSize > 0) |
| | | { |
| | |
| | | Stopwatch sw = new Stopwatch(); |
| | | sw.Start(); |
| | | |
| | | NetworkStream stream = null; |
| | | //NetworkStream stream = null; |
| | | lock (opClientLock) |
| | | { |
| | | do |
| | |
| | | InitialOpClient(); |
| | | |
| | | byte[] data = opFrame.GetSendWriteFrameBytes(item, CurrentSid); |
| | | stream = opClient.GetStream(); |
| | | stream.Write(data, 0, data.Length); |
| | | //stream = opClient.GetStream(); |
| | | opStream.Write(data, 0, data.Length); |
| | | |
| | | sw.Stop(); |
| | | if (sw.ElapsedMilliseconds > 10) |
| | | { |
| | | LogAsync(DateTime.Now, $"发送完成,耗时:{sw.ElapsedMilliseconds}"); |
| | | LogAsync(DateTime.Now, $"发送完成", $"发送耗时:{sw.ElapsedMilliseconds}"); |
| | | } |
| | | repeatTime = 0; |
| | | |
| | |
| | | } |
| | | else |
| | | { |
| | | LogAsync(DateTime.Now, "Send Exception:" + ex.GetExceptionMessage()); |
| | | LogAsync(DateTime.Now, "Send Exception", ex.GetExceptionMessage()); |
| | | } |
| | | } |
| | | } while (repeatTime > 0); |
| | |
| | | try |
| | | { |
| | | byte[] buffer = new byte[1024]; |
| | | int bufferCount = stream.Read(buffer, 0, buffer.Length); |
| | | int bufferCount = opStream.Read(buffer, 0, buffer.Length); |
| | | if (bufferCount > 0) |
| | | { |
| | | opFrame.AnalyseReceivedItems(buffer.Take(bufferCount).ToArray(), item); |
| | |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | LogAsync(DateTime.Now, "写入反馈异常\r\n" + ex.GetExceptionMessage()); |
| | | LogAsync(DateTime.Now, "写入反馈异常", ex.GetExceptionMessage()); |
| | | } |
| | | } |
| | | else |
| | |
| | | try |
| | | { |
| | | byte[] buffer = new byte[1024]; |
| | | int bufferCount = stream.Read(buffer, 0, buffer.Length); |
| | | int bufferCount = opStream.Read(buffer, 0, buffer.Length); |
| | | if (bufferCount > 0) |
| | | { |
| | | opFrame.AnalyseReceivedItems(buffer.Take(bufferCount).ToArray(), item); |
| | |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | LogAsync(DateTime.Now, "写入反馈异常\r\n" + ex.GetExceptionMessage()); |
| | | LogAsync(DateTime.Now, "写入反馈异常", ex.GetExceptionMessage()); |
| | | } |
| | | }); |
| | | } |
| | |
| | | |
| | | item.ADDRESS = "D" + address.ToString(); |
| | | item.ITEM_LENGTH = 1; |
| | | item.ITEM_VALUE_TYPE = 1; |
| | | item.ITEM_VALUE = writeValue.ToString(); |
| | | |
| | | //lock (opClient) |
| | |
| | | static object opClientLock = new object(); |
| | | |
| | | TcpClient scanClient = new TcpClient(); |
| | | NetworkStream scanStream = null; |
| | | TcpClient opClient = new TcpClient(); |
| | | NetworkStream opStream = null; |
| | | |
| | | IPEndPoint plcEP; |
| | | |
| | |
| | | scanFrame = new FinsFrame(IConfig.DNA, serverNode, IConfig.DA2, IConfig.SNA, scanNode, IConfig.SA2, false); |
| | | |
| | | byte[] dataRequest = scanFrame.GetTcpRequestFrame(0); |
| | | var stream = scanClient.GetStream(); |
| | | stream.Write(dataRequest, 0, dataRequest.Length); |
| | | scanStream = scanClient.GetStream(); |
| | | scanStream.Write(dataRequest, 0, dataRequest.Length); |
| | | byte[] dataRead = new byte[2048]; |
| | | int readSize = stream.Read(dataRead, 0, dataRead.Length); |
| | | int readSize = scanStream.Read(dataRead, 0, dataRead.Length); |
| | | |
| | | if (readSize <= 0) |
| | | { |
| | |
| | | |
| | | opFrame = new FinsFrame(IConfig.DNA, serverNode, IConfig.DA2, IConfig.SNA, opNode, IConfig.SA2, false); |
| | | byte[] dataRequest = opFrame.GetTcpRequestFrame(0); |
| | | var stream = opClient.GetStream(); |
| | | stream.Write(dataRequest, 0, dataRequest.Length); |
| | | opStream = opClient.GetStream(); |
| | | opStream.Write(dataRequest, 0, dataRequest.Length); |
| | | byte[] dataRead = new byte[2048]; |
| | | int readSize = stream.Read(dataRead, 0, dataRead.Length); |
| | | int readSize = opStream.Read(dataRead, 0, dataRead.Length); |
| | | |
| | | if (readSize <= 0) |
| | | { |
| | |
| | | PLC_ITEM item = new PLC_ITEM(); |
| | | item.ADDRESS = "D" + startAddress; |
| | | item.OP_TYPE = 1; |
| | | item.ITEM_VALUE_TYPE = 1; |
| | | item.ITEM_LENGTH = length; |
| | | |
| | | var data = opFrame.GetSendReadFrameBytes(item, CurrentSid); |
| | | var stream = opClient.GetStream(); |
| | | stream.Write(data, 0, data.Length); |
| | | opStream.Write(data, 0, data.Length); |
| | | opStream.Flush(); |
| | | |
| | | byte[] buffer = new byte[2048]; |
| | | int readSize = stream.Read(buffer, 0, buffer.Length); |
| | | int readSize = opStream.Read(buffer, 0, buffer.Length); |
| | | |
| | | if (readSize > 0) |
| | | { |
| | |
| | | PLC_ITEM item = new PLC_ITEM(); |
| | | item.ADDRESS = "D" + startAddress; |
| | | item.OP_TYPE = 1; |
| | | item.ITEM_VALUE_TYPE = 1; |
| | | item.ITEM_LENGTH = length; |
| | | |
| | | if (scanBuffer == null) |
| | |
| | | scanBuffer = scanFrame.GetSendReadFrameBytes(item, CurrentSid); |
| | | } |
| | | |
| | | var stream = scanClient.GetStream(); |
| | | stream.Write(scanBuffer, 0, scanBuffer.Length); |
| | | //var stream = scanClient.GetStream(); |
| | | scanStream.Write(scanBuffer, 0, scanBuffer.Length); |
| | | |
| | | byte[] buffer = new byte[2048]; |
| | | int readSize = stream.Read(buffer, 0, buffer.Length); |
| | | int readSize = scanStream.Read(buffer, 0, buffer.Length); |
| | | |
| | | if (readSize > 0) |
| | | { |
| | |
| | | return new List<int>(); |
| | | } |
| | | } |
| | | #endregion |
| | | |
| | | #region Log |
| | | object logLock = new object(); |
| | | private async void LogAsync(DateTime dt, string info) |
| | | protected override void OnMethodInvoked(IAsyncResult ar) |
| | | { |
| | | await Task.Run(() => |
| | | MonitorSet monitorSet = ar.AsyncState as MonitorSet; |
| | | |
| | | ProcessResponse resValues = monitorSet.Response; |
| | | |
| | | if (resValues.ResultValue == (int)PLCReplyValue.IGNORE) |
| | | { |
| | | if (IConfig.IsEnabelLog) |
| | | { |
| | | lock (logLock) |
| | | { |
| | | DirectoryInfo dir = new DirectoryInfo(IConfig.LogPath); |
| | | if (!dir.Exists) |
| | | { |
| | | dir.Create(); |
| | | return; |
| | | } |
| | | |
| | | string filePath = Path.Combine(IConfig.LogPath, "PLC_" + DateTime.Now.ToString("yyyyMMdd") + ".txt"); |
| | | |
| | | using (StreamWriter writer = new StreamWriter(filePath, true)) |
| | | if (monitorSet.ReplyDataAddress != -1 && resValues.DataList.Count > 0) |
| | | { |
| | | writer.WriteLine(dt.ToString("HH:mm:ss.fff")); |
| | | writer.WriteLine(info); |
| | | writer.WriteLine(); |
| | | |
| | | writer.Flush(); |
| | | writer.Close(); |
| | | } |
| | | } |
| | | } |
| | | }); |
| | | PLC_ITEM item = new PLC_ITEM(); |
| | | item.OP_TYPE = 2; |
| | | item.ITEM_LENGTH = resValues.DataList.Count; |
| | | item.ADDRESS = "D" + monitorSet.ReplyDataAddress.ToString(); |
| | | item.ITEM_VALUE = String.Join(",", resValues.DataList); |
| | | item.ITEM_VALUE_TYPE = (int)PLCItemType.Integer; |
| | | WriteItem(item, false); |
| | | } |
| | | |
| | | //private async void LogAsync(DateTime dt, IModbusFrame frame) |
| | | //{ |
| | | // await Task.Run(() => |
| | | // { |
| | | // if (IConfig.IsEnabelLog) |
| | | // { |
| | | // lock (logLock) |
| | | // { |
| | | // DirectoryInfo dir = new DirectoryInfo(IConfig.LogPath); |
| | | // if (!dir.Exists) |
| | | // { |
| | | // dir.Create(); |
| | | // } |
| | | if (monitorSet.NoticeAddress != -1) |
| | | { |
| | | int repeatTime = 5; |
| | | |
| | | // string filePath = Path.Combine(IConfig.LogPath, "PLC_" + DateTime.Now.ToString("yyyyMMdd") + ".txt"); |
| | | do |
| | | { |
| | | try |
| | | { |
| | | this.WriteSingleAddress(monitorSet.NoticeAddress, resValues.ResultValue, false); |
| | | repeatTime = 0; |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | repeatTime--; |
| | | |
| | | // using (StreamWriter writer = new StreamWriter(filePath, true)) |
| | | // { |
| | | // writer.WriteLine(dt.ToString("HH:mm:ss.fff")); |
| | | // writer.WriteLine(frame.GetFrameString()); |
| | | // writer.WriteLine(); |
| | | |
| | | // writer.Flush(); |
| | | // writer.Close(); |
| | | // } |
| | | // } |
| | | // } |
| | | // }); |
| | | //} |
| | | if (repeatTime <= 0) |
| | | { |
| | | new ProcessException("PLC反馈写入异常", ExceptionLevel.Info, ex); |
| | | } |
| | | } |
| | | } while (repeatTime > 0); |
| | | } |
| | | } |
| | | #endregion |
| | | } |
| | | } |
| | |
| | | </Reference> |
| | | <Reference Include="System" /> |
| | | <Reference Include="System.Core" /> |
| | | <Reference Include="System.Drawing" /> |
| | | <Reference Include="System.Xml.Linq" /> |
| | | <Reference Include="System.Data.DataSetExtensions" /> |
| | | <Reference Include="Microsoft.CSharp" /> |
| | |
| | | using Bro.Common.Base; |
| | | using Bro.Common.Helper; |
| | | using Bro.Common.Model.Helper; |
| | | using Bro.Common.Model; |
| | | using Newtonsoft.Json; |
| | | using Newtonsoft.Json.Linq; |
| | | using System; |
| | | using System.Collections.Generic; |
| | | using System.ComponentModel; |
| | | using System.Drawing.Design; |
| | | using System.Linq; |
| | | using System.Net; |
| | | using System.Runtime.InteropServices; |
| | | using System.Text; |
| | | using System.Threading.Tasks; |
| | | |
| | | namespace Bro.Device.SeerAGV |
| | | { |
| | |
| | | |
| | | [Category("监听配置")] |
| | | [Description("监听间隔,单位ms")] |
| | | public int ScanInterval { get; set; } = 200; |
| | | public int ScanInterval { get; set; } = 500; |
| | | |
| | | //[Category("监听配置")] |
| | | //[Description("监听的IO信息配置集合")] |
| | | //[TypeConverter(typeof(CollectionCountConvert))] |
| | | //[Editor(typeof(ComplexCollectionEditor<IODefinition>), typeof(UITypeEditor))] |
| | | //public List<IODefinition> IOCollection { get; set; } = new List<IODefinition>(); |
| | | |
| | | [Category("监听设置")] |
| | | [Description("监听操作配置集合")] |
| | | [TypeConverter(typeof(CollectionCountConvert))] |
| | | [Editor(typeof(ComplexCollectionEditor<MonitorSet>), typeof(UITypeEditor))] |
| | | public List<MonitorSet> MonitorSetCollection { get; set; } = new List<MonitorSet>(); |
| | | |
| | | [Category("监听配置")] |
| | | [Description("是否采用简单监听模式。true:简单模式,只获取任务状态;false:全部模式,获取任务所有信息")] |
| | |
| | | } |
| | | } |
| | | |
| | | private float batteryLvlToCharge_Recommand = 0.3f; |
| | | [Category("充电配置")] |
| | | [Description("充电电池容量,电池容量低于该值时,设备空闲时建议充电")] |
| | | public float BatteryLvlToCharge_Recommand |
| | | { |
| | | get => batteryLvlToCharge_Recommand; |
| | | set |
| | | { |
| | | if (value >= 1 || value <= 0) |
| | | { |
| | | value = 0.3f; |
| | | } |
| | | batteryLvlToCharge_Recommand = value; |
| | | } |
| | | } |
| | | |
| | | private float batteryLvlChargeDone = 0.9f; |
| | | [Category("充电配置")] |
| | | [Description("充电完成电池容量,电池容量高于该值时确认充电完成")] |
| | |
| | | batteryLvlChargeDone = value; |
| | | } |
| | | } |
| | | |
| | | [Category("动作设置")] |
| | | [Description("动作超时设置,单位min")] |
| | | public float OperationTimeout { get; set; } = 5; |
| | | } |
| | | |
| | | [Device("SeerAGV", "SeerAGV", EnumHelper.DeviceAttributeType.OperationConfig)] |
| | | public class SeerAGVOperationConfig : OperationConfigBase |
| | | { |
| | | [Category("导航配置")] |
| | | [Description("AGV行驶目的地")] |
| | | public string Location { get; set; } |
| | | |
| | | [Category("导航配置")] |
| | | [Description("是否等待完成信号")] |
| | | public bool IsWaitFinished { get; set; } = true; |
| | | } |
| | | |
| | | public class SeerMessage : IComplexDisplay |
| | |
| | | |
| | | if (data.Length < 16 + msg.DataLength) |
| | | { |
| | | throw new ProcessException("数据长度错误", null); |
| | | throw new ProcessException("数据长度错误"); |
| | | } |
| | | |
| | | msg.JsonData = data.Skip(16).Take(msg.DataLength).ToArray(); |
| | |
| | | { |
| | | QueryPosition = 0x03EC, |
| | | QueryTaskStatus = 0x03FC, |
| | | QueryIO = 0x03F5, |
| | | |
| | | CancelTask = 0x0BBB, |
| | | PauseTask = 0x0BB9, |
| | |
| | | using Bro.Common.Base; |
| | | using Bro.Common.Helper; |
| | | using Bro.Common.Interface; |
| | | using Bro.Common.Model; |
| | | using Bro.Common.Model.Interface; |
| | | using Newtonsoft.Json; |
| | | using Newtonsoft.Json.Linq; |
| | | using System; |
| | | using System.Collections.Generic; |
| | | using System.Linq; |
| | | using System.Net; |
| | | using System.Net.Sockets; |
| | | using System.Runtime.InteropServices; |
| | | using System.Text; |
| | | using System.Threading; |
| | | using System.Threading.Tasks; |
| | | |
| | | namespace Bro.Device.SeerAGV |
| | | { |
| | | [Device("SeerAGV", "SeerAGV", EnumHelper.DeviceAttributeType.Device)] |
| | | public class SeerAGVDriver : DeviceBase |
| | | public class SeerAGVDriver : DeviceBase, IMonitor |
| | | { |
| | | public Action<SeerAGVDriver, string> OnAGVPositoinChanged; |
| | | public Action<SeerAGVDriver, AGVTaskStatus> OnAGVTaskStatusChanged; |
| | | public Action<SeerAGVDriver, float, float> OnAGVBatteryLvlChanged; |
| | | public Action<SeerAGVDriver, IODefinition> OnAGVIOChanged; |
| | | |
| | | SeerAGVInitialConfig IConfig |
| | | { |
| | |
| | | { |
| | | InitialTcpClient(ref client_State, IConfig.StatusPort); |
| | | InitialTcpClient(ref client_Guide, IConfig.GuidePort); |
| | | |
| | | StateHandle = new ManualResetEvent(false); |
| | | PositionHandle = new ManualResetEvent(false); |
| | | BatteryHandle = new ManualResetEvent(false); |
| | | IOHandle = new ManualResetEvent(false); |
| | | |
| | | _taskDoneHandle = new ManualResetEvent(false); |
| | | _monitorLock = new ManualResetEvent(true); |
| | | _isTaskRunning = false; |
| | | } |
| | | |
| | | private void InitialTcpClient(ref TcpClient client, int port) |
| | |
| | | msg_Position = new SeerMessage((int)AGVCode.QueryPosition, SID); |
| | | msg_GuideStatus = new SeerMessage((int)AGVCode.QueryTaskStatus, SID, IConfig.IsSimpleMonitor ? JsonConvert.SerializeObject(new { simple = true }) : ""); |
| | | msg_Battery = new SeerMessage((int)AGVCode.QueryBattery, SID, IConfig.IsSimpleMonitor ? JsonConvert.SerializeObject(new { simple = true }) : ""); |
| | | msg_IO = new SeerMessage((int)AGVCode.QueryIO, SID); |
| | | |
| | | _taskDoneHandle.Reset(); |
| | | _monitorLock.Set(); |
| | | |
| | | Task.Run(() => |
| | | { |
| | | Monitor(); |
| | | }); |
| | | |
| | | Task.Run(() => |
| | | { |
| | |
| | | |
| | | protected override void Stop() |
| | | { |
| | | StateHandle.Set(); |
| | | PositionHandle.Set(); |
| | | BatteryHandle.Set(); |
| | | IOHandle.Set(); |
| | | |
| | | _taskDoneHandle.Set(); |
| | | _monitorLock.Set(); |
| | | |
| | | if (client_Guide != null && client_Guide.Connected) |
| | | { |
| | | CancelTask(); |
| | |
| | | } |
| | | } |
| | | |
| | | List<AGVTaskStatus> _taskRunningStates = new List<AGVTaskStatus>() { AGVTaskStatus.Running, AGVTaskStatus.Waiting, AGVTaskStatus.Suspended }; |
| | | List<AGVTaskStatus> _taskDoneStates = new List<AGVTaskStatus>() { AGVTaskStatus.Cancelled, AGVTaskStatus.Completed, AGVTaskStatus.Failed }; |
| | | AGVTaskStatus taskStatus = AGVTaskStatus.None; |
| | | public AGVTaskStatus TaskStatus |
| | | { |
| | |
| | | |
| | | if (taskStatus != AGVTaskStatus.None) |
| | | { |
| | | if (_isTaskRunning && _taskDoneStates.Contains(taskStatus)) |
| | | { |
| | | _taskDoneHandle.Set(); |
| | | _isTaskRunning = false; |
| | | } |
| | | |
| | | OnAGVTaskStatusChanged?.Invoke(this, taskStatus); |
| | | } |
| | | } |
| | |
| | | } |
| | | } |
| | | |
| | | public string LastStation { get; set; } |
| | | public string Destination { get; set; } = ""; |
| | | |
| | | SeerMessage msg_Position = new SeerMessage(); |
| | | SeerMessage msg_GuideStatus = new SeerMessage(); |
| | | SeerMessage msg_Battery = new SeerMessage(); |
| | | SeerMessage msg_IO = new SeerMessage(); |
| | | |
| | | public ManualResetEvent StateHandle { get; set; } = new ManualResetEvent(false); |
| | | public ManualResetEvent PositionHandle { get; set; } = new ManualResetEvent(false); |
| | | public ManualResetEvent BatteryHandle { get; set; } = new ManualResetEvent(false); |
| | | public ManualResetEvent IOHandle { get; set; } = new ManualResetEvent(false); |
| | | |
| | | private void MonitorAGV() |
| | | { |
| | | while (CurrentState != EnumHelper.DeviceState.DSClose && CurrentState != EnumHelper.DeviceState.DSExcept) |
| | | { |
| | | _monitorLock.WaitOne(); |
| | | try |
| | | { |
| | | SendMsg(client_State, IConfig.StatusPort, msg_Position); |
| | |
| | | Thread.Sleep(IConfig.ScanInterval); |
| | | SendMsg(client_State, IConfig.StatusPort, msg_Battery); |
| | | Thread.Sleep(IConfig.ScanInterval); |
| | | SendMsg(client_State, IConfig.StatusPort, msg_IO); |
| | | Thread.Sleep(IConfig.ScanInterval); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | OnLog?.Invoke(DateTime.Now, this, $"{Name}监听异常:{ex.GetExceptionMessage()}"); |
| | | LogAsync(DateTime.Now, "监听异常", ex.GetExceptionMessage()); |
| | | } |
| | | } |
| | | } |
| | |
| | | |
| | | var stream = client.GetStream(); |
| | | stream.Write(msg.Frame, 0, msg.Frame.Length); |
| | | |
| | | stream.Flush(); |
| | | int recSize = stream.Read(buffer, 0, buffer.Length); |
| | | if (recSize > 0) |
| | | { |
| | |
| | | SeerMessage recMsg = SeerMessage.GetSeerMessage(rec); |
| | | if (recMsg.TypeCode != (10000 + msg.TypeCode) || recMsg.SeqNum != msg.SeqNum) |
| | | { |
| | | throw new ProcessException("反馈信息和发送信息不一致", null); |
| | | throw new ProcessException("反馈信息和发送信息不一致"); |
| | | } |
| | | |
| | | if (recMsg.JValues.ContainsKey("ret_code")) |
| | |
| | | int retCode = recMsg.JValues.Value<int>("ret_code"); |
| | | if (retCode != 0) |
| | | { |
| | | throw new ProcessException($"反馈信息异常,异常码:{retCode}", null); |
| | | throw new ProcessException($"反馈信息异常,异常码:{retCode}"); |
| | | } |
| | | } |
| | | |
| | |
| | | catch (SocketException ex) |
| | | { |
| | | repeatTime--; |
| | | new ProcessException("AGV网络通信异常", ex); |
| | | new ProcessException("AGV网络通信异常", ExceptionLevel.Warning, ex); |
| | | |
| | | if (repeatTime <= 0) |
| | | { |
| | | throw new ProcessException("AGV网络通信失败", ex); |
| | | throw new ProcessException("AGV网络通信失败", ExceptionLevel.Warning, ex); |
| | | } |
| | | |
| | | Thread.Sleep(IConfig.ScanInterval); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | throw new ProcessException("AGV信息发送异常", ex); |
| | | throw new ProcessException("AGV信息发送异常", ExceptionLevel.Warning, ex); |
| | | } |
| | | |
| | | } while (repeatTime > 0); |
| | |
| | | { |
| | | case (int)AGVCode.QueryPosition: |
| | | CurrentPosition = recMsg.JValues.Value<string>("current_station"); |
| | | LastStation = recMsg.JValues.Value<string>("last_station"); |
| | | |
| | | PositionHandle.Set(); |
| | | break; |
| | | case (int)AGVCode.QueryTaskStatus: |
| | | TaskStatus = (AGVTaskStatus)recMsg.JValues.Value<int>("task_status"); |
| | | |
| | | StateHandle.Set(); |
| | | break; |
| | | case (int)AGVCode.QueryBattery: |
| | | BatteryLvl = recMsg.JValues.Value<float>("battery_level"); |
| | | |
| | | BatteryHandle.Set(); |
| | | break; |
| | | case (int)AGVCode.QueryIO: |
| | | //recMsg.JValues.Value<JArray>("DI").ToList().ForEach(j => |
| | | //{ |
| | | // var ioDefinition = IConfig.IOCollection.FirstOrDefault(i => i.IOIndex == j.Value<int>("id")); |
| | | |
| | | // if (ioDefinition != null && j.Value<bool>("status") != ioDefinition.CurrentValue) |
| | | // { |
| | | // ioDefinition.CurrentValue = j.Value<bool>("status"); |
| | | // OnAGVIOChanged?.Invoke(this, ioDefinition); |
| | | // } |
| | | //}); |
| | | |
| | | //高电平 1 低电平 0 |
| | | _ioDict = recMsg.JValues.Value<JArray>("DI").ToDictionary(u => u.Value<int>("id"), u => u.Value<bool>("status") ? 1 : 0); |
| | | IOHandle.Set(); |
| | | break; |
| | | default: |
| | | break; |
| | |
| | | SendMsg(client_Guide, IConfig.GuidePort, msg); |
| | | } |
| | | |
| | | public void TaskOrder(string dest) |
| | | ManualResetEvent _taskDoneHandle = new ManualResetEvent(false); |
| | | ManualResetEvent _monitorLock = new ManualResetEvent(true); |
| | | bool _isTaskRunning = false; |
| | | public void TaskOrder(string dest, bool isWaitFinished) |
| | | { |
| | | CurrentPosition = ""; |
| | | SeerMessage msg = new SeerMessage((int)AGVCode.TaskOrder, SID, JsonConvert.SerializeObject(new { id = dest })); |
| | | _monitorLock.Reset(); |
| | | Thread.Sleep(IConfig.ScanInterval); |
| | | |
| | | OnLog?.BeginInvoke(DateTime.Now, this, $"{Name}行驶向 {dest}", null, null); |
| | | CurrentPosition = ""; |
| | | Destination = dest; |
| | | TaskStatus = AGVTaskStatus.None; |
| | | |
| | | SeerMessage msg = new SeerMessage((int)AGVCode.TaskOrder, SID, JsonConvert.SerializeObject(new { id = dest })); |
| | | //OnLog?.BeginInvoke(DateTime.Now, this, $"{Name}行驶向 {dest}", null, null); |
| | | LogAsync(DateTime.Now, "", $"{Name}行驶向 {dest}"); |
| | | |
| | | SendMsg(client_Guide, IConfig.GuidePort, msg); |
| | | //Thread.Sleep(IConfig.ScanInterval); |
| | | _monitorLock.Set(); |
| | | |
| | | if (isWaitFinished) |
| | | { |
| | | _isTaskRunning = true; |
| | | _taskDoneHandle.Reset(); |
| | | bool isNotTimeout = _taskDoneHandle.WaitOne((int)(IConfig.OperationTimeout * 60 * 1000)); |
| | | if (!isNotTimeout) |
| | | { |
| | | throw new ProcessException($"{Name}执行去往{dest}动作超时"); |
| | | } |
| | | |
| | | if (TaskStatus == AGVTaskStatus.Failed) |
| | | { |
| | | throw new ProcessException($"{Name}执行去往{dest}动作失败"); |
| | | } |
| | | else if (TaskStatus == AGVTaskStatus.Cancelled) |
| | | { |
| | | //OnLog?.Invoke(DateTime.Now, this, $"{Name}执行去往{dest}动作取消"); |
| | | LogAsync(DateTime.Now, "", $"{Name}执行去往{dest}动作取消"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | #region IMonitor |
| | | public event OnMonitorInvokeDelegate OnMonitorInvoke; |
| | | public event OnMonitorAlarmDelegate OnMonitorAlarm; |
| | | |
| | | Dictionary<int, int> _ioDict = new Dictionary<int, int>(); |
| | | public void Monitor() |
| | | { |
| | | while (CurrentState != EnumHelper.DeviceState.DSClose && CurrentState != EnumHelper.DeviceState.DSExcept) |
| | | { |
| | | try |
| | | { |
| | | IOHandle.Reset(); |
| | | |
| | | var isNotTimeout = IOHandle.WaitOne(IConfig.ScanInterval * 3); |
| | | |
| | | if (!isNotTimeout) |
| | | throw new ProcessException($"{Name}监听超时"); |
| | | |
| | | IConfig.MonitorSetCollection.ForEach(m => |
| | | { |
| | | if (_ioDict.ContainsKey(m.TriggerIndex)) |
| | | { |
| | | if ((m.TriggerValue == _ioDict[m.TriggerIndex] || m.TriggerIndex == -999) && _ioDict[m.TriggerIndex] != m.CurrentValue) |
| | | { |
| | | if (m.OpConfig == null) |
| | | { |
| | | m.OpConfig = new OperationConfigBase(); |
| | | } |
| | | |
| | | m.OpConfig.InputPara = m.InputDataIndex.ConvertAll(index => |
| | | { |
| | | if (_ioDict.ContainsKey(index)) |
| | | { |
| | | return _ioDict[index]; |
| | | } |
| | | else |
| | | { |
| | | return -1; |
| | | } |
| | | }).ToList(); |
| | | |
| | | OnMonitorInvoke?.BeginInvoke(DateTime.Now, this, m, null, null); |
| | | } |
| | | |
| | | m.CurrentValue = _ioDict[m.TriggerIndex]; |
| | | } |
| | | }); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | OnLog?.Invoke(DateTime.Now, this, $"{Name}监听异常:{ex.GetExceptionMessage()}"); |
| | | } |
| | | } |
| | | } |
| | | #endregion |
| | | } |
| | | } |