Example #1
0
def main():
    """
    The tendril-genpcbpricing script entry point.
    """
    parser = _get_parser()
    args = parser.parse_args()
    force = args.force
    lazy = args.lazy
    if args.all:
        regenerate_all(force=force, lazy=lazy, dry_run=args.dry_run)
    else:
        if not len(args.projfolders):
            parser.print_help()
        for projfolder in args.projfolders:
            if not os.path.isabs(projfolder):
                projfolder = os.path.join(os.getcwd(), projfolder)
                projfolder = os.path.normpath(projfolder)
            if not in_directory(projfolder, PROJECTS_ROOT):
                logger.error(
                    'Provided directory does not seem to be under the '
                    'tendril PROJECTS_ROOT. Skipping ' + projfolder)
                continue
            targets = [projfolder]
            if args.recurse:
                lprojects, lpcbs, lcards, lcard_reporoot = \
                    projects.get_projects(projfolder)
                targets.extend([lpcbs[x] for x in lpcbs.keys()])
            for target in targets:
                try:
                    if args.dry_run is False:
                        csil.generate_pcb_pricing(target,
                                                  forceregen=force,
                                                  noregen=lazy)
                        logger.info("Checked " + target)
                    else:
                        conffile.ConfigsFile(target)
                        logger.info("Will check " + target)
                except NoGedaProjectError:
                    # Make a guess.
                    if os.path.split(target)[1] == 'configs.yaml':
                        target == os.path.split(target)[0]
                    if os.path.split(target)[1] == 'schematic':
                        target == os.path.split(target)[0]
                    try:
                        if args.dry_run is False:
                            csil.generate_pcb_pricing(target,
                                                      forceregen=force,
                                                      noregen=lazy)
                            logger.info("Checked " + target)
                        else:
                            conffile.ConfigsFile(target)
                            logger.info("Will check " + target)
                    except NoGedaProjectError:
                        logger.error("No gEDA Project found at " + target)
Example #2
0
def get_projects(basefolder=None):
    lcards = {}
    lpcbs = {}
    lprojects = {}
    lcard_reporoot = {}
    lcable_projects = {}

    if basefolder is None:
        basefolder = PROJECTS_ROOT

    for root, dirs, files in os.walk(basefolder):
        dirs[:] = [d for d in dirs
                   if not d.endswith('.git') and not d.endswith('.svn')]
        for d in dirs:
            if is_project_folder(os.path.join(root, d)):
                lprojects[
                    os.path.relpath(os.path.join(root, d), basefolder)
                ] = os.path.join(root, d)
                cf = conffile.ConfigsFile(os.path.join(root, d))
                if cf.is_pcb:
                    lpcbs[cf.pcbname] = os.path.join(root, d)
                else:
                    lcable_projects[cf.cblname] = os.path.join(root, d)
                for config in cf.configuration_names:
                    lcards[config] = os.path.join(root, d)
                    lcard_reporoot[config] = \
                        os.path.relpath(os.path.join(root, d), basefolder)

    return lprojects, lpcbs, lcards, lcard_reporoot, lcable_projects
