using EasyInject.Attributes; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.Playables; using UnityEngine.Timeline; [GameObjectBean(ENameType.GameObjectName)] public class StackerDevice : AbstractDevice { [SerializeField] private StackerData stackerData; private DeviceData deviceData; [Header("前叉货叉光电描述")] [SerializeField] private string frontHasGoodsDesc; [Autowired] private LocationStorageManage storageManage; [Autowired] private ContainersManage containersManage; [Autowired] private StackerPopUI stackerPopUI; [SerializeField] private List forkActionBinding; /// /// 深浅伸叉动作映射 /// [Header("深浅伸叉动作映射")] [SerializeField] private ForkActionMap forkActionMap; /// /// 上一次动作 /// [SerializeField] private StackerForkActionEnum lastforkActionEnum; /// /// 堆垛机移动的方向 /// [SerializeField] private Direction moveDirection = Direction.X; [SerializeField] private GameObject forkCargo; [SerializeField] private GameObject fork; [SerializeField] private Vector3 forkCargoOffset = Vector3.zero; [Header("行走巷道内载货台高度偏移")] [SerializeField] private float isStorageforkCargoYOffset = 0.8f; [Header("站台列载货台高度偏移")] [SerializeField] private float isDeviceforkCargoYOffset = -0.15f; [SerializeField] private float speed = 0.3f; [SerializeField] private float forkCargoUpOrDownSpeed = 0.3f; [Header("载货台起升下降距离")] [SerializeField] private float LiftingAndLoweringDistance = 0.3f; public override Transform Transform => forkCargo.transform; public override string DeviceType => "STACKER"; public override string DeviceCode => name; public override DeviceData DeviceData => deviceData; private bool allowUpOrDown; private float upOrDownSpeed; private bool isOpenPop; public enum Direction { Z,X } private PlayableDirector playableDirector; /// /// 所有动画轨道 /// private List trackAssets = new(); private void Start() { playableDirector = transform.GetComponent(); if(storageManage == null) { storageManage = ApplicationBoot.Instance.GetBean(); } playableDirector.Stop(); StopActionClip(); var graph = playableDirector.playableAsset as TimelineAsset; if (graph == null) { throw new Exception(name + ":未找到 TimelineAsset 资源"); } trackAssets = new List(graph.GetRootTracks().ToArray()); } private void Update() { //堆垛机本体移动 StackerBodyMove(); //载货台移动 ForkCargoVerticalMove(); //伸叉后起升或下降一段距离 ForkCargoUpOrDown(); //货叉动作执行 ExtendForkActionHandle(); } private void ForkCargoUpOrDown() { if (!allowUpOrDown) { return; } forkCargo.transform.Translate(Vector3.up * upOrDownSpeed * forkCargoUpOrDownSpeed * Time.deltaTime); } /// /// 堆垛机主体移动 /// private void StackerBodyMove() { //排列成层 有其一值为0则不处理 if(stackerData.frontRow == 0 || stackerData.special1 == 0 || stackerData.frontLayer == 0) { return; } //获取库位坐标 string row = stackerData.frontRow.ToString("D2"); string column = stackerData.special1.ToString("D2"); string layer = stackerData.frontLayer.ToString("D2"); //当取货或方法是站台列,直接目的地就是站台列 if(stackerData.toColumn >= 200) { column = stackerData.toColumn.ToString(); layer = stackerData.toLayer.ToString("D2"); } string storageName = string.Format("{0}-{1}-{2}", row, column, layer); GameObject storageObject = storageManage.GetStorageByName(storageName); if (storageObject == null) { // throw new System.Exception(string.Format("【{1}】没有找到库位【{0}】坐标",storageName,name)); Debug.LogWarning(string.Format("【{1}】没有找到库位【{0}】坐标",storageName,name)); return; } //解析堆垛机可用坐标 Vector3 target = Vector3.zero; if (moveDirection == Direction.X) { target = new Vector3(storageObject.transform.position.x + forkCargoOffset.x,transform.position.y,transform.position.z); } if (moveDirection == Direction.Z) { target = new Vector3(transform.position.x, transform.position.y, storageObject.transform.position.z+ forkCargoOffset.z); } if(target == Vector3.zero) { return; } //往目标点移动 transform.position = Vector3.MoveTowards(transform.position,target, speed * Time.deltaTime); } /// /// 载货台垂直移动 /// private void ForkCargoVerticalMove() { //货叉动作是静止的才允许提升 if(StackerForkActionEnum.ForkRest == stackerData.frontForkAction ) { //排列层 有其一值为0则不处理 //if (stackerData.frontRow == 0 || stackerData.special1 == 0 || stackerData.frontLayer == 0) //{ // return; //} if (stackerData.toRow == 0 || stackerData.toColumn == 0 || stackerData.toLayer == 0) { return; } Vector3 target = ForkCargoTarget(); //往目标点移动 forkCargo.transform.position = Vector3.MoveTowards(forkCargo.transform.position, target, speed * Time.deltaTime); } } private Vector3 ForkCargoTarget() { //获取库位坐标 string row = stackerData.toRow.ToString("D2"); string column = stackerData.toColumn.ToString("D2"); string layer = stackerData.toLayer.ToString("D2"); //当取货或方法是站台列,直接目的地就是站台列 //if (stackerData.toColumn >= 200) //{ // column = stackerData.toColumn.ToString(); // layer = stackerData.toLayer.ToString("D2"); //} string storageName = string.Format("{0}-{1}-{2}", row, column, layer); GameObject storageObject = storageManage.GetStorageByName(storageName); if (storageObject == null) { throw new System.Exception(string.Format("【{1}】没有找到库位【{0}】坐标", storageName, name)); } //这里因为取货或者放货要保持抬起跟下降动作,增加额外值 float targetY = stackerData.frontForkHasGoods == true ? forkCargoOffset.y : 0; float forkCargoBodyY = int.Parse(column) >= 200 ? isDeviceforkCargoYOffset : isStorageforkCargoYOffset; //解析货叉可用坐标 Vector3 target = new Vector3(forkCargo.transform.position.x, storageObject.transform.position.y + forkCargoBodyY + targetY, forkCargo.transform.position.z); return target; } /// /// 货叉伸叉动作处理 /// private void ExtendForkActionHandle() { //向左伸叉 if(stackerData.frontForkAction == StackerForkActionEnum.LeftExtendFork) { if(lastforkActionEnum != StackerForkActionEnum.LeftExtendFork) { //播放动画 PlayTrackGroup(GetForkActionName(StackerForkActionEnum.LeftExtendFork)); lastforkActionEnum = StackerForkActionEnum.LeftExtendFork; } } //向右伸叉 if (stackerData.frontForkAction == StackerForkActionEnum.RightExtendFork) { if (lastforkActionEnum != StackerForkActionEnum.RightExtendFork) { //播放动画 string name = GetForkActionName(StackerForkActionEnum.RightExtendFork); PlayTrackGroup(GetForkActionName(StackerForkActionEnum.RightExtendFork)); lastforkActionEnum = StackerForkActionEnum.RightExtendFork; } } //向右缩叉 if (stackerData.frontForkAction == StackerForkActionEnum.RightRetractFork) { if (lastforkActionEnum != StackerForkActionEnum.RightRetractFork) { //缩动画播放之前,绑定容器 CheckAndBindContainer(); //继续播放 string name = GetForkActionName(StackerForkActionEnum.RightRetractFork); PlayTrackGroup(name,false); lastforkActionEnum = StackerForkActionEnum.RightRetractFork; allowUpOrDown = true; } } //向左缩叉 if (stackerData.frontForkAction == StackerForkActionEnum.LeftRetractFork) { if (lastforkActionEnum != StackerForkActionEnum.LeftRetractFork) { CheckAndBindContainer(); //继续播放 PlayTrackGroup(GetForkActionName(StackerForkActionEnum.LeftRetractFork), false); lastforkActionEnum = StackerForkActionEnum.LeftRetractFork; allowUpOrDown = true; } } //清空货叉状态 if (stackerData.frontForkAction == StackerForkActionEnum.ForkRest) { if(lastforkActionEnum != StackerForkActionEnum.ForkRest) { lastforkActionEnum = StackerForkActionEnum.ForkRest; //StopActionClip(); } } } /// /// 货叉缩叉动作完成处理(缩叉完成后自动触发) /// public void RetractForkActionFinishHandle() { //清空状态 //StopActionClip(); Debug.LogWarning("清理货叉状态"); } /// /// 货叉缩叉动作处理(当伸叉到位后会自动触发) /// public void ForkActionStopHandle(float speed) { //劫持动画,让其暂停 upOrDownSpeed = stackerData.frontForkHasGoods ? -speed*2 : speed; playableDirector.Pause(); } public void ForkActionStartHandle() { //开始动画 allowUpOrDown = false; //针对放货 if (stackerData.frontForkHasGoods) { ForkBindContainer(); } } public void StopActionClip() { playableDirector.time = 0; playableDirector.Stop(); foreach (var track in trackAssets) { track.muted = true; } } /// /// 绑定容器到货叉 /// private void CheckAndBindContainer() { if (!allowUpOrDown && stackerData.frontForkHasGoods) { return; } ForkBindContainer(); } private void ForkBindContainer() { //直接获取容器 Container container = containersManage.GetContainerByCode(stackerData.frontContainerCode); Debug.Log(string.Format("容器号 【{0},场景实例 【{1}】】", stackerData.frontContainerCode, container)); if (container != null) { if (stackerData.frontForkHasGoods == true) { //绑定库位 string storageName = string.Format("{0}-{1}-{2}", stackerData.toRow.ToString("D2"), stackerData.toColumn.ToString("D2"), stackerData.toLayer.ToString("D2")); GameObject storage = storageManage.GetStorageByName(storageName); if (storage == null) { throw new Exception(string.Format("设备【{1}】未找到库位【{0}】",storageName,name)); } if(stackerData.toColumn >= 200) { //暂停代理 container.Agent.isStopped = false; //启用 container.Agent.enabled = true; } container.transform.parent = storage.transform; } else { //暂停代理 container.Agent.isStopped = true; //清空身上代理路径 container.Agent.ResetPath(); //关闭唤醒状态 container.isAwke = false; container.Agent.enabled = false; //绑定到货叉 container.transform.parent = fork.transform; Debug.LogWarning(name + ":绑定容器 =" + container); } } } private string GetForkActionName(StackerForkActionEnum forkActionEnum) { string row = stackerData.toRow.ToString("D2"); //左伸 if (StackerForkActionEnum.LeftExtendFork == forkActionEnum || StackerForkActionEnum.RightRetractFork == forkActionEnum) { //左浅 if (string.Equals(forkActionMap.leftShaollow, row)) { return stackerData.frontForkHasGoods == true ? ForkActionNameConstant.leftPutShaollw : ForkActionNameConstant.leftTakeShaollw; } //左深 if (string.Equals(forkActionMap.leftDeeps, row)) { return stackerData.frontForkHasGoods == true ? ForkActionNameConstant.leftPutDeep : ForkActionNameConstant.leftTakeDeep; } } //右伸 if (StackerForkActionEnum.RightExtendFork == forkActionEnum || StackerForkActionEnum.LeftRetractFork == forkActionEnum) { //右浅 if (string.Equals(forkActionMap.rightShaollow, row)) { return stackerData.frontForkHasGoods == true ? ForkActionNameConstant.RightPutShaollw : ForkActionNameConstant.RightTakeShaollw; } //右深 if (string.Equals(forkActionMap.rightDeeps, row)) { return stackerData.frontForkHasGoods == true ? ForkActionNameConstant.RightPutDeep : ForkActionNameConstant.RightTakeDeep; } } throw new Exception(name + ":未配置伸叉映射"); } public override void MouseSingleClick() { base.MouseSingleClick(); isOpenPop = !isOpenPop; stackerPopUI.Open(this, isOpenPop); } /// /// 获取要播放动画名称 /// /// /// private string GetActionAnimationName(StackerForkActionEnum actionEnum) { return forkActionBinding.Find(v => v.forkActionEnum == actionEnum).actionClipName; } public override void DataHandle(object data, object other = null) { //接收数据 stackerData = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(data)); frontHasGoodsDesc = stackerData.frontForkHasGoods == true ? "前叉有货" : "无货"; deviceData = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(other)); } public override void ProcessCompleted() { } [System.Serializable] public class StackerForkActionBinding { public StackerForkActionEnum forkActionEnum; public string actionClipName; } /// /// 播放分组 /// private void PlayTrackGroup(string groupName, bool isRest = true) { var graph = playableDirector.playableAsset as TimelineAsset; if (graph == null) { throw new Exception(name+":未找到 TimelineAsset 资源"); } if (isRest) { List others = new List(); foreach (var track in trackAssets) { if (string.Equals(track.name, groupName)) { track.muted = false; foreach (var clip in track.GetClips()) { } } else { others.Add(track); } } //关闭其他动画 others.ForEach(v => v.muted = true); playableDirector.Stop(); } playableDirector.Play(); } public override T DynamicData() { return (T)(object)stackerData; } [System.Serializable] public class ForkActionMap { public string leftShaollow; public string leftDeeps; public string rightShaollow; public string rightDeeps; } /// /// 堆垛机动作动画名称常量 /// private class ForkActionNameConstant { /// /// 左浅取货 /// public static string leftTakeShaollw = "Lef_take_shallow"; /// /// 左深取货 /// public static string leftTakeDeep = "Lef_take_deep"; /// /// 左浅放货 /// public static string leftPutShaollw = "Left_put_shallow"; /// /// 左深放货 /// public static string leftPutDeep = "Left_put_deep"; /// /// 右浅取货 /// public static string RightTakeShaollw = "Right_take_shallow"; /// /// 右深取货 /// public static string RightTakeDeep = "Right_take_deep"; /// /// 右浅放货 /// public static string RightPutShaollw = "Right_put_shallow"; /// /// 右深放货 /// public static string RightPutDeep = "Right_put_deep"; public static string ForkCargoUp = "载货台上升"; public static string ForkCargoDown = "载货台下降"; } }