• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pcb2gcode / pcb2gcode / 19637993896

24 Nov 2025 02:34PM UTC coverage: 59.007% (-15.0%) from 74.006%
19637993896

push

github

web-flow
Merge pull request #730 from mar0x/master

Enable windows build in CI

2199 of 4409 branches covered (49.88%)

Branch coverage included in aggregate %.

1902 of 2541 relevant lines covered (74.85%)

117318.67 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

34.22
/options.cpp
1
/*
2
 * This file is part of pcb2gcode.
3
 *
4
 * Copyright (C) 2009, 2010 Patrick Birnzain <pbirnzain@users.sourceforge.net>
5
 * Copyright (C) 2010 Bernhard Kubicek <kubicek@gmx.at>
6
 * Copyright (C) 2013 Erik Schuster <erik@muenchen-ist-toll.de>
7
 * Copyright (C) 2014-2017 Nicola Corna <nicola@corna.info>
8
 *
9
 * pcb2gcode is free software: you can redistribute it and/or modify
10
 * it under the terms of the GNU General Public License as published by
11
 * the Free Software Foundation, either version 3 of the License, or
12
 * (at your option) any later version.
13
 *
14
 * pcb2gcode is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU General Public License
20
 * along with pcb2gcode.  If not, see <http://www.gnu.org/licenses/>.
21
 */
22

23
#include "options.hpp"
24
#include "config.h"
25

26
#include <fstream>
27
#include <list>
28
#include <boost/exception/all.hpp>
29
#include <boost/algorithm/string.hpp>
30
#include <boost/range/adaptor/reversed.hpp>
31
#include <boost/variant.hpp>
32
#include "units.hpp"
33
#include "available_drills.hpp"
34

35
#include <string>
36
using std::string;
37
using std::to_string;
38

39
#include <iostream>
40
using std::cerr;
41
using std::endl;
42

43
/******************************************************************************/
44
/*
45
 */
46
/******************************************************************************/
47
options& options::instance() {
167✔
48
    static options singleton;
167!
49
    return singleton;
167✔
50
}
51

52
void options::maybe_throw(const std::string& what, ErrorCodes error_code) {
×
53
  if (instance().vm["ignore-warnings"].as<bool>()) {
×
54
    cerr << "Ignoring error code " << error_code << ": " << what << endl;
×
55
  } else {
56
    throw pcb2gcode_parse_exception(what, error_code);
×
57
  }
58
}
×
59

60
/* Adjusts the processed varibles map in place to fix for deprecated variables,
61
 * etc.
62
 */
63
void fix_variables_map(po::variables_map& vm) {
8✔
64
  // Deal with the deprecated milldrill option.
65
  if (vm["min-milldrill-hole-diameter"].defaulted() && vm["milldrill"].as<bool>()) {
32!
66
    vm.at("min-milldrill-hole-diameter").value() = Length(0);
×
67
  }
68
  // Deal with deprecated offset option.
69
  if (vm.count("offset") && vm.at("mill-diameters").defaulted()) {
32!
70
    vm.at("mill-diameters").as<std::vector<CommaSeparated<Length>>>().clear();
14!
71
    vm.at("mill-diameters").as<std::vector<CommaSeparated<Length>>>().push_back({vm["offset"].as<Length>()*2.0});
28!
72
    vm.at("offset").value() = Length(0);
14!
73
  }
74

75
  if (vm["bridgesnum"].as<unsigned int>() > 0 && vm["bridges"].as<Length>().asInch(1) <= 0) {
32!
76
    vm.at("bridgesnum").value() = (unsigned int) 0;
16!
77
  }
78
}
8✔
79

80
/* parse options, both command line and from the millproject file if it exists.
81
 * Throws on error.
82
 */
83
void options::parse(int argc, const char** argv) {
10✔
84
    // guessing causes problems when one option is the start of another
85
    // (--drill, --drill-diameter); see bug 3089930
86
    int style = po::command_line_style::default_style
87
                & ~po::command_line_style::allow_guessing;
88

89
    po::options_description generic;
10✔
90
    generic.add(instance().cli_options).add(instance().cfg_options);
10!
91

92
    try {
93
      po::store(po::parse_command_line(argc, argv, generic, style),
10✔
94
                instance().vm);
10!
95
    } catch (std::logic_error& e) {
2!
96
      throw pcb2gcode_parse_exception(std::string("Error: You've supplied an invalid parameter.\n"
2!
97
                                                  "Details: ")
98
                                      + e.what(), ERR_UNKNOWNPARAMETER);
6!
99
    }
2✔
100

101
    po::notify(instance().vm);
8!
102

103
    if (!instance().vm["noconfigfile"].as<bool>()) {
16!
104
      parse_files(
8!
105
          flatten(
16!
106
              instance().vm["config"].as<std::vector<CommaSeparated<string>>>()),
16!
107
          instance().vm["config"].defaulted());
16!
108
    }
109

110
    if (instance().vm.count("basename")) {
16!
111
      /*
112
       * this needs to be an extra step, as --basename modifies the default
113
       * values of the --...-output parameters
114
       */
115
      string basename = "";
×
116
      basename = instance().vm["basename"].as<string>() + "_";
×
117
      instance().vm.at("front-output").value() = basename + "front.ngc";
×
118
      instance().vm.at("back-output").value() = basename + "back.ngc";
×
119
      instance().vm.at("drill-output").value() = basename + "drill.ngc";
×
120
      instance().vm.at("outline-output").value() = basename + "outline.ngc";
×
121
      instance().vm.at("milldrill-output").value() = basename + "milldrill.ngc";
×
122
    }
123

124
    if (instance().vm.count("tolerance")) {
16!
125
      if (instance().vm.count("g64")) {
×
126
        maybe_throw("You can't specify both tolerance and g64!", ERR_BOTHTOLERANCEG64);
×
127
      }
128
    } else {
129
        const double cfactor = instance().vm["metric"].as<bool>() ? 25.4 : 1;
16!
130
        double tolerance;
131

132
        if (instance().vm.count("g64"))
16!
133
        {
134
            tolerance = instance().vm["g64"].as<double>();
×
135
        }
136
        else {
137
          tolerance = 0.0004 * cfactor;
8✔
138
        }
139

140
        string tolerance_str = "--tolerance=" + to_string(tolerance);
8!
141

142
        const char *fake_tolerance_command_line[] = { "",
8!
143
                                            tolerance_str.c_str() };
8✔
144

145
        po::store(po::parse_command_line(2,
16!
146
                        (char**) fake_tolerance_command_line,
147
                        generic, style), instance().vm);
8!
148
    }
149
    fix_variables_map(instance().vm);
8!
150

151
    po::notify(instance().vm);
8!
152
}
10✔
153

154
/******************************************************************************/
155
/*
156
 */
157
/******************************************************************************/
158
string options::help()
×
159
{
160
    std::stringstream msg;
×
161
    msg << PACKAGE_STRING << "\n\n";
×
162
    msg << instance().cli_options << instance().cfg_options;
×
163
    return msg.str();
×
164
}
×
165

166
/******************************************************************************/
167
/*
168
 */
169
/******************************************************************************/
170
void options::parse_files(const std::vector<std::string>& config_files,
8✔
171
                          const bool defaulted) {
172
  // We do it in serve order so that the latter config file in the
173
  // list overrides the former.
174
  for (const auto& file : boost::adaptors::reverse(config_files)) {
16✔
175
    try {
176
      std::ifstream stream(file);
8!
177
      if (!defaulted && stream.fail()) {
8!
178
        // If the user explicitly specified the config files to use
179
        // then we will throw an error if any of those files are not
180
        // found.
181
        maybe_throw("Missing configuration file \"" + file + "\"",
×
182
                    ERR_INVALIDPARAMETER);
183
      }
184
      po::store(po::parse_config_file(stream, instance().cfg_options),
8!
185
                instance().vm);
8!
186
    } catch (std::exception& e) {
8!
187
      maybe_throw("Error parsing configuration file \"" + file + "\": " +
×
188
                  e.what(), ERR_INVALIDPARAMETER);
×
189
    }
×
190
  }
191
  po::notify(instance().vm);
8✔
192
}
8✔
193

194
/******************************************************************************/
195
/*
196
 */
