wokwi / easyeda2kicad

Convert EasyEDA designs to KiCad EDA
https://wokwi.com/tools/easyeda2kicad
Other
287 stars 39 forks source link

"Warning: unsupported shape SVGNODE [...]" #38

Closed planetsLightningArrester closed 4 years ago

planetsLightningArrester commented 4 years ago

Hi.

I developed a simple PCB that I'd like to import in FreeCAD using this addon which seems to works great with some .kicad_pcb files from a KiCad project.

I can generate my EasyEDA file "input.json" an get the "output.kicad_pcb" in both Node library on my pc and the online converter. But FreeCAD can't open the file. As I pointed out it seems to work just fine with another directly generated .kicad_pcb files so I don't think the issue is the addon.

I got two warnings:

Running easyeda2kicad v1.8.0: Reading file input.json... Parsing content... Detected EasyEDA board (PCB) file. Warning: unsupported shape SVGNODE in footprint ggef90f9c042f2df78c Warning: unsupported shape SVGNODE in footprint gge5701536e7eb809af Writing output to input.kicad_pcb... Conversion done!

I can make FreeCAD open the file if I open the "output.kicad_pcb" in KiCad and save it as another file. The input.json, output.kicad_pcb and the savedAsWorking.kicad_pcb files are attached. It may be an enhancement once it works to open the output in Kicad. But it would be really nice to have this converter to works just like a KiCad "Save as ..". Anyway thank you so much for your time and let me know if I can help you with more info.

inOutFiles.zip SavedAsWorking.zip

Windows 10 Home Chrome 83.0.4103.97 EasyEDA v6.3.53 Node v12.16.3 NPM v6.14.4

urish commented 4 years ago

Thanks for reporting! Does the FreeCAD plugin report any specific error?

planetsLightningArrester commented 4 years ago

Hi! At first, it doesn't prompt any useful error just: "Incompatible file format". So I looked over the addon python files and it seems that it was just that the addon looks in the first line of the file for the kicad description like:

"(kicad_pcb (version 20171130) (host pcbnew "(5.1.6)-1")"

Which is in the second line of your generated file. This error is resolved just deleting the first empty line of the .kicad_pcb output file.

But loading the files still prompts a lot of errors. I'm pretty sure its because the addon uses regex expression expecting a specific indentation/spacing/formatting for the whole file. For example layer "F.SilkS", fp_text reference "R1" and every other layer/net_name and so must not have quotation marks. For the addon identifies it correctly it should be layer F.SilkS, fp_text reference R1. Change it all resolves some issues.

But the formatting issues are more complex. Following this error:

[' D1(at 0.762 1.016 0)(layer F.SilkS)\n      (effects (font (size 1.143 1.143) (thickness 0.152)) (justify left)'][]Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "C:\Users\franc\AppData\Roaming\FreeCAD\Mod\pcb\PCBbrd.py", line 66, in open
    importBRD(filename, wersjaFormatu[0])
  File "C:\Users\franc\AppData\Roaming\FreeCAD\Mod\pcb\PCBbrd.py", line 235, in importBRD
    plytka = plytkaPCB.generate(doc)
  File "C:\Users\franc\AppData\Roaming\FreeCAD\Mod\pcb\formats\PCBmainForms.py", line 134, in generate
    self.importParts()
  File "C:\Users\franc\AppData\Roaming\FreeCAD\Mod\pcb\formats\PCBmainForms.py", line 247, in importParts
    for i in self.wersjaFormatu.getParts():
  File "C:\Users\franc\AppData\Roaming\FreeCAD\Mod\pcb\formats\kicad_v3.py", line 833, in getParts
    i['EL_Name'] = dataName[0]
<class 'IndexError'>: list index out of range

I reached this method:

        def getParts(self):
        self.getElements()
        parts = []
        ###########
        for i in self.elements:
            if i['side'] == 1:
                i['side'] = "TOP"
            else:
                i['side'] = "BOTTOM"
            ####################################
            dataName = self.getAnnotations(re.findall(r'\(fp_text reference(.+?)\)\n\s+\)\n\s+\(', i['dataElement'], re.MULTILINE|re.DOTALL), mode='param')
            i['EL_Name'] = dataName[0]
            i['EL_Name']["text"] = "NAME"
            i['EL_Name']["x"] = i['EL_Name']["x"] + i["x"]
            i['EL_Name']["y"] = i['EL_Name']["y"] + i["y"]
            i['EL_Name']["rot"] = i['EL_Name']["rot"] - i["rot"]
            ####################################
            dataValue = self.getAnnotations(re.findall(r'\(fp_text value(.+?)\)\n\s+\)\n\s+\(', i['dataElement'], re.MULTILINE|re.DOTALL), mode='param')
            i['EL_Value'] = dataValue[0]
            i['EL_Value']["text"] = "VALUE"
            i['EL_Value']["x"] = i['EL_Value']["x"] + i["x"]
            i['EL_Value']["y"] = i['EL_Value']["y"] + i["y"]
            i['EL_Value']["rot"] = i['EL_Value']["rot"] - i["rot"]
            ####################################
            parts.append(i)
        #
        return parts

And dataName returns an empty list most of the time which rises index errors on the next line. The getAnnotations method is as follows.

    def getAnnotations(self, data, mode='anno'):
        adnotacje = []
        #
        for i in data:
            try:
                txt = re.search(r'\s(.*?)\s\(at', i).groups(0)[0].replace('"', '').replace('\r\n', '\n').replace('\r', '\n').replace('\\n', '\n')
                [x, y, rot] = re.search(r'\(at\s+([0-9\.-]*?)\s+([0-9\.-]*?)(\s+[0-9\.-]*?|)\)', i).groups()
                layer = re.search(r'\(layer\s+(.+?)\)', i).groups()[0]
                size = re.search(r'\(size\s+([0-9\.]*?)\s+[0-9\.]*?\)', i).groups()[0]
                justify = re.findall(r'( \(justify .*?\)|)\)', i)[-1].strip()
            except:
                continue
            #
            if rot == '':
                rot = 0.0
            else:
                rot = float(rot)

            if layer.startswith('F.') or (self.spisWarstw[layer] in [15, 21] and self.databaseType == "kicad") or (self.spisWarstw[layer] in [0, 33, 35, 37, 39, 40, 41, 42, 43, 44, 45, 47, 49] and self.databaseType == "kicad_v4"):
                side = 'TOP'
            else:
                side = 'BOTTOM'

            extra = re.findall(r'\(justify( [left|right]+|)( mirror|)\)', justify, re.DOTALL)
            if len(extra):
                if extra[0][0].strip() == 'right':
                    align = 'center-right'
                elif extra[0][0].strip() == 'left':
                    align = 'center-left'
                else:
                    align = 'center'

                if extra[0][1].strip() == 'mirror':
                    mirror = 2
                else:
                    mirror = 0
            else:
                align = 'center'
                mirror = False

            adnotacje.append({
                "text": txt,
                "x": float(x),
                "y": float(y) * (-1),
                "z": 0,
                "size": float(size),
                "rot": rot,
                "side": side,
                "align": align,
                "spin": False,
                "font": 'Proportional',
                "display": True,
                "distance": 1,
                "tracking": 0,
                "mirror": mirror,
                "mode": mode
            })
        #
        return adnotacje

So the file must follow the regex expressions so the addon can identify the parameters. It's not robust for files slightly different.

urish commented 4 years ago

Thanks for looking into this!

Removing the first empty line should be simple, but it seems like the correct solution would actually be to rewrite the parser of the FreeCAD-PCB addon to parse the file according to the the S-expression format (section 4 describes the format). It shouldn't be too hard - the JavaScript code that parses this format takes roughly 60 lines of code.

But IMHO, the best solution for you could just be to create a python script that will load the file in pcbnew, and then save it again under a different name. I believe that should take less than 10 lines of code, and will probably be the lowest hanging fruit here.

planetsLightningArrester commented 4 years ago

I'll probably stick with Python script's solution. Thank you so much for your help!