TwinAgentAutoProcessor 가중치 완료조건 및 Entity별 터미널 로그 분리

- CompletionResult enum 추가 (Complete/Warning/Error)로 완료 상태 분류
- WeightedCompletion으로 SubStep별 가중치 기반 랜덤 완료 조건 구현
  - Server: OctopusHub 75% / NotFound 25%(Error, 중지)
  - Protocol: API 75% / MQTT 25%
  - ServerStatusCheck: Done 85% / Busy 10%(Warning, 중지) / Error 5%(중지)
  - Speed: 5~120ms 80% / "300ms~" 20%
- IsFullPipelineCompleted 속성으로 전체 파이프라인 완료 여부 판별
- MessageHistory 버퍼로 Entity별 터미널 로그 이력 관리
- Entity 전환 시 ClearAllProcessorScopedBindings()로 이전 UI 바인딩 해제
- EntityPropertyAdapter에서 PropertyItem 복원과 터미널 복원 순서 분리

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
wsh
2026-02-10 15:58:31 +09:00
parent 9e9314d32d
commit f840158bbf
9 changed files with 4186 additions and 734 deletions

View File

@@ -65,6 +65,11 @@ namespace OCTOPUS_TWIN
public bool IsRouteActive => routeSequence != null && routeSequence.IsActive();
private Sequence routeSequence;
private CameraRoute currentRoute;
public bool IsRouteRunning { get; private set; }
public Action onRouteComplete;
public CameraRoute route;
public bool IsClickUI
{
get
@@ -150,6 +155,10 @@ namespace OCTOPUS_TWIN
private void LateUpdate()
{
if (Input.GetKeyDown(KeyCode.Q))
{
SetRoute(route);
}
//UI <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ī<>޶<EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>
if (IsClickUI || IsOnTheUI)
return;
@@ -232,11 +241,11 @@ namespace OCTOPUS_TWIN
currentAzimuth += input.mouseX * rotateSpeed;
currentAzimuth %= 360;
//if(viewMode == ViewMode.PerspectiveView)
//{
// currentElevation -= input.mouseY * rotateSpeed;
// currentElevation = Mathf.Clamp(currentElevation, minElevation, maxElevation);
//}
if(viewMode == ViewMode.PerspectiveView)
{
currentElevation -= input.mouseY * rotateSpeed;
currentElevation = Mathf.Clamp(currentElevation, minElevation, maxElevation);
}
isRotateOperation = true;
}
@@ -354,71 +363,122 @@ namespace OCTOPUS_TWIN
throw new NotImplementedException();
}
public void SetRoute(CameraRoute route)
{
StopRoute();
currentRoute = route;
var points = currentRoute.movePoints;
if (points == null || points.Length == 0) return;
IsRouteRunning = true;
Enable = false;
routeSequence = DOTween.Sequence();
camera.transform.position = points[0].transform.position;
camera.transform.rotation = points[0].transform.rotation;
for (int i = 0; i < route.PointCount; i++)
if (points.Length == 1)
{
var (position, point) = route.GetWaypoint(i);
float elev = point != null ? point.elevation : currentElevation;
float azi = point != null ? point.azimuth : currentAzimuth;
float dist = point != null ? point.distance : currentDistance;
float dur = point != null ? point.duration : 0.5f;
float wait = point != null ? point.waitTime : 0f;
routeSequence.Append(
DOTween.To(() => nextPosition, x => nextPosition = x, position, dur));
routeSequence.Join(
DOTween.To(() => currentElevation, x => currentElevation = x, elev, dur));
routeSequence.Join(
DOTween.To(() => currentAzimuth, x => currentAzimuth = x, azi, dur));
routeSequence.Join(
DOTween.To(() => currentDistance, x => currentDistance = x, dist, dur));
if (wait > 0f)
routeSequence.AppendInterval(wait);
CompleteRoute();
return;
}
if (route.loop)
routeSequence.SetLoops(-1, LoopType.Restart);
int segCount = points.Length - 1;
float[] segDurations = new float[segCount];
float totalDuration = 0f;
for (int i = 0; i < segCount; i++)
{
float dist = Vector3.Distance(points[i].transform.position, points[i + 1].transform.position);
float spd = points[i + 1].speed > 0f ? points[i + 1].speed : moveSpeed;
segDurations[i] = spd > 0f ? dist / spd : 1f;
totalDuration += segDurations[i];
}
routeSequence.OnUpdate(LastPositioning);
routeSequence.OnKill(() => Enable = true);
float[] segEnd = new float[segCount];
float acc = 0f;
for (int i = 0; i < segCount; i++)
{
acc += segDurations[i];
segEnd[i] = acc / totalDuration;
}
Vector3[] pos = new Vector3[points.Length];
Quaternion[] rot = new Quaternion[points.Length];
for (int i = 0; i < points.Length; i++)
{
pos[i] = points[i].transform.position;
rot[i] = points[i].transform.rotation;
}
routeSequence = DOTween.Sequence();
routeSequence.Append(
DOVirtual.Float(0f, 1f, totalDuration, t =>
{
int seg = segCount - 1;
for (int i = 0; i < segEnd.Length; i++)
{
if (t <= segEnd[i]) { seg = i; break; }
}
float start = seg > 0 ? segEnd[seg - 1] : 0f;
float localT = Mathf.InverseLerp(start, segEnd[seg], t);
int idx = seg + 1;
Vector3 p0 = (idx >= 2) ? pos[idx - 2] : pos[idx - 1];
Vector3 p1 = pos[idx - 1];
Vector3 p2 = pos[idx];
Vector3 p3 = (idx + 1 < pos.Length) ? pos[idx + 1] : pos[idx];
camera.transform.position = CatmullRom(p0, p1, p2, p3, localT);
camera.transform.rotation = Quaternion.Slerp(rot[idx - 1], rot[idx], localT);
}).SetEase(Ease.Linear));
routeSequence.OnComplete(CompleteRoute);
}
private static Vector3 CatmullRom(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t)
{
float t2 = t * t;
float t3 = t2 * t;
return 0.5f * (
2f * p1 +
(-p0 + p2) * t +
(2f * p0 - 5f * p1 + 4f * p2 - p3) * t2 +
(-p0 + 3f * p1 - 3f * p2 + p3) * t3
);
}
public void StopRoute()
{
if (routeSequence != null && routeSequence.IsActive())
{
routeSequence.Kill();
routeSequence = null;
}
if (!IsRouteRunning) return;
routeSequence?.Kill();
routeSequence = null;
currentRoute = null;
IsRouteRunning = false;
SyncFromCamera();
Enable = true;
}
public void AnimateToState(Vector3 pivotPosition, Vector3 eulerAngles, float distance, float duration = 0.4f)
private void CompleteRoute()
{
// <20>ִϸ<D6B4><CFB8>̼<EFBFBD> <20>߿<EFBFBD><DFBF><EFBFBD> <20><><EFBFBD><20>Է<EFBFBD> <20><>Ȱ<EFBFBD><C8B0>ȭ
Enable = false;
// DoTween<65><6E> <20><><EFBFBD><EFBFBD>Ͽ<EFBFBD> <20><>Ʈ<EFBFBD>ѷ<EFBFBD><D1B7><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20>ε巴<CEB5><E5B7B4> <20><><EFBFBD><EFBFBD>
DOTween.To(() => nextPosition, x => nextPosition = x, pivotPosition, duration);
DOTween.To(() => currentElevation, x => currentElevation = x, eulerAngles.x, duration);
DOTween.To(() => currentAzimuth, x => currentAzimuth = x, eulerAngles.y, duration);
DOTween.To(() => currentDistance, x => currentDistance = x, distance, duration)
.OnComplete(() => {
// <20>ִϸ<D6B4><CFB8>̼<EFBFBD><CCBC><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><20>Է<EFBFBD><D4B7><EFBFBD> <20>ٽ<EFBFBD> Ȱ<><C8B0>ȭ
Enable = true;
});
routeSequence = null;
currentRoute = null;
IsRouteRunning = false;
SyncFromCamera();
Enable = true;
onRouteComplete?.Invoke();
}
//public bool IsOperation()
//{
// if(is)
//}
private void SyncFromCamera()
{
var euler = camera.transform.eulerAngles;
currentElevation = euler.x;
currentAzimuth = euler.y;
var offset = Quaternion.Euler(euler.x, euler.y, 0f) * new Vector3(0, 0, -currentDistance);
nextPosition = camera.transform.position - offset;
cameraPivot.transform.position = nextPosition;
}
}
}