Skip to content

Commit

Permalink
new version 0.0.3
Browse files Browse the repository at this point in the history
  • Loading branch information
prrvchr committed Jan 17, 2020
1 parent 295878e commit ccdc5c6
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 33 deletions.
27 changes: 21 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,29 +13,44 @@

### Use:

#### Required Eeschema custom fields:

It is necessary for the operation to add 4 additional custom fields in Eeschema, which are:
- `Manufacturer`
- `PartNumber`
- `Supplier`
- `SupplierRef`

The required custom field `Supplier` must be set to one of the predefined supplier (LCSC or JLCPcb for example) or any other supplier name case insensitive.
The required field `Supplier` must be set to one of the predefined supplier (LCSC or JLCPcb for example) or any other supplier name case insensitive.

The required field `SupplierRef` can be replaced by other custom fields such as: `LCSCRef` or `JLCPcbRef` in order to make possible the referencing of several suppliers on a Eeschema component.
In this case, changing the value of the `Supplier` field will allow you to switch to the correct supplier reference automatically.

#### Optional Eeschema custom fields:

The required custom field `SupplierRef` can be replaced by other custom fields such as: `LCSCRef` or `JLCPcbRef` in order to make possible the referencing of several suppliers on a Eeschema component. In this case, changing the value of the `Supplier` field will allow you to switch to the correct supplier reference.
Two additional fields can be add to an Eeschema componant, witch are:
- `Quantity`
- `Rotation`

it is also possible to add an optional `Quantity` custom field in order to be able to manage the quantities in Eeschema.
In the absence of this field, the default quantity is 1. If value is set to 0 or is invalid, the component will not be integrated to any BOM file...
The optional `Quantity` field allow to manage the quantities in Eeschema.
In the absence of this field, the default quantity is 1.
If value is set to 0 or is invalid, the component will not be integrated to any BOM file...

The optional `Rotation` field allow to correct rotation, if needed when generating the CPL file...

#### Customisation:

If necessary, a grouping is carried out on the quantities, making it possible to generate a single row for identical components in the BOM file.

If you know a little Python, the grouping and the order of the components in the BOM files is fully configurable ...

When launching the plugin (in Eeschema BOM) it will create as many BOM files as there are different `Supplier` encountered.

It is possible to add a quantity argument at the end of the plugin command line (ie: `bom-cpl-plugin.py" "%I" "%O" 1234`), without this argument or with an invalid value, a default quantity of 1 will be used.
It is possible to add a quantity argument at the end of the plugin command line (ie: `bom-cpl-plugin.py" "%I" "%O" 1234`).
Without this argument or with an invalid value, a default quantity of 1 will be used.

It also creates the CPL file for JLCPcb from one of the position files found in the working directory (ie: `your_project-all-pos.csv`, `your_project-top-pos.csv`, `your_project-bottom-pos.csv`).

A correction on the rotations will be made according to the `Rotation` field of each component.

### Predefined suppliers:

Expand Down
82 changes: 55 additions & 27 deletions bom-cpl-plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
g_bomext = 'BOM'
g_cplext = 'CPL'
g_posfiles = ('all-pos', 'top-pos', 'bottom-pos')
g_rotations = {'Reference': 0, 'Value': 5, 'Format': '%.6f'}

g_suppliers = {}
g_suppliers['Default'] = {}
Expand Down Expand Up @@ -144,6 +145,9 @@ def getHeaders(supplier):
return g_suppliers[supplier]['cplheaders']
return ()

def getRotations(value):
return g_rotations[value]

def getInputs(path):
inputs = []
for post in g_posfiles:
Expand All @@ -156,12 +160,21 @@ def getInput(path, post, ext='csv'):
def getOutput(path, supplier, post, ext='csv'):
return "%s_%s_%s.%s" % (path, supplier.title(), post, ext)

def getQuantity(quantity, minimum=0):
def getInteger(string, default=0, minimum=None):
try:
i = int(string)
except ValueError:
i = default
if minimum is not None:
i = max(i, minimum)
return i

def getFloat(string, default=0):
try:
i = int(quantity)
i = float(string)
except ValueError:
i = minimum
return max(i, minimum)
i = default
return i


class Component(object):
Expand All @@ -186,6 +199,7 @@ def __init__(self, component):
self.PartNumber = ''
self.SupplierRef = ''
self.Quantity = 1
self.Rotation = 0

@property
def isvalid(self):
Expand Down Expand Up @@ -226,9 +240,12 @@ def setCustomFields(self, fields, suppliers):
reference = fields.find('./field[@name="SupplierRef"]')
if reference is not None:
self.SupplierRef = reference.text
rotation = fields.find('./field[@name="Rotation"]')
if rotation is not None:
self.Rotation = getInteger(rotation.text, 0)
quantity = fields.find('./field[@name="Quantity"]')
if quantity is not None:
self.Quantity = getQuantity(quantity.text)
self.Quantity = getInteger(quantity.text, 0, 0)
if all((getattr(self, a) for a in getValid(self.Supplier))):
if self.Supplier not in suppliers:
suppliers.append(self.Supplier)
Expand All @@ -238,8 +255,8 @@ def setCustomFields(self, fields, suppliers):

def generateBom(xml, path, quantity):
suppliers, components, missings = parseXml(xml, quantity)
writeCsv(suppliers, components, path)
return suppliers, missings
rotations = writeCsv(suppliers, components, path)
return suppliers, missings, rotations


def parseXml(xml, quantity):
Expand Down Expand Up @@ -273,7 +290,10 @@ def parseXml(xml, quantity):


def writeCsv(suppliers, components, path):
rotations = {}
for supplier in suppliers:
if needCpl(supplier) and supplier not in rotations:
rotations[supplier] = {}
out = getOutput(path, supplier, g_bomext)
columns = getFields(supplier).keys()
delimiter = getDelimiter(supplier)
Expand All @@ -293,32 +313,40 @@ def writeCsv(suppliers, components, path):
for key, value in fields:
row[key] = getattr(component, value)
c.writerow(row)
if needCpl(supplier) and component.Rotation != 0:
rotations[supplier][component.ref] = component.Rotation
return rotations


def generateCpl(suppliers, path):
rewrited = {}
rotations = {}
ref = getRotations('Reference')
value = getRotations('Value')
format = getRotations('Format')
for supplier in suppliers:
if needCpl(supplier):
rewrited[supplier] = False
for i in getInputs(path):
if os.path.isfile(i):
out = getOutput(path, supplier, g_cplext)
headers = getHeaders(supplier)
copyCsv(i, out, headers)
rewrited[supplier] = True
break
return rewrited


def copyCsv(input, output, headers):
with open(input) as r, open(output, 'w') as w:
rotations[supplier] = False
for i in getInputs(path):
if os.path.isfile(i):
out = getOutput(path, supplier, g_cplext)
headers = getHeaders(supplier)
copyCsv(i, out, headers, suppliers[supplier], ref, value, format)
rotations[supplier] = True
break
return rotations


def copyCsv(i, o, headers, rotations, ref, value, format):
with open(i) as r, open(o, 'w') as w:
reader = csv.reader(r)
writer = csv.writer(w)
header = next(reader)
for i, value in headers.items():
header[i] = value
for k, v in headers.items():
header[k] = v
writer.writerow(header)
for row in reader:
if row[ref] in rotations:
rotation = getFloat(row[value]) + rotations[row[ref]]
row[value] = format % rotation
writer.writerow(row)


Expand All @@ -327,13 +355,13 @@ def getArguments():
path = sys.argv[2]
quantity = 1
if len(sys.argv) > 3:
quantity = getQuantity(sys.argv[3], 1)
quantity = getInteger(sys.argv[3], 1, 1)
return xml, path, quantity


if __name__ == "__main__":
xml, path, quantity = getArguments()
suppliers, missings = generateBom(xml, path, quantity)
suppliers, missings, rotations = generateBom(xml, path, quantity)
print("")
print(">>> Generating BOM (Bill Of Materials) csv file for (Quantity = %s):" % quantity)
for supplier in suppliers:
Expand All @@ -345,7 +373,7 @@ def getArguments():
print("WARNING: These componants cannot be integrated:")
print(", ".join(missings))

suppliers = generateCpl(suppliers, path)
suppliers = generateCpl(rotations, path)
if len(suppliers):
print("")
print(">>> Generating CPL (Component Placement List) csv file for:")
Expand Down

0 comments on commit ccdc5c6

Please sign in to comment.