I'm trying to use the QCanBus filters (Qt version 6.4.0) like so:
if (QCanBus::instance()->plugins().contains(QStringLiteral("vectorcan"))) {
QString errorString = QString("Error");
canDevice = QCanBus::instance()->createDevice(QStringLiteral("vectorcan"), QStringLiteral("can0"), &errorString);
if (!canDevice)
{
qDebug() << "Error: " << errorString << Qt::endl;
}
else
{
QCanBusDevice::Filter filter;
QList<QCanBusDevice::Filter> filterList;
filter.frameId = 0x18FFC281;
filter.frameIdMask = 0x00FFFFFF;
filter.format = QCanBusDevice::Filter::MatchExtendedFormat;
filter.type = QCanBusFrame::DataFrame;
filterList.append(filter);
//apply filter
canDevice->setConfigurationParameter(QCanBusDevice::RawFilterKey, QVariant::fromValue(filterList));
//set baud rate
canDevice->setConfigurationParameter(QCanBusDevice::BitRateKey, QVariant(250000));
bool connected = canDevice->connectDevice();
if (connected)
{
connect(canDevice, &QCanBusDevice::framesReceived, this, &CANHandler::ProcessFramesReceived);
}
else
{
qDebug() << "Error connecting to device." << Qt::endl;
}
}
}
I'm expecting this only let messages through that match the filter, yet when I print the ID of incoming messages, it seems no filtering is happening:
void CANHandler::ProcessFramesReceived()
{
while(canDevice->framesAvailable())
{
QCanBusFrame frame = canDevice->readFrame();
qDebug() << QString::number(frame.frameId(), 16) << Qt::endl;
//every CAN message on the bus appears here
}
}
Am I using the filter wrong? Do I have the wrong expectation?
EDIT: After looking into the documentation further I realized the Vector plugin does not actually support filtering at the time of this post. I'm not exactly sure as to why, seems like only a few plugins offered by Qt support filtering.
Related
Lets say Typed Actor A needs to command Typed Actor B to do something. Actor A also needs to know if the command ran succesfully or not but does not want to block operation until this response arrives. My current working theory is that this is best satisfied with Requests. More specifically request(...).then
There is a nice example called "request.cpp" that I have been playing with. My challenge is that I don't really need actor B to return any data. I just need to know if the command was successful or not and if not what error was thrown.
So my question is two fold: 1) Am I correct in thinking that request(...).then is the correct mechanism to do what I want and 2) if so then can a request handle a response that has no data?
This is what I'm trying:
#include <chrono>
#include <cstdint>
#include <iostream>
#include <vector>
#include "caf/all.hpp"
using std::endl;
using std::vector;
using std::chrono::seconds;
using namespace caf;
using cell
= typed_actor<result<void>(get_atom, int32_t)>;
struct cell_state {
static constexpr inline const char* name = "cell";
cell::pointer self;
cell_state(cell::pointer ptr) : self(ptr) {}
cell_state(const cell_state&) = delete;
cell_state& operator=(const cell_state&) = delete;
cell::behavior_type make_behavior() {
return {
[=](get_atom, int32_t input) -> result<void> {
if (input != 5) { // Simulate command successful or not
return; // If successful, then return;
}
else {
return sec::unexpected_message; // If not then return error.
}
},
};
}
};
using cell_impl = cell::stateful_impl<cell_state>;
void multiplexed_testee(event_based_actor* self, vector<cell> cells) {
for (cell& x : cells) {
aout(self) << "cell #" << x.id() << " calling" << endl;
self->request(x, seconds(1), get_atom_v, static_cast<int32_t>(x.id()))
.then(
[=](void) {
aout(self) << "cell #" << x.id() << " -> " << "success" << endl;
},
[=](error& err) {
aout(self) << "cell #" << x.id() << " -> " << to_string(err) << endl;
});
}
}
void caf_main(actor_system& system) {
vector<cell> cells;
for (int32_t i = 0; i < 5; ++i)
cells.emplace_back(system.spawn<cell_impl>());
scoped_actor self{ system };
auto x2 = self->spawn(multiplexed_testee, cells);
self->wait_for(x2);
}
CAF_MAIN()
When I compile, I get an error on the empty return statement saying "return-statement with no value, in function returning caf::result<void>. Is there a better way to do this?
My backup plan is to change my command definition to just return a standard error and return sec::none if the operation was successful. But I'm afraid that approach violates the spirit of the whole optional-second-parameter for error conditions. How well am I thinking about all this?
Is there a better way to do this?
You had the right idea. The result<void> expects either an error or a 'void' value. Since void{} isn't a thing in C++, you can do return caf::unit; to tell result<void> to construct an "empty" result. On the receiver's side, you already did the right thing: then with a lambda taking no arguments.
Minor point:
[=](void) { ... }
This is a C-ism from the early days where the compiler allowed you to do silly things. Just drop the void, it serves no purpose. :)
I'm developing a Qt app which need to read data from weight scale model and can't quite understand how exactly the Bluetooth Low Energy works and how to implement it in Qt.
I have a UC-352BLE weight scale which uses BLE to send data. What I want to achieve is this:
After initial pairing the scale with my Raspberry Pi on which my app is running, when the scale sends data (it does it automatically when you take a measurement), my app receive it. For example I have a blood pressure monitor which uses normal Bluetooth and here it's easy. In my app, I create a QBluetoothServer and call its listen() method. Then when the (already paired) device sends a measurement, it connects with my app automatically, then I create QBluetoothSocket and read the data. But with the scale and it's BLE it seems that you can't do it that way. I tried to follow the Qt documentation on this and right now I just have to manually press the button which connects to the scale when it's sending the data. And every time it connects to the device it discovers it's characteristics and services etc. Don't know if I can do it so the app automatically receives a connection and reads a data when the scale sends a measurement. And even when I try to connects like that sometimes it connects and sometimes don't (I get Unknown error form QLowEnergyController::Error when connecting). Here is what I already have:
Bletest::Bletest(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::Bletest)
{
ui->setupUi(this);
if (localDevice.isValid()) {
localDevice.powerOn();
localDevice.setHostMode(QBluetoothLocalDevice::HostDiscoverable);
connect(&localDevice, &QBluetoothLocalDevice::deviceConnected, this, &Bletest::deviceConnected);
connect(&localDevice, &QBluetoothLocalDevice::deviceDisconnected, this, &Bletest::deviceDisconnected);
connect(&localDevice, &QBluetoothLocalDevice::pairingFinished, this, &Bletest::pairingFinished);
}
discoveryAgent = new QBluetoothDeviceDiscoveryAgent(this);
connect(discoveryAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, this, &Bletest::addDevice);
connect(discoveryAgent, &QBluetoothDeviceDiscoveryAgent::finished, this, &Bletest::scanFinished);
connect(discoveryAgent, &QBluetoothDeviceDiscoveryAgent::canceled, this, &Bletest::scanFinished);
discoveryAgent->start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod);
}
Bletest::~Bletest()
{
delete ui;
}
// Local device slots
void Bletest::deviceConnected(const QBluetoothAddress &address)
{
qDebug() << address.toString() << " connected";
}
void Bletest::deviceDisconnected(const QBluetoothAddress &address)
{
qDebug() << address.toString() << " disconnected";
}
void Bletest::pairingFinished(const QBluetoothAddress &address, QBluetoothLocalDevice::Pairing pairing)
{
}
// Agent slots
void Bletest::addDevice(const QBluetoothDeviceInfo &device)
{
if (device.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration) {
if (device.name().contains("352")) {
bleDevice = device;
qDebug() << "Found: " + device.name() + "\t" + device.address().toString();
}
}
}
void Bletest::scanFinished()
{
qDebug() << "Devices scan finished";
}
///////////
void Bletest::on_connectButton_clicked()
{
controller = QLowEnergyController::createCentral(bleDevice, this);
connect(controller, &QLowEnergyController::serviceDiscovered, this, &Bletest::serviceDiscovered);
connect(controller, &QLowEnergyController::discoveryFinished, this, &Bletest::serviceScanFinished);
connect(controller, static_cast<void (QLowEnergyController::*)(QLowEnergyController::Error)>(&QLowEnergyController::error),
this, [this](QLowEnergyController::Error error) {
qDebug() << "Cannot connect to device: " + QString::number(error);
});
connect(controller, &QLowEnergyController::connected, this, [this]() {
qDebug() << "Connected to device";
controller->discoverServices();
});
connect(controller, &QLowEnergyController::disconnected, this, [this]() {
qDebug() << "Disconnected";
});
controller->connectToDevice();
}
// Controller slots
void Bletest::serviceDiscovered(const QBluetoothUuid &gatt)
{
qDebug() << "Service discovered: " << gatt.toString();
if (gatt.toString().contains("0000181d-0000-1000-8000-00805f9b34fb")) {
service = controller->createServiceObject(QBluetoothUuid(QBluetoothUuid::WeightScale));
if (service) {
qDebug() << "Found weight scale service";
connect(service, &QLowEnergyService::stateChanged, this, &Bletest::serviceStateChanged);
connect(service, &QLowEnergyService::characteristicChanged, this, &Bletest::updateWeight);
connect(service, &QLowEnergyService::characteristicRead, this, &Bletest::updateWeight);
service->discoverDetails();
}
}
}
void Bletest::serviceScanFinished()
{
qDebug() << "Service scan finished";
}
////////////////////////////
// Service slots
void Bletest::serviceStateChanged(QLowEnergyService::ServiceState newState)
{
if (controller->state() == QLowEnergyController::UnconnectedState)
return;
if (newState == QLowEnergyService::DiscoveringServices) {
qDebug() << "Discovering services state";
} else if (QLowEnergyService::ServiceDiscovered) {
qDebug() << "Service discovered.";
const QLowEnergyCharacteristic weightChar = service->characteristic(QBluetoothUuid(QBluetoothUuid::WeightMeasurement));
if (!weightChar.isValid()) {
qDebug() << "Weight data not found";
return;
}
qDebug() << "Weight data found";
//service->readCharacteristic(weightChar);
desc = weightChar.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration);
if (desc.isValid()) {
qDebug() << "Writing descriptor";
service->writeDescriptor(desc, QByteArray::fromHex("0100"));
}
}
}
void Bletest::updateWeight(const QLowEnergyCharacteristic &c, const QByteArray &value)
{
qDebug() << "Updating weight";
if (c.uuid() != QBluetoothUuid(QBluetoothUuid::WeightMeasurement))
return;
double weight = qFromLittleEndian<qint16>(value.mid(1, 2).data()) * RESOLUTION;
qDebug() << "New weight: " << value.mid(1, 2);
qDebug() << "New weight: " + QString::number(weight, 'f', 2);
}
///////////////////////////
Can someone point me in the right direction with this? Is is even possible with BLE to automatically connects to my app and receive data?
Thanks.
I am creating a thread and inside that thread :
m_client = new QMqttClient(this);
m_client->setHostname("ps01.xx.com");
m_client->setPort(1883);
m_client->setClientId("Sas-RASPi-001");
m_client->connectToHost();
connecting to the mqt broker.
any signal connecting attempt like :
QObject::connect(m_client, &QMqttClient::stateChanged, this, &Messenqt::updateLogStateChange);
produce an error like :
QObject::connect: Cannot queue arguments of type 'ClientState'
(Make sure 'ClientState' is registered using qRegisterMetaType().)
I added :
qRegisterMetaType<QMqttClient::ClientState>("QMqttClient::ClientState");
in the beginning of the code (above code)
still same problem.
What is the proper way to use mqt from different thread in QT signal/slot way ?
EDIT:
for (int i = 20 ; i < 20 ; i++){
QThread::sleep(1);
if(m_client->publish(topic_, QString("testing. . . ").toLocal8Bit() , 1 ,true) == -1;
qDebug() << " error" <<;
}
Hi,
I am using qmqtt in a for loop with sleep 1 second and then process waiting to send messages until all 20 messages published.
When the loop finishe all 20 messages send .
But . if we will not use QThread::sleep(1) . then each messages sending individually. why . ?
Is there any way to force send to each messages immediatelly when its published?
EDIT 2
Below example not sending the images to the broker. It is blocking some place somewhere. I need flush the messages :)
minimal example :
m_client = new QMqttClient(this);
m_client->setHostname("ps01.xxx.com");
m_client->setPort(1883);
m_client->setClientId("RASPBERRY-009");
m_client->setUsername("vv");
m_client->setPassword("vv");
m_client->setCleanSession(false);
m_client->connectToHost();
//yeni slot mekanizmasi. kendi icinde &Publisher örneği..
QObject::connect(m_client, &QMqttClient::stateChanged, this, &Publisher::updateStateChange);
QObject::connect(m_client, &QMqttClient::connected, this, &Publisher::sendMessages);
an the send message:
void Publisher::sendMessages()
{
QDir dir("/ram");
int count = 0;
QThread::sleep(5);
while (true){
QStringList images_metas = dir.entryList(QStringList() << "*.png" ,QDir::Files);
if (images_metas.size() > 0){
foreach(QString filename, images_metas) {
dataLoad *dl = new dataLoad;
QString img_path = "/ram/" + filename;
QImage img(img_path);
qDebug() << " : : : " << QImage("/ram/00-02-2018-04-05-2.png").size() << endl;
dl->image = img;
dl->text = "Deneme MEsajıdır . . ";
QByteArray byteArray_;
QDataStream stream(&byteArray_, QIODevice::WriteOnly);
stream.setVersion(QDataStream::Qt_5_10);
stream << dl->image << dl->text;
qDebug() << count++ << " sending : " << byteArray_.size() << " " << filename << endl;
qDebug() << testPublish(byteArray_)<< endl;
}
}
else {
qDebug() << " folder is empty waiting... " << endl;
}
//Check every 1 second
QThread::sleep(1);
}
}
and the publishing:
qint32 Publisher::testPublish(QByteArray &bytarray)
{
QMqttTopicName topic_ = QString("qtmqtt/topic1");
QString mesaj = ".x.x.x.x.x.x.x.";
auto id = m_client->publish(topic_, mesaj.toLocal8Bit() , 1 ,true);
return id;
}
Please try below code:
msg_timer = new QTimer(this);
//TODO big chip save timer.
connect(msg_timer, SIGNAL(timeout()) , this , SLOT(sendMessages()));
msg_timer->start(1000);
dont use while loop. It is blocking everything..
Best
I get a solution to the problem of "Qmqtt QObject::connect: Cannot queue arguments of type 'ClientState' (Make sure 'ClientState' is registered using qRegisterMetaType().)" caused by working in threads. Although I have not made clear the specific meaning, at least it can work well.
You can try it:
connect(m_client, &QMqttClient::stateChanged, [&](QMqttClient::ClientState state){
qDebug()<< state;
});
I am receiving data in this shape:
Q1\n
9.70E-6\n
OK>
from an external device via QSerialPort, but with my reading routine
QString request = "Hello";
qDebug() << "TransAction started!";
QByteArray requestData = request.toLocal8Bit();
qDebug() << "Writing data: " << requestData;
serial->write(requestData);
qDebug() << "Data written";
if(serial->waitForBytesWritten(waitTimeout))
{
if(serial->waitForReadyRead(waitTimeout))
{
qDebug() << "Waiting for data!";
QByteArray responseData = serial->readAll();
while(serial->waitForReadyRead(100))
responseData += serial->readAll();
responseData.replace('\n', ' ');
QString response(responseData);
QByteArray response_arr = response.toLocal8Bit();
qDebug() << "Response is: " << response_arr.toHex();
emit this->response(response);
}
else
{
qDebug() << "Wait read response timeout";
emit this->timeout(tr("Wait read response timeout %1").arg(QTime::currentTime().toString()));
}
}
else
{
qDebug() << "Wait write request timeout!";
emit this->timeout(tr("Wait write request timeout %1").arg(QTime::currentTime().toString()));
}
I only get
Q1
as response. How can I modify my code such that I am able to read all input data?
Update:
When testing it with the serial port script described here: https://stackoverflow.com/a/7654527/2546099, everything works. Apparently the problem is that the qt-version stops reading after the first line break. This problem consists also if I add
char buffer[1000];
for(int i = 0; i < 1000; i++)
{
int tmp = serial->read(buffer, 1000);
if(tmp > 0)
qDebug() << buffer;
}
directly after the line
qDebug() << "Waiting for data!";
Then I still only get the first line (without the \n). Changing times does not change the received data.
The answer to my problem is (partly) described in this question: External vs internal declaration of QByteArray. My problem (why I did not receive any further data) was that I did not send a \x00D after the input line, thus the device just echoed my input, and was waiting for the Enter afterwards. After the input looks exactly as the first line, I misunderstood it for just getting the first line, and nothing else.
I am writing a class to parse Itunes Libray File using QXmlQuery and QT-XSLT.
Here's my sample code:
ItunesLibParser::ItunesLibParser()
{
pathToLib = QString("/Users/rakesh/temp/itunes_xslt/itunes_music_library.xml");
}
void ItunesLibParser::createXSLFile(QFile &inFile)
{
if (inFile.exists()) {
inFile.remove();
}
inFile.open(QIODevice::WriteOnly);
QTextStream out(&inFile);
out << QString("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>");
out << QString("<xsl:stylesheet version=\"2.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">");
out << QString("<xsl:output method=\"text\" />");
out << QString("<xsl:template name=\"playlistNames\">");
out << QString("<xsl:value-of select=\"child::integer[preceding-sibling::key[1]='Playlist ID']\"/>");
out << QString("<xsl:text>
</xsl:text>");
out << QString("<xsl:value-of select=\"child::string[preceding-sibling::key[1]='Name']\"/>");
out << QString("<xsl:text>
</xsl:text>");
out << QString("</xsl:template>");
out << QString("<xsl:template match=\"/\">");
out << QString("<xsl:for-each select=\"plist/dict/array/dict\">");
out << QString("<xsl:call-template name=\"playlistNames\"/>");
out << QString("</xsl:for-each>");
out << QString("</xsl:template>");
out << QString("</xsl:stylesheet>");
inFile.close();
return;
}
void ItunesLibParser::dumpPlayList()
{
QXmlQuery query(QXmlQuery::XSLT20);
query.setFocus(QUrl(pathToLib));
QFile xslFile("plist.xsl");
createXSLFile(xslFile);
query.setQuery(QUrl("plist.xsl"));
QStringList* outDump = new QStringList();
query.evaluateTo(outDump);
if(outDump != NULL) {
QStringList::iterator iter = (*outDump).begin();
for (; iter != (*outDump).end();
++iter)
//code flow doesn't come here. It means being() == end()
std::cout << (*iter).toLocal8Bit().constData() << std::endl;
}
return;
}
OutDump here doesn't contain data. While in Shell (xmlpatterns-4.7 mystlye.xsl itunes_music_library.xml ), If I run my Query I get proper output.
Is there anything, wrong I am doing while calling it programatically? I checked out plist.xsl is created properly, but my doubt is whether "/Users/rakesh/temp/itunes_xslt/itunes_music_library.xml" this is getting loaded or not? Or there might be another reasons, I am confused. Is there any experts to throw some light onto problem, I will be glad.
Intead from reading from the file, I read the file into buffer and converted that int string as passed to setquery. That solved the problem.
Here's sample code for those who could face similar problem in future.
void ITunesMlibParser::parsePlayListItemXml(int plistId)
{
QXmlQuery xQuery(QXmlQuery::XSLT20);
QFile inFile("/Users/rakesh/temp/itunes_xslt/itunes_music_library.xml");
if (!inFile.open(QIODevice::ReadOnly)) {
return;
}
QByteArray bArray;
while (!inFile.atEnd()) {
bArray += inFile.readLine();
}
QBuffer xOriginalContent(&bArray);
xOriginalContent.open(QBuffer::ReadWrite);
xOriginalContent.reset();
if (xQuery.setFocus(&xOriginalContent))
std::cout << "File Loaded" << std::endl;
//..
//..
}
Thanks
Rakesh