Example #3
0
def gen_pcb_dxf(projfolder, force=False):
    """
    Generates a DXF file of the PCB provided by the gEDA project.

    The pcb file is the one listed in the gEDA project file, and the
    pcbname is the one specified in the
    :mod:`tendril.gedaif.conffile.ConfigsFile`.

    This function does not use jinja2 and latex. It relies on
    :func:`tendril.connectors.geda.pcb.conv_pcb2dxf` instead.

    :param projfolder: The gEDA project folder.
    :type projfolder: str
    :param force: Regenerate even if up-to-date.
    :type force: bool
    :return: The output file path.

    .. rubric:: Paths

    * Output File :  ``<projectfolder>/pcb/<pcbfile>.dxf``
    * Source Files : The project's `.pcb` file.

    """
    configfile = conffile.ConfigsFile(projfolder)
    gpf = projfile.GedaProjectFile(configfile.projectfolder)
    pcb_mtime = fsutils.get_file_mtime(
        os.path.join(configfile.projectfolder, 'pcb', gpf.pcbfile + '.pcb'),
    )
    if pcb_mtime is None:
        logger.warning("PCB does not seem to exist for : " + projfolder)
        return
    docfolder = get_project_doc_folder(projfolder)
    dxffile = path.normpath(os.path.join(docfolder, os.pardir,
                            configfile.pcbname + '.dxf'))
    bottom_dxffile = path.normpath(os.path.join(docfolder, os.pardir,
                                   configfile.pcbname + 'bottom.dxf'))

    outf_mtime = fsutils.get_file_mtime(dxffile, fs=refdoc_fs)

    if not force and outf_mtime is not None and outf_mtime > pcb_mtime:
        logger.debug('Skipping up-to-date ' + dxffile)
        return dxffile

    logger.info('Regenerating ' + dxffile + os.linesep +
                'Last modified : ' + str(pcb_mtime) +
                '; Last Created : ' + str(outf_mtime))

    workspace_folder = workspace_fs.getsyspath(path.dirname(dxffile))
    workspace_fs.makedir(path.dirname(dxffile),
                         recursive=True, allow_recreate=True)

    pcb.conv_pcb2dxf(
        os.path.join(configfile.projectfolder, 'pcb', gpf.pcbfile + '.pcb'),
        workspace_folder, configfile.pcbname
    )

    copyfile(workspace_fs, dxffile, refdoc_fs, dxffile, overwrite=True)
    copyfile(workspace_fs, bottom_dxffile, refdoc_fs, bottom_dxffile, overwrite=True)
    return dxffile
Example #4
0
def is_project_folder(folder):
    if folder in projects.values():
        return True
    try:
        conffile.ConfigsFile(folder)
    except conffile.NoGedaProjectError:
        return False
    return True
Example #5
0
def gen_cobom_csv(projfolder, namebase, force=False):
    """
    Generates a CSV file in the
    :mod:`tendril.boms.outputbase.CompositeOutputBom` format, including the
    BOMs of the all the defined configurations of the project. This function
    uses a :mod:`csv.writer` instead of rendering a jinja2 template.

    It also generates configdocs for all the defined configurations of the
    project, using :func:`gen_confpdf`.

    :param projfolder: The gEDA project folder.
    :type projfolder: str
    :param namebase: The project name.
    :type namebase: str
    :param force: Regenerate even if up-to-date.
    :type force: bool
    :return: The output file path.

    .. rubric:: Paths

    * Output Files :  ``<project_doc_folder>/confdocs/conf_boms.csv``
    * Also triggers : :func:`gen_confpdf` for all listed configurations.
    * Source Files : The project's schematic folder.

    """
    gpf = projfile.GedaProjectFile(projfolder)
    configfile = conffile.ConfigsFile(projfolder)
    sch_mtime = fsutils.get_folder_mtime(gpf.configsfile.schfolder)

    docfolder = get_project_doc_folder(projfolder)
    cobom_csv_path = path.join(docfolder, 'confdocs', 'conf-boms.csv')
    outf_mtime = fsutils.get_file_mtime(cobom_csv_path, fs=refdoc_fs)

    if not force and outf_mtime is not None and outf_mtime > sch_mtime:
        logger.debug('Skipping up-to-date ' + cobom_csv_path)
        return cobom_csv_path

    logger.info('Regenerating ' + cobom_csv_path + os.linesep +
                'Last modified : ' + str(sch_mtime) +
                '; Last Created : ' + str(outf_mtime))

    bomlist = []
    for cfn in configfile.configuration_names:
        gen_confpdf(projfolder, cfn, namebase, force=force)
        lbom = boms_electronics.import_pcb(projfolder)
        lobom = lbom.create_output_bom(cfn)
        bomlist.append(lobom)
    cobom = boms_outputbase.CompositeOutputBom(bomlist)

    with refdoc_fs.open(cobom_csv_path, 'wb') as f:
        writer = csv.writer(f)
        writer.writerow(['device'] +
                        [x.configname for x in cobom.descriptors])
        for line in cobom.lines:
            writer.writerow([line.ident] + line.columns)
