--- /sessions/rcw-01levexvnksg51fwlydnyzff/mnt/PixelstoMotors/デジタルツイン学習R2R2R/サービスロボット導入シミュレーション/simulation/v2_8/haizen_sim.py	2026-10-24 23:58:07.432138539 +0000
+++ /sessions/rcw-01levexvnksg51fwlydnyzff/mnt/PixelstoMotors/デジタルツイン学習R2R2R/サービスロボット導入シミュレーション/simulation/v2_9/haizen_sim.py	2026-10-30 00:13:52.903726056 +0000
@@ -1,5 +1,16 @@
 """
-サービスロボット導入シミュレーション #1 配膳ロボット（試作 v2.4）
+サービスロボット導入シミュレーション #1 配膳ロボット（試作 v2.9）
+
+v2.9 で直したこと（2026-10-30。エイイチと決めた：フロントの実感では、レジと案内を優先して仕事をしていた）
+  会計のスタッフを頼む時刻を「客が食べ終えてレジに並んだとき」にした。v2.8 までは、レジの順番が来てから
+  スタッフを頼んだので、ほかの優先1の仕事（案内・料理・呼び出し）の後ろに並び、そのあいだレジが止まっていた。
+  v2.9 では、会計の頼みはレジに並んだ時刻の古さで並ぶ（優先は1のまま）。スタッフが会計に付くのは、
+  これまでどおりレジが空いてから（レジ1つにつき1組ずつ・pay_s 秒）。会計が終わると先にレジを空け、
+  次の組の会計の頼みが並んでからスタッフの手を放すので、レジに並ぶ組がいれば会計の係がそのまま次を取る。
+  Unity（ServiceSim）の頼み方と同じ（依頼31の調べ。Unity は会計のスタッフがほぼいつも1人レジにいる）。
+  会計が速く回るぶん、片付けの仕事が速く入ってくる。乱数の使い方は変えていない。
+
+（以下、v2.4 の説明）
 
 v2.4 で直したこと（2026-10-22。動画の台本を codex で検証したときに見つかった2点。直し方はエイイチと決めた）
   1. 入店待ちの終わりを「案内されて卓に着いたとき」にした。v2.3 までは、案内したスタッフが説明（guide_s）を
@@ -95,7 +106,7 @@
     "util_sample_s": 10,      # 手がふさがっている人数・台数を数える間隔（秒）
     "max_extra_min": 600,     # 数える時間のあと、追いかけるのを打ち切る上限（分）。店が回らない条件の保険
     "robot_first_wait_s": 120,  # v2.7 ロボット優先：料理はロボットを待つ。これだけ待っても来なければスタッフが運ぶ（秒）
-    "model": "v2.8",          # モデルの版（前の版の結果を使い回さないための印）
+    "model": "v2.9",          # モデルの版（前の版の結果を使い回さないための印）
 }
 
 # ---------------------------------------------------------------
@@ -203,6 +214,17 @@
     return rng.triangular(lo, hi, mode)
 
 
+class _AgedRequest(simpy.resources.resource.PriorityRequest):
+    """v2.9：頼んだ時刻を since にさかのぼらせたスタッフの頼み（同じ優先の中で、since の古い順に並ぶ）"""
+    def __init__(self, resource, priority, since):
+        self.priority = priority
+        self.preempt = False
+        self.time = since
+        self.usage_since = None
+        self.key = (self.priority, self.time, not self.preempt)
+        simpy.resources.resource.Request.__init__(self, resource)
+
+
 class Store:
     def __init__(self, env, rng, p):
         self.env, self.rng, self.p = env, rng, p
@@ -276,9 +298,11 @@
             self.finished.succeed()
 
     # ---- スタッフの仕事（priority 0=ロボット救出, 1=配膳・案内・会計, 2=下げ膳）
-    def staff_job(self, priority, walk_m, work_s, waited=None):
+    def staff_job(self, priority, walk_m, work_s, waited=None, since=None):
         t0 = self.env.now
-        with self.staff.request(priority=priority) as req:
+        # v2.9：since があれば、その時刻に頼んだものとして並ぶ（会計は、レジに並んだ時刻の古さで並ぶ）
+        req0 = _AgedRequest(self.staff, priority, since) if since is not None else self.staff.request(priority=priority)
+        with req0 as req:
             yield req
             if waited is not None:
                 waited.append(self.env.now - t0)   # 手が空くのを待った時間（案内待ち）
@@ -525,10 +549,18 @@
         if extras:
             yield simpy.AllOf(env, extras)
         # v2.8 会計：卓に座ったままレジの順番を待ち、順番が来たらスタッフの手が空くのを待って会計する
+        # v2.9 会計のスタッフの頼みは、レジに並んだ時刻（p0）の古さで並ぶ。会計が終わったら先にレジを空け、
+        #      次にレジに入った組の頼みが並んでからスタッフの手を放す（会計の係がそのまま次の会計を取れる。Unity と同じ）
         p0 = env.now
-        with self.register.request() as rq:
-            yield rq
-            yield env.process(self.staff_job(1, 0, p["pay_s"]))
+        rq = self.register.request()
+        yield rq
+        sreq = _AgedRequest(self.staff, 1, p0)
+        yield sreq
+        self.staff_busy_s += p["pay_s"]
+        yield env.timeout(p["pay_s"])
+        yield self.register.release(rq)      # レジを空ける（次の組がレジに入る）
+        yield env.timeout(0)                 # 次の組の会計の頼みが並ぶのを待ってから
+        self.staff.release(sreq)             # スタッフの手を放す
         if counted:
             self.pay_waits.append((env.now - p0 - p["pay_s"]) / 60)
         self._acc()
