Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add egeniouss receiver. #5637

Merged
merged 24 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
aac7851
Add egeniouss receiver.
mohsenD98 Sep 10, 2024
2f2e5e7
Select EgenioussDevice.
mohsenD98 Sep 10, 2024
f02339a
Refresh PositioningInformationModel on device details change.
mohsenD98 Sep 12, 2024
084823b
Fix ui and try to decode received message.
mohsenD98 Sep 12, 2024
76963a1
Read payload.
mohsenD98 Sep 12, 2024
2009f87
Remove detailsChanged signal and use positionInformationChanged.
mohsenD98 Sep 13, 2024
7d9a9bc
Set quality to 1 to pass `mPositionInformation.isValid()` condition.
mohsenD98 Sep 13, 2024
089b1da
Try to set utc and fix crash on android.
mohsenD98 Sep 13, 2024
ee635cf
Nanosecond utc + Connect address and port from ui.
mohsenD98 Sep 13, 2024
817566a
Try to use mAddress instead of localhost.
mohsenD98 Sep 13, 2024
be89cc5
Address review - part 1.
mohsenD98 Sep 14, 2024
661a623
Address review - part 2.
mohsenD98 Sep 14, 2024
cd74cc9
[Try 1] to fix startup crash.
mohsenD98 Sep 14, 2024
deca732
[Try 2] Remove destructor.
mohsenD98 Sep 15, 2024
65c6d8b
[Try 3] add "this" to requestPermission.
mohsenD98 Sep 15, 2024
7330347
Cleanup ui and some clean code.
mohsenD98 Sep 15, 2024
d30d81f
Add egeniouss icon.
mohsenD98 Sep 15, 2024
5c72442
Fix style.
mohsenD98 Sep 15, 2024
ea00df0
Enable egeniouss receiver using plugin.
mohsenD98 Sep 16, 2024
781d9f9
Plugin manager signal when enabling / disabling plugin.
mohsenD98 Sep 18, 2024
183fd28
Address issues.
mohsenD98 Sep 23, 2024
12f5ae5
Fix permission problem in android.
mohsenD98 Sep 23, 2024
d05d013
Fix losing the positioning button on the lower right corner.
mohsenD98 Sep 23, 2024
7020594
Fix style.
mohsenD98 Sep 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ set(QFIELD_CORE_SRCS
positioning/gnsspositioninformation.cpp
positioning/internalgnssreceiver.cpp
positioning/nmeagnssreceiver.cpp
positioning/egenioussreceiver.cpp
positioning/tcpreceiver.cpp
positioning/udpreceiver.cpp
positioning/positioning.cpp
Expand Down Expand Up @@ -150,6 +151,7 @@ set(QFIELD_CORE_HDRS
positioning/positioningdevicemodel.h
positioning/internalgnssreceiver.h
positioning/nmeagnssreceiver.h
positioning/egenioussreceiver.h
positioning/tcpreceiver.h
positioning/udpreceiver.h
positioning/geofencer.h
Expand Down
1 change: 1 addition & 0 deletions src/core/positioning/abstractgnssreceiver.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ class AbstractGnssReceiver : public QObject

private:
friend class InternalGnssReceiver;
friend class EgenioussReceiver;
friend class NmeaGnssReceiver;
friend class BluetoothReceiver;
friend class TcpReceiver;
Expand Down
107 changes: 107 additions & 0 deletions src/core/positioning/egenioussreceiver.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#include "egenioussreceiver.h"

#include <QHostAddress>
#include <QJsonDocument>
#include <QJsonValue>

EgenioussReceiver::EgenioussReceiver( QObject *parent )
: AbstractGnssReceiver( parent ), mTcpSocket( new QTcpSocket( this ) )
{
connect( mTcpSocket, &QTcpSocket::readyRead, this, &EgenioussReceiver::onReadyRead );
connect( mTcpSocket, &QTcpSocket::errorOccurred, this, &EgenioussReceiver::onErrorOccurred );
connect( mTcpSocket, &QTcpSocket::connected, this, &EgenioussReceiver::connected );
connect( mTcpSocket, &QTcpSocket::disconnected, this, &EgenioussReceiver::disconnected );
}

void EgenioussReceiver::handleConnectDevice()
{
mTcpSocket->connectToHost( mAddress, mPort, QTcpSocket::ReadWrite );
}

void EgenioussReceiver::connected()
{
mSocketState = QAbstractSocket::ConnectedState;
emit socketStateChanged( mSocketState );
}

void EgenioussReceiver::handleDisconnectDevice()
{
mTcpSocket->disconnectFromHost();
}

void EgenioussReceiver::disconnected()
{
if ( mTcpSocket->state() == QAbstractSocket::ConnectedState )
{
mSocketState = QAbstractSocket::UnconnectedState;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels a bit strange to me that subclasses of postioning device have to manage mSocketState that way. mSocketState and mSocketStateString have a quite risk to get out of sync. Wouldn't be it a cleaner interface to reimplement AbstractGnssReceiver::socketState() and socketStateString in subclasses and get rid of the variables mSocketState and mSocketStateString?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@domi4484 , not a bad idea. Can we do that in a follow up PR?

emit socketStateChanged( mSocketState );
}
}

QList<QPair<QString, QVariant>> EgenioussReceiver::details()
domi4484 marked this conversation as resolved.
Show resolved Hide resolved
{
QList<QPair<QString, QVariant>> detailsList;

if ( mPayload.isEmpty() )
{
return detailsList;
}

detailsList.append( qMakePair( "q", mPayload.value( "q" ).toString() ) );

return detailsList;
}

void EgenioussReceiver::onReadyRead()
{
QByteArray mReceivedData = mTcpSocket->readAll();
if ( mReceivedData.size() < 9 )
nirvn marked this conversation as resolved.
Show resolved Hide resolved
{
return; // Received data is too short to process.
nirvn marked this conversation as resolved.
Show resolved Hide resolved
}
if ( static_cast<uint8_t>( mReceivedData[0] ) != 0xFE )
{
return; // Invalid start byte
nirvn marked this conversation as resolved.
Show resolved Hide resolved
}
uint32_t payloadLength;
QDataStream dataStream( mReceivedData.mid( 4, 4 ) );
dataStream.setByteOrder( QDataStream::LittleEndian );
dataStream >> payloadLength;
if ( mReceivedData.size() < 8 + payloadLength )
{
return; // Received data is too short to contain the payload.
}
QJsonDocument jsonDoc = QJsonDocument::fromJson( mReceivedData.mid( 8, payloadLength ) );
if ( jsonDoc.isNull() || !jsonDoc.isObject() )
{
return; // Failed to parse JSON
}
mPayload = jsonDoc.object();
const double latitude = mPayload.value( "lat" ).toDouble() == 0 ? std::numeric_limits<double>::quiet_NaN() : mPayload.value( "lat" ).toDouble();
const double longitude = mPayload.value( "lon" ).toDouble() == 0 ? std::numeric_limits<double>::quiet_NaN() : mPayload.value( "lon" ).toDouble();
const double elevation = mPayload.value( "alt" ).toDouble() == 0 ? std::numeric_limits<double>::quiet_NaN() : mPayload.value( "alt" ).toDouble();
mLastGnssPositionInformation = GnssPositionInformation(
latitude,
longitude,
elevation,
std::numeric_limits<double>::quiet_NaN(),
std::numeric_limits<double>::quiet_NaN(),
QList<QgsSatelliteInfo>(),
0,
0,
0,
std::numeric_limits<double>::quiet_NaN(),
std::numeric_limits<double>::quiet_NaN(),
QDateTime::fromMSecsSinceEpoch( mPayload.value( "utc" ).toDouble() / 1e6, Qt::UTC ),
QChar(),
0,
1 );

emit lastGnssPositionInformationChanged( mLastGnssPositionInformation );
}

void EgenioussReceiver::onErrorOccurred( QAbstractSocket::SocketError socketError )
{
mLastError = mTcpSocket->errorString();
emit lastErrorChanged( mLastError );
}
34 changes: 34 additions & 0 deletions src/core/positioning/egenioussreceiver.h
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would move this class to an egeniouss directory inside positioning

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@domi4484 , let's maybe not do this right away. ATM we have our other device classes sitting in positioning (Bluetooth, TCP, UDP, serial) here and having a directory for a single pair of .CPP, .H feels a bit too much.

If we end up needing another class (eg egenioussutils) let's maybe consider moving all of it into an src/core/egeniouss directory)

Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#pragma once
nirvn marked this conversation as resolved.
Show resolved Hide resolved

#include "abstractgnssreceiver.h"

#include <QJsonObject>
#include <QTcpSocket>

class EgenioussReceiver : public AbstractGnssReceiver
{
Q_OBJECT

public:
explicit EgenioussReceiver( QObject *parent = nullptr );

private:
void handleConnectDevice() override;
void handleDisconnectDevice() override;
QList<QPair<QString, QVariant>> details() override;

private slots:
void onReadyRead();
void onErrorOccurred( QAbstractSocket::SocketError socketError );

private:
void processReceivedData();
void connected();
void disconnected();

private:
QTcpSocket *mTcpSocket = nullptr;
QJsonObject mPayload;
const QHostAddress::SpecialAddress mAddress = QHostAddress::LocalHost;
const int mPort = 1235;
};
5 changes: 5 additions & 0 deletions src/core/positioning/positioning.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#ifdef WITH_SERIALPORT
#include "serialportreceiver.h"
#endif
#include "egenioussreceiver.h"
#include "internalgnssreceiver.h"
#include "positioning.h"
#include "positioningutils.h"
Expand Down Expand Up @@ -205,6 +206,10 @@ void Positioning::setupDevice()
const int port = mDeviceId.mid( portSeparator + 1 ).toInt();
mReceiver = new UdpReceiver( address, port, this );
}
else if ( mDeviceId.startsWith( QStringLiteral( "egeniouss:" ) ) )
{
mReceiver = new EgenioussReceiver( this );
}
#ifdef WITH_SERIALPORT
else if ( mDeviceId.startsWith( QStringLiteral( "serial:" ) ) )
{
Expand Down
1 change: 0 additions & 1 deletion src/core/positioning/positioning.h
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,6 @@ class Positioning : public QObject
void setLogging( bool logging );

signals:

void activeChanged();
void deviceIdChanged();
void deviceChanged();
Expand Down
3 changes: 3 additions & 0 deletions src/core/positioning/positioningdevicemodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,9 @@ const QString PositioningDeviceModel::deviceId( const Device &device ) const

case SerialPortDevice:
return QStringLiteral( "serial:%1" ).arg( device.settings.value( QStringLiteral( "address" ) ).toString() );

case EgenioussDevice:
return QStringLiteral( "egeniouss:%1:%2" ).arg( device.settings.value( QStringLiteral( "address" ) ).toString(), QString::number( device.settings.value( QStringLiteral( "port" ) ).toInt() ) );
}

return QString();
Expand Down
1 change: 1 addition & 0 deletions src/core/positioning/positioningdevicemodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class PositioningDeviceModel : public QAbstractListModel
BluetoothDevice,
TcpDevice,
UdpDevice,
EgenioussDevice,
SerialPortDevice,
};
Q_ENUM( Type )
Expand Down
65 changes: 65 additions & 0 deletions src/qml/EgenioussDeviceChooser.qml
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would put this file into a egeniouss directory. Maybe into positioning/egeniouss and move as well other positioning related files in there to gain some structure

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest this structural change be kept in a separate PR

Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import org.qfield
import Theme

