556 lines
18 KiB
C#
556 lines
18 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using UnityEngine;
|
|
using XRLib;
|
|
|
|
namespace XED
|
|
{
|
|
public class StackerCrane : MonoBehaviour, ISingle
|
|
{
|
|
public Transform VerticalPillar;
|
|
public Transform Bucket;
|
|
public Transform Fork;
|
|
|
|
private float forkRange = 1.5f;
|
|
|
|
public Queue<StackerCraneTask> taskQueue = new Queue<StackerCraneTask>();
|
|
public StackerCraneTask currentTask = new StackerCraneTask(ETaskType.None);
|
|
public StackerCraneTask previousTask = new StackerCraneTask(ETaskType.None);
|
|
|
|
[Label(typeof(Cell), "InComingPos")]
|
|
public Cell incomeCell;
|
|
[Label(typeof(Cell), "OriginPos")]
|
|
public Cell originPos;
|
|
[Label(typeof(Cell), "OutgoingPos")]
|
|
public Cell outCell;
|
|
|
|
public Cell currentTargetCell;
|
|
|
|
public Transform Storage;
|
|
public Transform ForkLoadPlaceRoot;
|
|
|
|
private Vector3 VerticalPillarLerpStartPos;
|
|
private Vector3 BucketLerpStartPos;
|
|
private Vector3 ForkLerpStartPos;
|
|
|
|
private float verticalpillarT;
|
|
private float bucketT;
|
|
private float forkT;
|
|
|
|
private float verticalpollarTargetDistance;
|
|
private float bucketTargetDistance;
|
|
private float forkTargetDistance;
|
|
|
|
public event Action<StackerCrane> onAddTaskCompletedEvent;
|
|
public event Action<StackerCrane> onTaskReadyCompletedEvent;
|
|
public event Action<StackerCrane> onLoadCompletedEvent;
|
|
public event Action<StackerCrane> onTaskEndCompletedEvent;
|
|
public event Action<StackerCrane> onStateChangeEvent;
|
|
|
|
private List<Cell> cellList = new List<Cell>();
|
|
|
|
private float moveSpeed = 6f;
|
|
|
|
private float stackerCraneMove;
|
|
private bool IsBodyMoveCompleted => verticalpillarT >= 1f && bucketT >= 1f;
|
|
private bool IsForkMoveCompleted => forkT >= 1f;
|
|
|
|
public enum EStackeCraneState
|
|
{
|
|
Idle,
|
|
TaskReady,
|
|
Move,
|
|
MoveEnd,
|
|
Load,
|
|
LoadEnd,
|
|
UnLoad,
|
|
UnLoadEnd,
|
|
TaskEnd,
|
|
}
|
|
|
|
public EStackeCraneState _stackeCraneState = EStackeCraneState.Idle;
|
|
|
|
public EStackeCraneState stackeCraneState
|
|
{
|
|
get { return _stackeCraneState; }
|
|
set
|
|
{
|
|
_stackeCraneState = value;
|
|
onStateChangeEvent?.Invoke(this);
|
|
ChangeStateAction();
|
|
}
|
|
}
|
|
|
|
public override void AfterAwake()
|
|
{
|
|
FindSingle<StackerCraneManager>().InitStackerCrane(this);
|
|
|
|
InitStackerCell();
|
|
InitStackerCrane();
|
|
}
|
|
|
|
private void InitStackerCell()
|
|
{
|
|
for (int i = 0; i < Storage.childCount; i++)
|
|
{
|
|
Transform cells = Storage.GetChild(i);
|
|
|
|
for (int j = 0; j < cells.childCount; j++)
|
|
{
|
|
Cell cell = cells.GetChild(j).GetComponent<Cell>();
|
|
cellList.Add(cell);
|
|
}
|
|
}
|
|
|
|
cellList = cellList
|
|
.OrderBy(cell => Vector3.Distance(cell.transform.position,outCell.transform.position))
|
|
.ToList();
|
|
}
|
|
|
|
public void AddTask(StackerCraneTask task)
|
|
{
|
|
taskQueue.Enqueue(task);
|
|
onAddTaskCompletedEvent?.Invoke(this);
|
|
|
|
if (stackeCraneState == EStackeCraneState.Idle)
|
|
ChangeStateAction();
|
|
}
|
|
|
|
private void ChangeStateAction()
|
|
{
|
|
switch (stackeCraneState)
|
|
{
|
|
case EStackeCraneState.Idle:
|
|
Idle();
|
|
break;
|
|
case EStackeCraneState.TaskReady:
|
|
TaskReady();
|
|
break;
|
|
case EStackeCraneState.Move:
|
|
StartCoroutine(Move());
|
|
break;
|
|
case EStackeCraneState.MoveEnd:
|
|
MoveEnd();
|
|
break;
|
|
case EStackeCraneState.Load:
|
|
StartCoroutine(Load());
|
|
break;
|
|
case EStackeCraneState.LoadEnd:
|
|
LoadEnd();
|
|
break;
|
|
case EStackeCraneState.UnLoad:
|
|
StartCoroutine(UnLoad());
|
|
break;
|
|
case EStackeCraneState.UnLoadEnd:
|
|
UnLoadEnd();
|
|
break;
|
|
case EStackeCraneState.TaskEnd:
|
|
TaskEnd();
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void Idle()
|
|
{
|
|
if (taskQueue.Count > 0)
|
|
{
|
|
stackeCraneState = EStackeCraneState.TaskReady;
|
|
}
|
|
}
|
|
|
|
private void TaskReady()
|
|
{
|
|
if (taskQueue.Count == 0)
|
|
{
|
|
stackeCraneState = EStackeCraneState.Idle;
|
|
return;
|
|
}
|
|
|
|
if(taskQueue.Peek().taskType == ETaskType.Incoming)
|
|
{
|
|
if (GetInComeCellLoadCount() == 0)
|
|
{
|
|
Debug.LogWarning("시작 지점에 Load가 없습니다");
|
|
taskQueue.Dequeue();
|
|
currentTargetCell = originPos;
|
|
stackeCraneState = EStackeCraneState.Move;
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (GetLoadCellCount() == 0)
|
|
{
|
|
Debug.LogWarning("stacker에 로드된 cell이 없습니다.");
|
|
taskQueue.Dequeue();
|
|
currentTargetCell = originPos;
|
|
stackeCraneState = EStackeCraneState.Move;
|
|
return;
|
|
}
|
|
}
|
|
//PredictWorkEndTime(taskQueue.Count);
|
|
|
|
previousTask = currentTask;
|
|
currentTask = taskQueue.Dequeue();
|
|
|
|
stackerCellIndex = -1;
|
|
InitStackerCrane();
|
|
|
|
switch (currentTask.taskType)
|
|
{
|
|
case ETaskType.Incoming:
|
|
currentTargetCell = incomeCell;
|
|
break;
|
|
case ETaskType.Outgoing:
|
|
currentTargetCell = GetFullCell();
|
|
break;
|
|
}
|
|
|
|
onTaskReadyCompletedEvent?.Invoke(this);
|
|
stackeCraneState = EStackeCraneState.Move;
|
|
}
|
|
|
|
private IEnumerator Move()
|
|
{
|
|
while (!IsBodyMoveCompleted)
|
|
{
|
|
stackerCraneMove = Time.deltaTime * moveSpeed;
|
|
MoveVerticalpollarToTarget();
|
|
MoveBucketToTarget();
|
|
|
|
yield return null;
|
|
}
|
|
stackeCraneState = EStackeCraneState.MoveEnd;
|
|
}
|
|
|
|
private void MoveEnd()
|
|
{
|
|
InitStackerCrane();
|
|
|
|
//TODO:: 위치 선정방식 변경
|
|
if (currentTargetCell == originPos)
|
|
{
|
|
elapsedDistance = 0f;
|
|
currentTask = new StackerCraneTask(ETaskType.None);
|
|
onTaskEndCompletedEvent?.Invoke(this);
|
|
stackeCraneState = EStackeCraneState.TaskReady;
|
|
}
|
|
else if (currentTargetCell == outCell)
|
|
{
|
|
stackeCraneState = EStackeCraneState.UnLoad;
|
|
}
|
|
else if(currentTargetCell == incomeCell)
|
|
{
|
|
stackeCraneState = EStackeCraneState.Load;
|
|
}
|
|
else if(currentTargetCell == GetEmptyCell())
|
|
{
|
|
if (currentTask.taskType == ETaskType.Incoming)
|
|
stackeCraneState = EStackeCraneState.UnLoad;
|
|
}
|
|
else// if(currentTargetCell == GetFullCell())
|
|
{
|
|
stackeCraneState = EStackeCraneState.Load;
|
|
}
|
|
}
|
|
|
|
private IEnumerator Load()
|
|
{
|
|
while (!IsForkMoveCompleted)
|
|
{
|
|
stackerCraneMove = Time.deltaTime * moveSpeed;
|
|
MoveForkToTarget();
|
|
|
|
yield return null;
|
|
}
|
|
|
|
currentTargetCell.load.transform.parent = ForkLoadPlaceRoot;
|
|
currentTargetCell.load.transform.localPosition = Vector3.zero;
|
|
currentTargetCell.isLoad = false;
|
|
currentTargetCell.load = null;
|
|
|
|
onLoadCompletedEvent.Invoke(this);
|
|
|
|
if (currentTask.taskType == ETaskType.Outgoing)
|
|
stackerCellIndex = cellList.IndexOf(currentTargetCell);
|
|
|
|
InitStackerCrane();
|
|
|
|
while (!IsForkMoveCompleted)
|
|
{
|
|
stackerCraneMove = Time.deltaTime * moveSpeed;
|
|
MoveForkToOrigin();
|
|
|
|
yield return null;
|
|
}
|
|
|
|
InitStackerCrane();
|
|
stackeCraneState = EStackeCraneState.LoadEnd;
|
|
}
|
|
|
|
private void LoadEnd()
|
|
{
|
|
if (currentTask.taskType == ETaskType.Incoming)
|
|
currentTargetCell = GetEmptyCell();
|
|
else
|
|
currentTargetCell = outCell;
|
|
|
|
InitStackerCrane();
|
|
stackeCraneState = EStackeCraneState.Move;
|
|
}
|
|
|
|
private IEnumerator UnLoad()
|
|
{
|
|
while (!IsForkMoveCompleted)
|
|
{
|
|
MoveForkToTarget();
|
|
|
|
yield return null;
|
|
}
|
|
|
|
GameObject load = ForkLoadPlaceRoot.GetChild(0).gameObject;
|
|
load.transform.parent = currentTargetCell.transform;
|
|
load.transform.localPosition = Vector3.zero;
|
|
currentTargetCell.isLoad = true;
|
|
currentTargetCell.load = load;
|
|
|
|
if (currentTask.taskType == ETaskType.Incoming)
|
|
stackerCellIndex = cellList.IndexOf(currentTargetCell);
|
|
|
|
InitStackerCrane();
|
|
|
|
while (!IsForkMoveCompleted)
|
|
{
|
|
MoveForkToOrigin();
|
|
|
|
yield return null;
|
|
}
|
|
|
|
InitStackerCrane();
|
|
stackeCraneState = EStackeCraneState.UnLoadEnd;
|
|
}
|
|
|
|
private void UnLoadEnd()
|
|
{
|
|
stackeCraneState = EStackeCraneState.TaskEnd;
|
|
}
|
|
|
|
private void TaskEnd()
|
|
{
|
|
if (taskQueue.Count == 0)
|
|
{
|
|
//goto home
|
|
InitStackerCrane();
|
|
currentTargetCell = originPos;
|
|
stackeCraneState = EStackeCraneState.Move;
|
|
}
|
|
else
|
|
{
|
|
elapsedDistance = 0f;
|
|
currentTask = new StackerCraneTask(ETaskType.None);
|
|
onTaskEndCompletedEvent?.Invoke(this);
|
|
stackeCraneState = EStackeCraneState.TaskReady;
|
|
}
|
|
}
|
|
|
|
public int GetInComeCellLoadCount()
|
|
{
|
|
return incomeCell.transform.childCount;
|
|
}
|
|
|
|
public int GetLoadCellCount()
|
|
{
|
|
return cellList.Count(cell => cell.isLoad);
|
|
}
|
|
|
|
Cell GetEmptyCell()
|
|
{
|
|
var emptyCells = cellList.Where(cell => !cell.isLoad);
|
|
return emptyCells.FirstOrDefault();
|
|
}
|
|
|
|
Cell GetFullCell()
|
|
{
|
|
var fullCells = cellList.Where(cell => cell.isLoad);
|
|
return fullCells.FirstOrDefault();
|
|
}
|
|
|
|
private void InitStackerCrane()
|
|
{
|
|
verticalpillarT = 0f;
|
|
bucketT = 0f;
|
|
forkT = 0f;
|
|
|
|
VerticalPillarLerpStartPos = VerticalPillar.position;
|
|
BucketLerpStartPos = Bucket.position;
|
|
ForkLerpStartPos = Fork.position;
|
|
}
|
|
|
|
private void MoveVerticalpollarToTarget()
|
|
{
|
|
verticalpollarTargetDistance = Mathf.Abs(VerticalPillarLerpStartPos.x - currentTargetCell.transform.position.x);
|
|
verticalpillarT += stackerCraneMove / verticalpollarTargetDistance;
|
|
Vector3 verticalPillarPos = VerticalPillar.position;
|
|
verticalPillarPos.x = Mathf.Lerp(VerticalPillarLerpStartPos.x, currentTargetCell.transform.position.x, verticalpillarT);
|
|
VerticalPillar.position = verticalPillarPos;
|
|
if(verticalpollarTargetDistance >= bucketTargetDistance)
|
|
elapsedDistance += stackerCraneMove;
|
|
}
|
|
|
|
private void MoveBucketToTarget()
|
|
{
|
|
bucketTargetDistance = Mathf.Abs(BucketLerpStartPos.y - currentTargetCell.transform.position.y);
|
|
bucketT += stackerCraneMove / bucketTargetDistance;
|
|
Vector3 bucketPos = Bucket.position;
|
|
bucketPos.y = Mathf.Lerp(BucketLerpStartPos.y, currentTargetCell.transform.position.y, bucketT);
|
|
Bucket.position = bucketPos;
|
|
if (bucketTargetDistance >= verticalpollarTargetDistance)
|
|
elapsedDistance += stackerCraneMove;
|
|
}
|
|
|
|
private void MoveForkToTarget()
|
|
{
|
|
forkT += stackerCraneMove / forkRange;
|
|
Vector3 forkPos = Fork.position;
|
|
forkPos.z = Mathf.Lerp(ForkLerpStartPos.z, currentTargetCell.transform.position.z >= 0 ? forkRange : -forkRange, forkT);
|
|
Fork.position = forkPos;
|
|
elapsedDistance += stackerCraneMove;
|
|
}
|
|
|
|
private void MoveForkToOrigin()
|
|
{
|
|
forkT += stackerCraneMove / forkRange;
|
|
Vector3 forkPos = Fork.position;
|
|
forkPos.z = Mathf.Lerp(ForkLerpStartPos.z, 0f, forkT);
|
|
Fork.position = forkPos;
|
|
elapsedDistance += stackerCraneMove;
|
|
}
|
|
|
|
private float totalDistance = 0f;
|
|
private float elapsedDistance = 0f;
|
|
int stackerCellIndex = -1;
|
|
List<Cell> modifiedCellList = new List<Cell>();
|
|
Cell targetStackerCell = null;
|
|
|
|
public void PredictWorkEndTime(StackerCraneTask targetTask)
|
|
{
|
|
if (!IsTaskValid(targetTask))
|
|
{
|
|
Debug.LogWarning("잘못된 task가 포함되어 있습니다.");
|
|
return;
|
|
}
|
|
|
|
StackerCraneTask tempPreviousTask = previousTask;
|
|
StackerCraneTask tempCurrentTask = currentTask;
|
|
|
|
totalDistance = 0;
|
|
|
|
ProcessTask(tempPreviousTask, tempCurrentTask);
|
|
tempPreviousTask = tempCurrentTask;
|
|
|
|
foreach (StackerCraneTask task in taskQueue)
|
|
{
|
|
tempCurrentTask = task;
|
|
ProcessTask(tempPreviousTask, tempCurrentTask);
|
|
tempPreviousTask = tempCurrentTask;
|
|
|
|
if (targetTask == tempCurrentTask)
|
|
{
|
|
print("소요예정시간 " + (totalDistance - elapsedDistance) / moveSpeed);
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < modifiedCellList.Count; i++)
|
|
{
|
|
modifiedCellList[i].isLoad = !modifiedCellList[i].isLoad;
|
|
}
|
|
modifiedCellList.Clear();
|
|
}
|
|
|
|
private bool IsTaskValid(StackerCraneTask targetTask)
|
|
{
|
|
int outgoingTaskCount = 0;
|
|
bool isLoadOrUnloadComplete = stackerCellIndex != -1;
|
|
int loadCountAdjustment = currentTask.taskType == ETaskType.Incoming ? 1 : -1;
|
|
|
|
foreach (StackerCraneTask task in taskQueue)
|
|
{
|
|
outgoingTaskCount += task.taskType == ETaskType.Outgoing ? 1 : -1;
|
|
|
|
int totalLoadCount = GetLoadCellCount() + (isLoadOrUnloadComplete ? 0 : loadCountAdjustment);
|
|
|
|
if (totalLoadCount < outgoingTaskCount)
|
|
{
|
|
print("소요예정시간 " + float.MaxValue);
|
|
return false;
|
|
}
|
|
|
|
if (task == targetTask)
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private void ProcessTask(StackerCraneTask previousTask, StackerCraneTask currentTask)
|
|
{
|
|
List<(Cell, Cell)> pathList = new List<(Cell, Cell)>();
|
|
Cell previousEndPos = originPos;
|
|
|
|
switch (previousTask.taskType)
|
|
{
|
|
case ETaskType.Incoming:
|
|
previousEndPos = stackerCellIndex != -1 ? cellList[stackerCellIndex] : targetStackerCell;
|
|
stackerCellIndex = -1;
|
|
break;
|
|
case ETaskType.Outgoing:
|
|
previousEndPos = outCell;
|
|
break;
|
|
}
|
|
|
|
switch (currentTask.taskType)
|
|
{
|
|
case ETaskType.Incoming:
|
|
targetStackerCell = GetEmptyCell();
|
|
pathList = new List<(Cell, Cell)>
|
|
{
|
|
(previousEndPos, incomeCell),
|
|
(incomeCell, targetStackerCell)
|
|
};
|
|
targetStackerCell.isLoad = true;
|
|
break;
|
|
case ETaskType.Outgoing:
|
|
targetStackerCell = GetFullCell();
|
|
pathList = new List<(Cell, Cell)>
|
|
{
|
|
(previousEndPos, targetStackerCell),
|
|
(targetStackerCell, outCell)
|
|
};
|
|
targetStackerCell.isLoad = false;
|
|
break;
|
|
}
|
|
modifiedCellList.Add(targetStackerCell);
|
|
totalDistance += CalculatePathDistance(pathList) + forkRange * 4;
|
|
}
|
|
|
|
private float CalculatePathDistance(List<(Cell, Cell)> pathList)
|
|
{
|
|
float distance = 0f;
|
|
foreach (var path in pathList)
|
|
{
|
|
distance += CalculateDistance(path);
|
|
}
|
|
return distance;
|
|
}
|
|
|
|
private float CalculateDistance((Cell, Cell) value)
|
|
{
|
|
float xDistance = Mathf.Abs(value.Item1.transform.position.x - value.Item2.transform.position.x);
|
|
float yDistance = Mathf.Abs(value.Item1.transform.position.y - value.Item2.transform.position.y);
|
|
return Mathf.Max(xDistance, yDistance);
|
|
}
|
|
}
|
|
}
|