Coverage for JLC2KiCadLib/symbol/symbol.py: 95%

1 import json

2 import logging

3 import os

4 import re

5

6 import requests

7

8 from .symbol_handlers import handlers

9

10 template_lib_header = """\

11(kicad_symbol_lib (version 20210201) (generator TousstNicolas/JLC2KiCad_lib)

12"""

13

14 template_lib_footer = ")\n"

15

16 supported_value_types = [

17 "Resistance",

18 "Capacitance",

19 "Inductance",

20 "Frequency",

21] # define which attribute/value from JLCPCB/LCSC will be added in the "value" field

22

23

24 def create_symbol(

25 symbol_component_uuid,

26 footprint_name,

27 datasheet_link,

28 library_name,

29 symbol_path,

30 output_dir,

31 component_id,

32 skip_existing,

33):

34 class kicad_symbol:

35 drawing = ""

36 pinNamesHide = "(pin_names hide)"

37 pinNumbersHide = "(pin_numbers hide)"

38

39 kicad_symbol = kicad_symbol()

40

41 ComponentName = ""

42 for component_uuid in symbol_component_uuid:

43 response = requests.get(f"https://easyeda.com/api/components/{component_uuid}")

44 if response.status_code == requests.codes.ok:

45 data = json.loads(response.content.decode())

46 else:

47 logging.error(

48 f"create_symbol error. Requests returned with error code "

49 f"{response.status_code}"

50 )

51 return ()

52

53 symbol_shape = data["result"]["dataStr"]["shape"]

54 symmbol_prefix = data["result"]["packageDetail"]["dataStr"]["head"]["c_para"][

55 "pre"

56 ].replace("?", "")

57 component_title = (

58 data["result"]["title"]

59 .replace(" ", "_")

60 .replace(".", "_")

61 .replace("/", "{slash}")

62 .replace("\\", "{backslash}")

63 .replace("<", "{lt}")

64 .replace(">", "{gt}")

65 .replace(":", "{colon}")

66 .replace('"', "{dblquote}")

67 )

68

69 component_types_values = []

70 for value_type in supported_value_types:

71 if value_type in data["result"]["dataStr"]["head"]["c_para"]:

72 component_types_values.append(

73 (

74 value_type,

75 data["result"]["dataStr"]["head"]["c_para"][value_type],

76 )

77 )

78

79 if not ComponentName:

80 ComponentName = component_title

81 component_title += "_0"

82 if (

83 len(symbol_component_uuid) >= 2

84 and component_uuid == symbol_component_uuid[0]

85 ):

86 continue

87

88 # if library_name is not defined, use component_title as library name

89 if not library_name:

90 library_name = ComponentName

91

92 filename = f"{output_dir}/{symbol_path}/{library_name}.kicad_sym"

93

94 logging.info(f"Creating symbol {component_title} in {library_name}")

95

96 kicad_symbol.drawing += f'''\n (symbol "{component_title}_1"'''

97

98 for line in symbol_shape:

99 args = [i for i in line.split("~")] # split arguments

100 model = args[0]

101 logging.debug(args)

102 if model not in handlers:

103 logging.warning("symbol : parsing model not in handler : " + model)

104 else:

105 handlers.get(model)(

106 data=args[1:],

107 translation=(

108 data["result"]["dataStr"]["head"]["x"],

109 data["result"]["dataStr"]["head"]["y"],

110 ),

111 kicad_symbol=kicad_symbol,

112 )

113 kicad_symbol.drawing += """\n )"""

114

115 # ruff: disable [E501]

116 template_lib_component = f"""\

117 (symbol "{ComponentName}" {kicad_symbol.pinNamesHide} {kicad_symbol.pinNumbersHide} (in_bom yes) (on_board yes)

118 (property "Reference" "{symmbol_prefix}" (id 0) (at 0 1.27 0)

119 (effects (font (size 1.27 1.27)))

120 )

121 (property "Value" "{ComponentName}" (id 1) (at 0 -2.54 0)

122 (effects (font (size 1.27 1.27)))

123 )

124 (property "Footprint" "{footprint_name}" (id 2) (at 0 -10.16 0)

125 (effects (font (size 1.27 1.27) italic) hide)

126 )

127 (property "Datasheet" "{datasheet_link}" (id 3) (at -2.286 0.127 0)

128 (effects (font (size 1.27 1.27)) (justify left) hide)

129 )

130 (property "ki_keywords" "{component_id}" (id 4) (at 0 0 0)

131 (effects (font (size 1.27 1.27)) hide)

132 )

133 (property "LCSC" "{component_id}" (id 5) (at 0 0 0)

134 (effects (font (size 1.27 1.27)) hide)

135 )

136 {get_type_values_properties(6, component_types_values)}{kicad_symbol.drawing}

137 )

138"""

139 # ruff: enable [E501]

140

141 if not os.path.exists(f"{output_dir}/{symbol_path}"):

142 os.makedirs(f"{output_dir}/{symbol_path}")

143

144 if os.path.exists(filename):

145 update_library(

146 library_name,

147 symbol_path,

148 ComponentName,

149 template_lib_component,

150 output_dir,

151 skip_existing,

152 )

153 else:

154 with open(filename, "w") as f:

155 logging.info(f"writing in {filename} file")

156 f.write(template_lib_header)

157 f.write(template_lib_footer)

158 update_library(

159 library_name,

160 symbol_path,

161 ComponentName,

162 template_lib_component,

163 output_dir,

164 skip_existing,

165 )

166

167

168 def get_type_values_properties(start_index, component_types_values):

169 # ruff: disable [E501]

170 return "\n".join(

171 [

172 f"""(property "{type_value[0]}" "{type_value[1]}" (id {start_index + index}) (at 0 0 0)

173 (effects (font (size 1.27 1.27)) hide)

174 )"""

175 for index, type_value in enumerate(component_types_values)

176 ]

177 )

178 # ruff: enable [E501]

179

180

181 def update_library(

182 library_name,

183 symbol_path,

184 component_title,

185 template_lib_component,

186 output_dir,

187 skip_existing,

188):

189 """

190 if component is already in library,

191 the library will be updated,

192 if not already present in library,

193 the component will be added at the end

194 """

195

196 with open(

197 f"{output_dir}/{symbol_path}/{library_name}.kicad_sym", "rb+"

198 ) as lib_file:

199 pattern = rf' \(symbol "{component_title}" (\n|.)*?\n \)'

200 file_content = lib_file.read().decode()

201

202 if f'symbol "{component_title}"' in file_content:

203 if skip_existing:

204 logging.info(

205 f"component {component_title} already in symbols library, skipping"

206 )

207 return

208 # use regex to find the old component template in the file and

209 # replace it with the new one

210 logging.info(

211 f"found component already in {library_name}, updating {library_name}"

212 )

213 sub = re.sub(

214 pattern=pattern,

215 repl=template_lib_component,

216 string=file_content,

217 flags=re.DOTALL,

218 count=1,

219 )

220 lib_file.seek(0)

221 # delete the file content and rewrite it

222 lib_file.truncate()

223 lib_file.write(sub.encode())

224 else:

225 # move before the library footer and write the component template

226 # see https://github.com/TousstNicolas/JLC2KiCad_lib/issues/46

227 new_content = file_content[: file_content.rfind(")")]

228 new_content = new_content + template_lib_component + template_lib_footer

229 lib_file.seek(0)

230 lib_file.write(new_content.encode())