RobotTask.cs 8.15 KB
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Rcs.Domain.Attributes;
using Rcs.Domain.Entities.DomainEvents.RobotTask;

namespace Rcs.Domain.Entities;

/// <summary>
/// 任务实体类
/// @author zzy
/// </summary>
[Table("robot_tasks")]
public class RobotTask : Entity
{
    /// <summary>
    /// 系统ID
    /// </summary>
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    [Column("task_id")]
    public Guid TaskId { get; set; }

    /// <summary>
    /// 任务编码
    /// </summary>
    [Required]
    [Column("task_code")]
    [MaxLength(50)]
    public string TaskCode { get; set; }

    /// <summary>
    /// 任务名称
    /// </summary>
    [Required]
    [Column("task_name")]
    [MaxLength(100)]
    public string? TaskName { get; set; }

    /// <summary>
    /// 机器人ID(外键)
    /// </summary>
    [Column("robot_id")]
    public Guid? RobotId { get; set; }

    /// <summary>
    /// 任务模板ID(可空)
    /// @author zzy
    /// </summary>
    [Column("task_Template_id")]
    public Guid? TaskTemplateId { get; set; }

    /// <summary>
    /// 起点库位ID
    /// @author zzy
    /// </summary>
    [Column("begin_location_id")]
    public Guid? BeginLocationId { get; set; }

    /// <summary>
    /// 终点库位ID
    /// @author zzy
    /// </summary>
    [Column("end_location_id")]
    public Guid? EndLocationId { get; set; }

    /// <summary>
    /// 任务状态
    /// </summary>
    [Column("status")]
    public TaskStatus Status { get; set; }
    /// <summary>
    /// 任暂停
    /// </summary>
    [Column("pause")]
    public bool Pause { get; set; }

    /// <summary>
    /// 优先级
    /// </summary>
    [Column("priority")]
    public int Priority { get; set; } = 99;
    /// <summary>
    /// 单据来源
    /// </summary>
    [Column("source")]
    public string? Source { get; set; }
    /// <summary>
    /// 关联单据
    /// </summary>
    [Column("relation")]
    public string? Relation { get; set; }
        
    /// <summary>
    /// 货架编码
    /// @author zzy
    /// </summary>
    [Column("shelf_code")]
    [MaxLength(50)]
    public string? ShelfCode { get; set; }

    /// <summary>
    /// 故障信息
    /// </summary>
    [Column("error_info", TypeName = "text")]
    public string? ErrorInfo { get; set; }

    /// <summary>
    /// 创建时间
    /// </summary>
    [Column("created_at", TypeName = "timestamp")]
    public DateTime CreatedAt { get; set; }

    /// <summary>
    /// 更新时间
    /// </summary>
    [Column("updated_at", TypeName = "timestamp")]
    public DateTime? UpdatedAt { get; set; }

    #region 外键 => 导航属性,ManyToOne/OneToOne

    /// <summary>
    /// 机器人导航属性
    /// </summary>
    [ForeignKey(nameof(RobotId))]
    public virtual Robot? Robot { get; set; }

    /// <summary>
    /// 起点库位导航属性
    /// @author zzy
    /// </summary>
    [ForeignKey(nameof(BeginLocationId))]
    public virtual StorageLocation? BeginLocation { get; set; }

    /// <summary>
    /// 终点库位导航属性
    /// @author zzy
    /// </summary>
    [ForeignKey(nameof(EndLocationId))]
    public virtual StorageLocation? EndLocation { get; set; }

    /// <summary>
    /// 任务模板导航属性
    /// @author zzy
    /// </summary>
    [ForeignKey(nameof(TaskTemplateId))]
    public virtual TaskTemplate? TaskTemplate { get; set; }

    /// <summary>
    /// 子任务集合导航属性
    /// @author zzy
    /// </summary>
    public virtual ICollection<RobotSubTask> SubTasks { get; set; } = new List<RobotSubTask>();

    #endregion

    #region 领域方法

