From a4aaca534a5d53c57e7139f2c1c91aac7639a241 Mon Sep 17 00:00:00 2001 From: Zhirong Zhang <zhirong.zhang@cern.ch> Date: Fri, 6 Sep 2024 16:01:01 -0700 Subject: [PATCH 1/5] Add a script to interpose powerboards on itkpd. --- database/pbv3_add_interposer.py | 168 ++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 database/pbv3_add_interposer.py diff --git a/database/pbv3_add_interposer.py b/database/pbv3_add_interposer.py new file mode 100644 index 0000000..e6d8198 --- /dev/null +++ b/database/pbv3_add_interposer.py @@ -0,0 +1,168 @@ +import os +import itkdb +import csv +import argparse +import sys + +''' +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. +''' + + +# ========================================== +# Pre-req: set up client and retrive the interposer +# componet template. +# ========================================== +c = itkdb.Client(expires_after={"days": 7}) + +# Bypass all cache +c.headers.update({"cache-control": "no-cache"}) + +interposer_template = c.get('generateComponentTypeDtoSample', json={'project': 'S', 'code': 'PWB_INTERPOSER'}) + + +# ========================================== +# Functions +# ========================================== + +# ------------------------------------------ +# 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(panel_ls): + PWB_ls = [] + + for panel_ID in panel_ls: + panel = c.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(PWB_ls, component_template=interposer_template, check_interposer=True): + + for PWB_ID in PWB_ls: + + PWB = c.get("getComponent", json={"component": PWB_ID}) + if PWB.get('inTransit', False) == True: + print(f"PWB {PWB['serialNumber']} is in transit and cannot be loaded with an interposer.") + continue + + 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 + + try: + new_interposer = c.post( + "registerComponent", + json={ + **component_template, + 'subproject': 'SB', + 'institution': 'LBNL_STRIP_POWERBOARDS', + } + ) + + # Hard coded parameters. + output = c.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}") + + + + +# ========================================== +# Work space below +# ========================================== + +# Create the parser +parser = argparse.ArgumentParser(description="Process either PWB number or Panel number, but not neither.") + +# Add optional arguments for 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)") + +# Parse the arguments +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() # Print the help message + sys.exit(1) # Exit the script with an error status +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) + +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(args.panel) + +load_interposer(pwb_ls) -- GitLab From 40ffef967c71ec6ed6b53d71f6f84cd43f343232 Mon Sep 17 00:00:00 2001 From: Zhirong Zhang <zhirong.zhang@cern.ch> Date: Mon, 21 Oct 2024 11:08:17 -0700 Subject: [PATCH 2/5] Modified so that it can used in autoRegister --- database/pbv3_add_interposer.py | 109 +++++++++++++++----------------- 1 file changed, 51 insertions(+), 58 deletions(-) diff --git a/database/pbv3_add_interposer.py b/database/pbv3_add_interposer.py index e6d8198..f4cd7a6 100644 --- a/database/pbv3_add_interposer.py +++ b/database/pbv3_add_interposer.py @@ -4,29 +4,14 @@ 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. ''' - -# ========================================== -# Pre-req: set up client and retrive the interposer -# componet template. -# ========================================== -c = itkdb.Client(expires_after={"days": 7}) - -# Bypass all cache -c.headers.update({"cache-control": "no-cache"}) - -interposer_template = c.get('generateComponentTypeDtoSample', json={'project': 'S', 'code': 'PWB_INTERPOSER'}) - - -# ========================================== -# Functions -# ========================================== - # ------------------------------------------ # Function: read_PWB_from_csv # Description: temp function for reading CSV files @@ -60,18 +45,18 @@ def read_PWB_from_csv(csv_file_path): # Arguments: a list of panel ID # Returns: a list of child PWB ID # ------------------------------------------ -def read_panel(panel_ls): +def read_panel(client, panel_ls): PWB_ls = [] for panel_ID in panel_ls: - panel = c.get("getComponent", json={"component": panel_ID}) + 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.") @@ -83,23 +68,25 @@ def read_panel(panel_ls): # Arguments: a list of PWB ID # Returns: NA # ------------------------------------------ -def load_interposer(PWB_ls, component_template=interposer_template, check_interposer=True): +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 = c.get("getComponent", json={"component": PWB_ID}) - if PWB.get('inTransit', False) == True: - print(f"PWB {PWB['serialNumber']} is in transit and cannot be loaded with an interposer.") - continue + 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 = c.post( - "registerComponent", + new_interposer = client.post( + "registerComponent", json={ **component_template, 'subproject': 'SB', @@ -108,10 +95,10 @@ def load_interposer(PWB_ls, component_template=interposer_template, check_interp ) # Hard coded parameters. - output = c.post( - "assembleComponent", + output = client.post( + "assembleComponent", json={ - "parent": PWB['serialNumber'], + "parent": PWB['serialNumber'], "child": new_interposer['component']['code'], "properties": { "GLASS_BEADS_USED": True, @@ -128,41 +115,47 @@ def load_interposer(PWB_ls, component_template=interposer_template, check_interp 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) -# ========================================== -# Work space below -# ========================================== +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Process either PWB number or Panel number, but not neither.") -# Create the parser -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)") -# Add optional arguments for 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() -# Parse the arguments -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) -# 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() # Print the help message - sys.exit(1) # Exit the script with an error status -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 = [] -pwb_ls = [] + if args.pwbSN: + print(f"PWB number(s) provided: {args.pwbSN}") + pwb_ls = args.pwbSN -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(args.panel) + if args.panel: + print(f"Panel number(s) provided: {args.panel}") + pwb_ls = read_panel(c, args.panel) -load_interposer(pwb_ls) + load_interposer(c, pwb_ls) -- GitLab From 4db18c23ac250484ef8018f77f2f2394535c4e1b Mon Sep 17 00:00:00 2001 From: Zhirong Zhang <zhirong.zhang@cern.ch> Date: Mon, 21 Oct 2024 12:02:54 -0700 Subject: [PATCH 3/5] Insert interposer --- database/pbv3_autoRegister.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/database/pbv3_autoRegister.py b/database/pbv3_autoRegister.py index e94da98..23adc00 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 @@ -328,7 +329,10 @@ 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)) - + + 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 -- GitLab From ea92a684d2d0c69c00bd47c40776b6cc0a0677e9 Mon Sep 17 00:00:00 2001 From: Zhirong Zhang <zhirong.zhang@cern.ch> Date: Thu, 24 Oct 2024 13:41:55 -0700 Subject: [PATCH 4/5] Read in flex thickness and has interposer for flex properties --- database/pbv3_autoRegister.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/database/pbv3_autoRegister.py b/database/pbv3_autoRegister.py index 23adc00..70f0af6 100755 --- a/database/pbv3_autoRegister.py +++ b/database/pbv3_autoRegister.py @@ -247,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'] @@ -330,8 +341,10 @@ def updatePanel(sheet, irow, client, checkTests = False, checkAMAC = False, flex except Exception: print("Ooooops.... Failed to load amac "+str(die_num)) - print('====== Next step: load interposer...') - pbv3_add_interposer.load_interposer_for_panel(client, panelid) + 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'] -- GitLab From a361474e2d3fa4b50912a899d4d462800b2483b8 Mon Sep 17 00:00:00 2001 From: Zhirong Zhang <zhirong.zhang@cern.ch> Date: Thu, 24 Oct 2024 13:43:02 -0700 Subject: [PATCH 5/5] modify the flex registration function to accommodate for flex thickness and has interposer --- database/pwbdbtools.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/database/pwbdbtools.py b/database/pwbdbtools.py index bbe6f9b..6a121b2 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) -- GitLab