197
/******************************************************************************/
198
options::options()
1✔
199
         : cli_options("command line only options"), cfg_options("Generic options (CLI and config files)") {
2!
200

201
   cli_options.add_options()
1!
202
       ("noconfigfile", po::value<bool>()->default_value(false)->implicit_value(true), "ignore any configuration file")
1!
203
       ("config",
1!
204
        po::value<std::vector<CommaSeparated<string>>>()->
205
        default_value(std::vector<CommaSeparated<string>>{{"millproject"}})->multitoken(),
4!
206
        "list of comma-separated config files")
207
       ("help,?", "produce help message")
1!
208
       ("version,V", "show the current software version");
1!
209
   po::options_description drilling_options("Drilling options, for making holes in the PCB");
1!
210

211
   drilling_options.add_options()
1!
212
       ("drill", po::value<string>(), "Excellon drill file")
1!
213
       ("milldrill", po::value<bool>()->default_value(false)->implicit_value(true), "[DEPRECATED] Use min-milldrill-hole-diameter=0 instead")
1!
214
       ("milldrill-diameter", po::value<Length>(), "diameter of the end mill used for drilling with --milldrill")
1!
215
       ("min-milldrill-hole-diameter", po::value<Length>()->default_value(Length(std::numeric_limits<double>::infinity())),
1!
216
        "minimum hole width or milldrilling.  Holes smaller than this are drilled.  This implies milldrill")
217
       ("zdrill", po::value<Length>(), "drilling depth")
1!
218
       ("zmilldrill", po::value<Length>(), "milldrilling depth")
1!
219
       ("drill-feed", po::value<Velocity>(), "drill feed in [i/m] or [mm/m]")
1!
220
       ("drill-speed", po::value<Rpm>(), "spindle rpm when drilling")
1!
221
       ("drill-front", po::value<bool>()->implicit_value(true), "[DEPRECATED, use drill-side instead] drill through the front side of board")
1!
222
       ("drill-side", po::value<BoardSide::BoardSide>()->default_value(BoardSide::AUTO),
1!
223
        "drill side; valid choices are front, back or auto (default)")
224
       ("drills-available", po::value<std::vector<AvailableDrills>>()
1!
225
        ->default_value(std::vector<AvailableDrills>{})
1!
226
        ->multitoken(), "list of drills available")
1!
227
       ("onedrill", po::value<bool>()->default_value(false)->implicit_value(true), "use only one drill bit size")
1!
228
       ("drill-output", po::value<string>()->default_value("drill.ngc"), "output file for drilling")
2!
229
       ("nog91-1", po::value<bool>()->default_value(false)->implicit_value(true), "do not explicitly set G91.1 in drill headers")
1!
230
       ("nog81", po::value<bool>()->default_value(false)->implicit_value(true), "replace G81 with G0+G1")
1!
231
       ("nom6", po::value<bool>()->default_value(false)->implicit_value(true), "do not emit M6 on tool changes")
1!
232
       ("milldrill-output", po::value<string>()->default_value("milldrill.ngc"), "output file for milldrilling");
2!
233
   cfg_options.add(drilling_options);
1!
234

235
   po::options_description milling_options("Milling options, for milling traces into the PCB");
1!
236
   milling_options.add_options()
1!
237
       ("front", po::value<string>(),"front side RS274-X .gbr")
1!
238
       ("back", po::value<string>(), "back side RS274-X .gbr")
1!
239
       ("voronoi", po::value<bool>()->default_value(false)->implicit_value(true), "generate voronoi regions")
1!
240
       ("offset", po::value<Length>()->default_value(0), "Note: Prefer to use --mill-diameters and --milling-overlap if you just that's what you mean."
1!
241
        "  An optional offset to add to all traces, useful if the bit has a little slop that you want to keep out of the trace.")
242
       ("mill-diameters", po::value<std::vector<CommaSeparated<Length>>>()->default_value({{Length(0)}})
2!
243
        ->multitoken(), "Diameters of mill bits, used in the order that they are provided.")
3!
244
       ("milling-overlap", po::value<boost::variant<Length, Percent>>()->default_value(parse_unit<Percent>("50%")),
3!
245
        "How much to overlap milling passes, from 0% to 100% or an absolute length")
246
       ("isolation-width", po::value<Length>()->default_value(Length(0)),
2!
247
        "Minimum isolation width between copper surfaces")
248
       ("extra-passes", po::value<int>()->default_value(0), "[DEPRECATED] use --isolation-width instead. "
1!
249
        "Specify the the number of extra isolation passes, increasing the isolation width half the tool diameter with each pass")
250
       ("pre-milling-gcode", po::value<std::vector<string>>()->default_value(std::vector<string>{}, ""),
2!
251
        "custom gcode inserted before the start of milling each trace (used to activate pump or fan or laser connected to fan)")
252
       ("post-milling-gcode", po::value<std::vector<string>>()->default_value(std::vector<string>{}, ""),
2!
253
        "custom gcode inserted after the end of milling each trace (used to deactivate pump or fan or laser connected to fan)")
254
       ("zwork", po::value<Length>(), "milling depth in inches (Z-coordinate while engraving)")
1!
255
       ("mill-feed", po::value<Velocity>(), "feed while isolating in [i/m] or [mm/m]")
1!
256
       ("mill-vertfeed", po::value<Velocity>(), "vertical feed while isolating in [i/m] or [mm/m]")
1!
257
       ("mill-infeed", po::value<Length>(), "maximum milling depth; PCB may be cut in multiple passes")
1!
258
       ("mill-speed", po::value<Rpm>(), "spindle rpm when milling")
1!
259
       ("mill-feed-direction", po::value<MillFeedDirection::MillFeedDirection>()->default_value(MillFeedDirection::ANY),
1!
260
        "In which direction should all milling occur")
261
       ("invert-gerbers", po::value<bool>()->default_value(false)->implicit_value(true),
1!
262
        "Invert polarity of front and back gerbers, causing the milling to occur inside the shapes")
263
       ("draw-gerber-lines", po::value<bool>()->default_value(false)->implicit_value(true),
1!
264
        "Draw lines in the gerber file as just lines and not as filled in shapes")
265
       ("preserve-thermal-reliefs", po::value<bool>()->default_value(true)->implicit_value(true), "generate mill paths for thermal reliefs in voronoi mode")
1!
266
       ("front-output", po::value<string>()->default_value("front.ngc"), "output file for front layer")
2!
267
       ("back-output", po::value<string>()->default_value("back.ngc"), "output file for back layer");
2!
268
   cfg_options.add(milling_options);
1!
269

270
   po::options_description outline_options("Outline options, for cutting the PCB out of the FR4");
1!
271
   outline_options.add_options()
1!
272
       ("outline", po::value<string>(), "pcb outline polygon RS274-X .gbr")
1!
273
       ("fill-outline", po::value<bool>()->default_value(true)->implicit_value(true), "accept a contour instead of a polygon as outline (enabled by default)")
1!
274
       ("cutter-diameter", po::value<Length>(), "diameter of the end mill used for cutting out the PCB")
1!
275
       ("zcut", po::value<Length>(), "PCB cutting depth in inches")
1!
276
       ("cut-feed", po::value<Velocity>(), "PCB cutting feed in [i/m] or [mm/m]")
1!
277
       ("cut-vertfeed", po::value<Velocity>(), "PCB vertical cutting feed in [i/m] or [mm/m]")
1!
278
       ("cut-speed", po::value<Rpm>(), "spindle rpm when cutting")
1!
279
       ("cut-infeed", po::value<Length>(), "maximum cutting depth; PCB may be cut in multiple passes")
1!
280
       ("cut-front", po::value<bool>()->implicit_value(true), "[DEPRECATED, use cut-side instead] cut from front side. ")
1!
281
       ("cut-side", po::value<BoardSide::BoardSide>()->default_value(BoardSide::AUTO), "cut side; valid choices are front, back or auto (default)")
1!
282
       ("bridges", po::value<Length>()->default_value(Length(0)), "add bridges with the given width to the outline cut")
1!
283
       ("bridgesnum", po::value<unsigned int>()->default_value(2), "specify how many bridges should be created")
1!
284
       ("zbridges", po::value<Length>(), "bridges height (Z-coordinates while engraving bridges, default to zsafe) ")
1!
285
       ("outline-output", po::value<string>()->default_value("outline.ngc"), "output file for outline");
2!
286
   cfg_options.add(outline_options);
1!
287

288
   po::options_description optimization_options("Optimization options, for faster PCB creation, smaller output files, and different algorithms.");
1!
289
   optimization_options.add_options()
1!
290
       ("optimise", po::value<Length>()->default_value(parse_unit<Length>("0.0001in"))->implicit_value(parse_unit<Length>("0.0001in")),
2!
291
        "Reduce output file size by up to 40% while accepting a little loss of precision.  Larger values reduce file sizes and processing time even further.  Set to 0 to disable.")
292
       ("eulerian-paths", po::value<bool>()->default_value(true)->implicit_value(true), "Don't mill the same path twice if milling loops overlap.  This can save up to 50% of milling time.  Enabled by default.")
1!
293
       ("vectorial", po::value<bool>()->default_value(true)->implicit_value(true), "enable or disable the vectorial rendering engine")
1!
294
       ("tsp-2opt", po::value<bool>()->default_value(true)->implicit_value(true), "use TSP 2OPT to find a faster toolpath (but slows down gcode generation)")
1!
295
       ("path-finding-limit", po::value<size_t>()->default_value(1), "Use path finding for up to this many steps in the search (more is slower but makes a faster gcode path)")
1!
296
       ("g0-vertical-speed", po::value<Velocity>()->default_value(parse_unit<Velocity>("50in/min")), "speed of vertical G0 movements, for use in path-finding")
2!
297
       ("g0-horizontal-speed", po::value<Velocity>()->default_value(parse_unit<Velocity>("100in/min")), "speed of horizontal G0 movements, for use in path-finding")
2!
298
       ("backtrack", po::value<Velocity>()->default_value(std::numeric_limits<double>::infinity()), "allow retracing a milled path if it's faster than retract-move-lower.  For example, set to 5in/s if you are willing to remill 5 inches of trace in order to save 1 second of milling time.");
1!
299
   cfg_options.add(optimization_options);
1!
300

301
   po::options_description autolevelling_options("Autolevelling options, for generating gcode to automatically probe the board and adjust milling depth to the actual board height");
1!
302
   autolevelling_options.add_options()
1!
303
       ("al-front", po::value<bool>()->default_value(false)->implicit_value(true), "enable the z autoleveller for the front layer")
1!
304
       ("al-back", po::value<bool>()->default_value(false)->implicit_value(true),
1!
305
        "enable the z autoleveller for the back layer")
306
       ("software", po::value<Software::Software>(),
1!
307
        "choose the destination software (useful only with the autoleveller). Supported programs are linuxcnc, mach3, mach4 and custom")
308
       ("al-x", po::value<Length>(), "max x distance between probes")
1!
309
       ("al-y", po::value<Length>(), "max y distance bewteen probes")
1!
310
       ("al-probefeed", po::value<Velocity>(), "speed during the probing")
1!
311
       ("al-probe-on", po::value<string>()->default_value("(MSG, Attach the probe tool)@M0 ( Temporary machine stop. )"),
1!
312
        "execute this commands to enable the probe tool (default is M0)")
313
       ("al-probe-off", po::value<string>()->default_value("(MSG, Detach the probe tool)@M0 ( Temporary machine stop. )"),
2!
314
        "execute this commands to disable the probe tool (default is M0)")
315
       ("al-probecode", po::value<string>()->default_value("G31"), "custom probe code (default is G31)")
2!
316
       ("al-probevar", po::value<unsigned int>()->default_value(2002), "number of the variable where the result of the probing is saved (default is 2002)")
1!
317
       ("al-setzzero", po::value<string>()->default_value("G92 Z0"), "gcode for setting the actual position as zero (default is G92 Z0)");
2!
318
   cfg_options.add(autolevelling_options);
1!
319

320
   po::options_description alignment_options("Alignment options, useful for aligning the milling on opposite sides of the PCB");
1!
321
   alignment_options.add_options()
1!
322
       ("x-offset", po::value<Length>()->default_value(0), "offset the origin in the x-axis by this length")
1!
323
       ("y-offset", po::value<Length>()->default_value(0), "offset the origin in the y-axis by this length")
1!
324
       ("zero-start", po::value<bool>()->default_value(false)->implicit_value(true), "set the starting point of the project at (0,0)")
1!
325
       ("mirror-absolute", po::value<bool>()->default_value(true)->implicit_value(true),
1!
326
        "[DEPRECATED, must always be true] mirror back side along absolute zero instead of board center")
327
       ("mirror-axis", po::value<Length>()->default_value(Length(0)), "For two-sided boards, the PCB needs to be flipped along the axis x=VALUE")
1!
328
       ("mirror-yaxis", po::value<bool>()->default_value(false), "For two-sided boards, the PCB needs to be flipped along the y axis instead");
1!
329
   cfg_options.add(alignment_options);
1!
330

331
   po::options_description cnc_options("CNC options, common to all the milling, drilling, and cutting");
1!
332
   cnc_options.add_options()
1!
333
       ("zsafe", po::value<Length>(), "safety height (Z-coordinate during rapid moves)")
1!
334
       ("spinup-time", po::value<Time>()->default_value(parse_unit<Time>("1 ms")), "time required to the spindle to reach the correct speed")
1!
335
       ("spindown-time", po::value<Time>(), "time required to the spindle to return to 0 rpm")
1!
336
       ("zchange", po::value<Length>(), "tool changing height")
1!
337
       ("zchange-absolute", po::value<bool>()->default_value(false)->implicit_value(true), "use zchange as a machine coordinates height (G53)")
1!
338
       ("tile-x", po::value<int>()->default_value(1), "number of tiling columns. Default value is 1")
1!
339
       ("tile-y", po::value<int>()->default_value(1), "number of tiling rows. Default value is 1");
1!
340
   cfg_options.add(cnc_options);
1!
341

342
   cfg_options.add_options()
1!
343
       ("ignore-warnings", po::value<bool>()->default_value(false)->implicit_value(true), "Ignore warnings")
1!
344
       ("svg", po::value<string>(), "[DEPRECATED] use --vectorial, SVGs will be generated automatically; this option has no effect")
1!
345
       ("metric", po::value<bool>()->default_value(false)->implicit_value(true), "use metric units for parameters. does not affect gcode output")
1!
346
       ("metricoutput", po::value<bool>()->default_value(false)->implicit_value(true), "use metric units for output")
1!
347
       ("g64", po::value<double>(), "[DEPRECATED, use tolerance instead] maximum deviation from toolpath, overrides internal calculation")
1!
348
       ("tolerance", po::value<double>(), "maximum toolpath tolerance")
1!
349
       ("nog64", po::value<bool>()->default_value(false)->implicit_value(true), "do not set an explicit g64")
1!
350
       ("output-dir", po::value<string>()->default_value(""), "output directory")
1!
351
       ("basename", po::value<string>(), "prefix for default output file names")
1!
352
       ("preamble-text", po::value<string>(), "preamble text file, inserted at the very beginning as a comment.")
1!
353
       ("preamble", po::value<string>(), "gcode preamble file, inserted at the very beginning.")
1!
354
       ("postamble", po::value<string>(), "gcode postamble file, inserted before M9 and M2.")
1!
355
       ("no-export", po::value<bool>()->default_value(false)->implicit_value(true), "skip the exporting process");
1!
356
}
7!
357

