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

990 lines
28 KiB
C#

using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Rs.MotionPlat.Flow;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
namespace Rs.MotionPlat.Commom
{
/// <summary>
/// 排料通讯
/// </summary>
public class Scheduling
{
/// <summary>
/// 指令
/// </summary>
public EInstruction Instruction { get; set; }
/// <summary>
/// 组ID
/// </summary>
public int GroupID { get; set; }
/// <summary>
/// 周转ID
/// </summary>
public int TurnoverID { get; set; }
/// <summary>
/// 发送或接收时间
/// </summary>
public DateTime Time { get; set; } = DateTime.Now;
public override string ToString() => JsonConvert.SerializeObject(this, new StringEnumConverter());
}
public class SchedulingConnectionInfo : Scheduling
{
public EType Type { get; set; }
public enum EType
{
Scheduling,
ScannerMotion
}
}
/// <summary>
/// 上下料
/// </summary>
public class SchedulingMaterial : Scheduling
{
public List<ActionItem> ActionItems { get; set; }
public SchedulingMaterial() { }
}
public struct ActionItem
{
/// <summary>
/// 产品条码
/// </summary>
public string SN;
/// <summary>
/// 取料信息
/// </summary>
public ActionInfo Load;
/// <summary>
/// 放料信息
/// </summary>
public ActionInfo To;
public ActionItem(ActionInfo load, ActionInfo to,string _SN)
{
SN=_SN;
Load = load;
To = to;
}
}
public struct ActionInfo
{
/// <summary>
/// 类型
/// </summary>
public TurnoverType Type;
/// <summary>
/// 分区名
/// </summary>
public string RangeName;
/// <summary>
/// 层(仅多功能时用)
/// </summary>
public int Floor;
/// <summary>
/// 料盘索引(仅周转盘用)
/// </summary>
public int Index;
public ActionInfo(TurnoverType type, int floor, string rangeName, int index)
{
Type = type;
RangeName = rangeName;
Floor = floor;
Index = index;
}
}
public class TurnoverInfos : Scheduling
{
/// <summary>
/// 周转信息
/// </summary>
public List<TurnoverInfo> Infos { get; set; }
/// <summary>
/// 跳过取放信息
/// </summary>
public List<SkipInfo> SkipInfos { get; set; }
}
public class TurnoverInfo
{
#region 冗余字段
[JsonIgnore]
public EInstruction Instruction;
[JsonIgnore]
public int GroupID;
[JsonIgnore]
public int TurnoverID;
[JsonIgnore]
public string GUID = string.Empty;
[JsonIgnore]
public ETaskMode taskMode = ETaskMode.Load;
#endregion
public string SN;
/// <summary>
/// 来源类型
/// </summary>
public TurnoverType FromType;
/// <summary>
/// 来源料仓所在层,如是周转盘则为0
/// </summary>
public int FromFloor;
/// <summary>
/// 来源索引
/// </summary>
public int FromIndex;
/// <summary>
/// 目标类型
/// </summary>
public TurnoverType ToType;
/// <summary>
/// 目标料仓所在层,如是周转盘则为0
/// </summary>
public int ToFloor;
/// <summary>
/// 目标索引
/// </summary>
public int ToIndex;
/// <summary>
/// 使用的吸嘴编号
/// </summary>
public int SuckerNo;
public bool Dealed;
public TurnoverInfo(EInstruction _Instruction,int _GroupID,int _TurnoverID,string _SN,TurnoverType fromType, int fromFloor, int fromIndex, TurnoverType toType, int toFloor, int toIndex, int suckerNo,bool _Dealed=false)
{
Dealed = _Dealed;
Instruction = _Instruction;
GroupID = _GroupID;
TurnoverID = _TurnoverID;
SN = _SN;
FromType = fromType;
FromFloor = fromFloor;
FromIndex = fromIndex;
ToType = toType;
ToFloor = toFloor;
ToIndex = toIndex;
SuckerNo = suckerNo;
}
public TurnoverInfo() { }
}
public struct SkipInfo
{
public TurnoverType Type;
public int Floor;
public int Index;
public SkipCause Cause;
public string Message;
public SkipInfo(TurnoverType type, int floor, int index, SkipCause cause, string message)
{
Type = type;
Floor = floor;
Index = index;
Cause = cause;
Message = message;
}
}
public enum SkipCause
{
Empty, Error
}
public enum TurnoverType
{
/// <summary>
/// 未知
/// </summary>
Unknown,
/// <summary>
/// 待测料仓
/// </summary>
ToBeTested,
/// <summary>
/// OK仓
/// </summary>
Passed,
/// <summary>
/// NG仓
/// </summary>
Failed,
/// <summary>
/// 多功能仓
/// </summary>
Multifunction,
/// <summary>
/// 周转盘
/// </summary>
Turnover,
/// <summary>
/// 换料缓存
/// </summary>
Buffer,
/// <summary>
/// 重测缓存盘
/// </summary>
BufferTray,
/// <summary>
/// 清洗料盘
/// </summary>
CleanPad,
/// <summary>
/// 测试治具
/// </summary>
Tester
}
/// <summary>
/// 取料失败
/// </summary>
public class SchedulingTakingError : Scheduling
{
/// <summary>
/// ID
/// </summary>
public int ID { get; set; }
/// <summary>
/// 周转类型
/// </summary>
public TurnoverType Type { get; set; }
/// <summary>
/// 当前层
/// </summary>
public int Floor { get; set; }
/// <summary>
/// 托盘索引
/// </summary>
public int Index { get; set; }
/// <summary>
/// 使用的吸嘴索引
/// </summary>
public int SuckerIndex { get; set; } = -1;
/// <summary>
/// 处理方式,中控返回时提供
/// </summary>
public TakeErrorHandle Handle { get; set; }
}
public enum TakeErrorHandle
{
/// <summary>
/// 重取
/// </summary>
Retry,
/// <summary>
/// 跳过当前穴位
/// </summary>
Skip,
/// <summary>
/// 结束本轮取料
/// </summary>
End
}
public class SchedulingSiloBase : Scheduling
{
public ESiloType SiloType { get; set; }
public EMultifunctionType MultifunctionType { get; set; }
public enum ESiloType
{
/// <summary>
/// 待测料仓
/// </summary>
ToBeTested,
/// <summary>
/// OK仓
/// </summary>
Passed,
/// <summary>
/// NG仓
/// </summary>
Failed,
/// <summary>
/// 多功能仓
/// </summary>
Multifunction
}
public enum EMultifunctionType
{
/// <summary>
/// 无
/// </summary>
None,
/// <summary>
/// 默认
/// </summary>
Default,
/// <summary>
/// 第二层
/// </summary>
Floor1,
/// <summary>
/// 第三层
/// </summary>
Floor2,
/// <summary>
/// 第四层
/// </summary>
Floor3
}
}
/// <summary>
/// 托盘产品分区范围
/// </summary>
public class TrayProductRange : SchedulingSiloBase
{
/// <summary>
/// 该分区的名称
/// </summary>
public string Name { get; set; }
public int Left { get; set; }
public int Top { get; set; }
public int Right { get; set; }
public int Bottom { get; set; }
}
public class SiloRearrange : SchedulingSiloBase
{
public enum EOrientation { Horizontal, Vertical }
/// <summary>
/// 排列方向
/// </summary>
public EOrientation Orientation { get; set; }
}
public class SchedulingSilo : SchedulingSiloBase
{
public enum ESwitchOrientation
{
/// <summary>
/// 切到下一个
/// </summary>
Next,
/// <summary>
/// 切回上一个
/// </summary>
Previous
}
public enum ESiloStatus
{
/// <summary>
/// 停止
/// </summary>
Stopped,
/// <summary>
/// 就绪
/// </summary>
Standby,
/// <summary>
/// 切换中
/// </summary>
Switching,
/// <summary>
/// 空,无料盘
/// </summary>
None
}
/// <summary>
/// 料仓状态
/// </summary>
public ESiloStatus SiloStatus { get; set; }
/// <summary>
/// 切换方向
/// </summary>
public ESwitchOrientation Orientation { get; set; }
/// <summary>
/// 料仓当前层 <see cref="SiloStatus"/> == <see cref="ESiloStatus.Switching"/>时无效
/// </summary>
public int SiloFloor { get; set; }
}
public class SchedulingMoveScanner : Scheduling
{
/// <summary>
/// 点位 1~32 (<= 100) 托盘穴位点位, 101~102 (>100) 托盘条码点位
/// </summary>
public int Point { get; set; }
}
public class SchedulingMoveScannerResult : SchedulingMessage
{
/// <summary>
/// 当前点位或移动中的目标点位
/// </summary>
public int Point { get; set; }
/// <summary>
/// 当前移动状态
/// </summary>
public EMoveState MoveState { get; set; }
public enum EMoveState
{
/// <summary>
/// 已到达
/// </summary>
Ready,
/// <summary>
/// 移动中
/// </summary>
Moving,
/// <summary>
/// 发生错误,无法恢复
/// </summary>
Error,
/// <summary>
/// 中断,出现异常中断,可恢复
/// </summary>
Interrupt
}
}
/// <summary>
/// 排料报警
/// </summary>
//public class SchedulingAlarms : Scheduling
//{
// /// <summary>
// /// 报警项,如果item1 == item2 将会认作同一报警
// /// </summary>
// public IList<AlarmItem> Alarms { get; set; }
// public struct AlarmItem : IAlarmItem
// {
// /// <summary>
// /// 报警序号
// /// </summary>
// public int NO { get; set; }
// /// <summary>
// /// 引发报警的节点UID
// /// </summary>
// public long NodeUID { get; set; }
// /// <summary>
// /// 引发报警节点的穴位索引(如:周转盘穴位 3)
// /// </summary>
// public int? Index
// {
// readonly get => Indexes is HashSet<int> list && list.Count > 0 ? list.Last() : null;
// set
// {
// if (value is not int index) return;
// HashSet<int> list = Indexes ??= new();
// if (!list.Contains(index))
// {
// list.Add(index);
// }
// }
// }
// /// <summary>
// /// 引发报警的节点的穴位索引列表
// /// </summary>
// public HashSet<int> Indexes { get; set; }
// /// <summary>
// /// 报警标签
// /// </summary>
// public string Tag { get; set; }
// /// <summary>
// /// 该报警是否会造成停机
// /// </summary>
// public bool Pause { get; set; }
// /// <summary>
// /// 报警等级
// /// </summary>
// public AlarmLevel Level { get; set; }
// /// <summary>
// /// 已在别的地方展示,只收录,不弹窗
// /// </summary>
// public bool ShowElsewhere { get; set; }
// /// <summary>
// /// 报警消息英文版
// /// </summary>
// public string EN { get; set; }
// /// <summary>
// /// 报警消息中文版
// /// </summary>
// public string ZH { get; set; }
// readonly IReadOnlySet<int> IAlarmItem.Indexes => Indexes;
// public readonly string GetMessage(CultureInfo culture)
// {
// return culture.TwoLetterISOLanguageName.ToLower() switch
// {
// "en" => EN,
// _ => ZH,
// };
// }
// public readonly string ToDescription(CultureInfo culture) => $"{GetString(nameof(NO), culture)}: {NO} "
// + $"{GetString(nameof(NodeUID), culture)}: {NodeUID} "
// + $"{GetString(nameof(Index), culture)}: {Indexes.ToStringEx()} "
// + $"{GetString(nameof(Tag), culture)}: {Tag} "
// + $"{GetString(nameof(Pause), culture)}: {Pause} "
// + $"{GetString(nameof(EN), culture)}: {EN} "
// + $"{GetString(nameof(ZH), culture)}: {ZH}";
// public override readonly bool Equals(object obj) => obj is IAlarmItem other && Equals(other);
// /// <summary>
// /// 相等比较方法
// /// </summary>
// /// <param name="other"></param>
// /// <returns></returns>
// public readonly bool Equals(IAlarmItem other) => NO == other.NO && NodeUID == other.NodeUID && SequenceEqual(Indexes, other.Indexes) && Tag == other.Tag;
// public override readonly int GetHashCode() => HashCode.Combine(NO, NodeUID, Indexes, Tag);
// public override readonly string ToString() => $"{Tag} No.{NO} {NodeUID} {Indexes.ToStringEx()} {ZH}";
// public static bool operator ==(AlarmItem left, AlarmItem right) => left.Equals(right);
// public static bool operator !=(AlarmItem left, AlarmItem right) => !(left == right);
// }
// public SchedulingAlarms() { }
// public SchedulingAlarms(params AlarmItem[] items) => Alarms = items;
//}
/// <summary>
/// 排料状态信息
/// </summary>
public class SchedulingStatusInfo : Scheduling
{
/// <summary>
/// 查询<see cref="EInstruction.InquireStatus"/>或切换<see cref="EInstruction.SwitchStatus"/>时的状态类型
/// </summary>
public InfoType Type { get; set; }
/// <summary>
/// 查询<see cref="EInstruction.InquireStatus"/>时返回或切换<see cref="EInstruction.SwitchStatus"/>时需要切换到的状态信息
/// </summary>
public string Info { get; set; }
public bool TryConvertInfoTo<TEnum>(out TEnum result) where TEnum : struct
{
try
{
if (typeof(TEnum).IsEnum)
return Enum.TryParse(Info, true, out result);
result = (TEnum)Convert.ChangeType(Info, typeof(TEnum));
return true;
}
catch
{
result = default;
return false;
}
}
public enum InfoType
{
/// <summary>
/// 上下料状态,<see cref="ERunState"/>
/// </summary>
State,
/// <summary>
/// 设备运行状态,<see cref="ERunStatus"/>
/// </summary>
RunStatus,
/// <summary>
/// 运行模式,<see cref="ERunMode"/>
/// </summary>
RunMode,
/// <summary>
/// 初始化状态,<see cref="EInitializeState"/>
/// </summary>
InitializeState,
/// <summary>
/// 物料分配模式(带料OR不带料),<see cref="EAssignMode"/>
/// </summary>
AssignMode,
/// <summary>
/// 温度,<see cref="double"/>
/// </summary>
Temperature,
/// <summary>
/// 气压(double)
/// </summary>
AirPressure
}
}
/// <summary>
/// 排料消息
/// </summary>
public class SchedulingMessage : Scheduling
{
/// <summary>
/// 消息
/// </summary>
public string Message { get; set; }
}
/// <summary>
/// 中控状态
/// </summary>
public class MachineState : SchedulingMessage
{
/// <summary>
/// 设备状态
/// </summary>
public ERunState State { get; set; }
public MachineState() { }
}
/// <summary>
/// 排料结果
/// </summary>
public class SchedulingResult : SchedulingMessage
{
/// <summary>
/// 结果
/// </summary>
public bool Result { get; set; } = true;
/// <summary>
/// 设备状态
/// </summary>
public ERunState State { get; set; }
public SchedulingResult() { }
}
/// <summary>
/// 排料弹窗请求
/// </summary>
public class SchedulingMessageBox : SchedulingMessage
{
[Flags]
public enum ETipButton
{
None = 0,
Yes = 1 << 0,
No = 1 << 1,
Ok = 1 << 2,
Cancel = 1 << 3,
Retry = 1 << 4,
Skip = 1 << 5,
YesNo = Yes | No,
OkCancel = Ok | Cancel,
OkRetryCancel = Ok | Retry | Cancel,
RetrySkip = Retry | Skip
}
/// <summary>
/// 请求弹窗按钮,返回用户点击的按钮
/// </summary>
public ETipButton Button { get; set; } = ETipButton.YesNo;
/// <summary>
/// 请求的ID,返回时将带上该id作为标识
/// </summary>
public int ID { get; set; }
/// <summary>
/// 弹窗标题
/// </summary>
public string Title { get; set; }
/// <summary>
/// 图片路径,如为null或<see cref="string.Empty"/>则不显示图片
/// </summary>
public string PicturePath { get; set; }
/// <summary>
/// 字体大小
/// </summary>
public double FontSize { get; set; } = 15;
/// <summary>
/// 窗口宽度
/// </summary>
public double WindowWidth { get; set; } = 800;
/// <summary>
/// 窗口高度
/// </summary>
public double WindowHeight { get; set; } = 800;
/// <summary>
/// 倒计时,单位秒,小于0不启用
/// </summary>
public int Countdown { get; set; } = System.Threading.Timeout.Infinite;
/// <summary>
/// 倒计时结束时按下的按钮
/// </summary>
public ETipButton CountdownResult { get; set; } = ETipButton.Yes;
public SchedulingMessageBox() { }
}
/// <summary>
/// 指令
/// </summary>
public enum EInstruction
{
/// <summary>
/// 查询连接功能 发送:<see cref="Scheduling"/> 回复:<see cref="SchedulingConnectionInfo"/>
/// </summary>
InquireConnectionInfo,
/// <summary>
/// 上下料 发送:<see cref="SchedulingMaterial"/> 回复:<see cref="SchedulingResult"/>
/// </summary>
LoadAndUnload,
/// <summary>
/// 排料机回传上下料结果 排料机发送<see cref="TurnoverInfos"/>
/// </summary>
LoadAndUnloadResult,
/// <summary>
/// 取消当前<see cref="Scheduling.GroupID"/> <see cref="Scheduling.TurnoverID"/>执行中的排料指令,发送:<see cref="Scheduling"/> 回复:<see cref="SchedulingResult"/>
/// </summary>
CancelLoadAndUnload,
/// <summary>
/// 查询状态 发送:<see cref="SchedulingStatusInfo"/> 回复:<see cref="SchedulingStatusInfo"/>
/// </summary>
InquireStatus,
/// <summary>
/// 切换状态 发送:<see cref="SchedulingStatusInfo"/> 回复:<see cref="SchedulingStatusInfo"/>
/// </summary>
SwitchStatus,
/// <summary>
/// 清除报警 发送:<see cref="Scheduling"/> 回复:<see cref="SchedulingResult"/>
/// </summary>
ClearAlarm,
/// <summary>
/// 获取配方列表 发送:<see cref="Scheduling"/> 回复:<see cref="SchedulingResult"/>
/// </summary>
GetRecipeList,
/// <summary>
/// 设置配方 发送:<see cref="SchedulingMessage"/> 回复:<see cref="SchedulingResult"/>
/// </summary>
SetRecipe,
/// <summary>
/// 获取当前配方 发送:<see cref="Scheduling"/> 回复:<see cref="SchedulingResult"/>
/// </summary>
GetCurrentRecipe,
/// <summary>
/// 周转盘就绪查询 排料机发送<see cref="Scheduling"/> 中控回复: <see cref="SchedulingResult"/>
/// </summary>
TurnoverReady,
/// <summary>
/// 通知排料运行状态变更 发送<see cref="MachineState"/> 回复:<see cref="SchedulingResult"/>
/// </summary>
ChangeState,
/// <summary>
/// 弹消息提示窗 排料机发送<see cref="SchedulingMessageBox"/> 中控回复: <see cref="SchedulingMessageBox"/>
/// </summary>
ShowMessage,
/// <summary>
/// 取消消息弹窗 排料机发送<see cref="SchedulingMessageBox"/> 中控回复: <see cref="SchedulingMessageBox"/>
/// </summary>
CloseMessage,
/// <summary>
/// 设备按钮按下 排料机发送<see cref="SchedulingMessage"/> 中控回复: <see cref="SchedulingMessage"/>,
/// <para>其中<see cref="SchedulingMessage.Message"/>内容为<see cref="EMachineButton"/>枚举.ToString()</para>
/// </summary>
MachineButtonDown,
/// <summary>
/// 设备按钮松开 排料机发送<see cref="SchedulingMessage"/> 中控回复: <see cref="SchedulingMessage"/>,
/// <para>其中<see cref="SchedulingMessage.Message"/>内容为<see cref="EMachineButton"/>枚举.ToString()</para>
/// </summary>
MachineButtonUp,
/// <summary>
/// 设置托盘产品分区范围 发送:<see cref="Communication.TrayProductRange"/> 回复:<see cref="SchedulingResult"/>
/// </summary>
TrayProductRange,
/// <summary>
/// 清空托盘产品分区范围 发送:<see cref=Scheduling"/> 回复:<see cref="SchedulingResult"/>
/// </summary>
ClearTrayProductRange,
/// <summary>
/// 请求切换料盘 发送<see cref="SchedulingSilo"/> 回复:<see cref="SchedulingSilo"/>
/// </summary>
RequestSwitchTray,
/// <summary>
/// 开始切换料盘 发送<see cref="SchedulingSilo"/> 回复:<see cref="SchedulingSilo"/>
/// </summary>
BeginSwitchTray,
/// <summary>
/// 查询料仓状态 发送<see cref="SchedulingSilo"/> 回复:<see cref="SchedulingSilo"/>
/// </summary>
InquireSiloStatus,
/// <summary>
/// 移动扫码枪 发送<see cref="SchedulingMoveScanner"/> 回复收到的:<see cref="SchedulingMoveScanner"/>
/// </summary>
MoveScanner,
/// <summary>
/// 查询移动扫码枪结果 发送<see cref="Scheduling"/> 回复:<see cref="SchedulingMoveScannerResult"/>
/// </summary>
InquireMoveScannerState,
/// <summary>
/// 设置报警 排料机发送<see cref="SchedulingAlarms"/> 中控回复收到的<see cref="SchedulingAlarms"/>
/// </summary>
SetAlarms,
/// <summary>
/// 取消报警 排料机发送<see cref="SchedulingAlarms"/> 中控回复收到的<see cref="SchedulingAlarms"/>
/// </summary>
CancelAlarms,
/// <summary>
/// 最后一盘, 发送<see cref="Scheduling"/> 回复<see cref="SchedulingResult"/>
/// </summary>
IsLastBeforeTray,
/// <summary>
/// 结束上新, 发送<see cref="Scheduling"/> 回复<see cref="SchedulingResult"/>
/// </summary>
EndInput,
/// <summary>
/// 取料失败, 排料机发送<see cref="SchedulingTakingError"/> 回复<see cref="SchedulingTakingError"/>, <see cref="SchedulingTakingError.Handle"/>里提供接下来该做的处理方式
/// </summary>
TakingError,
/// <summary>
/// 取消取料失败, 排料机发送<see cref="SchedulingTakingError"/>, <see cref="SchedulingTakingError.Handle"/>里提供接下来该做的处理方式
/// </summary>
CancelTakingError,
/// <summary>
/// 重排料仓当前料盘物料方向 发送:<see cref="SiloRearrange"/> 回复:<see cref="SiloRearrange"/>
/// </summary>
Rearrange,
/// <summary>
/// 重排料仓当前料盘物料方向的结果 发送:<see cref="TurnoverInfos"/>
/// </summary>
RearrangeResult,
/// <summary>
/// 扫描条码, 中控发送<see cref="ScanTray"/> 回复<see cref="ScanTray"/>
/// </summary>
ScanBarcode
}
/// <summary>
/// 运行模式
/// </summary>
public enum ERunMode
{
/// <summary>
/// 手动
/// </summary>
Manual,
/// <summary>
/// 自动
/// </summary>
Automatic
}
/// <summary>
/// 设备运行状态
/// </summary>
public enum ERunStatus
{
/// <summary>
/// 已开始
/// </summary>
Started,
/// <summary>
/// 已停止
/// </summary>
Stopped,
/// <summary>
/// 报警中
/// </summary>
InAlarm,
/// <summary>
/// 急停中
/// </summary>
EMGStop
}
/// <summary>
/// 初始化状态
/// </summary>
public enum EInitializeState
{
/// <summary>
/// 未初始化
/// </summary>
Uninitialized,
/// <summary>
/// 初始化中
/// </summary>
Initializing,
/// <summary>
/// 已初始化
/// </summary>
Initialized
}
/// <summary>
/// 物料分配模式
/// </summary>
public enum EAssignMode
{
/// <summary>
/// 正常
/// </summary>
Normal,
/// <summary>
/// 无料空跑
/// </summary>
NoDevice
}
/// <summary>
/// 设备按钮
/// </summary>
public enum EMachineButton
{
Start, Stop, EMGStop, Initialize, Ok, Cancel, Retry, Skip, Lighting, Door
}
/// <summary>
/// 上下料状态
/// </summary>
public enum ERunState
{
/// <summary>
/// 等待指令
/// </summary>
Waiting,
/// <summary>
/// 忙碌中
/// </summary>
Busying,
/// <summary>
/// 下料中
/// </summary>
Unloading,
/// <summary>
/// 发生错误,无法恢复
/// </summary>
Error,
/// <summary>
/// 中断,出现异常中断,可恢复
/// </summary>
Interrupt
}
}