diff --git a/database/pbv3_add_interposer.py b/database/pbv3_add_interposer.py new file mode 100644 index 0000000000000000000000000000000000000000..f4cd7a6bdb56b9bdad60fe3fbae0f88747396219 --- /dev/null +++ b/database/pbv3_add_interposer.py @@ -0,0 +1,161 @@ +import os +import itkdb +import csv +import argparse +import sys + +from database import pwbdbtools + +''' +Run this script to manually add an interposer component to existing powerboards, +given their parent panel ID. +Assume that the user has configurated their itkpd account. +''' + +# ------------------------------------------ +# Function: read_PWB_from_csv +# Description: temp function for reading CSV files +# Arguments: path to the csv file +# Returns: a list of PWB ID +# ------------------------------------------ +def read_PWB_from_csv(csv_file_path): + loaded_PWB = [] + + with open(csv_file_path, mode='r', newline='') as file: + reader = csv.reader(file) + + # Skip the first row (header) + next(reader) + + for row in reader: + # Extract the fourth to thirteenth columns (index 3 to 12) + selected_columns = row[3:13] + # Filter out blank entries + filtered_columns = [f'20USBP0{value}' for value in selected_columns if value.strip()] + # Only append rows that have non-blank entries + if filtered_columns: + loaded_PWB.extend(filtered_columns) + + return loaded_PWB + + +# ------------------------------------------ +# Function: read_panel +# Description: use panel ID to search for PWB +# Arguments: a list of panel ID +# Returns: a list of child PWB ID +# ------------------------------------------ +def read_panel(client, panel_ls): + PWB_ls = [] + + for panel_ID in panel_ls: + panel = client.get("getComponent", json={"component": panel_ID}) + + child_PWB = [ + child["component"]["id"] + for child in panel.get("children", []) + if (child["component"] and child['componentType']['code']=="PWB") + ] + + PWB_ls.extend(child_PWB) + print(f"Panel {panel_ID} has been processed and {child_PWB} are added to the list.") + + return PWB_ls + +# ------------------------------------------ +# Function: load_interposer +# Description: create and load an interpsoer for each of the PWB +# Arguments: a list of PWB ID +# Returns: NA +# ------------------------------------------ +def load_interposer(client, PWB_ls, check_interposer=True): + + component_template = client.get('generateComponentTypeDtoSample', json={'project': 'S', 'code': 'PWB_INTERPOSER'}) + for PWB_ID in PWB_ls: + + PWB = client.get("getComponent", json={"component": PWB_ID}) + + if check_interposer: + if any((PWBchild['componentType']['code'] == 'PWB_INTERPOSER' and PWBchild['component']) for PWBchild in PWB.get("children", [])): + print(f"PWB {PWB['serialNumber']} has already been loaded with an interposer.") + continue + + if PWB.get('inTransit', False) == True: + print(f"PWB {PWB['serialNumber']} is in transit and cannot be loaded with an interposer.") + continue + + try: + new_interposer = client.post( + "registerComponent", + json={ + **component_template, + 'subproject': 'SB', + 'institution': 'LBNL_STRIP_POWERBOARDS', + } + ) + + # Hard coded parameters. + output = client.post( + "assembleComponent", + json={ + "parent": PWB['serialNumber'], + "child": new_interposer['component']['code'], + "properties": { + "GLASS_BEADS_USED": True, + 'INTERPOSER_MATERIAL': 'Kapton', + 'INTERPOSER_THICKNESS': 50, + 'INTERPOSER_ADHESIVE': 'SE4445', + 'ADHESIVE_THICKNESS': 110, + 'INTERPOSER_SURFACE_PREP': 'IPA', + }, + } + ) + print(f"Interposer {new_interposer['component']['code']} has been created and mounted on PWB {output['serialNumber']}.") + + except Exception as e: + print(f"An error occurred while processing PWB {PWB['serialNumber']}: {e}") + +# ------------------------------------------ +# Function: load_interposer_for_panel +# Description: use panel number to add interposer for all pwb's on this panel +# Arguments: panel id +# Returns: NA +# ------------------------------------------ +def load_interposer_for_panel(client, panel_id, check_interposer_in=True): + pwb_ls_in = read_panel(client, [panel_id]) + load_interposer(client, pwb_ls_in, check_interposer=check_interposer_in) + + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Process either PWB number or Panel number, but not neither.") + + # Take in PWB and Panel numbers + parser.add_argument('-p', '--pwbSN', nargs='+', help="PWB serial number(s)") + parser.add_argument('-c', '--panel', nargs='+', help="Panel serial number(s)") + + args = parser.parse_args() + + # Check if either PWB number or Panel number is provided (but not both or neither) + if not args.pwbSN and not args.panel: + print("Error: You must provide either a PWB number or a Panel number.") + parser.print_help() + sys.exit(1) + elif args.pwbSN and args.panel: + print("Error: You cannot provide both a PWB number and a Panel number.") + parser.print_help() + sys.exit(1) + + c = pwbdbtools.get_db_client() + + pwb_ls = [] + + if args.pwbSN: + print(f"PWB number(s) provided: {args.pwbSN}") + pwb_ls = args.pwbSN + + if args.panel: + print(f"Panel number(s) provided: {args.panel}") + pwb_ls = read_panel(c, args.panel) + + load_interposer(c, pwb_ls) diff --git a/database/pbv3_autoRegister.py b/database/pbv3_autoRegister.py index e94da986069833e3dc56ded017f9847d330eccf9..70f0af61d9c9a47a7452fe6908fcf8f911b7374a 100755 --- a/database/pbv3_autoRegister.py +++ b/database/pbv3_autoRegister.py @@ -15,6 +15,7 @@ from database import pbv3_panel_loadshieldbox from database import pbv3_panel_singulate from database import pbv3_panel_loadhvmux from database import pbv3_panel_loadamac +from database import pbv3_add_interposer import gspread import glob @@ -246,7 +247,18 @@ def updatePanel(sheet, irow, client, checkTests = False, checkAMAC = False, flex if flexArray is None: print('Register powerboard flex array for '+panelid+" now...") - pwbdbtools.registerFlexArray(client, panelid, flex_array_batch, arr_type='PB_FLEX_ARRAY_3V2') + flexThickness = None + hasInterposer = None + cell_interposer = sheet.find("Has Interposer?") + cell_flexThickness = sheet.find("Flex Thickness") + if (cell_interposer is not None): + if sheet.cell(irow, cell_ready.col).value == "Yes": + hasInterposer = True + elif sheet.cell(irow, cell_ready.col).value == "No": + hasInterposer = False + if (cell_flexThickness is not None) and (sheet.cell(irow, cell_ready.col).value): + flexThickness = sheet.cell(irow, cell_ready.col).value + pwbdbtools.registerFlexArray(client, panelid, flex_array_batch, arr_type='PB_FLEX_ARRAY_3V2', flex_thickness=flexThickness, has_interposer=hasInterposer) flexArray = client.get('getComponent', json={'component':'20USBPF'+panelid}) print('Found flex array: '+flexArray['code']) currentStage = flexArray['currentStage']['code'] @@ -328,7 +340,12 @@ def updatePanel(sheet, irow, client, checkTests = False, checkAMAC = False, flex pbv3_panel_loadamac.loadwafer(client, panelid, icol-icol_wafer-1, wafer, die_num, amac_sn) except Exception: print("Ooooops.... Failed to load amac "+str(die_num)) - + + cell_interposer = sheet.find("Has Interposer?") + if (cell_interposer is not None) and (sheet.cell(irow, cell_ready.col).value == "Yes"): + print('====== Next step: load interposer...') + pbv3_add_interposer.load_interposer_for_panel(client, panelid) + panel = client.get('getComponent', json={'component':panelid, 'alternativeIdentifier':True}) currentStage = panel['currentStage']['code'] if currentStage == "LOADED" or currentStage == "HYBBURN": # PB is no longer on panel anymore diff --git a/database/pwbdbtools.py b/database/pwbdbtools.py index bbe6f9b179e7759ea00050977d22155a45642a75..6a121b2539ffc74fbadd4b92b9042ea262c27f1a 100644 --- a/database/pwbdbtools.py +++ b/database/pwbdbtools.py @@ -208,9 +208,15 @@ def getLatestTestRunByComponent(client, component, testType, stage=None): return testRun -def registerFlexArray(client, panelid, flex_batch, arr_type='PB_FLEX_ARRAY_3V2'): +def registerFlexArray(client, panelid, flex_batch, arr_type='PB_FLEX_ARRAY_3V2', flex_thickness = None, has_interposer = None): version, batch, number = parse_serial(panelid, fullsn=False) + properties_lib = {'VERSION':str(version), 'BATCH':batch, 'NUMBER':number} + if flex_thickness is not None: + properties_lib.update({'FLEX_THICKNESS': flex_thickness}) + if has_interposer is not None: + properties_lib.update({'HAS_INTERPOSER': has_interposer}) + regdata = { 'project':'S', 'subproject':'SB', @@ -219,7 +225,7 @@ def registerFlexArray(client, panelid, flex_batch, arr_type='PB_FLEX_ARRAY_3V2') 'type':arr_type, 'serialNumber':'20USBPF'+panelid, 'batches':{'PWB_FLEX_BATCH':flex_batch}, - 'properties':{'VERSION':str(version), 'BATCH':batch, 'NUMBER':number} + 'properties': properties_lib } client.post('registerComponent', json=regdata)