1104
This commit is contained in:
8
Assets/Scripts/Simulator/Components/AGV.meta
Normal file
8
Assets/Scripts/Simulator/Components/AGV.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d41ad1523c22d2b4cb6d5a7963823faf
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
160
Assets/Scripts/Simulator/Components/AGV/AGV.cs
Normal file
160
Assets/Scripts/Simulator/Components/AGV/AGV.cs
Normal file
@@ -0,0 +1,160 @@
|
||||
using Gpm.Ui.Sample;
|
||||
using System.Collections.Generic;
|
||||
using TMPro;
|
||||
using Unity.VisualScripting;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SocialPlatforms;
|
||||
using UVC.Data.Core;
|
||||
|
||||
namespace Simulator.Data.Transport
|
||||
{
|
||||
public class AGV : ComponentBase
|
||||
{
|
||||
private Vector3 targetPosition;
|
||||
private Vector3 startPosition;
|
||||
private Quaternion targetRotation;
|
||||
private float moveSpeed;
|
||||
private float t = 0f;
|
||||
|
||||
public GameObject entitySocket;
|
||||
public List<Entity> possessEntities = new List<Entity>();
|
||||
int index = 0;
|
||||
public AGVData data;
|
||||
bool getdata = false;
|
||||
|
||||
public void UpdatePosition(Vector3 position)
|
||||
{
|
||||
transform.position = position;
|
||||
}
|
||||
|
||||
public void SetTargetPosition(DataObject agvData)
|
||||
{
|
||||
var datas = agvData.GetDataObject("data");
|
||||
var timing = datas.GetDataObject("timing");
|
||||
var duration = timing.GetDataObject("duration");
|
||||
targetPosition = AGVNodeManager.Instance.GetNodePosition(datas.GetString("to_node"));
|
||||
startPosition = AGVNodeManager.Instance.GetNodePosition(datas.GetString("from_node"));
|
||||
moveSpeed = (float)duration.GetFloat("real_seconds");
|
||||
t = 0f;
|
||||
getdata = true;
|
||||
//Debug.Log($"{datas.GetDateTime("timestamp").ToString()}{index}{data.name},from:{datas.GetString("from_node")},to:{datas.GetString("to_node")}");
|
||||
//Debug.Log($"move{agvData.GetString("component_id")}");
|
||||
}
|
||||
private void PlaceNext(Entity entity)
|
||||
{
|
||||
if (entity == null) return;
|
||||
|
||||
if (entitySocket == null)
|
||||
{
|
||||
Debug.LogWarning("[AGV] entitySocket 이 null 입니다. 배치를 건너뜁니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 부모/로컬 트랜스폼 설정
|
||||
entity.transform.SetParent(entitySocket.transform, worldPositionStays: false);
|
||||
|
||||
// Z 방향 적층(기존 로직 유지)
|
||||
float stepZ = EntityManager.Instance.ObjectSize.z;
|
||||
entity.transform.localPosition = new Vector3(0f, 0f, stepZ * index);
|
||||
Debug.Log(stepZ);
|
||||
Debug.Log(index);
|
||||
entity.transform.localRotation = Quaternion.identity;
|
||||
|
||||
// 중복 방지 후 등록
|
||||
if (!possessEntities.Contains(entity))
|
||||
possessEntities.Add(entity);
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
public void LoadEntity(DataObject agvData)
|
||||
{
|
||||
var datas = agvData.GetDataObject("data");
|
||||
var entityIds = datas["entity_ids"].ConvertTo<List<string>>();
|
||||
|
||||
if (entityIds == null || entityIds.Count == 0) return;
|
||||
|
||||
var entities = EntityManager.Instance.GetEntities(entityIds, this);
|
||||
if (entities == null || entities.Count == 0) return;
|
||||
|
||||
foreach (var e in entities)
|
||||
{
|
||||
PlaceNext(e);
|
||||
}
|
||||
}
|
||||
|
||||
public override void DecreaseEntity(Entity entity)
|
||||
{
|
||||
if (entity == null) return;
|
||||
|
||||
// 내부 리스트에서 제거(부모 변경/파괴 등 어떤 상태든 Remove는 안전)
|
||||
possessEntities.Remove(entity);
|
||||
|
||||
// 재배치
|
||||
ReflowEntities();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 현재 보유 엔티티들을 0번 인덱스부터 다시 적층하여 빈칸 제거
|
||||
/// 순회-수정 예외 방지를 위해 스냅샷을 만든 뒤, 원본을 비우고 다시 채움
|
||||
/// </summary>
|
||||
public void ReflowEntities()
|
||||
{
|
||||
|
||||
// 위치 인덱스 초기화
|
||||
index = 0;
|
||||
|
||||
// 스냅샷 생성 (null 제거)
|
||||
var snapshot = new List<Entity>(possessEntities.Count);
|
||||
foreach (var e in possessEntities)
|
||||
{
|
||||
if (e != null) snapshot.Add(e);
|
||||
}
|
||||
|
||||
// 원본을 비운 뒤 스냅샷 기반으로 재배치
|
||||
possessEntities.Clear();
|
||||
|
||||
foreach (var e in snapshot)
|
||||
{
|
||||
PlaceNext(e);
|
||||
}
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (getdata)
|
||||
{
|
||||
if (moveSpeed <= 0f) return;
|
||||
|
||||
t += Time.deltaTime / moveSpeed;
|
||||
|
||||
// 위치 업데이트
|
||||
if (t <= 1f)
|
||||
{
|
||||
transform.position = Vector3.Lerp(startPosition, targetPosition, t);
|
||||
}
|
||||
else
|
||||
{
|
||||
transform.position = targetPosition;
|
||||
getdata = false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// 회전 업데이트
|
||||
if (isRotating)
|
||||
{
|
||||
float currentAngle = transform.eulerAngles.y;
|
||||
float targetAngle = targetRotation.eulerAngles.y;
|
||||
float newAngle = Mathf.SmoothDampAngle(currentAngle, targetAngle, ref angularVelocity, rotationSpeed, Mathf.Infinity, dampedTime);
|
||||
transform.rotation = Quaternion.Euler(0, newAngle, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
transform.rotation = targetRotation;
|
||||
angularVelocity = 0f;
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Simulator/Components/AGV/AGV.cs.meta
Normal file
2
Assets/Scripts/Simulator/Components/AGV/AGV.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c84839a87b39b2a40aad67c2327e5c19
|
||||
22
Assets/Scripts/Simulator/Components/AGV/AGVData.cs
Normal file
22
Assets/Scripts/Simulator/Components/AGV/AGVData.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
|
||||
namespace Simulator.Data.Transport
|
||||
{
|
||||
[Serializable]
|
||||
public class AGVData
|
||||
{
|
||||
public string name;
|
||||
public string label;
|
||||
public string initial_node;
|
||||
public int max_load_capacity;
|
||||
public AGVTimePolicy loading_time_policy;
|
||||
public AGVTimePolicy unloading_time_policy;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class AGVTimePolicy
|
||||
{
|
||||
public string type;
|
||||
public int value;
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Simulator/Components/AGV/AGVData.cs.meta
Normal file
2
Assets/Scripts/Simulator/Components/AGV/AGVData.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d8cb78bd44077624d8a1ca1574857417
|
||||
163
Assets/Scripts/Simulator/Components/AGV/AGVManager.cs
Normal file
163
Assets/Scripts/Simulator/Components/AGV/AGVManager.cs
Normal file
@@ -0,0 +1,163 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using NUnit.Framework.Interfaces;
|
||||
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;
|
||||
using UVC.Factory.Component;
|
||||
using UVC.Pool;
|
||||
|
||||
namespace Simulator.Data.Transport
|
||||
{
|
||||
public enum AGVDataType
|
||||
{
|
||||
Moving,
|
||||
Loading,
|
||||
UnLoading
|
||||
}
|
||||
public class AGVManager : SingletonScene<AGVManager>
|
||||
{
|
||||
private readonly Dictionary<string, string> prefabPaths = new Dictionary<string, string>()
|
||||
{
|
||||
{"agv","prefabs/agv"},
|
||||
};
|
||||
|
||||
private GameObjectPool<AGV>? agvPool;
|
||||
public GameObjectPool<AGV> AGVPool
|
||||
{
|
||||
get
|
||||
{
|
||||
if (agvPool == null)
|
||||
{
|
||||
//Debug.LogError("Pool is not initialized. Please call InitializePoolAsync first.");
|
||||
}
|
||||
return agvPool!;
|
||||
}
|
||||
}
|
||||
private DataMapper agvMoveMapper;
|
||||
private DataMapper agvLoadMapper;
|
||||
private DataMapper agvUnLoadMapper;
|
||||
|
||||
protected override void Init()
|
||||
{
|
||||
InitializeAGVPoolAsync().ContinueWith(() =>
|
||||
{
|
||||
var agvMoveDataMask = new DataMask();
|
||||
agvMoveDataMask.ObjectName = "agv"; // AGV 객체의 이름을 설정합니다.
|
||||
agvMoveDataMask.ObjectIdKey = "component_id"; // AGV의 고유 식별자로 사용할 키를 설정합니다.
|
||||
agvMoveDataMask["component_id"] = "";
|
||||
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"] = "";
|
||||
agvLoadDataMask["event_name"] = "";
|
||||
agvLoadDataMask["timestamp"] = new DateTime();
|
||||
agvLoadDataMask["data"] = new DataMask()
|
||||
{
|
||||
["from_node"] = "",
|
||||
["to_node"] = "",
|
||||
["entity_ids"] = new List<string>(),
|
||||
};
|
||||
|
||||
agvLoadMapper = new DataMapper(agvLoadDataMask);
|
||||
|
||||
var agvUnLoadDataMask = new DataMask();
|
||||
agvUnLoadDataMask.ObjectName = "agv"; // AGV 객체의 이름을 설정합니다.
|
||||
agvUnLoadDataMask.ObjectIdKey = "component_id"; // AGV의 고유 식별자로 사용할 키를 설정합니다.
|
||||
agvUnLoadDataMask["component_id"] = "";
|
||||
agvUnLoadDataMask["event_name"] = "";
|
||||
agvUnLoadDataMask["timestamp"] = new DateTime();
|
||||
agvUnLoadDataMask["data"] = new DataMask()
|
||||
{
|
||||
["component"]="",
|
||||
["entity_ids"] = new List<string>(),
|
||||
};
|
||||
|
||||
agvUnLoadMapper = new DataMapper(agvUnLoadDataMask);
|
||||
});
|
||||
}
|
||||
|
||||
public void InitAGV(List<AGVData> datas)
|
||||
{
|
||||
foreach (var data in datas)
|
||||
{
|
||||
var agv = agvPool.GetItem($"{data.name}");
|
||||
agv.data = data;
|
||||
agv.UpdatePosition(AGVNodeManager.Instance.GetNodePosition(data.initial_node));
|
||||
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);
|
||||
mqttConfigAGVLoading.SetHandler((value) => OnUpdateData(value, AGVDataType.Loading));
|
||||
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);
|
||||
mqttConfigAGVUnLoading.SetHandler((value) => OnUpdateData(value, AGVDataType.UnLoading));
|
||||
DataRepository.Instance.MqttReceiver.Add(mqttConfigAGVUnLoading);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnUpdateData(IDataObject data, AGVDataType type)
|
||||
{
|
||||
if (data == null) return;
|
||||
|
||||
DataObject? obj = data as DataObject;
|
||||
if (obj == null) return;
|
||||
//Debug.Log($"OnUpdateData:{obj}, {obj["data"]}");
|
||||
AGV? agv = agvPool.GetItem(obj.GetString("component_id")!);
|
||||
switch (type)
|
||||
{
|
||||
case AGVDataType.Moving:
|
||||
agv.SetTargetPosition(obj);
|
||||
break;
|
||||
case AGVDataType.Loading:
|
||||
agv.LoadEntity(obj);
|
||||
break;
|
||||
case AGVDataType.UnLoading:
|
||||
//Debug.Log("unload");
|
||||
//agv.UnLoadEntity(obj);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 40a549d01589ef14bb8b3d3c57a05619
|
||||
10
Assets/Scripts/Simulator/Components/AGV/AGVNode.cs
Normal file
10
Assets/Scripts/Simulator/Components/AGV/AGVNode.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using Simulator.Data.Transport;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Simulator.Data.Transport
|
||||
{
|
||||
public class AGVNode : MonoBehaviour
|
||||
{
|
||||
public NodeDataClass data;
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Simulator/Components/AGV/AGVNode.cs.meta
Normal file
2
Assets/Scripts/Simulator/Components/AGV/AGVNode.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ccd5d405d7db0864d86d76c73cbab45b
|
||||
109
Assets/Scripts/Simulator/Components/AGV/AGVNodeManager.cs
Normal file
109
Assets/Scripts/Simulator/Components/AGV/AGVNodeManager.cs
Normal file
@@ -0,0 +1,109 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UVC.Core;
|
||||
using UVC.Pool;
|
||||
|
||||
namespace Simulator.Data.Transport
|
||||
{
|
||||
public class AGVNodeManager : SingletonScene<AGVNodeManager>
|
||||
{
|
||||
private readonly Dictionary<string, string> prefabPaths = new Dictionary<string, string>()
|
||||
{
|
||||
{"node","prefabs/Node"},
|
||||
{"path","prefabs/Path"},
|
||||
};
|
||||
|
||||
Dictionary<string, AGVNode> nodeDict = new Dictionary<string, AGVNode>();
|
||||
|
||||
private GameObjectPool<AGVNode>? nodePool;
|
||||
private GameObjectPool<AGVPath>? pathPool;
|
||||
public GameObjectPool<AGVNode> NodePool
|
||||
{
|
||||
get
|
||||
{
|
||||
if (nodePool == null)
|
||||
{
|
||||
//Debug.LogError("Pool is not initialized. Please call InitializePoolAsync first.");
|
||||
}
|
||||
return nodePool!;
|
||||
}
|
||||
}
|
||||
|
||||
public GameObjectPool<AGVPath> PathPool
|
||||
{
|
||||
get
|
||||
{
|
||||
if (pathPool == null)
|
||||
{
|
||||
//Debug.LogError("Pool is not initialized. Please call InitializePoolAsync first.");
|
||||
}
|
||||
return pathPool!;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Init()
|
||||
{
|
||||
InitializeNodePoolAsync().ContinueWith(() =>
|
||||
{
|
||||
});
|
||||
InitializePathPoolAsync().ContinueWith(() =>
|
||||
{
|
||||
});
|
||||
}
|
||||
|
||||
public Vector3 GetNodePosition(string name)
|
||||
{
|
||||
if (!nodeDict.ContainsKey(name))
|
||||
{
|
||||
return Vector3.zero;
|
||||
}
|
||||
return nodeDict[name].transform.position;
|
||||
}
|
||||
|
||||
public void SpawnNode(List<NodeDataClass> datas)
|
||||
{
|
||||
foreach (var data in datas)
|
||||
{
|
||||
var node = nodePool.GetItem($"{data.id}");
|
||||
node.transform.position = data.GetPosition();
|
||||
node.data = data;
|
||||
nodeDict.Add(data.id, node);
|
||||
}
|
||||
}
|
||||
|
||||
public void LinkNode(List<PathDataClass> datas)
|
||||
{
|
||||
foreach (var data in datas)
|
||||
{
|
||||
var path = pathPool.GetItem($"{data.from}{data.to}");
|
||||
path.SetPathData(data, GetNodePosition(data.from), GetNodePosition(data.to));
|
||||
}
|
||||
}
|
||||
|
||||
private async UniTask InitializeNodePoolAsync()
|
||||
{
|
||||
if (nodePool != null) return;
|
||||
|
||||
var prefab = await Resources.LoadAsync<GameObject>(prefabPaths["node"]) as GameObject;
|
||||
if (prefab == null)
|
||||
{
|
||||
Debug.LogError($"Prefab not found at path: {prefabPaths["node"]}");
|
||||
return;
|
||||
}
|
||||
nodePool = new GameObjectPool<AGVNode>(prefab, transform);
|
||||
}
|
||||
private async UniTask InitializePathPoolAsync()
|
||||
{
|
||||
if (pathPool != null) return;
|
||||
|
||||
var prefab = await Resources.LoadAsync<GameObject>(prefabPaths["path"]) as GameObject;
|
||||
if (prefab == null)
|
||||
{
|
||||
Debug.LogError($"Prefab not found at path: {prefabPaths["path"]}");
|
||||
return;
|
||||
}
|
||||
pathPool = new GameObjectPool<AGVPath>(prefab, transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5eabff7fec7b41b4a9d71f4e3e07a802
|
||||
22
Assets/Scripts/Simulator/Components/AGV/AGVPath.cs
Normal file
22
Assets/Scripts/Simulator/Components/AGV/AGVPath.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using Simulator.Data.Transport;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
using UVC.Data.Core;
|
||||
|
||||
public class AGVPath : MonoBehaviour
|
||||
{
|
||||
PathDataClass data;
|
||||
LineRenderer lineRenderer;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
lineRenderer=GetComponent<LineRenderer>();
|
||||
}
|
||||
|
||||
public void SetPathData(PathDataClass data,Vector3 from,Vector3 to)
|
||||
{
|
||||
this.data = data;
|
||||
lineRenderer.SetPosition(0, from);
|
||||
lineRenderer.SetPosition(1, to);
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Simulator/Components/AGV/AGVPath.cs.meta
Normal file
2
Assets/Scripts/Simulator/Components/AGV/AGVPath.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7a2b122b7b81e124bb04810ff54ff57a
|
||||
8
Assets/Scripts/Simulator/Components/AGV/DataClass.meta
Normal file
8
Assets/Scripts/Simulator/Components/AGV/DataClass.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 303872d5c57e4ed449cb0415d729ca4f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Simulator.Data.Transport
|
||||
{
|
||||
[Serializable]
|
||||
public class NodeDataClass
|
||||
{
|
||||
public string id="";
|
||||
public string type="";
|
||||
public Position position=new Position();
|
||||
public Properties properties=new Properties();
|
||||
|
||||
public Vector3 GetPosition()
|
||||
{
|
||||
return new Vector3(position.x,position.z, position.y);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Properties
|
||||
{
|
||||
public int capacity=0;
|
||||
public string description= "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d4b0f50147f60ae44a554f16faaf0b2a
|
||||
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Simulator.Data.Transport
|
||||
{
|
||||
[Serializable]
|
||||
public class PathDataClass
|
||||
{
|
||||
public string to = "";
|
||||
public string from = "";
|
||||
public bool bidirectional = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6c7b8b8c9de345046beea547c59e9e4b
|
||||
8
Assets/Scripts/Simulator/Components/ASRS.meta
Normal file
8
Assets/Scripts/Simulator/Components/ASRS.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 25a0f380ffc52174fa79a438fea26374
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
49
Assets/Scripts/Simulator/Components/ASRS/ASRSComponent.cs
Normal file
49
Assets/Scripts/Simulator/Components/ASRS/ASRSComponent.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using Gpm.Ui.Sample;
|
||||
using Simulator.Data;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography;
|
||||
using UnityEditor.Rendering;
|
||||
using UnityEngine;
|
||||
using UVC.Data.Core;
|
||||
|
||||
public class ASRSComponent : ComponentBase
|
||||
{
|
||||
ASRSDataClass asrsData = new ASRSDataClass();
|
||||
public RackComponent rack;
|
||||
public StackerCrane stacker;
|
||||
|
||||
public void SetComponent(ASRSDataClass asrsData)
|
||||
{
|
||||
this.asrsData = asrsData;
|
||||
data = asrsData;
|
||||
SetRack(asrsData.asrs_layout);
|
||||
SetStackerCrane(asrsData.asrs_layout);
|
||||
}
|
||||
|
||||
public override void GetModelData(DataObject modelData)
|
||||
{
|
||||
var name = modelData.GetString("event_name");
|
||||
if (string.Equals(name,"moving"))
|
||||
{
|
||||
stacker.SetTargetPosition(modelData,rack);
|
||||
}
|
||||
if (string.Equals(name, "fork_operation"))
|
||||
{
|
||||
stacker.SetForkPosition(modelData,rack);
|
||||
}
|
||||
if (string.Equals(name, "arrived"))
|
||||
{
|
||||
rack.GetModelData(modelData);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetRack(rack_layout layout)
|
||||
{
|
||||
rack.SpawnCell(layout,true);
|
||||
}
|
||||
|
||||
void SetStackerCrane(rack_layout layout)
|
||||
{
|
||||
stacker.SetPosition(layout);
|
||||
}
|
||||
}
|
||||
169
Assets/Scripts/Simulator/Components/ASRS/StackerCrane.cs
Normal file
169
Assets/Scripts/Simulator/Components/ASRS/StackerCrane.cs
Normal file
@@ -0,0 +1,169 @@
|
||||
using Simulator.Data;
|
||||
using Simulator.Data.Transport;
|
||||
using System;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UVC.Data.Core;
|
||||
|
||||
public class StackerCrane : ComponentBase
|
||||
{
|
||||
[SerializeField]
|
||||
GameObject horizonObject;
|
||||
[SerializeField]
|
||||
GameObject verticalObject;
|
||||
[SerializeField]
|
||||
GameObject forkObject;
|
||||
|
||||
bool horizontalPosition = false;
|
||||
bool verticalPosition = false;
|
||||
bool forkPosition = false;
|
||||
bool operationflag = false;
|
||||
bool operationcounting = false;
|
||||
Vector3 horizontalTarget;
|
||||
Vector3 verticalTarget;
|
||||
Vector3 forkTarget;
|
||||
Vector3 horizontalStart;
|
||||
Vector3 verticalStart;
|
||||
Vector3 forkStart;
|
||||
Vector3 startposition;
|
||||
string entityId;
|
||||
float horizontalTime;
|
||||
float verticalTime;
|
||||
float forkTime;
|
||||
float ht;
|
||||
float vt;
|
||||
float ft;
|
||||
|
||||
public void SetPosition(rack_layout layout)
|
||||
{
|
||||
horizonObject.transform.localPosition = new Vector3(layout.x_length * -0.55f, layout.z_length * 0, layout.y_length * -0.5f);
|
||||
startposition = new Vector3(layout.x_length * -0.55f, layout.z_length * 0, layout.y_length * -0.5f);
|
||||
forkStart = new Vector3(0, 0, 0.3f);
|
||||
}
|
||||
public void SetTargetPosition(DataObject agvData, RackComponent rack)
|
||||
{
|
||||
var datas = agvData.GetDataObject("data");
|
||||
var crane_movement = datas.GetDataObject("crane_movement");
|
||||
horizontalTime = (float)crane_movement.GetFloat("horizontal_time");
|
||||
horizontalTime = horizontalTime / 10f;
|
||||
verticalTime = (float)crane_movement.GetFloat("vertical_time");
|
||||
verticalTime = verticalTime / 10f;
|
||||
var from = crane_movement.GetDataObject("from_position");
|
||||
var fx = (int)from.GetInt("x");
|
||||
var fy = (int)from.GetInt("y");
|
||||
var fz = (int)from.GetInt("z");
|
||||
horizontalStart = new Vector3(horizonObject.transform.position.x, 0, startposition.z);
|
||||
verticalStart = new Vector3(-0.03920534f, -0.2531985f, verticalObject.transform.position.z);
|
||||
var to = crane_movement.GetDataObject("to_position");
|
||||
var tx = (int)to.GetInt("x");
|
||||
var ty = (int)to.GetInt("y");
|
||||
var tz = (int)to.GetInt("z");
|
||||
var rackp = rack.cellComponents[(tx, tz, ty)].transform.position;
|
||||
//Debug.Log($"{tx},{ty},{tz}");
|
||||
horizontalTarget = new Vector3(rackp.x, 0, startposition.z);
|
||||
verticalTarget = new Vector3(-0.03920534f, -0.2531985f, rackp.y);
|
||||
ht = 0f;
|
||||
vt = 0f;
|
||||
horizontalPosition = true;
|
||||
verticalPosition = true;
|
||||
}
|
||||
|
||||
public void SetForkPosition(DataObject agvData, RackComponent rack)
|
||||
{
|
||||
var datas = agvData.GetDataObject("data");
|
||||
entityId = datas.GetString("entity_id");
|
||||
var operation = datas.GetString("operation");
|
||||
if (string.Equals(operation, "up"))
|
||||
{
|
||||
operationflag = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
operationflag = false;
|
||||
}
|
||||
operationcounting = true;
|
||||
var timing = datas.GetDataObject("timing");
|
||||
forkTime = (float)timing.GetFloat("total");
|
||||
var position = datas.GetDataObject("position");
|
||||
var x = (int)position.GetInt("x");
|
||||
var y = (int)position.GetInt("y");
|
||||
var z = (int)position.GetInt("z");
|
||||
var rackp = rack.cellComponents[(x, z, y)].transform.position;
|
||||
ft = 0f;
|
||||
forkTarget = new Vector3(-rackp.z+startposition.x,0, 0.3f);
|
||||
Debug.Log($"t{forkTarget}");
|
||||
Debug.Log($"s{forkStart}");
|
||||
forkPosition = true;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (horizontalPosition)
|
||||
{
|
||||
if (horizontalTime >= 0f)
|
||||
{
|
||||
ht += Time.deltaTime / horizontalTime;
|
||||
|
||||
// 위치 업데이트
|
||||
if (ht <= 1f)
|
||||
{
|
||||
horizonObject.transform.localPosition = Vector3.Lerp(horizontalStart, horizontalTarget, ht);
|
||||
}
|
||||
else
|
||||
{
|
||||
horizonObject.transform.localPosition = horizontalTarget;
|
||||
horizontalPosition = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (verticalPosition)
|
||||
{
|
||||
if (verticalTime >= 0f)
|
||||
{
|
||||
vt += Time.deltaTime / verticalTime;
|
||||
|
||||
// 위치 업데이트
|
||||
if (vt <= 1f)
|
||||
{
|
||||
verticalObject.transform.localPosition = Vector3.Lerp(verticalStart, verticalTarget, vt);
|
||||
}
|
||||
else
|
||||
{
|
||||
verticalObject.transform.localPosition = verticalTarget;
|
||||
verticalPosition = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (forkPosition)
|
||||
{
|
||||
if (forkTime >= 0f)
|
||||
{
|
||||
ft += Time.deltaTime / forkTime;
|
||||
// 위치 업데이트
|
||||
if (ft <= 0.5f)
|
||||
{
|
||||
forkObject.transform.localPosition = Vector3.Lerp(forkStart, forkTarget, ft);
|
||||
}
|
||||
else if (ft >= 0.5f && ft <= 1f)
|
||||
{
|
||||
if (operationcounting)
|
||||
{
|
||||
if (operationflag)
|
||||
{
|
||||
var entity = EntityManager.Instance.GetEntity(entityId,this);
|
||||
entity.transform.parent = forkObject.transform;
|
||||
entity.transform.localPosition = new Vector3(0, 0, 0);
|
||||
}
|
||||
operationcounting = false;
|
||||
}
|
||||
forkObject.transform.localPosition = Vector3.Lerp(forkTarget, forkStart, ft);
|
||||
}
|
||||
else
|
||||
{
|
||||
forkObject.transform.localPosition = forkStart;
|
||||
forkPosition = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ed3ea1e82c6dfe64b8b181f304a8a0c7
|
||||
@@ -1,12 +0,0 @@
|
||||
using Simulator.Data;
|
||||
using UnityEngine;
|
||||
|
||||
public class ASRSComponent : ComponentBase
|
||||
{
|
||||
ASRSDataClass asrsData = new ASRSDataClass();
|
||||
public void ChangeComponent(ASRSDataClass asrsData)
|
||||
{
|
||||
this.asrsData = asrsData;
|
||||
data = asrsData;
|
||||
}
|
||||
}
|
||||
6
Assets/Scripts/Simulator/Components/CellComponent.cs
Normal file
6
Assets/Scripts/Simulator/Components/CellComponent.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class CellComponent : MonoBehaviour
|
||||
{
|
||||
public GameObject Socket;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8c30230088e7bde47afaf84efbcb905f
|
||||
@@ -7,6 +7,8 @@ public enum ComponentType
|
||||
{
|
||||
Source,
|
||||
Sink,
|
||||
Rack,
|
||||
Queue,
|
||||
ASRS
|
||||
}
|
||||
public class ComponentBase : MonoBehaviour
|
||||
@@ -23,4 +25,9 @@ public class ComponentBase : MonoBehaviour
|
||||
public virtual void GetModelData(DataObject modelData)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void DecreaseEntity(Entity entity)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,247 +1,387 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Simulator.Config;
|
||||
using Simulator.Data;
|
||||
using Simulator.Data.Transport;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Unity.VisualScripting;
|
||||
using UnityEngine;
|
||||
using UVC.Core;
|
||||
using UVC.Data;
|
||||
using UVC.Data.Core;
|
||||
using UVC.Data.Mqtt;
|
||||
using UVC.Factory.Component;
|
||||
using UVC.Network;
|
||||
using UVC.Pool;
|
||||
using static Unity.VisualScripting.Member;
|
||||
using static UnityEditor.Progress;
|
||||
|
||||
public class ComponentsManager : SingletonScene<ComponentsManager>
|
||||
namespace Simulator.Data
|
||||
{
|
||||
private readonly Dictionary<string, string> prefabPaths = new Dictionary<string, string>()
|
||||
public class ComponentsManager : SingletonScene<ComponentsManager>
|
||||
{
|
||||
private readonly Dictionary<string, string> prefabPaths = new Dictionary<string, string>()
|
||||
{
|
||||
{"source","prefabs/pallet"},
|
||||
{"queue","prefabs/pallet"},
|
||||
{"queue","prefabs/queue"},
|
||||
{"rack","prefabs/rack"},
|
||||
{"sink","prefabs/Sink_Container"},
|
||||
{"asrs","prefabs/Store_BufferRack"},
|
||||
{"asrs","prefabs/ASRS"},
|
||||
};
|
||||
|
||||
Dictionary<string, ComponentBase> componentDatas=new Dictionary<string, ComponentBase>();
|
||||
Dictionary<ComponentType, DataMapper> dataMapperDict=new Dictionary<ComponentType, DataMapper>();
|
||||
public Dictionary<string, ComponentBase> componentDatas = new Dictionary<string, ComponentBase>();
|
||||
Dictionary<ComponentType, DataMapper> dataMapperDict = new Dictionary<ComponentType, DataMapper>();
|
||||
|
||||
private GameObjectPool<SourceComponent>? sourcePool;
|
||||
private GameObjectPool<SinkComponent>? sinkPool;
|
||||
private GameObjectPool<ASRSComponent>? asrsPool;
|
||||
private GameObjectPool<SourceComponent>? sourcePool;
|
||||
private GameObjectPool<SinkComponent>? sinkPool;
|
||||
private GameObjectPool<QueueComponent>? queuePool;
|
||||
private GameObjectPool<RackComponent>? rackPool;
|
||||
private GameObjectPool<ASRSComponent>? asrsPool;
|
||||
|
||||
public logicDetailData logicDetailData;
|
||||
public SimulatorCodeDataClass codedata;
|
||||
public LogicDetailData logicDetailData;
|
||||
public SimulatorCodeDataClass codedata;
|
||||
|
||||
public GameObjectPool<SourceComponent> SourcePool
|
||||
{
|
||||
get
|
||||
public GameObjectPool<SourceComponent> SourcePool
|
||||
{
|
||||
if (sourcePool == null)
|
||||
get
|
||||
{
|
||||
Debug.LogError("Pool is not initialized. Please call InitializePoolAsync first.");
|
||||
if (sourcePool == null)
|
||||
{
|
||||
Debug.LogError("Pool is not initialized. Please call InitializePoolAsync first.");
|
||||
}
|
||||
return sourcePool!;
|
||||
}
|
||||
return sourcePool!;
|
||||
}
|
||||
}
|
||||
|
||||
public GameObjectPool<SinkComponent> SinkPool
|
||||
{
|
||||
get
|
||||
public GameObjectPool<SinkComponent> SinkPool
|
||||
{
|
||||
if (sourcePool == null)
|
||||
get
|
||||
{
|
||||
Debug.LogError("Pool is not initialized. Please call InitializePoolAsync first.");
|
||||
if (sourcePool == null)
|
||||
{
|
||||
Debug.LogError("Pool is not initialized. Please call InitializePoolAsync first.");
|
||||
}
|
||||
return sinkPool!;
|
||||
}
|
||||
return sinkPool!;
|
||||
}
|
||||
}
|
||||
|
||||
public GameObjectPool<ASRSComponent> AsrsPool
|
||||
{
|
||||
get
|
||||
public GameObjectPool<QueueComponent> QueuePool
|
||||
{
|
||||
if (asrsPool == null)
|
||||
get
|
||||
{
|
||||
Debug.LogError("Pool is not initialized. Please call InitializePoolAsync first.");
|
||||
if (queuePool == null)
|
||||
{
|
||||
Debug.LogError("Pool is not initialized. Please call InitializePoolAsync first.");
|
||||
}
|
||||
return queuePool!;
|
||||
}
|
||||
return asrsPool!;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Init()
|
||||
{
|
||||
InitializeSourcePoolAsync().ContinueWith(() =>
|
||||
public GameObjectPool<RackComponent> RackPool
|
||||
{
|
||||
//playback에서도 데이터를 업데이트 하기에 DataRepository에 핸들러를 추가합니다.
|
||||
// DataRepository.Instance.AddDataUpdateHandler("AGV", OnUpdateData);
|
||||
|
||||
var sourceDataMask = new DataMask();
|
||||
sourceDataMask.ObjectName = "Source"; // AGV 객체의 이름을 설정합니다.
|
||||
sourceDataMask.ObjectIdKey = "component_id"; // AGV의 고유 식별자로 사용할 키를 설정합니다.
|
||||
sourceDataMask["component_id"] = "";
|
||||
sourceDataMask["data"] = new DataMask()
|
||||
get
|
||||
{
|
||||
["total_entity"] = 0
|
||||
if (rackPool == null)
|
||||
{
|
||||
Debug.LogError("Pool is not initialized. Please call InitializePoolAsync first.");
|
||||
}
|
||||
return rackPool!;
|
||||
}
|
||||
}
|
||||
|
||||
public GameObjectPool<ASRSComponent> AsrsPool
|
||||
{
|
||||
get
|
||||
{
|
||||
if (asrsPool == null)
|
||||
{
|
||||
Debug.LogError("Pool is not initialized. Please call InitializePoolAsync first.");
|
||||
}
|
||||
return asrsPool!;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Init()
|
||||
{
|
||||
InitializeSourcePoolAsync().ContinueWith(() =>
|
||||
{
|
||||
var sourceDataMask = new DataMask();
|
||||
sourceDataMask.ObjectName = "Source"; // AGV 객체의 이름을 설정합니다.
|
||||
sourceDataMask.ObjectIdKey = "component_id"; // AGV의 고유 식별자로 사용할 키를 설정합니다.
|
||||
sourceDataMask["component_id"] = "";
|
||||
sourceDataMask["event_name"] = "";
|
||||
sourceDataMask["timestamp"] = new DateTime();
|
||||
sourceDataMask["data"] = new DataMask()
|
||||
{
|
||||
["entity_ids"] = new List<string>(),
|
||||
["total_entity"] = 0
|
||||
};
|
||||
|
||||
DataMapper sourceMapper = new DataMapper(sourceDataMask);
|
||||
dataMapperDict.Add(ComponentType.Source, sourceMapper);
|
||||
});
|
||||
InitializeSinkPoolAsync().ContinueWith(() =>
|
||||
{
|
||||
var sinkDataMask = new DataMask();
|
||||
sinkDataMask.ObjectName = "Sink"; // AGV 객체의 이름을 설정합니다.
|
||||
sinkDataMask.ObjectIdKey = "component_id"; // AGV의 고유 식별자로 사용할 키를 설정합니다.
|
||||
sinkDataMask["component_id"] = "";
|
||||
sinkDataMask["event_name"] = "";
|
||||
sinkDataMask["timestamp"] = new DateTime();
|
||||
sinkDataMask["data"] = new DataMask()
|
||||
{
|
||||
["entity_ids"] = new List<string>(),
|
||||
};
|
||||
|
||||
DataMapper sinkmapper = new DataMapper(sinkDataMask);
|
||||
dataMapperDict.Add(ComponentType.Sink, sinkmapper);
|
||||
|
||||
});
|
||||
InitializeQueuePoolAsync().ContinueWith(() =>
|
||||
{
|
||||
dataMapperDict.Add(ComponentType.Queue, null);
|
||||
});
|
||||
InitializeRackPoolAsync().ContinueWith(() =>
|
||||
{
|
||||
var rackDataMask = new DataMask();
|
||||
rackDataMask.ObjectName = "Rack";
|
||||
rackDataMask.ObjectIdKey = "component_id";
|
||||
rackDataMask["component_id"] = "";
|
||||
rackDataMask["event_name"] = "";
|
||||
rackDataMask["timestamp"] = new DateTime();
|
||||
rackDataMask["data"] = new DataMask()
|
||||
{
|
||||
["entity_id"] = "",
|
||||
["coordinates"] = new DataMask()
|
||||
{
|
||||
["x"] = 0,
|
||||
["y"] = 0,
|
||||
["z"] = 0
|
||||
}
|
||||
};
|
||||
|
||||
DataMapper rackmapper = new DataMapper(rackDataMask);
|
||||
dataMapperDict.Add(ComponentType.Rack, rackmapper);
|
||||
});
|
||||
InitializeAsrsPoolAsync().ContinueWith(() =>
|
||||
{
|
||||
var asrsDataMask = new DataMask();
|
||||
DataMapper asrsmapper = new DataMapper(asrsDataMask);
|
||||
dataMapperDict.Add(ComponentType.ASRS, asrsmapper);
|
||||
});
|
||||
testRequest();
|
||||
}
|
||||
|
||||
private async void testRequest()
|
||||
{
|
||||
var data = await HttpRequester.RequestGet<Totaljson>($"{Constants.HTTP_DOMAIN}/simulation/logics/35", null, null, true);
|
||||
logicDetailData = data.data.data;
|
||||
SimulatorCreateRequestParameter param = new SimulatorCreateRequestParameter();
|
||||
param.speed = SimulationConfig.speed;
|
||||
Dictionary<string, object> body = new Dictionary<string, object>()
|
||||
{
|
||||
{ "projectId",18 },
|
||||
{ "logicId",35 },
|
||||
{ "logicData",null },
|
||||
{ "name","새 시뮬레이션2" },
|
||||
{ "parameters",param }
|
||||
};
|
||||
|
||||
DataMapper mapper = new DataMapper(sourceDataMask);
|
||||
dataMapperDict.Add(ComponentType.Source, mapper);
|
||||
/*
|
||||
var mqttConfig = new MqttSubscriptionConfig("Source");
|
||||
mqttConfig.SetDataMapper(mapper);
|
||||
|
||||
|
||||
// 생성한 파이프라인 정보를 전역 MQTT 파이프라인에 추가합니다.
|
||||
DataRepository.Instance.MqttReceiver.Add(mqttConfig);
|
||||
*/
|
||||
});
|
||||
InitializeSinkPoolAsync().ContinueWith(() =>
|
||||
{
|
||||
var sinkDataMask = new DataMask();
|
||||
sinkDataMask.ObjectName = "Sink"; // AGV 객체의 이름을 설정합니다.
|
||||
sinkDataMask.ObjectIdKey = "component_id"; // AGV의 고유 식별자로 사용할 키를 설정합니다.
|
||||
/*
|
||||
sinkDataMask["component_id"] = "";
|
||||
sinkDataMask["data"] = new DataMask()
|
||||
var cdata = await HttpRequester.RequestPost<SimulatorCodeDataClass>($"{Constants.HTTP_DOMAIN}/simulation/histories", body, null, true);
|
||||
codedata = cdata;
|
||||
SimulationConfig.SimulationCode = codedata.data.data.simulationCode;
|
||||
if (data.data.data.production_system.conveyors != null && data.data.data.production_system.conveyors.Count >= 1)
|
||||
{
|
||||
//["sink_id"] = 0
|
||||
};
|
||||
*/
|
||||
ConveyorManager.Instance.Build(data.data.data.production_system.conveyors);
|
||||
}
|
||||
if (data.data.data.transport_system != null)
|
||||
{
|
||||
if (data.data.data.transport_system.node_networks != null && data.data.data.transport_system.node_networks.Count >= 1)
|
||||
{
|
||||
AGVNodeManager.Instance.SpawnNode(data.data.data.transport_system.node_networks[0].nodes);
|
||||
AGVNodeManager.Instance.LinkNode(data.data.data.transport_system.node_networks[0].paths);
|
||||
}
|
||||
if (data.data.data.transport_system.transport_managers != null && data.data.data.transport_system.transport_managers.Count >= 1)
|
||||
{
|
||||
if (data.data.data.transport_system.transport_managers[0].vehicle_fleets != null && data.data.data.transport_system.transport_managers[0].vehicle_fleets.Count >= 1)
|
||||
{
|
||||
AGVManager.Instance.InitAGV(data.data.data.transport_system.transport_managers[0].vehicle_fleets[0].vehicles);
|
||||
}
|
||||
}
|
||||
}
|
||||
SpawnComponents(data.data.data);
|
||||
SubscribeTopic();
|
||||
DataRepository.Instance.MqttReceiver.Start();
|
||||
}
|
||||
|
||||
DataMapper mapper = new DataMapper(sinkDataMask);
|
||||
dataMapperDict.Add(ComponentType.Sink, mapper);
|
||||
|
||||
});
|
||||
InitializeAsrsPoolAsync().ContinueWith(() =>
|
||||
void SubscribeTopic()
|
||||
{
|
||||
});
|
||||
testRequest();
|
||||
}
|
||||
foreach (var componentdata in componentDatas)
|
||||
{
|
||||
DataRepository.Instance.MqttReceiver.AddTopic($"simulation/{codedata.data.data.simulationCode}/components/+/{componentdata.Key}/#");
|
||||
var mqttConfig = new MqttSubscriptionConfig($"simulation/{SimulationConfig.SimulationCode}/components/+/{componentdata.Key}/#");
|
||||
mqttConfig.SetDataMapper(dataMapperDict[componentdata.Value.componentType]);
|
||||
mqttConfig.SetHandler((value) => OnUpdateData(value, componentdata.Value.componentType));
|
||||
//mqttConfig.SetHandler((value)=>Debug.Log(value));
|
||||
DataRepository.Instance.MqttReceiver.Add(mqttConfig);
|
||||
}
|
||||
}
|
||||
|
||||
private async void testRequest()
|
||||
{
|
||||
var data = await HttpRequester.RequestGet<totaljson>($"{Constants.HTTP_DOMAIN}/simulation/logics/30", null, null, true);
|
||||
SimulatorCreateRequestParameter param=new SimulatorCreateRequestParameter();
|
||||
Dictionary<string, object> body = new Dictionary<string, object>()
|
||||
public void OnUpdateData(IDataObject data, ComponentType type)
|
||||
{
|
||||
{ "projectId",18 },
|
||||
{ "logicId",30 },
|
||||
{ "logicData",null },
|
||||
{ "name","새 시뮬레이션2" },
|
||||
{ "parameters",param }
|
||||
};
|
||||
var cdata = await HttpRequester.RequestPost<SimulatorCodeDataClass>($"{Constants.HTTP_DOMAIN}/simulation/histories", body, null, true);
|
||||
codedata = cdata;
|
||||
logicDetailData = data.data.data;
|
||||
SpawnComponents(data.data.data);
|
||||
SubscribeTopic();
|
||||
DataRepository.Instance.MqttReceiver.Start();
|
||||
}
|
||||
|
||||
void SubscribeTopic()
|
||||
{
|
||||
foreach (var componentdata in componentDatas)
|
||||
if (data == null) return;
|
||||
|
||||
DataObject? obj = data as DataObject;
|
||||
if (obj == null) return;
|
||||
|
||||
//Debug.Log($"OnUpdateData:{obj}, {obj["data"]}");
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case ComponentType.Source:
|
||||
if (string.Equals(obj.GetString("event_name"), "completed"))
|
||||
{
|
||||
SourceComponent? source = sourcePool.GetItem(obj.GetString("component_id")!);
|
||||
source.GetModelData(obj);
|
||||
}
|
||||
break;
|
||||
case ComponentType.Sink:
|
||||
if (string.Equals(obj.GetString("event_name"), "completed"))
|
||||
{
|
||||
SinkComponent? sink = sinkPool.GetItem(obj.GetString("component_id")!);
|
||||
sink.GetModelData(obj);
|
||||
Debug.Log(obj);
|
||||
}
|
||||
break;
|
||||
case ComponentType.Queue:
|
||||
if (string.Equals(obj.GetString("event_name"), "completed"))
|
||||
{
|
||||
QueueComponent? queue = QueuePool.GetItem(obj.GetString("component_id")!);
|
||||
queue.GetModelData(obj);
|
||||
}
|
||||
break;
|
||||
case ComponentType.Rack:
|
||||
if (string.Equals(obj.GetString("event_name"), "arrived"))
|
||||
{
|
||||
RackComponent? rack = rackPool.GetItem(obj.GetString("component_id")!);
|
||||
rack.GetModelData(obj);
|
||||
}
|
||||
break;
|
||||
case ComponentType.ASRS:
|
||||
ASRSComponent? asrs = asrsPool.GetItem(obj.GetString("component_id")!);
|
||||
asrs.GetModelData(obj);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private async UniTask InitializeSourcePoolAsync()
|
||||
{
|
||||
DataRepository.Instance.MqttReceiver.AddTopic($"simulation/{codedata.data.data.simulationCode}/components/+/{componentdata.Key}/#");
|
||||
var mqttConfig = new MqttSubscriptionConfig($"simulation/{codedata.data.data.simulationCode}/components/+/{componentdata.Key}/#");
|
||||
mqttConfig.SetDataMapper(dataMapperDict[componentdata.Value.componentType]);
|
||||
mqttConfig.SetHandler((value)=>OnUpdateData(value, componentdata.Value.componentType));
|
||||
DataRepository.Instance.MqttReceiver.Add(mqttConfig);
|
||||
if (sourcePool != null) return;
|
||||
|
||||
var prefab = await Resources.LoadAsync<GameObject>(prefabPaths["source"]) as GameObject;
|
||||
if (prefab == null)
|
||||
{
|
||||
Debug.LogError($"Prefab not found at path: {prefabPaths["source"]}");
|
||||
return;
|
||||
}
|
||||
sourcePool = new GameObjectPool<SourceComponent>(prefab, transform);
|
||||
}
|
||||
|
||||
private async UniTask InitializeSinkPoolAsync()
|
||||
{
|
||||
if (sinkPool != null) return;
|
||||
|
||||
var prefab = await Resources.LoadAsync<GameObject>(prefabPaths["sink"]) as GameObject;
|
||||
if (prefab == null)
|
||||
{
|
||||
Debug.LogError($"Prefab not found at path: {prefabPaths["sink"]}");
|
||||
return;
|
||||
}
|
||||
sinkPool = new GameObjectPool<SinkComponent>(prefab, transform);
|
||||
}
|
||||
|
||||
private async UniTask InitializeQueuePoolAsync()
|
||||
{
|
||||
if (queuePool != null) return;
|
||||
|
||||
var prefab = await Resources.LoadAsync<GameObject>(prefabPaths["queue"]) as GameObject;
|
||||
if (prefab == null)
|
||||
{
|
||||
Debug.LogError($"Prefab not found at path: {prefabPaths["queue"]}");
|
||||
return;
|
||||
}
|
||||
queuePool = new GameObjectPool<QueueComponent>(prefab, transform);
|
||||
}
|
||||
|
||||
private async UniTask InitializeRackPoolAsync()
|
||||
{
|
||||
if (rackPool != null) return;
|
||||
|
||||
var prefab = await Resources.LoadAsync<GameObject>(prefabPaths["rack"]) as GameObject;
|
||||
if (prefab == null)
|
||||
{
|
||||
Debug.LogError($"Prefab not found at path: {prefabPaths["rack"]}");
|
||||
return;
|
||||
}
|
||||
rackPool = new GameObjectPool<RackComponent>(prefab, transform);
|
||||
}
|
||||
|
||||
private async UniTask InitializeAsrsPoolAsync()
|
||||
{
|
||||
if (asrsPool != null) return;
|
||||
|
||||
var prefab = await Resources.LoadAsync<GameObject>(prefabPaths["asrs"]) as GameObject;
|
||||
if (prefab == null)
|
||||
{
|
||||
Debug.LogError($"Prefab not found at path: {prefabPaths["asrs"]}");
|
||||
return;
|
||||
}
|
||||
asrsPool = new GameObjectPool<ASRSComponent>(prefab, transform);
|
||||
}
|
||||
|
||||
private void SpawnComponents(LogicDetailData data)
|
||||
{
|
||||
foreach (var component in data.production_system.sources)
|
||||
{
|
||||
var source = sourcePool.GetItem($"{component.name}");
|
||||
source.componentType = ComponentType.Source;
|
||||
source.SetComponent(component);
|
||||
source.SetPosition();
|
||||
componentDatas.Add(component.name, source);
|
||||
}
|
||||
foreach (var component in data.production_system.sinks)
|
||||
{
|
||||
var sink = sinkPool.GetItem($"{component.name}");
|
||||
sink.componentType = ComponentType.Sink;
|
||||
sink.SetComponent(component);
|
||||
sink.SetPosition();
|
||||
componentDatas.Add(component.name, sink);
|
||||
}
|
||||
|
||||
foreach (var component in data.infrastructure.queues)
|
||||
{
|
||||
var queue = queuePool.GetItem($"{component.name}");
|
||||
queue.componentType = ComponentType.Queue;
|
||||
queue.SetComponent(component);
|
||||
queue.SetPosition();
|
||||
componentDatas.Add(component.name, queue);
|
||||
}
|
||||
if (data.production_system.racks != null)
|
||||
{
|
||||
foreach (var component in data.production_system.racks)
|
||||
{
|
||||
var rack = rackPool.GetItem($"{component.name}");
|
||||
rack.componentType = ComponentType.Rack;
|
||||
rack.SetComponent(component);
|
||||
componentDatas.Add(component.name, rack);
|
||||
}
|
||||
}
|
||||
if (data.production_system.asrs != null)
|
||||
{
|
||||
foreach (var component in data.production_system.asrs)
|
||||
{
|
||||
var asrs = asrsPool.GetItem($"{component.name}");
|
||||
asrs.componentType = ComponentType.ASRS;
|
||||
asrs.SetComponent(component);
|
||||
componentDatas.Add(component.name, asrs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnUpdateData(IDataObject data,ComponentType type)
|
||||
{
|
||||
|
||||
if (data == null) return;
|
||||
|
||||
DataObject? obj = data as DataObject;
|
||||
if (obj == null) return;
|
||||
|
||||
Debug.Log($"OnUpdateData:{obj}, {obj["data"]}");
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case ComponentType.Source:
|
||||
SourceComponent? source = sourcePool.GetItem(obj.GetString("component_id")!);
|
||||
source.GetModelData(obj);
|
||||
break;
|
||||
case ComponentType.Sink:
|
||||
break;
|
||||
case ComponentType.ASRS:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private async UniTask InitializeSourcePoolAsync()
|
||||
{
|
||||
if (sourcePool != null) return;
|
||||
|
||||
var prefab = await Resources.LoadAsync<GameObject>(prefabPaths["source"]) as GameObject;
|
||||
if (prefab == null)
|
||||
{
|
||||
Debug.LogError($"Prefab not found at path: {prefabPaths["source"]}");
|
||||
return;
|
||||
}
|
||||
sourcePool = new GameObjectPool<SourceComponent>(prefab, transform);
|
||||
}
|
||||
|
||||
private async UniTask InitializeSinkPoolAsync()
|
||||
{
|
||||
if (sinkPool != null) return;
|
||||
|
||||
var prefab = await Resources.LoadAsync<GameObject>(prefabPaths["sink"]) as GameObject;
|
||||
if (prefab == null)
|
||||
{
|
||||
Debug.LogError($"Prefab not found at path: {prefabPaths["sink"]}");
|
||||
return;
|
||||
}
|
||||
sinkPool = new GameObjectPool<SinkComponent>(prefab, transform);
|
||||
}
|
||||
|
||||
private async UniTask InitializeAsrsPoolAsync()
|
||||
{
|
||||
if (asrsPool != null) return;
|
||||
|
||||
var prefab = await Resources.LoadAsync<GameObject>(prefabPaths["asrs"]) as GameObject;
|
||||
if (prefab == null)
|
||||
{
|
||||
Debug.LogError($"Prefab not found at path: {prefabPaths["asrs"]}");
|
||||
return;
|
||||
}
|
||||
asrsPool = new GameObjectPool<ASRSComponent>(prefab, transform);
|
||||
}
|
||||
|
||||
private void SpawnComponents(logicDetailData data)
|
||||
{
|
||||
foreach (var component in data.production_system.sources)
|
||||
{
|
||||
var source=sourcePool.GetItem($"{component.name}");
|
||||
source.componentType = ComponentType.Source;
|
||||
source.SetComponent(component);
|
||||
source.SetPosition();
|
||||
componentDatas.Add(component.name, source);
|
||||
}
|
||||
foreach (var component in data.production_system.sinks)
|
||||
{
|
||||
var sink = sinkPool.GetItem($"{component.name}");
|
||||
sink.componentType = ComponentType.Sink;
|
||||
sink.ChangeComponent(component);
|
||||
sink.SetPosition();
|
||||
componentDatas.Add(component.name, sink);
|
||||
}
|
||||
/*
|
||||
foreach (var component in data.production_system.asrs)
|
||||
{
|
||||
var asrs = asrsPool.GetItem($"{component.name}");
|
||||
asrs.componentType = ComponentType.ASRS;
|
||||
asrs.ChangeComponent(component);
|
||||
//asrs.SetPosition();
|
||||
componentDatas.Add(component.name, asrs);
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
8
Assets/Scripts/Simulator/Components/Conveyor.meta
Normal file
8
Assets/Scripts/Simulator/Components/Conveyor.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 12d78a9e5b17bbd478d373304c67471d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,229 @@
|
||||
using sc.modeling.splines.runtime;
|
||||
using System.Collections.Generic;
|
||||
using Unity.VisualScripting;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Splines;
|
||||
using UVC.Data.Core;
|
||||
|
||||
public enum conveyorType
|
||||
{
|
||||
partial,
|
||||
overall
|
||||
}
|
||||
public class ConveyorComponent : ComponentBase
|
||||
{
|
||||
public List<ConveyorTarget> ConveyorQueue = new List<ConveyorTarget>();
|
||||
public Stack<ConveyorTarget> WaitingQueue = new Stack<ConveyorTarget>();
|
||||
public List<SplineMesher> mesher = new List<SplineMesher>();
|
||||
SplineContainer spline;
|
||||
float duration = 3f;
|
||||
|
||||
// === 추가: 슬롯 기반 제어 ===
|
||||
public conveyorType type = conveyorType.overall;
|
||||
public int segmentCount = 10; // 칸 개수
|
||||
public float borderEpsilon = 1e-4f; // 경계 떨림 방지
|
||||
bool[] occupancy; // 프레임 점유 캐시
|
||||
|
||||
// Start is called once before the first execution of Update after the MonoBehaviour is created
|
||||
void Start()
|
||||
{
|
||||
spline = GetComponent<SplineContainer>();
|
||||
occupancy = new bool[Mathf.Max(1, segmentCount)];
|
||||
EntityManager.Instance.OnEntityDestroyed += UnSetEntity;
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
/*
|
||||
if (Input.GetKeyDown(KeyCode.G))
|
||||
{
|
||||
GameObject target = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
||||
var cTraget = target.AddComponent<ConveyorTarget>();
|
||||
cTraget.Progress = 0f;
|
||||
ConveyorQueue.Add(cTraget);
|
||||
}
|
||||
|
||||
if (Input.GetKeyDown(KeyCode.S))
|
||||
{
|
||||
// 출구에서 한 개 배출
|
||||
if (WaitingQueue.Count > 0)
|
||||
{
|
||||
var target = WaitingQueue.Pop();
|
||||
Destroy(target.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
if (type == conveyorType.overall)
|
||||
{
|
||||
if (WaitingQueue.Count < 1)
|
||||
{
|
||||
SetProgress(Time.deltaTime / duration);
|
||||
MoveOnConveyor();
|
||||
}
|
||||
}
|
||||
else // partial
|
||||
{
|
||||
SetProgress(Time.deltaTime / duration);
|
||||
MoveOnConveyor();
|
||||
}
|
||||
*/
|
||||
SetProgress(Time.deltaTime / duration);
|
||||
MoveOnConveyor();
|
||||
}
|
||||
|
||||
|
||||
void SetConveyorEntity(Entity entity)
|
||||
{
|
||||
var cTarget = entity.gameObject.GetComponent<ConveyorTarget>();
|
||||
cTarget.transform.SetParent(this.transform, worldPositionStays: false);
|
||||
cTarget.Progress = 0f;
|
||||
ConveyorQueue.Add(cTarget);
|
||||
}
|
||||
|
||||
public void SetModelData(DataObject datas)
|
||||
{
|
||||
var data = datas.GetDataObject("data");
|
||||
var entityId = data.GetString("entity_id_original");
|
||||
var entity = EntityManager.Instance.GetEntity(entityId,this);
|
||||
if (entity == null)
|
||||
{
|
||||
Debug.Log(entityId);
|
||||
Debug.Log(entity);
|
||||
}
|
||||
duration = (float)data.GetFloat("travel_time");
|
||||
SetConveyorEntity(entity);
|
||||
}
|
||||
|
||||
public void UnSetEntity(Entity entity)
|
||||
{
|
||||
var cTarget = entity.gameObject.GetComponent<ConveyorTarget>();
|
||||
if (ConveyorQueue.Contains(cTarget))
|
||||
{
|
||||
ConveyorQueue.Remove(cTarget);
|
||||
}
|
||||
}
|
||||
|
||||
public void UnSetModelData(DataObject datas)
|
||||
{
|
||||
var data = datas.GetDataObject("data");
|
||||
var entityId = data.GetString("entity_id_original");
|
||||
var entity = EntityManager.Instance.GetEntity(entityId,this);
|
||||
var cTarget = entity.gameObject.GetComponent<ConveyorTarget>();
|
||||
if (ConveyorQueue.Contains(cTarget))
|
||||
{
|
||||
ConveyorQueue.Remove(cTarget);
|
||||
}
|
||||
}
|
||||
void SetProgress(float progress)
|
||||
{
|
||||
if (ConveyorQueue.Count == 0) return;
|
||||
|
||||
if (type == conveyorType.overall)
|
||||
{
|
||||
// 기존: 전 화물 동일 진행
|
||||
foreach (var target in ConveyorQueue)
|
||||
{
|
||||
target.Progress += progress;
|
||||
if (target.Progress > 1f) target.Progress = 1f;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 슬롯 기반: 끝 칸이 막혀 있어도 압축 전진
|
||||
PartialAdvance(progress);
|
||||
}
|
||||
}
|
||||
|
||||
void MoveOnConveyor()
|
||||
{
|
||||
Spline s = spline.Splines[0];
|
||||
|
||||
for (int i = ConveyorQueue.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var target = ConveyorQueue[i];
|
||||
Vector3 localPos = SplineUtility.EvaluatePosition(s, target.Progress);
|
||||
target.transform.position = localPos;
|
||||
target.transform.position += new Vector3(0, 1f, -0.275f);
|
||||
|
||||
// 끝(1.0) 도달 → WaitingQueue로 이동(배출 대기)
|
||||
if (target.Progress >= 1f)
|
||||
{
|
||||
ConveyorQueue.RemoveAt(i);
|
||||
WaitingQueue.Push(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// === 한 칸씩 채우는 압축 로직 ===
|
||||
void PartialAdvance(float delta)
|
||||
{
|
||||
// 끝에 가까운 순서로 처리 → 뒤따르는 한계 계산이 정확
|
||||
ConveyorQueue.Sort((a, b) => b.Progress.CompareTo(a.Progress));
|
||||
|
||||
int cellCount = Mathf.Max(1, segmentCount);
|
||||
float cell = 1f / cellCount;
|
||||
|
||||
if (occupancy == null || occupancy.Length != cellCount)
|
||||
occupancy = new bool[cellCount];
|
||||
System.Array.Clear(occupancy, 0, occupancy.Length);
|
||||
|
||||
// ★ 중요: 출구가 막혀 있으면 마지막 칸을 항상 점유된 것으로 간주
|
||||
if (WaitingQueue.Count > 0)
|
||||
occupancy[cellCount - 1] = true;
|
||||
|
||||
foreach (var t in ConveyorQueue)
|
||||
{
|
||||
float desired = Mathf.Min(1f, t.Progress + delta);
|
||||
|
||||
int currCell = CellIndex(t.Progress, cellCount);
|
||||
int wantCell = CellIndex(desired, cellCount);
|
||||
|
||||
int allowedCell = currCell;
|
||||
// 앞칸이 비어 있으면 한 칸 더, 또 비어 있으면 또 한 칸… 식으로 최대 전진
|
||||
for (int c = currCell + 1; c <= wantCell && c < cellCount; c++)
|
||||
{
|
||||
if (occupancy[c]) break;
|
||||
allowedCell = c;
|
||||
}
|
||||
|
||||
// 허용 칸의 끝 경계 직전까지
|
||||
float maxProg = Mathf.Min(desired, CellEnd(allowedCell, cellCount) - borderEpsilon);
|
||||
|
||||
// 마지막 칸이면 1.0까지 허용(도달 시 MoveOnConveyor에서 대기열로 이동)
|
||||
if (allowedCell == cellCount - 1)
|
||||
maxProg = Mathf.Min(desired, 1f);
|
||||
|
||||
t.Progress = Mathf.Clamp01(maxProg);
|
||||
|
||||
// 점유 표시
|
||||
int newCell = CellIndex(t.Progress, cellCount);
|
||||
occupancy[newCell] = true;
|
||||
}
|
||||
}
|
||||
|
||||
int CellIndex(float progress, int cellCount)
|
||||
{
|
||||
float cell = 1f / cellCount;
|
||||
int idx = Mathf.FloorToInt(progress / cell);
|
||||
return Mathf.Clamp(idx, 0, cellCount - 1);
|
||||
}
|
||||
float CellEnd(int cellIndex, int cellCount)
|
||||
{
|
||||
float cell = 1f / cellCount;
|
||||
return (cellIndex + 1) * cell;
|
||||
}
|
||||
|
||||
void UpdateKnot(BezierKnot knot, int index, int splineIndex)
|
||||
{
|
||||
spline.Splines[splineIndex].SetKnot(index, knot);
|
||||
}
|
||||
|
||||
void UpdateMesh()
|
||||
{
|
||||
foreach (var m in mesher)
|
||||
{
|
||||
m.Rebuild();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b3e67873ff4016a4b979185f7c352619
|
||||
217
Assets/Scripts/Simulator/Components/Conveyor/ConveyorManager.cs
Normal file
217
Assets/Scripts/Simulator/Components/Conveyor/ConveyorManager.cs
Normal file
@@ -0,0 +1,217 @@
|
||||
using sc.modeling.splines.runtime;
|
||||
using Simulator.Data;
|
||||
using Simulator.Data.Transport;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Splines;
|
||||
using UVC.Core;
|
||||
using UVC.Data;
|
||||
using UVC.Data.Core;
|
||||
using UVC.Data.Mqtt;
|
||||
public class ConveyorPathMeta : MonoBehaviour
|
||||
{
|
||||
public string Name;
|
||||
public string From;
|
||||
public string To;
|
||||
public float Speed;
|
||||
public float Length;
|
||||
public int Capacity;
|
||||
}
|
||||
|
||||
public class ConveyorManager : SingletonScene<ConveyorManager>
|
||||
{
|
||||
[Header("Root & Visual")]
|
||||
[SerializeField] private Transform rootParent; // 생성될 경로의 부모(없으면 빌더 자신)
|
||||
[SerializeField] private float tangentScale = 0.25f; // 곡률(탄젠트 길이 비율)
|
||||
[SerializeField]
|
||||
ConveyorComponent splineprefab;
|
||||
[SerializeField]
|
||||
GameObject cube;
|
||||
|
||||
public Dictionary<string, ConveyorNode> Nodes = new Dictionary<string, ConveyorNode>();
|
||||
public Dictionary<(string,string), ConveyorComponent> Paths = new Dictionary<(string, string), ConveyorComponent>();
|
||||
public DataMapper conveyorStartDataMapper;
|
||||
public DataMapper conveyorCompleteDataMapper;
|
||||
|
||||
protected override void Init()
|
||||
{
|
||||
var conveyorStartDataMask = new DataMask();
|
||||
conveyorStartDataMask.ObjectName = "conveyor"; // AGV 객체의 이름을 설정합니다.
|
||||
conveyorStartDataMask.ObjectIdKey = "component_id"; // AGV의 고유 식별자로 사용할 키를 설정합니다.
|
||||
conveyorStartDataMask["component_id"] = "";
|
||||
conveyorStartDataMask["event_name"] = "";
|
||||
conveyorStartDataMask["timestamp"] = new DateTime();
|
||||
conveyorStartDataMask["data"] = new DataMask()
|
||||
{
|
||||
["entity_id_original"] = "",
|
||||
["start_node"] = "",
|
||||
["end_node"] = "",
|
||||
["travel_time"]=0.0f
|
||||
};
|
||||
conveyorStartDataMapper=new DataMapper(conveyorStartDataMask);
|
||||
|
||||
var conveyorCompleteDataMask = new DataMask();
|
||||
conveyorCompleteDataMask.ObjectName = "conveyor"; // AGV 객체의 이름을 설정합니다.
|
||||
conveyorCompleteDataMask.ObjectIdKey = "component_id"; // AGV의 고유 식별자로 사용할 키를 설정합니다.
|
||||
conveyorCompleteDataMask["component_id"] = "";
|
||||
conveyorCompleteDataMask["event_name"] = "";
|
||||
conveyorCompleteDataMask["timestamp"] = new DateTime();
|
||||
conveyorCompleteDataMask["data"] = new DataMask()
|
||||
{
|
||||
["entity_id_original"] = "",
|
||||
["start_node"] = "",
|
||||
["end_node"] = "",
|
||||
};
|
||||
conveyorCompleteDataMapper = new DataMapper(conveyorCompleteDataMask);
|
||||
}
|
||||
|
||||
public void Build(List<ConveyorDataClass> datas)
|
||||
{
|
||||
if (datas==null||datas.Count <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var data = datas[0];
|
||||
if (rootParent == null) rootParent = transform;
|
||||
|
||||
if (data.nodes != null)
|
||||
{
|
||||
foreach (var n in data.nodes)
|
||||
{
|
||||
Nodes[n.name] = n;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var path in data.paths)
|
||||
{
|
||||
CreatePathGOWithSpline(path, Nodes[path.from_node], Nodes[path.to_node]);
|
||||
}
|
||||
SpawnJunction();
|
||||
|
||||
SubscribeConveyor(data);
|
||||
}
|
||||
|
||||
public void SubscribeConveyor(ConveyorDataClass data)
|
||||
{
|
||||
DataRepository.Instance.MqttReceiver.AddTopic($"simulation/{SimulationConfig.SimulationCode}/components/+/{data.name}/+/node_started");
|
||||
var mqttConfigConveyorStart = new MqttSubscriptionConfig($"simulation/{SimulationConfig.SimulationCode}/components/+/{data.name}/+/node_started");
|
||||
mqttConfigConveyorStart.SetDataMapper(conveyorStartDataMapper);
|
||||
mqttConfigConveyorStart.SetHandler(OnSetData);
|
||||
DataRepository.Instance.MqttReceiver.Add(mqttConfigConveyorStart);
|
||||
/*
|
||||
DataRepository.Instance.MqttReceiver.AddTopic($"simulation/{SimulationConfig.SimulationCode}/components/+/{data.name}/+/node_completed");
|
||||
var mqttConfigConveyorComplete = new MqttSubscriptionConfig($"simulation/{SimulationConfig.SimulationCode}/components/+/{data.name}/+/node_completed");
|
||||
mqttConfigConveyorComplete.SetDataMapper(conveyorCompleteDataMapper);
|
||||
mqttConfigConveyorComplete.SetHandler(OnUnSetData);
|
||||
DataRepository.Instance.MqttReceiver.Add(mqttConfigConveyorComplete);
|
||||
*/
|
||||
}
|
||||
|
||||
private void CreatePathGOWithSpline(ConveyorPath path, ConveyorNode fromNode, ConveyorNode toNode)
|
||||
{
|
||||
TryGetNodePos(fromNode.name, out Vector3 fromPos);
|
||||
TryGetNodePos(toNode.name, out Vector3 toPos);
|
||||
string goName = path.name;
|
||||
|
||||
var go = Instantiate(splineprefab);
|
||||
Paths.Add((fromNode.name,toNode.name), go);
|
||||
go.transform.SetParent(rootParent, worldPositionStays: false);
|
||||
|
||||
var container = go.GetComponent<SplineContainer>();
|
||||
|
||||
var spline = new Spline();
|
||||
|
||||
Vector3 dir = (toPos - fromPos);
|
||||
float dist = dir.magnitude;
|
||||
Vector3 dirNorm = dist > 1e-5f ? dir / dist : Vector3.forward;
|
||||
|
||||
Vector3 tan = dirNorm * dist * Mathf.Clamp01(tangentScale);
|
||||
|
||||
var k0 = new BezierKnot(fromPos)
|
||||
{
|
||||
TangentOut = tan,
|
||||
TangentIn = Vector3.zero
|
||||
};
|
||||
|
||||
var k1 = new BezierKnot(toPos)
|
||||
{
|
||||
TangentIn = -tan,
|
||||
TangentOut = Vector3.zero
|
||||
};
|
||||
|
||||
spline.Add(k0, TangentMode.Broken);
|
||||
spline.Add(k1, TangentMode.Broken);
|
||||
|
||||
container.Spline = spline;
|
||||
var mesher = go.GetComponent<SplineMesher>();
|
||||
if(string.Equals(fromNode.node_type, "junction"))
|
||||
{
|
||||
mesher.DetachCap(0);
|
||||
}
|
||||
if (string.Equals(toNode.node_type, "junction"))
|
||||
{
|
||||
mesher.DetachCap(1);
|
||||
}
|
||||
mesher.UpdateCaps();
|
||||
mesher.Rebuild();
|
||||
}
|
||||
|
||||
private void SpawnJunction()
|
||||
{
|
||||
foreach(var node in Nodes)
|
||||
{
|
||||
if (string.Equals(node.Value.node_type, "junction"))
|
||||
{
|
||||
var module = Instantiate(cube);
|
||||
module.transform.position=ToVector3(node.Value.position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSetData(IDataObject idata)
|
||||
{
|
||||
if (idata == null) return;
|
||||
|
||||
DataObject? obj = idata as DataObject;
|
||||
if (obj == null) return;
|
||||
//Debug.Log($"OnUpdateData:{obj}, {obj["data"]}");
|
||||
var data = obj.GetDataObject("data");
|
||||
var fromNodeName = data.GetString("start_node");
|
||||
var toNodeName = data.GetString("end_node");
|
||||
ConveyorComponent? conveyor = Paths[(fromNodeName,toNodeName)];
|
||||
conveyor.SetModelData(obj);
|
||||
}
|
||||
|
||||
private void OnUnSetData(IDataObject idata)
|
||||
{
|
||||
if (idata == null) return;
|
||||
|
||||
DataObject? obj = idata as DataObject;
|
||||
if (obj == null) return;
|
||||
//Debug.Log($"OnUpdateData:{obj}, {obj["data"]}");
|
||||
var data = obj.GetDataObject("data");
|
||||
var fromNodeName = data.GetString("start_node");
|
||||
var toNodeName = data.GetString("end_node");
|
||||
ConveyorComponent? conveyor = Paths[(fromNodeName, toNodeName)];
|
||||
conveyor.UnSetModelData(obj);
|
||||
}
|
||||
|
||||
private bool TryGetNodePos(string nodeName, out Vector3 pos)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(nodeName) && Nodes.TryGetValue(nodeName, out ConveyorNode node))
|
||||
{
|
||||
pos = ToVector3(node.position);
|
||||
return true;
|
||||
}
|
||||
|
||||
pos = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
private Vector3 ToVector3(Position p)
|
||||
{
|
||||
return new Vector3(p.x, p.z, p.y);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 677f767b766c8c14b90b1ea42b7b89fe
|
||||
@@ -0,0 +1,6 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class ConveyorTarget : MonoBehaviour
|
||||
{
|
||||
public float Progress = 0f;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c6e8eb3ed7788e04fbee1a2627fccd43
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d071f7d2c7896784e9ba89d0e2cfd860
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,34 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Simulator.Data
|
||||
{
|
||||
[Serializable]
|
||||
public class ConveyorDataClass
|
||||
{
|
||||
public string name;
|
||||
public List<ConveyorNode> nodes;
|
||||
public List<ConveyorPath> paths;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ConveyorNode
|
||||
{
|
||||
public string name;
|
||||
public int capacity;
|
||||
public Position position;
|
||||
public string node_type;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ConveyorPath
|
||||
{
|
||||
public string name;
|
||||
public float speed;
|
||||
public float length;
|
||||
public string to_node;
|
||||
public int capacity;
|
||||
public string from_node;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 556ab1c161f61e147b353f5e5770feee
|
||||
59
Assets/Scripts/Simulator/Components/Conveyor/SplineTarget.cs
Normal file
59
Assets/Scripts/Simulator/Components/Conveyor/SplineTarget.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.Splines;
|
||||
|
||||
public class SplineTarget : MonoBehaviour
|
||||
{
|
||||
public BezierKnot knot;
|
||||
public event Action<BezierKnot, int, int> onKnotChanged;
|
||||
public event Action onChangeFinished;
|
||||
bool isClick;
|
||||
public int index;
|
||||
public int splineIndex;
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
/*
|
||||
if (isClick)
|
||||
{
|
||||
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
|
||||
RaycastHit hit;
|
||||
if (Physics.Raycast(ray, out hit))
|
||||
{
|
||||
transform.position = hit.point + new Vector3(0, 2, 0);
|
||||
knot.Position = transform.position - new Vector3(0, 2, 0);
|
||||
onKnotChanged?.Invoke(knot, index, splineIndex);
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
public void Init(BezierKnot knot, int index, int splineIndex = 0)
|
||||
{
|
||||
this.knot = knot;
|
||||
this.index = index;
|
||||
this.splineIndex = splineIndex;
|
||||
}
|
||||
|
||||
/*
|
||||
public void OnPointerDown(PointerEventData eventData)
|
||||
{
|
||||
isClick = true;
|
||||
transform.GetComponent<Collider>().enabled = false;
|
||||
}
|
||||
|
||||
public void OnPointerUp(PointerEventData eventData)
|
||||
{
|
||||
isClick = false;
|
||||
transform.GetComponent<Collider>().enabled = true;
|
||||
onChangeFinished?.Invoke();
|
||||
}
|
||||
|
||||
public void OnPointerClick(PointerEventData eventData)
|
||||
{
|
||||
Debug.Log("click");
|
||||
}
|
||||
*/
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 20514b0c6cbc9f14e9bb98a1734bdd22
|
||||
@@ -8,5 +8,6 @@ namespace Simulator.Data
|
||||
{
|
||||
public string name;
|
||||
public string label;
|
||||
public rack_layout asrs_layout;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,6 @@ namespace Simulator.Data
|
||||
{
|
||||
public class ComponentDataBase
|
||||
{
|
||||
public physical physical;
|
||||
public Physical physical;
|
||||
}
|
||||
}
|
||||
@@ -9,9 +9,7 @@ namespace Simulator.Data
|
||||
public string name;
|
||||
public string label;
|
||||
public int capacity;
|
||||
public bool is_unlimited;
|
||||
public string policy;
|
||||
public float wait_cost;
|
||||
public float cost_time;
|
||||
public string queue_type;
|
||||
public bool backpressure_enabled;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Simulator.Data
|
||||
{
|
||||
[Serializable]
|
||||
public class RackDataClass : ComponentDataBase
|
||||
{
|
||||
public string name;
|
||||
public string label;
|
||||
//public List<string> inputs=new List<string>();
|
||||
//public List<string> outputs=new List<string>();
|
||||
public rack_layout rack_layout=new rack_layout();
|
||||
public string connected_input_node;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class rack_layout
|
||||
{
|
||||
public float x;
|
||||
public float y;
|
||||
public float z;
|
||||
public float x_length;
|
||||
public float y_length;
|
||||
public float z_length;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b44e9bb545fa72c429abfd451e113d07
|
||||
@@ -1,57 +1,95 @@
|
||||
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Asn1.Mozilla;
|
||||
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Bcpg;
|
||||
using Newtonsoft.Json;
|
||||
using Simulator.Data.Transport;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Simulator.Data
|
||||
{
|
||||
[Serializable]
|
||||
public class totaljson
|
||||
public class Totaljson
|
||||
{
|
||||
public int status;
|
||||
public string code;
|
||||
public logicData data;
|
||||
public LogicData data;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class logicData
|
||||
public class LogicData
|
||||
{
|
||||
public int id;
|
||||
public logicDetailData data;
|
||||
public LogicDetailData data;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class logicDetailData
|
||||
public class LogicDetailData
|
||||
{
|
||||
public string name;
|
||||
public infrastructure infrastructure;
|
||||
public production_system production_system;
|
||||
public Infrastructure infrastructure;
|
||||
public Transport_System transport_system;
|
||||
public Production_System production_system;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class infrastructure
|
||||
public class Infrastructure
|
||||
{
|
||||
public List<QueueDataClass> queues;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class production_system
|
||||
public class Transport_System
|
||||
{
|
||||
public List<Node_Networks> node_networks=new List<Node_Networks>();
|
||||
public List<Transport_Manager> transport_managers=new List<Transport_Manager>();
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Node_Networks
|
||||
{
|
||||
public List<NodeDataClass> nodes= new List<NodeDataClass>();
|
||||
public List<PathDataClass> paths=new List<PathDataClass>();
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Transport_Manager
|
||||
{
|
||||
public string label;
|
||||
public string manager_id;
|
||||
public string network_id;
|
||||
public string manager_type;
|
||||
public List<Vehicle_Fleet> vehicle_fleets;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Vehicle_Fleet
|
||||
{
|
||||
public string label;
|
||||
public string fleet_id;
|
||||
public List<AGVData> vehicles;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Production_System
|
||||
{
|
||||
public List<SourceDataClass> sources;
|
||||
public List<SinkDataClass> sinks;
|
||||
public List<ASRSDataClass> asrs;
|
||||
public List<RackDataClass> racks;
|
||||
public List<ConveyorDataClass> conveyors;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class physical
|
||||
public class Physical
|
||||
{
|
||||
public position position = new position();
|
||||
public dimensions dimensions = new dimensions();
|
||||
public Position position = new Position();
|
||||
public Dimensions dimensions = new Dimensions();
|
||||
public float orientation;
|
||||
public access_points access_points = new access_points();
|
||||
public Access_Points access_points = new Access_Points();
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class position
|
||||
public class Position
|
||||
{
|
||||
public float x = 0.0f;
|
||||
public float y = 0.0f;
|
||||
@@ -59,7 +97,7 @@ namespace Simulator.Data
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class dimensions
|
||||
public class Dimensions
|
||||
{
|
||||
public float width = 0.0f;
|
||||
public float height = 0.0f;
|
||||
@@ -67,21 +105,21 @@ namespace Simulator.Data
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class access_points
|
||||
public class Access_Points
|
||||
{
|
||||
public input input;
|
||||
public output output;
|
||||
public Input input;
|
||||
public Output output;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class input
|
||||
public class Input
|
||||
{
|
||||
public position local_position = new position();
|
||||
public Position local_position = new Position();
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class output
|
||||
public class Output
|
||||
{
|
||||
public position local_position = new position();
|
||||
public Position local_position = new Position();
|
||||
}
|
||||
}
|
||||
38
Assets/Scripts/Simulator/Components/QueueComponent.cs
Normal file
38
Assets/Scripts/Simulator/Components/QueueComponent.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using Simulator.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Unity.VisualScripting;
|
||||
using UnityEngine;
|
||||
using UVC.Data.Core;
|
||||
|
||||
namespace Simulator.Data
|
||||
{
|
||||
public class QueueComponent : ComponentBase
|
||||
{
|
||||
QueueDataClass queueData;
|
||||
public GameObject entitySocket;
|
||||
public void SetComponent(QueueDataClass queueData)
|
||||
{
|
||||
this.queueData = queueData;
|
||||
data = queueData;
|
||||
}
|
||||
|
||||
public override void GetModelData(DataObject modelData)
|
||||
{
|
||||
Debug.Log("실행되어선 안되는 함수입니다");
|
||||
}
|
||||
|
||||
public void PlaceEntity(Entity entity)
|
||||
{
|
||||
if (entitySocket == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
entity.transform.SetParent(entitySocket.transform, worldPositionStays: false);
|
||||
float stepZ = EntityManager.Instance.ObjectSize.z;
|
||||
entity.transform.localPosition = new Vector3(0f, 0f, 0f);
|
||||
entity.transform.localRotation = Quaternion.identity;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a4c1c358f5a165d4e8cf781ec680c15a
|
||||
72
Assets/Scripts/Simulator/Components/RackComponent.cs
Normal file
72
Assets/Scripts/Simulator/Components/RackComponent.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
using Simulator.Model;
|
||||
using System.Collections.Generic;
|
||||
using Unity.VisualScripting;
|
||||
using UnityEngine;
|
||||
using UVC.Data.Core;
|
||||
|
||||
namespace Simulator.Data {
|
||||
public class RackComponent : ComponentBase
|
||||
{
|
||||
public GameObject cellPrefab;
|
||||
public RackDataClass rackData;
|
||||
|
||||
public Dictionary<(int x,int y, int z),CellComponent> cellComponents=new Dictionary<(int x, int y, int z), CellComponent>();
|
||||
public List<GameObject> entitys=new List<GameObject>();
|
||||
|
||||
public void SetComponent(RackDataClass data)
|
||||
{
|
||||
this.data = data;
|
||||
rackData= data;
|
||||
SpawnCell(data.rack_layout,false);
|
||||
}
|
||||
|
||||
public override void GetModelData(DataObject modelData)
|
||||
{
|
||||
var datas = modelData.GetDataObject("data");
|
||||
var entityid = datas.GetString("entity_id");
|
||||
List<string> entityids = new List<string>() { entityid };
|
||||
var coordinates = datas.GetDataObject("coordinates");
|
||||
var x = (int)coordinates.GetInt("x");
|
||||
var y = (int)coordinates.GetInt("y");
|
||||
var z = (int)coordinates.GetInt("z");
|
||||
var entity=EntityManager.Instance.GetEntities(entityids,this);
|
||||
entity[0].transform.SetParent(cellComponents[(x, z, y)].Socket.transform);
|
||||
entity[0].transform.localPosition = new Vector3(0, 0, 0);
|
||||
entitys.Add(entity[0].gameObject);
|
||||
}
|
||||
|
||||
public void SpawnCell(rack_layout layout,bool asrs)
|
||||
{
|
||||
Vector3 center = new Vector3(layout.x * layout.x_length / 2f, 0f, layout.y * layout.y_length / 2f);
|
||||
transform.position = center;
|
||||
|
||||
for (int z = 0; z < layout.z; z++)
|
||||
{
|
||||
for(int y=0;y<layout.y; y++)
|
||||
{
|
||||
for(int x=0;x<layout.x; x++)
|
||||
{
|
||||
var cell = Instantiate(cellPrefab);
|
||||
cell.transform.localScale=new Vector3(layout.x_length, layout.z_length, layout.y_length);
|
||||
cell.transform.parent = this.transform;
|
||||
cell.transform.position = new Vector3(layout.x_length * x*0.55f, layout.z_length * z * 0.55f, layout.y_length * y * 0.55f);
|
||||
cellComponents.Add((x, z, y), cell.GetComponent<CellComponent>());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (asrs)
|
||||
{
|
||||
var pickcell = Instantiate(cellPrefab);
|
||||
pickcell.transform.localScale = new Vector3(layout.x_length, layout.z_length, layout.y_length);
|
||||
pickcell.transform.parent = this.transform;
|
||||
pickcell.transform.position = new Vector3(layout.x_length * -0.55f, layout.z_length * 0, layout.y_length * 0);
|
||||
cellComponents.Add((-1, 0, 0), pickcell.GetComponent<CellComponent>());
|
||||
var dropcell = Instantiate(cellPrefab);
|
||||
dropcell.transform.localScale = new Vector3(layout.x_length, layout.z_length, layout.y_length);
|
||||
dropcell.transform.parent = this.transform;
|
||||
dropcell.transform.position = new Vector3(layout.x_length * (layout.x+ 0.55f), layout.z_length * 0, layout.y_length * 0);
|
||||
cellComponents.Add(((int)(layout.x + 1), 0, 0), dropcell.GetComponent<CellComponent>());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 10cc8432a692c434b96251d427314548
|
||||
@@ -2,12 +2,14 @@ using Simulator.Data;
|
||||
using Simulator.Model;
|
||||
using UnityEngine;
|
||||
using UVC.Data.Core;
|
||||
using System.Collections.Generic;
|
||||
using Unity.VisualScripting;
|
||||
|
||||
public class SinkComponent : ComponentBase
|
||||
{
|
||||
public SinkDataClass sourceData = new SinkDataClass();
|
||||
public SinkModelData sinkModelData = new SinkModelData();
|
||||
public void ChangeComponent(SinkDataClass sourceData)
|
||||
public void SetComponent(SinkDataClass sourceData)
|
||||
{
|
||||
this.sourceData = sourceData;
|
||||
data = sourceData;
|
||||
@@ -15,6 +17,9 @@ public class SinkComponent : ComponentBase
|
||||
|
||||
public override void GetModelData(DataObject modelData)
|
||||
{
|
||||
sinkModelData.component_id=modelData.GetString("component_id");
|
||||
sinkModelData.component_id = modelData.GetString("component_id");
|
||||
var datas = modelData.GetDataObject("data");
|
||||
var sinkdata = datas["entity_ids"].ConvertTo<List<string>>();
|
||||
EntityManager.Instance.DestroyEnity(sinkdata);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,19 @@
|
||||
using Simulator.Data;
|
||||
using Simulator.Model;
|
||||
using System.Collections.Generic;
|
||||
using Unity.VisualScripting;
|
||||
using UnityEngine;
|
||||
using UVC.Data.Core;
|
||||
|
||||
|
||||
public class SourceComponent : ComponentBase
|
||||
{
|
||||
public SourceDataClass sourceData=new SourceDataClass();
|
||||
public SourceModelData sourceModelData=new SourceModelData();
|
||||
SourceDataClass sourceData=new SourceDataClass();
|
||||
SourceModelData sourceModelData=new SourceModelData();
|
||||
public List<GameObject> entitySocket;
|
||||
List<Entity> possessEntities=new List<Entity>();
|
||||
public int count=0;
|
||||
public int index = 0;
|
||||
|
||||
public void SetComponent(SourceDataClass sourceData)
|
||||
{
|
||||
@@ -15,8 +21,109 @@ public class SourceComponent : ComponentBase
|
||||
data = sourceData;
|
||||
}
|
||||
|
||||
void IncreaseCount()
|
||||
{
|
||||
count++;
|
||||
if (count >= entitySocket.Count)
|
||||
{
|
||||
index++;
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void PlaceNext(Entity entity)
|
||||
{
|
||||
if (entitySocket == null || entitySocket.Count == 0)
|
||||
{
|
||||
Debug.LogWarning("[SourceComponent] entitySocket이 비어있습니다. 배치를 건너뜁니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 현재 소켓이 비어있으면 다음 유효 소켓을 찾는다(안전장치)
|
||||
int guard = 0;
|
||||
while ((entitySocket[count] == null) && guard < entitySocket.Count)
|
||||
{
|
||||
IncreaseCount();
|
||||
guard++;
|
||||
}
|
||||
|
||||
if (entitySocket[count] == null)
|
||||
{
|
||||
Debug.LogWarning("[SourceComponent] 사용 가능한 소켓이 없습니다. 배치를 건너뜁니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 부모/로컬 트랜스폼 설정
|
||||
entity.transform.SetParent(entitySocket[count].transform, worldPositionStays: false);
|
||||
|
||||
// Z방향으로 적층(원래 로직 유지)
|
||||
float stepZ = EntityManager.Instance.ObjectSize.z;
|
||||
entity.transform.localPosition = new Vector3(0f, 0f, stepZ * index);
|
||||
entity.transform.localRotation = Quaternion.identity;
|
||||
|
||||
if (!possessEntities.Contains(entity))
|
||||
possessEntities.Add(entity);
|
||||
IncreaseCount();
|
||||
}
|
||||
|
||||
public override void GetModelData(DataObject modelData)
|
||||
{
|
||||
sourceModelData.total_entity = modelData.GetDataObject("data").GetInt("total_entity")??0;
|
||||
// total_entity 업데이트(참조용)
|
||||
sourceModelData.total_entity = modelData.GetDataObject("data").GetInt("total_entity") ?? 0;
|
||||
|
||||
// entity_ids -> 여러 개 동시 스폰
|
||||
var datas = modelData.GetDataObject("data");
|
||||
var entityIds = datas["entity_ids"].ConvertTo<List<string>>();
|
||||
|
||||
if (entityIds == null || entityIds.Count == 0)
|
||||
{
|
||||
Debug.LogWarning("[SourceComponent] entity_ids가 비어있습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 여러 개를 한 번에 스폰
|
||||
var entities = EntityManager.Instance.SpawnEntites(entityIds,this);
|
||||
if (entities == null || entities.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 배치
|
||||
foreach (var e in entities)
|
||||
{
|
||||
if (e == null) continue;
|
||||
PlaceNext(e);
|
||||
}
|
||||
|
||||
// (선택) 총계 갱신
|
||||
// sourceModelData.total_entity += entities.Count; // 필요 시 해제
|
||||
}
|
||||
|
||||
public override void DecreaseEntity(Entity entity)
|
||||
{
|
||||
possessEntities.Remove(entity);
|
||||
ReflowEntities();
|
||||
}
|
||||
|
||||
public void ReflowEntities()
|
||||
{
|
||||
// 위치 인덱스 초기화
|
||||
count = 0;
|
||||
index = 0;
|
||||
|
||||
// 🔑 핵심: 스냅샷을 만든 뒤 원본을 비우고, 스냅샷 기반으로 재배치
|
||||
// 이렇게 하면 순회 중 리스트 수정으로 인한 예외가 발생하지 않습니다.
|
||||
var snapshot = new List<Entity>(possessEntities.Count);
|
||||
foreach (var e in possessEntities)
|
||||
{
|
||||
if (e != null) snapshot.Add(e);
|
||||
}
|
||||
|
||||
possessEntities.Clear();
|
||||
|
||||
foreach (var e in snapshot)
|
||||
{
|
||||
PlaceNext(e); // 여기서 다시 possessEntities에 안전하게 채워짐
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user