Example #6
0
def validate_project(projectfolder, s=False, statuses=None):
    """
    Report validation errors for all modules provided by the specified project.
    :param projectfolder: The path to the project folder.
    :param s: Whether to report sourcing errors as well.
    :param statuses: Criterion for module status to include the module.
    """
    try:
        projectfolder = get_project_folder(projectfolder)
        cf = conffile.ConfigsFile(projectfolder)
        modulenames = cf.configuration_names
        for modulename in modulenames:
            validate_module(modulename, s=s, statuses=statuses)
    except conffile.NoGedaProjectError:
        logger.error("No gEDA Project found at " + projectfolder)
Example #7
0
def regenerate_all(force=False, dry_run=False):
    """
    Regenerates documentation for all projects

    :param force: Regenerate even for up-to-date projects.
    :param dry_run: Check only. Don't actually generate any documentation.

    """
    for project in projects.projects:
        if dry_run:
            conffile.ConfigsFile(projects.projects[project])
            logger.info("Will check " + projects.projects[project])
        else:
            logger.info("Checking " + projects.projects[project])
            gedaproject.generate_docs(projects.projects[project], force=force)
Example #8
0
def get_img_list(projfolder, cardname=None):
    """
    Returns a list of :class:`docstore.ExposedDocument` instances, pointing to
    the generated renders for the gEDA project or card specified by the
    parameters.

    Currently, the ``cardname`` parameter is ignored, since no configuration
    specific images are generated.

    :param projfolder: The gEDA project folder.
    :param cardname: The cardname.
    :return: list of :class:`ExposedDocument`

    """
    configfile = conffile.ConfigsFile(projfolder)
    gpf = projfile.GedaProjectFile(configfile.projectfolder)
    namebase = configfile.pcbname

    project_doc_folder = get_project_doc_folder(projfolder)
    if not project_doc_folder:
        return []

    project_img_folder = os.path.join(project_doc_folder, os.pardir, 'img')
    rval = [
        ExposedDocument(
            namebase + ' PCB Top View',
            path.join(project_img_folder, gpf.pcbfile + '.top.png'),
            refdoc_fs
        ),
        ExposedDocument(
            namebase + ' PCB Bottom View',
            path.join(project_img_folder, gpf.pcbfile + '.bottom.png'),
            refdoc_fs
        ),
        ExposedDocument(
            namebase + ' PCB Layers',
            path.join(project_img_folder, gpf.pcbfile + '.devel.png'),
            refdoc_fs
        )
    ]
    for img in rval:
        if not img.exists:
            rval.remove(img)
    return rval
Example #9
0
def regenerate_all(force=False, lazy=False, dry_run=False):
    """
    Regenerates PCB pricing information for all projects.

    :param force: Regenerate even for up-to-date projects.
    :param lazy: New projects only. Doesn't regenerate for projects with
                 out-of-date pricing information
    :param dry_run: Check only. Don't actually generate any documentation.

    """
    for project in projects.pcbs:
        if dry_run:
            conffile.ConfigsFile(projects.pcbs[project])
            logger.info("Will check " + projects.pcbs[project])
        else:
            logger.info("Checking " + projects.pcbs[project])
            csil.generate_pcb_pricing(projects.pcbs[project],
                                      forceregen=force,
                                      noregen=lazy)
Example #10
0
def generate_docs(projfolder, force=False):
    """
    Generates all the docs for a specified gEDA project.

    :param projfolder: The gEDA project folder.
    :type projfolder: str
    :param force: Regenerate even if up-to-date.
    :type force: bool
    :return: The output file path.

    .. rubric:: Paths

    * Output File :  ``<project_doc_folder>/confdocs/<configname>-doc.pdf``
    * Source Files : The project's schematic folder.

    .. rubric:: Generated Documents

    * Master Doc, generated by :func:`gen_masterdoc`
    * Cobom CSV, generated by :func:`gen_cobom_csv`
    * PCB PDF, generated by :func:`gen_pcb_pdf`
    * PCB Gerber, generated by :func:`gen_pcb_gbr`
    * PCB DXF, generated by :func:`gen_pcb_dxf`
    * PCB Pricing, generated by :func:`gen_pcbpricing`

    """
    configfile = conffile.ConfigsFile(projfolder)
    namebase = configfile.pcbname
    if namebase is None:
        try:
            namebase = configfile.rawconfig['cblname']
        except KeyError:
            logger.error("Project does not have a known identifier. "
                         "Skipping : " + projfolder)
            return
    gen_masterdoc(projfolder, namebase, force)
    gen_cobom_csv(projfolder, namebase, force)
    if configfile.is_pcb:
        gen_pcb_pdf(projfolder, force)
        gen_pcb_gbr(projfolder, force)
        gen_pcb_dxf(projfolder, force)
        gen_pcbpricing(projfolder, namebase, force)
