From 459ae163dad74f7e304d8208ed1c5634d49665f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sun, 3 Dec 2023 14:42:20 +0100 Subject: [PATCH 1/7] Restrict the depth of the category tree shown inside KiCAD to improve performance The depth can be controlled via the EDA_KICAD_CATEGORY_DEPTH env --- .docker/symfony.conf | 1 + .env | 8 +++++ config/parameters.yaml | 1 + config/services.yaml | 7 +++++ src/Controller/KiCadApiController.php | 4 +-- .../AbstractPartsContainingRepository.php | 22 ++++++++++++++ .../KiCADHelper.php => EDA/KiCadHelper.php} | 30 +++++++++++++++---- 7 files changed, 65 insertions(+), 8 deletions(-) rename src/Services/{EDAIntegration/KiCADHelper.php => EDA/KiCadHelper.php} (86%) diff --git a/.docker/symfony.conf b/.docker/symfony.conf index de87ceb4..9569f80c 100644 --- a/.docker/symfony.conf +++ b/.docker/symfony.conf @@ -42,6 +42,7 @@ PassEnv PROVIDER_TME_KEY PROVIDER_TME_SECRET PROVIDER_TME_CURRENCY PROVIDER_TME_LANGUAGE PROVIDER_TME_COUNTRY PROVIDER_TME_GET_GROSS_PRICES PassEnv PROVIDER_OCTOPART_CLIENT_ID PROVIDER_OCTOPART_SECRET PROVIDER_OCTOPART_CURRENCY PROVIDER_OCTOPART_COUNTRY PROVIDER_OCTOPART_SEARCH_LIMIT PROVIDER_OCTOPART_ONLY_AUTHORIZED_SELLERS PassEnv PROVIDER_MOUSER_KEY PROVIDER_MOUSER_SEARCH_OPTION PROVIDER_MOUSER_SEARCH_LIMIT PROVIDER_MOUSER_SEARCH_WITH_SIGNUP_LANGUAGE + PassEnv EDA_KICAD_CATEGORY_DEPTH # For most configuration files from conf-available/, which are # enabled or disabled at a global level, it is possible to diff --git a/.env b/.env index c1a3d63c..95fe5455 100644 --- a/.env +++ b/.env @@ -159,6 +159,14 @@ PROVIDER_MOUSER_SEARCH_LIMIT=50 # Used when searching for keywords in the language specified when you signed up for Search API. PROVIDER_MOUSER_SEARCH_WITH_SIGNUP_LANGUAGE='true' +################################################################################## +# EDA integration related settings +################################################################################## + +# This value determines the depth of the category tree, that is visible inside KiCad +# 0 means that only the top level categories are visible. Set to a value > 0 to show more levels. +EDA_KICAD_CATEGORY_DEPTH=0 + ################################################################################### # SAML Single sign on-settings ################################################################################### diff --git a/config/parameters.yaml b/config/parameters.yaml index 8c2bad17..596492eb 100644 --- a/config/parameters.yaml +++ b/config/parameters.yaml @@ -142,3 +142,4 @@ parameters: env(HISTORY_SAVE_REMOVED_DATA): 1 env(HISTORY_SAVE_NEW_DATA): 1 + env(EDA_KICAD_CATEGORY_DEPTH): 0 diff --git a/config/services.yaml b/config/services.yaml index 44831820..ccfe14a0 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -316,6 +316,13 @@ services: $global_locale: '%partdb.locale%' $global_timezone: '%partdb.timezone%' + #################################################################################################################### + # EDA system + #################################################################################################################### + App\Services\EDA\KiCadHelper: + arguments: + $category_depth: '%env(int:EDA_KICAD_CATEGORY_DEPTH)%' + #################################################################################################################### # Symfony overrides #################################################################################################################### diff --git a/src/Controller/KiCadApiController.php b/src/Controller/KiCadApiController.php index cd63e627..1dcb0d9a 100644 --- a/src/Controller/KiCadApiController.php +++ b/src/Controller/KiCadApiController.php @@ -25,7 +25,7 @@ namespace App\Controller; use App\Entity\Parts\Category; use App\Entity\Parts\Part; -use App\Services\EDAIntegration\KiCADHelper; +use App\Services\EDA\KiCadHelper; use App\Services\Trees\NodesListBuilder; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; @@ -36,7 +36,7 @@ use Symfony\Component\Routing\Annotation\Route; class KiCadApiController extends AbstractController { public function __construct( - private readonly KiCADHelper $kiCADHelper, + private readonly KiCadHelper $kiCADHelper, ) { } diff --git a/src/Repository/AbstractPartsContainingRepository.php b/src/Repository/AbstractPartsContainingRepository.php index 3a389610..e3c7f610 100644 --- a/src/Repository/AbstractPartsContainingRepository.php +++ b/src/Repository/AbstractPartsContainingRepository.php @@ -64,6 +64,11 @@ abstract class AbstractPartsContainingRepository extends StructuralDBElementRepo return $this->getPartsCountRecursiveWithDepthN($element, self::RECURSION_LIMIT); } + public function getPartsRecursive(AbstractPartsContainingDBElement $element): array + { + return $this->getPartsRecursiveWithDepthN($element, self::RECURSION_LIMIT); + } + /** * The implementation of the recursive function to get the parts count. * This function is used to limit the recursion depth (remaining_depth is decreased on each call). @@ -91,6 +96,23 @@ abstract class AbstractPartsContainingRepository extends StructuralDBElementRepo return $count; } + protected function getPartsRecursiveWithDepthN(AbstractPartsContainingDBElement $element, int $remaining_depth): array + { + if ($remaining_depth <= 0) { + throw new \RuntimeException('Recursion limit reached!'); + } + + //Add direct parts + $parts = $this->getParts($element); + + //Then iterate over all children and add their parts + foreach ($element->getChildren() as $child) { + $parts = array_merge($parts, $this->getPartsRecursiveWithDepthN($child, $remaining_depth - 1)); + } + + return $parts; + } + protected function getPartsByField(object $element, array $order_by, string $field_name): array { if (!$element instanceof AbstractPartsContainingDBElement) { diff --git a/src/Services/EDAIntegration/KiCADHelper.php b/src/Services/EDA/KiCadHelper.php similarity index 86% rename from src/Services/EDAIntegration/KiCADHelper.php rename to src/Services/EDA/KiCadHelper.php index 5e8d21a6..2f5d6ed4 100644 --- a/src/Services/EDAIntegration/KiCADHelper.php +++ b/src/Services/EDA/KiCadHelper.php @@ -21,7 +21,7 @@ declare(strict_types=1); -namespace App\Services\EDAIntegration; +namespace App\Services\EDA; use App\Entity\Parts\Category; use App\Entity\Parts\Part; @@ -33,7 +33,7 @@ use Symfony\Contracts\Cache\ItemInterface; use Symfony\Contracts\Cache\TagAwareCacheInterface; use Symfony\Contracts\Translation\TranslatorInterface; -class KiCADHelper +class KiCadHelper { public function __construct( @@ -43,7 +43,11 @@ class KiCADHelper private readonly ElementCacheTagGenerator $tagGenerator, private readonly UrlGeneratorInterface $urlGenerator, private readonly TranslatorInterface $translator, + private readonly int $category_depth, ) { + if ($this->category_depth < 0) { + throw new \InvalidArgumentException('The category depth must be greater than or equal to 0'); + } } /** @@ -55,7 +59,7 @@ class KiCADHelper */ public function getCategories(): array { - return $this->kicadCache->get('kicad_categories', function (ItemInterface $item) { + return $this->kicadCache->get('kicad_categories_' . $this->category_depth, function (ItemInterface $item) { //Invalidate the cache on category changes $secure_class_name = $this->tagGenerator->getElementTypeCacheTag(Category::class); $item->tag($secure_class_name); @@ -69,9 +73,17 @@ class KiCADHelper continue; } + //Skip categories with a depth greater than the configured one + if ($category->getLevel() > $this->category_depth) { + continue; + } + /** @var $category Category */ //Ensure that the category contains parts - if ($repo->getPartsCount($category) < 1) { + //For the last level, we need to use a recursive query, otherwise we can use a simple query + $parts_count = $category->getLevel() >= $this->category_depth ? $repo->getPartsCountRecursive($category) : $repo->getPartsCount($category); + + if ($parts_count < 1) { continue; } @@ -94,7 +106,7 @@ class KiCADHelper */ public function getCategoryParts(Category $category): array { - return $this->kicadCache->get('kicad_category_parts_'.$category->getID(), + return $this->kicadCache->get('kicad_category_parts_'.$category->getID() . '_' . $this->category_depth, function (ItemInterface $item) use ($category) { $item->tag([ $this->tagGenerator->getElementTypeCacheTag(Category::class), @@ -102,7 +114,13 @@ class KiCADHelper ]); $category_repo = $this->em->getRepository(Category::class); - $parts = $category_repo->getParts($category); + if ($category->getLevel() >= $this->category_depth) { + //Get all parts for the category and its children + $parts = $category_repo->getPartsRecursive($category); + } else { + //Get only direct parts for the category (without children), as the category is not collapsed + $parts = $category_repo->getParts($category); + } $result = []; foreach ($parts as $part) { From 6926f6b2331efbb59e2c6a95087493871f243a1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sun, 3 Dec 2023 15:03:00 +0100 Subject: [PATCH 2/7] Allow to show all parts of all categories in a single KiCad category by setting EDA_KICAD_CATEGORY_DEPTH to -1 --- .env | 1 + src/Controller/KiCadApiController.php | 8 +++-- src/Services/EDA/KiCadHelper.php | 45 ++++++++++++++++++++------- 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/.env b/.env index 95fe5455..47919cb7 100644 --- a/.env +++ b/.env @@ -165,6 +165,7 @@ PROVIDER_MOUSER_SEARCH_WITH_SIGNUP_LANGUAGE='true' # This value determines the depth of the category tree, that is visible inside KiCad # 0 means that only the top level categories are visible. Set to a value > 0 to show more levels. +# Set to -1, to show all parts of Part-DB inside a single category in KiCad EDA_KICAD_CATEGORY_DEPTH=0 ################################################################################### diff --git a/src/Controller/KiCadApiController.php b/src/Controller/KiCadApiController.php index 1dcb0d9a..14548b7b 100644 --- a/src/Controller/KiCadApiController.php +++ b/src/Controller/KiCadApiController.php @@ -62,9 +62,13 @@ class KiCadApiController extends AbstractController } #[Route('/parts/category/{category}.json', name: 'kicad_api_category')] - public function categoryParts(Category $category): Response + public function categoryParts(?Category $category): Response { - $this->denyAccessUnlessGranted('read', $category); + if ($category) { + $this->denyAccessUnlessGranted('read', $category); + } else { + $this->denyAccessUnlessGranted('@categories.read'); + } $this->denyAccessUnlessGranted('@parts.read'); return $this->json($this->kiCADHelper->getCategoryParts($category)); diff --git a/src/Services/EDA/KiCadHelper.php b/src/Services/EDA/KiCadHelper.php index 2f5d6ed4..7dd3c657 100644 --- a/src/Services/EDA/KiCadHelper.php +++ b/src/Services/EDA/KiCadHelper.php @@ -28,6 +28,7 @@ use App\Entity\Parts\Part; use App\Services\Cache\ElementCacheTagGenerator; use App\Services\Trees\NodesListBuilder; use Doctrine\ORM\EntityManagerInterface; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Contracts\Cache\ItemInterface; use Symfony\Contracts\Cache\TagAwareCacheInterface; @@ -43,11 +44,9 @@ class KiCadHelper private readonly ElementCacheTagGenerator $tagGenerator, private readonly UrlGeneratorInterface $urlGenerator, private readonly TranslatorInterface $translator, + /** The maximum level of the shown categories. 0 Means only the top level categories are shown. -1 means only a single one containing */ private readonly int $category_depth, ) { - if ($this->category_depth < 0) { - throw new \InvalidArgumentException('The category depth must be greater than or equal to 0'); - } } /** @@ -64,6 +63,18 @@ class KiCadHelper $secure_class_name = $this->tagGenerator->getElementTypeCacheTag(Category::class); $item->tag($secure_class_name); + //If the category depth is smaller than 1, create only one dummy category + if ($this->category_depth < 1) { + return [ + [ + 'id' => '0', + 'name' => 'Part-DB', + ] + ]; + } + + //Otherwise just get the categories and filter them + $categories = $this->nodesListBuilder->typeToNodesList(Category::class); $repo = $this->em->getRepository(Category::class); $result = []; @@ -101,25 +112,35 @@ class KiCadHelper /** * Returns an array of objects containing all parts for the given category in the format required by KiCAD. * The result is cached for performance and invalidated on category or part changes. - * @param Category $category + * @param Category|null $category * @return array */ - public function getCategoryParts(Category $category): array + public function getCategoryParts(?Category $category): array { - return $this->kicadCache->get('kicad_category_parts_'.$category->getID() . '_' . $this->category_depth, + return $this->kicadCache->get('kicad_category_parts_'.($category?->getID() ?? 0) . '_' . $this->category_depth, function (ItemInterface $item) use ($category) { $item->tag([ $this->tagGenerator->getElementTypeCacheTag(Category::class), $this->tagGenerator->getElementTypeCacheTag(Part::class) ]); - $category_repo = $this->em->getRepository(Category::class); - if ($category->getLevel() >= $this->category_depth) { - //Get all parts for the category and its children - $parts = $category_repo->getPartsRecursive($category); + if ($this->category_depth >= 0) { + //Ensure that the category is set + if (!$category) { + throw new NotFoundHttpException('Category must be set, if category_depth is greater than 1!'); + } + + $category_repo = $this->em->getRepository(Category::class); + if ($category->getLevel() >= $this->category_depth) { + //Get all parts for the category and its children + $parts = $category_repo->getPartsRecursive($category); + } else { + //Get only direct parts for the category (without children), as the category is not collapsed + $parts = $category_repo->getParts($category); + } } else { - //Get only direct parts for the category (without children), as the category is not collapsed - $parts = $category_repo->getParts($category); + //Get all parts + $parts = $this->em->getRepository(Part::class)->findAll(); } $result = []; From 1825080d9e38698420a0b2c45b6ae246c5cc43e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sun, 3 Dec 2023 15:07:41 +0100 Subject: [PATCH 3/7] Added documentation about the EDA_KICAD_CATEGORY_DEPTH env --- docs/configuration.md | 8 ++++++++ docs/usage/eda_integration.md | 13 ++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/docs/configuration.md b/docs/configuration.md index 7a5daa53..1796b7df 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -128,6 +128,14 @@ then `HISTORY_SAVE_CHANGED_FIELDS`, `HISTORY_SAVE_CHANGED_DATA` and `HISTORY_SAV * `ERROR_PAGE_SHOW_HELP`: Set this 0, to disable the solution hints shown on an error page. These hints should not contain sensitive information, but could confuse end-users. +### EDA related settings + +* `EDA_KICAD_CATEGORY_DEPTH`: A number, which determines how many levels of Part-DB categories should be shown inside KiCad. + All parts in the selected category and all subcategories are shown in KiCad. + For performance reason this value should not be too high. The default is 0, which means that only the top level categories are shown in KiCad. + All parts in the selected category and all subcategories are shown in KiCad. Set this to a higher value, if you want to show more categories in KiCad. + When you set this value to -1, all parts are shown inside a single category in KiCad. + ### SAML SSO settings The following settings can be used to enable and configure Single-Sign on via SAML. This allows users to log in to diff --git a/docs/usage/eda_integration.md b/docs/usage/eda_integration.md index 55d0a770..11c4b231 100644 --- a/docs/usage/eda_integration.md +++ b/docs/usage/eda_integration.md @@ -55,4 +55,15 @@ You can define this on a per-part basis using the KiCad symbol and KiCad footpri For example to configure the values for an BC547 transistor you would put `Transistor_BJT:BC547` on the parts Kicad symbol to give it the right schematic symbol in EEschema and `Package_TO_SOT_THT:TO-92` to give it the right footprint in PcbNew. -If you type in a character, you will get an autocomplete list of all symbols and footprints available in the kicad standard library. You can also input your own value. \ No newline at end of file +If you type in a character, you will get an autocomplete list of all symbols and footprints available in the kicad standard library. You can also input your own value. + +### Category depth in KiCad + +For performance reasons, only the most top level categories of Part-DB are shown as categories in KiCad. All parts in the subcategories are shown in the top level category. + +You can configure the depth of the categories shown in KiCad, via the `EDA_KICAD_CATEGORY_DEPTH` env option. The default value is 0, which meabs only the top level categories are shown. +To show more levels of categories, you can set this value to a higher number. + +If you set this value to -1, all parts are shown inside a single category in KiCad, without any subcategories. + +You can view the "real" category path of a part in the part details dialog in KiCad. \ No newline at end of file From bc37d11f1319daa3d6f51418c61ab9bd0ac912d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sun, 3 Dec 2023 15:11:06 +0100 Subject: [PATCH 4/7] Fixed static analysis issue --- src/Services/EDA/KiCadHelper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Services/EDA/KiCadHelper.php b/src/Services/EDA/KiCadHelper.php index 7dd3c657..455ce7eb 100644 --- a/src/Services/EDA/KiCadHelper.php +++ b/src/Services/EDA/KiCadHelper.php @@ -89,9 +89,9 @@ class KiCadHelper continue; } - /** @var $category Category */ //Ensure that the category contains parts //For the last level, we need to use a recursive query, otherwise we can use a simple query + /** @var Category $category */ $parts_count = $category->getLevel() >= $this->category_depth ? $repo->getPartsCountRecursive($category) : $repo->getPartsCount($category); if ($parts_count < 1) { From 7d69d6ba308c9136bbc40643be531abb395d5530 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sun, 3 Dec 2023 15:29:17 +0100 Subject: [PATCH 5/7] Changed logic of invisible to a (forced) visibility field --- src/Entity/EDA/EDACategoryInfo.php | 14 +++++++------- src/Entity/EDA/EDAPartInfo.php | 14 +++++++------- src/Form/Part/EDA/EDACategoryInfoType.php | 5 +++-- src/Form/Part/EDA/EDAPartInfoType.php | 5 +++-- src/Services/EDA/KiCadHelper.php | 4 ++-- templates/admin/category_admin.html.twig | 2 +- templates/parts/edit/_eda.html.twig | 2 +- templates/parts/info/_extended_infos.html.twig | 2 +- translations/messages.en.xlf | 18 ++++++++++++------ 9 files changed, 37 insertions(+), 29 deletions(-) diff --git a/src/Entity/EDA/EDACategoryInfo.php b/src/Entity/EDA/EDACategoryInfo.php index db56485a..61d99988 100644 --- a/src/Entity/EDA/EDACategoryInfo.php +++ b/src/Entity/EDA/EDACategoryInfo.php @@ -38,10 +38,10 @@ class EDACategoryInfo #[Groups(['full', 'category:read', 'category:write'])] private ?string $reference_prefix = null; - /** @var bool|null If this is true, then this part is invisible for the EDA software */ - #[Column(type: Types::BOOLEAN, nullable: true)] + /** @var bool|null Visibility of this part to EDA software in trinary logic. True=Visible, False=Invisible, Null=Auto */ + #[Column(name: 'invisible', type: Types::BOOLEAN, nullable: true)] //TODO: Rename column to visibility #[Groups(['full', 'category:read', 'category:write'])] - private ?bool $invisible = null; + private ?bool $visibility = null; /** @var bool|null If this is set to true, then this part will be excluded from the BOM */ #[Column(type: Types::BOOLEAN, nullable: true)] @@ -74,14 +74,14 @@ class EDACategoryInfo return $this; } - public function getInvisible(): ?bool + public function getVisibility(): ?bool { - return $this->invisible; + return $this->visibility; } - public function setInvisible(?bool $invisible): EDACategoryInfo + public function setVisibility(?bool $visibility): EDACategoryInfo { - $this->invisible = $invisible; + $this->visibility = $visibility; return $this; } diff --git a/src/Entity/EDA/EDAPartInfo.php b/src/Entity/EDA/EDAPartInfo.php index e314d580..5742921a 100644 --- a/src/Entity/EDA/EDAPartInfo.php +++ b/src/Entity/EDA/EDAPartInfo.php @@ -43,10 +43,10 @@ class EDAPartInfo #[Groups(['full', 'eda_info:read', 'eda_info:write'])] private ?string $value = null; - /** @var bool|null If this is true, then this part is invisible for the EDA software */ - #[Column(type: Types::BOOLEAN, nullable: true)] + /** @var bool|null Visibility of this part to EDA software in trinary logic. True=Visible, False=Invisible, Null=Auto */ + #[Column(name: 'invisible', type: Types::BOOLEAN, nullable: true)] //TODO: Rename column to visibility #[Groups(['full', 'eda_info:read', 'eda_info:write'])] - private ?bool $invisible = null; + private ?bool $visibility = null; /** @var bool|null If this is set to true, then this part will be excluded from the BOM */ #[Column(type: Types::BOOLEAN, nullable: true)] @@ -100,14 +100,14 @@ class EDAPartInfo return $this; } - public function getInvisible(): ?bool + public function getVisibility(): ?bool { - return $this->invisible; + return $this->visibility; } - public function setInvisible(?bool $invisible): EDAPartInfo + public function setVisibility(?bool $visibility): EDAPartInfo { - $this->invisible = $invisible; + $this->visibility = $visibility; return $this; } diff --git a/src/Form/Part/EDA/EDACategoryInfoType.php b/src/Form/Part/EDA/EDACategoryInfoType.php index 3e60913e..24fe6717 100644 --- a/src/Form/Part/EDA/EDACategoryInfoType.php +++ b/src/Form/Part/EDA/EDACategoryInfoType.php @@ -45,8 +45,9 @@ class EDACategoryInfoType extends AbstractType ] ] ) - ->add('invisible', TriStateCheckboxType::class, [ - 'label' => 'eda_info.invisible', + ->add('visibility', TriStateCheckboxType::class, [ + 'help' => 'eda_info.visibility.help', + 'label' => 'eda_info.visibility', ]) ->add('exclude_from_bom', TriStateCheckboxType::class, [ 'label' => 'eda_info.exclude_from_bom', diff --git a/src/Form/Part/EDA/EDAPartInfoType.php b/src/Form/Part/EDA/EDAPartInfoType.php index 2e75d9e8..e8cac681 100644 --- a/src/Form/Part/EDA/EDAPartInfoType.php +++ b/src/Form/Part/EDA/EDAPartInfoType.php @@ -50,8 +50,9 @@ class EDAPartInfoType extends AbstractType 'placeholder' => t('eda_info.value.placeholder'), ] ]) - ->add('invisible', TriStateCheckboxType::class, [ - 'label' => 'eda_info.invisible', + ->add('visibility', TriStateCheckboxType::class, [ + 'help' => 'eda_info.visibility.help', + 'label' => 'eda_info.visibility', ]) ->add('exclude_from_bom', TriStateCheckboxType::class, [ 'label' => 'eda_info.exclude_from_bom', diff --git a/src/Services/EDA/KiCadHelper.php b/src/Services/EDA/KiCadHelper.php index 455ce7eb..cab52a19 100644 --- a/src/Services/EDA/KiCadHelper.php +++ b/src/Services/EDA/KiCadHelper.php @@ -80,7 +80,7 @@ class KiCadHelper $result = []; foreach ($categories as $category) { //Skip invisible categories - if ($category->getEdaInfo()->getInvisible() ?? false) { + if ($category->getEdaInfo()->getVisibility() === false) { continue; } @@ -146,7 +146,7 @@ class KiCadHelper $result = []; foreach ($parts as $part) { //If the part is invisible, then skip it - if ($part->getEdaInfo()->getInvisible() ?? $part->getCategory()?->getEdaInfo()->getInvisible() ?? false) { + if ($part->getEdaInfo()->getVisibility() === false || $part->getCategory()?->getEdaInfo()->getVisibility() === false) { continue; } diff --git a/templates/admin/category_admin.html.twig b/templates/admin/category_admin.html.twig index ddbf9b9b..5811640b 100644 --- a/templates/admin/category_admin.html.twig +++ b/templates/admin/category_admin.html.twig @@ -41,7 +41,7 @@
- {{ form_row(form.eda_info.invisible) }} + {{ form_row(form.eda_info.visibility) }}
diff --git a/templates/parts/edit/_eda.html.twig b/templates/parts/edit/_eda.html.twig index c1cd3991..4df675c4 100644 --- a/templates/parts/edit/_eda.html.twig +++ b/templates/parts/edit/_eda.html.twig @@ -3,7 +3,7 @@
- {{ form_row(form.eda_info.invisible) }} + {{ form_row(form.eda_info.visibility) }}
diff --git a/templates/parts/info/_extended_infos.html.twig b/templates/parts/info/_extended_infos.html.twig index 6eab4826..4ed60a09 100644 --- a/templates/parts/info/_extended_infos.html.twig +++ b/templates/parts/info/_extended_infos.html.twig @@ -92,7 +92,7 @@
{% trans %}eda_info.value{% endtrans %}: {{ part.edaInfo.value }}
- {% trans %}eda_info.invisible{% endtrans %}: {{ helper.boolean_badge( part.edaInfo.invisible ?? part.category.edaInfo.invisible ?? false) }} + {% trans %}eda_info.visibility{% endtrans %}: {{ helper.boolean_badge( part.edaInfo.visibility ?? part.category.edaInfo.visibility) }}
{% trans %}eda_info.exclude_from_bom{% endtrans %}: {{ helper.boolean_badge( part.edaInfo.excludeFromBom ?? part.category.edaInfo.excludeFromBom ?? false) }}
diff --git a/translations/messages.en.xlf b/translations/messages.en.xlf index c49b8448..10a96df5 100644 --- a/translations/messages.en.xlf +++ b/translations/messages.en.xlf @@ -12107,12 +12107,6 @@ Please note, that you can not impersonate a disabled user. If you try you will g e.g. 100n - - - eda_info.invisible - Invisible to EDA software - - eda_info.exclude_from_bom @@ -12179,5 +12173,17 @@ Please note, that you can not impersonate a disabled user. If you try you will g KiCad API root URL + + + eda_info.visibility + Force visibility + + + + + eda_info.visibility.help + By default, the visibility to the EDA software is automatically determined. With this checkbox, you can force the part to be visible or invisible. + + From 74d1904df1f866be74fc2c220a68bed048ed21c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sun, 3 Dec 2023 20:22:47 +0100 Subject: [PATCH 6/7] Only show parts and their categories in KiCad if they have useful info defined on them --- src/Services/EDA/KiCadHelper.php | 101 +++++++++++++++++++++++++++++-- 1 file changed, 97 insertions(+), 4 deletions(-) diff --git a/src/Services/EDA/KiCadHelper.php b/src/Services/EDA/KiCadHelper.php index cab52a19..f50e52cd 100644 --- a/src/Services/EDA/KiCadHelper.php +++ b/src/Services/EDA/KiCadHelper.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace App\Services\EDA; use App\Entity\Parts\Category; +use App\Entity\Parts\Footprint; use App\Entity\Parts\Part; use App\Services\Cache\ElementCacheTagGenerator; use App\Services\Trees\NodesListBuilder; @@ -63,8 +64,8 @@ class KiCadHelper $secure_class_name = $this->tagGenerator->getElementTypeCacheTag(Category::class); $item->tag($secure_class_name); - //If the category depth is smaller than 1, create only one dummy category - if ($this->category_depth < 1) { + //If the category depth is smaller than 0, create only one dummy category + if ($this->category_depth < 0) { return [ [ 'id' => '0', @@ -98,6 +99,11 @@ class KiCadHelper continue; } + //Check if the category should be visible + if (!$this->shouldCategoryBeVisible($category)) { + continue; + } + //Format the category for KiCAD $result[] = [ 'id' => (string)$category->getId(), @@ -121,7 +127,9 @@ class KiCadHelper function (ItemInterface $item) use ($category) { $item->tag([ $this->tagGenerator->getElementTypeCacheTag(Category::class), - $this->tagGenerator->getElementTypeCacheTag(Part::class) + $this->tagGenerator->getElementTypeCacheTag(Part::class), + //Visibility can change based on the footprint + $this->tagGenerator->getElementTypeCacheTag(Footprint::class) ]); if ($this->category_depth >= 0) { @@ -146,7 +154,7 @@ class KiCadHelper $result = []; foreach ($parts as $part) { //If the part is invisible, then skip it - if ($part->getEdaInfo()->getVisibility() === false || $part->getCategory()?->getEdaInfo()->getVisibility() === false) { + if (!$this->shouldPartBeVisible($part)) { continue; } @@ -225,6 +233,91 @@ class KiCadHelper return $result; } + /** + * Determine if the given part should be visible for the EDA. + * @param Category $category + * @return bool + */ + private function shouldCategoryBeVisible(Category $category): bool + { + $eda_info = $category->getEdaInfo(); + + //If the category visibility is explicitly set, then use it + if ($eda_info->getVisibility() !== null) { + return $eda_info->getVisibility(); + } + + //try to check if the fields were set + if ($eda_info->getKicadSymbol() !== null + || $eda_info->getReferencePrefix() !== null) { + return true; + } + + //Check if there is any part in this category, which should be visible + $category_repo = $this->em->getRepository(Category::class); + if ($category->getLevel() >= $this->category_depth) { + //Get all parts for the category and its children + $parts = $category_repo->getPartsRecursive($category); + } else { + //Get only direct parts for the category (without children), as the category is not collapsed + $parts = $category_repo->getParts($category); + } + + foreach ($parts as $part) { + if ($this->shouldPartBeVisible($part)) { + return true; + } + } + + //Otherwise the category should be not visible + return false; + } + + /** + * Determine if the given part should be visible for the EDA. + * @param Part $part + * @return bool + */ + private function shouldPartBeVisible(Part $part): bool + { + $eda_info = $part->getEdaInfo(); + $category = $part->getCategory(); + + //If the user set a visibility, then use it + if ($eda_info->getVisibility() !== null) { + return $part->getEdaInfo()->getVisibility(); + } + + //If the part has a category, then use the category visibility if possible + if ($category && $category->getEdaInfo()->getVisibility() !== null) { + return $category->getEdaInfo()->getVisibility(); + } + + //If both are null, then we try to determine the visibility based on if fields are set + if ($eda_info->getKicadSymbol() !== null + || $eda_info->getKicadFootprint() !== null + || $eda_info->getReferencePrefix() !== null + || $eda_info->getValue() !== null) { + return true; + } + + //Check also if the fields are set for the category (if it exists) + if ($category && ( + $category->getEdaInfo()->getKicadSymbol() !== null + || $category->getEdaInfo()->getReferencePrefix() !== null + )) { + return true; + } + + //And on the footprint + if ($part->getFootprint() && $part->getFootprint()->getEdaInfo()->getKicadFootprint() !== null) { + return true; + } + + //Otherwise the part should be not visible + return false; + } + /** * Converts a boolean value to the format required by KiCAD. * @param bool $value From 62cbc168fb2fe8b3fa1921f09d3af6b49e2d3e49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sun, 3 Dec 2023 20:30:49 +0100 Subject: [PATCH 7/7] Updated documentation about new visibility changes. --- docs/usage/eda_integration.md | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/docs/usage/eda_integration.md b/docs/usage/eda_integration.md index 11c4b231..3bbee4da 100644 --- a/docs/usage/eda_integration.md +++ b/docs/usage/eda_integration.md @@ -25,7 +25,8 @@ You require a user account in Part-DB, which has the permission to access Part-D To connect KiCad with Part-DB do following steps: 1. Create an API token on the user settings page for the KiCAD application and copy/save it, when it is shown. Currently KiCAD can only read Part-DB database, so a token with read only scope is enough. -2. Create a file `partd.kicad_httplib` (or similar, only the extension is important) with the following content: +2. Add some EDA metadata to parts, categories or footprints. Only parts with useable info will show up in KiCad. See below for more info. +3. Create a file `partd.kicad_httplib` (or similar, only the extension is important) with the following content: ``` { "meta": { @@ -41,11 +42,11 @@ To connect KiCad with Part-DB do following steps: } } ``` -3. Replace the `root_url` with the URL of your Part-DB instance plus `/en/kicad-api/`. You can find the right value for this in the Part-DB user settings page under "API endpoints" in the "API tokens" panel. -4. Replace the `token` field value with the token you have generated in step 1. -5. Open KiCad and add this created file as library in the KiCad symbol table under (Preferences --> Manage Symbol Libraries) +4. Replace the `root_url` with the URL of your Part-DB instance plus `/en/kicad-api/`. You can find the right value for this in the Part-DB user settings page under "API endpoints" in the "API tokens" panel. +5. Replace the `token` field value with the token you have generated in step 1. +6. Open KiCad and add this created file as library in the KiCad symbol table under (Preferences --> Manage Symbol Libraries) -If you then place a new part, the library dialog opens and you should be able to see the categories and parts from Part-DB. +If you then place a new part, the library dialog opens, and you should be able to see the categories and parts from Part-DB. ### How to associate footprints and symbols with parts @@ -57,6 +58,15 @@ For example to configure the values for an BC547 transistor you would put `Trans If you type in a character, you will get an autocomplete list of all symbols and footprints available in the kicad standard library. You can also input your own value. +### Parts and category visibility + +Only parts and their categories, on which there is any kind of EDA metadata are defined show up in KiCad. So if you want to see parts in KiCad, +you need to define at least a symbol, footprint, reference prefix or value on a part, category or footprint. + +You can use the "Force visibility" checkbox on a part or category to override this behavior and force parts to be visible or hidden in KiCad. + +*Please note that KiCad caches the library categories. So if you change something, which would change the visibile categories in KiCad, you have to reload EEschema to see the changes.* + ### Category depth in KiCad For performance reasons, only the most top level categories of Part-DB are shown as categories in KiCad. All parts in the subcategories are shown in the top level category.