222 lines
5.3 KiB
Python
222 lines
5.3 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Copyright (c) 2023 Salvador E. Tropea
|
|
# Copyright (c) 2023 Instituto Nacional de Tecnología Industrial
|
|
# License: MIT
|
|
# Project: KiBot (formerly KiPlot)
|
|
|
|
from decimal import Decimal
|
|
from lark import Lark, Transformer
|
|
import os
|
|
from ..gs import GS
|
|
from .. import log
|
|
|
|
logger = log.get_logger()
|
|
# Metric to imperial package sizes
|
|
TO_IMPERIAL = {'0402': '01005',
|
|
'0603': '0201',
|
|
'1005': '0402',
|
|
'1608': '0603',
|
|
'2012': '0805',
|
|
'2520': '1008',
|
|
'3216': '1206',
|
|
'3225': '1210',
|
|
'4516': '1806',
|
|
'4532': '1812',
|
|
'5025': '2010',
|
|
'6332': '2512'}
|
|
parser = None
|
|
|
|
|
|
class ComponentTransformer(Transformer):
|
|
""" Transforms a tree parsed by Lark to the electro-grammar dict """
|
|
def __init__(self):
|
|
self.parsed = {}
|
|
# Extra information, not in the original lib and needed for internal purposes
|
|
self.extra = {}
|
|
|
|
def value3(self, d, type):
|
|
""" VALUE [METRIC_PREFIX [MANTISSA]] """
|
|
v = Decimal(d[0])
|
|
c = len(d)
|
|
if c >= 3:
|
|
# We have something like 2n2
|
|
dec = d[2]
|
|
c_dec = len(dec)
|
|
v += Decimal(dec)/(Decimal(10)*c_dec)
|
|
self.extra['val'] = v
|
|
if c >= 2:
|
|
# Metric prefix
|
|
v *= d[1]
|
|
self.extra['mult'] = d[1]
|
|
else:
|
|
self.extra['mult'] = Decimal(1)
|
|
v = float(v)
|
|
self.parsed[type] = v
|
|
return v
|
|
|
|
def value2(self, d, type):
|
|
""" VALUE [MANTISSA] """
|
|
v = float(d[0])
|
|
c = len(d)
|
|
if c >= 2:
|
|
# We have something like 3V3
|
|
dec = d[1]
|
|
c_dec = len(dec)
|
|
v += float(dec)/(10.0*c_dec)
|
|
self.parsed[type] = v
|
|
return v
|
|
|
|
def value1(self, d, type):
|
|
""" VALUE """
|
|
v = float(d[0])
|
|
iv = round(v)
|
|
if iv == v:
|
|
v = iv
|
|
self.parsed[type] = v
|
|
return v
|
|
|
|
def tolerance(self, d):
|
|
return self.value1(d, 'tolerance')
|
|
|
|
def voltage_rating(self, d):
|
|
return self.value2(d, 'voltage_rating')
|
|
|
|
def temp_coef(self, d):
|
|
c_len = len(d)
|
|
if c_len == 3:
|
|
# Class 2: i.e. X7R
|
|
v = d[0].value+d[1].value+d[2].value
|
|
else:
|
|
# Class 1: i.e. C0G
|
|
v = d[0].type
|
|
self.parsed['characteristic'] = v.upper()
|
|
return v
|
|
|
|
def power_rating(self, d):
|
|
if len(d) == 1:
|
|
# 1 W
|
|
v = float(d[0])
|
|
elif d[0].type == 'INT':
|
|
# 1/4 W
|
|
v = float(d[0].value)/float(d[1].value)
|
|
else:
|
|
# 250 mW
|
|
v = float(Decimal(d[0].value)*d[1])
|
|
self.parsed['power_rating'] = v
|
|
return v
|
|
|
|
def color(self, d):
|
|
c = d[0].value.lower()
|
|
self.parsed['color'] = c
|
|
return c
|
|
|
|
def set_type(self, type, d):
|
|
self.parsed['type'] = type
|
|
return d
|
|
|
|
# Package size
|
|
def imperial_size(self, d):
|
|
s = d[0].value
|
|
self.parsed['size'] = s
|
|
return s
|
|
|
|
def unambigious_metric_size(self, d):
|
|
s = TO_IMPERIAL[d[0].value]
|
|
self.parsed['size'] = s
|
|
return s
|
|
|
|
metric_size_base = unambigious_metric_size
|
|
|
|
# RLC
|
|
def resistance(self, d):
|
|
return self.value3(d, 'resistance')
|
|
|
|
resistance_no_r = resistance
|
|
|
|
def inductance(self, d):
|
|
return self.value3(d, 'inductance')
|
|
|
|
inductance_no_henry = inductance
|
|
|
|
def capacitance(self, d):
|
|
return self.value3(d, 'capacitance')
|
|
|
|
capacitance_no_farad = capacitance
|
|
|
|
# Known components
|
|
def inductor(self, d):
|
|
return self.set_type('inductor', d)
|
|
|
|
def capacitor(self, d):
|
|
return self.set_type('capacitor', d)
|
|
|
|
def resistor(self, d):
|
|
return self.set_type('resistor', d)
|
|
|
|
def led(self, d):
|
|
return self.set_type('led', d)
|
|
|
|
# Metrix prefixes
|
|
def giga(self, _):
|
|
return Decimal('1e9')
|
|
|
|
def mega(self, _):
|
|
return Decimal('1e6')
|
|
|
|
def kilo(self, _):
|
|
return Decimal('1e3')
|
|
|
|
def unit(self, _):
|
|
return Decimal(1)
|
|
|
|
def milli(self, _):
|
|
return Decimal('1e-3')
|
|
|
|
def nano(self, _):
|
|
return Decimal('1e-9')
|
|
|
|
def micro(self, _):
|
|
return Decimal('1e-6')
|
|
|
|
def pico(self, _):
|
|
return Decimal('1e-12')
|
|
|
|
def femto(self, _):
|
|
return Decimal('1e-15')
|
|
|
|
def crap(self, v):
|
|
if 'discarded' in self.extra:
|
|
self.extra['discarded'].append(v[0].value)
|
|
else:
|
|
self.extra['discarded'] = [v[0].value]
|
|
return None
|
|
|
|
|
|
def initialize():
|
|
global parser
|
|
if parser is not None:
|
|
return
|
|
with open(os.path.join(GS.get_resource_path('parsers'), 'electro.lark'), 'rt') as f:
|
|
g = f.read()
|
|
parser = Lark(g, start='main') # , debug=DEBUG)
|
|
|
|
|
|
def parse(text, with_extra=False, stronger=False):
|
|
initialize()
|
|
if stronger:
|
|
text = text.replace('+/-', ' +/-')
|
|
text = text.replace(' - ', ' ')
|
|
try:
|
|
tree = parser.parse(text)
|
|
except Exception as e:
|
|
logger.debugl(2, str(e))
|
|
return {}
|
|
logger.debugl(3, tree.pretty())
|
|
res_o = ComponentTransformer()
|
|
res = res_o.transform(tree)
|
|
logger.debugl(3, res)
|
|
res = res_o.parsed
|
|
if with_extra:
|
|
res.update(res_o.extra)
|
|
return res
|