1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
|
/*
* This file is part of pcb2gcode.
*
* Copyright (C) 2009, 2010 Patrick Birnzain <pbirnzain@users.sourceforge.net>
*
* pcb2gcode is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* pcb2gcode is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with pcb2gcode. If not, see <http://www.gnu.org/licenses/>.
*/
#include "options.hpp"
#include "config.h"
#include <fstream>
#include <list>
#include <boost/foreach.hpp>
#include <boost/exception/all.hpp>
#include <iostream>
using std::cerr;
using std::endl;
options&
options::instance()
{
static options singleton;
return singleton;
}
void
options::parse( int argc, char** argv )
{
// guessing causes problems when one option is the start of another
// (--drill, --drill-diameter); see bug 3089930
int style = po::command_line_style::default_style & ~po::command_line_style::allow_guessing;
po::options_description generic;
generic.add(instance().cli_options).add(instance().cfg_options);
try {
po::store(po::parse_command_line(argc, argv, generic, style), instance().vm);
}
catch( std::logic_error& e ) {
cerr << "Error: You've supplied an invalid parameter.\n"
<< "Note that spindle speeds are integers!\n"
<< "Details: " << e.what() << endl;
exit(101);
}
po::notify( instance().vm );
parse_files();
/*
* this needs to be an extra step, as --basename modifies the default
* values of the --...-output parameters
*/
string basename="";
if( instance().vm.count("basename"))
{
basename = instance().vm["basename"].as<string>()+"_";
}
string front_output="--front-output="+basename+"front.ngc";
string back_output="--back-output="+basename+"back.ngc";
string outline_output="--outline-output="+basename+"outline.ngc";
string drill_output="--drill-output="+basename+"drill.ngc";
const char *fake_basename_command_line[] = {
"",
front_output.c_str(),
back_output.c_str(),
outline_output.c_str(),
drill_output.c_str()
};
po::store(po::parse_command_line(5, (char**)fake_basename_command_line, generic, style), instance().vm);
po::notify(instance().vm);
}
string
options::help()
{
std::stringstream msg;
msg << PACKAGE_STRING << "\n\n";
msg << instance().cli_options << instance().cfg_options;
return msg.str();
}
void
options::parse_files()
{
std::string file("millproject");
try {
std::ifstream stream;
try {
stream.open(file.c_str());
po::store( po::parse_config_file( stream, instance().cfg_options), instance().vm);
} catch( std::exception& e ) {
cerr << "Error parsing configuration file \"" << file << "\": "
<< e.what() << endl;
}
stream.close();
}
catch( std::exception& e ) {
cerr << "Error reading configuration file \"" << file << "\": " << e.what() << endl;
}
po::notify( instance().vm );
}
options::options() : cli_options("command line only options"),
cfg_options("generic options (CLI and config files)")
{
cli_options.add_options()
("help,?", "produce help message")
("version", "\n")
;
cfg_options.add_options()
("front", po::value<string>(), "front side RS274-X .gbr")
("back", po::value<string>(), "back side RS274-X .gbr")
("outline", po::value<string>(), "pcb outline polygon RS274-X .gbr")
("drill", po::value<string>(), "Excellon drill file\n")
("svg", po::value<string>(), "SVG output file. EXPERIMENTAL\n")
("zwork", po::value<double>(), "milling depth in inches (Z-coordinate while engraving)")
("zsafe", po::value<double>(), "safety height (Z-coordinate during rapid moves)")
("offset", po::value<double>(), "distance between the PCB traces and the end mill path in inches; usually half the isolation width")
("mill-feed", po::value<double>(), "feed while isolating in ipm")
("mill-speed", po::value<int>(), "spindle rpm when milling")
("milldrill", "drill using the mill head")
("extra-passes", po::value<int>(), "specify the the number of extra isolation passes, increasing the isolation width half the tool diameter with each pass\n")
("fill-outline", po::value<bool>()->zero_tokens(), "accept a contour instead of a polygon as outline")
("outline-width", po::value<double>(), "width of the outline")
("cutter-diameter", po::value<double>(), "diameter of the end mill used for cutting out the PCB")
("zcut", po::value<double>(), "PCB cutting depth in inches.")
("cut-feed", po::value<double>(), "PCB cutting feed")
("cut-speed", po::value<int>(), "PCB cutting spindle speed")
("cut-infeed", po::value<double>(), "Maximum cutting depth; PCB may be cut in multiple passes\n")
("zdrill", po::value<double>(), "drill depth")
("zchange", po::value<double>(), "tool changing height")
("drill-feed", po::value<double>(), "drill feed; ipm")
("drill-speed", po::value<int>(), "spindle rpm when drilling")
("drill-front", po::value<bool>()->zero_tokens(), "drill through the front side of board\n")
("metric", "use metric units for parameters. does not affect gcode output")
("dpi", po::value<int>()->default_value(1000), "virtual photoplot resolution")
("mirror-absolute", po::value<bool>()->zero_tokens(), "mirror back side along absolute zero instead of board center\n")
("basename", po::value<string>(), "prefix for default output file names")
("front-output", po::value<string>()->default_value("front.ngc"), "output file for front layer")
("back-output", po::value<string>()->default_value("back.ngc"), "output file for back layer")
("outline-output", po::value<string>()->default_value("outline.ngc"), "output file for outline")
("drill-output", po::value<string>()->default_value("drill.ngc"), "output file for drilling\n")
("preamble", po::value<string>(), "gcode preamble file")
("postamble", po::value<string>(), "gcode postamble file")
;
}
static void check_generic_parameters( po::variables_map const& vm )
{
int dpi = vm["dpi"].as<int>();
if( dpi < 100 ) cerr << "Warning: very low DPI value." << endl;
if( dpi > 10000 ) cerr << "Warning: very high DPI value, processing may take extremely long" << endl;
if( !vm.count("zsafe") ) {
cerr << "Error: Safety height not specified.\n";
exit(5);
}
if( !vm.count("zchange") ) {
cerr << "Error: Tool changing height not specified.\n";
exit(15);
}
}
static void check_milling_parameters( po::variables_map const& vm )
{
if(vm.count("front") || vm.count("back")) {
if( !vm.count("zwork") ) {
cerr << "Error: --zwork not specified.\n";
exit(1);
} else if( vm["zwork"].as<double>() > 0 ) {
cerr << "Warning: Engraving depth (--zwork) is greater than zero!\n";
}
if( !vm.count("offset") ) {
cerr << "Error: Etching --offset not specified.\n";
exit(4);
}
if( !vm.count("mill-feed") ) {
cerr << "Error: Milling feed [ipm] not specified.\n";
exit(13);
}
if( !vm.count("mill-speed") ) {
cerr << "Error: Milling speed [rpm] not specified.\n";
exit(14);
}
// required parameters present. check for validity.
if( vm["zsafe"].as<double>() <= vm["zwork"].as<double>() ) {
cerr << "Error: The safety height --zsafe is lower than the milling "
<< "height --zwork. Are you sure this is correct?\n";
exit(15);
}
if( vm["mill-feed"].as<double>() < 0 ) {
cerr << "Error: Negative milling feed (--mill-feed).\n";
exit(17);
}
if( vm["mill-speed"].as<int>() < 0 ) {
cerr << "Error: --mill-speed < 0.\n";
exit(16);
}
}
}
static void check_drilling_parameters( po::variables_map const& vm )
{
if( vm.count("drill") ) {
if( !vm.count("zdrill") ) {
cerr << "Error: Drilling depth (--zdrill) not specified.\n";
exit(9);
}
if( !vm.count("zchange") ) {
cerr << "Error: Drill bit changing height (--zchange) not specified.\n";
exit(10);
}
if( !vm.count("drill-feed") ) {
cerr << "Error:: Drilling feed (--drill-feed) not specified.\n";
exit(11);
}
if( !vm.count("drill-speed") ) {
cerr << "Error: Drilling spindle RPM (--drill-speed) not specified.\n";
exit(12);
}
if( vm["zsafe"].as<double>() <= vm["zdrill"].as<double>() ) {
cerr << "Error: The safety height --zsafe is lower than the drilling "
<< "height --zdrill!\n";
exit(18);
}
if( vm["zchange"].as<double>() <= vm["zdrill"].as<double>() ) {
cerr << "Error: The safety height --zsafe is lower than the tool "
<< "change height --zchange!\n";
exit(19);
}
if( vm["drill-feed"].as<double>() <= 0 ) {
cerr << "Error: The drilling feed --drill-feed is <= 0.\n";
exit(20);
}
if( vm["drill-speed"].as<int>() < 0 ) {
cerr << "Error: --drill-speed < 0.\n";
exit(17);
}
}
}
static void check_cutting_parameters( po::variables_map const& vm )
{
if( vm.count("outline") || (vm.count("drill") && vm.count("milldrill"))) {
if( vm.count("fill-outline") ) {
if(!vm.count("outline-width")) {
cerr << "Error: For outline filling, a width (--outline-width) has to be specified.\n";
exit(25);
} else {
double outline_width = vm["outline-width"].as<double>();
if( outline_width < 0 ) {
cerr << "Error: Specified outline width is less than zero!\n";
exit(26);
} else if( outline_width == 0 ) {
cerr << "Error. Specified outline width is zero!\n";
exit(27);
} else {
std::stringstream width_sb;
if( (vm.count("metric") && outline_width >= 10)
|| (!vm.count("metric") && outline_width >= 0.4) ) {
width_sb << outline_width << (vm.count("metric") ? " mm" : " inch");
cerr << "Warning: You specified an outline-width of " << width_sb.str() << "!\n";
}
}
}
}
if( !vm.count("zcut") ) {
cerr << "Error: Board cutting depth (--zcut) not specified.\n";
exit(5);
}
if( !vm.count("cutter-diameter") ) {
cerr << "Error: Cutter diameter not specified.\n";
exit(15);
}
if( !vm.count("cut-feed") ) {
cerr << "Error: Board cutting feed (--cut-feed) not specified.\n";
exit(6);
}
if( !vm.count("cut-speed") ) {
cerr << "Error: Board cutting spindle RPM (--cut-speed) not specified.\n";
exit(7);
}
if( !vm.count("cut-infeed") ) {
cerr << "Error: Board cutting infeed (--cut-infeed) not specified.\n";
exit(8);
}
if( vm["zsafe"].as<double>() <= vm["zcut"].as<double>() ) {
cerr << "Error: The safety height --zsafe is lower than the cutting "
<< "height --zcut!\n";
exit(21);
}
if( vm["cut-feed"].as<double>() <= 0 ) {
cerr << "Error: The cutting feed --cut-feed is <= 0.\n";
exit(22);
}
if( vm["cut-speed"].as<int>() < 0 ) {
cerr << "Error: The cutting spindle speed --cut-speed is smaler than 0.\n";
exit(23);
}
if( vm["cut-infeed"].as<double>() < 0.001 ) {
cerr << "Error: Too small cutting infeed --cut-infeed.\n";
exit(24);
}
}
}
void options::check_parameters()
{
po::variables_map const& vm = instance().vm;
try {
check_generic_parameters( vm );
check_milling_parameters( vm );
check_cutting_parameters( vm );
check_drilling_parameters( vm );
} catch ( std::runtime_error& re ) {
cerr << "Error: Invalid parameter. :-(\n";
exit(100);
}
}
|