Item {
width: parent.width

property alias deviceAddress: egenioussDeviceAddress.text
property alias devicePort: egenioussDevicePort.text

function generateName() {
return deviceAddress + ' (' + devicePort + ')';
}

function setSettings(settings) {
deviceAddress = settings['address'];
devicePort = settings['port'];
}

function getSettings() {
return {
"address": deviceAddress.trim(),
"port": parseInt(devicePort)
};
}

GridLayout {
width: parent.width
columns: 1
columnSpacing: 0
rowSpacing: 5

Label {
Layout.fillWidth: true
text: qsTr("Address:")
font: Theme.defaultFont
wrapMode: Text.WordWrap
}

QfTextField {
id: egenioussDeviceAddress
Layout.fillWidth: true
font: Theme.defaultFont
text: '127.0.0.1'
inputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhNoAutoUppercase | Qt.ImhPreferLowercase
}

Label {
Layout.fillWidth: true
text: qsTr("Port:")
font: Theme.defaultFont
wrapMode: Text.WordWrap
}

QfTextField {
id: egenioussDevicePort
Layout.fillWidth: true
font: Theme.defaultFont
text: '1235'
inputMethodHints: Qt.ImhFormattedNumbersOnly
}
}
}
12 changes: 11 additions & 1 deletion src/qml/PositioningDeviceSettings.qml
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ Popup {
id: positioningDeviceName
Layout.fillWidth: true
font: Theme.defaultFont
placeholderText: displayText == '' ? qsTr('Leave empty to auto-fill') : ''
placeholderText: displayText === '' ? qsTr('Leave empty to auto-fill') : ''
}