    /// <summary>
    /// 创建任务
    /// @author zzy
    /// </summary>
    /// <param name="taskCode">任务编码</param>
    /// <param name="taskName">任务名称</param>
    /// <param name="beginLocationId">起点库位ID</param>
    /// <param name="endLocationId">终点库位ID</param>
    /// <param name="priority">优先级(默认99)</param>
    /// <param name="taskTemplateId">任务模板ID</param>
    /// <param name="shelfCode">货架编码</param>
    /// <param name="robotId">机器人ID</param>
    /// <param name="status">任务状态</param>
    public void Create(string taskCode, string? taskName, Guid? beginLocationId, Guid? endLocationId,
        int priority = 99, Guid? taskTemplateId = null, string? shelfCode = null,
        Guid? robotId = null, TaskStatus status = TaskStatus.Pending)
    {
        TaskId = Guid.NewGuid();
        TaskCode = taskCode;
        TaskName = taskName;
        RobotId = robotId;
        BeginLocationId = beginLocationId;
        EndLocationId = endLocationId;
        Priority = priority;
        TaskTemplateId = taskTemplateId;
        ShelfCode = shelfCode;
        Status = status;
        CreatedAt = DateTime.Now;
        AddDomainEvent(new TaskCreatedDomainEvent(TaskId));
    }

    /// <summary>
    /// 挂起任务
    /// @author zzy
    /// </summary>
    public void Suspend()
    {
        Status = TaskStatus.WaitingForResource;
        UpdatedAt = DateTime.Now;
        AddDomainEvent(new TaskSuspendedDomainEvent(TaskId));
    }
    /// <summary>
    /// 挂起任务
    /// @author zzy
    /// </summary>
    public void Completed(Guid? robotId = null)
    {
        if (robotId.HasValue) robotId = RobotId;
        Status = TaskStatus.Completed;
        UpdatedAt = DateTime.Now;
        AddDomainEvent(new TaskCompletedDomainEvent(TaskId));
    }

    /// <summary>
    /// 暂停任务
    /// @author zzy
    /// </summary>
    public void Paused()
    {
        Pause =  true;
        UpdatedAt = DateTime.Now;
        AddDomainEvent(new TaskPausedDomainEvent(TaskId));
    }

    /// <summary>
    /// 继续任务
    /// @author zzy
    /// </summary>
    public void Resume()
    {
        Pause = false;
        UpdatedAt = DateTime.Now;
        AddDomainEvent(new TaskResumedDomainEvent(TaskId));
    }
    /// <summary>
    /// 任务已分配
    /// @author zzy
    /// </summary>
    public void Assign(Guid robotId)
    {
        RobotId = robotId;
        Status = TaskStatus.Assigned;
        UpdatedAt = DateTime.Now;
        AddDomainEvent(new TaskAssignedDomainEvent(TaskId));
    }

    /// <summary>
    /// 取消任务
    /// @author zzy
    /// </summary>
    public void Cancel()
    {
        Status = TaskStatus.Cancelled;
        UpdatedAt = DateTime.Now;
        AddDomainEvent(new TaskCancelledDomainEvent(TaskId, RobotId));
    }

    /// <summary>
    /// 任务失败
    /// @author zzy
    /// </summary>
    /// <param name="errorInfo">错误信息</param>
    public void Fail(string? errorInfo = null)
    {
        Status = TaskStatus.Failed;
        ErrorInfo = errorInfo;
        UpdatedAt = DateTime.Now;
        AddDomainEvent(new TaskFailedDomainEvent(TaskId, errorInfo));
    }

    /// <summary>
    /// 判断任务是否为执行中状态
    /// 等待中、已分配、已完成、已取消为非执行中状态,其他为执行中状态
    /// @author zzy
    /// </summary>
    /// <returns>是否为执行中状态</returns>
    public bool IsInProgress()
    {
        return Status != TaskStatus.Pending
               && Status != TaskStatus.Completed
               && Status != TaskStatus.Cancelled;
    }
    /// <summary>
    /// 开始执行任务
    /// @author zzy
    /// </summary>
    public void StartExecution()
    {
        Status = TaskStatus.InProgress;
        UpdatedAt = DateTime.Now;
    }

    #endregion
}

/// <summary>
/// 任务状态枚举 - RCS调度任务状态
/// @author zzy
/// </summary>
public enum TaskStatus
{
    [EnumDescription("等待中", "Pending")]
    Pending = 1,

    [EnumDescription("已分配", "Assigned")]
    Assigned = 2,

    [EnumDescription("执行中", "Pick In Progress")]
    InProgress = 3,

    [EnumDescription("等待资源", "Waiting for Resource")]
    WaitingForResource,

    [EnumDescription("已完成", "Completed")]
    Completed,

    [EnumDescription("已取消", "Cancelled")]
    Cancelled,

    [EnumDescription("执行失败", "Failed")]
    Failed,

    [EnumDescription("超时", "Timeout")]
    Timeout,

    [EnumDescription("重新分配", "Reassigned")]
    Reassigned
}