Example #11
0
def get_module_config(modulename):
    if modulename not in cards.keys():
        raise KeyError("Couldn't find {0} in the library!".format(modulename))
    gcf = conffile.ConfigsFile(cards[modulename])
    return gcf
Example #12
0
"""
This file is part of tendril
See the COPYING, README, and INSTALL files for more information
"""

import os
import csv

from tendril.entityhub import projects
from tendril.gedaif import conffile

from tendril.utils.config import INSTANCE_ROOT

if __name__ == '__main__':
    fpath = os.path.join(INSTANCE_ROOT, 'scratch', 'costing-summary-all.csv')
    with open(fpath, 'w') as f:
        writer = csv.writer(f)
        writer.writerow(["Card", "Indicative Cost"])
        for card, cardfolder in sorted(projects.cards.iteritems()):
            cfg = conffile.ConfigsFile(cardfolder)
            for configuration in cfg.configdata['configurations']:
                if configuration['configname'] == card:
                    carddesc = configuration['desc']
            if carddesc is None:
                carddesc = ''
            cost = projects.get_card_indicative_cost(card)
            if cost is not None:
                writer.writerow([card, round(cost), carddesc])
            else:
                writer.writerow([card, None, carddesc])
Example #13
0
def get_docs_list(projfolder, cardname=None):
    """
    Returns a list of :class:`docstore.ExposedDocument` instances, pointing to
    the documentation linked to the gEDA project or card specified by the
    parameters.

    If the ``cardname`` is not specified, the documents linked to the base PCB
    only are returned.

    If the ``cardname`` is specified, the documents defining the specific
    configuration only are returned.

    :param projfolder: The gEDA project folder.
    :param cardname: The cardname.
    :return: list of :class:`ExposedDocument`

    """
    configfile = conffile.ConfigsFile(projfolder)
    namebase = configfile.pcbname
    is_cable = False
    if namebase is None:
        try:
            namebase = configfile.rawconfig['cblname']
            is_cable = True
        except KeyError:
            logger.error("Project does not have a known identifier. "
                         "Skipping : " + projfolder)
            return
    project_doc_folder = get_project_doc_folder(projfolder)
    if not project_doc_folder:
        return []
    if not cardname:
        # Get all docs linked to the project
        rval = [
            ExposedDocument(
                'Project Master Doc',
                path.join(project_doc_folder, namebase + '-masterdoc.pdf'),
                refdoc_fs),
            ExposedDocument(
                namebase + ' Schematic (Full)',
                path.join(project_doc_folder, namebase + '-schematic.pdf'),
                refdoc_fs),
            ExposedDocument(
                'Composite Bom (All Configs)',
                path.join(project_doc_folder, 'confdocs', 'conf-boms.csv'),
                refdoc_fs),
        ]
        if is_cable:
            return rval
        gpf = projfile.GedaProjectFile(configfile.projectfolder)
        rval.extend([
            ExposedDocument(
                namebase + ' PCB Layers',
                path.join(project_doc_folder, namebase + '-pcb.pdf'),
                refdoc_fs),
            ExposedDocument(
                namebase + ' PCB Pricing',
                path.join(project_doc_folder, namebase + '-pricing.pdf'),
                refdoc_fs),
            ExposedDocument(
                namebase + ' PCB DXF',
                path.join(project_doc_folder, os.pardir,
                          configfile.pcbname + '.dxf'), refdoc_fs),
            ExposedDocument(
                namebase + ' PCB Gerber',
                path.join(project_doc_folder, os.pardir,
                          gpf.pcbfile + '-gerber.zip'), refdoc_fs),
        ])
        return rval
    else:
        cardname = cardname.strip()
        rval = [
            ExposedDocument(
                cardname + ' Doc',
                path.join(project_doc_folder, 'confdocs', cardname + '.pdf'),
                refdoc_fs),
            ExposedDocument(
                cardname + ' Reference BOM',
                path.join(project_doc_folder, 'confdocs',
                          cardname + '-bom.pdf'), refdoc_fs),
            ExposedDocument(
                cardname + ' Schematic (Configured)',
                path.join(project_doc_folder, 'confdocs',
                          namebase + '-conf-schematic.pdf'), refdoc_fs),
            ExposedDocument(
                cardname + ' Schematic (Full)',
                path.join(project_doc_folder, namebase + '-schematic.pdf'),
                refdoc_fs),
            ExposedDocument(
                'Composite Bom (All Configs)',
                path.join(project_doc_folder, 'confdocs', 'conf-boms.csv'),
                refdoc_fs),
            ExposedDocument(
                'Project Master Doc',
                path.join(project_doc_folder, namebase + '-masterdoc.pdf'),
                refdoc_fs),
        ]
        return rval
