This commit is contained in:
2026-04-22 11:31:45 -04:00
parent da8716cccc
commit c10d785bfa
4 changed files with 231 additions and 399 deletions

View File

@@ -1,25 +1,24 @@
#LWP-Cookies-2.0 #LWP-Cookies-2.0
Set-Cookie3: dslang=US-EN; path="/"; domain=.apple.com; path_spec; secure; discard; HttpOnly=None; version=0 Set-Cookie3: dslang=US-EN; path="/"; domain=.apple.com; path_spec; secure; discard; HttpOnly=None; version=0
Set-Cookie3: site=USA; path="/"; domain=.apple.com; path_spec; secure; discard; HttpOnly=None; version=0 Set-Cookie3: site=USA; path="/"; domain=.apple.com; path_spec; secure; discard; HttpOnly=None; version=0
Set-Cookie3: acn01="siA7JPBndkEejEnE/127L8mBwoisazQDFIhojgAQ/KEGDE9i"; path="/"; domain=.apple.com; path_spec; secure; expires="2027-04-16 02:29:40Z"; HttpOnly=None; version=0 Set-Cookie3: acn01=KVgOa2TlSALDmkRBN3HjFqQsmjntJ3LJQtZC9wATSlL+oYci; path="/"; domain=.apple.com; path_spec; secure; expires="2027-04-21 18:02:51Z"; HttpOnly=None; version=0
Set-Cookie3: aasp=31DACD6478D254816871CEC71746594E9EB265E23843BFE54ACC23E6CD21C7967B4A74875C4B64D8D546102A6808900C1DFFE23602181DADE792ED7B73E9C5A4CC7C56CCF9C822DA32264AE474FEDF84ADC645D0F5B9D01FF71B500A76FBE7FF8AA5B0B9A6B0FB8F7A61CDB741E9973C9A04245DF7E939AB; path="/"; domain=.idmsa.apple.com; path_spec; secure; discard; HttpOnly=None; version=0 Set-Cookie3: aasp=97997103CCD7FEC9173190AED7457252EBCD7B770AB7B784C902761FC601C83B02A28C2DE1B84D51A690620FBBE43A9F53E20534400A5BABA174C06BCDF6D1239746712B62A97006E8A95F03A96A584A7185B1EB24404B0F03A152E9F46C23451139DEBF74FACF2C476512DC59E85823694E20CF535BBE50; path="/"; domain=.idmsa.apple.com; path_spec; secure; discard; HttpOnly=None; version=0
Set-Cookie3: DES580750186337023c50d1415a6e6ca44a2="HSARMTKNSRVXWFlajR2ecD1662phQjqU9vXxnL49ZjypuVYYXHDpA3wTiX6Mf2J4WDlIhZj52z81aDOuz+VC80bVhV41TSNN4ggoPjW8WnsQrjniTQYkgJycPQNnzhkK4hfe2AMrr/bhrJJm8sHHc+Oh1HUckN6T7T4c1bmf2Qg9tRwsdRDNyMMyFH/Ml/cQlWKj39/YHlY=SRVX"; path="/"; domain=.idmsa.apple.com; path_spec; secure; expires="2026-04-21 03:09:12Z"; HttpOnly=None; version=0
Set-Cookie3: X-APPLE-UNIQUE-CLIENT-ID="\"BA==\""; path="/"; domain=.icloud.com; path_spec; domain_dot; secure; discard; version=0 Set-Cookie3: X-APPLE-UNIQUE-CLIENT-ID="\"BA==\""; path="/"; domain=.icloud.com; path_spec; domain_dot; secure; discard; version=0
Set-Cookie3: X-APPLE-WEBAUTH-LOGIN="\"v=1:t=BA==BST_IAAAAAAABLwIAAAAAGngXy8RDmdzLmljbG91ZC5hdXRovQD04k7cSA0I7tz8P6YR10gGD7CeiE4Sp9KmB4NcfSJGJ9mMfV3s3UQpxtOj9bruFqCinlYpdqRIEyP2Gq5A_XBK1p7RsIkCrmxvCBTILwOf1RyEF9rd5VMxpyFJXo26hjHe8Cb6EB346qaJbrZ0r89Gs_Dp-A~~\""; path="/"; domain=.icloud.com; path_spec; domain_dot; secure; discard; HttpOnly=None; version=0 Set-Cookie3: X-APPLE-WEBAUTH-LOGIN="\"v=1:t=BA==BST_IAAAAAAABLwIAAAAAGnnu8wRDmdzLmljbG91ZC5hdXRovQDEXUpbkZ6MgVpJex6704NeoKMirs7DTjQaMiLP3D7RDPPgRrN1eyRakxlhJwOczfTWOBtEP6_JywPTG3CmSA0RzajX5msyXsLEbn2K7IC-IF2NRP6-O8ACrZc8NLmZhrpYcMuYd7-cRNCfP9YrPhaLOOIYjQ~~\""; path="/"; domain=.icloud.com; path_spec; domain_dot; secure; discard; HttpOnly=None; version=0
Set-Cookie3: X-APPLE-WEBAUTH-VALIDATE="\"v=1:t=BA==BST_IAAAAAAABLwIAAAAAGngXy8RDmdzLmljbG91ZC5hdXRovQD04k7cSA0I7tz8P6YR10gGD7CeiE4Sp9KmB4NcfSJGJ9mMfV3s3UQpxtOj9bruFqCinlYpdqRIEyP2Gq5A_XBK1p7RsIkCrmxvCBTILwOf1bJlsm_AS1Djsp7P1ioqPimN8HaW0JDK-1gjsxQX4O6MgDpkxQ~~\""; path="/"; domain=.icloud.com; path_spec; domain_dot; secure; discard; version=0 Set-Cookie3: X-APPLE-WEBAUTH-VALIDATE="\"v=1:t=BA==BST_IAAAAAAABLwIAAAAAGnnu8wRDmdzLmljbG91ZC5hdXRovQDEXUpbkZ6MgVpJex6704NeoKMirs7DTjQaMiLP3D7RDPPgRrN1eyRakxlhJwOczfTWOBtEP6_JywPTG3CmSA0RzajX5msyXsLEbn2K7IC-IHq8h87PbRbxm_5NFEGqbnrBegyo4fAVl3K0v8zi4ALPFmK-uw~~\""; path="/"; domain=.icloud.com; path_spec; domain_dot; secure; discard; version=0
Set-Cookie3: X-APPLE-WEBAUTH-USER="\"v=1:s=1:d=157320350\""; path="/"; domain=.icloud.com; path_spec; domain_dot; secure; expires="2026-05-16 04:01:54Z"; HttpOnly=None; version=0 Set-Cookie3: X-APPLE-WEBAUTH-USER="\"v=1:s=1:d=157320350\""; path="/"; domain=.icloud.com; path_spec; domain_dot; secure; expires="2026-05-21 18:02:55Z"; HttpOnly=None; version=0
Set-Cookie3: X_APPLE_WEB_KB-FHMLYL_TPMN_3A8D3KIPPI0C_EC="\"v=1:t=BA==BST_IAAAAAAABLwIAAAAAGm_OLQRDmdzLmljbG91ZC5hdXRovQD38nYoxQenHW9WggeFKkoDa8I8zeKoOshv6I4dsZQalR2itry1r6kUZe9d_BZan1W-oKlImTrYi_-Vt5Q4YEJWJITWeqN8QChxvbTXB0o8sQ-wAIzBL1J5sQIRBqMadrtP5U0wslkRg0u0AguK20CM4TGoGg~~\""; path="/"; domain=.icloud.com; path_spec; domain_dot; secure; expires="2026-05-21 00:32:52Z"; HttpOnly=None; version=0 Set-Cookie3: X_APPLE_WEB_KB-FHMLYL_TPMN_3A8D3KIPPI0C_EC="\"v=1:t=BA==BST_IAAAAAAABLwIAAAAAGm_OLQRDmdzLmljbG91ZC5hdXRovQD38nYoxQenHW9WggeFKkoDa8I8zeKoOshv6I4dsZQalR2itry1r6kUZe9d_BZan1W-oKlImTrYi_-Vt5Q4YEJWJITWeqN8QChxvbTXB0o8sQ-wAIzBL1J5sQIRBqMadrtP5U0wslkRg0u0AguK20CM4TGoGg~~\""; path="/"; domain=.icloud.com; path_spec; domain_dot; secure; expires="2026-05-21 00:32:52Z"; HttpOnly=None; version=0
Set-Cookie3: X-APPLE-DS-WEB-SESSION-TOKEN="\"AQE2jvAclZD065mntDlVLyvYiJo3gR1bQrXcbC4UJg7cfXVcjogcjL3EORqqwOCw/77BmeCLIYZVCGY/rdu/a7P8a3P628zN4M3/97eFNWavQnlNfVotwXIPv7qcWQoIm7C2G7Gf4JHklBOmzWU9tmub3JpV5bzxRYb41I1YvwUYt8wWigFwQeX1p0djcpB0YbQp7Ign0RzHelQ0LE0046v2mWkeTXN59E57xpVChCkcsydb1LQ7dARyG6K8nDVBxYMrB4CiWR93/w9GbdECWXDl1zGyUcUEKjveXj/f4yUMmP97xlNYSr4T1srOGXuqyGQ35QBbcVhfgciSTSC8IWp1CfoJYDFoRjgsock39xnQIzkV0kgqbqh9KmMnRpra+BvTwFpkZooM5yqBH6q83k1mrQwv3WpGpKSgJf7DHk/DllkZi9mnZHVgukX+KWlgCnl5IPACmg72mbM9Lt43Rn6NxCXE4LoOxSRCg0OM7U5VB2KQEW0itAifwYbTeLDYEy2FK0GysmpQUdiRLPqbY/5tmR29VsrH11ILDmWcMkuClbf4MgFo723r2wPIZc8VXWSNAwArChh6gGmYRmmgpJwMoAADz+e53bH9t2zdNeo5Gl4UXCo2krrwW6hPDvIvIsO2G9RsqxPjfWe8TEhWxqSNZ0NCXihX0AZ+jYVR1eNu6d55TEBavM/wYPxJW7y8YCFJV3xXIpZRmPWAO/+WiqeBBH3jRSIGQZcCe5Sa8114Bf5zVUl2Ccgpo6A4ONfoWkXlDHbeXSxmuRKE7QXjToAcLgEErM1UOPo=\""; path="/"; domain=.icloud.com; path_spec; domain_dot; secure; expires="2026-05-16 04:01:54Z"; HttpOnly=None; version=0 Set-Cookie3: X-APPLE-DS-WEB-SESSION-TOKEN="\"AQGaLX9cVeJ7EnQI/NCzMqxhb2pDyS9YSfQ1UGym2zF1fVrljZeKrlUA3e7k6IbGJwPd7ssN4+feYO2egAdRyJhQ0LJ89jY0jkyCAahyW4neQn/tbHwb49oChj+vjIK0Yv52Y3293nImzWSw1iUtNL6EhIgISPRk47tBH3PrEPwaFuPEK+YtFT8TeKaAWdIbXRIGxBkYEJYyl8BUFt4dobnS2shzOX7dfjOaBKClBF4y2x3/EVb6TeR6+kBoyLbZHe0+RMvyqrO0FFhCagmUZ0+xPAABPJq4r4JTmVfvKOU6j5gPO9Jsz97Damf9G6QyaqKcy4MjSmow4v+diEVaUdR4HyByCRTYcH+biNLnoVBeSPoVIuUQAlqdcDry6epZquivcATUeCUBEMroSWRz6gdLWkdCMSGqP+TR+aV0VROoJkGjT3lKlOJ4ElgfD5aQh4PHKyg70o9K5Eq8C+nMlphuFAgPVypRYL5rDebOWBfIlJRNuUSUIvvVyiBq94Ii2YI8BwX07TdZszdPj9oHtnX264nZe3YUw4W9XCkdPDufluUzFIP3bMIifRrOk6yEbXObUjbf5POU1taM6S2zaMvN7GspAkV5FvYE+E2NHRPGGq8PZ+8fqSIAdOx+O+zQq9T69NmxpvloIZYAUZUlwjOFtXr3vfHQhik82eLifYuiqsp/9M4yDIJvQfC7H6Mc4aM4aNHfHt2BdgXaRR2Zsx35CKLCKQtxpqT5DdO3y4+dDZt/Le7lIF/N\""; path="/"; domain=.icloud.com; path_spec; domain_dot; secure; expires="2026-05-21 18:02:55Z"; HttpOnly=None; version=0
Set-Cookie3: X-APPLE-WEBAUTH-HSA-TRUST="\"28a33818a1dce9a0eecde38e7c8fcc6f080b70bc9feb505599fb2855903a4792_HSARMTKNSRVXWFlajR2ecD1662phQjqU9vXxnL49ZjypuVYYXHDpA3wTiX6Mf2J4WDlIhZj52z81aDOuz+VC80bVhV41TSNN4ggoPjW8WnsQrjniTQYkgJycPQNnzhkK4hfe2AMrr/bhrJJm8sHHc+Oh1HUckN6T7T4c1bmf2Qg9tRwsdRDNyMMyFH/Ml/cQlWKj39/YHlY=SRVX\""; path="/"; domain=.icloud.com; path_spec; domain_dot; secure; expires="2026-07-15 04:01:51Z"; HttpOnly=None; version=0 Set-Cookie3: X-APPLE-WEBAUTH-HSA-TRUST="\"28a33818a1dce9a0eecde38e7c8fcc6f080b70bc9feb505599fb2855903a4792_HSARMTKNSRVXWFlajR2ecD1662phQjqU9vXxnL49ZjypuVYYXHDpA3wTiX6Mf2J4WDlIhZj52z81aDOuz+VC80bVhV41TSNN4ggoPjW8WnsQrjniTQYkgJycPQNnzhkK4hfe2AMrr/bhrJJm8sHHc+Oh1HUckN6T7T4c1bmf2Qg9tRwsdRDNyMMyFH/Ml/cQlWKj39/YHlY=SRVX\""; path="/"; domain=.icloud.com; path_spec; domain_dot; secure; expires="2026-07-19 17:15:45Z"; HttpOnly=None; version=0
Set-Cookie3: X-APPLE-WEBAUTH-PCS-Events="\"S2V5QXBwbDoBAAAA8QRZAADyjPVu3Zmkep80V+qJNHa5C3NwfKpeMyW67J1rMKpK4Q==\""; path="/"; domain=.icloud.com; path_spec; domain_dot; secure; expires="2026-05-16 04:01:54Z"; HttpOnly=None; version=0 Set-Cookie3: X-APPLE-WEBAUTH-PCS-Events="\"S2V5QXBwbDoBAAAA8QRuAAD03QLs6kAdJCQM23wVykZFD0clWmO+gTubXQ9MuQcaLA==\""; path="/"; domain=.icloud.com; path_spec; domain_dot; secure; expires="2026-05-21 18:02:55Z"; HttpOnly=None; version=0
Set-Cookie3: X-APPLE-WEBAUTH-PCS-Documents="\"S2V5QXBwbDoBAAAAAgRZAABipc9p/kcwMIQ/uzTmZZxdtx00DjfUVtPIn8qIK3qBiw==\""; path="/"; domain=.icloud.com; path_spec; domain_dot; secure; expires="2026-05-16 04:01:54Z"; HttpOnly=None; version=0 Set-Cookie3: X-APPLE-WEBAUTH-PCS-Documents="\"S2V5QXBwbDoBAAAAAgRuAAAy5taxxsiD1jDBrDQrQ1SiNyKmIxvvaWA7D0nA4Dl1iw==\""; path="/"; domain=.icloud.com; path_spec; domain_dot; secure; expires="2026-05-21 18:02:55Z"; HttpOnly=None; version=0
Set-Cookie3: X-APPLE-WEBAUTH-PCS-Photos="\"S2V5QXBwbDoBAAAAAwRZAABdIvbUGMfQwMvJBKaERpK7lH+LKZUfwl8xaaM0s/9G3A==\""; path="/"; domain=.icloud.com; path_spec; domain_dot; secure; expires="2026-05-16 04:01:54Z"; HttpOnly=None; version=0 Set-Cookie3: X-APPLE-WEBAUTH-PCS-Photos="\"S2V5QXBwbDoBAAAAAwRuAADS4+q95r+DyWl1ZGAmQT9MS2LUEBKAYun3XGW61iPTXg==\""; path="/"; domain=.icloud.com; path_spec; domain_dot; secure; expires="2026-05-21 18:02:55Z"; HttpOnly=None; version=0
Set-Cookie3: X-APPLE-WEBAUTH-PCS-Cloudkit="\"S2V5QXBwbDoBAAAABARZAABLV1DirvkB63ct2xhJbufRGeA6yHF1dzCZyT2lUxmHqg==\""; path="/"; domain=.icloud.com; path_spec; domain_dot; secure; expires="2026-05-16 04:01:54Z"; HttpOnly=None; version=0 Set-Cookie3: X-APPLE-WEBAUTH-PCS-Cloudkit="\"S2V5QXBwbDoBAAAABARuAABOeS5LblTFkG8u6j7mIwaaRXTJwPVJ0sZ8/g9jV8910g==\""; path="/"; domain=.icloud.com; path_spec; domain_dot; secure; expires="2026-05-21 18:02:55Z"; HttpOnly=None; version=0
Set-Cookie3: X-APPLE-WEBAUTH-PCS-Safari="\"S2V5QXBwbDoBAAAAFgRZAAD1MhBAfPNJA/LzwgvGElc+wsl5KKnPYjLUu4/gMxexQg==\""; path="/"; domain=.icloud.com; path_spec; domain_dot; secure; expires="2026-05-16 04:01:54Z"; HttpOnly=None; version=0 Set-Cookie3: X-APPLE-WEBAUTH-PCS-Safari="\"S2V5QXBwbDoBAAAAFgRuAABsSNg/+jBeWh2M6qOISYiUBEq79JiSuu7otGjyBpBf4Q==\""; path="/"; domain=.icloud.com; path_spec; domain_dot; secure; expires="2026-05-21 18:02:55Z"; HttpOnly=None; version=0
Set-Cookie3: X-APPLE-WEBAUTH-PCS-Mail="\"S2V5QXBwbDoBAAAABwRZAABpYZPPRGY/AiCTZ+FxbMFSnuLaJ83tH7xhjpL0BjSDDg==\""; path="/"; domain=.icloud.com; path_spec; domain_dot; secure; expires="2026-05-16 04:01:54Z"; HttpOnly=None; version=0 Set-Cookie3: X-APPLE-WEBAUTH-PCS-Mail="\"S2V5QXBwbDoBAAAABwRuAADF1hkuqi8C9dtlFR42VnoNAyVPpWhcrELIRAjf+N3M0g==\""; path="/"; domain=.icloud.com; path_spec; domain_dot; secure; expires="2026-05-21 18:02:55Z"; HttpOnly=None; version=0
Set-Cookie3: X-APPLE-WEBAUTH-PCS-Notes="\"S2V5QXBwbDoBAAAACQRZAAAHGYf+WnAkIKAbEuOfLXsDNDwkpc9fc9XRBT7MuUB8oA==\""; path="/"; domain=.icloud.com; path_spec; domain_dot; secure; expires="2026-05-16 04:01:54Z"; HttpOnly=None; version=0 Set-Cookie3: X-APPLE-WEBAUTH-PCS-Notes="\"S2V5QXBwbDoBAAAACQRuAACUkk2Drkms4w+Wh9VFeFzbkOOMxla+5RXOacqRl/OT5A==\""; path="/"; domain=.icloud.com; path_spec; domain_dot; secure; expires="2026-05-21 18:02:55Z"; HttpOnly=None; version=0
Set-Cookie3: X-APPLE-WEBAUTH-PCS-News="\"S2V5QXBwbDoBAAAACwRZAADjYc71gjGFasFxH7Z1SUqX3HeZxOksxRuTIbRcuYhNlA==\""; path="/"; domain=.icloud.com; path_spec; domain_dot; secure; expires="2026-05-16 04:01:54Z"; HttpOnly=None; version=0 Set-Cookie3: X-APPLE-WEBAUTH-PCS-News="\"S2V5QXBwbDoBAAAACwRuAACI8hcv3wQKVQc/jrosTdSeaOV7wKGolh3SsnB2KV4l5Q==\""; path="/"; domain=.icloud.com; path_spec; domain_dot; secure; expires="2026-05-21 18:02:55Z"; HttpOnly=None; version=0
Set-Cookie3: X-APPLE-WEBAUTH-PCS-Sharing="\"S2V5QXBwbDoBAAAADARZAABkkP52Jyp0ESCIUjeMwkIQt7V6sNsaAMg/Fqp2BpcFBg==\""; path="/"; domain=.icloud.com; path_spec; domain_dot; secure; expires="2026-05-16 04:01:54Z"; HttpOnly=None; version=0 Set-Cookie3: X-APPLE-WEBAUTH-PCS-Sharing="\"S2V5QXBwbDoBAAAADARuAAAMNHurlvF3BExXBzOliO8/XXMKPJVICVoKrapQdZ6DtA==\""; path="/"; domain=.icloud.com; path_spec; domain_dot; secure; expires="2026-05-21 18:02:55Z"; HttpOnly=None; version=0
Set-Cookie3: X-APPLE-WEBAUTH-TOKEN="\"v=2:t=BA==BST_IAAAAAAABLwIAAAAAGngbgERDmdzLmljbG91ZC5hdXRovQA3sNfo8nQ2WqjRiW8b2v6FBA7xVM9ABBbUseXK6M1xna_9nP0GiTFniTIZZOBZKPWrAYTMjtv4yy7kM54yBmzwmpnsWfZi5eEJsLoboChOa7f6SKHTZUIsYnzV_GQH8efLnI9WppfwGYtR-h1kK71sisAa3g~~\""; path="/"; domain=.icloud.com; path_spec; domain_dot; secure; expires="2026-04-30 05:05:05Z"; HttpOnly=None; version=0 Set-Cookie3: X-APPLE-WEBAUTH-TOKEN="\"v=2:t=BA==BST_IAAAAAAABLwIAAAAAGnoSj8RDmdzLmljbG91ZC5hdXRovQBIW8hdgh_zfyx02_y4Hn4HvHqQgiLD7363DvLz7pwBtrTvdm3V7I3ffhB0g2EHzkFt1dIaDrk1sdCY46eySjfCpcSrwcRxUhMWIuvWzV4dvZbUKrHr4X5v98wrzMX-Xwvwo1OOFuv04U9NhQTZqEdQF22Oeg~~\""; path="/"; domain=.icloud.com; path_spec; domain_dot; secure; expires="2026-05-06 04:10:39Z"; HttpOnly=None; version=0
Set-Cookie3: X-APPLE-WEBAUTH-FMIP="\"BA==BST_IAAAAAAABLwIAAAAAGngbgERDmdzLmljbG91ZC5hdXRovQA3sNfo8nQ2WqjRiW8b2v6FBA7xVM9ABBbUseXK6M1xna_9nP0GiTFniTIZZOBZKPWrAYTMjtv4yy7kM54yBmzwmpnsWfZi5eEJsLoboChOa_OhPIyPKAsZumke00vIotvQgxBnmoPPwJSambgtmkzhunV0BQ~~\""; path="/"; domain=.icloud.com; path_spec; domain_dot; secure; discard; HttpOnly=None; version=0 Set-Cookie3: X-APPLE-WEBAUTH-FMIP="\"BA==BST_IAAAAAAABLwIAAAAAGnoSj8RDmdzLmljbG91ZC5hdXRovQBIW8hdgh_zfyx02_y4Hn4HvHqQgiLD7363DvLz7pwBtrTvdm3V7I3ffhB0g2EHzkFt1dIaDrk1sdCY46eySjfCpcSrwcRxUhMWIuvWzV4dvbKKudZw4ESK__dFUngctbzufvHZm3Ik0x6Ql7094pfT7Iuo-Q~~\""; path="/"; domain=.icloud.com; path_spec; domain_dot; secure; discard; HttpOnly=None; version=0
Set-Cookie3: xr_3n2093n1a="B50g9gcKmoM+KF+j8hPX6wKXv22QcYsBKEONZTiNR4g="; path="/"; domain=p144-fmipweb.icloud.com; path_spec; secure; discard; HttpOnly=None; version=0 Set-Cookie3: xr_3n2093n1a="tF9S/YsknhHffdhwA1W+2NKKwF/D5M+PVtX/BUh5l6Q="; path="/"; domain=p144-fmipweb.icloud.com; path_spec; secure; discard; HttpOnly=None; version=0

