example 'meshtastic': make it work on android again (sqlite)

This commit is contained in:
pls.153 2023-07-01 13:32:42 +02:00
parent e15e58daaa
commit f3c80bbce4
13 changed files with 94 additions and 48 deletions

View file

@ -2,7 +2,6 @@
:serial t
:depends-on (#-depends-loaded :uiop
#-depends-loaded :cl-base64
#-depends-loaded :sqlite
#-depends-loaded :my-cl-protobufs
#-depends-loaded :trivial-package-local-nicknames)
:components ((:file "lisp/package")

View file

@ -27,10 +27,10 @@ QMAKE_EXTRA_COMPILERS += lisp
win32: PRE_TARGETDEPS = tmp/app.lib
!win32: PRE_TARGETDEPS = tmp/libapp.a
QT += quick qml bluetooth
QT += quick qml bluetooth sql
TEMPLATE = app
CONFIG += c++17 no_keywords release
DEFINES += DESKTOP_APP BACKGROUND_INI_LISP INI_ECL_CONTRIB QT_EXTENSION
DEFINES += DESKTOP_APP INI_ECL_CONTRIB QT_EXTENSION BACKGROUND_INI_LISP
INCLUDEPATH = /usr/local/include
ECL_VERSION = $$lower($$system(ecl -v))
ECL_VERSION = $$replace(ECL_VERSION, " ", "-")

View file

@ -1,5 +1,7 @@
#include "qt.h"
#include "ble_meshtastic.h"
#include <QSqlQuery>
#include <QSqlError>
#include <QtDebug>
QT_BEGIN_NAMESPACE
@ -16,6 +18,8 @@ QT::QT() : QObject() {
ble = new BLE_ME;
}
// BLE_ME
QVariant QT::startDeviceDiscovery(const QVariant& vName) {
auto name = vName.toString();
if (!name.isNull()) {
@ -43,4 +47,41 @@ QVariant QT::write2(const QVariant& bytes) {
return QVariant();
}
// SQLite
QVariant QT::iniDb(const QVariant& name) {
db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName(name.toString());
return name;
}
QVariant QT::sqlQuery(const QVariant& vQuery, const QVariant& vValues) {
// very simple, we don't need more
QVariantList results;
QSqlQuery query(db);
if (db.open()) {
query.prepare(vQuery.toString());
const QVariantList values = vValues.value<QVariantList>();
for (auto value : values) {
query.addBindValue(value);
}
if (query.exec()) {
while (query.next()) {
results << query.value(0);
}
db.close();
return results;
}
db.close();
}
QString text;
if (query.lastError().isValid()) {
text = query.lastError().text();
} else {
text = db.lastError().text();
}
qDebug() << "SQL error:" << text;
return QVariant();
}
QT_END_NAMESPACE

View file

@ -1,6 +1,7 @@
#pragma once
#include <QtCore>
#include <QSqlDatabase>
#ifdef Q_CC_MSVC
#define LIB_EXPORT __declspec(dllexport)
@ -24,9 +25,14 @@ public:
Q_INVOKABLE QVariant read2();
Q_INVOKABLE QVariant write2(const QVariant&);
// SQLite
Q_INVOKABLE QVariant iniDb(const QVariant&);
Q_INVOKABLE QVariant sqlQuery(const QVariant&, const QVariant&);
QT();
BLE_ME* ble;
QSqlDatabase db;
};
QT_END_NAMESPACE

View file

@ -1,4 +1,4 @@
QT += bluetooth
QT += bluetooth sql
TEMPLATE = lib
CONFIG += c++17 plugin release no_keywords
DEFINES += PLUGIN

View file

@ -2,33 +2,31 @@
(defvar *file* (merge-pathnames "data/db"))
(defun query (query &rest values)
(qt:sql-query qt:*cpp* query values))
(defun ini ()
(with-open-database (db *file*)
(execute-non-query
db "create table if not exists messages (mid integer primary key, uid integer, message text)")))
(ensure-directories-exist *file*)
(qt:ini-db qt:*cpp* (namestring *file*))
(query "create table if not exists messages (mid integer primary key, uid integer, message text)"))
(defun save-message (mid uid message)
(with-open-database (db *file*)
(execute-non-query
db "insert into messages (mid, uid, message) values (?, ?, ?)" mid uid message)))
(query "insert into messages (mid, uid, message) values (?, ?, ?)"
mid uid message))
(defun load-message (mid)
(with-open-database (db *file*)
(execute-single
db "select message from messages where mid = ?" mid)))
(first (query "select message from messages where mid = ?"
mid)))
(defun update-message (mid message)
(with-open-database (db *file*)
(execute-non-query
db "update messages set message = ? where mid = ?" message mid)))
(query "update messages set message = ? where mid = ?"
message mid))
(defun load-messages (uid)
(with-open-database (db *file*)
(execute-to-list
db "select message from messages where uid = ? order by mid" uid)))
(query "select message from messages where uid = ? order by mid"
uid))
(defun max-message-id ()
(or (with-open-database (db *file*)
(execute-single db "select max(mid) from messages"))
0))
(let ((val (first (query "select max(mid) from messages"))))
(if (numberp val) val 0)))

View file

