forked from IQ.Lvbs/IQ.Pilot
IQ.Pilot Prebuilt Release @ 67fd9c2
This commit is contained in:
131
iqpilot/system/ui/widgets/esim_scanner.py
Normal file
131
iqpilot/system/ui/widgets/esim_scanner.py
Normal file
@@ -0,0 +1,131 @@
|
||||
import numpy as np
|
||||
import pyray as rl
|
||||
|
||||
from iqpilot.system.ui.lib.application import gui_app, FontWeight
|
||||
from iqpilot.system.ui.lib.multilang import tr
|
||||
from iqpilot.system.ui.widgets import DialogResult, NavWidget
|
||||
from iqpilot.system.ui.widgets.label import gui_label
|
||||
from iqpilot.selfdrive.ui.ui_state import ui_state, device
|
||||
from iqpilot.system.hardware.tici.qr_decode import decode_qr, stable_code_key, validate_lpa_activation_code
|
||||
|
||||
if gui_app.big_ui():
|
||||
from iqpilot.selfdrive.ui.onroad.driver_camera_dialog import DriverCameraDialog
|
||||
else:
|
||||
from iqpilot.selfdrive.ui.mici.onroad.driver_camera_dialog import DriverCameraDialog
|
||||
|
||||
|
||||
class EsimQrScannerDialog(NavWidget):
|
||||
REQUIRED_MATCH_FRAMES = 3
|
||||
DECODE_EVERY_N_FRAMES = 3
|
||||
NO_CAMERA_MESSAGE_DELAY_SEC = 3.0
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._is_big_ui = gui_app.big_ui()
|
||||
self._camera = DriverCameraDialog() if self._is_big_ui else DriverCameraDialog(no_escape=True)
|
||||
self._candidate_key = ""
|
||||
self._candidate_count = 0
|
||||
self.code: str | None = None
|
||||
self._status = tr("Point the driver camera at a carrier eSIM QR code (LPA:...)")
|
||||
self._frame_count = 0
|
||||
self._no_camera_elapsed_sec = 0.0
|
||||
self._result = DialogResult.NO_ACTION
|
||||
|
||||
self.set_back_callback(lambda: setattr(self, "_result", DialogResult.CANCEL))
|
||||
|
||||
def _get_frame(self):
|
||||
return self._camera.frame if self._is_big_ui else self._camera._camera_view.frame
|
||||
|
||||
def show_event(self):
|
||||
super().show_event()
|
||||
self._camera.show_event()
|
||||
ui_state.params.put_bool_nonblocking("IsDriverViewEnabled", True)
|
||||
device.set_override_interactive_timeout(300)
|
||||
self._no_camera_elapsed_sec = 0.0
|
||||
|
||||
def hide_event(self):
|
||||
super().hide_event()
|
||||
ui_state.params.put_bool("IsDriverViewEnabled", False)
|
||||
device.set_override_interactive_timeout(None)
|
||||
self._camera.hide_event()
|
||||
|
||||
def _update_state(self):
|
||||
super()._update_state()
|
||||
if device.awake and not ui_state.params.get_bool("IsDriverViewEnabled"):
|
||||
ui_state.params.put_bool_nonblocking("IsDriverViewEnabled", True)
|
||||
self._frame_count += 1
|
||||
|
||||
frame = self._get_frame()
|
||||
if frame is None:
|
||||
self._no_camera_elapsed_sec += 1.0 / gui_app.target_fps
|
||||
if self._no_camera_elapsed_sec >= self.NO_CAMERA_MESSAGE_DELAY_SEC:
|
||||
self._status = tr("Waiting for driver camera. Use the carrier eSIM QR code or enter the LPA code manually.")
|
||||
return
|
||||
|
||||
self._no_camera_elapsed_sec = 0.0
|
||||
if frame is None or (self._frame_count % self.DECODE_EVERY_N_FRAMES != 0):
|
||||
return
|
||||
|
||||
try:
|
||||
y = np.frombuffer(frame.data[:frame.uv_offset], dtype=np.uint8).reshape((-1, frame.stride))[:frame.height, :frame.width]
|
||||
except Exception:
|
||||
return
|
||||
|
||||
codes = decode_qr(y)
|
||||
if not codes:
|
||||
self._candidate_key = ""
|
||||
self._candidate_count = 0
|
||||
self._status = tr("No QR detected")
|
||||
return
|
||||
|
||||
valid_payload = ""
|
||||
validation_error = ""
|
||||
for payload in codes:
|
||||
valid, err = validate_lpa_activation_code(payload)
|
||||
if valid:
|
||||
valid_payload = payload
|
||||
break
|
||||
validation_error = err
|
||||
|
||||
if not valid_payload:
|
||||
self._candidate_key = ""
|
||||
self._candidate_count = 0
|
||||
self._status = validation_error or tr("Invalid QR code")
|
||||
return
|
||||
|
||||
key = stable_code_key(valid_payload)
|
||||
if key == self._candidate_key:
|
||||
self._candidate_count += 1
|
||||
else:
|
||||
self._candidate_key = key
|
||||
self._candidate_count = 1
|
||||
|
||||
if self._candidate_count >= self.REQUIRED_MATCH_FRAMES:
|
||||
self.code = valid_payload
|
||||
self._status = tr("QR accepted")
|
||||
self._result = DialogResult.CONFIRM
|
||||
else:
|
||||
self._status = tr("Hold steady...")
|
||||
|
||||
def _render(self, rect: rl.Rectangle):
|
||||
self._camera.render(rect)
|
||||
|
||||
if self._get_frame() is None:
|
||||
gui_label(
|
||||
rect,
|
||||
tr("camera starting"),
|
||||
font_size=72 if gui_app.big_ui() else 44,
|
||||
font_weight=FontWeight.BOLD,
|
||||
alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER,
|
||||
)
|
||||
else:
|
||||
text_rect = rl.Rectangle(rect.x + 40, rect.y + 40, rect.width - 80, 90)
|
||||
gui_label(
|
||||
text_rect,
|
||||
self._status,
|
||||
font_size=50 if gui_app.big_ui() else 30,
|
||||
font_weight=FontWeight.MEDIUM,
|
||||
alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT,
|
||||
)
|
||||
|
||||
return self._result
|
||||
Reference in New Issue
Block a user