View File

@@ -1 +1 @@
{"client_id": "a803c3ce-2586-11f1-a724-8f6777a1d2b5", "session_id": "31DACD6478D254816871CEC71746594E9EB265E23843BFE54ACC23E6CD21C7967B4A74875C4B64D8D546102A6808900C1DFFE23602181DADE792ED7B73E9C5A4CC7C56CCF9C822DA32264AE474FEDF84ADC645D0F5B9D01FF71B500A76FBE7FF8AA5B0B9A6B0FB8F7A61CDB741E9973C9A04245DF7E939AB", "auth_attributes": "TZEZq6jNK7opptLHNWdfaXDIdIxdU7QFMD77vSobzh8nCntqlxgqc//nJp+R019Uu1BCkbJQ2kQqR1klW6lvKS0MLfWf9rNzaflu5uQgHMQhqOm4MJzIb8Mebqfmh2rsPlbm137EJO468E/sZbLLwl1PKOjj3o1IqEhfG17Xsa7TlxJAfLQorZTGJtMAapcxBu3NJIAnYSJRoY7h11keWBRWxGSb6zuYAy0yXAD4x3Pu7tFVtYwQTiUX3+HrcGFVJa/D8f/3vfiryreNgQX7ABD8oRF8Ooc=", "scnt": "AAAA-jMxREFDRDY0NzhEMjU0ODE2ODcxQ0VDNzE3NDY1OTRFOUVCMjY1RTIzODQzQkZFNTRBQ0MyM0U2Q0QyMUM3OTY3QjRBNzQ4NzVDNEI2NEQ4RDU0NjEwMkE2ODA4OTAwQzFERkZFMjM2MDIxODFEQURFNzkyRUQ3QjczRTlDNUE0Q0M3QzU2Q0NGOUM4MjJEQTMyMjY0QUU0NzRGRURGODRBREM2NDVEMEY1QjlEMDFGRjcxQjUwMEE3NkZCRTdGRjhBQTVCMEI5QTZCMEZCOEY3QTYxQ0RCNzQxRTk5NzNDOUEwNDI0NURGN0U5MzlBQnwyAAABnZQtJsgyKZp42zbZ-0Z4oWrpA3YXiaHUTiSnXR609FFr-Gp8mXl0K2uZyfgHABD8oPi9bmzasngL_Wz-FTxJCZqjsaEPEy_FduILu2V9ZZicjLLe-w", "account_country": "USA", "session_token": "r1NSUQJqcNyXMokmn8eZ7kwyXJSKyZ+bfM6J1EO3EK+JWP5PGpyTXp2LoUGHTr14FeEigC9HrJ+Fldhzd+lFxZ5vwSik14II4CD3cJdJ0OS/+hJRQDEqfW4YPsYHia7RsWL7Bn9NcZ2V2CjwROu7KChcUT0Lpjwz2JVsfkX+h8m25H8Rh3jz5Osz+V7raXq5OlAOx7crzXt2dtuZm0zFlhg0X9lmI/NUmx3GlP4kou82pHPWcLQOa+i5sk86WsyjyhoeeCGS1ShuWch6+Cf+0G4ROe/6xSvnaNWU/Lqe8OMddybR0bbvkIAwsD7roIAxk0kRcL8k4MYUBByIw3C0KUKB1cJDdHTyoTJRq9UtUcH0vb+bbs4j2NfuJGO6k99/iG1yFNUj3X+DewCaEeKlb0sMw4Ja9BRfktZ8ql5/2blPa61cg9CNwUl93OXrJ58ux+QYHGKCQbT9v6YwGOLdfRZ24D9EV6n9nKeXrQg6xazO5R3EOtvmiGltLGFIhIWFb2L56juB1hahXXL4yJRL4w7x0kcPIFwdAFXQ0rJCqHWs/DiijrXDINFRgEeEJroyHKw/heO+yiGnHY8xLh51LoRR+5Wx55NJQm54z2P9JXKUqIjj5KPE1kbOBMJQXsUYL3aNVUbzPfDeOrsNCJ/3O0jjyoNfPvU/YPwCBGD5WjYhQJhvmk5J/QVTLantotSj2tU+NwN+BOiCDrs5cC+DKFTph0mN1xap3TSudHMSKNqlSGQ4kTAJaAcG2ECbraedI3b6WhjRPgiWx/fRCmpxpyPZvldFJq3QQXMM64hPQkk/8KG8D2ZLX1rKvZXZRKHo10PbRGYt8injSUjvgYf26+9sK2rCR/mngu0Rye+0fDG6BI4g2FfQ1v2vZW7Y7Q1UXhNxmXXZE/1Lgv7qm6gbHhOEO6JFCn2XfAt1Iee/purvYvkn0ldiuw20/3TNB6DR4AAQ/KEXuV7c", "trust_eligible": "true", "grant_code": "c9517c9fb90e44df386b19607971fb557.0.srzwz.W1TnJS7PpGpVTWSqASYfRw", "trust_token": "HSARMTKNSRVXWFlajR2ecD1662phQjqU9vXxnL49ZjypuVYYXHDpA3wTiX6Mf2J4WDlIhZj52z81aDOuz+VC80bVhV41TSNN4ggoPjW8WnsQrjniTQYkgJycPQNnzhkK4hfe2AMrr/bhrJJm8sHHc+Oh1HUckN6T7T4c1bmf2Qg9tRwsdRDNyMMyFH/Ml/cQlWKj39/YHlY=SRVX"} {"client_id": "a803c3ce-2586-11f1-a724-8f6777a1d2b5", "session_id": "97997103CCD7FEC9173190AED7457252EBCD7B770AB7B784C902761FC601C83B02A28C2DE1B84D51A690620FBBE43A9F53E20534400A5BABA174C06BCDF6D1239746712B62A97006E8A95F03A96A584A7185B1EB24404B0F03A152E9F46C23451139DEBF74FACF2C476512DC59E85823694E20CF535BBE50", "auth_attributes": "FhGAEYBRc1+aEJafKEWyMXwiTKgOCf4ENrADIEy/mfp/TywQkHi2sHOKpvo4RqEGn1nUCPCD/nfX9Gyi4W0N6XDtZ7HOCNGtlVhaSGJYoWSNk4/5dBcodUuA2hlYQdPs7P2MqVEIotktBhNmd/YYTGPmgjAd7gyRHhAQqC+07dw9Fwqtvv8q0dazx35kIyuErdWQ5Shb8dZ4GRjO59OCjqlwyfjWjvG4OX1pLDHwVHkljxlRfeGLRR/ILOwIwWQY0/8QvHoyFuN8d3qaDFceABK2qXKADwU=", "scnt": "AAAA-jk3OTk3MTAzQ0NEN0ZFQzkxNzMxOTBBRUQ3NDU3MjUyRUJDRDdCNzcwQUI3Qjc4NEM5MDI3NjFGQzYwMUM4M0IwMkEyOEMyREUxQjg0RDUxQTY5MDYyMEZCQkU0M0E5RjUzRTIwNTM0NDAwQTVCQUJBMTc0QzA2QkNERjZEMTIzOTc0NjcxMkI2MkE5NzAwNkU4QTk1RjAzQTk2QTU4NEE3MTg1QjFFQjI0NDA0QjBGMDNBMTUyRTlGNDZDMjM0NTExMzlERUJGNzRGQUNGMkM0NzY1MTJEQzU5RTg1ODIzNjk0RTIwQ0Y1MzVCQkU1MHwzAAABnbFDT1gK_eXTVA7MXRwWGzc7EYk78w8VdprXZA2_PjYhD7pR3jUfI2JNzgoAABK2qWyRXF7vCiS5BW4XleIhsTtwY-WrzQDbwxWdrOD8PmGOC4IDBA", "account_country": "USA", "session_token": "L5N1qDC1TDTKZcGkEcC0YgEUG63qGf9QDs7tuISwtYdNAYqz9ErHPJL3bmQIqs8+2xAQiPTOaceoglSsjq8BThr/OpJBQPFncwAcE2c66T+rK712+3GWgraF5K8yZl2vnXxFY3nLpDYAIz+jt5jiMNxgD9sOugH32kGlal2QBJSRiXnz7SrWe8hI35zfXmjrM/ARD45ZNzuSfYJZ+G6g4bK7+EWqwC3TOhrIpVXzGrqpm7FWgj2W/ZZnNGfpVq+fZ7CZ1Yp0HkEgEiM4WA8+ewstJzGOzh7pVTSc/aTA0l5/8jwqJjEvdc7C+WaJN4tjydlRre+Z6jnWuHcHP0TzhXyKfU3oHKiKMMll1WEJfNFGXzBJyhC2ZgkNMte+Ddr3u/68iv2T5foIsoJv8B8ztcD8ka/lcnf8oP5myT1CYt0P8NlOn3kg9PKWjQR5GbzgDOtmnqeHkhTkAdswD5cidKUXCnP534tur4997bYvZY3bgt6kR7ix4v9F8gBzWkDeUyP7gu0IeYisyDA8Xf1d4C6HdP+65YRoXn/Xqo94+n0A5g19Y+p5pb6X2vE2obKewTZCmH0d+VMMvFECTs9tr37zkYDV2sjXv7oSOTzdT9AWp3zVkL12+IoqswhppcZ+xWBnA2+VX4n83EKf8enRimYFx2gssnSbGJCRBIlkSggoXTO15+5j2l4eO3akjA9d4hDfL+agH/RT/h1oTm08ImD6Vw0lRkOUhFZydUUMgcbt15ZFzBt9bly+rN4BbmM2X7SStGYLRZXxPNe3iu6oy/tTucWeRXjCAHkAWqqrpIfGF/adhaDbRAwaRyfw61YxQM8P/yJy1vXXov3lnyFM9yJiggZrc1DSov7PMfBdhhpFTcAvuiEimP7Utou6b1BjfvyA/nrXqBiCDc6lkHkEd4ozamIMsyM77ZOBsb1fyrNZ867pbubUdqtr/70OuyQkkmVUgRSpJ2N+o7pvL1FY7Ux3Ukh7RNF/O+jATX6XdWBdEi8HF0C0+sHCCQxXAqgIMwDKeqwIQUD3im2i4c7aFQPwIAAStqlygRYH", "trust_eligible": "true", "grant_code": "c7f4eb1fa9c0c4821bb56e7f0d86f4410.0.srzwz.Qg-aHawHsScPT5s8mVqKoA", "trust_token": "HSARMTKNSRVXWFlajR2ecD1662phQjqU9vXxnL49ZjypuVYYXHDpA3wTiX6Mf2J4WDlIhZj52z81aDOuz+VC80bVhV41TSNN4ggoPjW8WnsQrjniTQYkgJycPQNnzhkK4hfe2AMrr/bhrJJm8sHHc+Oh1HUckN6T7T4c1bmf2Qg9tRwsdRDNyMMyFH/Ml/cQlWKj39/YHlY=SRVX"}

