Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Q
qgroundcontrol
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Valentin Platzgummer
qgroundcontrol
Commits
dd0e4204
Commit
dd0e4204
authored
May 14, 2010
by
pixhawk
Browse files
Options
Browse Files
Download
Plain Diff
Fixed MAV selection bug in parameter view
parents
89a3286f
3555ae74
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
236 additions
and
93 deletions
+236
-93
.gitignore
.gitignore
+2
-0
linux_create_debian_packet.sh
deploy/linux_create_debian_packet.sh
+13
-0
mac_create_dmg.sh
deploy/mac_create_dmg.sh
+13
-0
mac_audiotest.sh
mac_audiotest.sh
+2
-0
LogCompressor.cc
src/LogCompressor.cc
+5
-0
UAS.cc
src/uas/UAS.cc
+10
-1
MainWindow.cc
src/ui/MainWindow.cc
+75
-33
MainWindow.h
src/ui/MainWindow.h
+36
-34
MainWindow.ui
src/ui/MainWindow.ui
+10
-0
QGCParamWidget.cc
src/ui/QGCParamWidget.cc
+67
-25
QGCParamWidget.h
src/ui/QGCParamWidget.h
+3
-0
No files found.
.gitignore
View file @
dd0e4204
...
...
@@ -20,3 +20,5 @@ qgroundcontrol
qgroundcontrol.xcodeproj/**
doc/html
doc/doxy.log
deploy/mac
deploy/linux
deploy/linux_create_debian_packet.sh
0 → 100644
View file @
dd0e4204
#!/bin/sh
# Clean build directories
rm
-rf
linux
mkdir
-p
linux
# Change to build directory and compile application
cd
..
make
-j4
# Copy and build the application bundle
cd
deploy
cp
-r
qgroundcontrol linux/.
cp
-r
../audio linux/.
# FIXME Create debian packet
echo
-e
'\n QGroundControl Debian packet is now ready for publishing\n'
deploy/mac_create_dmg.sh
0 → 100644
View file @
dd0e4204
#!/bin/sh
# Clean build directories
rm
-rf
mac
mkdir
-p
mac
# Change to build directory and compile application
cd
..
make
-j4
# Copy and build the application bundle
cd
deploy
cp
-r
../bin/mac/qgroundcontrol.app mac/.
cp
-r
../audio mac/qgroundcontrol.app/Contents/MacOs/.
macdeployqt qgroundcontrol.app
--bundle
echo
-e
'\n QGroundControl .DMG file is now ready for publishing\n'
mac_audiotest.sh
0 → 100644
View file @
dd0e4204
#!/bin/sh
cp
-r
audio bin/mac/qgroundcontrol.app/Contents/MacOs/.
src/LogCompressor.cc
View file @
dd0e4204
...
...
@@ -81,6 +81,11 @@ void LogCompressor::run()
QString
time
=
parts
.
first
();
QString
field
=
parts
.
at
(
2
);
QString
value
=
parts
.
at
(
3
);
// Enforce NaN if no value is present
if
(
value
.
length
()
==
0
||
value
==
""
||
value
==
" "
||
value
==
"
\t
"
||
value
==
"
\n
"
)
{
value
=
"NaN"
;
}
// Get matching output line
quint64
index
=
times
->
indexOf
(
time
);
QString
outLine
=
outLines
->
at
(
index
);
...
...
src/uas/UAS.cc
View file @
dd0e4204
...
...
@@ -285,6 +285,16 @@ void UAS::receiveMessage(LinkInterface* link, mavlink_message_t message)
emit
valueChanged
(
uasId
,
"vis. x"
,
pos
.
x
,
time
);
emit
valueChanged
(
uasId
,
"vis. y"
,
pos
.
y
,
time
);
emit
valueChanged
(
uasId
,
"vis. z"
,
pos
.
z
,
time
);
// FIXME Only for testing for now
emit
valueChanged
(
uasId
,
"vis. rot r1"
,
pos
.
r1
,
time
);
emit
valueChanged
(
uasId
,
"vis. rot r2"
,
pos
.
r2
,
time
);
emit
valueChanged
(
uasId
,
"vis. rot r3"
,
pos
.
r3
,
time
);
emit
valueChanged
(
uasId
,
"vis. rot r4"
,
pos
.
r4
,
time
);
emit
valueChanged
(
uasId
,
"vis. rot r5"
,
pos
.
r5
,
time
);
emit
valueChanged
(
uasId
,
"vis. rot r6"
,
pos
.
r6
,
time
);
emit
valueChanged
(
uasId
,
"vis. rot r7"
,
pos
.
r7
,
time
);
emit
valueChanged
(
uasId
,
"vis. rot r8"
,
pos
.
r8
,
time
);
emit
valueChanged
(
uasId
,
"vis. rot r9"
,
pos
.
r9
,
time
);
}
break
;
case
MAVLINK_MSG_ID_POSITION
:
...
...
@@ -533,7 +543,6 @@ void UAS::requestParameters()
mavlink_msg_param_request_list_pack
(
mavlink
->
getSystemId
(),
mavlink
->
getComponentId
(),
&
msg
,
this
->
getUASID
(),
0
);
// Send message twice to increase chance of reception
sendMessage
(
msg
);
sendMessage
(
msg
);
}
void
UAS
::
writeParameters
()
...
...
src/ui/MainWindow.cc
View file @
dd0e4204
...
...
@@ -120,7 +120,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent),
QList
<
QHostAddress
>
hostAddresses
=
QNetworkInterface
::
allAddresses
();
QString
windowname
=
qApp
->
applicationName
()
+
" "
+
qApp
->
applicationVersion
();
/*
windowname
.
append
(
" ("
+
QHostInfo
::
localHostName
()
+
": "
);
bool
prevAddr
=
false
;
for
(
int
i
=
0
;
i
<
hostAddresses
.
size
();
i
++
)
...
...
@@ -135,7 +135,6 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent),
}
windowname
.
append
(
")"
);
*/
setWindowTitle
(
windowname
);
#ifndef Q_WS_MAC
...
...
@@ -216,18 +215,28 @@ void MainWindow::saveScreen()
}
}
/**
* Reload the style sheet from disk. The function tries to load "qgroundcontrol.css" from the application
* directory (which by default does not exist). If it fails, it will load the bundled default CSS
* from memory.
* To customize the application, just create a qgroundcontrol.css file in the application directory
*/
void
MainWindow
::
reloadStylesheet
()
{
// Load style sheet
//QFile styleSheet(MG::DIR::getSupportFilesDirectory() + "/images/style-mission.css");
QFile
styleSheet
(
":/images/style-mission.css"
);
if
(
styleSheet
.
open
(
QIODevice
::
ReadOnly
|
QIODevice
::
Text
))
{
QString
style
=
QString
(
styleSheet
.
readAll
());
style
.
replace
(
"ICONDIR"
,
MG
::
DIR
::
getIconDirectory
());
QFile
*
styleSheet
=
new
QFile
(
QCoreApplication
::
applicationDirPath
()
+
"/qgroundcontrol.css"
);
if
(
!
styleSheet
->
exists
())
{
styleSheet
=
new
QFile
(
":/images/style-mission.css"
);
}
if
(
styleSheet
->
open
(
QIODevice
::
ReadOnly
|
QIODevice
::
Text
))
{
QString
style
=
QString
(
styleSheet
->
readAll
());
style
.
replace
(
"ICONDIR"
,
QCoreApplication
::
applicationDirPath
()
+
"/images/"
);
qApp
->
setStyleSheet
(
style
);
}
else
{
qDebug
()
<<
"Style not set:"
<<
styleSheet
.
fileName
()
<<
"opened: "
<<
styleSheet
.
isOpen
();
qDebug
()
<<
"Style not set:"
<<
styleSheet
->
fileName
()
<<
"opened: "
<<
styleSheet
->
isOpen
();
}
delete
styleSheet
;
}
void
MainWindow
::
showStatusMessage
(
const
QString
&
status
,
int
timeout
)
...
...
@@ -235,14 +244,9 @@ void MainWindow::showStatusMessage(const QString& status, int timeout)
statusBar
->
showMessage
(
status
,
timeout
);
}
void
MainWindow
::
s
etLastAction
(
QString
status
)
void
MainWindow
::
s
howStatusMessage
(
const
QString
&
status
)
{
showStatusMessage
(
status
,
5
);
}
void
MainWindow
::
setLinkStatus
(
QString
status
)
{
showStatusMessage
(
status
,
15
);
statusBar
->
showMessage
(
status
,
5
);
}
/**
...
...
@@ -270,6 +274,7 @@ void MainWindow::connectActions()
connect
(
ui
.
actionEngineerView
,
SIGNAL
(
triggered
()),
this
,
SLOT
(
loadEngineerView
()));
connect
(
ui
.
actionOperatorView
,
SIGNAL
(
triggered
()),
this
,
SLOT
(
loadOperatorView
()));
connect
(
ui
.
actionSettingsView
,
SIGNAL
(
triggered
()),
this
,
SLOT
(
loadSettingsView
()));
connect
(
ui
.
actionShow_full_view
,
SIGNAL
(
triggered
()),
this
,
SLOT
(
loadAllView
()));
connect
(
ui
.
actionStyleConfig
,
SIGNAL
(
triggered
()),
this
,
SLOT
(
reloadStylesheet
()));
// Joystick configuration
...
...
@@ -501,28 +506,65 @@ void MainWindow::loadEngineerView()
void
MainWindow
::
loadAllView
()
{
clearView
();
GAudioOutput
::
instance
()
->
say
(
"Loaded complete view"
);
QDockWidget
*
containerPFD
=
new
QDockWidget
(
tr
(
"Primary Flight Display"
),
this
);
containerPFD
->
setWidget
(
headDown1
);
addDockWidget
(
Qt
::
RightDockWidgetArea
,
containerPFD
);
QDockWidget
*
containerPayload
=
new
QDockWidget
(
tr
(
"Payload Status"
),
this
);
containerPayload
->
setWidget
(
headDown2
);
addDockWidget
(
Qt
::
RightDockWidgetArea
,
containerPayload
);
headDown1
->
start
();
headDown2
->
start
();
// UAS CONTROL
QDockWidget
*
containerControl
=
new
QDockWidget
(
tr
(
"Control"
),
this
);
containerControl
->
setWidget
(
control
);
addDockWidget
(
Qt
::
LeftDockWidgetArea
,
containerControl
);
// UAS LIST
QDockWidget
*
containerUASList
=
new
QDockWidget
(
tr
(
"Unmanned Systems"
),
this
);
containerUASList
->
setWidget
(
list
);
addDockWidget
(
Qt
::
BottomDockWidgetArea
,
containerUASList
);
// UAS STATUS
QDockWidget
*
containerStatus
=
new
QDockWidget
(
tr
(
"Status Details"
),
this
);
containerStatus
->
setWidget
(
info
);
addDockWidget
(
Qt
::
LeftDockWidgetArea
,
containerStatus
);
// WAYPOINT LIST
QDockWidget
*
containerWaypoints
=
new
QDockWidget
(
tr
(
"Waypoint List"
),
this
);
containerWaypoints
->
setWidget
(
waypoints
);
addDockWidget
(
Qt
::
BottomDockWidgetArea
,
containerWaypoints
);
// DEBUG CONSOLE
QDockWidget
*
containerComm
=
new
QDockWidget
(
tr
(
"Communication Console"
),
this
);
containerComm
->
setWidget
(
debugConsole
);
addDockWidget
(
Qt
::
BottomDockWidgetArea
,
containerComm
);
// OBJECT DETECTION
QDockWidget
*
containerObjRec
=
new
QDockWidget
(
tr
(
"Object Recognition"
),
this
);
containerObjRec
->
setWidget
(
detection
);
addDockWidget
(
Qt
::
RightDockWidgetArea
,
containerObjRec
);
// LINE CHART
linechart
->
setActive
(
true
);
centerStack
->
setCurrentWidget
(
linechart
);
// ONBOARD PARAMETERS
QDockWidget
*
containerParams
=
new
QDockWidget
(
tr
(
"Onboard Parameters"
),
this
);
containerParams
->
setWidget
(
parameters
);
addDockWidget
(
Qt
::
RightDockWidgetArea
,
containerParams
);
this
->
show
();
}
void
MainWindow
::
loadWidgets
()
{
loadOperatorView
();
//
loadEngineerView();
//
loadOperatorView();
loadEngineerView
();
//loadPilotView();
}
/*
void MainWindow::removeCommConfAct(QAction* action)
{
ui.menuNetwork->removeAction(action);
}*/
void
MainWindow
::
runTests
()
{
// TODO Remove after debugging: Add fake data
static
double
testvalue
=
0.0
f
;
testvalue
+=
0.01
f
;
linechart
->
appendData
(
126
,
"test data"
,
testvalue
,
MG
::
TIME
::
getGroundTimeNow
());
}
src/ui/MainWindow.h
View file @
dd0e4204
...
...
@@ -76,32 +76,6 @@ public:
MainWindow
(
QWidget
*
parent
=
0
);
~
MainWindow
();
QSettings
settings
;
UASControlWidget
*
control
;
LinechartWidget
*
linechart
;
UASInfoWidget
*
info
;
CameraView
*
camera
;
UASListWidget
*
list
;
WaypointList
*
waypoints
;
ObjectDetectionView
*
detection
;
HUD
*
hud
;
PFD
*
pfd
;
GaugePanel
*
gaugePanel
;
// Popup widgets
JoystickWidget
*
joystickWidget
;
JoystickInput
*
joystick
;
/** User interface actions **/
QAction
*
connectUASAct
;
QAction
*
disconnectUASAct
;
QAction
*
startUASAct
;
QAction
*
returnUASAct
;
QAction
*
stopUASAct
;
QAction
*
killUASAct
;
QAction
*
simulateUASAct
;
public
slots
:
/**
* @brief Shows a status message on the bottom status bar
...
...
@@ -112,8 +86,15 @@ public slots:
* @param timeout how long the status should be displayed
*/
void
showStatusMessage
(
const
QString
&
status
,
int
timeout
);
void
setLastAction
(
QString
status
);
void
setLinkStatus
(
QString
status
);
/**
* @brief Shows a status message on the bottom status bar
*
* The status message will be overwritten if a new message is posted to this function.
* it will be automatically hidden after 5 seconds.
*
* @param status message text
*/
void
showStatusMessage
(
const
QString
&
status
);
void
addLink
();
void
addLink
(
LinkInterface
*
link
);
void
configure
();
...
...
@@ -133,10 +114,8 @@ public slots:
/** @brief Load view with all widgets */
void
loadAllView
();
/** @brief Reload the CSS style sheet */
void
reloadStylesheet
();
void
runTests
();
protected:
QStatusBar
*
statusBar
;
QStatusBar
*
createStatusBar
();
...
...
@@ -151,16 +130,39 @@ protected:
MAVLinkSimulationLink
*
simulationLink
;
LinkInterface
*
udpLink
;
QDockWidget
*
controlDock
;
QStackedWidget
*
centerStack
;
QSettings
settings
;
UASControlWidget
*
control
;
LinechartWidget
*
linechart
;
UASInfoWidget
*
info
;
CameraView
*
camera
;
UASListWidget
*
list
;
WaypointList
*
waypoints
;
ObjectDetectionView
*
detection
;
HUD
*
hud
;
DebugConsole
*
debugConsole
;
MapWidget
*
map
;
ParameterInterface
*
parameters
;
XMLCommProtocolWidget
*
protocol
;
HDDisplay
*
headDown1
;
HDDisplay
*
headDown2
;
GaugePanel
*
gaugePanel
;
// Popup widgets
JoystickWidget
*
joystickWidget
;
JoystickInput
*
joystick
;
/** User interface actions **/
QAction
*
connectUASAct
;
QAction
*
disconnectUASAct
;
QAction
*
startUASAct
;
QAction
*
returnUASAct
;
QAction
*
stopUASAct
;
QAction
*
killUASAct
;
QAction
*
simulateUASAct
;
QDockWidget
*
controlDock
;
QStackedWidget
*
centerStack
;
LogCompressor
*
comp
;
QString
screenFileName
;
...
...
src/ui/MainWindow.ui
View file @
dd0e4204
...
...
@@ -74,6 +74,7 @@
<addaction
name=
"actionOperatorView"
/>
<addaction
name=
"actionSettingsView"
/>
<addaction
name=
"separator"
/>
<addaction
name=
"actionShow_full_view"
/>
<addaction
name=
"actionStyleConfig"
/>
</widget>
<addaction
name=
"menuMGround"
/>
...
...
@@ -243,6 +244,15 @@
<string>
Simulate one vehicle to test and evaluate this application
</string>
</property>
</action>
<action
name=
"actionShow_full_view"
>
<property
name=
"icon"
>
<iconset
resource=
"../../mavground.qrc"
>
<normaloff>
:/images/status/network-transmit-receive.svg
</normaloff>
:/images/status/network-transmit-receive.svg
</iconset>
</property>
<property
name=
"text"
>
<string>
Show full view
</string>
</property>
</action>
</widget>
<layoutdefault
spacing=
"6"
margin=
"11"
/>
<resources>
...
...
src/ui/QGCParamWidget.cc
View file @
dd0e4204
...
...
@@ -43,7 +43,8 @@ This file is part of the QGROUNDCONTROL project
QGCParamWidget
::
QGCParamWidget
(
UASInterface
*
uas
,
QWidget
*
parent
)
:
QWidget
(
parent
),
mav
(
uas
),
components
(
new
QMap
<
int
,
QTreeWidgetItem
*>
())
components
(
new
QMap
<
int
,
QTreeWidgetItem
*>
()),
changedValues
()
//QMap<int, QMap<QString, float>* >())
{
// Create tree widget
tree
=
new
QTreeWidget
(
this
);
...
...
@@ -82,6 +83,8 @@ QGCParamWidget::QGCParamWidget(UASInterface* uas, QWidget *parent) :
// Connect signals/slots
connect
(
this
,
SIGNAL
(
parameterChanged
(
int
,
QString
,
float
)),
mav
,
SLOT
(
setParameter
(
int
,
QString
,
float
)));
connect
(
tree
,
SIGNAL
(
itemChanged
(
QTreeWidgetItem
*
,
int
)),
this
,
SLOT
(
parameterItemChanged
(
QTreeWidgetItem
*
,
int
)));
// New parameters from UAS
connect
(
uas
,
SIGNAL
(
parameterChanged
(
int
,
int
,
QString
,
float
)),
this
,
SLOT
(
addParameter
(
int
,
int
,
QString
,
float
)));
}
...
...
@@ -138,8 +141,26 @@ void QGCParamWidget::addParameter(int uas, int component, QString parameterName,
{
addComponent
(
uas
,
component
,
"Component #"
+
QString
::
number
(
component
));
}
components
->
value
(
component
)
->
addChild
(
item
);
item
->
setFlags
(
item
->
flags
()
|
Qt
::
ItemIsEditable
);
bool
found
=
false
;
QTreeWidgetItem
*
parent
=
components
->
value
(
component
);
for
(
int
i
=
0
;
i
<
parent
->
childCount
();
i
++
)
{
QTreeWidgetItem
*
child
=
parent
->
child
(
i
);
QString
key
=
child
->
data
(
0
,
Qt
::
DisplayRole
).
toString
();
if
(
key
==
parameterName
)
{
qDebug
()
<<
"UPDATED CHILD"
;
child
->
setData
(
1
,
Qt
::
DisplayRole
,
value
);
found
=
true
;
}
}
if
(
!
found
)
{
components
->
value
(
component
)
->
addChild
(
item
);
item
->
setFlags
(
item
->
flags
()
|
Qt
::
ItemIsEditable
);
}
//connect(item, SIGNAL())
tree
->
expandAll
();
tree
->
update
();
...
...
@@ -156,6 +177,40 @@ void QGCParamWidget::requestParameterList()
mav
->
requestParameters
();
}
void
QGCParamWidget
::
parameterItemChanged
(
QTreeWidgetItem
*
current
,
int
column
)
{
if
(
current
&&
column
>
0
)
{
QTreeWidgetItem
*
parent
=
current
->
parent
();
while
(
parent
->
parent
()
!=
NULL
)
{
parent
=
parent
->
parent
();
}
// Parent is now top-level component
int
key
=
components
->
key
(
parent
);
if
(
!
changedValues
.
contains
(
key
))
{
changedValues
.
insert
(
key
,
new
QMap
<
QString
,
float
>
());
}
QMap
<
QString
,
float
>*
map
=
changedValues
.
value
(
key
,
NULL
);
if
(
map
)
{
bool
ok
;
QString
str
=
current
->
data
(
0
,
Qt
::
DisplayRole
).
toString
();
float
value
=
current
->
data
(
1
,
Qt
::
DisplayRole
).
toDouble
(
&
ok
);
// Send parameter to MAV
if
(
ok
)
{
if
(
ok
)
{
qDebug
()
<<
"PARAM CHANGED: COMP:"
<<
key
<<
"KEY:"
<<
str
<<
"VALUE:"
<<
value
;
map
->
insert
(
str
,
value
);
}
}
}
}
}
/**
* @param component the subsystem which has the parameter
* @param parameterName name of the parameter, as delivered by the system
...
...
@@ -171,36 +226,23 @@ void QGCParamWidget::setParameter(int component, QString parameterName, float va
*/
void
QGCParamWidget
::
setParameters
()
{
//mav->setParameter(component, parameterName, value);
// Iterate through all components, through all parameters and emit them
QMap
<
int
,
QTreeWidgetItem
*>::
iterator
i
;
// Iterate through all components / subsystems
for
(
i
=
components
->
begin
();
i
!=
components
->
end
();
++
i
)
QMap
<
int
,
QMap
<
QString
,
float
>*>::
iterator
i
;
for
(
i
=
changedValues
.
begin
();
i
!=
changedValues
.
end
();
++
i
)
{
//
Get all parameters of this
component
//
Iterate through the parameters of the
component
int
compid
=
i
.
key
();
QTreeWidgetItem
*
item
=
i
.
value
();
for
(
int
j
=
0
;
j
<
item
->
childCount
();
++
j
)
QMap
<
QString
,
float
>*
comp
=
i
.
value
();
{
QTreeWidgetItem
*
param
=
item
->
child
(
j
);
// First column is name, second column value
bool
ok
=
true
;
QString
key
=
param
->
data
(
0
,
Qt
::
DisplayRole
).
toString
();
float
value
=
param
->
data
(
1
,
Qt
::
DisplayRole
).
toDouble
(
&
ok
);
// Send parameter to MAV
if
(
ok
)
QMap
<
QString
,
float
>::
iterator
j
;
for
(
j
=
comp
->
begin
();
j
!=
comp
->
end
();
++
j
)
{
emit
parameterChanged
(
compid
,
key
,
value
);
qDebug
()
<<
"KEY:"
<<
key
<<
"VALUE:"
<<
value
;
}
else
{
qDebug
()
<<
__FILE__
<<
__LINE__
<<
"CONVERSION ERROR!"
;
emit
parameterChanged
(
compid
,
j
.
key
(),
j
.
value
());
}
}
}
clear
();
//mav->requestParameters
();
changedValues
.
clear
();
qDebug
()
<<
__FILE__
<<
__LINE__
<<
"SETTING ALL PARAMETERS"
;
}
...
...
src/ui/QGCParamWidget.h
View file @
dd0e4204
...
...
@@ -67,10 +67,13 @@ public slots:
void
writeParameters
();
/** @brief Clear the parameter list */
void
clear
();
/** @brief Update when user changes parameters */
void
parameterItemChanged
(
QTreeWidgetItem
*
prev
,
int
column
);
protected:
UASInterface
*
mav
;
///< The MAV this widget is controlling
QTreeWidget
*
tree
;
///< The parameter tree
QMap
<
int
,
QTreeWidgetItem
*>*
components
;
///< The list of components
QMap
<
int
,
QMap
<
QString
,
float
>*
>
changedValues
;
///< Changed values
};
...
...
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