358
/******************************************************************************/
359
/*
360
 */
361
/******************************************************************************/
362
static void check_generic_parameters(po::variables_map const& vm)
×
363
{
364
    double unit;      //factor for imperial/metric conversion
365

366
    unit = vm["metric"].as<bool>() ? (1. / 25.4) : 1;
×
367

368
    //---------------------------------------------------------------------------
369
    //Check spinup(down)-time parameters:
370

371
    if (vm["spinup-time"].as<Time>().asMillisecond(1) < 0) {
×
372
      options::maybe_throw("spinup-time can't be negative!", ERR_NEGATIVESPINUP);
×
373
    }
374

375
    if (vm.count("spindown-time") && vm["spindown-time"].as<Time>().asMillisecond(1) < 0) {
×
376
      options::maybe_throw("spindown-time can't be negative!", ERR_NEGATIVESPINDOWN);
×
377
    }
378

379
    //---------------------------------------------------------------------------
380
    //Check g64 parameter:
381

382
    if (vm.count("g64"))
×
383
    {
384
        cerr << "g64 is deprecated, use tolerance.\n";
×
385
    }
386

387
    //---------------------------------------------------------------------------
388
    //Check mirror-absolute parameter:
389

390
    if (!vm["mirror-absolute"].as<bool>()) {
×
391
      options::maybe_throw("mirror-absolute is deprecated, it must be true.", ERR_FALSEMIRRORABSOLUTE);
×
392
    }
393

394
    //---------------------------------------------------------------------------
395
    //Check tolerance parameter:
396

397
    //Upper threshold value of tolerance parameter for warning
398
    double tolerance_th = vm["metric"].as<bool>() ? 0.2 : 0.008;
×
399

400
    if (vm.count("tolerance"))              //tolerance parameter is given
×
401
    {
402
        if (vm["tolerance"].as<double>() > tolerance_th)
×
403
            cerr << "Warning: high tolerance value (allowed deviation from toolpath) given.\n"
×
404
                 << endl;
405

406
        else if (vm["tolerance"].as<double>() == 0)
×
407
            cerr << "Warning: Deviation from commanded toolpath set to 0 (tolerance=0). No smooth milling is most likely!\n"
×
408
                 << endl;
409

410
        else if (vm["tolerance"].as<double>() < 0) {
×
411
          options::maybe_throw("tolerance can't be negative!", ERR_NEGATIVETOLERANCE);
×
412
        }
413
    }
414

415
    //---------------------------------------------------------------------------
416
    //Check svg parameter:
417

418
    if (vm.count("svg")) {
×
419
      cerr << "--svg is deprecated and has no effect anymore, use --vectorial to generate SVGs.\n";
×
420
    }
421

422
    //---------------------------------------------------------------------------
423
    //Check for available board dimensions:
424

425
    if (vm.count("drill")
×
426
            && !(vm.count("front") || vm.count("back") || vm.count("outline")))
×
427
    {
428
        cerr << "Warning: Board dimensions unknown. Gcode for drilling will be probably misaligned.\n";
×
429
    }
430

431
    //---------------------------------------------------------------------------
432
    //Check for tile parameters
433

434
    if (vm["tile-x"].as<int>() < 1) {
×
435
      options::maybe_throw("tile-x can't be negative!", ERR_NEGATIVETILEX);
×
436
    }
437

438
    if (vm["tile-y"].as<int>() < 1) {
×
439
      options::maybe_throw("tile-y can't be negative!", ERR_NEGATIVETILEY);
×
440
    }
441

442
    //---------------------------------------------------------------------------
443
    //Check for safety height parameter:
444

445
    if (!vm.count("zsafe")) {
×
446
      options::maybe_throw("Error: Safety height not specified.", ERR_NOZSAFE);
×
447
    }
448

449
    //---------------------------------------------------------------------------
450
    //Check for zchange parameter parameter:
451

452
    if (!vm.count("zchange")) {
×
453
      options::maybe_throw("Error: Tool changing height not specified.", ERR_NOZCHANGE);
×
454
    }
455

456
    //---------------------------------------------------------------------------
457
    //Check for autoleveller parameters
458

459
    if (vm["al-front"].as<bool>() || vm["al-back"].as<bool>())
×
460
    {
461
        if (!vm.count("software")) {
×
462
          options::maybe_throw("Error: unspecified or unsupported software, please specify a supported software (linuxcnc, mach3, mach4 or custom).", ERR_NOSOFTWARE);
×
463
        }
464

465
        if (!vm.count("al-x")) {
×
466
          options::maybe_throw("Error: autoleveller probe width x not specified.", ERR_NOALX);
×
467
        } else if (vm["al-x"].as<Length>().asInch(unit) <= 0) {
×
468
          options::maybe_throw("Error: al-x < 0!", ERR_NEGATIVEALX);
×
469
        }
470

471
        if (!vm.count("al-y")) {
×
472
          options::maybe_throw("Error: autoleveller probe width y not specified.", ERR_NOALY);
×
473
        } else if (vm["al-y"].as<Length>().asInch(unit) <= 0) {
×
474
          options::maybe_throw("Error: al-y < 0!", ERR_NEGATIVEALY);
×
475
        }
476

477
        if (!vm.count("al-probefeed")) {
×
478
          options::maybe_throw("Error: autoleveller probe feed rate not specified.", ERR_NOALPROBEFEED);
×
479
        } else if (vm["al-probefeed"].as<Velocity>().asInchPerMinute(unit) <= 0) {
×
480
          options::maybe_throw("Error: al-probefeed < 0!", ERR_NEGATIVEPROBEFEED);
×
481
        }
482
    }
483
    if (vm["mill-feed-direction"].as<MillFeedDirection::MillFeedDirection>() != MillFeedDirection::ANY &&
×
484
        vm["tsp-2opt"].as<bool>()) {
×
485
      options::maybe_throw("Error: Can't use tsp-2opt together with mill-feed-direction", ERR_INVALIDPARAMETER);
×
486
    }
487
}
×
488