Binary file not shown.

View File

@@ -114,10 +114,10 @@ class LocationSimulationState:
self.simulation_queue: asyncio.Queue = asyncio.Queue() self.simulation_queue: asyncio.Queue = asyncio.Queue()
self.simulation_queue_data: Dict = {} self.simulation_queue_data: Dict = {}
self.simulation_queue_order: list[str] = [] self.simulation_queue_order: list[str] = []
self.simulation_queue_deleted_items: list[str] = []
self.simulation_queue_pending_ids: set[str] = set() self.simulation_queue_pending_ids: set[str] = set()
self.simulation_queue_state: str = "STOPPED" self.simulation_queue_state: str = "STOPPED"
self.simulation_noise: bool = False self.simulation_noise: bool = False
self.set_location_enabled: bool = True
self.simulation_active: bool = False self.simulation_active: bool = False
self.simulation_task: Optional[asyncio.Task] = None self.simulation_task: Optional[asyncio.Task] = None
self.sio: socketio.AsyncServer = socketio.AsyncServer( self.sio: socketio.AsyncServer = socketio.AsyncServer(
@@ -642,6 +642,7 @@ class TunneldRunnerSio:
"command": "start", "command": "start",
"message": "Simulation started", "message": "Simulation started",
"data": { "data": {
"simulation_active": self.context.simulation_active, "simulation_active": self.context.simulation_active,
"simulation_queue_state": self.context.simulation_queue_state "simulation_queue_state": self.context.simulation_queue_state
} }
@@ -667,10 +668,11 @@ class TunneldRunnerSio:
try: try:
if self.context.test_mode: if self.context.test_mode:
logger.info("Simulation worker: test mode enabled") logger.info("Simulation worker: test mode enabled")
with LocationSimulationTestQueue(self.context) as locate_simulation: locate_simulation = LocationSimulationQueue(None, self.context)
await locate_simulation.play_queue() await locate_simulation.play_queue()
return
if self.context.udid is None and not self.context.test_mode: if self.context.udid is None:
active_udids = sorted( active_udids = sorted(
{ {
t.udid t.udid
@@ -841,7 +843,10 @@ class TunneldRunnerSio:
"command_class": "simulation_control", "command_class": "simulation_control",
"command": "add", "command": "add",
"message": f"Location {loc_id} added to the queue", "message": f"Location {loc_id} added to the queue",
"data": location_item, "data": {
"simulation_queue": get_simulation_status(),
"location_item": location_item,
},
} }
add_item(loc_id, location_item) add_item(loc_id, location_item)
await enqueue_next_simulation_item() await enqueue_next_simulation_item()
@@ -854,6 +859,49 @@ class TunneldRunnerSio:
"message": "Invalid location data", "data": data} "message": "Invalid location data", "data": data}
return resp return resp
async def delete_location_from_simulation_queue(data):
loc_id = (
data.get("loc_id")
if isinstance(data, dict)
else getattr(data, "loc_id", None)
)
if loc_id is not None:
if not loc_id in self.context.simulation_queue_order:
resp = {
"command_status": "ERROR",
"command_class": "simulation_control",
"command": "delete",
"message": f"Location {loc_id} not found in the queue",
"data": {
"simulation_queue": get_simulation_status(),
"location_item": self.context.simulation_queue_data[loc_id],
},
}
logger.info("Deleting location %s from the queue", loc_id)
await delete_item(loc_id)
resp = {
"command_status": "OK",
"command_class": "simulation_control",
"command": "delete",
"message": f"Location {loc_id} deleted from the queue",
"data": {
"simulation_queue": get_simulation_status(),
"simulation_queue": get_simulation_status(),
"location_item": self.context.simulation_queue_data[loc_id],
},
}
else:
resp = {
"command_status": "ERROR",
"command_class": "simulation_control",
"command": "delete",
"message": "Invalid location data",
"data": {
"simulation_queue:": get_simulation_status(),
},
}
return resp
async def pause_simulation_queue(): async def pause_simulation_queue():
"""Pauses asyncio.Queue playback""" """Pauses asyncio.Queue playback"""
self.context.simulation_queue_state = "PAUSED" self.context.simulation_queue_state = "PAUSED"
@@ -864,7 +912,8 @@ class TunneldRunnerSio:
await update_queue_times() await update_queue_times()
async def advance_simulation_queue(): async def advance_simulation_queue():
self.context.simulation_queue_state = "NEXT" current_loc_id = self.context.get_current_loc_id()
self.context.simulation_queue_data[current_loc_id]["delay"] = 0
await update_queue_times() await update_queue_times()
async def update_queue_times(): async def update_queue_times():
@@ -885,23 +934,12 @@ class TunneldRunnerSio:
await update_queue_data() await update_queue_data()
async def update_queue_data(): async def update_queue_data():
data = { await self.context.sio.emit("queue_data_update", get_simulation_status(), namespace="/")
"simulation_queue": {
"active": self.context.simulation_active,
"data": self.context.simulation_queue_data,
"order": self.context.simulation_queue_order,
"state": self.context.simulation_queue_state,
"worker_task": self.context.simulation_task.get_name() if self.context.simulation_task else None,
}
}
await self.context.sio.emit("queue_data_update", {
"data": data}, namespace="/")
async def empty_simulation_queue(): async def empty_simulation_queue():
"""Empties all items from an asyncio.Queue.""" """Empties all items from an asyncio.Queue."""
logger.info("Clearing location simulation queue...") logger.info("Clearing location simulation queue...")
q = self.context.simulation_queue q = self.context.simulation_queue
self.context.set_location_enabled = False
while not q.empty(): while not q.empty():
try: try:
item = q.get_nowait() item = q.get_nowait()
@@ -926,6 +964,7 @@ class TunneldRunnerSio:
def reset_queue(): def reset_queue():
self.context.simulation_queue_data = {} self.context.simulation_queue_data = {}
self.context.simulation_queue_order = [] self.context.simulation_queue_order = []
self.context.simulation_queue_deleted_items = []
self.context.set_current_loc_id(None) self.context.set_current_loc_id(None)
def remove_future_items(): def remove_future_items():
@@ -938,12 +977,16 @@ class TunneldRunnerSio:
else: else:
self.context.simulation_queue_data = {} self.context.simulation_queue_data = {}
self.context.simulation_queue_order = [] self.context.simulation_queue_order = []
self.context.simulation_queue_deleted_items = []
self.context.set_current_loc_id(None) self.context.set_current_loc_id(None)
def clear_item(item_id): async def delete_item(item_id):
if item_id in self.context.simulation_queue_order: if item_id in self.context.simulation_queue_order:
self.context.simulation_queue_data[item_id]["status"] = "deleted" self.context.simulation_queue_data[item_id]["status"] = "deleted"
self.context.simulation_queue_order.remove(item_id)
self.context.simulation_queue_deleted_items.append(item_id)
await update_queue_times()
def clear_future_items(): def clear_future_items():
current_loc_id = self.context.get_current_loc_id() current_loc_id = self.context.get_current_loc_id()
@@ -951,6 +994,8 @@ class TunneldRunnerSio:
current_loc_id) if current_loc_id else 0 current_loc_id) if current_loc_id else 0
for item in self.context.simulation_queue_order[current_index + 1:]: for item in self.context.simulation_queue_order[current_index + 1:]:
self.context.simulation_queue_data[item]["status"] = "deleted" self.context.simulation_queue_data[item]["status"] = "deleted"
self.context.simulation_queue_order.remove(item)
self.context.simulation_queue_deleted_items.append(item)
def get_item(item_id): def get_item(item_id):
return self.context.simulation_queue_data[item_id] return self.context.simulation_queue_data[item_id]
@@ -1006,20 +1051,13 @@ class TunneldRunnerSio:
end_simulation_worker(), name="end-simulation-worker" end_simulation_worker(), name="end-simulation-worker"
) )
result = await end_task result = await end_task
data = { return result
"command_status": "OK",
"command_class": "simulation_control",
"command": "end",
"message": "Simulation ended"
}
return data
async def end_simulation_worker() -> bool: async def end_simulation_worker() -> bool:
"""Ends asyncio.Queue playback and closes tunnel""" """Ends asyncio.Queue playback and closes tunnel"""
logger.info("End location simulation request") logger.info("End location simulation request")
try: try:
q = self.context.simulation_queue q = self.context.simulation_queue
self.context.set_location_enabled = False
self.context.simulation_queue_state = "SHUTDOWN" self.context.simulation_queue_state = "SHUTDOWN"
# Drain pending queue entries. # Drain pending queue entries.
@@ -1056,7 +1094,6 @@ class TunneldRunnerSio:
await locate_simulation.clear() await locate_simulation.clear()
self.context.simulation_active = False self.context.simulation_active = False
self.context.simulation_task = None self.context.simulation_task = None
self.context.set_location_enabled = True
self.context.next_move = None self.context.next_move = None
self.context.set_current_loc_id(None) self.context.set_current_loc_id(None)
self.context.simulation_queue_state = "STOPPED" self.context.simulation_queue_state = "STOPPED"
@@ -1079,6 +1116,19 @@ class TunneldRunnerSio:
self.context.simulation_noise = not self.context.simulation_noise self.context.simulation_noise = not self.context.simulation_noise
return {"simulation_noise": self.context.simulation_noise} return {"simulation_noise": self.context.simulation_noise}
def get_simulation_status() -> dict:
resp = {
"active": self.context.simulation_active,
"data": self.context.simulation_queue_data,
"order": self.context.simulation_queue_order,
"deleted_items": self.context.simulation_queue_deleted_items,
"state": self.context.simulation_queue_state,
"gps_noise": self.context.simulation_noise,
"worker_task": self.context.simulation_task.get_name() if self.context.simulation_task else None,
"test_mode": self.context.test_mode,
}
return resp
def get_status() -> dict: def get_status() -> dict:
current_loc_id = self.context.get_current_loc_id() current_loc_id = self.context.get_current_loc_id()
current_item = self.context.simulation_queue_data.get( current_item = self.context.simulation_queue_data.get(
@@ -1111,16 +1161,7 @@ class TunneldRunnerSio:
}, },
"tunnel_watcher_running": True if self.context.tunnel_watcher_task and not self.context.tunnel_watcher_task.done() else False, "tunnel_watcher_running": True if self.context.tunnel_watcher_task and not self.context.tunnel_watcher_task.done() else False,
"next_move": self.context.next_move, "next_move": self.context.next_move,
"simulation_queue": { "simulation_queue": get_simulation_status(),
"active": self.context.simulation_active,
"data": self.context.simulation_queue_data,
"order": self.context.simulation_queue_order,
"state": self.context.simulation_queue_state,
"gps_noise": self.context.simulation_noise,
"worker_task": self.context.simulation_task.get_name() if self.context.simulation_task else None,
},
"set_location_enable": self.context.set_location_enabled,
"test_mode": self.context.test_mode,
"tunnel": self.context.tunnel.service.address[0] if self.context.tunnel else None, "tunnel": self.context.tunnel.service.address[0] if self.context.tunnel else None,
"udid": self.context.udid, "udid": self.context.udid,
} }
@@ -1648,58 +1689,96 @@ class TunneldRunnerSio:
try: try:
match command: match command:
case "next": case "next":
resp = await advance_simulation_queue() await advance_simulation_queue()
resp = {
"command": command,
"command_class": "simulation_control",
"command_status": "OK",
"message": "Advanced simulation queue",
"data": {
"simulation_queue": get_simulation_status(),
}
}
return resp return resp
case "test-mode": case "test-mode":
data = toggle_test_mode() toggle_test_mode()
return { resp = {
"command": command, "command": command,
"command_class": "simulation_control", "command_class": "simulation_control",
"command_status": "OK", "command_status": "OK",
"message": "test-mode toggled", "message": "test-mode toggled",
"data": data "data": {
"simulation_queue": get_simulation_status(),
} }
}
return resp
case "add": case "add":
""" Add a location to the simulation queue""" """ Add a location to the simulation queue"""
resp = await add_location_to_simulation_queue(data) resp = await add_location_to_simulation_queue(data)
return resp return resp
case "delete":
""" Delete a location to the simulation queue"""
resp = await delete_location_from_simulation_queue(data)
return resp
case "clear": case "clear":
""" Clear the simulation queue""" """ Clear the simulation queue"""
clear_future_items() clear_future_items()
return { resp = {
"command": command, "command": command,
"command_class": "simulation_control", "command_class": "simulation_control",
"command_status": "OK", "command_status": "OK",
"message": "Simulation cleared", "message": "Simulation cleared",
"data": {
"simulation_queue": get_simulation_status(),
} }
}
return resp
case "pause": case "pause":
""" Pause the simulation queue""" """ Pause the simulation queue"""
await pause_simulation_queue() await pause_simulation_queue()
return { resp = {
"command": command, "command": command,
"command_class": "simulation_control", "command_class": "simulation_control",
"command_status": "OK", "command_status": "OK",
"message": "Simulation paused", "message": "Simulation paused",
"data": {
"simulation_queue": get_simulation_status(),
} }
}
return resp
case "resume": case "resume":
""" Resume the simulation queue""" """ Resume the simulation queue"""
await resume_simulation_queue() await resume_simulation_queue()
return { resp = {
"command": command, "command": command,
"command_status": "OK", "command_status": "OK",
"command_class": "simulation_control", "command_class": "simulation_control",
"message": "Simulation resumed", "message": "Simulation resumed",
"data": {
"simulation_queue": get_simulation_status(),
} }
}
return resp
case "end": case "end":
""" End the simulation queue""" """ End the simulation queue"""
logger.info( logger.info(
"End location simulation request from %s", sid) "End location simulation request from %s", sid)
data = await end_simulation_queue() cstat = "OK" if await end_simulation_queue() else "ERROR"
return data resp = {
"command_status": cstat,
"command_class": "simulation_control",
"command": command,
"message": "Simulation ended",
"data": {
"simulation_queue": get_simulation_status(),
}
}
return resp
case "start": case "start":
""" Start the simulation queue""" """ Start the simulation queue"""
logger.info( logger.info(
@@ -1709,19 +1788,24 @@ class TunneldRunnerSio:
case "gps-noise": case "gps-noise":
""" Toggle GPS noise""" """ Toggle GPS noise"""
before_toggle = self.context.simulation_noise before_toggle = self.context.simulation_noise
data = toggle_gps_noise() toggle_gps_noise()
data['command_status'] = "OK" resp = {
data['command_class'] = "simulation_control" "command_status": "OK",
data['message'] = f"GPS noise toggled from { "command_class": "simulation_control",
before_toggle} to {self.context.simulation_noise}" "command": command,
data['command'] = command "message": f"GPS noise toggled from {before_toggle} to {self.context.simulation_noise}",
return data "data": {
"simulation_queue": get_simulation_status(),
}
}
return resp
case _: case _:
logger.warning( logger.warning(
"Invalid command received from %s: %s", sid, command "Invalid command received from %s: %s", sid, command
) )
return {"command_status": "ERROR", "message": "Invalid command"} return {"command_status": "ERROR", "message": "Invalid command"}
finally: finally:
logger.info("Simulation Control command: %s completed, sending status update...", command)
await sio_send_status(sid) await sio_send_status(sid)
@self.context.sio.event @self.context.sio.event
@@ -1754,6 +1838,37 @@ class TunneldRunnerSio:
else: else:
return {"command_status": "ERROR", "command": "update", "command_class": "location_item", "message": "Invalid request, Location Item Unchanged", "data": self.context.simulation_queue_data[loc_id]} return {"command_status": "ERROR", "command": "update", "command_class": "location_item", "message": "Invalid request, Location Item Unchanged", "data": self.context.simulation_queue_data[loc_id]}
@self.context.sio.event
async def queue_order_update(sid, data):
logger.info("Queue order update received: %s", data)
new_simulation_queue_order = []
new_order = (
data.get("newOrder")
if isinstance(data, dict)
else getattr(data, "newOrder", None)
)
if new_order:
for item in new_order:
if item in self.context.simulation_queue_data:
new_simulation_queue_order.append(item)
self.context.simulation_queue_order = new_simulation_queue_order
resp = {
"command_status": "OK",
"command": "queue_order_update",
"command_class": "queue_order",
"message": "Queue Order Updated",
"data": self.context.simulation_queue_order
}
else:
resp = {
"command_status": "ERROR",
"command": "queue_order_update",
"command_class": "queue_order",
"message": "Invalid request, Queue Order Unchanged",
"data": self.context.simulation_queue_order
}
return resp
@self.context.sio.event @self.context.sio.event
async def icloud_monitor_control(sid, data): async def icloud_monitor_control(sid, data):
command = ( command = (
@@ -1961,6 +2076,9 @@ class TunneldRunnerSio:
class LocationSimulationQueue(LocationSimulation): class LocationSimulationQueue(LocationSimulation):
def __init__(self, dvt, context: LocationSimulationState): def __init__(self, dvt, context: LocationSimulationState):
if dvt is None:
LocationSimulationBase.__init__(self)
else:
super().__init__(dvt) super().__init__(dvt)
self.context = context self.context = context
self._dt_simulate_location = None self._dt_simulate_location = None
@@ -1981,6 +2099,10 @@ class LocationSimulationQueue(LocationSimulation):
return self._dt_simulate_location return self._dt_simulate_location
async def set(self, latitude: float, longitude: float) -> None: async def set(self, latitude: float, longitude: float) -> None:
if self.context.test_mode:
await asyncio.sleep(0.1)
logger.info("Test mode enabled, simulated location set to %s, %s", latitude, longitude)
return
if not self._prefer_dt_simulate_location: if not self._prefer_dt_simulate_location:
try: try:
await super().set(latitude, longitude) await super().set(latitude, longitude)
@@ -1994,6 +2116,9 @@ class LocationSimulationQueue(LocationSimulation):
await fallback.set(latitude, longitude) await fallback.set(latitude, longitude)
async def clear(self) -> None: async def clear(self) -> None:
if self.context.test_mode:
logger.info("Test mode enabled, skipping location clear")
return
dvt_clear_error = None dvt_clear_error = None
if not self._prefer_dt_simulate_location: if not self._prefer_dt_simulate_location:
try: try:
@@ -2036,8 +2161,6 @@ class LocationSimulationQueue(LocationSimulation):
break break
if self.context.simulation_queue_state == "SHUTDOWN": if self.context.simulation_queue_state == "SHUTDOWN":
break break
if not self.context.set_location_enabled:
continue
if not self.context.simulation_noise: if not self.context.simulation_noise:
continue continue
if self.context.get_current_loc_id() != loc_id: if self.context.get_current_loc_id() != loc_id:
@@ -2064,16 +2187,16 @@ class LocationSimulationQueue(LocationSimulation):
async def _update_queue_data(self): async def _update_queue_data(self):
data = { data = {
"simulation_queue": {
"active": self.context.simulation_active, "active": self.context.simulation_active,
"data": self.context.simulation_queue_data, "data": self.context.simulation_queue_data,
"order": self.context.simulation_queue_order, "order": self.context.simulation_queue_order,
"deleted_items": self.context.simulation_queue_deleted_items,
"state": self.context.simulation_queue_state, "state": self.context.simulation_queue_state,
"worker_task": self.context.simulation_task.get_name() if self.context.simulation_task else None, "worker_task": self.context.simulation_task.get_name() if self.context.simulation_task else None,
"gps_noise": self.context.simulation_noise,
"test_mode": self.context.test_mode,
} }
} await self.context.sio.emit("queue_data_update", data, namespace="/")
await self.context.sio.emit("queue_data_update", {
"data": data}, namespace="/")
async def _update_location_item(self, loc_id: str) -> None: async def _update_location_item(self, loc_id: str) -> None:
await self.context.sio.emit( await self.context.sio.emit(
@@ -2085,6 +2208,18 @@ class LocationSimulationQueue(LocationSimulation):
namespace="/", namespace="/",
) )
async def _update_current_location(self, active_loc_id, new_latitude, new_longitude) -> None:
await self.context.sio.emit(
"simulation_status",
{
"status": self.context.simulation_active,
"loc_id": active_loc_id,
"latitude": new_latitude,
"longitude": new_longitude
},
namespace="/",
)
async def _enqueue_next_queue_item(self) -> Optional[str]: async def _enqueue_next_queue_item(self) -> Optional[str]:
if self.context.simulation_queue_state == "SHUTDOWN": if self.context.simulation_queue_state == "SHUTDOWN":
return None return None
@@ -2156,37 +2291,17 @@ class LocationSimulationQueue(LocationSimulation):
if isinstance(current_location_item, dict) if isinstance(current_location_item, dict)
else None else None
) )
if self.context.set_location_enabled:
while self.context.simulation_queue_data[loc_id]["delay"] > 0: while self.context.simulation_queue_data[loc_id]["delay"] > 0:
if self.context.simulation_queue_state == "NEXT":
self.context.simulation_queue_state = "RUNNING"
break
if self.context.simulation_queue_state == "SHUTDOWN": if self.context.simulation_queue_state == "SHUTDOWN":
break break
while self.context.simulation_queue_state == "PAUSED": while self.context.simulation_queue_state == "PAUSED":
await asyncio.sleep(0.1) await asyncio.sleep(0.1)
if self.context.simulation_queue_state == "SHUTDOWN": if self.context.simulation_queue_state == "SHUTDOWN":
break break
if self.context.simulation_queue_state == "NEXT":
self.context.simulation_queue_state = "RUNNING"
break
if self.context.simulation_queue_state == "SHUTDOWN": if self.context.simulation_queue_state == "SHUTDOWN":
break break
self.context.next_move = location_item.get("delay") - 1 self.context.next_move = location_item.get("delay") - 1
self.context.simulation_queue_data[loc_id]["delay"] -= 1 self.context.simulation_queue_data[loc_id]["delay"] -= 1
# await self.context.sio.emit(
# "simulation_status",
# {
# "status": self.context.simulation_active,
# "loc_id": current_loc_id,
# "latitude": current_latitude,
# "longitude": current_longitude,
# "start": current_start,
# "next_move": self.context.next_move,
# },
# namespace="/",
# )
await self._update_location_item(loc_id) await self._update_location_item(loc_id)
await asyncio.sleep(1) await asyncio.sleep(1)
if self.context.simulation_queue_state == "SHUTDOWN": if self.context.simulation_queue_state == "SHUTDOWN":
@@ -2199,301 +2314,19 @@ class LocationSimulationQueue(LocationSimulation):
self.context.simulation_queue_data[current_loc_id]["status"] = "done" self.context.simulation_queue_data[current_loc_id]["status"] = "done"
self.context.simulation_queue_data[current_loc_id]["end"] = datetime.now( self.context.simulation_queue_data[current_loc_id]["end"] = datetime.now(
timezone.utc).isoformat() timezone.utc).isoformat()
self.context.simulation_queue_data[current_loc_id]["delay"] = new_delay
await self._update_location_item(current_loc_id) await self._update_location_item(current_loc_id)
await self._update_queue_data() await self._update_queue_data()
await self._stop_noise_task() await self._stop_noise_task()
await self.set(new_latitude, new_longitude) await self.set(new_latitude, new_longitude)
self.context.simulation_queue_data[loc_id]["status"] = "set" self.context.simulation_queue_data[loc_id]["status"] = "set"
await self._update_location_item(loc_id)
self.context.set_current_loc_id(loc_id) self.context.set_current_loc_id(loc_id)
active_loc_id = self.context.get_current_loc_id()
await self._update_current_location(active_loc_id, new_latitude, new_longitude)
if self.context.simulation_noise: if self.context.simulation_noise:
self._start_noise_task( self._start_noise_task(
loc_id, new_latitude, new_longitude) loc_id, new_latitude, new_longitude)
active_loc_id = self.context.get_current_loc_id()
# await self.context.sio.emit(
# "simulation_status",
# {
# "status": self.context.simulation_active,
# "loc_id": active_loc_id,
# "latitude": new_latitude,
# "longitude": new_longitude,
# "start": new_start,
# "next_move": None,
# },
# namespace="/",
# )
logger.info(
"Set simulated location to %s, %s after %ss delay",
new_latitude,
new_longitude,
new_delay,
)
await self._update_queue_data()
self.context.simulation_queue_pending_ids.discard(loc_id)
self.context.simulation_queue.task_done()
await self._enqueue_next_queue_item()
finally:
await self._stop_noise_task()
class LocationSimulationTestQueue(LocationSimulationBase):
def __init__(self, context: LocationSimulationState):
super().__init__()
self.context = context
self._noise_task: Optional[asyncio.Task] = None
self._noise_loc_id: Optional[str] = None
def __enter__(self):
return self
def __exit__(self):
return self
async def set(self, latitude: float, longitude: float) -> None:
await asyncio.sleep(0.1)
logger.info("Simulated location set to %s, %s", latitude, longitude)
async def clear(self) -> None:
q = self.context.simulation_queue
self.context.set_location_enabled = False
self.context.simulation_queue_state = "SHUTDOWN"
while not q.empty():
try:
item = q.get_nowait()
if isinstance(item, str):
self.context.simulation_queue_pending_ids.discard(item)
q.task_done()
logger.info("Discarding item from queue: %s", item)
except asyncio.QueueEmpty:
break
self.context.simulation_queue_pending_ids.clear()
await q.put(None)
if self.context.simulation_task is not None and not self.context.simulation_task.done():
try:
await asyncio.wait_for(self.context.simulation_task, timeout=5)
except TimeoutError:
self.context.simulation_task.cancel()
with suppress(asyncio.CancelledError):
await self.context.simulation_task
self.context.simulation_active = False
self.context.simulation_queue_state = "SHUTDOWN"
@staticmethod
def _add_gps_noise(lat: float, lon: float, std_dev_meters: float = 5.0) -> tuple[float, float]:
"""Apply Gaussian jitter in meters and convert to lat/lon deltas."""
earth_radius = 6378137.0
lat_sigma_deg = (std_dev_meters / earth_radius) * (180.0 / math.pi)
cos_lat = math.cos(math.radians(lat))
if abs(cos_lat) < 1e-6:
cos_lat = 1e-6
lon_sigma_deg = (std_dev_meters / (earth_radius *
cos_lat)) * (180.0 / math.pi)
noised_lat = lat + random.gauss(0.0, lat_sigma_deg)
noised_lon = lon + random.gauss(0.0, lon_sigma_deg)
return noised_lat, noised_lon
async def _stop_noise_task(self) -> None:
if self._noise_task is not None:
self._noise_task.cancel()
with suppress(asyncio.CancelledError):
await self._noise_task
self._noise_task = None
self._noise_loc_id = None
async def _noise_loop(self, loc_id: str, base_latitude: float, base_longitude: float) -> None:
while True:
await asyncio.sleep(random.randint(45, 180))
if not self.context.simulation_active:
break
if self.context.simulation_queue_state == "SHUTDOWN":
break
if not self.context.set_location_enabled:
continue
if not self.context.simulation_noise:
continue
if self.context.get_current_loc_id() != loc_id:
break
noised_latitude, noised_longitude = self._add_gps_noise(
base_latitude, base_longitude
)
await self.set(noised_latitude, noised_longitude)
logger.info(
"Applied simulation noise to active location loc_id=%s sent=%s,%s base=%s,%s",
loc_id,
noised_latitude,
noised_longitude,
base_latitude,
base_longitude,
)
def _start_noise_task(self, loc_id: str, base_latitude: float, base_longitude: float) -> None:
self._noise_loc_id = loc_id
self._noise_task = asyncio.create_task(
self._noise_loop(loc_id, base_latitude, base_longitude),
name=f"simulation-noise-{loc_id}",
)
async def _update_queue_data(self):
data = {
"simulation_queue": {
"active": self.context.simulation_active,
"data": self.context.simulation_queue_data,
"order": self.context.simulation_queue_order,
"state": self.context.simulation_queue_state,
"worker_task": self.context.simulation_task.get_name() if self.context.simulation_task else None,
}
}
await self.context.sio.emit("queue_data_update", {
"data": data}, namespace="/")
async def _update_location_item(self, loc_id: str) -> None:
await self.context.sio.emit(
"location_item_update",
{
"loc_id": loc_id,
"data": self.context.simulation_queue_data[loc_id]
},
namespace="/",
)
async def _enqueue_next_queue_item(self) -> Optional[str]:
if self.context.simulation_queue_state == "SHUTDOWN":
return None
for item_id in self.context.simulation_queue_order:
if item_id in self.context.simulation_queue_pending_ids:
continue
item = self.context.simulation_queue_data.get(item_id)
if not isinstance(item, dict):
continue
if item.get("status") != "queued":
continue
self.context.simulation_queue_pending_ids.add(item_id)
await self.context.simulation_queue.put(item_id)
logger.info("Worker scheduled queue item %s", item_id)
return item_id
return None
async def play_queue(
self, disable_sleep: bool = False, timing_randomness_range: int = 0
) -> None:
try:
while True:
if self.context.simulation_queue_state == "PAUSED":
await asyncio.sleep(0.1)
continue
if self.context.simulation_queue_state == "SHUTDOWN":
break
await self._enqueue_next_queue_item()
loc_id = await self.context.simulation_queue.get()
if loc_id is None:
self.context.simulation_queue.task_done()
break
location_item = self.context.simulation_queue_data.get(loc_id)
if location_item is None:
logger.warning(
"Test simulation queue item missing for loc_id=%s; skipping stale entry",
loc_id,
)
self.context.simulation_queue_pending_ids.discard(loc_id)
self.context.simulation_queue.task_done()
await self._enqueue_next_queue_item()
continue
new_status = location_item.get("status")
if new_status == "deleted":
self.context.simulation_queue_pending_ids.discard(loc_id)
self.context.simulation_queue.task_done()
await self._enqueue_next_queue_item()
continue
new_latitude = location_item.get("latitude")
new_longitude = location_item.get("longitude")
new_delay = location_item.get("delay")
new_delay = 0 if new_delay is None else new_delay
new_start = location_item.get("start")
current_loc_id = self.context.get_current_loc_id()
current_location_item = self.context.simulation_queue_data.get(
current_loc_id) if current_loc_id else None
current_latitude = (
current_location_item.get("latitude")
if isinstance(current_location_item, dict)
else None
)
current_longitude = (
current_location_item.get("longitude")
if isinstance(current_location_item, dict)
else None
)
current_start = (
current_location_item.get("start")
if isinstance(current_location_item, dict)
else None
)
if self.context.set_location_enabled:
while self.context.simulation_queue_data[loc_id]["delay"] > 0:
if self.context.simulation_queue_state == "NEXT":
self.context.simulation_queue_state = "RUNNING"
break
if self.context.simulation_queue_state == "SHUTDOWN":
break
while self.context.simulation_queue_state == "PAUSED":
await asyncio.sleep(0.1)
if self.context.simulation_queue_state == "SHUTDOWN":
break
if self.context.simulation_queue_state == "NEXT":
self.context.simulation_queue_state = "RUNNING"
break
if self.context.simulation_queue_state == "SHUTDOWN":
break
self.context.next_move = location_item.get("delay") - 1
self.context.simulation_queue_data[loc_id]["delay"] -= 1
# await self.context.sio.emit(
# "simulation_status",
# {
# "status": self.context.simulation_active,
# "loc_id": current_loc_id,
# "latitude": current_latitude,
# "longitude": current_longitude,
# "start": current_start,
# "next_move": self.context.next_move,
# },
# namespace="/",
# )
await self._update_location_item(loc_id)
await asyncio.sleep(1)
if self.context.simulation_queue_state == "SHUTDOWN":
self.context.simulation_queue_pending_ids.discard(loc_id)
self.context.simulation_queue.task_done()
break
self.context.simulation_queue_data[loc_id]["start"] = datetime.now(
timezone.utc).isoformat()
if current_loc_id is not None:
self.context.simulation_queue_data[current_loc_id]["status"] = "done"
self.context.simulation_queue_data[current_loc_id]["end"] = datetime.now(
timezone.utc).isoformat()
await self._update_location_item(current_loc_id)
await self._update_queue_data()
await self._stop_noise_task()
await self.set(new_latitude, new_longitude)
self.context.simulation_queue_data[loc_id]["status"] = "set"
await self._update_location_item(loc_id)
self.context.set_current_loc_id(loc_id)
if self.context.simulation_noise:
self._start_noise_task(
loc_id, new_latitude, new_longitude)
active_loc_id = self.context.get_current_loc_id()
# await self.context.sio.emit(
# "simulation_status",
# {
# "status": self.context.simulation_active,
# "loc_id": active_loc_id,
# "latitude": new_latitude,
# "longitude": new_longitude,
# "start": new_start,
# "next_move": None,
# },
# namespace="/",
# )
logger.info( logger.info(
"Set simulated location to %s, %s after %ss delay", "Set simulated location to %s, %s after %ss delay",
new_latitude, new_latitude,