2025-11-04 11:02:02 +09:00
|
|
|
using Cysharp.Threading.Tasks;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using TMPro;
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
using UnityEngine.SocialPlatforms;
|
|
|
|
|
using UVC.Core;
|
|
|
|
|
using UVC.Data;
|
|
|
|
|
using UVC.Data.Core;
|
|
|
|
|
using UVC.Data.Mqtt;
|
2025-12-08 10:59:29 +09:00
|
|
|
using Simulator.Config;
|
2025-11-04 11:02:02 +09:00
|
|
|
using UVC.Pool;
|
|
|
|
|
|
|
|
|
|
namespace Simulator.Data.Transport
|
|
|
|
|
{
|
|
|
|
|
public enum AGVDataType
|
|
|
|
|
{
|
|
|
|
|
Moving,
|
2026-02-03 11:40:26 +09:00
|
|
|
LoadingStart,
|
|
|
|
|
LoadingEnd,
|
|
|
|
|
UnLoadingStart,
|
|
|
|
|
UnLoadingEnd,
|
|
|
|
|
Idle
|
2025-11-04 11:02:02 +09:00
|
|
|
}
|
|
|
|
|
public class AGVManager : SingletonScene<AGVManager>
|
|
|
|
|
{
|
|
|
|
|
private readonly Dictionary<string, string> prefabPaths = new Dictionary<string, string>()
|
|
|
|
|
{
|
|
|
|
|
{"agv","prefabs/agv"},
|
2025-12-08 10:59:29 +09:00
|
|
|
{"afl","prefabs/afl"},
|
|
|
|
|
{"forkLift","prefabs/forkLift"}
|
2025-11-04 11:02:02 +09:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
private GameObjectPool<AGV>? agvPool;
|
|
|
|
|
public GameObjectPool<AGV> AGVPool
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
if (agvPool == null)
|
|
|
|
|
{
|
|
|
|
|
//Debug.LogError("Pool is not initialized. Please call InitializePoolAsync first.");
|
|
|
|
|
}
|
|
|
|
|
return agvPool!;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-12-08 10:59:29 +09:00
|
|
|
private GameObjectPool<AGV>? aflPool;
|
|
|
|
|
public GameObjectPool<AGV> AFLPool
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
if (aflPool == null)
|
|
|
|
|
{
|
|
|
|
|
//Debug.LogError("Pool is not initialized. Please call InitializePoolAsync first.");
|
|
|
|
|
}
|
|
|
|
|
return aflPool!;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
private GameObjectPool<AGV>? forkLiftPool;
|
|
|
|
|
public GameObjectPool<AGV> ForkLiftPool
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
if (forkLiftPool == null)
|
|
|
|
|
{
|
|
|
|
|
//Debug.LogError("Pool is not initialized. Please call InitializePoolAsync first.");
|
|
|
|
|
}
|
|
|
|
|
return forkLiftPool!;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-11-04 11:02:02 +09:00
|
|
|
private DataMapper agvMoveMapper;
|
|
|
|
|
private DataMapper agvLoadMapper;
|
|
|
|
|
private DataMapper agvUnLoadMapper;
|
2025-12-08 10:59:29 +09:00
|
|
|
private Dictionary<string, AGV> agvMap = new Dictionary<string, AGV>();
|
2025-11-04 11:02:02 +09:00
|
|
|
|
|
|
|
|
protected override void Init()
|
|
|
|
|
{
|
|
|
|
|
InitializeAGVPoolAsync().ContinueWith(() =>
|
|
|
|
|
{
|
|
|
|
|
var agvMoveDataMask = new DataMask();
|
|
|
|
|
agvMoveDataMask.ObjectName = "agv"; // AGV 객체의 이름을 설정합니다.
|
|
|
|
|
agvMoveDataMask.ObjectIdKey = "component_id"; // AGV의 고유 식별자로 사용할 키를 설정합니다.
|
|
|
|
|
agvMoveDataMask["component_id"] = "";
|
2025-12-08 10:59:29 +09:00
|
|
|
agvMoveDataMask["component_type"] = "";
|
2025-11-04 11:02:02 +09:00
|
|
|
agvMoveDataMask["event_name"] = "";
|
|
|
|
|
agvMoveDataMask["timestamp"] = new DateTime();
|
|
|
|
|
agvMoveDataMask["data"] = new DataMask()
|
|
|
|
|
{
|
|
|
|
|
["from_node"] = "",
|
|
|
|
|
["to_node"] = "",
|
|
|
|
|
["timing"] = new DataMask()
|
|
|
|
|
{
|
|
|
|
|
["duration"] = new DataMask()
|
|
|
|
|
{
|
|
|
|
|
["real_seconds"] = 0.0f
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
agvMoveMapper = new DataMapper(agvMoveDataMask);
|
|
|
|
|
|
|
|
|
|
var agvLoadDataMask = new DataMask();
|
|
|
|
|
agvLoadDataMask.ObjectName = "agv"; // AGV 객체의 이름을 설정합니다.
|
|
|
|
|
agvLoadDataMask.ObjectIdKey = "component_id"; // AGV의 고유 식별자로 사용할 키를 설정합니다.
|
|
|
|
|
agvLoadDataMask["component_id"] = "";
|
2025-12-08 10:59:29 +09:00
|
|
|
agvLoadDataMask["component_type"] = "";
|
2025-11-04 11:02:02 +09:00
|
|
|
agvLoadDataMask["event_name"] = "";
|
|
|
|
|
agvLoadDataMask["timestamp"] = new DateTime();
|
|
|
|
|
agvLoadDataMask["data"] = new DataMask()
|
|
|
|
|
{
|
|
|
|
|
["from_node"] = "",
|
|
|
|
|
["to_node"] = "",
|
2026-02-03 11:40:26 +09:00
|
|
|
["entity_ids"] = new DataMask()
|
|
|
|
|
{
|
|
|
|
|
["entity_id"] = ""
|
|
|
|
|
}
|
2025-11-04 11:02:02 +09:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
agvLoadMapper = new DataMapper(agvLoadDataMask);
|
|
|
|
|
|
|
|
|
|
var agvUnLoadDataMask = new DataMask();
|
|
|
|
|
agvUnLoadDataMask.ObjectName = "agv"; // AGV 객체의 이름을 설정합니다.
|
|
|
|
|
agvUnLoadDataMask.ObjectIdKey = "component_id"; // AGV의 고유 식별자로 사용할 키를 설정합니다.
|
|
|
|
|
agvUnLoadDataMask["component_id"] = "";
|
2025-12-08 10:59:29 +09:00
|
|
|
agvUnLoadDataMask["component_type"] = "";
|
2025-11-04 11:02:02 +09:00
|
|
|
agvUnLoadDataMask["event_name"] = "";
|
|
|
|
|
agvUnLoadDataMask["timestamp"] = new DateTime();
|
|
|
|
|
agvUnLoadDataMask["data"] = new DataMask()
|
|
|
|
|
{
|
2025-12-08 10:59:29 +09:00
|
|
|
["component"] = "",
|
2025-11-04 11:02:02 +09:00
|
|
|
["entity_ids"] = new List<string>(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
agvUnLoadMapper = new DataMapper(agvUnLoadDataMask);
|
|
|
|
|
});
|
2025-12-08 10:59:29 +09:00
|
|
|
InitializeAFLPoolAsync().ContinueWith(() => { });
|
|
|
|
|
InitializeForkLiftPoolAsync().ContinueWith(() => { });
|
2025-11-04 11:02:02 +09:00
|
|
|
}
|
|
|
|
|
|
2025-12-08 10:59:29 +09:00
|
|
|
public void SpawnAGV(List<AGVData> datas, string type)
|
2025-11-04 11:02:02 +09:00
|
|
|
{
|
|
|
|
|
foreach (var data in datas)
|
|
|
|
|
{
|
2025-12-08 10:59:29 +09:00
|
|
|
switch (type)
|
|
|
|
|
{
|
2025-12-11 18:26:09 +09:00
|
|
|
case "agv":
|
2026-02-03 11:40:26 +09:00
|
|
|
if (data.initial_node == null)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
2025-12-08 10:59:29 +09:00
|
|
|
var agv = agvPool.GetItem($"{data.name}");
|
|
|
|
|
agv.data = data;
|
|
|
|
|
agv.UpdatePosition(AGVNodeManager.Instance.GetNodePosition(data.initial_node));
|
|
|
|
|
agvMap.Add(data.name, agv);
|
2026-02-03 11:40:26 +09:00
|
|
|
agv.dataType = AGVDataType.Idle;
|
|
|
|
|
PlayerPropertyDataBase.Instance.CollectSpawnAGV(agv);
|
2025-12-08 10:59:29 +09:00
|
|
|
break;
|
|
|
|
|
case "afl":
|
|
|
|
|
var afl = aflPool.GetItem($"{data.name}");
|
|
|
|
|
afl.data = data;
|
|
|
|
|
afl.UpdatePosition(AGVNodeManager.Instance.GetNodePosition(data.initial_node));
|
|
|
|
|
agvMap.Add(data.name, afl);
|
2026-02-03 11:40:26 +09:00
|
|
|
afl.dataType = AGVDataType.Idle;
|
|
|
|
|
PlayerPropertyDataBase.Instance.CollectSpawnAGV(afl);
|
2025-12-08 10:59:29 +09:00
|
|
|
break;
|
|
|
|
|
case "forklift":
|
|
|
|
|
var forkLift = forkLiftPool.GetItem($"{data.name}");
|
|
|
|
|
forkLift.data = data;
|
|
|
|
|
forkLift.UpdatePosition(AGVNodeManager.Instance.GetNodePosition(data.initial_node));
|
|
|
|
|
agvMap.Add(data.name, forkLift);
|
2026-02-03 11:40:26 +09:00
|
|
|
forkLift.dataType = AGVDataType.Idle;
|
|
|
|
|
PlayerPropertyDataBase.Instance.CollectSpawnAGV(forkLift);
|
2025-12-08 10:59:29 +09:00
|
|
|
break;
|
|
|
|
|
}
|
2025-11-11 09:42:47 +09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void InitAGV(List<AGVData> datas)
|
|
|
|
|
{
|
|
|
|
|
foreach (var data in datas)
|
|
|
|
|
{
|
2025-11-04 11:02:02 +09:00
|
|
|
DataRepository.Instance.MqttReceiver.AddTopic($"simulation/{SimulationConfig.SimulationCode}/components/+/{data.name}/+/moving");
|
|
|
|
|
var mqttConfigAGVMoving = new MqttSubscriptionConfig($"simulation/{SimulationConfig.SimulationCode}/components/+/{data.name}/+/moving");
|
|
|
|
|
mqttConfigAGVMoving.SetDataMapper(agvMoveMapper);
|
|
|
|
|
mqttConfigAGVMoving.SetHandler((value) => OnUpdateData(value, AGVDataType.Moving));
|
|
|
|
|
DataRepository.Instance.MqttReceiver.Add(mqttConfigAGVMoving);
|
|
|
|
|
DataRepository.Instance.MqttReceiver.AddTopic($"simulation/{SimulationConfig.SimulationCode}/components/+/{data.name}/+/loading_end");
|
|
|
|
|
var mqttConfigAGVLoading = new MqttSubscriptionConfig($"simulation/{SimulationConfig.SimulationCode}/components/+/{data.name}/+/loading_end");
|
|
|
|
|
mqttConfigAGVLoading.SetDataMapper(agvLoadMapper);
|
2026-02-03 11:40:26 +09:00
|
|
|
mqttConfigAGVLoading.SetHandler((value) => OnUpdateData(value, AGVDataType.LoadingEnd));
|
|
|
|
|
DataRepository.Instance.MqttReceiver.Add(mqttConfigAGVLoading);
|
|
|
|
|
DataRepository.Instance.MqttReceiver.AddTopic($"simulation/{SimulationConfig.SimulationCode}/components/+/{data.name}/+/loading_start");
|
|
|
|
|
var mqttConfigAGVLoadingStart = new MqttSubscriptionConfig($"simulation/{SimulationConfig.SimulationCode}/components/+/{data.name}/+/loading_start");
|
|
|
|
|
mqttConfigAGVLoadingStart.SetDataMapper(agvLoadMapper);
|
|
|
|
|
mqttConfigAGVLoadingStart.SetHandler((value) => OnUpdateData(value, AGVDataType.LoadingStart));
|
2025-11-04 11:02:02 +09:00
|
|
|
DataRepository.Instance.MqttReceiver.Add(mqttConfigAGVLoading);
|
|
|
|
|
DataRepository.Instance.MqttReceiver.AddTopic($"simulation/{SimulationConfig.SimulationCode}/components/+/{data.name}/+/unloading_end");
|
|
|
|
|
var mqttConfigAGVUnLoading = new MqttSubscriptionConfig($"simulation/{SimulationConfig.SimulationCode}/components/+/{data.name}/+/unloading_end");
|
|
|
|
|
mqttConfigAGVUnLoading.SetDataMapper(agvUnLoadMapper);
|
2026-02-03 11:40:26 +09:00
|
|
|
mqttConfigAGVUnLoading.SetHandler((value) => OnUpdateData(value, AGVDataType.UnLoadingEnd));
|
2025-11-04 11:02:02 +09:00
|
|
|
DataRepository.Instance.MqttReceiver.Add(mqttConfigAGVUnLoading);
|
2026-02-03 11:40:26 +09:00
|
|
|
DataRepository.Instance.MqttReceiver.AddTopic($"simulation/{SimulationConfig.SimulationCode}/components/+/{data.name}/+/unloading_start");
|
|
|
|
|
var mqttConfigAGVUnLoadingStart = new MqttSubscriptionConfig($"simulation/{SimulationConfig.SimulationCode}/components/+/{data.name}/+/unloading_start");
|
|
|
|
|
mqttConfigAGVUnLoadingStart.SetDataMapper(agvUnLoadMapper);
|
|
|
|
|
mqttConfigAGVUnLoadingStart.SetHandler((value) => OnUpdateData(value, AGVDataType.UnLoadingStart));
|
|
|
|
|
DataRepository.Instance.MqttReceiver.Add(mqttConfigAGVUnLoadingStart);
|
2025-11-04 11:02:02 +09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void OnUpdateData(IDataObject data, AGVDataType type)
|
|
|
|
|
{
|
|
|
|
|
if (data == null) return;
|
|
|
|
|
|
|
|
|
|
DataObject? obj = data as DataObject;
|
|
|
|
|
if (obj == null) return;
|
2025-12-08 10:59:29 +09:00
|
|
|
var componentName = obj.GetString("component_id");
|
|
|
|
|
AGV agv = agvMap[componentName];
|
2025-11-04 11:02:02 +09:00
|
|
|
switch (type)
|
|
|
|
|
{
|
|
|
|
|
case AGVDataType.Moving:
|
|
|
|
|
agv.SetTargetPosition(obj);
|
2026-02-03 11:40:26 +09:00
|
|
|
agv.dataType = AGVDataType.Moving;
|
2025-11-04 11:02:02 +09:00
|
|
|
break;
|
2026-02-03 11:40:26 +09:00
|
|
|
case AGVDataType.LoadingEnd:
|
2025-11-04 11:02:02 +09:00
|
|
|
agv.LoadEntity(obj);
|
2026-02-03 11:40:26 +09:00
|
|
|
agv.dataType = AGVDataType.Idle;
|
|
|
|
|
break;
|
|
|
|
|
case AGVDataType.LoadingStart:
|
|
|
|
|
agv.dataType = AGVDataType.LoadingStart;
|
|
|
|
|
break;
|
|
|
|
|
case AGVDataType.UnLoadingEnd:
|
|
|
|
|
agv.dataType = AGVDataType.Idle;
|
2025-11-04 11:02:02 +09:00
|
|
|
break;
|
2026-02-03 11:40:26 +09:00
|
|
|
case AGVDataType.UnLoadingStart:
|
|
|
|
|
agv.dataType = AGVDataType.UnLoadingStart;
|
2025-11-04 11:02:02 +09:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async UniTask InitializeAGVPoolAsync()
|
|
|
|
|
{
|
|
|
|
|
if (agvPool != null) return;
|
|
|
|
|
|
|
|
|
|
var prefab = await Resources.LoadAsync<GameObject>(prefabPaths["agv"]) as GameObject;
|
|
|
|
|
if (prefab == null)
|
|
|
|
|
{
|
|
|
|
|
Debug.LogError($"Prefab not found at path: {prefabPaths["agv"]}");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
agvPool = new GameObjectPool<AGV>(prefab, transform);
|
|
|
|
|
}
|
2025-12-08 10:59:29 +09:00
|
|
|
private async UniTask InitializeAFLPoolAsync()
|
|
|
|
|
{
|
|
|
|
|
if (aflPool != null) return;
|
|
|
|
|
|
|
|
|
|
var prefab = await Resources.LoadAsync<GameObject>(prefabPaths["afl"]) as GameObject;
|
|
|
|
|
if (prefab == null)
|
|
|
|
|
{
|
|
|
|
|
Debug.LogError($"Prefab not found at path: {prefabPaths["afl"]}");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
aflPool = new GameObjectPool<AGV>(prefab, transform);
|
|
|
|
|
}
|
|
|
|
|
private async UniTask InitializeForkLiftPoolAsync()
|
|
|
|
|
{
|
|
|
|
|
if (forkLiftPool != null) return;
|
|
|
|
|
|
|
|
|
|
var prefab = await Resources.LoadAsync<GameObject>(prefabPaths["forkLift"]) as GameObject;
|
|
|
|
|
if (prefab == null)
|
|
|
|
|
{
|
|
|
|
|
Debug.LogError($"Prefab not found at path: {prefabPaths["forkLift"]}");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
forkLiftPool = new GameObjectPool<AGV>(prefab, transform);
|
|
|
|
|
}
|
2025-11-04 11:02:02 +09:00
|
|
|
}
|
|
|
|
|
}
|