489
/******************************************************************************/
490
/*
491
 */
492
/******************************************************************************/
493
static void check_milling_parameters(po::variables_map const& vm)
×
494
{
495

496
    double unit;      //factor for imperial/metric conversion
497

498
    unit = vm["metric"].as<bool>() ? (1. / 25.4) : 1;
×
499

500
    if (vm.count("front") || vm.count("back"))
×
501
    {
502

503
      if (!vm.count("zwork")) {
×
504
        options::maybe_throw("Error: --zwork not specified.", ERR_NOZWORK);
×
505
      } else if (vm["zwork"].as<Length>().asDouble() > 0){
×
506
        cerr << "Warning: Engraving depth (--zwork) is greater than zero!\n";
×
507
      }
508

509
      if (!vm["vectorial"].as<bool>()) {
×
510
        options::maybe_throw("Error: --vectorial is mandatory", ERR_INVALIDPARAMETER);
×
511
      }
512
      if (!vm.count("mill-diameters")) {
×
513
        options::maybe_throw("Error: no --mill-diameters specified.", ERR_NOOFFSET);
×
514
      }
515

516
      if (!vm.count("mill-feed")) {
×
517
        options::maybe_throw("Error: Milling feed [i/m or mm/m] not specified.", ERR_NOMILLFEED);
×
518
      }
519

520
      if (!vm.count("mill-speed")) {
×
521
        options::maybe_throw("Error: Milling speed [rpm] not specified.", ERR_NOMILLSPEED);
×
522
      }
523

524
        // required parameters present. check for validity.
525
      if (vm["zsafe"].as<Length>().asInch(unit) <= vm["zwork"].as<Length>().asInch(unit)) {
×
526
        options::maybe_throw("Error: The safety height --zsafe is lower than the milling "
×
527
                             "height --zwork. Are you sure this is correct?", ERR_ZSAFELOWERZWORK);
528
      }
529

530
      if (vm["mill-feed"].as<Velocity>().asDouble() <= 0) {
×
531
        options::maybe_throw("Error: Negative or equal to 0 milling feed (--mill-feed).", ERR_NEGATIVEMILLFEED);
×
532
      }
533

534
      if (vm.count("mill-vertfeed") && vm["mill-vertfeed"].as<Velocity>().asInchPerMinute(unit) <= 0) {
×
535
        options::maybe_throw("Error: Negative or equal to 0 vertical milling feed (--mill-vertfeed).", ERR_NEGATIVEMILLVERTFEED);
×
536
      }
537

538
      if (vm.count("mill-infeed")>0 && vm["mill-infeed"].as<Length>().asInch(unit) <= 0.0) {
×
539
        options::maybe_throw("Error: The milling infeed --mill-infeed. seems too low.", ERR_LOWMILLINFEED);
×
540
      }
541

542
      if (vm["mill-speed"].as<Rpm>().asDouble() < 0) {
×
543
        options::maybe_throw("Error: --mill-speed < 0.", ERR_NEGATIVEMILLSPEED);
×
544
      }
545
    }
546
}
×
547

