--- cs:140/Robot/ServingRobotController.cs +++ cs:141/Robot/ServingRobotController.cs @@ -336,6 +336,21 @@ /// この走りで寄る卓の数 public int RunStopCount { get { return _stops.Count; } } + readonly List _stopArrivedS = new List(); + + /// + /// 止まる所ごとに着いた時刻(店の中, 秒)。着いていない所は -1(依頼 v8 の 2)。 + /// trips.csv の stop_arrived_s に出す + /// + public IReadOnlyList StopArrivedS { get { return _stopArrivedS; } } + + /// + /// 止まる所に着いた(引数:ロボット・止まる所の番号)。依頼 v8 の 1。 + /// ServiceOps が受けて、その所の皿を「届いた」にする。 + /// 受け渡しの 25 秒(tableDwellSec)の **前** に出る + /// + public event Action StopArrived; + /// 1往復が終わったときに呼ばれる public event Action TripCompleted; @@ -540,16 +555,19 @@ RunStartS = startS; _stops.Clear(); _tableIds.Clear(); + _stopArrivedS.Clear(); if (stops != null) { for (int i = 0; i < stops.Count; i++) { _stops.Add(stops[i]); _tableIds.Add(stops[i].tableId); + _stopArrivedS.Add(-1f); // 着いたら時刻を入れる(依頼 v8 の 2) } } _stopIndex = 0; StopsCompleted = 0; + ResetTo = ""; // 打ち切りで置き直したときだけ入れる(依頼 v8b の A) if (_stops.Count == 0) { _phase = Phase.Idle; return; } BeginRun(passPoint); @@ -801,7 +819,11 @@ if (TotalS > spec.maxTripSec) { TimedOut = true; - PlaceAt(_passPoint, transform.rotation); + // ほかの台と重ならない所へ置き直す(依頼 v8b の A)。 + // 前は必ず受け渡し口の点に置いていたので、2台が打ち切られると + // 同じ点で重なり、お互いを「もう1台のロボット」として止まり続けていた + Vector3 to = PickResetPoint(); + PlaceAt(to, transform.rotation); Finish(); return; } @@ -1069,6 +1091,79 @@ List _sideCandidates; + // ---------------- 打ち切りで置き直す先(依頼 v8b の A)---------------- + + /// + /// 打ち切りで置き直した先(trips.csv の reset_to)。 + /// pass / standby1 / standby2 …。打ち切りでない往復は空 + /// + public string ResetTo { get; private set; } + + /// + /// 置く先を選ぶ。ほかの台が近く(直径+余裕)にいない最初の点。 + /// 受け渡し口の点 → 待機場所を順に + /// どこも空いていなければ、ほかの台からいちばん遠い待機場所 + /// (待機場所が無ければ受け渡し口の点)。 + /// + Vector3 PickResetPoint() + { + float near = spec != null ? spec.diameter + ResetClearMarginM : 0.8f; + + IList standby = ops != null && ops.store != null + ? ops.store.RobotStandbyPoints : null; + + // 1. 受け渡し口の点 + if (NearestRobotDistance(_passPoint) > near) { ResetTo = "pass"; return _passPoint; } + + // 2. 待機場所を順に + if (standby != null) + { + for (int i = 0; i < standby.Count; i++) + { + if (NearestRobotDistance(standby[i]) <= near) continue; + ResetTo = "standby" + (i + 1); + return standby[i]; + } + + // 3. どこも空いていない。ほかの台からいちばん遠い待機場所 + int best = -1; + float bestD = -1f; + for (int i = 0; i < standby.Count; i++) + { + float d = NearestRobotDistance(standby[i]); + if (d <= bestD) continue; + best = i; bestD = d; + } + if (best >= 0) + { + ResetTo = "standby" + (best + 1); + return standby[best]; + } + } + + ResetTo = "pass"; + return _passPoint; + } + + /// 置く先として見るときの、直径に足す余裕(m) + const float ResetClearMarginM = 0.3f; + + /// その点から、いちばん近いほかの台までの距離(m)。ほかに台が無ければ大きい値 + float NearestRobotDistance(Vector3 p) + { + p.y = 0f; + float best = float.MaxValue; + for (int i = 0; i < _all.Count; i++) + { + ServingRobotController o = _all[i]; + if (o == null || o == this) continue; + Vector3 q = o.transform.position; q.y = 0f; + float d = Vector3.Distance(p, q); + if (d < best) best = d; + } + return best; + } + /// 相手の外周と自分の外周のあいだの空き(m) float Gap(ServingRobotController other) { @@ -1545,6 +1640,14 @@ _dwellTimer = 0f; _speed = 0f; StopsCompleted = _stopIndex + 1; // この卓には届いた + + // 卓に着いた時点を残して知らせる(依頼 v8 の 1)。 + // サイトの試算と同じで、ここで皿は「届いた」。 + // 受け渡しの 25 秒(tableDwellSec)はこのあと + int at = _stopIndex; + if (at >= 0 && at < _stopArrivedS.Count) + _stopArrivedS[at] = ops != null ? ops.NowS : 0f; + if (StopArrived != null) StopArrived(this, at); } else if (_phase == Phase.ToPass) {