Example #14
0
def gen_pcb_gbr(projfolder, force=False):
    """
    Generates gerber files for the PCB provided by the gEDA project, and also
    creates a ``zip`` file of the generated gerbers.

    The pcbfile is the one listed in the gEDA project file, and the
    pcbname is the one specified in the
    :mod:`tendril.gedaif.conffile.ConfigsFile`.

    This function does not use jinja2 and latex. It relies on
    :func:`tendril.gedaif.pcb.conv_pcb2gbr` instead.

    :param projfolder: The gEDA project folder.
    :type projfolder: str
    :param force: Regenerate even if up-to-date.
    :type force: bool
    :return: The output file path.

    .. rubric:: Paths

    * Output Files :  ``<project_doc_folder>/../gerber/*``
    * Output Zip File : ``<project_doc_folder>/../<pcbfile>-gerber.zip``
    * Source Files : The project's `.pcb` file.

    """
    configfile = conffile.ConfigsFile(projfolder)
    gpf = projfile.GedaProjectFile(configfile.projectfolder)
    pcb_mtime = fsutils.get_file_mtime(
        os.path.join(configfile.projectfolder, 'pcb', gpf.pcbfile + '.pcb'))
    if pcb_mtime is None:
        logger.warning("PCB does not seem to exist for : " + projfolder)
        return
    docfolder = get_project_doc_folder(projfolder)
    imgfolder = os.path.join(docfolder, os.pardir, 'img')
    gbrfolder = os.path.join(docfolder, os.pardir, 'gerber')
    outf_mtime = None
    if not refdoc_fs.exists(gbrfolder):
        refdoc_fs.makedir(gbrfolder)
    else:
        outf_mtime = fsutils.get_folder_mtime(gbrfolder, fs=refdoc_fs)
    if not refdoc_fs.exists(imgfolder):
        refdoc_fs.makedir(imgfolder)

    if not force and outf_mtime is not None and outf_mtime > pcb_mtime:
        logger.debug('Skipping up-to-date ' + gbrfolder)
        return gbrfolder

    logger.info('Regenerating ' + gbrfolder + os.linesep + 'Last modified : ' +
                str(pcb_mtime) + '; Last Created : ' + str(outf_mtime))
    rf = refdoc_fs.listdir(gbrfolder, files_only=True, full=True)
    for f in rf:
        refdoc_fs.remove(f)

    workspace_folder = workspace_fs.getsyspath(gbrfolder)
    workspace_fs.makedir(gbrfolder, recursive=True, allow_recreate=True)

    gbrfolder = pcb.conv_pcb2gbr(
        os.path.join(configfile.projectfolder, 'pcb', gpf.pcbfile + '.pcb'),
        workspace_folder)

    workspace_fs.makedir(imgfolder, recursive=True, allow_recreate=True)

    img_workspace_folder = workspace_fs.getsyspath(imgfolder)
    gen_pcb_img(gbrfolder,
                outfolder=img_workspace_folder,
                outfname=gpf.pcbfile,
                force=False)

    for f in os.listdir(img_workspace_folder):
        fpath = os.path.relpath(os.path.join(img_workspace_folder, f),
                                workspace_fs.getsyspath('/'))
        copyfile(workspace_fs, fpath, refdoc_fs, fpath, overwrite=True)

    zfile = os.path.join(workspace_folder, os.pardir,
                         gpf.pcbfile + '-gerber.zip')
    fsutils.zipdir(gbrfolder, zfile)

    for f in os.listdir(workspace_folder):
        fpath = os.path.relpath(os.path.join(workspace_folder, f),
                                workspace_fs.getsyspath('/'))
        copyfile(workspace_fs, fpath, refdoc_fs, fpath, overwrite=True)
    zfpath = os.path.relpath(os.path.join(workspace_folder, zfile),
                             workspace_fs.getsyspath('/'))
    copyfile(workspace_fs, zfpath, refdoc_fs, zfpath, overwrite=True)

    return gbrfolder