548
/******************************************************************************/
549
/*
550
 */
551
/******************************************************************************/
552
static void check_drilling_parameters(po::variables_map const& vm)
×
553
{
554

555
    double unit;      //factor for imperial/metric conversion
556

557
    unit = vm["metric"].as<bool>() ? (1. / 25.4) : 1;
×
558

559
    //only check the parameters if a drill file is given
560
    if (vm.count("drill"))
×
561
    {
562

563
        if (!vm.count("zdrill")) {
×
564
          options::maybe_throw("Error: Drilling depth (--zdrill) not specified.\n", ERR_NOZDRILL);
×
565
        }
566

567
        if (vm["zsafe"].as<Length>().asInch(unit) <= vm["zdrill"].as<Length>().asInch(unit)) {
×
568
          options::maybe_throw("Error: The safety height --zsafe is lower than the drilling "
×
569
                               "height --zdrill!\n", ERR_ZSAFELOWERZDRILL);
570
        }
571

572
        if (!vm.count("zchange")) {
×
573
          options::maybe_throw("Error: Drill bit changing height (--zchange) not specified.", ERR_NOZCHANGE);
×
574
        } else if (!vm["zchange-absolute"].as<bool>() && vm["zchange"].as<Length>().asInch(unit) <= vm["zdrill"].as<Length>().asInch(unit)) {
×
575
          options::maybe_throw("Error: The safety height --zsafe is lower than the tool "
×
576
                               "change height --zchange!", ERR_ZSAFELOWERZCHANGE);
577
        }
578

579
        if (!vm.count("drill-feed")) {
×
580
          options::maybe_throw("Error:: Drilling feed (--drill-feed) not specified.", ERR_NODRILLFEED);
×
581
        } else if (vm["drill-feed"].as<Velocity>().asInchPerMinute(unit) <= 0) {
×
582
          options::maybe_throw("Error: The drilling feed --drill-feed is <= 0.", ERR_NEGATIVEDRILLFEED);
×
583
        }
584

585
        if (!vm.count("drill-speed")) {
×
586
          options::maybe_throw("Error: Drilling spindle RPM (--drill-speed) not specified.", ERR_NODRILLSPEED);
×
587
        } else if (vm["drill-speed"].as<Rpm>().asRpm(1) < 0) {        //no need to support both directions?
×
588
          options::maybe_throw("Error: --drill-speed < 0.", ERR_NEGATIVEDRILLSPEED);
×
589
        }
590

591
        if (vm.count("drill-front")) {
×
592
          cerr << "drill-front is deprecated, use drill-side.\n";
×
593

594
          if (!vm["drill-side"].defaulted()) {
×
595
            options::maybe_throw("You can't specify both drill-front and drill-side!", ERR_BOTHDRILLFRONTSIDE);
×
596
          }
597
        }
598
    }
599

600
}
×
601

