Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Valentin Platzgummer
qgroundcontrol
Commits
b485d7f6
Commit
b485d7f6
authored
Jul 15, 2014
by
Lorenz Meier
Browse files
Merge branch 'master' of github.com:mavlink/qgroundcontrol
parents
03ced70a
a1cc1b5e
Changes
31
Hide whitespace changes
Inline
Side-by-side
files/flightgear/Aircraft/arducopter/arducopter.xml
View file @
b485d7f6
...
...
@@ -103,11 +103,11 @@
</ground_reactions>
<!-- the front and rear motors spin clockwise, and the left and right motors spin counter-clockwise. -->
<propulsion>
<engine
file=
"a2830-12"
name=
"
fron
t"
>
<engine
file=
"a2830-12"
name=
"
righ
t"
>
<location
unit=
"M"
>
<x>
-
0.
283
</x>
<y>
0.
00
</y>
<z>
0.00
</z>
<x>
0.
000
</x>
<y>
0.
283
</y>
<z>
0.00
0
</z>
</location>
<orient
unit=
"DEG"
>
<pitch>
90.00
</pitch>
...
...
@@ -117,8 +117,8 @@
<feed>
0
</feed>
<thruster
file=
"prop10x4.5"
>
<location
unit=
"M"
>
<x>
-
0.
283
</x>
<y>
0.
00
</y>
<x>
0.
000
</x>
<y>
0.
283
</y>
<z>
0.125
</z>
</location>
<orient
unit=
"DEG"
>
...
...
@@ -130,11 +130,12 @@
<p_factor>
10
</p_factor>
</thruster>
</engine>
<engine
file=
"a2830-12"
name=
"rear"
>
<engine
file=
"a2830-12"
name=
"left"
>
<location
unit=
"M"
>
<x>
0.
283
</x>
<y>
0.
000
</y>
<z>
0.00
0
</z>
<x>
0.
00
</x>
<y>
-
0.
283
</y>
<z>
0.00
</z>
</location>
<orient
unit=
"DEG"
>
<pitch>
90.00
</pitch>
...
...
@@ -144,8 +145,8 @@
<feed>
0
</feed>
<thruster
file=
"prop10x4.5"
>
<location
unit=
"M"
>
<x>
0.
283
</x>
<y>
0.
000
</y>
<x>
0.
00
</x>
<y>
-
0.
283
</y>
<z>
0.125
</z>
</location>
<orient
unit=
"DEG"
>
...
...
@@ -157,11 +158,12 @@
<p_factor>
10
</p_factor>
</thruster>
</engine>
<engine
file=
"a2830-12"
name=
"left"
>
<engine
file=
"a2830-12"
name=
"front"
>
<location
unit=
"M"
>
<x>
0.
00
</x>
<y>
0.
283
</y>
<z>
0.00
</z>
<x>
-
0.
283
</x>
<y>
0.
00
</y>
<z>
0.00
</z>
</location>
<orient
unit=
"DEG"
>
<pitch>
90.00
</pitch>
...
...
@@ -171,8 +173,8 @@
<feed>
0
</feed>
<thruster
file=
"prop10x4.5"
>
<location
unit=
"M"
>
<x>
0.
00
</x>
<y>
0.
283
</y>
<x>
-
0.
283
</x>
<y>
0.
00
</y>
<z>
0.125
</z>
</location>
<orient
unit=
"DEG"
>
...
...
@@ -184,10 +186,11 @@
<p_factor>
10
</p_factor>
</thruster>
</engine>
<engine
file=
"a2830-12"
name=
"right"
>
<engine
file=
"a2830-12"
name=
"rear"
>
<location
unit=
"M"
>
<x>
0.
000
</x>
<y>
-
0.
283
</y>
<x>
0.
283
</x>
<y>
0.
000
</y>
<z>
0.000
</z>
</location>
<orient
unit=
"DEG"
>
...
...
@@ -198,8 +201,8 @@
<feed>
0
</feed>
<thruster
file=
"prop10x4.5"
>
<location
unit=
"M"
>
<x>
0.
000
</x>
<y>
-
0.
283
</y>
<x>
0.
283
</x>
<y>
0.
000
</y>
<z>
0.125
</z>
</location>
<orient
unit=
"DEG"
>
...
...
@@ -211,6 +214,7 @@
<p_factor>
10
</p_factor>
</thruster>
</engine>
<tank
type=
"FUEL"
number=
"0"
>
<location
unit=
"M"
>
<x>
0.00
</x>
...
...
@@ -222,6 +226,7 @@
<contents
unit=
"KG"
>
0.0
</contents>
</tank>
</propulsion>
<aerodynamics>
<axis
name=
"LIFT"
>
</axis>
...
...
files/flightgear/Protocol/qgroundcontrol-quadrotor.xml
View file @
b485d7f6
...
...
@@ -6,7 +6,7 @@
<output>
<line_separator>
newline
</line_separator>
<var_separator>
tab
</var_separator>
<chunk>
<name>
time (sec)
</name>
<type>
float
</type>
...
...
@@ -46,7 +46,7 @@
<node>
/orientation/roll-deg
</node>
<factor>
0.01745329251994329576
</factor>
<!-- degrees to radians -->
</chunk>
<chunk>
<name>
pitch angle (rad)
</name>
<type>
float
</type>
...
...
@@ -54,7 +54,7 @@
<node>
/orientation/pitch-deg
</node>
<factor>
0.01745329251994329576
</factor>
<!-- degrees to radians -->
</chunk>
<chunk>
<name>
yaw angle
</name>
<type>
float
</type>
...
...
@@ -109,7 +109,7 @@
</chunk>
<!-- Velocities -->
<chunk>
<name>
Velocity North ("vn" mps)
</name>
<type>
float
</type>
...
...
@@ -141,7 +141,42 @@
<node>
/velocities/airspeed-kt
</node>
<factor>
0.514444444444444
</factor>
<!-- knots to mps -->
</chunk>
<!-- Magnetometer -->
<chunk>
<name>
Magnetic Variation (rad)
</name>
<type>
float
</type>
<format>
%.8f
</format>
<node>
/environment/magnetic-variation-deg
</node>
<factor>
0.01745329251994329576
</factor>
<!-- degrees to radians -->
</chunk>
<chunk>
<name>
Magnetic Dip (rad)
</name>
<type>
float
</type>
<format>
%.8f
</format>
<node>
/environment/magnetic-dip-deg
</node>
<factor>
0.01745329251994329576
</factor>
<!-- degrees to radians -->
</chunk>
<!-- Temperature -->
<chunk>
<name>
Temperature (deg C)
</name>
<type>
float
</type>
<format>
%.8f
</format>
<node>
/environment/temperature-degc
</node>
<factor>
1
</factor>
</chunk>
<!-- Pressure -->
<chunk>
<name>
Pressure (hPa)
</name>
<type>
float
</type>
<format>
%.8f
</format>
<node>
/environment/pressure-inhg
</node>
<factor>
33.86389
</factor>
<!-- inhg to hpa -->
</chunk>
</output>
<input>
...
...
@@ -152,28 +187,33 @@
<!-- motors, in range 0.0 to 1.0 -->
<chunk>
<name>
throttle0
</name>
<type>
double
</type>
<type>
float
</type>
<node>
/controls/engines/engine[0]/throttle
</node>
</chunk>
</chunk>
<chunk>
<name>
throttle1
</name>
<type>
double
</type>
<type>
float
</type>
<node>
/controls/engines/engine[1]/throttle
</node>
</chunk>
</chunk>
<chunk>
<name>
throttle2
</name>
<type>
double
</type>
<type>
float
</type>
<node>
/controls/engines/engine[2]/throttle
</node>
</chunk>
</chunk>
<chunk>
<name>
running
</name>
<type>
bool
</type>
<node>
/engines/engine/running
</node>
</chunk>
<chunk>
<name>
throttle3
</name>
<type>
double
</type>
<type>
float
</type>
<node>
/controls/engines/engine[3]/throttle
</node>
</chunk>
</chunk>
</input>
</generic>
</PropertyList>
\ No newline at end of file
</PropertyList>
libs/mavlink/include/mavlink/v1.0/checksum.h
View file @
b485d7f6
...
...
@@ -73,7 +73,7 @@ static inline uint16_t crc_calculate(const uint8_t* pBuffer, uint16_t length)
* @param data new bytes to hash
* @param crcAccum the already accumulated checksum
**/
static
inline
void
crc_accumulate_buffer
(
uint16_t
*
crcAccum
,
const
char
*
pBuffer
,
uint
8
_t
length
)
static
inline
void
crc_accumulate_buffer
(
uint16_t
*
crcAccum
,
const
char
*
pBuffer
,
uint
16
_t
length
)
{
const
uint8_t
*
p
=
(
const
uint8_t
*
)
pBuffer
;
while
(
length
--
)
{
...
...
libs/mavlink/include/mavlink/v1.0/common/common.h
View file @
b485d7f6
...
...
@@ -5,6 +5,10 @@
#ifndef COMMON_H
#define COMMON_H
#ifndef MAVLINK_H
#error Wrong include order: common.h MUST NOT BE DIRECTLY USED. Include mavlink.h from the same directory instead or set all defines from mavlink.h manually.
#endif
#ifdef __cplusplus
extern
"C"
{
#endif
...
...
libs/mavlink/include/mavlink/v1.0/mavlink_helpers.h
View file @
b485d7f6
...
...
@@ -73,7 +73,6 @@ MAVLINK_HELPER uint16_t mavlink_finalize_message_chan(mavlink_message_t* msg, ui
#endif
{
// This code part is the same for all messages;
uint16_t
checksum
;
msg
->
magic
=
MAVLINK_STX
;
msg
->
len
=
length
;
msg
->
sysid
=
system_id
;
...
...
@@ -81,12 +80,13 @@ MAVLINK_HELPER uint16_t mavlink_finalize_message_chan(mavlink_message_t* msg, ui
// One sequence number per component
msg
->
seq
=
mavlink_get_channel_status
(
chan
)
->
current_tx_seq
;
mavlink_get_channel_status
(
chan
)
->
current_tx_seq
=
mavlink_get_channel_status
(
chan
)
->
current_tx_seq
+
1
;
checksum
=
crc_calculate
((
uint8_t
*
)
&
msg
->
len
,
length
+
MAVLINK_CORE_HEADER_LEN
);
msg
->
checksum
=
crc_calculate
(((
const
uint8_t
*
)(
msg
))
+
3
,
MAVLINK_CORE_HEADER_LEN
);
crc_accumulate_buffer
(
&
msg
->
checksum
,
_MAV_PAYLOAD
(
msg
),
msg
->
len
);
#if MAVLINK_CRC_EXTRA
crc_accumulate
(
crc_extra
,
&
checksum
);
crc_accumulate
(
crc_extra
,
&
msg
->
checksum
);
#endif
mavlink_ck_a
(
msg
)
=
(
uint8_t
)(
checksum
&
0xFF
);
mavlink_ck_b
(
msg
)
=
(
uint8_t
)(
checksum
>>
8
);
mavlink_ck_a
(
msg
)
=
(
uint8_t
)(
msg
->
checksum
&
0xFF
);
mavlink_ck_b
(
msg
)
=
(
uint8_t
)(
msg
->
checksum
>>
8
);
return
length
+
MAVLINK_NUM_NON_PAYLOAD_BYTES
;
}
...
...
@@ -133,7 +133,7 @@ MAVLINK_HELPER void _mav_finalize_message_chan_send(mavlink_channel_t chan, uint
buf
[
4
]
=
mavlink_system
.
compid
;
buf
[
5
]
=
msgid
;
status
->
current_tx_seq
++
;
checksum
=
crc_calculate
((
uint8_t
*
)
&
buf
[
1
],
MAVLINK_CORE_HEADER_LEN
);
checksum
=
crc_calculate
((
const
uint8_t
*
)
&
buf
[
1
],
MAVLINK_CORE_HEADER_LEN
);
crc_accumulate_buffer
(
&
checksum
,
packet
,
length
);
#if MAVLINK_CRC_EXTRA
crc_accumulate
(
crc_extra
,
&
checksum
);
...
...
@@ -158,6 +158,7 @@ MAVLINK_HELPER void _mavlink_resend_uart(mavlink_channel_t chan, const mavlink_m
ck
[
0
]
=
(
uint8_t
)(
msg
->
checksum
&
0xFF
);
ck
[
1
]
=
(
uint8_t
)(
msg
->
checksum
>>
8
);
// XXX use the right sequence here
MAVLINK_START_UART_SEND
(
chan
,
MAVLINK_NUM_NON_PAYLOAD_BYTES
+
msg
->
len
);
_mavlink_send_uart
(
chan
,
(
const
char
*
)
&
msg
->
magic
,
MAVLINK_NUM_HEADER_BYTES
);
...
...
@@ -172,7 +173,13 @@ MAVLINK_HELPER void _mavlink_resend_uart(mavlink_channel_t chan, const mavlink_m
*/
MAVLINK_HELPER
uint16_t
mavlink_msg_to_send_buffer
(
uint8_t
*
buffer
,
const
mavlink_message_t
*
msg
)
{
memcpy
(
buffer
,
(
const
uint8_t
*
)
&
msg
->
magic
,
MAVLINK_NUM_NON_PAYLOAD_BYTES
+
(
uint16_t
)
msg
->
len
);
memcpy
(
buffer
,
(
const
uint8_t
*
)
&
msg
->
magic
,
MAVLINK_NUM_HEADER_BYTES
+
(
uint16_t
)
msg
->
len
);
uint8_t
*
ck
=
buffer
+
(
MAVLINK_NUM_HEADER_BYTES
+
(
uint16_t
)
msg
->
len
);
ck
[
0
]
=
(
uint8_t
)(
msg
->
checksum
&
0xFF
);
ck
[
1
]
=
(
uint8_t
)(
msg
->
checksum
>>
8
);
return
MAVLINK_NUM_NON_PAYLOAD_BYTES
+
(
uint16_t
)
msg
->
len
;
}
...
...
libs/mavlink/include/mavlink/v1.0/mavlink_types.h
View file @
b485d7f6
...
...
@@ -28,6 +28,7 @@
#define MAVLINK_MAX_EXTENDED_PAYLOAD_LEN (MAVLINK_MAX_EXTENDED_PACKET_LEN - MAVLINK_EXTENDED_HEADER_LEN - MAVLINK_NUM_NON_PAYLOAD_BYTES)
#pragma pack(push, 1)
typedef
struct
param_union
{
union
{
float
param_float
;
...
...
@@ -62,13 +63,12 @@ typedef struct __mavlink_message {
uint64_t
payload64
[(
MAVLINK_MAX_PAYLOAD_LEN
+
MAVLINK_NUM_CHECKSUM_BYTES
+
7
)
/
8
];
}
mavlink_message_t
;
typedef
struct
__mavlink_extended_message
{
mavlink_message_t
base_msg
;
int32_t
extended_payload_len
;
///< Length of extended payload if any
uint8_t
extended_payload
[
MAVLINK_MAX_EXTENDED_PAYLOAD_LEN
];
}
mavlink_extended_message_t
;
#pragma pack(pop)
typedef
enum
{
MAVLINK_TYPE_CHAR
=
0
,
...
...
qgroundcontrol.pro
View file @
b485d7f6
...
...
@@ -186,22 +186,27 @@ DebugBuild {
src
/
qgcunittest
/
MockUASManager
.
h
\
src
/
qgcunittest
/
MockUAS
.
h
\
src
/
qgcunittest
/
MockQGCUASParamManager
.
h
\
src
/
qgcunittest
/
MockMavlinkInterface
.
h
\
src
/
qgcunittest
/
MockMavlinkFileServer
.
h
\
src
/
qgcunittest
/
MultiSignalSpy
.
h
\
src
/
qgcunittest
/
FlightModeConfigTest
.
h
\
src
/
qgcunittest
/
FlightGearTest
.
h
\
src
/
qgcunittest
/
TCPLinkTest
.
h
\
src
/
qgcunittest
/
TCPLoopBackServer
.
h
src
/
qgcunittest
/
TCPLoopBackServer
.
h
\
src
/
qgcunittest
/
QGCUASFileManagerTest
.
h
SOURCES
+=
\
src
/
qgcunittest
/
UASUnitTest
.
cc
\
src
/
qgcunittest
/
MockUASManager
.
cc
\
src
/
qgcunittest
/
MockUAS
.
cc
\
src
/
qgcunittest
/
MockQGCUASParamManager
.
cc
\
src
/
qgcunittest
/
MockMavlinkFileServer
.
cc
\
src
/
qgcunittest
/
MultiSignalSpy
.
cc
\
src
/
qgcunittest
/
FlightModeConfigTest
.
cc
\
src
/
qgcunittest
/
FlightGearTest
.
cc
\
src
/
qgcunittest
/
TCPLinkTest
.
cc
\
src
/
qgcunittest
/
TCPLoopBackServer
.
cc
src
/
qgcunittest
/
TCPLoopBackServer
.
cc
\
src
/
qgcunittest
/
QGCUASFileManagerTest
.
cc
}
#
...
...
@@ -296,6 +301,7 @@ FORMS += \
src
/
ui
/
designer
/
QGCCommandButton
.
ui
\
src
/
ui
/
QGCMAVLinkLogPlayer
.
ui
\
src
/
ui
/
QGCWaypointListMulti
.
ui
\
src
/
ui
/
QGCUASFileViewMulti
.
ui
\
src
/
ui
/
QGCUDPLinkConfiguration
.
ui
\
src
/
ui
/
QGCTCPLinkConfiguration
.
ui
\
src
/
ui
/
QGCSettingsWidget
.
ui
\
...
...
@@ -373,7 +379,8 @@ FORMS += \
src
/
ui
/
px4_configuration
/
QGCPX4AirframeConfig
.
ui
\
src
/
ui
/
px4_configuration
/
QGCPX4MulticopterConfig
.
ui
\
src
/
ui
/
px4_configuration
/
QGCPX4SensorCalibration
.
ui
\
src
/
ui
/
designer
/
QGCXYPlot
.
ui
src
/
ui
/
designer
/
QGCXYPlot
.
ui
\
src
/
ui
/
QGCUASFileView
.
ui
HEADERS
+=
\
src
/
MG
.
h
\
...
...
@@ -461,6 +468,7 @@ HEADERS += \
src
/
comm
/
MAVLinkSimulationMAV
.
h
\
src
/
uas
/
QGCMAVLinkUASFactory
.
h
\
src
/
ui
/
QGCWaypointListMulti
.
h
\
src
/
ui
/
QGCUASFileViewMulti
.
h
\
src
/
ui
/
QGCUDPLinkConfiguration
.
h
\
src
/
ui
/
QGCTCPLinkConfiguration
.
h
\
src
/
ui
/
QGCSettingsWidget
.
h
\
...
...
@@ -567,6 +575,8 @@ HEADERS += \
src
/
ui
/
menuactionhelper
.
h
\
src
/
uas
/
UASManagerInterface
.
h
\
src
/
uas
/
QGCUASParamManagerInterface
.
h
\
src
/
uas
/
QGCUASFileManager
.
h
\
src
/
ui
/
QGCUASFileView
.
h
\
src
/
uas
/
QGCUASWorker
.
h
\
src
/
CmdLineOptParser
.
h
\
src
/
uas
/
QGXPX4UAS
.
h
...
...
@@ -651,6 +661,7 @@ SOURCES += \
src
/
comm
/
MAVLinkSimulationMAV
.
cc
\
src
/
uas
/
QGCMAVLinkUASFactory
.
cc
\
src
/
ui
/
QGCWaypointListMulti
.
cc
\
src
/
ui
/
QGCUASFileViewMulti
.
cc
\
src
/
ui
/
QGCUDPLinkConfiguration
.
cc
\
src
/
ui
/
QGCTCPLinkConfiguration
.
cc
\
src
/
ui
/
QGCSettingsWidget
.
cc
\
...
...
@@ -753,6 +764,8 @@ SOURCES += \
src
/
ui
/
px4_configuration
/
QGCPX4SensorCalibration
.
cc
\
src
/
ui
/
designer
/
QGCXYPlot
.
cc
\
src
/
ui
/
menuactionhelper
.
cpp
\
src
/
uas
/
QGCUASFileManager
.
cc
\
src
/
ui
/
QGCUASFileView
.
cc
\
src
/
uas
/
QGCUASWorker
.
cc
\
src
/
CmdLineOptParser
.
cc
\
src
/
uas
/
QGXPX4UAS
.
cc
src/qgcunittest/MockMavlinkFileServer.cc
0 → 100644
View file @
b485d7f6
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009 - 2014 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
This file is part of the QGROUNDCONTROL project
QGROUNDCONTROL 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.
QGROUNDCONTROL 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 QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
======================================================================*/
#include
"MockMavlinkFileServer.h"
const
MockMavlinkFileServer
::
FileTestCase
MockMavlinkFileServer
::
rgFileTestCases
[
MockMavlinkFileServer
::
cFileTestCases
]
=
{
// File fits one Read Ack packet, partially filling data
{
"partial.qgc"
,
sizeof
(((
QGCUASFileManager
::
Request
*
)
0
)
->
data
)
-
1
},
// File fits one Read Ack packet, exactly filling all data
{
"exact.qgc"
,
sizeof
(((
QGCUASFileManager
::
Request
*
)
0
)
->
data
)
},
// File is larger than a single Read Ack packets, requires multiple Reads
{
"multi.qgc"
,
sizeof
(((
QGCUASFileManager
::
Request
*
)
0
)
->
data
)
+
1
},
};
// We only support a single fixed session
const
uint8_t
MockMavlinkFileServer
::
_sessionId
=
1
;
MockMavlinkFileServer
::
MockMavlinkFileServer
(
void
)
{
}
/// @brief Handles List command requests. Only supports root folder paths.
/// File list returned is set using the setFileList method.
void
MockMavlinkFileServer
::
_listCommand
(
QGCUASFileManager
::
Request
*
request
)
{
// FIXME: Does not support directories that span multiple packets
QGCUASFileManager
::
Request
ackResponse
;
QString
path
;
// We only support root path
path
=
(
char
*
)
&
request
->
data
[
0
];
if
(
!
path
.
isEmpty
()
&&
path
!=
"/"
)
{
_sendNak
(
QGCUASFileManager
::
kErrNotDir
);
return
;
}
// Offset requested is past the end of the list
if
(
request
->
hdr
.
offset
>
(
uint32_t
)
_fileList
.
size
())
{
_sendNak
(
QGCUASFileManager
::
kErrEOF
);
return
;
}
ackResponse
.
hdr
.
magic
=
'f'
;
ackResponse
.
hdr
.
opcode
=
QGCUASFileManager
::
kRspAck
;
ackResponse
.
hdr
.
session
=
0
;
ackResponse
.
hdr
.
offset
=
request
->
hdr
.
offset
;
ackResponse
.
hdr
.
size
=
0
;
if
(
request
->
hdr
.
offset
==
0
)
{
// Requesting first batch of file names
Q_ASSERT
(
_fileList
.
size
());
char
*
bufPtr
=
(
char
*
)
&
ackResponse
.
data
[
0
];
for
(
int
i
=
0
;
i
<
_fileList
.
size
();
i
++
)
{
strcpy
(
bufPtr
,
_fileList
[
i
].
toStdString
().
c_str
());
size_t
cchFilename
=
strlen
(
bufPtr
);
Q_ASSERT
(
cchFilename
);
ackResponse
.
hdr
.
size
+=
cchFilename
+
1
;
bufPtr
+=
cchFilename
+
1
;
}
_emitResponse
(
&
ackResponse
);
}
else
{
// FIXME: Does not support directories that span multiple packets
_sendNak
(
QGCUASFileManager
::
kErrEOF
);
}
}
/// @brief Handles Open command requests.
void
MockMavlinkFileServer
::
_openCommand
(
QGCUASFileManager
::
Request
*
request
)
{
QGCUASFileManager
::
Request
response
;
QString
path
;
size_t
cchPath
=
strnlen
((
char
*
)
request
->
data
,
sizeof
(
request
->
data
));
Q_ASSERT
(
cchPath
!=
sizeof
(
request
->
data
));
path
=
(
char
*
)
request
->
data
;
// Check path against one of our known test cases
bool
found
=
false
;
for
(
size_t
i
=
0
;
i
<
cFileTestCases
;
i
++
)
{
if
(
path
==
rgFileTestCases
[
i
].
filename
)
{
found
=
true
;
_readFileLength
=
rgFileTestCases
[
i
].
length
;
break
;
}
}
if
(
!
found
)
{
_sendNak
(
QGCUASFileManager
::
kErrNotFile
);
return
;
}
response
.
hdr
.
magic
=
'f'
;
response
.
hdr
.
opcode
=
QGCUASFileManager
::
kRspAck
;
response
.
hdr
.
session
=
_sessionId
;
response
.
hdr
.
size
=
0
;
_emitResponse
(
&
response
);
}
/// @brief Handles Read command requests.
void
MockMavlinkFileServer
::
_readCommand
(
QGCUASFileManager
::
Request
*
request
)
{
QGCUASFileManager
::
Request
response
;
if
(
request
->
hdr
.
session
!=
_sessionId
)
{
_sendNak
(
QGCUASFileManager
::
kErrNoSession
);
return
;
}
uint32_t
readOffset
=
request
->
hdr
.
offset
;
// offset into file for reading
uint8_t
cDataBytes
=
0
;
// current number of data bytes used
if
(
readOffset
>=
_readFileLength
)
{
_sendNak
(
QGCUASFileManager
::
kErrEOF
);
return
;
}
// Write length byte if needed
if
(
readOffset
==
0
)
{
response
.
data
[
0
]
=
_readFileLength
;
readOffset
++
;
cDataBytes
++
;
}
// Write file bytes. Data is a repeating sequence of 0x00, 0x01, .. 0xFF.
for
(;
cDataBytes
<
sizeof
(
response
.
data
)
&&
readOffset
<
_readFileLength
;
readOffset
++
,
cDataBytes
++
)
{
// Subtract one from readOffset to take into account length byte and start file data a 0x00
response
.
data
[
cDataBytes
]
=
(
readOffset
-
1
)
&
0xFF
;
}
// We should always have written something, otherwise there is something wrong with the code above
Q_ASSERT
(
cDataBytes
);
response
.
hdr
.
magic
=
'f'
;
response
.
hdr
.
session
=
_sessionId
;
response
.
hdr
.
size
=
cDataBytes
;
response
.
hdr
.
offset
=
request
->
hdr
.
offset
;
response
.
hdr
.
opcode
=
QGCUASFileManager
::
kRspAck
;
_emitResponse
(
&
response
);
}
/// @brief Handles Terminate commands
void
MockMavlinkFileServer
::
_terminateCommand
(
QGCUASFileManager
::
Request
*
request
)
{
if
(
request
->
hdr
.
session
!=
_sessionId
)
{
_sendNak
(
QGCUASFileManager
::
kErrNoSession
);
return
;
}
_sendAck
();
// Let our test harness know that we got a terminate command. This is used to validate the a Terminate is correctly
// sent after an Open.
emit
terminateCommandReceived
();
}
/// @brief Handles messages sent to the FTP server.
void
MockMavlinkFileServer
::
sendMessage
(
mavlink_message_t
message
)
{
QGCUASFileManager
::
Request
ackResponse
;
Q_ASSERT
(
message
.
msgid
==
MAVLINK_MSG_ID_ENCAPSULATED_DATA
);
mavlink_encapsulated_data_t
requestEncapsulatedData
;
mavlink_msg_encapsulated_data_decode
(
&
message
,
&
requestEncapsulatedData
);
QGCUASFileManager
::
Request
*
request
=
(
QGCUASFileManager
::
Request
*
)
&
requestEncapsulatedData
.
data
[
0
];
// Validate CRC
if
(
request
->
hdr
.
crc32
!=
QGCUASFileManager
::
crc32
(
request
))
{
_sendNak
(
QGCUASFileManager
::
kErrCrc
);
}
switch
(
request
->
hdr
.
opcode
)
{
case
QGCUASFileManager
::
kCmdTestNoAck
:
// ignored, ack not sent back, for testing only
break
;
case
QGCUASFileManager
::
kCmdReset
:
// terminates all sessions
// Fall through to send back Ack
case
QGCUASFileManager
::
kCmdNone
:
// ignored, always acked
ackResponse
.
hdr
.
magic
=
'f'
;
ackResponse
.
hdr
.
opcode
=
QGCUASFileManager
::
kRspAck
;
ackResponse
.
hdr
.
session
=
0
;
ackResponse
.
hdr
.
crc32
=
0
;
ackResponse
.
hdr
.
size
=
0
;
_emitResponse
(
&
ackResponse
);
break
;
case
QGCUASFileManager
::
kCmdList
:
_listCommand
(
request
);
break
;
case
QGCUASFileManager
::
kCmdOpen
:
_openCommand
(
request
);
break
;
case
QGCUASFileManager
::
kCmdRead
:
_readCommand
(
request
);
break
;
case
QGCUASFileManager
::
kCmdTerminate
:
_terminateCommand
(
request
);
break
;
// Remainder of commands are NYI
case
QGCUASFileManager
::
kCmdCreate
:
// creates <path> for writing, returns <session>
case
QGCUASFileManager
::
kCmdWrite
:
// appends <size> bytes at <offset> in <session>
case
QGCUASFileManager
::
kCmdRemove
:
// remove file (only if created by server?)
default:
// nack for all NYI opcodes
_sendNak
(
QGCUASFileManager
::
kErrUnknownCommand
);
break
;
}
}
/// @brief Sends an Ack
void
MockMavlinkFileServer
::
_sendAck
(
void
)
{
QGCUASFileManager
::
Request
ackResponse
;
ackResponse
.
hdr
.
magic
=
'f'
;
ackResponse
.
hdr
.
opcode
=
QGCUASFileManager
::
kRspAck
;
ackResponse
.
hdr
.
session
=
0
;
ackResponse
.
hdr
.
size
=
0
;
_emitResponse
(
&
ackResponse
);
}
/// @brief Sends a Nak with the specified error code.
void
MockMavlinkFileServer
::
_sendNak
(
QGCUASFileManager
::
ErrorCode
error
)
{
QGCUASFileManager
::
Request
nakResponse
;
nakResponse
.
hdr
.
magic
=
'f'
;
nakResponse
.
hdr
.
opcode
=
QGCUASFileManager
::
kRspNak
;
nakResponse
.
hdr
.
session
=
0
;
nakResponse
.
hdr
.
size
=
1
;
nakResponse
.
data
[
0
]
=
error
;
_emitResponse
(
&
nakResponse
);
}
/// @brief Emits a Request through the messageReceived signal.
void
MockMavlinkFileServer
::
_emitResponse
(
QGCUASFileManager
::
Request
*
request
)
{
mavlink_message_t
mavlinkMessage
;
request
->
hdr
.
crc32
=
QGCUASFileManager
::
crc32
(
request
);
mavlink_msg_encapsulated_data_pack
(
250
,
0
,
&
mavlinkMessage
,
0
/*_encdata_seq*/
,
(
uint8_t
*
)
request
);
emit
messageReceived
(
NULL
,
mavlinkMessage
);
}
\ No newline at end of file
src/qgcunittest/MockMavlinkFileServer.h
0 → 100644
View file @
b485d7f6
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009 - 2014 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
This file is part of the QGROUNDCONTROL project
QGROUNDCONTROL 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.
QGROUNDCONTROL 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 QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
======================================================================*/
#ifndef MOCKMAVLINKFILESERVER_H
#define MOCKMAVLINKFILESERVER_H
#include
"MockMavlinkInterface.h"
#include
"QGCUASFileManager.h"
/// @file
/// @brief Mock implementation of Mavlink FTP server. Used as mavlink plugin to MockUAS.
/// Only root directory access is supported.
///
/// @author Don Gagne <don@thegagnes.com>
#include
<QStringList>
class
MockMavlinkFileServer
:
public
MockMavlinkInterface
{
Q_OBJECT
public:
MockMavlinkFileServer
(
void
);
/// @brief Sets the list of files returned by the List command. Prepend names with F or D
/// to indicate (F)ile or (D)irectory.
void
setFileList
(
QStringList
&
fileList
)
{
_fileList
=
fileList
;
}
// From MockMavlinkInterface
virtual
void
sendMessage
(
mavlink_message_t
message
);
struct
FileTestCase
{
const
char
*
filename
;
uint8_t
length
;
};
static
const
size_t
cFileTestCases
=
3
;
static
const
FileTestCase
rgFileTestCases
[
cFileTestCases
];
signals:
void
terminateCommandReceived
(
void
);
private:
void
_sendAck
(
void
);
void
_sendNak
(
QGCUASFileManager
::
ErrorCode
error
);
void
_emitResponse
(
QGCUASFileManager
::
Request
*
request
);
void
_listCommand
(
QGCUASFileManager
::
Request
*
request
);
void
_openCommand
(
QGCUASFileManager
::
Request
*
request
);
void
_readCommand
(
QGCUASFileManager
::
Request
*
request
);
void
_terminateCommand
(
QGCUASFileManager
::
Request
*
request
);
QStringList
_fileList
;
///< List of files returned by List command
static
const
uint8_t
_sessionId
;
uint8_t
_readFileLength
;
///< Length of active file being read
};
#endif
src/qgcunittest/MockMavlinkInterface.cc
0 → 100644
View file @
b485d7f6
//
// MockMavlinkInterface.cc
// QGroundControl
//
// Created by Donald Gagne on 6/19/14.
// Copyright (c) 2014 Donald Gagne. All rights reserved.
//
#include
"MockMavlinkInterface.h"
src/qgcunittest/MockMavlinkInterface.h
0 → 100644
View file @
b485d7f6
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009 - 2014 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
This file is part of the QGROUNDCONTROL project
QGROUNDCONTROL 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.
QGROUNDCONTROL 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 QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
======================================================================*/
#include
<QObject>
#include
"QGCMAVLink.h"
#include
"LinkInterface.h"
#ifndef MOCKMAVLINKINTERFACE_H
#define MOCKMAVLINKINTERFACE_H
class
MockMavlinkInterface
:
public
QObject
{
Q_OBJECT
public:
virtual
void
sendMessage
(
mavlink_message_t
message
)
=
0
;
signals:
// link argument will always be NULL
void
messageReceived
(
LinkInterface
*
link
,
mavlink_message_t
message
);
};
#endif
src/qgcunittest/MockUAS.cc
View file @
b485d7f6
...
...
@@ -27,7 +27,8 @@ QString MockUAS::_bogusStaticString;
MockUAS
::
MockUAS
(
void
)
:
_systemType
(
MAV_TYPE_QUADROTOR
),
_systemId
(
1
)
_systemId
(
1
),
_mavlinkPlugin
(
NULL
)
{
}
...
...
@@ -41,4 +42,13 @@ void MockUAS::setMockParametersAndSignal(MockQGCUASParamManager::ParamMap_t& map
i
.
next
();
emit
parameterChanged
(
_systemId
,
0
,
i
.
key
(),
i
.
value
());
}
}
void
MockUAS
::
sendMessage
(
mavlink_message_t
message
)
{
if
(
!
_mavlinkPlugin
)
{
Q_ASSERT
(
false
);
}
_mavlinkPlugin
->
sendMessage
(
message
);
}
\ No newline at end of file
src/qgcunittest/MockUAS.h
View file @
b485d7f6
...
...
@@ -26,6 +26,7 @@
#include
"UASInterface.h"
#include
"MockQGCUASParamManager.h"
#include
"MockMavlinkInterface.h"
#include
<limits>
...
...
@@ -50,6 +51,9 @@ public:
virtual
int
getUASID
(
void
)
const
{
return
_systemId
;
}
virtual
QGCUASParamManagerInterface
*
getParamManager
()
{
return
&
_paramManager
;
};
// sendMessage is only supported if a mavlink plugin is installed.
virtual
void
sendMessage
(
mavlink_message_t
message
);
public:
// MockUAS methods
MockUAS
(
void
);
...
...
@@ -63,12 +67,15 @@ public:
/// allows you to simulate parameter input and validate parameter setting
MockQGCUASParamManager
*
getMockQGCUASParamManager
(
void
)
{
return
&
_paramManager
;
}
/// Sets the parameter map into the mock QGCUASParamManager and signals parameterChanged for
///
@brief
Sets the parameter map into the mock QGCUASParamManager and signals parameterChanged for
/// each param
void
setMockParametersAndSignal
(
MockQGCUASParamManager
::
ParamMap_t
&
map
);
void
emitRemoteControlChannelRawChanged
(
int
channel
,
float
raw
)
{
emit
remoteControlChannelRawChanged
(
channel
,
raw
);
}
/// @brief Installs a mavlink plugin. Only a single mavlink plugin is supported at a time.
void
setMockMavlinkPlugin
(
MockMavlinkInterface
*
mavlinkPlugin
)
{
_mavlinkPlugin
=
mavlinkPlugin
;
};
public:
// Unimplemented UASInterface overrides
virtual
QString
getUASName
()
const
{
Q_ASSERT
(
false
);
return
_bogusString
;
};
...
...
@@ -109,6 +116,10 @@ public:
virtual
bool
systemCanReverse
()
const
{
Q_ASSERT
(
false
);
return
false
;
};
virtual
QString
getSystemTypeName
()
{
Q_ASSERT
(
false
);
return
_bogusString
;
};
virtual
int
getAutopilotType
()
{
Q_ASSERT
(
false
);
return
0
;
};
virtual
QGCUASFileManager
*
getFileManager
()
{
Q_ASSERT
(
false
);
return
NULL
;
}
/** @brief Send a message over this link (to this or to all UAS on this link) */
virtual
void
sendMessage
(
LinkInterface
*
link
,
mavlink_message_t
message
){
Q_UNUSED
(
link
);
Q_UNUSED
(
message
);
Q_ASSERT
(
false
);
}
virtual
QString
getAutopilotTypeName
()
{
Q_ASSERT
(
false
);
return
_bogusString
;
};
virtual
void
setAutopilotType
(
int
apType
)
{
Q_UNUSED
(
apType
);
Q_ASSERT
(
false
);
};
virtual
QMap
<
int
,
QString
>
getComponents
()
{
Q_ASSERT
(
false
);
return
_bogusMapIntQString
;
};
...
...
@@ -176,6 +187,8 @@ private:
int
_systemId
;
MockQGCUASParamManager
_paramManager
;
MockMavlinkInterface
*
_mavlinkPlugin
;
///< Mock Mavlink plugin, NULL for none
// Bogus variables used for return types of NYI methods
QString
_bogusString
;
...
...
src/qgcunittest/QGCUASFileManagerTest.cc
0 → 100644
View file @
b485d7f6
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009 - 2014 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
This file is part of the QGROUNDCONTROL project
QGROUNDCONTROL 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.
QGROUNDCONTROL 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 QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
======================================================================*/
#include
"QGCUASFileManagerTest.h"
/// @file
/// @brief QGCUASFileManager unit test. Note: All code here assumes all work between
/// the unit test, mack mavlink file server and file manager is happening on
/// the same thread.
///
/// @author Don Gagne <don@thegagnes.com>
QGCUASFileManagerUnitTest
::
QGCUASFileManagerUnitTest
(
void
)
:
_fileManager
(
NULL
),
_multiSpy
(
NULL
)
{
}
// Called once before all test cases are run
void
QGCUASFileManagerUnitTest
::
initTestCase
(
void
)
{
_mockUAS
.
setMockMavlinkPlugin
(
&
_mockFileServer
);
}
// Called before every test case
void
QGCUASFileManagerUnitTest
::
init
(
void
)
{
Q_ASSERT
(
_multiSpy
==
NULL
);
_fileManager
=
new
QGCUASFileManager
(
NULL
,
&
_mockUAS
);
Q_CHECK_PTR
(
_fileManager
);
bool
connected
=
connect
(
&
_mockFileServer
,
SIGNAL
(
messageReceived
(
LinkInterface
*
,
mavlink_message_t
)),
_fileManager
,
SLOT
(
receiveMessage
(
LinkInterface
*
,
mavlink_message_t
)));
Q_ASSERT
(
connected
);
connected
=
connect
(
_fileManager
,
SIGNAL
(
statusMessage
(
const
QString
&
)),
this
,
SLOT
(
statusMessage
(
const
QString
&
)));
Q_ASSERT
(
connected
);
_rgSignals
[
statusMessageSignalIndex
]
=
SIGNAL
(
statusMessage
(
const
QString
&
));
_rgSignals
[
errorMessageSignalIndex
]
=
SIGNAL
(
errorMessage
(
const
QString
&
));
_rgSignals
[
resetStatusMessagesSignalIndex
]
=
SIGNAL
(
resetStatusMessages
(
void
));
_multiSpy
=
new
MultiSignalSpy
();
Q_CHECK_PTR
(
_multiSpy
);
QCOMPARE
(
_multiSpy
->
init
(
_fileManager
,
_rgSignals
,
_cSignals
),
true
);
}
// Called after every test case
void
QGCUASFileManagerUnitTest
::
cleanup
(
void
)
{
Q_ASSERT
(
_multiSpy
);
Q_ASSERT
(
_fileManager
);
delete
_fileManager
;
delete
_multiSpy
;
_fileManager
=
NULL
;
_multiSpy
=
NULL
;
}
/// @brief Connected to QGCUASFileManager statusMessage signal in order to catch list command output
void
QGCUASFileManagerUnitTest
::
statusMessage
(
const
QString
&
msg
)
{
// Keep a list of all names received so we can test it for correctness
_fileListReceived
+=
msg
;
}
void
QGCUASFileManagerUnitTest
::
_ackTest
(
void
)
{
Q_ASSERT
(
_fileManager
);
Q_ASSERT
(
_multiSpy
);
Q_ASSERT
(
_multiSpy
->
checkNoSignals
()
==
true
);
// If the file manager doesn't receive an ack it will timeout and emit an error. So make sure
// we don't get any error signals.
QVERIFY
(
_fileManager
->
_sendCmdTestAck
());
QVERIFY
(
_multiSpy
->
checkNoSignals
());
}
void
QGCUASFileManagerUnitTest
::
_noAckTest
(
void
)
{
Q_ASSERT
(
_fileManager
);
Q_ASSERT
(
_multiSpy
);
Q_ASSERT
(
_multiSpy
->
checkNoSignals
()
==
true
);
// This should not get the ack back and timeout.
QVERIFY
(
_fileManager
->
_sendCmdTestNoAck
());
QTest
::
qWait
(
2000
);
// Let the file manager timeout, magic number 2 secs must be larger than file manager ack timeout
QCOMPARE
(
_multiSpy
->
checkOnlySignalByMask
(
errorMessageSignalMask
),
true
);
}
void
QGCUASFileManagerUnitTest
::
_resetTest
(
void
)
{
Q_ASSERT
(
_fileManager
);
Q_ASSERT
(
_multiSpy
);
Q_ASSERT
(
_multiSpy
->
checkNoSignals
()
==
true
);
// Send a reset command
// We should not get any signals back from this
QVERIFY
(
_fileManager
->
_sendCmdReset
());
QVERIFY
(
_multiSpy
->
checkNoSignals
());
}
void
QGCUASFileManagerUnitTest
::
_listTest
(
void
)
{
Q_ASSERT
(
_fileManager
);
Q_ASSERT
(
_multiSpy
);
Q_ASSERT
(
_multiSpy
->
checkNoSignals
()
==
true
);
// Send a bogus path
// We should get a single resetStatusMessages signal
// We should get a single errorMessage signal
_fileManager
->
listDirectory
(
"/bogus"
);
QCOMPARE
(
_multiSpy
->
checkOnlySignalByMask
(
errorMessageSignalMask
|
resetStatusMessagesSignalMask
),
true
);
_multiSpy
->
clearAllSignals
();
// Send a list command at the root of the directory tree
// We should get back a single resetStatusMessages signal
// We should not get back an errorMessage signal
// We should get back one or more statusMessage signals
// The returned list should match out inputs
QStringList
fileList
;
fileList
<<
"Ddir"
<<
"Ffoo"
<<
"Fbar"
;
_mockFileServer
.
setFileList
(
fileList
);
_fileListReceived
.
clear
();
_fileManager
->
listDirectory
(
"/"
);
QCOMPARE
(
_multiSpy
->
checkSignalByMask
(
resetStatusMessagesSignalMask
),
true
);
// We should be told to reset status messages
QCOMPARE
(
_multiSpy
->
checkNoSignalByMask
(
errorMessageSignalMask
),
true
);
// We should not get an error signals
QVERIFY
(
_fileListReceived
==
fileList
);
}
void
QGCUASFileManagerUnitTest
::
_validateFileContents
(
const
QString
&
filePath
,
uint8_t
length
)
{
QFile
file
(
filePath
);
// Make sure file size is correct
QCOMPARE
(
file
.
size
(),
(
qint64
)
length
);
// Read data
QVERIFY
(
file
.
open
(
QIODevice
::
ReadOnly
));
QByteArray
bytes
=
file
.
readAll
();
file
.
close
();
// Validate length byte
QCOMPARE
((
uint8_t
)
bytes
[
0
],
length
);
// Validate file contents:
// Repeating 0x00, 0x01 .. 0xFF until file is full
for
(
uint8_t
i
=
1
;
i
<
bytes
.
length
();
i
++
)
{
QCOMPARE
((
uint8_t
)
bytes
[
i
],
(
uint8_t
)((
i
-
1
)
&
0xFF
));
}
}
void
QGCUASFileManagerUnitTest
::
_openTest
(
void
)
{
Q_ASSERT
(
_fileManager
);
Q_ASSERT
(
_multiSpy
);
Q_ASSERT
(
_multiSpy
->
checkNoSignals
()
==
true
);
// Send a bogus path
// We should get a single resetStatusMessages signal
// We should get a single errorMessage signal
_fileManager
->
downloadPath
(
"bogus"
,
QDir
::
temp
());
QCOMPARE
(
_multiSpy
->
checkOnlySignalByMask
(
errorMessageSignalMask
|
resetStatusMessagesSignalMask
),
true
);
_multiSpy
->
clearAllSignals
();
// Clean previous downloads
for
(
size_t
i
=
0
;
i
<
MockMavlinkFileServer
::
cFileTestCases
;
i
++
)
{
QString
filePath
=
QDir
::
temp
().
absoluteFilePath
(
MockMavlinkFileServer
::
rgFileTestCases
[
i
].
filename
);
if
(
QFile
::
exists
(
filePath
))
{
Q_ASSERT
(
QFile
::
remove
(
filePath
));
}
}
// Run through the set of file test cases
// We setup a spy on the terminate command signal so that we can determine that a Terminate command was
// correctly sent after the Open/Read commands complete.
QSignalSpy
terminateSpy
(
&
_mockFileServer
,
SIGNAL
(
terminateCommandReceived
()));
for
(
size_t
i
=
0
;
i
<
MockMavlinkFileServer
::
cFileTestCases
;
i
++
)
{
_fileManager
->
downloadPath
(
MockMavlinkFileServer
::
rgFileTestCases
[
i
].
filename
,
QDir
::
temp
());
// We should get a single resetStatusMessages signal
// We should get a single statusMessage signal, which indicated download completion
QCOMPARE
(
_multiSpy
->
checkOnlySignalByMask
(
statusMessageSignalMask
|
resetStatusMessagesSignalMask
),
true
);
_multiSpy
->
clearAllSignals
();
// We should get a single Terminate command
QCOMPARE
(
terminateSpy
.
count
(),
1
);
terminateSpy
.
clear
();
QString
filePath
=
QDir
::
temp
().
absoluteFilePath
(
MockMavlinkFileServer
::
rgFileTestCases
[
i
].
filename
);
_validateFileContents
(
filePath
,
MockMavlinkFileServer
::
rgFileTestCases
[
i
].
length
);
}
}
src/qgcunittest/QGCUASFileManagerTest.h
0 → 100644
View file @
b485d7f6
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009 - 2014 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
This file is part of the QGROUNDCONTROL project
QGROUNDCONTROL 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.
QGROUNDCONTROL 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 QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
======================================================================*/
#ifndef QGCUASFILEMANAGERTEST_H
#define QGCUASFILEMANAGERTEST_H
#include
<QObject>
#include
<QtTest/QtTest>
#include
"AutoTest.h"
#include
"MockUAS.h"
#include
"MockMavlinkFileServer.h"
#include
"QGCUASFileManager.h"
#include
"MultiSignalSpy.h"
/// @file
/// @brief QGCUASFileManager unit test
///
/// @author Don Gagne <don@thegagnes.com>
class
QGCUASFileManagerUnitTest
:
public
QObject
{
Q_OBJECT
public:
QGCUASFileManagerUnitTest
(
void
);
private
slots
:
// Test case initialization
void
initTestCase
(
void
);
void
init
(
void
);
void
cleanup
(
void
);
// Test cases
void
_ackTest
(
void
);
void
_noAckTest
(
void
);
void
_resetTest
(
void
);
void
_listTest
(
void
);
void
_openTest
(
void
);
// Connected to QGCUASFileManager statusMessage signal
void
statusMessage
(
const
QString
&
);
private:
void
_validateFileContents
(
const
QString
&
filePath
,
uint8_t
length
);
enum
{
statusMessageSignalIndex
=
0
,
errorMessageSignalIndex
,
resetStatusMessagesSignalIndex
,
maxSignalIndex
};
enum
{
statusMessageSignalMask
=
1
<<
statusMessageSignalIndex
,
errorMessageSignalMask
=
1
<<
errorMessageSignalIndex
,
resetStatusMessagesSignalMask
=
1
<<
resetStatusMessagesSignalIndex
,
};
MockUAS
_mockUAS
;
MockMavlinkFileServer
_mockFileServer
;
QGCUASFileManager
*
_fileManager
;
MultiSignalSpy
*
_multiSpy
;
static
const
size_t
_cSignals
=
maxSignalIndex
;
const
char
*
_rgSignals
[
_cSignals
];
QStringList
_fileListReceived
;
};
DECLARE_TEST
(
QGCUASFileManagerUnitTest
)
#endif
src/uas/QGCMAVLinkUASFactory.cc
View file @
b485d7f6
...
...
@@ -35,6 +35,7 @@ UASInterface* QGCMAVLinkUASFactory::createUAS(MAVLinkProtocol* mavlink, LinkInte
// Connect this robot to the UAS object
connect
(
mavlink
,
SIGNAL
(
messageReceived
(
LinkInterface
*
,
mavlink_message_t
)),
mav
,
SLOT
(
receiveMessage
(
LinkInterface
*
,
mavlink_message_t
)));
connect
(
mavlink
,
SIGNAL
(
messageReceived
(
LinkInterface
*
,
mavlink_message_t
)),
mav
->
getFileManager
(),
SLOT
(
receiveMessage
(
LinkInterface
*
,
mavlink_message_t
)));
#ifdef QGC_PROTOBUF_ENABLED
connect
(
mavlink
,
SIGNAL
(
extendedMessageReceived
(
LinkInterface
*
,
std
::
tr1
::
shared_ptr
<
google
::
protobuf
::
Message
>
)),
mav
,
SLOT
(
receiveExtendedMessage
(
LinkInterface
*
,
std
::
tr1
::
shared_ptr
<
google
::
protobuf
::
Message
>
)));
#endif
...
...
src/uas/QGCUASFileManager.cc
0 → 100644
View file @
b485d7f6
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009 - 2014 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
This file is part of the QGROUNDCONTROL project
QGROUNDCONTROL 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.
QGROUNDCONTROL 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 QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
======================================================================*/
#include
"QGCUASFileManager.h"
#include
"QGC.h"
#include
"MAVLinkProtocol.h"
#include
<QFile>
#include
<QDir>
#include
<string>
static
const
quint32
crctab
[]
=
{
0x00000000
,
0x77073096
,
0xee0e612c
,
0x990951ba
,
0x076dc419
,
0x706af48f
,
0xe963a535
,
0x9e6495a3
,
0x0edb8832
,
0x79dcb8a4
,
0xe0d5e91e
,
0x97d2d988
,
0x09b64c2b
,
0x7eb17cbd
,
0xe7b82d07
,
0x90bf1d91
,
0x1db71064
,
0x6ab020f2
,
0xf3b97148
,
0x84be41de
,
0x1adad47d
,
0x6ddde4eb
,
0xf4d4b551
,
0x83d385c7
,
0x136c9856
,
0x646ba8c0
,
0xfd62f97a
,
0x8a65c9ec
,
0x14015c4f
,
0x63066cd9
,
0xfa0f3d63
,
0x8d080df5
,
0x3b6e20c8
,
0x4c69105e
,
0xd56041e4
,
0xa2677172
,
0x3c03e4d1
,
0x4b04d447
,
0xd20d85fd
,
0xa50ab56b
,
0x35b5a8fa
,
0x42b2986c
,
0xdbbbc9d6
,
0xacbcf940
,
0x32d86ce3
,
0x45df5c75
,
0xdcd60dcf
,
0xabd13d59
,
0x26d930ac
,
0x51de003a
,
0xc8d75180
,
0xbfd06116
,
0x21b4f4b5
,
0x56b3c423
,
0xcfba9599
,
0xb8bda50f
,
0x2802b89e
,
0x5f058808
,
0xc60cd9b2
,
0xb10be924
,
0x2f6f7c87
,
0x58684c11
,
0xc1611dab
,
0xb6662d3d
,
0x76dc4190
,
0x01db7106
,
0x98d220bc
,
0xefd5102a
,
0x71b18589
,
0x06b6b51f
,
0x9fbfe4a5
,
0xe8b8d433
,
0x7807c9a2
,
0x0f00f934
,
0x9609a88e
,
0xe10e9818
,
0x7f6a0dbb
,
0x086d3d2d
,
0x91646c97
,
0xe6635c01
,
0x6b6b51f4
,
0x1c6c6162
,
0x856530d8
,
0xf262004e
,
0x6c0695ed
,
0x1b01a57b
,
0x8208f4c1
,
0xf50fc457
,
0x65b0d9c6
,
0x12b7e950
,
0x8bbeb8ea
,
0xfcb9887c
,
0x62dd1ddf
,
0x15da2d49
,
0x8cd37cf3
,
0xfbd44c65
,
0x4db26158
,
0x3ab551ce
,
0xa3bc0074
,
0xd4bb30e2
,
0x4adfa541
,
0x3dd895d7
,
0xa4d1c46d
,
0xd3d6f4fb
,
0x4369e96a
,
0x346ed9fc
,
0xad678846
,
0xda60b8d0
,
0x44042d73
,
0x33031de5
,
0xaa0a4c5f
,
0xdd0d7cc9
,
0x5005713c
,
0x270241aa
,
0xbe0b1010
,
0xc90c2086
,
0x5768b525
,
0x206f85b3
,
0xb966d409
,
0xce61e49f
,
0x5edef90e
,
0x29d9c998
,
0xb0d09822
,
0xc7d7a8b4
,
0x59b33d17
,
0x2eb40d81
,
0xb7bd5c3b
,
0xc0ba6cad
,
0xedb88320
,
0x9abfb3b6
,
0x03b6e20c
,
0x74b1d29a
,
0xead54739
,
0x9dd277af
,
0x04db2615
,
0x73dc1683
,
0xe3630b12
,
0x94643b84
,
0x0d6d6a3e
,
0x7a6a5aa8
,
0xe40ecf0b
,
0x9309ff9d
,
0x0a00ae27
,
0x7d079eb1
,
0xf00f9344
,
0x8708a3d2
,
0x1e01f268
,
0x6906c2fe
,
0xf762575d
,
0x806567cb
,
0x196c3671
,
0x6e6b06e7
,
0xfed41b76
,
0x89d32be0
,
0x10da7a5a
,
0x67dd4acc
,
0xf9b9df6f
,
0x8ebeeff9
,
0x17b7be43
,
0x60b08ed5
,
0xd6d6a3e8
,
0xa1d1937e
,
0x38d8c2c4
,
0x4fdff252
,
0xd1bb67f1
,
0xa6bc5767
,
0x3fb506dd
,
0x48b2364b
,
0xd80d2bda
,
0xaf0a1b4c
,
0x36034af6
,
0x41047a60
,
0xdf60efc3
,
0xa867df55
,
0x316e8eef
,
0x4669be79
,
0xcb61b38c
,
0xbc66831a
,
0x256fd2a0
,
0x5268e236
,
0xcc0c7795
,
0xbb0b4703
,
0x220216b9
,
0x5505262f
,
0xc5ba3bbe
,
0xb2bd0b28
,
0x2bb45a92
,
0x5cb36a04
,
0xc2d7ffa7
,
0xb5d0cf31
,
0x2cd99e8b
,
0x5bdeae1d
,
0x9b64c2b0
,
0xec63f226
,
0x756aa39c
,
0x026d930a
,
0x9c0906a9
,
0xeb0e363f
,
0x72076785
,
0x05005713
,
0x95bf4a82
,
0xe2b87a14
,
0x7bb12bae
,
0x0cb61b38
,
0x92d28e9b
,
0xe5d5be0d
,
0x7cdcefb7
,
0x0bdbdf21
,
0x86d3d2d4
,
0xf1d4e242
,
0x68ddb3f8
,
0x1fda836e
,
0x81be16cd
,
0xf6b9265b
,
0x6fb077e1
,
0x18b74777
,
0x88085ae6
,
0xff0f6a70
,
0x66063bca
,
0x11010b5c
,
0x8f659eff
,
0xf862ae69
,
0x616bffd3
,
0x166ccf45
,
0xa00ae278
,
0xd70dd2ee
,
0x4e048354
,
0x3903b3c2
,
0xa7672661
,
0xd06016f7
,
0x4969474d
,
0x3e6e77db
,
0xaed16a4a
,
0xd9d65adc
,
0x40df0b66
,
0x37d83bf0
,
0xa9bcae53
,
0xdebb9ec5
,
0x47b2cf7f
,
0x30b5ffe9
,
0xbdbdf21c
,
0xcabac28a
,
0x53b39330
,
0x24b4a3a6
,
0xbad03605
,
0xcdd70693
,
0x54de5729
,
0x23d967bf
,
0xb3667a2e
,
0xc4614ab8
,
0x5d681b02
,
0x2a6f2b94
,
0xb40bbe37
,
0xc30c8ea1
,
0x5a05df1b
,
0x2d02ef8d
};
QGCUASFileManager
::
QGCUASFileManager
(
QObject
*
parent
,
UASInterface
*
uas
)
:
QObject
(
parent
),
_currentOperation
(
kCOIdle
),
_mav
(
uas
),
_encdata_seq
(
0
),
_activeSession
(
0
)
{
bool
connected
=
connect
(
&
_ackTimer
,
SIGNAL
(
timeout
()),
this
,
SLOT
(
_ackTimeout
()));
Q_ASSERT
(
connected
);
Q_UNUSED
(
connected
);
// Silence retail unused variable error
}
/// @brief Calculates a 32 bit CRC for the specified request.
/// @param request Request to calculate CRC for. request->size must be set correctly.
/// @param state previous crc state
/// @return Calculated CRC
quint32
QGCUASFileManager
::
crc32
(
Request
*
request
,
unsigned
state
)
{
uint8_t
*
data
=
(
uint8_t
*
)
request
;
size_t
cbData
=
sizeof
(
RequestHeader
)
+
request
->
hdr
.
size
;
// Always calculate CRC with 0 initial CRC value
quint32
crcSave
=
request
->
hdr
.
crc32
;
request
->
hdr
.
crc32
=
0
;
for
(
size_t
i
=
0
;
i
<
cbData
;
i
++
)
state
=
crctab
[(
state
^
data
[
i
])
&
0xff
]
^
(
state
>>
8
);
request
->
hdr
.
crc32
=
crcSave
;
return
state
;
}
/// @brief Respond to the Ack associated with the Open command with the next Read command.
void
QGCUASFileManager
::
_openAckResponse
(
Request
*
openAck
)
{
_currentOperation
=
kCORead
;
_activeSession
=
openAck
->
hdr
.
session
;
_readOffset
=
0
;
// Start reading at beginning of file
_readFileAccumulator
.
clear
();
// Start with an empty file
Request
request
;
request
.
hdr
.
magic
=
'f'
;
request
.
hdr
.
session
=
_activeSession
;
request
.
hdr
.
opcode
=
kCmdRead
;
request
.
hdr
.
offset
=
_readOffset
;
request
.
hdr
.
size
=
sizeof
(
request
.
data
);
_sendRequest
(
&
request
);
}
/// @brief Closes out a read session by writing the file and doing cleanup.
/// @param success true: successful download completion, false: error during download
void
QGCUASFileManager
::
_closeReadSession
(
bool
success
)
{
if
(
success
)
{
QString
downloadFilePath
=
_readFileDownloadDir
.
absoluteFilePath
(
_readFileDownloadFilename
);
QFile
file
(
downloadFilePath
);
if
(
!
file
.
open
(
QIODevice
::
WriteOnly
|
QIODevice
::
Truncate
))
{
_emitErrorMessage
(
tr
(
"Unable to open local file for writing (%1)"
).
arg
(
downloadFilePath
));
return
;
}
qint64
bytesWritten
=
file
.
write
((
const
char
*
)
_readFileAccumulator
,
_readFileAccumulator
.
length
());
if
(
bytesWritten
!=
_readFileAccumulator
.
length
())
{
file
.
close
();
_emitErrorMessage
(
tr
(
"Unable to write data to local file (%1)"
).
arg
(
downloadFilePath
));
return
;
}
file
.
close
();
_emitStatusMessage
(
tr
(
"Download complete '%1'"
).
arg
(
downloadFilePath
));
}
// Close the open session
_sendTerminateCommand
();
}
/// @brief Respond to the Ack associated with the Read command.
void
QGCUASFileManager
::
_readAckResponse
(
Request
*
readAck
)
{
if
(
readAck
->
hdr
.
session
!=
_activeSession
)
{
_currentOperation
=
kCOIdle
;
_readFileAccumulator
.
clear
();
_emitErrorMessage
(
tr
(
"Read: Incorrect session returned"
));
return
;
}
if
(
readAck
->
hdr
.
offset
!=
_readOffset
)
{
_currentOperation
=
kCOIdle
;
_readFileAccumulator
.
clear
();
_emitErrorMessage
(
tr
(
"Read: Offset returned (%1) differs from offset requested (%2)"
).
arg
(
readAck
->
hdr
.
offset
).
arg
(
_readOffset
));
return
;
}
_readFileAccumulator
.
append
((
const
char
*
)
readAck
->
data
,
readAck
->
hdr
.
size
);
if
(
readAck
->
hdr
.
size
==
sizeof
(
readAck
->
data
))
{
// Possibly still more data to read, send next read request
_currentOperation
=
kCORead
;
_readOffset
+=
readAck
->
hdr
.
size
;
Request
request
;
request
.
hdr
.
magic
=
'f'
;
request
.
hdr
.
session
=
_activeSession
;
request
.
hdr
.
opcode
=
kCmdRead
;
request
.
hdr
.
offset
=
_readOffset
;
_sendRequest
(
&
request
);
}
else
{
// We only receieved a partial buffer back. These means we are at EOF
_currentOperation
=
kCOIdle
;
_closeReadSession
(
true
/* success */
);
}
}
/// @brief Respond to the Ack associated with the List command.
void
QGCUASFileManager
::
_listAckResponse
(
Request
*
listAck
)
{
if
(
listAck
->
hdr
.
offset
!=
_listOffset
)
{
_currentOperation
=
kCOIdle
;
_emitErrorMessage
(
tr
(
"List: Offset returned (%1) differs from offset requested (%2)"
).
arg
(
listAck
->
hdr
.
offset
).
arg
(
_listOffset
));
return
;
}
uint8_t
offset
=
0
;
uint8_t
cListEntries
=
0
;
uint8_t
cBytes
=
listAck
->
hdr
.
size
;
// parse filenames out of the buffer
while
(
offset
<
cBytes
)
{
const
char
*
ptr
=
((
const
char
*
)
listAck
->
data
)
+
offset
;
// get the length of the name
uint8_t
cBytesLeft
=
cBytes
-
offset
;
size_t
nlen
=
strnlen
(
ptr
,
cBytesLeft
);
if
(
nlen
<
2
)
{
_currentOperation
=
kCOIdle
;
_emitErrorMessage
(
tr
(
"Incorrectly formed list entry: '%1'"
).
arg
(
ptr
));
return
;
}
else
if
(
nlen
==
cBytesLeft
)
{
_currentOperation
=
kCOIdle
;
_emitErrorMessage
(
tr
(
"Missing NULL termination in list entry"
));
return
;
}
// Returned names are prepended with D for directory, F for file, U for unknown
if
(
*
ptr
==
'F'
||
*
ptr
==
'D'
)
{
// put it in the view
_emitStatusMessage
(
ptr
);
}
// account for the name + NUL
offset
+=
nlen
+
1
;
cListEntries
++
;
}
if
(
listAck
->
hdr
.
size
==
0
)
{
// Directory is empty, we're done
Q_ASSERT
(
listAck
->
hdr
.
opcode
==
kRspAck
);
_currentOperation
=
kCOIdle
;
emit
listComplete
();
}
else
{
// Possibly more entries to come, need to keep trying till we get EOF
_currentOperation
=
kCOList
;
_listOffset
+=
cListEntries
;
_sendListCommand
();
}
}
void
QGCUASFileManager
::
receiveMessage
(
LinkInterface
*
link
,
mavlink_message_t
message
)
{
Q_UNUSED
(
link
);
if
(
message
.
msgid
!=
MAVLINK_MSG_ID_ENCAPSULATED_DATA
)
{
// wtf, not for us
return
;
}
_clearAckTimeout
();
mavlink_encapsulated_data_t
data
;
mavlink_msg_encapsulated_data_decode
(
&
message
,
&
data
);
Request
*
request
=
(
Request
*
)
&
data
.
data
[
0
];
// FIXME: Check CRC
if
(
request
->
hdr
.
opcode
==
kRspAck
)
{
switch
(
_currentOperation
)
{
case
kCOIdle
:
// we should not be seeing anything here.. shut the other guy up
_sendCmdReset
();
break
;
case
kCOAck
:
// We are expecting an ack back
_currentOperation
=
kCOIdle
;
break
;
case
kCOList
:
_listAckResponse
(
request
);
break
;
case
kCOOpen
:
_openAckResponse
(
request
);
break
;
case
kCORead
:
_readAckResponse
(
request
);
break
;
default:
_emitErrorMessage
(
"Ack received in unexpected state"
);
break
;
}
}
else
if
(
request
->
hdr
.
opcode
==
kRspNak
)
{
Q_ASSERT
(
request
->
hdr
.
size
==
1
);
// Should only have one byte of error code
OperationState
previousOperation
=
_currentOperation
;
uint8_t
errorCode
=
request
->
data
[
0
];
_currentOperation
=
kCOIdle
;
if
(
previousOperation
==
kCOList
&&
errorCode
==
kErrEOF
)
{
// This is not an error, just the end of the read loop
emit
listComplete
();
return
;
}
else
if
(
previousOperation
==
kCORead
&&
errorCode
==
kErrEOF
)
{
// This is not an error, just the end of the read loop
_closeReadSession
(
true
/* success */
);
return
;
}
else
{
// Generic Nak handling
if
(
previousOperation
==
kCORead
)
{
// Nak error during read loop, download failed
_closeReadSession
(
false
/* failure */
);
}
_emitErrorMessage
(
tr
(
"Nak received, error: %1"
).
arg
(
errorString
(
request
->
data
[
0
])));
}
}
else
{
// Note that we don't change our operation state. If something goes wrong beyond this, the operation
// will time out.
_emitErrorMessage
(
tr
(
"Unknown opcode returned from server: %1"
).
arg
(
request
->
hdr
.
opcode
));
}
}
void
QGCUASFileManager
::
listDirectory
(
const
QString
&
dirPath
)
{
if
(
_currentOperation
!=
kCOIdle
)
{
_emitErrorMessage
(
tr
(
"Command not sent. Waiting for previous command to complete."
));
return
;
}
// clear the text widget
emit
resetStatusMessages
();
// initialise the lister
_listPath
=
dirPath
;
_listOffset
=
0
;
_currentOperation
=
kCOList
;
// and send the initial request
_sendListCommand
();
}
void
QGCUASFileManager
::
_fillRequestWithString
(
Request
*
request
,
const
QString
&
str
)
{
strncpy
((
char
*
)
&
request
->
data
[
0
],
str
.
toStdString
().
c_str
(),
sizeof
(
request
->
data
));
request
->
hdr
.
size
=
strnlen
((
const
char
*
)
&
request
->
data
[
0
],
sizeof
(
request
->
data
));
}
void
QGCUASFileManager
::
_sendListCommand
(
void
)
{
Request
request
;
request
.
hdr
.
magic
=
'f'
;
request
.
hdr
.
session
=
0
;
request
.
hdr
.
opcode
=
kCmdList
;
request
.
hdr
.
offset
=
_listOffset
;
_fillRequestWithString
(
&
request
,
_listPath
);
_sendRequest
(
&
request
);
}
/// @brief Downloads the specified file.
/// @param from File to download from UAS, fully qualified path
/// @param downloadDir Local directory to download file to
void
QGCUASFileManager
::
downloadPath
(
const
QString
&
from
,
const
QDir
&
downloadDir
)
{
if
(
from
.
isEmpty
())
{
return
;
}
_readFileDownloadDir
.
setPath
(
downloadDir
.
absolutePath
());
// We need to strip off the file name from the fully qualified path. We can't use the usual QDir
// routines because this path does not exist locally.
int
i
;
for
(
i
=
from
.
size
()
-
1
;
i
>=
0
;
i
--
)
{
if
(
from
[
i
]
==
'/'
)
{
break
;
}
}
i
++
;
// move past slash
_readFileDownloadFilename
=
from
.
right
(
from
.
size
()
-
i
);
emit
resetStatusMessages
();
_currentOperation
=
kCOOpen
;
Request
request
;
request
.
hdr
.
magic
=
'f'
;
request
.
hdr
.
session
=
0
;
request
.
hdr
.
opcode
=
kCmdOpen
;
request
.
hdr
.
offset
=
0
;
_fillRequestWithString
(
&
request
,
from
);
_sendRequest
(
&
request
);
}
QString
QGCUASFileManager
::
errorString
(
uint8_t
errorCode
)
{
switch
(
errorCode
)
{
case
kErrNone
:
return
QString
(
"no error"
);
case
kErrNoRequest
:
return
QString
(
"bad request"
);
case
kErrNoSession
:
return
QString
(
"bad session"
);
case
kErrSequence
:
return
QString
(
"bad sequence number"
);
case
kErrNotDir
:
return
QString
(
"not a directory"
);
case
kErrNotFile
:
return
QString
(
"not a file"
);
case
kErrEOF
:
return
QString
(
"read beyond end of file"
);
case
kErrNotAppend
:
return
QString
(
"write not at end of file"
);
case
kErrTooBig
:
return
QString
(
"file too big"
);
case
kErrIO
:
return
QString
(
"device I/O error"
);
case
kErrPerm
:
return
QString
(
"permission denied"
);
case
kErrUnknownCommand
:
return
QString
(
"unknown command"
);
case
kErrCrc
:
return
QString
(
"bad crc"
);
default:
return
QString
(
"unknown error code"
);
}
}
/// @brief Sends a command which only requires an opcode and no additional data
/// @param opcode Opcode to send
/// @param newOpState State to put state machine into
/// @return TRUE: command sent, FALSE: command not sent, waiting for previous command to finish
bool
QGCUASFileManager
::
_sendOpcodeOnlyCmd
(
uint8_t
opcode
,
OperationState
newOpState
)
{
if
(
_currentOperation
!=
kCOIdle
)
{
// Can't have multiple commands in play at the same time
return
false
;
}
Request
request
;
request
.
hdr
.
magic
=
'f'
;
request
.
hdr
.
session
=
0
;
request
.
hdr
.
opcode
=
opcode
;
request
.
hdr
.
offset
=
0
;
request
.
hdr
.
size
=
0
;
_currentOperation
=
newOpState
;
_sendRequest
(
&
request
);
return
TRUE
;
}
/// @brief Starts the ack timeout timer
void
QGCUASFileManager
::
_setupAckTimeout
(
void
)
{
Q_ASSERT
(
!
_ackTimer
.
isActive
());
_ackTimer
.
setSingleShot
(
true
);
_ackTimer
.
start
(
_ackTimerTimeoutMsecs
);
}
/// @brief Clears the ack timeout timer
void
QGCUASFileManager
::
_clearAckTimeout
(
void
)
{
Q_ASSERT
(
_ackTimer
.
isActive
());
_ackTimer
.
stop
();
}
/// @brief Called when ack timeout timer fires
void
QGCUASFileManager
::
_ackTimeout
(
void
)
{
// Make sure to set _currentOperation state before emitting error message. Code may respond
// to error message signal by sending another command, which will fail if state is not back
// to idle. FileView UI works this way with the List command.
switch
(
_currentOperation
)
{
case
kCORead
:
_currentOperation
=
kCOAck
;
_emitErrorMessage
(
tr
(
"Timeout waiting for ack: Sending Terminate command"
));
_sendTerminateCommand
();
break
;
default:
_currentOperation
=
kCOIdle
;
_emitErrorMessage
(
tr
(
"Timeout waiting for ack"
));
break
;
}
}
void
QGCUASFileManager
::
_sendTerminateCommand
(
void
)
{
Request
request
;
request
.
hdr
.
magic
=
'f'
;
request
.
hdr
.
session
=
_activeSession
;
request
.
hdr
.
opcode
=
kCmdTerminate
;
_sendRequest
(
&
request
);
}
void
QGCUASFileManager
::
_emitErrorMessage
(
const
QString
&
msg
)
{
qDebug
()
<<
"QGCUASFileManager: Error"
<<
msg
;
emit
errorMessage
(
msg
);
}
void
QGCUASFileManager
::
_emitStatusMessage
(
const
QString
&
msg
)
{
qDebug
()
<<
"QGCUASFileManager: Status"
<<
msg
;
emit
statusMessage
(
msg
);
}
/// @brief Sends the specified Request out to the UAS.
void
QGCUASFileManager
::
_sendRequest
(
Request
*
request
)
{
mavlink_message_t
message
;
_setupAckTimeout
();
request
->
hdr
.
crc32
=
crc32
(
request
);
// FIXME: Send correct system id instead of harcoded 250
mavlink_msg_encapsulated_data_pack
(
250
,
0
,
&
message
,
_encdata_seq
,
(
uint8_t
*
)
request
);
_mav
->
sendMessage
(
message
);
}
src/uas/QGCUASFileManager.h
0 → 100644
View file @
b485d7f6
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009 - 2014 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
This file is part of the QGROUNDCONTROL project
QGROUNDCONTROL 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.
QGROUNDCONTROL 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 QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
======================================================================*/
#ifndef QGCUASFILEMANAGER_H
#define QGCUASFILEMANAGER_H
#include
<QObject>
#include
<QDir>
#include
"UASInterface.h"
class
QGCUASFileManager
:
public
QObject
{
Q_OBJECT
public:
QGCUASFileManager
(
QObject
*
parent
,
UASInterface
*
uas
);
/// These methods are only used for testing purposes.
bool
_sendCmdTestAck
(
void
)
{
return
_sendOpcodeOnlyCmd
(
kCmdNone
,
kCOAck
);
};
bool
_sendCmdTestNoAck
(
void
)
{
return
_sendOpcodeOnlyCmd
(
kCmdTestNoAck
,
kCOAck
);
};
bool
_sendCmdReset
(
void
)
{
return
_sendOpcodeOnlyCmd
(
kCmdReset
,
kCOAck
);
};
signals:
void
statusMessage
(
const
QString
&
msg
);
void
resetStatusMessages
();
void
errorMessage
(
const
QString
&
msg
);
void
listComplete
(
void
);
public
slots
:
void
receiveMessage
(
LinkInterface
*
link
,
mavlink_message_t
message
);
void
listDirectory
(
const
QString
&
dirPath
);
void
downloadPath
(
const
QString
&
from
,
const
QDir
&
downloadDir
);
protected:
struct
RequestHeader
{
uint8_t
magic
;
///> Magic byte 'f' to idenitfy FTP protocol
uint8_t
session
;
///> Session id for read and write commands
uint8_t
opcode
;
///> Command opcode
uint8_t
size
;
///> Size of data
uint32_t
crc32
;
///> CRC for entire Request structure, with crc32 set to 0
uint32_t
offset
;
///> Offsets for List and Read commands
};
struct
Request
{
struct
RequestHeader
hdr
;
// The entire Request must fit into the data member of the mavlink_encapsulated_data_t structure. We use as many leftover bytes
// after we use up space for the RequestHeader for the data portion of the Request.
uint8_t
data
[
sizeof
(((
mavlink_encapsulated_data_t
*
)
0
)
->
data
)
-
sizeof
(
RequestHeader
)];
};
enum
Opcode
{
// Commands
kCmdNone
,
///> ignored, always acked
kCmdTerminate
,
///> releases sessionID, closes file
kCmdReset
,
///> terminates all sessions
kCmdList
,
///> list files in <path> from <offset>
kCmdOpen
,
///> opens <path> for reading, returns <session>
kCmdRead
,
///> reads <size> bytes from <offset> in <session>
kCmdCreate
,
///> creates <path> for writing, returns <session>
kCmdWrite
,
///> appends <size> bytes at <offset> in <session>
kCmdRemove
,
///> remove file (only if created by server?)
// Responses
kRspAck
,
///> positive acknowledgement of previous command
kRspNak
,
///> negative acknowledgement of previous command
// Used for testing only, not part of protocol
kCmdTestNoAck
,
// ignored, ack not sent back, should timeout waiting for ack
};
enum
ErrorCode
{
kErrNone
,
kErrNoRequest
,
kErrNoSession
,
kErrSequence
,
kErrNotDir
,
kErrNotFile
,
kErrEOF
,
kErrNotAppend
,
kErrTooBig
,
kErrIO
,
kErrPerm
,
kErrUnknownCommand
,
kErrCrc
};
enum
OperationState
{
kCOIdle
,
// not doing anything
kCOAck
,
// waiting for an Ack
kCOList
,
// waiting for List response
kCOOpen
,
// waiting for Open response
kCORead
,
// waiting for Read response
};
protected
slots
:
void
_ackTimeout
(
void
);
protected:
bool
_sendOpcodeOnlyCmd
(
uint8_t
opcode
,
OperationState
newOpState
);
void
_setupAckTimeout
(
void
);
void
_clearAckTimeout
(
void
);
void
_emitErrorMessage
(
const
QString
&
msg
);
void
_emitStatusMessage
(
const
QString
&
msg
);
void
_sendRequest
(
Request
*
request
);
void
_fillRequestWithString
(
Request
*
request
,
const
QString
&
str
);
void
_openAckResponse
(
Request
*
openAck
);
void
_readAckResponse
(
Request
*
readAck
);
void
_listAckResponse
(
Request
*
listAck
);
void
_sendListCommand
(
void
);
void
_sendTerminateCommand
(
void
);
void
_closeReadSession
(
bool
success
);
static
quint32
crc32
(
Request
*
request
,
unsigned
state
=
0
);
static
QString
errorString
(
uint8_t
errorCode
);
OperationState
_currentOperation
;
///> Current operation of state machine
QTimer
_ackTimer
;
///> Used to signal a timeout waiting for an ack
static
const
int
_ackTimerTimeoutMsecs
=
1000
;
///> Timeout in msecs for ack timer
UASInterface
*
_mav
;
quint16
_encdata_seq
;
unsigned
_listOffset
;
///> offset for the current List operation
QString
_listPath
;
///> path for the current List operation
uint8_t
_activeSession
;
///> currently active session, 0 for none
uint32_t
_readOffset
;
///> current read offset
QByteArray
_readFileAccumulator
;
///> Holds file being downloaded
QDir
_readFileDownloadDir
;
///> Directory to download file to
QString
_readFileDownloadFilename
;
///> Filename (no path) for download file
// We give MockMavlinkFileServer friend access so that it can use the data structures and opcodes
// to build a mock mavlink file server for testing.
friend
class
MockMavlinkFileServer
;
};
#endif // QGCUASFILEMANAGER_H
src/uas/UAS.cc
View file @
b485d7f6
...
...
@@ -139,6 +139,7 @@ UAS::UAS(MAVLinkProtocol* protocol, QThread* thread, int id) : UASInterface(),
airSpeed
(
std
::
numeric_limits
<
double
>::
quiet_NaN
()),
groundSpeed
(
std
::
numeric_limits
<
double
>::
quiet_NaN
()),
waypointManager
(
this
),
fileManager
(
this
,
this
),
attitudeKnown
(
false
),
attitudeStamped
(
false
),
...
...
@@ -148,6 +149,8 @@ UAS::UAS(MAVLinkProtocol* protocol, QThread* thread, int id) : UASInterface(),
pitch
(
0.0
),
yaw
(
0.0
),
imagePackets
(
0
),
// We must initialize to 0, otherwise extended data packets maybe incorrectly thought to be images
blockHomePositionChanges
(
false
),
receivedMode
(
false
),
...
...
@@ -175,6 +178,8 @@ UAS::UAS(MAVLinkProtocol* protocol, QThread* thread, int id) : UASInterface(),
componentMulti
[
i
]
=
false
;
}
connect
(
mavlink
,
SIGNAL
(
messageReceived
(
LinkInterface
*
,
mavlink_message_t
)),
&
fileManager
,
SLOT
(
receiveMessage
(
LinkInterface
*
,
mavlink_message_t
)));
// Store a list of available actions for this UAS.
// Basically everything exposed as a SLOT with no return value or arguments.
...
...
@@ -1368,6 +1373,7 @@ void UAS::receiveMessage(LinkInterface* link, mavlink_message_t message)
// NO VALID TRANSACTION - ABORT
// Restart statemachine
imagePacketsArrived
=
0
;
break
;
}
for
(
int
i
=
0
;
i
<
imagePayload
;
++
i
)
...
...
@@ -1381,9 +1387,11 @@ void UAS::receiveMessage(LinkInterface* link, mavlink_message_t message)
++
imagePacketsArrived
;
// emit signal if all packets arrived
if
(
(
imagePacketsArrived
>=
imagePackets
)
)
if
(
imagePacketsArrived
>=
imagePackets
)
{
// Restart statemachine
imagePackets
=
0
;
imagePacketsArrived
=
0
;
emit
imageReady
(
this
);
//qDebug() << "imageReady emitted. all packets arrived";
}
...
...
@@ -2194,13 +2202,15 @@ QImage UAS::getImage()
if
(
!
image
.
loadFromData
(
imageRecBuffer
))
{
qDebug
()
<<
__FILE__
<<
__LINE__
<<
"Loading data from image buffer failed!"
;
return
QImage
();
}
}
// Restart statemachine
imagePacketsArrived
=
0
;
//imageRecBuffer.clear();
imagePackets
=
0
;
imageRecBuffer
.
clear
();
return
image
;
}
void
UAS
::
requestImage
()
...
...
src/uas/UAS.h
View file @
b485d7f6
...
...
@@ -42,6 +42,7 @@ This file is part of the QGROUNDCONTROL project
#include
"QGCJSBSimLink.h"
#include
"QGCXPlaneLink.h"
#include
"QGCUASParamManager.h"
#include
"QGCUASFileManager.h"
/**
...
...
@@ -370,6 +371,7 @@ public:
#endif
friend
class
UASWaypointManager
;
friend
class
QGCUASFileManager
;
protected:
//COMMENTS FOR TEST UNIT
/// LINK ID AND STATUS
...
...
@@ -472,6 +474,7 @@ protected: //COMMENTS FOR TEST UNIT
double
groundSpeed
;
///< Groundspeed
double
bearingToWaypoint
;
///< Bearing to next waypoint
UASWaypointManager
waypointManager
;
QGCUASFileManager
fileManager
;
/// ATTITUDE
bool
attitudeKnown
;
///< True if attitude was received, false else
...
...
@@ -554,6 +557,10 @@ public:
return
&
paramMgr
;
}
virtual
QGCUASFileManager
*
getFileManager
()
{
return
&
fileManager
;
}
/** @brief Get the HIL simulation */
QGCHilLink
*
getHILSimulation
()
const
{
return
simulation
;
...
...
Prev
1
2
Next
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new 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