Example #15
0
def gen_schpdf(projfolder, namebase, configname=None, force=False):
    """
    Generates a PDF file of all the project schematics listed in the
    gEDA project file. This function does not use jinja2 and latex. It
    relies on :func:`tendril.gedaif.gschem.conv_gsch2pdf` instead.

    :param projfolder: The gEDA project folder.
    :type projfolder: str
    :param namebase: The project name.
    :type namebase: str
    :param force: Regenerate even if up-to-date.
    :type force: bool
    :return: The output file path.

    .. rubric:: Paths

    * Output File :  ``<project_doc_folder>/<namebase>-schematic.pdf``
    * Source Files : The project's schematic folder.

    """
    gpf = projfile.GedaProjectFile(projfolder)
    sch_mtime = fsutils.get_folder_mtime(gpf.schfolder)

    configfile = conffile.ConfigsFile(projfolder)
    docfolder = get_project_doc_folder(projfolder)

    # TODO Consider converting all configurations in one go instead?
    if configname is None:
        schpdfpath = path.join(docfolder, namebase + '-schematic.pdf')
    else:
        docfolder = path.join(docfolder, 'confdocs')
        pdfname = configname + '-conf-schematic.pdf'
        schpdfpath = path.join(docfolder, pdfname)
    outf_mtime = fsutils.get_file_mtime(schpdfpath, fs=refdoc_fs)

    if not force and outf_mtime is not None and outf_mtime > sch_mtime:
        logger.debug('Skipping up-to-date ' + schpdfpath)
        return schpdfpath

    logger.info('Regenerating ' + schpdfpath + os.linesep +
                'Last modified : ' + str(sch_mtime) + '; Last Created : ' +
                str(outf_mtime))

    if configfile.rawconfig is not None:
        workspace_outpath = workspace_fs.getsyspath(schpdfpath)
        workspace_folder = workspace_fs.getsyspath(docfolder)
        workspace_fs.makedir(docfolder, recursive=True, allow_recreate=True)
        pdffiles = []
        obom = None
        if configname is not None:
            tfolder = path.join(docfolder, configname)
            workspace_tfolder = workspace_fs.getsyspath(tfolder)
            workspace_fs.makedir(tfolder, recursive=False, allow_recreate=True)
            bom = boms_electronics.import_pcb(projfolder)
            bom.configure_motifs(configname)
            obom = bom.create_output_bom(configname)
        for schematic in gpf.schfiles:
            schfile = os.path.normpath(projfolder + '/schematic/' + schematic)
            if configname is not None:
                tschfile = path.join(workspace_tfolder, schematic)
                gschem.rewrite_schematic(schfile, obom, gpf, tschfile)
                pdffile = gschem.conv_gsch2pdf(tschfile, workspace_tfolder)
                os.remove(tschfile)
            else:
                pdffile = gschem.conv_gsch2pdf(schfile, workspace_folder)
            pdffiles.append(pdffile)
        pdf.merge_pdf(pdffiles, workspace_outpath)
        for pdffile in pdffiles:
            os.remove(pdffile)
        copyfile(workspace_fs,
                 schpdfpath,
                 refdoc_fs,
                 schpdfpath,
                 overwrite=True)
        return schpdfpath