Skip to content
This repository has been archived by the owner on Dec 10, 2024. It is now read-only.

Tools: Add lcsc & mfr flag to libadd for auto-module #45

Merged
merged 1 commit into from
Sep 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/faebryk/libs/picker/jlcpcb/jlcpcb.py
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,10 @@ def attach(
f"{indent(module.pretty_params(), ' '*4)}"
)

@property
def mfr_name(self) -> str:
return asyncio.run(Manufacturers().get_from_id(self.manufacturer_id))


class ComponentQuery:
class Error(Exception): ...
Expand Down Expand Up @@ -482,6 +486,8 @@ def filter_by_manufacturer_pn(self, partnumber: str) -> Self:

def filter_by_manufacturer(self, manufacturer: str) -> Self:
assert self.Q
if not manufacturer:
return self
manufacturer_ids = asyncio.run(Manufacturers().get_ids(manufacturer))
self.Q &= Q(manufacturer_id__in=manufacturer_ids)
return self
Expand Down
77 changes: 56 additions & 21 deletions src/faebryk/libs/picker/jlcpcb/picker_lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@
from faebryk.core.module import Module
from faebryk.libs.e_series import E_SERIES_VALUES
from faebryk.libs.picker.jlcpcb.jlcpcb import (
Component,
ComponentQuery,
MappingParameterDB,
)
from faebryk.libs.picker.picker import (
DescriptiveProperties,
PickError,
)
from faebryk.libs.util import KeyErrorAmbiguous, KeyErrorNotFound

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -44,7 +46,21 @@ def f(x: str) -> F.Constant[T]:
return f


def find_lcsc_part(module: Module):
def find_component_by_lcsc_id(lcsc_id: str) -> Component:
parts = ComponentQuery().filter_by_lcsc_pn(lcsc_id).get()

if len(parts) < 1:
raise KeyErrorNotFound(f"Could not find part with LCSC part number {lcsc_id}")

if len(parts) > 1:
raise KeyErrorAmbiguous(
parts, f"Found multiple parts with LCSC part number {lcsc_id}"
)

return next(iter(parts))


def find_and_attach_by_lcsc_id(module: Module):
"""
Find a part in the JLCPCB database by its LCSC part number
"""
Expand All @@ -56,23 +72,47 @@ def find_lcsc_part(module: Module):

lcsc_pn = module.get_trait(F.has_descriptive_properties).get_properties()["LCSC"]

parts = ComponentQuery().filter_by_lcsc_pn(lcsc_pn).get()

if len(parts) < 1:
raise PickError(f"Could not find part with LCSC part number {lcsc_pn}", module)

if len(parts) > 1:
try:
part = find_component_by_lcsc_id(lcsc_pn)
except KeyErrorNotFound as e:
raise PickError(
f"Could not find part with LCSC part number {lcsc_pn}", module
) from e
except KeyErrorAmbiguous:
raise PickError(f"Found no exact match for LCSC part number {lcsc_pn}", module)

if parts[0].stock < qty:
if part.stock < qty:
raise PickError(
f"Part with LCSC part number {lcsc_pn} has insufficient stock", module
)

parts[0].attach(module, [])
part.attach(module, [])


def find_manufacturer_part(module: Module):
def find_component_by_mfr(mfr: str, mfr_pn: str) -> Component:
parts = (
ComponentQuery()
.filter_by_manufacturer_pn(mfr_pn)
.filter_by_manufacturer(mfr)
.filter_by_stock(qty)
.sort_by_price()
.get()
)

if len(parts) < 1:
raise KeyErrorNotFound(
f"Could not find part with manufacturer part number {mfr_pn}"
)

if len(parts) > 1:
raise KeyErrorAmbiguous(
parts, f"Found multiple parts with manufacturer part number {mfr_pn}"
)

return next(iter(parts))


def find_and_attach_by_mfr(module: Module):
"""
Find a part in the JLCPCB database by its manufacturer part number
"""
Expand All @@ -97,19 +137,14 @@ def find_manufacturer_part(module: Module):
DescriptiveProperties.manufacturer
]

parts = (
ComponentQuery()
.filter_by_manufacturer_pn(mfr_pn)
.filter_by_manufacturer(mfr)
.filter_by_stock(qty)
.sort_by_price()
.get()
)

if len(parts) < 1:
try:
parts = [find_component_by_mfr(mfr, mfr_pn)]
except KeyErrorNotFound as e:
raise PickError(
f"Could not find part with manufacturer part number {mfr_pn}", module
)
) from e
except KeyErrorAmbiguous as e:
parts = e.duplicates

for part in parts:
try:
Expand Down
4 changes: 2 additions & 2 deletions src/faebryk/libs/picker/jlcpcb/pickers.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ def add_jlcpcb_pickers(module: Module, base_prio: int = 0) -> None:

# Generic pickers
prio = base_prio
module.add(F.has_multi_picker(prio, JLCPCBPicker(P.find_lcsc_part)))
module.add(F.has_multi_picker(prio, JLCPCBPicker(P.find_manufacturer_part)))
module.add(F.has_multi_picker(prio, JLCPCBPicker(P.find_and_attach_by_lcsc_id)))
module.add(F.has_multi_picker(prio, JLCPCBPicker(P.find_and_attach_by_mfr)))

# Type specific pickers
prio = base_prio + 1
Expand Down
53 changes: 52 additions & 1 deletion src/faebryk/libs/pycodegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@

import logging
import re
import subprocess
from pathlib import Path
from textwrap import dedent
from typing import Callable, Iterable

import black

logger = logging.getLogger(__name__)


def sanitize_name(raw):
def sanitize_name(raw, expect_arithmetic: bool = False):
sanitized = raw
# braces
sanitized = sanitized.replace("(", "")
Expand All @@ -18,6 +24,9 @@ def sanitize_name(raw):
sanitized = sanitized.replace(".", "_")
sanitized = sanitized.replace(",", "_")
sanitized = sanitized.replace("/", "_")
if not expect_arithmetic:
sanitized = sanitized.replace("-", "_")

# special symbols
sanitized = sanitized.replace("'", "")
sanitized = sanitized.replace("*", "")
Expand Down Expand Up @@ -62,3 +71,45 @@ def handle_unknown_invalid_symbold(match):
return None, to_escape

return sanitized


def gen_repeated_block[T](
generator: Iterable[T],
func: Callable[[T], str] = dedent,
requires_pass: bool = False,
) -> str:
lines = list(map(func, generator))

if not lines and requires_pass:
lines = ["pass"]

return gen_block("\n".join(lines))


def gen_block(payload: str):
return f"#__MARK_BLOCK_BEGIN\n{payload}\n#__MARK_BLOCK_END"


def fix_indent(text: str) -> str:
indent_stack = [""]

out_lines = []
for line in text.splitlines():
if "#__MARK_BLOCK_BEGIN" in line:
indent_stack.append(line.removesuffix("#__MARK_BLOCK_BEGIN"))
elif "#__MARK_BLOCK_END" in line:
indent_stack.pop()
else:
out_lines.append(indent_stack[-1] + line)

return dedent("\n".join(out_lines))


def format_and_write(code: str, path: Path):
code = code.strip()
code = black.format_file_contents(code, fast=True, mode=black.FileMode())
path.write_text(code)

print("Ruff----")
subprocess.run(["ruff", "check", "--fix", path], check=True)
print("--------")
Loading