Label {
Expand Down Expand Up @@ -130,6 +130,8 @@ Popup {
return Theme.getThemeVectorIcon('ic_udp_receiver_black_24dp');
case PositioningDeviceModel.SerialPortDevice:
return Theme.getThemeVectorIcon('ic_serial_port_receiver_black_24dp');
case PositioningDeviceModel.EgenioussDevice:
return Theme.getThemeVectorIcon('ic_serial_port_receiver_black_24dp');
}
return '';
}
Expand All @@ -154,6 +156,8 @@ Popup {
return Theme.getThemeVectorIcon('ic_udp_receiver_black_24dp');
case PositioningDeviceModel.SerialPortDevice:
return Theme.getThemeVectorIcon('ic_serial_port_receiver_black_24dp');
case PositioningDeviceModel.EgenioussDevice:
return Theme.getThemeVectorIcon('ic_serial_port_receiver_black_24dp');
}
return '';
}
Expand All @@ -177,6 +181,10 @@ Popup {
name: qsTr('UDP (NMEA)')
value: PositioningDeviceModel.UdpDevice
}
ListElement {
name: qsTr('Egeniouss')
value: PositioningDeviceModel.EgenioussDevice
}
}

Loader {
Expand All @@ -193,6 +201,8 @@ Popup {
return "qrc:/qml/UdpDeviceChooser.qml";
case PositioningDeviceModel.SerialPortDevice:
return "qrc:/qml/SerialPortDeviceChooser.qml";
case PositioningDeviceModel.EgenioussDevice:
return "qrc:/qml/EgenioussDeviceChooser.qml";
}
return '';
}
Expand Down
8 changes: 4 additions & 4 deletions src/qml/PositioningInformationView.qml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Rectangle {
id: grid

readonly property real numberOfColumns: parent.width > 1000 ? 6 : parent.width > 620 ? 3 : 2
readonly property real numberOfRows: grid.count / numberOfColumns
readonly property real numberOfRows: Math.ceil(grid.count / numberOfColumns)

flow: GridView.FlowTopToBottom
boundsBehavior: Flickable.StopAtBounds
Expand All @@ -44,15 +44,15 @@ Rectangle {
distanceUnits: projectInfo.distanceUnits
coordinateDisplayCrs: projectInfo.coordinateDisplayCrs
}
height: numberOfRows * cellHeight
height: grid.numberOfRows * cellHeight
width: parent.width
cellHeight: positioningInformationView.cellHeight
cellWidth: parent.width / numberOfColumns
clip: true

delegate: Rectangle {
readonly property real currentColumn: parseInt(index / (grid.count / grid.numberOfColumns))
readonly property real currentRow: index % (grid.count / grid.numberOfColumns)
readonly property real currentColumn: parseInt(index / grid.numberOfRows)
readonly property real currentRow: index % grid.numberOfRows

width: grid.cellWidth
height: grid.cellHeight
Expand Down
1 change: 1 addition & 0 deletions src/qml/qml.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
<file>ScaleBar.qml</file>
<file>SensorInformationView.qml</file>
<file>Toast.qml</file>
<file>EgenioussDeviceChooser.qml</file>
<file>TcpDeviceChooser.qml</file>
<file>UdpDeviceChooser.qml</file>
<file>VariableEditor.qml</file>
Expand Down
Loading