From 6a43e0eadabdd06d238dca9a07e30d2e9439ec17 Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Tue, 9 Jul 2024 15:10:42 -0400 Subject: [PATCH] update keychip serial generation code --- core/const.py | 27 +++++++++-------- core/data/schema/arcade.py | 61 +++++++++++++++++++++++++++++++------- core/frontend.py | 9 ++++-- 3 files changed, 71 insertions(+), 26 deletions(-) diff --git a/core/const.py b/core/const.py index 98effb6..535a1bb 100644 --- a/core/const.py +++ b/core/const.py @@ -1,16 +1,18 @@ from enum import Enum -class MainboardPlatformCodes: - RINGEDGE = "AALE" - RINGWIDE = "AAML" - NU = "AAVE" - NUSX = "AAWE" - ALLS_UX = "ACAE" - ALLS_HX = "ACAX" +class MainboardPlatformCodes(Enum): + RINGEDGE = "AAL" + RINGEDGE2 = "AAS" + RINGWIDE = "AAM" + NU = "AAV" + NUSX = "AAW" + ALLS = "ACA" + #ALLS_UX = "ACAE" + #ALLS_HX = "ACAX" -class MainboardRevisions: +class MainboardRevisions(Enum): RINGEDGE = 1 RINGEDGE2 = 2 @@ -29,11 +31,10 @@ class MainboardRevisions: ALLS_HX2 = 12 -class KeychipPlatformsCodes: - RING = "A72E" - NU = ("A60E", "A60E", "A60E") - NUSX = ("A61X", "A69X") - ALLS = "A63E" +class KeychipPlatformsCodes(Enum): + RING = "72" + NU = ("60", "61", "69") + ALLS = "63" class AllnetCountryCode(Enum): diff --git a/core/data/schema/arcade.py b/core/data/schema/arcade.py index 680f827..3e83bc5 100644 --- a/core/data/schema/arcade.py +++ b/core/data/schema/arcade.py @@ -206,17 +206,6 @@ class ArcadeData(BaseData): return None return result.lastrowid - def format_serial( # TODO: Actual serial stuff - self, platform_code: str, platform_rev: int, serial_num: int, append: int = 8888 - ) -> str: - return f"{platform_code}{platform_rev:02d}A{serial_num:04d}{append:04d}" # 0x41 = A, 0x52 = R - - def validate_keychip_format(self, serial: str) -> bool: - if re.fullmatch(r"^A[0-9]{2}[E|X][-]?[0-9]{2}[A-HJ-NP-Z][0-9]{4}([0-9]{4})?$", serial) is None: - return False - - return True - async def get_arcade_by_name(self, name: str) -> Optional[List[Row]]: sql = arcade.select(or_(arcade.c.name.like(f"%{name}%"), arcade.c.nickname.like(f"%{name}%"))) result = await self.execute(sql) @@ -230,3 +219,53 @@ class ArcadeData(BaseData): if result is None: return None return result.fetchall() + + async def get_num_generated_keychips(self) -> Optional[int]: + result = await self.execute(select(func.count("serial LIKE 'A69A%'")).select_from(machine)) + if result: + return result.fetchone()['count_1'] + self.logger.error("Failed to count machine serials that start with A69A!") + + def format_serial( + self, platform_code: str, platform_rev: int, serial_letter: str, serial_num: int, append: int, dash: bool = False + ) -> str: + return f"{platform_code}{'-' if dash else ''}{platform_rev:02d}{serial_letter}{serial_num:04d}{append:04d}" + + def validate_keychip_format(self, serial: str) -> bool: + # For the 2nd letter, E and X are the only "real" values that have been observed + if re.fullmatch(r"^A[0-9]{2}[A-Z][-]?[0-9]{2}[A-HJ-NP-Z][0-9]{4}([0-9]{4})?$", serial) is None: + return False + + return True + + # Thanks bottersnike! + def get_keychip_suffix(self, year: int, month: int) -> str: + assert year > 1957 + assert 1 <= month <= 12 + + year -= 1957 + # Jan/Feb/Mar are from the previous tax year + if month < 4: + year -= 1 + assert year >= 1 and year <= 99 + + month = ((month - 1) + 9) % 12 # Offset so April=0 + return f"{year:02}{month // 6:01}{month % 6 + 1:01}" + + + def parse_keychip_suffix(self, suffix: str) -> tuple[int, int]: + year = int(suffix[0:2]) + half = int(suffix[2]) + assert half in (0, 1) + period = int(suffix[3]) + assert period in (1, 2, 3, 4, 5, 6) + + month = half * 6 + (period - 1) + month = ((month + 3) % 12) + 1 # Offset so Jan=1 + + # Jan/Feb/Mar are from the previous tax year + if month < 4: + year += 1 + year += 1957 + + return (year, month) diff --git a/core/frontend.py b/core/frontend.py index 1d19bfe..d070aa1 100644 --- a/core/frontend.py +++ b/core/frontend.py @@ -826,14 +826,19 @@ class FE_System(FE_Base): return RedirectResponse("/sys/?e=4", 303) if not serial: - serial = self.data.arcade.format_serial("A69E", 1, random.randint(1, 9999)) + append = self.data.arcade.get_keychip_suffix(datetime.now().year, datetime.now().month) + generated = await self.data.arcade.get_num_generated_keychips() + if not generated: + generated = 0 + serial = self.data.arcade.format_serial("A69A", 1, "A", generated + 1, int(append)) + serial_dash = self.data.arcade.format_serial("A69A", 1, "A", generated + 1, int(append), True) cab_id = await self.data.arcade.create_machine(int(shopid), serial, None, game_code if game_code else None) return Response(template.render( title=f"{self.core_config.server.name} | System", sesh=vars(usr_sesh), - cabadd={"id": cab_id, "serial": serial}, + cabadd={"id": cab_id, "serial": serial_dash}, ), media_type="text/html; charset=utf-8") async def render_logs(self, request: Request):