@ -38,7 +38,7 @@
(defun start-device-discovery (&optional (name ""))
(setf radios:*schedule-clear* t)
(setf *ble-names* nil)
(qt:start-device-discovery qt:*ble* name)
(qt:start-device-discovery qt:*cpp* name)
(q> |playing| ui:*busy* t))
(defun start-config ()
@ -56,6 +56,8 @@
(defun send-message (text)
"Sends TEXT to radio and adds it to QML item model."
(incf msg:*message-id*)
(when (stringp *receiver*)
(setf *receiver* (name-to-node *receiver*)))
(send-to-radio
(me:make-to-radio
:packet (me:make-mesh-packet
@ -78,15 +80,15 @@
(defun read-radio ()
"Triggers a read on the radio. Will call RECEIVED-FROM-RADIO on success."
(qrun* (qt:read* qt:*ble*)))
(qrun* (qt:read* qt:*cpp*)))
(defun send-to-radio (to-radio)
"Sends passed TO-RADIO, preceded by a header."
(pr:print-json to-radio)
(let ((bytes (pr:serialize-to-bytes to-radio)))
(qrun*
(qt:write* qt:*ble* (header (length bytes)))
(qt:write* qt:*ble* bytes))))
(qt:write* qt:*cpp* (header (length bytes)))
(qt:write* qt:*cpp* bytes))))
(defun received-from-radio (bytes &optional notified) ; called from Qt
(if notified
@ -109,6 +111,11 @@
(when (= num (me:num info))
(return (me:short-name (me:user info))))))
(defun name-to-node (name)
(dolist (info *node-infos*)
(when (string= name (me:short-name (me:user info)))
(return (me:num info)))))
(defun my-name ()
(me:short-name (me:user *my-node-info*)))
@ -121,7 +128,7 @@
"Walks *RECEIVED* FROM-RADIOs and saves relevant data."
(setf *received* (nreverse *received*))
(unless *ble-names*
(setf *ble-names* (qt:short-names qt:*ble*)))
(setf *ble-names* (qt:short-names qt:*cpp*)))
(dolist (struct *received*)
(cond ((me:from-radio.has-packet struct)
(let* ((packet (me:from-radio.packet struct))
@ -201,10 +208,7 @@
(qlog "config-complete id: ~A" *config-id*)
(unless (find (my-name) (app:setting :configured) :test 'string=)
(app:change-setting :configured (my-name) :cons t)
(qlater 'config-device))))
;; rebooted
((me:from-radio.has-rebooted struct)
(qlog "rebooted: ~A" (me:from-radio.rebooted)))))
(qlater 'config-device))))))
(setf *received* nil))
(defun send-admin (admin-message)
@ -249,7 +253,7 @@
(values))
(defun change-modem-preset (modem-preset) ; called from QML
(app:change-setting :modem-reset (app:kw modem-preset))
(app:change-setting :modem-preset (app:kw modem-preset))
(qlater 'change-lora-config)
(values))

View file

@ -16,7 +16,7 @@
(x:when-it (app:setting (getf message :sender) :custom-name)
(setf (getf message :sender-name) x:it)))
(unless loading
(db:save-message (getf message :mid)
(db:save-message (parse-integer (getf message :mid))
(parse-integer (getf message (if (getf message :me) :receiver :sender))
:radix 16)
(prin1-to-string message)))
@ -43,8 +43,8 @@
(defun show-messages ()
(x:when-it (app:setting :latest-receiver)
(q! |clear| ui:*messages*)
(dolist (row (db:load-messages (parse-integer x:it :radix 16)))
(add-message (read-from-string (first row)) t))
(dolist (message (db:load-messages (parse-integer x:it :radix 16)))
(add-message (read-from-string message) t))
(q! |positionViewAtEnd| ui:*message-view*)))
(defun receiver-changed ()

View file

@ -51,7 +51,7 @@
#:set-unread))
(defpackage :db
(:use :cl :sqlite)
(:use :cl)
(:export
#:ini
#:load-message

View file

@ -1,21 +1,23 @@
(defpackage :qt
(:use :cl :qml)
(:export
#:*ble*
#:*cpp*
#:ini
#:ini-db
#:start-device-discovery
#:read*
#:short-names
#:sql-query
#:write*))
(in-package :qt)
(defvar *ble* nil)
(defvar *cpp* nil)
(defun ini ()
(setf *ble*
(setf *cpp*
#+qt-plugin (qload-c++ "cpp/qt")
#-qt-plugin (qfind-child nil "QT"))
(let ((*package* (find-package :qt)))
(define-qt-wrappers *ble*)))
(define-qt-wrappers *cpp*)))

View file

@ -1,5 +0,0 @@
import QtQuick 2.15
Rectangle {
color: "#ccebc5"
}

View file

@ -36,7 +36,10 @@ You will see a json output of all data sent/received. It simply uses the
`print-json` convenience function from cl-protobufs.
The message db uses **sqlite**, but in a lispy manner, storing basically just a
plist for every message.
plist for every message. The reason I chose Qt instead of cl-sqlite is mobile:
Qt comes with its own version, which is pulled in automatially, so one doesn't
need to care about the OS limitations or indirect requirements. Additionally,
cffi (dependency of cl-sqlite) currently needs a small hack to work on mobile.

View file

@ -7,9 +7,7 @@
(push (merge-pathnames "./")
asdf:*central-registry*)
(asdf:load-system :uiop)
(asdf:load-system :cl-base64)
(asdf:load-system :sqlite)
(asdf:load-system :trivial-package-local-nicknames)
;; may take very long on mobile devices