Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
M
MarlinKimbra
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
machinery
MarlinKimbra
Commits
e7efd3aa
Commit
e7efd3aa
authored
May 16, 2015
by
MagoKimbra
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix
parent
caf65063
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
25 additions
and
211 deletions
+25
-211
Configuration_Delta.h
MarlinKimbra/Configuration_Delta.h
+6
-3
Marlin_main.cpp
MarlinKimbra/Marlin_main.cpp
+19
-31
motion_control.cpp
MarlinKimbra/motion_control.cpp
+0
-145
motion_control.h
MarlinKimbra/motion_control.h
+0
-32
No files found.
MarlinKimbra/Configuration_Delta.h
View file @
e7efd3aa
...
@@ -29,6 +29,9 @@
...
@@ -29,6 +29,9 @@
//Uncomment to enable autocalibration debug messages
//Uncomment to enable autocalibration debug messages
#define DEBUG_MESSAGES
#define DEBUG_MESSAGES
//Amount to lift head after probing a point
#define AUTOCAL_PROBELIFT 3 // mm
// Precision for G30 delta autocalibration function
// Precision for G30 delta autocalibration function
#define AUTOCALIBRATION_PRECISION 0.1 // mm
#define AUTOCALIBRATION_PRECISION 0.1 // mm
...
@@ -80,9 +83,9 @@
...
@@ -80,9 +83,9 @@
// ENDSTOP SETTINGS:
// ENDSTOP SETTINGS:
// Sets direction of endstop when homing; 1=MAX, -1=MIN
// Sets direction of endstop when homing; 1=MAX, -1=MIN
#define X_HOME_DIR 1 //DELTA MUST HAVE MAX ENDSTOP
#define X_HOME_DIR 1 //
DELTA MUST HAVE MAX ENDSTOP
#define Y_HOME_DIR 1 //DELTA MUST HAVE MAX ENDSTOP
#define Y_HOME_DIR 1 //
DELTA MUST HAVE MAX ENDSTOP
#define Z_HOME_DIR 1 //DELTA MUST HAVE MAX ENDSTOP
#define Z_HOME_DIR 1 //
DELTA MUST HAVE MAX ENDSTOP
#define min_software_endstops true // If true, axis won't move to coordinates less than HOME_POS.
#define min_software_endstops true // If true, axis won't move to coordinates less than HOME_POS.
#define max_software_endstops true // If true, axis won't move to coordinates greater than the defined lengths below.
#define max_software_endstops true // If true, axis won't move to coordinates greater than the defined lengths below.
...
...
MarlinKimbra/Marlin_main.cpp
View file @
e7efd3aa
...
@@ -376,7 +376,7 @@ bool target_direction;
...
@@ -376,7 +376,7 @@ bool target_direction;
};
};
static
float
z_offset
;
static
float
z_offset
;
static
float
bed_level_x
,
bed_level_y
,
bed_level_z
;
static
float
bed_level_x
,
bed_level_y
,
bed_level_z
;
static
float
bed_level_c
=
20
;
//used for inital bed probe safe distance (to avoid crashing into bed)
static
float
bed_level_c
=
20
;
//
used for inital bed probe safe distance (to avoid crashing into bed)
static
float
bed_level_ox
,
bed_level_oy
,
bed_level_oz
;
static
float
bed_level_ox
,
bed_level_oy
,
bed_level_oz
;
static
int
loopcount
;
static
int
loopcount
;
static
bool
home_all_axis
=
true
;
static
bool
home_all_axis
=
true
;
...
@@ -1635,9 +1635,9 @@ static void setup_for_endstop_move() {
...
@@ -1635,9 +1635,9 @@ static void setup_for_endstop_move() {
float
z_probe
()
{
float
z_probe
()
{
enable_endstops
(
true
);
enable_endstops
(
true
);
//
feedrate = homing_feedrate[X_AXIS];
feedrate
=
homing_feedrate
[
X_AXIS
];
//
prepare_move_raw();
prepare_move_raw
();
//
st_synchronize();
st_synchronize
();
float
start_z
=
current_position
[
Z_AXIS
];
float
start_z
=
current_position
[
Z_AXIS
];
long
start_steps
=
st_get_position
(
Z_AXIS
);
long
start_steps
=
st_get_position
(
Z_AXIS
);
...
@@ -1672,13 +1672,10 @@ static void setup_for_endstop_move() {
...
@@ -1672,13 +1672,10 @@ static void setup_for_endstop_move() {
float
probe_bed_z
,
probe_z
,
probe_h
,
probe_l
;
float
probe_bed_z
,
probe_z
,
probe_h
,
probe_l
;
int
probe_count
;
int
probe_count
;
for
(
int
y
=
3
;
y
>=
-
3
;
y
--
)
for
(
int
y
=
3
;
y
>=
-
3
;
y
--
)
{
{
int
dir
=
y
%
2
?
-
1
:
1
;
int
dir
=
y
%
2
?
-
1
:
1
;
for
(
int
x
=
-
3
*
dir
;
x
!=
4
*
dir
;
x
+=
dir
)
for
(
int
x
=
-
3
*
dir
;
x
!=
4
*
dir
;
x
+=
dir
)
{
{
if
(
x
*
x
+
y
*
y
<
11
)
{
if
(
x
*
x
+
y
*
y
<
11
)
{
destination
[
X_AXIS
]
=
AUTOLEVEL_GRID
*
x
-
z_probe_offset
[
X_AXIS
];
destination
[
X_AXIS
]
=
AUTOLEVEL_GRID
*
x
-
z_probe_offset
[
X_AXIS
];
if
(
destination
[
X_AXIS
]
<
X_MIN_POS
)
destination
[
X_AXIS
]
=
X_MIN_POS
;
if
(
destination
[
X_AXIS
]
<
X_MIN_POS
)
destination
[
X_AXIS
]
=
X_MIN_POS
;
if
(
destination
[
X_AXIS
]
>
X_MAX_POS
)
destination
[
X_AXIS
]
=
X_MAX_POS
;
if
(
destination
[
X_AXIS
]
>
X_MAX_POS
)
destination
[
X_AXIS
]
=
X_MAX_POS
;
...
@@ -1689,8 +1686,7 @@ static void setup_for_endstop_move() {
...
@@ -1689,8 +1686,7 @@ static void setup_for_endstop_move() {
probe_z
=
-
100
;
probe_z
=
-
100
;
probe_h
=
-
100
;
probe_h
=
-
100
;
probe_l
=
100
;
probe_l
=
100
;
do
do
{
{
probe_bed_z
=
probe_z
;
probe_bed_z
=
probe_z
;
probe_z
=
z_probe
()
+
z_offset
;
probe_z
=
z_probe
()
+
z_offset
;
if
(
probe_z
>
probe_h
)
probe_h
=
probe_z
;
if
(
probe_z
>
probe_h
)
probe_h
=
probe_z
;
...
@@ -1700,26 +1696,22 @@ static void setup_for_endstop_move() {
...
@@ -1700,26 +1696,22 @@ static void setup_for_endstop_move() {
bed_level
[
x
+
3
][
3
-
y
]
=
probe_bed_z
;
bed_level
[
x
+
3
][
3
-
y
]
=
probe_bed_z
;
}
}
else
else
{
{
bed_level
[
x
+
3
][
3
-
y
]
=
0.0
;
bed_level
[
x
+
3
][
3
-
y
]
=
0.0
;
}
}
}
}
// For unprobed positions just copy nearest neighbor.
// For unprobed positions just copy nearest neighbor.
if
(
abs
(
y
)
>=
3
)
if
(
abs
(
y
)
>=
3
)
{
{
bed_level
[
1
][
3
-
y
]
=
bed_level
[
2
][
3
-
y
];
bed_level
[
1
][
3
-
y
]
=
bed_level
[
2
][
3
-
y
];
bed_level
[
5
][
3
-
y
]
=
bed_level
[
4
][
3
-
y
];
bed_level
[
5
][
3
-
y
]
=
bed_level
[
4
][
3
-
y
];
}
}
if
(
abs
(
y
)
>=
2
)
if
(
abs
(
y
)
>=
2
)
{
{
bed_level
[
0
][
3
-
y
]
=
bed_level
[
1
][
3
-
y
];
bed_level
[
0
][
3
-
y
]
=
bed_level
[
1
][
3
-
y
];
bed_level
[
6
][
3
-
y
]
=
bed_level
[
5
][
3
-
y
];
bed_level
[
6
][
3
-
y
]
=
bed_level
[
5
][
3
-
y
];
}
}
// Print calibration results for manual frame adjustment.
// Print calibration results for manual frame adjustment.
ECHO_S
(
DB
);
ECHO_S
(
DB
);
for
(
int
x
=
-
3
;
x
<=
3
;
x
++
)
for
(
int
x
=
-
3
;
x
<=
3
;
x
++
)
{
{
ECHO_VM
(
bed_level
[
x
+
3
][
3
-
y
],
" "
,
3
);
ECHO_VM
(
bed_level
[
x
+
3
][
3
-
y
],
" "
,
3
);
}
}
ECHO_E
;
ECHO_E
;
...
@@ -1737,7 +1729,7 @@ static void setup_for_endstop_move() {
...
@@ -1737,7 +1729,7 @@ static void setup_for_endstop_move() {
destination
[
Y_AXIS
]
=
y
-
z_probe_offset
[
Y_AXIS
];
destination
[
Y_AXIS
]
=
y
-
z_probe_offset
[
Y_AXIS
];
if
(
destination
[
Y_AXIS
]
<
Y_MIN_POS
)
destination
[
Y_AXIS
]
=
Y_MIN_POS
;
if
(
destination
[
Y_AXIS
]
<
Y_MIN_POS
)
destination
[
Y_AXIS
]
=
Y_MIN_POS
;
if
(
destination
[
Y_AXIS
]
>
Y_MAX_POS
)
destination
[
Y_AXIS
]
=
Y_MAX_POS
;
if
(
destination
[
Y_AXIS
]
>
Y_MAX_POS
)
destination
[
Y_AXIS
]
=
Y_MAX_POS
;
destination
[
Z_AXIS
]
=
bed_level_c
-
z_probe_offset
[
Z_AXIS
]
+
3
;
destination
[
Z_AXIS
]
=
bed_level_c
-
z_probe_offset
[
Z_AXIS
]
+
AUTOCAL_PROBELIFT
;
prepare_move
();
prepare_move
();
st_synchronize
();
st_synchronize
();
...
@@ -1763,15 +1755,13 @@ static void setup_for_endstop_move() {
...
@@ -1763,15 +1755,13 @@ static void setup_for_endstop_move() {
float
probe_l
[
7
];
float
probe_l
[
7
];
float
range_h
=
0
,
range_l
=
0
;
float
range_h
=
0
,
range_l
=
0
;
for
(
int
x
=
0
;
x
<
7
;
x
++
)
for
(
int
x
=
0
;
x
<
7
;
x
++
)
{
{
probe_h
[
x
]
=
-
100
;
probe_h
[
x
]
=
-
100
;
probe_l
[
x
]
=
100
;
probe_l
[
x
]
=
100
;
}
}
// probe test loop
// probe test loop
for
(
int
x
=
0
;
x
<
3
;
x
++
)
for
(
int
x
=
0
;
x
<
3
;
x
++
)
{
{
bed_probe_all
();
bed_probe_all
();
if
(
bed_level_c
>
probe_h
[
0
])
probe_h
[
0
]
=
bed_level_c
;
if
(
bed_level_c
>
probe_h
[
0
])
probe_h
[
0
]
=
bed_level_c
;
...
@@ -1789,8 +1779,7 @@ static void setup_for_endstop_move() {
...
@@ -1789,8 +1779,7 @@ static void setup_for_endstop_move() {
if
(
bed_level_ox
>
probe_h
[
6
])
probe_h
[
6
]
=
bed_level_ox
;
if
(
bed_level_ox
>
probe_h
[
6
])
probe_h
[
6
]
=
bed_level_ox
;
if
(
bed_level_ox
<
probe_l
[
6
])
probe_l
[
6
]
=
bed_level_ox
;
if
(
bed_level_ox
<
probe_l
[
6
])
probe_l
[
6
]
=
bed_level_ox
;
}
}
for
(
int
x
=
0
;
x
<
7
;
x
++
)
for
(
int
x
=
0
;
x
<
7
;
x
++
)
{
{
if
(
probe_h
[
x
]
-
probe_l
[
x
]
>
range_h
)
range_h
=
probe_h
[
x
]
-
probe_l
[
x
];
if
(
probe_h
[
x
]
-
probe_l
[
x
]
>
range_h
)
range_h
=
probe_h
[
x
]
-
probe_l
[
x
];
if
(
probe_h
[
x
]
-
probe_l
[
x
]
<
range_l
)
range_l
=
probe_h
[
x
]
-
probe_l
[
x
];
if
(
probe_h
[
x
]
-
probe_l
[
x
]
<
range_l
)
range_l
=
probe_h
[
x
]
-
probe_l
[
x
];
}
}
...
@@ -1801,7 +1790,6 @@ static void setup_for_endstop_move() {
...
@@ -1801,7 +1790,6 @@ static void setup_for_endstop_move() {
//Probe all bed positions & store carriage positions
//Probe all bed positions & store carriage positions
bed_level_c
=
probe_bed
(
0.0
,
0.0
);
bed_level_c
=
probe_bed
(
0.0
,
0.0
);
save_carriage_positions
(
0
);
save_carriage_positions
(
0
);
//Probe all bed positions & store carriage positions
bed_level_z
=
probe_bed
(
0.0
,
bed_radius
);
bed_level_z
=
probe_bed
(
0.0
,
bed_radius
);
save_carriage_positions
(
1
);
save_carriage_positions
(
1
);
bed_level_oy
=
probe_bed
(
-
SIN_60
*
bed_radius
,
COS_60
*
bed_radius
);
bed_level_oy
=
probe_bed
(
-
SIN_60
*
bed_radius
,
COS_60
*
bed_radius
);
...
@@ -1876,7 +1864,7 @@ static void setup_for_endstop_move() {
...
@@ -1876,7 +1864,7 @@ static void setup_for_endstop_move() {
sync_plan_position
();
sync_plan_position
();
// Move all carriages up together until the first endstop is hit.
// Move all carriages up together until the first endstop is hit.
for
(
int
i
=
X_AXIS
;
i
<=
Z_AXIS
;
i
++
)
destination
[
i
]
=
3
*
max_length
[
Z_AXIS
]
;
for
(
int
i
=
X_AXIS
;
i
<=
Z_AXIS
;
i
++
)
destination
[
i
]
=
3
*
Z_MAX_LENGTH
;
feedrate
=
1.732
*
homing_feedrate
[
X_AXIS
];
feedrate
=
1.732
*
homing_feedrate
[
X_AXIS
];
line_to_destination
();
line_to_destination
();
st_synchronize
();
st_synchronize
();
...
...
MarlinKimbra/motion_control.cpp
deleted
100644 → 0
View file @
caf65063
/*
motion_control.c - high level interface for issuing motion commands
Part of Grbl
Copyright (c) 2009-2011 Simen Svale Skogsrud
Copyright (c) 2011 Sungeun K. Jeon
Grbl 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.
Grbl 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 Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Marlin.h"
#include "stepper.h"
#include "planner.h"
// The arc is approximated by generating a huge number of tiny, linear segments. The length of each
// segment is configured in settings.mm_per_arc_segment.
void
mc_arc
(
float
*
position
,
float
*
target
,
float
*
offset
,
uint8_t
axis_0
,
uint8_t
axis_1
,
uint8_t
axis_linear
,
float
feed_rate
,
float
radius
,
uint8_t
isclockwise
,
uint8_t
extruder
,
uint8_t
driver
)
{
// int acceleration_manager_was_enabled = plan_is_acceleration_manager_enabled();
// plan_set_acceleration_manager_enabled(false); // disable acceleration management for the duration of the arc
float
center_axis0
=
position
[
axis_0
]
+
offset
[
axis_0
];
float
center_axis1
=
position
[
axis_1
]
+
offset
[
axis_1
];
float
linear_travel
=
target
[
axis_linear
]
-
position
[
axis_linear
];
float
extruder_travel
=
target
[
E_AXIS
]
-
position
[
E_AXIS
];
float
r_axis0
=
-
offset
[
axis_0
];
// Radius vector from center to current location
float
r_axis1
=
-
offset
[
axis_1
];
float
rt_axis0
=
target
[
axis_0
]
-
center_axis0
;
float
rt_axis1
=
target
[
axis_1
]
-
center_axis1
;
// CCW angle between position and target from circle center. Only one atan2() trig computation required.
float
angular_travel
=
atan2
(
r_axis0
*
rt_axis1
-
r_axis1
*
rt_axis0
,
r_axis0
*
rt_axis0
+
r_axis1
*
rt_axis1
);
if
(
angular_travel
<
0
)
{
angular_travel
+=
2
*
M_PI
;
}
if
(
isclockwise
)
{
angular_travel
-=
2
*
M_PI
;
}
//20141002:full circle for G03 did not work, e.g. G03 X80 Y80 I20 J0 F2000 is giving an Angle of zero so head is not moving
//to compensate when start pos = target pos && angle is zero -> angle = 2Pi
if
(
position
[
axis_0
]
==
target
[
axis_0
]
&&
position
[
axis_1
]
==
target
[
axis_1
]
&&
angular_travel
==
0
)
{
angular_travel
+=
2
*
M_PI
;
}
//end fix G03
float
millimeters_of_travel
=
hypot
(
angular_travel
*
radius
,
fabs
(
linear_travel
));
if
(
millimeters_of_travel
<
0.001
)
{
return
;
}
uint16_t
segments
=
floor
(
millimeters_of_travel
/
MM_PER_ARC_SEGMENT
);
if
(
segments
==
0
)
segments
=
1
;
/*
// Multiply inverse feed_rate to compensate for the fact that this movement is approximated
// by a number of discrete segments. The inverse feed_rate should be correct for the sum of
// all segments.
if (invert_feed_rate) { feed_rate *= segments; }
*/
float
theta_per_segment
=
angular_travel
/
segments
;
float
linear_per_segment
=
linear_travel
/
segments
;
float
extruder_per_segment
=
extruder_travel
/
segments
;
/* Vector rotation by transformation matrix: r is the original vector, r_T is the rotated vector,
and phi is the angle of rotation. Based on the solution approach by Jens Geisler.
r_T = [cos(phi) -sin(phi);
sin(phi) cos(phi] * r ;
For arc generation, the center of the circle is the axis of rotation and the radius vector is
defined from the circle center to the initial position. Each line segment is formed by successive
vector rotations. This requires only two cos() and sin() computations to form the rotation
matrix for the duration of the entire arc. Error may accumulate from numerical round-off, since
all double numbers are single precision on the Arduino. (True double precision will not have
round off issues for CNC applications.) Single precision error can accumulate to be greater than
tool precision in some cases. Therefore, arc path correction is implemented.
Small angle approximation may be used to reduce computation overhead further. This approximation
holds for everything, but very small circles and large mm_per_arc_segment values. In other words,
theta_per_segment would need to be greater than 0.1 rad and N_ARC_CORRECTION would need to be large
to cause an appreciable drift error. N_ARC_CORRECTION~=25 is more than small enough to correct for
numerical drift error. N_ARC_CORRECTION may be on the order a hundred(s) before error becomes an
issue for CNC machines with the single precision Arduino calculations.
This approximation also allows mc_arc to immediately insert a line segment into the planner
without the initial overhead of computing cos() or sin(). By the time the arc needs to be applied
a correction, the planner should have caught up to the lag caused by the initial mc_arc overhead.
This is important when there are successive arc motions.
*/
// Vector rotation matrix values
float
cos_T
=
1
-
0.5
*
theta_per_segment
*
theta_per_segment
;
// Small angle approximation
float
sin_T
=
theta_per_segment
;
float
arc_target
[
4
];
float
sin_Ti
;
float
cos_Ti
;
float
r_axisi
;
uint16_t
i
;
int8_t
count
=
0
;
// Initialize the linear axis
arc_target
[
axis_linear
]
=
position
[
axis_linear
];
// Initialize the extruder axis
arc_target
[
E_AXIS
]
=
position
[
E_AXIS
];
for
(
i
=
1
;
i
<
segments
;
i
++
)
{
// Increment (segments-1)
if
(
count
<
N_ARC_CORRECTION
)
{
// Apply vector rotation matrix
r_axisi
=
r_axis0
*
sin_T
+
r_axis1
*
cos_T
;
r_axis0
=
r_axis0
*
cos_T
-
r_axis1
*
sin_T
;
r_axis1
=
r_axisi
;
count
++
;
}
else
{
// Arc correction to radius vector. Computed only every N_ARC_CORRECTION increments.
// Compute exact location by applying transformation matrix from initial radius vector(=-offset).
cos_Ti
=
cos
(
i
*
theta_per_segment
);
sin_Ti
=
sin
(
i
*
theta_per_segment
);
r_axis0
=
-
offset
[
axis_0
]
*
cos_Ti
+
offset
[
axis_1
]
*
sin_Ti
;
r_axis1
=
-
offset
[
axis_0
]
*
sin_Ti
-
offset
[
axis_1
]
*
cos_Ti
;
count
=
0
;
}
// Update arc_target location
arc_target
[
axis_0
]
=
center_axis0
+
r_axis0
;
arc_target
[
axis_1
]
=
center_axis1
+
r_axis1
;
arc_target
[
axis_linear
]
+=
linear_per_segment
;
arc_target
[
E_AXIS
]
+=
extruder_per_segment
;
clamp_to_software_endstops
(
arc_target
);
plan_buffer_line
(
arc_target
[
X_AXIS
],
arc_target
[
Y_AXIS
],
arc_target
[
Z_AXIS
],
arc_target
[
E_AXIS
],
feed_rate
,
extruder
,
driver
);
}
// Ensure last segment arrives at target location.
plan_buffer_line
(
target
[
X_AXIS
],
target
[
Y_AXIS
],
target
[
Z_AXIS
],
target
[
E_AXIS
],
feed_rate
,
extruder
,
driver
);
// plan_set_acceleration_manager_enabled(acceleration_manager_was_enabled);
}
MarlinKimbra/motion_control.h
deleted
100644 → 0
View file @
caf65063
/*
motion_control.h - high level interface for issuing motion commands
Part of Grbl
Copyright (c) 2009-2011 Simen Svale Skogsrud
Copyright (c) 2011 Sungeun K. Jeon
Grbl 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.
Grbl 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 Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef motion_control_h
#define motion_control_h
// Execute an arc in offset mode format. position == current xyz, target == target xyz,
// offset == offset from current xyz, axis_XXX defines circle plane in tool space, axis_linear is
// the direction of helical travel, radius == circle radius, isclockwise boolean. Used
// for vector transformation direction.
void
mc_arc
(
float
*
position
,
float
*
target
,
float
*
offset
,
unsigned
char
axis_0
,
unsigned
char
axis_1
,
unsigned
char
axis_linear
,
float
feed_rate
,
float
radius
,
unsigned
char
isclockwise
,
uint8_t
extruder
,
uint8_t
driver
);
#endif
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment