Files
Studio/Assets/Scripts/XED/Machine/StackerCrane/StackerCrane.cs
2025-02-21 11:57:09 +09:00

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);
}
}
}