602
/******************************************************************************/
603
/*
604
 */
605
/******************************************************************************/
606
static void check_cutting_parameters(po::variables_map const& vm) {
×
607

608
  double unit;      //factor for imperial/metric conversion
609

610
  unit = vm["metric"].as<bool>() ? (1. / 25.4) : 1;
×
611

612
  //only check the parameters if an outline file is given or milldrill is enabled
613
  if (vm.count("outline") ||
×
614
      (vm.count("drill") &&
×
615
       (vm["min-milldrill-hole-diameter"].as<Length>() < Length(std::numeric_limits<double>::infinity())))) {
×
616
    if (!vm.count("zcut")) {
×
617
      options::maybe_throw("Error: Board cutting depth (--zcut) not specified.", ERR_NOZCUT);
×
618
    } else if (vm["zcut"].as<Length>().asInch(unit) > 0) {
×
619
      options::maybe_throw("Error: Cutting depth (--zcut) is greater than zero!", ERR_NEGATIVEZWORK);
×
620
    }
621

622
    if (!vm.count("cutter-diameter")) {
×
623
      options::maybe_throw("Error: Cutter diameter not specified.", ERR_NOCUTTERDIAMETER);
×
624
    }
625

626
    if (!vm.count("cut-feed")) {
×
627
      options::maybe_throw("Error: Board cutting feed (--cut-feed) not specified.", ERR_NOCUTFEED);
×
628
    }
629

630
    if (!vm.count("cut-speed")) {
×
631
      options::maybe_throw("Error: Board cutting spindle RPM (--cut-speed) not specified.", ERR_NOCUTSPEED);
×
632
    }
633

634
    if (!vm.count("cut-infeed")) {
×
635
      options::maybe_throw("Error: Board cutting infeed (--cut-infeed) not specified.", ERR_NOCUTINFEED);
×
636
    }
637

638
    if (vm["zsafe"].as<Length>().asInch(unit) <= vm["zcut"].as<Length>().asInch(unit)) {
×
639
      options::maybe_throw("Error: The safety height --zsafe is lower than the cutting "
×
640
                           "height --zcut!", ERR_ZSAFELOWERZCUT);
641
    }
642

643
    if (vm["cut-feed"].as<Velocity>().asInchPerMinute(unit) <= 0) {
×
644
      options::maybe_throw("Error: The cutting feed --cut-feed is <= 0.", ERR_NEGATIVECUTFEED);
×
645
    }
646

647
    if (vm.count("cut-vertfeed") && vm["cut-vertfeed"].as<Velocity>().asInchPerMinute(unit) <= 0) {
×
648
      options::maybe_throw("Error: The cutting vertical feed --cut-vertfeed is <= 0.", ERR_NEGATIVECUTVERTFEED);
×
649
    }
650

651
    if (vm["cut-speed"].as<Rpm>().asRpm(1) < 0) {        //no need to support both directions?
×
652
      options::maybe_throw("Error: The cutting spindle speed --cut-speed is lower than 0.", ERR_NEGATIVESPINDLESPEED);
×
653
    }
654

655
    if (vm["cut-infeed"].as<Length>().asInch(unit) < 0.001) {
×
656
      options::maybe_throw("Error: The cutting infeed --cut-infeed. seems too low.", ERR_LOWCUTINFEED);
×
657
    }
658

659
    if (vm["bridges"].as<Length>().asInch(unit) < 0) {
×
660
      options::maybe_throw("Error: negative bridge value.", ERR_NEGATIVEBRIDGE);
×
661
    }
662

663
    if (vm.count("cut-front")) {
×
664
      cerr << "cut-front is deprecated, use cut-side.\n";
×
665
      if (!vm["cut-side"].defaulted()) {
×
666
        options::maybe_throw("You can't specify both cut-front and cut-side!\n", ERR_BOTHCUTFRONTSIDE);
×
667
      }
668
    }
669
  }
670
}
×
671

672
/******************************************************************************/
673
/*
674
 */
675
/******************************************************************************/
676
void options::check_parameters()
×
677
{
678
  po::variables_map const& vm = instance().vm;
×
679

680
  try {
681
    check_generic_parameters(vm);
×
682
    check_milling_parameters(vm);
×
683
    check_cutting_parameters(vm);
×
684
    check_drilling_parameters(vm);
×
685
  } catch (std::runtime_error& re) {
×
686
    maybe_throw("Error: Invalid parameter. :-(", ERR_INVALIDPARAMETER);
×
687
  }
×
688
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc