mirror of
https://gitlab.com/eql/lqml.git
synced 2025-12-15 14:51:14 -08:00
add new example 'meshtastic' (WIP!) -- just a proof of concept version
This commit is contained in:
parent
5f6d5b2c0f
commit
9591d26a69
87 changed files with 14836 additions and 0 deletions
12
examples/meshtastic/app.asd
Normal file
12
examples/meshtastic/app.asd
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
(defsystem :app
|
||||
:serial t
|
||||
:depends-on (#-depends-loaded :uiop
|
||||
#-depends-loaded :my-cl-protobufs
|
||||
#-depends-loaded :trivial-package-local-nicknames)
|
||||
:components ((:file "lisp/package")
|
||||
(:file "lisp/qt")
|
||||
(:file "lisp/ui-vars")
|
||||
(:file "lisp/radio")
|
||||
(:file "lisp/messages")
|
||||
(:file "lisp/main")))
|
||||
|
||||
122
examples/meshtastic/app.pro
Normal file
122
examples/meshtastic/app.pro
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
LISP_FILES = $$files(lisp/*) app.asd make.lisp
|
||||
|
||||
android {
|
||||
32bit {
|
||||
ECL = $$(ECL_ANDROID_32)
|
||||
} else {
|
||||
ECL = $$(ECL_ANDROID)
|
||||
}
|
||||
lisp.commands = $$ECL/../ecl-android-host/bin/ecl \
|
||||
-norc -shell $$PWD/make.lisp
|
||||
} else:ios {
|
||||
lisp.commands = $$(ECL_IOS)/../ecl-ios-host/bin/ecl \
|
||||
-norc -shell $$PWD/make.lisp
|
||||
} else:unix {
|
||||
lisp.commands = /usr/local/bin/ecl -shell $$PWD/make.lisp
|
||||
} else:win32 {
|
||||
lisp.commands = ecl.exe -shell $$PWD/make.lisp
|
||||
}
|
||||
|
||||
lisp.input = LISP_FILES
|
||||
|
||||
win32: lisp.output = tmp/app.lib
|
||||
!win32: lisp.output = tmp/libapp.a
|
||||
|
||||
QMAKE_EXTRA_COMPILERS += lisp
|
||||
|
||||
win32: PRE_TARGETDEPS = tmp/app.lib
|
||||
!win32: PRE_TARGETDEPS = tmp/libapp.a
|
||||
|
||||
QT += quick qml bluetooth
|
||||
TEMPLATE = app
|
||||
CONFIG += c++17 no_keywords release
|
||||
DEFINES += DESKTOP_APP INI_LISP INI_ECL_CONTRIB QT_EXTENSION
|
||||
INCLUDEPATH = /usr/local/include
|
||||
ECL_VERSION = $$lower($$system(ecl -v))
|
||||
ECL_VERSION = $$replace(ECL_VERSION, " ", "-")
|
||||
LIBS = -L/usr/local/lib -lecl
|
||||
LIBS += -L/usr/local/lib/$$ECL_VERSION
|
||||
LIBS += -lecl-help -ldeflate -lecl-cdb -lecl-curl -lql-minitar -lsockets
|
||||
DESTDIR = .
|
||||
TARGET = app
|
||||
OBJECTS_DIR = tmp
|
||||
MOC_DIR = tmp
|
||||
|
||||
linux: LIBS += -L../../../platforms/linux/lib
|
||||
macx: LIBS += -L../../../platforms/macos/lib
|
||||
win32: LIBS += -L../../../platforms/windows/lib
|
||||
|
||||
win32 {
|
||||
LIBS += -lws2_32
|
||||
|
||||
include(../../src/windows.pri)
|
||||
}
|
||||
|
||||
android {
|
||||
QT += androidextras
|
||||
DEFINES -= DESKTOP_APP
|
||||
INCLUDEPATH = $$ECL/include
|
||||
ECL_VERSION = $$lower($$system($$ECL/../ecl-android-host/bin/ecl -v))
|
||||
ECL_VERSION = $$replace(ECL_VERSION, " ", "-")
|
||||
LIBS = -L$$ECL/lib -lecl
|
||||
LIBS += -L$$ECL/lib/$$ECL_VERSION
|
||||
LIBS += -lecl-help -ldeflate -lecl-cdb -lecl-curl -lql-minitar -lsockets
|
||||
LIBS += -L../../../platforms/android/lib
|
||||
|
||||
ANDROID_EXTRA_LIBS += $$ECL/lib/libecl.so
|
||||
ANDROID_PACKAGE_SOURCE_DIR = ../platforms/android
|
||||
ANDROID_MIN_SDK_VERSION = 21
|
||||
ANDROID_TARGET_SDK_VERSION = 31
|
||||
|
||||
32bit {
|
||||
ANDROID_ABIS = "armeabi-v7a"
|
||||
} else {
|
||||
ANDROID_ABIS = "arm64-v8a"
|
||||
}
|
||||
}
|
||||
|
||||
ios {
|
||||
DEFINES -= DESKTOP_APP
|
||||
INCLUDEPATH = $$(ECL_IOS)/include
|
||||
ECL_VERSION = $$lower($$system($ECL_IOS/../ecl-ios-host/bin/ecl -v))
|
||||
ECL_VERSION = $$replace(ECL_VERSION, " ", "-")
|
||||
LIBS = -L$$(ECL_IOS)/lib -lecl
|
||||
LIBS += -leclatomic -leclffi -leclgc -leclgmp
|
||||
LIBS += -L$$(ECL_IOS)/lib/$$ECL_VERSION
|
||||
LIBS += -lecl-help -ldeflate -lecl-cdb -lecl-curl -lql-minitar -lsockets
|
||||
LIBS += -L../../../platforms/ios/lib
|
||||
|
||||
QMAKE_INFO_PLIST = platforms/ios/Info.plist
|
||||
QMAKE_ASSET_CATALOGS += platforms/ios/Assets.xcassets
|
||||
|
||||
launch.files = platforms/ios/designable.storyboard platforms/img/logo.png
|
||||
QMAKE_BUNDLE_DATA += launch
|
||||
}
|
||||
|
||||
32bit {
|
||||
LIBS += -llqml32 -llisp32
|
||||
} else {
|
||||
LIBS += -llqml -llisp
|
||||
}
|
||||
|
||||
LIBS += -Ltmp -lapp
|
||||
INCLUDEPATH += ../../../src/cpp
|
||||
|
||||
HEADERS += \
|
||||
../../src/cpp/main.h \
|
||||
cpp/ble.h \
|
||||
cpp/ble_meshtastic.h \
|
||||
cpp/qt.h
|
||||
|
||||
SOURCES += \
|
||||
../../src/cpp/main.cpp \
|
||||
cpp/ble.cpp \
|
||||
cpp/ble_meshtastic.cpp \
|
||||
cpp/qt.cpp
|
||||
|
||||
RESOURCES += $$files(qml/*)
|
||||
RESOURCES += $$files(i18n/*.qm)
|
||||
|
||||
lupdate_only {
|
||||
SOURCES += i18n/tr.h
|
||||
}
|
||||
2
examples/meshtastic/build-android/.gitignore
vendored
Normal file
2
examples/meshtastic/build-android/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
*
|
||||
!.gitignore
|
||||
3
examples/meshtastic/build-android/install-run.sh
Executable file
3
examples/meshtastic/build-android/install-run.sh
Executable file
|
|
@ -0,0 +1,3 @@
|
|||
# install/update (keeps app data)
|
||||
adb install -r android-build/*.apk
|
||||
adb shell am start -n org.qtproject.example.meshtastic/org.qtproject.qt5.android.bindings.QtActivity # Qt5
|
||||
6
examples/meshtastic/build-android/log.sh
Executable file
6
examples/meshtastic/build-android/log.sh
Executable file
|
|
@ -0,0 +1,6 @@
|
|||
# filter for logcat to show only messages from:
|
||||
# * (qlog ...) in Lisp
|
||||
# * console.log(...) in QML
|
||||
|
||||
adb logcat -c
|
||||
adb logcat -s "[LQML]"
|
||||
2
examples/meshtastic/build-ios/.gitignore
vendored
Normal file
2
examples/meshtastic/build-ios/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
*
|
||||
!.gitignore
|
||||
3
examples/meshtastic/build-ios/xcode.sh
Executable file
3
examples/meshtastic/build-ios/xcode.sh
Executable file
|
|
@ -0,0 +1,3 @@
|
|||
../../../platforms/ios/cross-compile.sh ../make.lisp
|
||||
|
||||
open app.xcodeproj
|
||||
2
examples/meshtastic/build/.gitignore
vendored
Normal file
2
examples/meshtastic/build/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
*
|
||||
!.gitignore
|
||||
173
examples/meshtastic/cpp/ble.cpp
Normal file
173
examples/meshtastic/cpp/ble.cpp
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
#include "ble.h"
|
||||
#include <QBluetoothAddress>
|
||||
#include <QBluetoothDeviceDiscoveryAgent>
|
||||
#include <QBluetoothDeviceInfo>
|
||||
#include <QBluetoothServiceDiscoveryAgent>
|
||||
#include <QList>
|
||||
#include <QMetaEnum>
|
||||
#include <QTimer>
|
||||
#include <QDebug>
|
||||
|
||||
BLE::BLE(const QBluetoothUuid& uuid) : mainServiceUuid(uuid) {
|
||||
discoveryAgent = new QBluetoothDeviceDiscoveryAgent();
|
||||
discoveryAgent->setLowEnergyDiscoveryTimeout(5000);
|
||||
|
||||
connect(discoveryAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered,
|
||||
this, &BLE::addDevice);
|
||||
connect(discoveryAgent, QOverload<QBluetoothDeviceDiscoveryAgent::Error>::of(&QBluetoothDeviceDiscoveryAgent::error),
|
||||
this, &BLE::deviceScanError);
|
||||
connect(discoveryAgent, &QBluetoothDeviceDiscoveryAgent::finished, this, &BLE::deviceScanFinished);
|
||||
|
||||
QTimer::singleShot(0, this, &BLE::startDeviceDiscovery);
|
||||
}
|
||||
|
||||
void BLE::startDeviceDiscovery() {
|
||||
devices.clear();
|
||||
qDebug() << "scanning for devices...";
|
||||
discoveryAgent->start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod);
|
||||
}
|
||||
|
||||
void BLE::addDevice(const QBluetoothDeviceInfo& device) {
|
||||
if (deviceFilter(device)) {
|
||||
qDebug() << "device added: " << device.name();
|
||||
}
|
||||
}
|
||||
|
||||
void BLE::deviceScanFinished() {
|
||||
const QList<QBluetoothDeviceInfo> found = discoveryAgent->discoveredDevices();
|
||||
for (auto device : found) {
|
||||
if (deviceFilter(device)) {
|
||||
devices << device;
|
||||
}
|
||||
}
|
||||
if (devices.isEmpty()) {
|
||||
qDebug() << "no BLE devices found";
|
||||
} else {
|
||||
qDebug() << "device scan done";
|
||||
}
|
||||
QTimer::singleShot(0, this, &BLE::scanServices);
|
||||
}
|
||||
|
||||
void BLE::scanServices() {
|
||||
if (devices.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (!currentDevice.isValid()) {
|
||||
currentDevice = devices.at(0);
|
||||
}
|
||||
services.clear();
|
||||
qDebug() << "connecting to device...";
|
||||
if (controller && (previousAddress != currentDevice.address())) {
|
||||
Q_EMIT deviceDisconnecting();
|
||||
controller->disconnectFromDevice();
|
||||
delete controller;
|
||||
controller = nullptr;
|
||||
}
|
||||
|
||||
if (!controller) {
|
||||
controller = QLowEnergyController::createCentral(currentDevice);
|
||||
connect(controller, &QLowEnergyController::connected,
|
||||
this, &BLE::deviceConnected);
|
||||
connect(controller, QOverload<QLowEnergyController::Error>::of(&QLowEnergyController::error),
|
||||
this, &BLE::errorReceived);
|
||||
connect(controller, &QLowEnergyController::disconnected,
|
||||
this, &BLE::deviceDisconnected);
|
||||
connect(controller, &QLowEnergyController::serviceDiscovered,
|
||||
this, &BLE::addLowEnergyService);
|
||||
connect(controller, &QLowEnergyController::discoveryFinished,
|
||||
this, &BLE::serviceScanDone);
|
||||
}
|
||||
|
||||
controller->connectToDevice();
|
||||
previousAddress = currentDevice.address();
|
||||
}
|
||||
|
||||
void BLE::setCurrentDevice(const QBluetoothDeviceInfo& device) {
|
||||
if (device != currentDevice) {
|
||||
currentDevice = device;
|
||||
scanned = false;
|
||||
retryScan();
|
||||
}
|
||||
}
|
||||
|
||||
void BLE::addLowEnergyService(const QBluetoothUuid& serviceUuid) {
|
||||
QLowEnergyService* service = controller->createServiceObject(serviceUuid);
|
||||
if (!service) {
|
||||
qDebug() << "cannot create service for UUID";
|
||||
return;
|
||||
}
|
||||
services << service;
|
||||
|
||||
if (serviceUuid == mainServiceUuid) {
|
||||
mainService = service;
|
||||
}
|
||||
}
|
||||
|
||||
void BLE::serviceScanDone() {
|
||||
scanned = true;
|
||||
Q_EMIT mainServiceReady();
|
||||
qDebug() << "service scan done";
|
||||
}
|
||||
|
||||
void BLE::connectToService(const QString& uuid) {
|
||||
QLowEnergyService* service = nullptr;
|
||||
for (auto s: qAsConst(services)) {
|
||||
if (s->serviceUuid().toString() == uuid) {
|
||||
service = s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!service) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (service->state() == QLowEnergyService::DiscoveryRequired) {
|
||||
service->discoverDetails();
|
||||
qDebug() << "discovering details...";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void BLE::deviceConnected() {
|
||||
connected = true;
|
||||
controller->discoverServices();
|
||||
}
|
||||
|
||||
void BLE::retryScan() {
|
||||
if (connected && !scanned) {
|
||||
QTimer::singleShot(0, this, &BLE::scanServices);
|
||||
}
|
||||
}
|
||||
|
||||
void BLE::errorReceived(QLowEnergyController::Error) {
|
||||
qDebug() << "BLE error: " << controller->errorString();
|
||||
retryScan();
|
||||
}
|
||||
|
||||
void BLE::disconnectFromDevice() {
|
||||
if (controller->state() != QLowEnergyController::UnconnectedState) {
|
||||
controller->disconnectFromDevice();
|
||||
} else {
|
||||
deviceDisconnected();
|
||||
}
|
||||
}
|
||||
|
||||
void BLE::deviceDisconnected() {
|
||||
connected = false;
|
||||
qDebug() << "disconnect from device";
|
||||
}
|
||||
|
||||
void BLE::deviceScanError(QBluetoothDeviceDiscoveryAgent::Error error) {
|
||||
if (error == QBluetoothDeviceDiscoveryAgent::PoweredOffError) {
|
||||
qDebug() << "the Bluetooth adaptor is powered off, power it on before doing discovery";
|
||||
} else if (error == QBluetoothDeviceDiscoveryAgent::InputOutputError) {
|
||||
qDebug() << "writing or reading from the device resulted in an error";
|
||||
} else {
|
||||
static QMetaEnum qme = discoveryAgent->metaObject()->enumerator(
|
||||
discoveryAgent->metaObject()->indexOfEnumerator("Error"));
|
||||
qDebug() << "error: " + QLatin1String(qme.valueToKey(error));
|
||||
}
|
||||
retryScan();
|
||||
}
|
||||
|
||||
68
examples/meshtastic/cpp/ble.h
Normal file
68
examples/meshtastic/cpp/ble.h
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
// inspired by Qt5 example 'lowenergyscanner'
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QVariant>
|
||||
#include <QList>
|
||||
#include <QBluetoothServiceDiscoveryAgent>
|
||||
#include <QBluetoothDeviceDiscoveryAgent>
|
||||
#include <QLowEnergyController>
|
||||
#include <QBluetoothServiceInfo>
|
||||
|
||||
class BLE: public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
BLE(const QBluetoothUuid& = QBluetoothUuid()); // pass 'mainServiceUuid'
|
||||
|
||||
/*** <INTERFACE> **********************************************************/
|
||||
|
||||
// main service and its UUID
|
||||
QLowEnergyService* mainService = nullptr;
|
||||
QBluetoothUuid mainServiceUuid; // see constructor
|
||||
|
||||
// current device
|
||||
QBluetoothDeviceInfo currentDevice; // if not defined, first one discovered
|
||||
QList<QBluetoothDeviceInfo> devices;
|
||||
void setCurrentDevice(const QBluetoothDeviceInfo&);
|
||||
|
||||
// custom device filter (e.g. name filter)
|
||||
virtual bool deviceFilter(const QBluetoothDeviceInfo&) { return true; }
|
||||
|
||||
Q_SIGNALS:
|
||||
// notify
|
||||
void mainServiceReady();
|
||||
void deviceDisconnecting();
|
||||
|
||||
/*** </INTERFACE> *********************************************************/
|
||||
|
||||
public Q_SLOTS:
|
||||
void startDeviceDiscovery();
|
||||
void scanServices();
|
||||
void connectToService(const QString&);
|
||||
void disconnectFromDevice();
|
||||
|
||||
private Q_SLOTS:
|
||||
// QBluetoothDeviceDiscoveryAgent related
|
||||
void addDevice(const QBluetoothDeviceInfo&);
|
||||
void deviceScanFinished();
|
||||
void deviceScanError(QBluetoothDeviceDiscoveryAgent::Error);
|
||||
|
||||
// QLowEnergyController realted
|
||||
void addLowEnergyService(const QBluetoothUuid&);
|
||||
void deviceConnected();
|
||||
void errorReceived(QLowEnergyController::Error);
|
||||
void serviceScanDone();
|
||||
void deviceDisconnected();
|
||||
|
||||
private:
|
||||
void retryScan();
|
||||
QBluetoothDeviceDiscoveryAgent* discoveryAgent;
|
||||
QList<QLowEnergyService*> services;
|
||||
QBluetoothAddress previousAddress;
|
||||
QLowEnergyController* controller = nullptr;
|
||||
bool connected = false;
|
||||
bool scanned = false;
|
||||
};
|
||||
|
||||
157
examples/meshtastic/cpp/ble_meshtastic.cpp
Normal file
157
examples/meshtastic/cpp/ble_meshtastic.cpp
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
#include "ble_meshtastic.h"
|
||||
#include <QMetaEnum>
|
||||
#include <QTimer>
|
||||
|
||||
#ifdef PLUGIN
|
||||
#include <ecl_fun_plugin.h>
|
||||
#else
|
||||
#include <ecl_fun.h>
|
||||
#endif
|
||||
|
||||
// service
|
||||
const UUID BLE_ME::uuid_service = UUID(STR("{6ba1b218-15a8-461f-9fa8-5dcae273eafd}"));
|
||||
|
||||
// characteristics
|
||||
const UUID BLE_ME::uuid_toRadio = UUID(STR("{f75c76d2-129e-4dad-a1dd-7866124401e7}"));
|
||||
const UUID BLE_ME::uuid_fromRadio = UUID(STR("{2c55e69e-4993-11ed-b878-0242ac120002}"));
|
||||
const UUID BLE_ME::uuid_fromNum = UUID(STR("{ed9da18c-a800-4f66-a670-aa7547e34453}"));
|
||||
|
||||
BLE_ME::BLE_ME() : BLE(uuid_service) {
|
||||
connect(this, &BLE::mainServiceReady, this, &BLE_ME::ini);
|
||||
connect(this, &BLE::deviceDisconnecting, this, &BLE_ME::disconnecting);
|
||||
|
||||
#ifdef PLUGIN
|
||||
ini_lisp();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool BLE_ME::deviceFilter(const QBluetoothDeviceInfo& info) {
|
||||
return info.name().contains("meshtastic", Qt::CaseInsensitive) &&
|
||||
(info.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration);
|
||||
}
|
||||
|
||||
void BLE_ME::ini() {
|
||||
connect(mainService, &QLowEnergyService::stateChanged,
|
||||
this, &BLE_ME::serviceStateChanged);
|
||||
connect(mainService, &QLowEnergyService::characteristicChanged,
|
||||
this, &BLE_ME::characteristicChanged);
|
||||
connect(mainService, &QLowEnergyService::characteristicRead,
|
||||
this, &BLE_ME::characteristicRead);
|
||||
connect(mainService, &QLowEnergyService::characteristicWritten,
|
||||
this, &BLE_ME::characteristicWritten);
|
||||
connect(mainService, QOverload<QLowEnergyService::ServiceError>::of(&QLowEnergyService::error),
|
||||
this, &BLE_ME::serviceError);
|
||||
|
||||
connect(mainService, &QLowEnergyService::descriptorWritten,
|
||||
[](const QLowEnergyDescriptor&, const QByteArray& value) {
|
||||
qDebug() << "notifications changed:" << value;
|
||||
});
|
||||
|
||||
if (mainService->state() == QLowEnergyService::DiscoveryRequired) {
|
||||
qDebug() << "discovering details...";
|
||||
mainService->discoverDetails();
|
||||
} else {
|
||||
searchCharacteristics();
|
||||
}
|
||||
}
|
||||
|
||||
void BLE_ME::serviceStateChanged(QLowEnergyService::ServiceState state) {
|
||||
qDebug() << "service state changed:" << state;
|
||||
if (state == QLowEnergyService::ServiceDiscovered) {
|
||||
searchCharacteristics();
|
||||
}
|
||||
}
|
||||
|
||||
void BLE_ME::searchCharacteristics() {
|
||||
qDebug() << "searching characteristics...";
|
||||
const auto characteristics = mainService->characteristics();
|
||||
for (auto ch : characteristics) {
|
||||
if (ch.isValid()) {
|
||||
if ((ch.properties() & QLowEnergyCharacteristic::Write) &&
|
||||
(ch.uuid() == uuid_toRadio)) { // toRadio
|
||||
toRadio = ch;
|
||||
qDebug() << "...found 'toRadio'";
|
||||
}
|
||||
if (ch.properties() & QLowEnergyCharacteristic::Read) {
|
||||
if (ch.uuid() == uuid_fromRadio) { // fromRadio
|
||||
fromRadio = ch;
|
||||
qDebug() << "...found 'fromRadio'";
|
||||
} else if (ch.uuid() == uuid_fromNum) {
|
||||
fromNum = ch;
|
||||
qDebug() << "...found 'fromNum'"; // fromNum
|
||||
|
||||
// enable notifications
|
||||
notifications = ch.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration);
|
||||
if (notifications.isValid()) {
|
||||
qDebug() << "enabling notifications...";
|
||||
mainService->writeDescriptor(notifications, QByteArray::fromHex("0100"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (toRadio.isValid() && fromRadio.isValid() && fromNum.isValid()) {
|
||||
ecl_fun("radio:set-ready");
|
||||
}
|
||||
}
|
||||
|
||||
void BLE_ME::characteristicChanged(const QLowEnergyCharacteristic&,
|
||||
const QByteArray& data) {
|
||||
if (!data.isEmpty()) {
|
||||
ecl_fun("radio:received-from-radio", data, "notified");
|
||||
}
|
||||
}
|
||||
|
||||
void BLE_ME::characteristicRead(const QLowEnergyCharacteristic&,
|
||||
const QByteArray& data) {
|
||||
if (data.isEmpty()) {
|
||||
ecl_fun("radio:receiving-done");
|
||||
} else {
|
||||
ecl_fun("radio:received-from-radio", data);
|
||||
QTimer::singleShot(0, this, &BLE_ME::read);
|
||||
}
|
||||
}
|
||||
|
||||
void BLE_ME::characteristicWritten(const QLowEnergyCharacteristic&,
|
||||
const QByteArray&) {
|
||||
QTimer::singleShot(0, this, &BLE_ME::read);
|
||||
}
|
||||
|
||||
void BLE_ME::serviceError(QLowEnergyService::ServiceError error) {
|
||||
static QMetaEnum qme = mainService->metaObject()->enumerator(
|
||||
mainService->metaObject()->indexOfEnumerator("ServiceError"));
|
||||
qDebug() << "service error:" << QLatin1String(qme.valueToKey(error));
|
||||
}
|
||||
|
||||
// read/write
|
||||
|
||||
void BLE_ME::read() {
|
||||
if ((mainService != nullptr) && fromRadio.isValid()) {
|
||||
// will call 'characteristicRead()' on success
|
||||
mainService->readCharacteristic(fromRadio);
|
||||
} else {
|
||||
qDebug() << "not ready to read";
|
||||
}
|
||||
}
|
||||
|
||||
void BLE_ME::write(const QByteArray& data) {
|
||||
if ((mainService != nullptr) && toRadio.isValid()) {
|
||||
// will call 'characteristicWritten()' on success
|
||||
mainService->writeCharacteristic(toRadio, data);
|
||||
} else {
|
||||
qDebug() << "not ready to write";
|
||||
}
|
||||
}
|
||||
|
||||
// on device disconnect
|
||||
|
||||
void BLE_ME::disconnecting() {
|
||||
if ((mainService != nullptr) && notifications.isValid()) {
|
||||
// disable notifications
|
||||
mainService->writeDescriptor(notifications, QByteArray::fromHex("0000"));
|
||||
}
|
||||
ecl_fun("radio:set-ready", false);
|
||||
delete mainService; mainService = nullptr;
|
||||
}
|
||||
|
||||
39
examples/meshtastic/cpp/ble_meshtastic.h
Normal file
39
examples/meshtastic/cpp/ble_meshtastic.h
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#include "ble.h"
|
||||
|
||||
#define UUID QBluetoothUuid
|
||||
#define STR QStringLiteral
|
||||
|
||||
class BLE_ME : public BLE {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
BLE_ME();
|
||||
|
||||
static const UUID uuid_service;
|
||||
static const UUID uuid_fromRadio;
|
||||
static const UUID uuid_fromNum;
|
||||
static const UUID uuid_toRadio;
|
||||
|
||||
QLowEnergyCharacteristic fromRadio;
|
||||
QLowEnergyCharacteristic fromNum;
|
||||
QLowEnergyCharacteristic toRadio;
|
||||
|
||||
QLowEnergyDescriptor notifications;
|
||||
|
||||
bool deviceFilter(const QBluetoothDeviceInfo&) override;
|
||||
|
||||
void write(const QByteArray&);
|
||||
void searchCharacteristics();
|
||||
|
||||
public Q_SLOTS:
|
||||
void ini();
|
||||
void read();
|
||||
void serviceStateChanged(QLowEnergyService::ServiceState);
|
||||
void characteristicChanged(const QLowEnergyCharacteristic&, const QByteArray&);
|
||||
void characteristicRead(const QLowEnergyCharacteristic&, const QByteArray&);
|
||||
void characteristicWritten(const QLowEnergyCharacteristic&, const QByteArray&);
|
||||
void serviceError(QLowEnergyService::ServiceError);
|
||||
void disconnecting();
|
||||
};
|
||||
29
examples/meshtastic/cpp/qt.cpp
Normal file
29
examples/meshtastic/cpp/qt.cpp
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#include "qt.h"
|
||||
#include "ble_meshtastic.h"
|
||||
#include <QtDebug>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QObject* ini() {
|
||||
static QObject* qt = nullptr;
|
||||
if (qt == nullptr) {
|
||||
qt = new QT;
|
||||
}
|
||||
return qt;
|
||||
}
|
||||
|
||||
QT::QT() : QObject() {
|
||||
ble = new BLE_ME;
|
||||
}
|
||||
|
||||
QVariant QT::read2() {
|
||||
ble->read();
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QVariant QT::write2(const QVariant& bytes) {
|
||||
ble->write(bytes.toByteArray());
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
30
examples/meshtastic/cpp/qt.h
Normal file
30
examples/meshtastic/cpp/qt.h
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#include <QtCore>
|
||||
|
||||
#ifdef Q_CC_MSVC
|
||||
#define LIB_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define LIB_EXPORT
|
||||
#endif
|
||||
|
||||
class BLE_ME;
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
extern "C" { LIB_EXPORT QObject* ini(); }
|
||||
|
||||
class QT : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
// BLE_ME
|
||||
Q_INVOKABLE QVariant read2();
|
||||
Q_INVOKABLE QVariant write2(const QVariant&);
|
||||
|
||||
QT();
|
||||
|
||||
BLE_ME* ble;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
34
examples/meshtastic/cpp/qt.pro
Normal file
34
examples/meshtastic/cpp/qt.pro
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
QT += bluetooth
|
||||
TEMPLATE = lib
|
||||
CONFIG += c++17 plugin release no_keywords
|
||||
DEFINES += PLUGIN
|
||||
INCLUDEPATH = /usr/local/include ../../../src/cpp
|
||||
LIBS = -L/usr/local/lib -lecl
|
||||
DESTDIR = ./
|
||||
TARGET = qt
|
||||
OBJECTS_DIR = ./tmp/
|
||||
MOC_DIR = ./tmp/
|
||||
|
||||
HEADERS += \
|
||||
ble.h \
|
||||
ble_meshtastic.h \
|
||||
qt.h
|
||||
|
||||
SOURCES += \
|
||||
ble.cpp \
|
||||
ble_meshtastic.cpp \
|
||||
qt.cpp
|
||||
|
||||
linux {
|
||||
LIBS += -L../../../platforms/linux/lib
|
||||
}
|
||||
|
||||
macx {
|
||||
LIBS += -L../../../platforms/macos/lib
|
||||
}
|
||||
|
||||
win32 {
|
||||
include(../../../src/windows.pri)
|
||||
|
||||
LIBS += -L../../../platforms/windows/lib
|
||||
}
|
||||
76
examples/meshtastic/hacks/float-features.diff
Normal file
76
examples/meshtastic/hacks/float-features.diff
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
diff --git a/_float-features.lisp b/float-features.lisp
|
||||
index b377c0d..6d56968 100644
|
||||
--- a/_float-features.lisp
|
||||
+++ b/float-features.lisp
|
||||
@@ -334,6 +334,8 @@
|
||||
(ext:single-float-to-bits float)
|
||||
#+cmucl
|
||||
(ldb (byte 32 0) (kernel:single-float-bits float))
|
||||
+ #+ecl
|
||||
+ (si:single-float-bits float)
|
||||
#+lispworks
|
||||
(let ((v (sys:make-typed-aref-vector 4)))
|
||||
(declare (optimize (speed 3) (float 0) (safety 0)))
|
||||
@@ -344,7 +346,7 @@
|
||||
(mezzano.extensions:single-float-to-ieee-binary32 float)
|
||||
#+sbcl
|
||||
(ldb (byte 32 0) (sb-kernel:single-float-bits float))
|
||||
- #-(or abcl allegro ccl clasp cmucl lispworks mezzano sbcl)
|
||||
+ #-(or abcl allegro ccl clasp cmucl ecl lispworks mezzano sbcl)
|
||||
(progn float (error "Implementation not supported.")))
|
||||
|
||||
(declaim (ftype (function (T) (unsigned-byte 64)) double-float-bits))
|
||||
@@ -364,6 +366,8 @@
|
||||
(ldb (byte 64 0)
|
||||
(logior (kernel:double-float-low-bits float)
|
||||
(ash (kernel:double-float-high-bits float) 32)))
|
||||
+ #+ecl
|
||||
+ (si:double-float-bits float)
|
||||
#+lispworks
|
||||
(let ((v (sys:make-typed-aref-vector 8)))
|
||||
(declare (optimize (speed 3) (float 0) (safety 0)))
|
||||
@@ -378,7 +382,7 @@
|
||||
(ldb (byte 64 0)
|
||||
(logior (sb-kernel:double-float-low-bits float)
|
||||
(ash (sb-kernel:double-float-high-bits float) 32)))
|
||||
- #-(or abcl allegro ccl clasp cmucl lispworks mezzano sbcl)
|
||||
+ #-(or abcl allegro ccl clasp cmucl ecl lispworks mezzano sbcl)
|
||||
(progn float (error "Implementation not supported.")))
|
||||
|
||||
(declaim (ftype (function (T) (unsigned-byte 128)) long-float-bits))
|
||||
@@ -447,6 +451,8 @@
|
||||
(flet ((s32 (x)
|
||||
(logior x (- (mask-field (byte 1 31) x))) ))
|
||||
(kernel:make-single-float (s32 bits)))
|
||||
+ #+ecl
|
||||
+ (si:bits-single-float bits)
|
||||
#+lispworks
|
||||
(let ((v (sys:make-typed-aref-vector 4)))
|
||||
(declare (optimize speed (float 0) (safety 0)))
|
||||
@@ -458,7 +464,7 @@
|
||||
#+sbcl
|
||||
(sb-kernel:make-single-float
|
||||
(sb-c::mask-signed-field 32 (the (unsigned-byte 32) bits)))
|
||||
- #-(or abcl allegro ccl clasp cmucl lispworks mezzano sbcl)
|
||||
+ #-(or abcl allegro ccl clasp cmucl ecl lispworks mezzano sbcl)
|
||||
(progn bits (error "Implementation not supported.")))
|
||||
|
||||
(declaim (ftype (function (T) double-float) bits-double-float))
|
||||
@@ -477,6 +483,8 @@
|
||||
(logior x (- (mask-field (byte 1 31) x))) ))
|
||||
(kernel:make-double-float (s32 (ldb (byte 32 32) bits))
|
||||
(ldb (byte 32 0) bits)))
|
||||
+ #+ecl
|
||||
+ (si:bits-double-float bits)
|
||||
#+lispworks
|
||||
(let ((v (sys:make-typed-aref-vector 8)))
|
||||
(declare (optimize speed (float 0) (safety 0)))
|
||||
@@ -491,7 +499,7 @@
|
||||
(sb-kernel:make-double-float
|
||||
(sb-c::mask-signed-field 32 (ldb (byte 32 32) (the (unsigned-byte 64) bits)))
|
||||
(ldb (byte 32 0) bits))
|
||||
- #-(or abcl allegro ccl clasp cmucl lispworks mezzano sbcl)
|
||||
+ #-(or abcl allegro ccl clasp cmucl ecl lispworks mezzano sbcl)
|
||||
(progn bits (error "Implementation not supported.")))
|
||||
|
||||
(declaim (ftype (function (T) long-float) bits-long-float))
|
||||
508
examples/meshtastic/hacks/float-features.lisp
Normal file
508
examples/meshtastic/hacks/float-features.lisp
Normal file
|
|
@ -0,0 +1,508 @@
|
|||
#|
|
||||
This file is a part of float-features
|
||||
(c) 2018 Shirakumo http://tymoon.eu (shinmera@tymoon.eu)
|
||||
Author: Nicolas Hafner <shinmera@tymoon.eu>
|
||||
|#
|
||||
|
||||
(defpackage #:float-features
|
||||
(:nicknames #:org.shirakumo.float-features)
|
||||
(:use #:cl)
|
||||
(:export
|
||||
#:short-float-positive-infinity
|
||||
#:short-float-negative-infinity
|
||||
#:short-float-nan
|
||||
#:single-float-positive-infinity
|
||||
#:single-float-negative-infinity
|
||||
#:single-float-nan
|
||||
#:double-float-positive-infinity
|
||||
#:double-float-negative-infinity
|
||||
#:double-float-nan
|
||||
#:long-float-positive-infinity
|
||||
#:long-float-negative-infinity
|
||||
#:long-float-nan
|
||||
#:float-infinity-p
|
||||
#:float-nan-p
|
||||
#:with-float-traps-masked
|
||||
#:short-float-bits
|
||||
#:single-float-bits
|
||||
#:double-float-bits
|
||||
#:long-float-bits
|
||||
#:bits-short-float
|
||||
#:bits-single-float
|
||||
#:bits-double-float
|
||||
#:bits-long-float))
|
||||
|
||||
(in-package #:org.shirakumo.float-features)
|
||||
|
||||
(defconstant short-float-positive-infinity
|
||||
#+ccl 1S++0
|
||||
#+clasp ext:short-float-positive-infinity
|
||||
#+cmucl extensions:short-float-positive-infinity
|
||||
#+ecl ext:short-float-positive-infinity
|
||||
#+mezzano mezzano.extensions:short-float-positive-infinity
|
||||
#+mkcl ext:short-float-positive-infinity
|
||||
#+sbcl sb-ext:short-float-positive-infinity
|
||||
#+lispworks 1S++0
|
||||
#+allegro (coerce excl:*infinity-single* 'short-float)
|
||||
#-(or ccl clasp cmucl ecl mezzano mkcl sbcl lispworks allegro)
|
||||
most-positive-short-float)
|
||||
|
||||
(defconstant short-float-negative-infinity
|
||||
#+ccl -1S++0
|
||||
#+clasp ext:short-float-negative-infinity
|
||||
#+cmucl extensions:short-float-negative-infinity
|
||||
#+ecl ext:short-float-negative-infinity
|
||||
#+mezzano mezzano.extensions:short-float-negative-infinity
|
||||
#+mkcl ext:short-float-negative-infinity
|
||||
#+sbcl sb-ext:short-float-negative-infinity
|
||||
#+lispworks -1S++0
|
||||
#+allegro (coerce excl:*negative-infinity-single* 'short-float)
|
||||
#-(or ccl clasp cmucl ecl mezzano mkcl sbcl lispworks allegro)
|
||||
most-negative-short-float)
|
||||
|
||||
(defconstant single-float-positive-infinity
|
||||
#+abcl extensions:single-float-positive-infinity
|
||||
#+allegro excl:*infinity-single*
|
||||
#+ccl 1F++0
|
||||
#+clasp ext:single-float-positive-infinity
|
||||
#+cmucl extensions:single-float-positive-infinity
|
||||
#+ecl ext:single-float-positive-infinity
|
||||
#+mezzano mezzano.extensions:single-float-positive-infinity
|
||||
#+mkcl mkcl:single-float-positive-infinity
|
||||
#+sbcl sb-ext:single-float-positive-infinity
|
||||
#+lispworks 1F++0
|
||||
#-(or abcl allegro ccl clasp cmucl ecl mezzano mkcl sbcl lispworks)
|
||||
most-positive-single-float)
|
||||
|
||||
(defconstant single-float-negative-infinity
|
||||
#+abcl extensions:single-float-negative-infinity
|
||||
#+allegro excl:*negative-infinity-single*
|
||||
#+ccl -1F++0
|
||||
#+clasp ext:single-float-negative-infinity
|
||||
#+cmucl extensions:single-float-negative-infinity
|
||||
#+ecl ext:single-float-negative-infinity
|
||||
#+mezzano mezzano.extensions:single-float-negative-infinity
|
||||
#+mkcl mkcl:single-float-negative-infinity
|
||||
#+sbcl sb-ext:single-float-negative-infinity
|
||||
#+lispworks -1F++0
|
||||
#-(or abcl allegro ccl clasp cmucl ecl mezzano mkcl sbcl lispworks)
|
||||
most-negative-single-float)
|
||||
|
||||
(defconstant double-float-positive-infinity
|
||||
#+abcl extensions:double-float-positive-infinity
|
||||
#+allegro excl:*infinity-double*
|
||||
#+ccl 1D++0
|
||||
#+clasp ext:double-float-positive-infinity
|
||||
#+cmucl extensions:double-float-positive-infinity
|
||||
#+ecl ext:double-float-positive-infinity
|
||||
#+mezzano mezzano.extensions:double-float-positive-infinity
|
||||
#+mkcl mkcl:double-float-positive-infinity
|
||||
#+sbcl sb-ext:double-float-positive-infinity
|
||||
#+lispworks 1D++0
|
||||
#-(or abcl allegro ccl clasp cmucl ecl mezzano mkcl sbcl lispworks)
|
||||
most-positive-double-float)
|
||||
|
||||
(defconstant double-float-negative-infinity
|
||||
#+abcl extensions:double-float-negative-infinity
|
||||
#+allegro excl:*negative-infinity-double*
|
||||
#+ccl -1D++0
|
||||
#+clasp ext:double-float-negative-infinity
|
||||
#+cmucl extensions:double-float-negative-infinity
|
||||
#+ecl ext:double-float-negative-infinity
|
||||
#+mezzano mezzano.extensions:double-float-negative-infinity
|
||||
#+mkcl mkcl:double-float-negative-infinity
|
||||
#+sbcl sb-ext:double-float-negative-infinity
|
||||
#+lispworks -1D++0
|
||||
#-(or abcl allegro ccl clasp cmucl ecl mezzano mkcl sbcl lispworks)
|
||||
most-negative-double-float)
|
||||
|
||||
(defconstant long-float-positive-infinity
|
||||
#+ccl 1L++0
|
||||
#+clasp ext:long-float-positive-infinity
|
||||
#+cmucl extensions:long-float-positive-infinity
|
||||
#+ecl ext:long-float-positive-infinity
|
||||
#+mezzano mezzano.extensions:long-float-positive-infinity
|
||||
#+mkcl ext:long-float-positive-infinity
|
||||
#+sbcl sb-ext:long-float-positive-infinity
|
||||
#+lispworks 1L++0
|
||||
#-(or ccl clasp cmucl ecl mezzano mkcl sbcl lispworks)
|
||||
most-positive-long-float)
|
||||
|
||||
(defconstant long-float-negative-infinity
|
||||
#+ccl -1L++0
|
||||
#+clasp ext:long-float-negative-infinity
|
||||
#+cmucl extensions:long-float-negative-infinity
|
||||
#+ecl ext:long-float-negative-infinity
|
||||
#+mezzano mezzano.extensions:long-float-negative-infinity
|
||||
#+mkcl ext:long-float-negative-infinity
|
||||
#+sbcl sb-ext:long-float-negative-infinity
|
||||
#+lispworks -1L++0
|
||||
#-(or ccl clasp cmucl ecl mezzano mkcl sbcl lispworks)
|
||||
most-negative-long-float)
|
||||
|
||||
(declaim (inline float-infinity-p
|
||||
float-nan-p))
|
||||
|
||||
(defun float-infinity-p (float)
|
||||
#+abcl (system:float-infinity-p float)
|
||||
#+allegro (excl:infinityp float)
|
||||
#+ccl (ccl::infinity-p float)
|
||||
#+clasp (ext:float-infinity-p float)
|
||||
#+cmucl (extensions:float-infinity-p float)
|
||||
#+ecl (ext:float-infinity-p float)
|
||||
#+mezzano (mezzano.extensions:float-infinity-p float)
|
||||
#+sbcl (sb-ext:float-infinity-p float)
|
||||
#-(or abcl allegro ccl clasp cmucl ecl mezzano sbcl)
|
||||
(etypecase float
|
||||
(short-float (or (= float short-float-negative-infinity)
|
||||
(= float short-float-positive-infinity)))
|
||||
(single-float (or (= float single-float-negative-infinity)
|
||||
(= float single-float-positive-infinity)))
|
||||
(double-float (or (= float double-float-negative-infinity)
|
||||
(= float double-float-positive-infinity)))
|
||||
(long-float (or (= float long-float-negative-infinity)
|
||||
(= float long-float-positive-infinity)))))
|
||||
|
||||
(defun float-nan-p (float)
|
||||
#+abcl (system:float-nan-p float)
|
||||
#+allegro (excl:nanp float)
|
||||
#+ccl (and (ccl::nan-or-infinity-p float)
|
||||
(not (ccl::infinity-p float)))
|
||||
#+clasp (ext:float-nan-p float)
|
||||
#+cmucl (extensions:float-nan-p float)
|
||||
#+ecl (ext:float-nan-p float)
|
||||
#+mezzano (mezzano.extensions:float-nan-p float)
|
||||
#+sbcl (sb-ext:float-nan-p float)
|
||||
#+lispworks (sys::nan-p float)
|
||||
#-(or abcl allegro ccl clasp cmucl ecl mezzano sbcl lispworks)
|
||||
(/= float float))
|
||||
|
||||
(defun keep (list &rest keeps)
|
||||
(loop for item in list
|
||||
when (find item keeps)
|
||||
collect item))
|
||||
|
||||
(defmacro with-float-traps-masked (traps &body body)
|
||||
(let ((traps (etypecase traps
|
||||
((eql T) '(:underflow :overflow :inexact :invalid :divide-by-zero :denormalized-operand))
|
||||
(list traps))))
|
||||
#+abcl
|
||||
(let ((previous (gensym "PREVIOUS")))
|
||||
`(let ((,previous (extensions:get-floating-point-modes)))
|
||||
(unwind-protect
|
||||
(progn
|
||||
(extensions:set-floating-point-modes
|
||||
:traps ',(keep traps :overflow :underflow))
|
||||
NIL ,@body)
|
||||
(apply #'extensions:set-floating-point-modes ,previous))))
|
||||
#+ccl
|
||||
(let ((previous (gensym "PREVIOUS"))
|
||||
(traps (loop for thing in traps
|
||||
for trap = (case thing
|
||||
(:underflow :underflow)
|
||||
(:overflow :overflow)
|
||||
(:divide-by-zero :division-by-zero)
|
||||
(:invalid :invalid)
|
||||
(:inexact :inexact))
|
||||
when trap collect trap)))
|
||||
`(let ((,previous (ccl:get-fpu-mode)))
|
||||
(unwind-protect
|
||||
(progn
|
||||
(ccl:set-fpu-mode
|
||||
,@(loop for trap in traps
|
||||
collect trap collect NIL))
|
||||
NIL ,@body)
|
||||
(apply #'ccl:set-fpu-mode ,previous))))
|
||||
#+clisp
|
||||
(if (find :underflow)
|
||||
`(ext:without-floating-point-underflow
|
||||
,@body)
|
||||
`(progn
|
||||
,@body))
|
||||
#+cmucl
|
||||
`(extensions:with-float-traps-masked #+x86 ,traps #-x86 ,(remove :denormalized-operand traps)
|
||||
,@body)
|
||||
#+ecl
|
||||
(let ((previous (gensym "PREVIOUS")))
|
||||
`(let ((,previous (si::trap-fpe :last T)))
|
||||
(unwind-protect
|
||||
(progn
|
||||
,@(loop for trap in traps
|
||||
for keyword = (case trap
|
||||
(:underlow :floating-point-underflow)
|
||||
(:overflow :floating-point-overflow)
|
||||
(:inexact :floating-point-inexact)
|
||||
(:invalid :floating-point-invalid)
|
||||
(:divide-by-zero :division-by-zero))
|
||||
when keyword collect `(si::trap-fpe ,keyword T))
|
||||
NIL ,@body)
|
||||
(si::trap-fpe ,previous NIL))))
|
||||
#+clasp
|
||||
`(ext:with-float-traps-masked ,traps
|
||||
,@body)
|
||||
#+mezzano
|
||||
(let ((previous (gensym "PREVIOUS"))
|
||||
(traps (loop for thing in traps
|
||||
for trap = (case thing
|
||||
(:underflow :underflow)
|
||||
(:overflow :overflow)
|
||||
(:divide-by-zero :divide-by-zero)
|
||||
(:invalid :invalid-operation)
|
||||
(:inexact :precision)
|
||||
#+x86-64
|
||||
(:denormalized-operand :denormal-operand))
|
||||
when trap collect trap)))
|
||||
`(let ((,previous (mezzano.runtime::get-fpu-mode)))
|
||||
(unwind-protect
|
||||
(progn
|
||||
(mezzano.runtime::set-fpu-mode
|
||||
,@(loop for trap in traps
|
||||
collect trap collect T))
|
||||
NIL ,@body)
|
||||
(apply #'mezzano.runtime::set-fpu-mode ,previous))))
|
||||
#+sbcl
|
||||
`(sb-int:with-float-traps-masked #+x86 ,traps #-x86 ,(remove :denormalized-operand traps)
|
||||
,@body)
|
||||
#-(or abcl ccl clasp clisp cmucl ecl mezzano sbcl)
|
||||
(declare (ignore traps))
|
||||
#-(or abcl ccl clasp clisp cmucl ecl mezzano sbcl)
|
||||
`(progn ,@body)))
|
||||
|
||||
(declaim (inline short-float-bits
|
||||
single-float-bits
|
||||
double-float-bits
|
||||
long-float-bits
|
||||
bits-short-float
|
||||
bits-single-float
|
||||
bits-double-float
|
||||
bits-long-float))
|
||||
|
||||
(declaim (ftype (function (T) (unsigned-byte 16)) short-float-bits))
|
||||
(defun short-float-bits (float)
|
||||
(declare (ignorable float))
|
||||
#+mezzano
|
||||
(mezzano.extensions:short-float-to-ieee-binary16 float)
|
||||
#+(or ecl sbcl cmucl allegro ccl
|
||||
(and 64-bit lispworks))
|
||||
(let* ((bits (single-float-bits float))
|
||||
(sign (ldb (byte 1 31) bits))
|
||||
(exp (- (ldb (byte 8 23) bits) 127))
|
||||
(sig (ldb (byte 23 0) bits)))
|
||||
(cond
|
||||
((or (eql 0s0 float)
|
||||
(< exp -24))
|
||||
;;underflow
|
||||
(ash sign 15))
|
||||
((< exp -14)
|
||||
;; encode as denormal if possible
|
||||
(logior (ash sign 15)
|
||||
0
|
||||
(ash (ldb (byte 11 13)
|
||||
(logior (ash 1 23) sig))
|
||||
(+ exp 14))))
|
||||
((< exp 16)
|
||||
;; encode directly
|
||||
(logior (ash sign 15)
|
||||
(ash (+ exp 15) 10)
|
||||
(ash sig -13)))
|
||||
((zerop sig)
|
||||
;; infinity
|
||||
(if (zerop sign)
|
||||
#b0111110000000000
|
||||
#b1111110000000000))
|
||||
(t
|
||||
;;NaN
|
||||
(logior (ash sign 15)
|
||||
(ash #x1f 10)
|
||||
(ldb (byte 10 13) sig)))))
|
||||
;; clisp short-float is 1+8+16
|
||||
;; 32bit lispworks 5+ is 1+8+??, lw4 only has double
|
||||
;; not sure about others?
|
||||
#- (or mezzano ecl sbcl cmucl allegro ccl (and 64-bit lispworks))
|
||||
(progn float (error "Implementation not supported.")))
|
||||
|
||||
(declaim (ftype (function (T) (unsigned-byte 32)) single-float-bits))
|
||||
(defun single-float-bits (float)
|
||||
#+abcl
|
||||
(ldb (byte 32 0) (system:single-float-bits float))
|
||||
#+allegro
|
||||
(multiple-value-bind (high low) (excl:single-float-to-shorts float)
|
||||
(logior low (ash high 16)))
|
||||
#+ccl
|
||||
(ccl::single-float-bits float)
|
||||
#+clasp
|
||||
(ext:single-float-to-bits float)
|
||||
#+cmucl
|
||||
(ldb (byte 32 0) (kernel:single-float-bits float))
|
||||
#+ecl
|
||||
(si:single-float-bits float)
|
||||
#+lispworks
|
||||
(let ((v (sys:make-typed-aref-vector 4)))
|
||||
(declare (optimize (speed 3) (float 0) (safety 0)))
|
||||
(declare (dynamic-extent v))
|
||||
(setf (sys:typed-aref 'single-float v 0) float)
|
||||
(sys:typed-aref '(unsigned-byte 32) v 0))
|
||||
#+mezzano
|
||||
(mezzano.extensions:single-float-to-ieee-binary32 float)
|
||||
#+sbcl
|
||||
(ldb (byte 32 0) (sb-kernel:single-float-bits float))
|
||||
#-(or abcl allegro ccl clasp cmucl ecl lispworks mezzano sbcl)
|
||||
(progn float (error "Implementation not supported.")))
|
||||
|
||||
(declaim (ftype (function (T) (unsigned-byte 64)) double-float-bits))
|
||||
(defun double-float-bits (float)
|
||||
#+abcl
|
||||
(logior (system::double-float-low-bits float)
|
||||
(ash (system::double-float-high-bits float) 32))
|
||||
#+allegro
|
||||
(multiple-value-bind (s3 s2 s1 s0) (excl:double-float-to-shorts float)
|
||||
(logior s0 (ash s1 16) (ash s2 32) (ash s3 48)))
|
||||
#+ccl
|
||||
(multiple-value-bind (high low) (ccl::double-float-bits float)
|
||||
(logior low (ash high 32)))
|
||||
#+clasp
|
||||
(ext:double-float-to-bits float)
|
||||
#+cmucl
|
||||
(ldb (byte 64 0)
|
||||
(logior (kernel:double-float-low-bits float)
|
||||
(ash (kernel:double-float-high-bits float) 32)))
|
||||
#+ecl
|
||||
(si:double-float-bits float)
|
||||
#+lispworks
|
||||
(let ((v (sys:make-typed-aref-vector 8)))
|
||||
(declare (optimize (speed 3) (float 0) (safety 0)))
|
||||
(declare (dynamic-extent v))
|
||||
(setf (sys:typed-aref 'double-float v 0) float)
|
||||
#+x86-64 (sys:typed-aref '(unsigned-byte 64) v 0)
|
||||
#-x64-64 (logior (sys:typed-aref '(unsigned-byte 32) v 0)
|
||||
(ash (sys:typed-aref '(unsigned-byte 32) v 4) 32)))
|
||||
#+mezzano
|
||||
(mezzano.extensions:double-float-to-ieee-binary64 float)
|
||||
#+sbcl
|
||||
(ldb (byte 64 0)
|
||||
(logior (sb-kernel:double-float-low-bits float)
|
||||
(ash (sb-kernel:double-float-high-bits float) 32)))
|
||||
#-(or abcl allegro ccl clasp cmucl ecl lispworks mezzano sbcl)
|
||||
(progn float (error "Implementation not supported.")))
|
||||
|
||||
(declaim (ftype (function (T) (unsigned-byte 128)) long-float-bits))
|
||||
(defun long-float-bits (float)
|
||||
(declare (ignore float))
|
||||
(error "Implementation not supported."))
|
||||
|
||||
(declaim (ftype (function (T) short-float) bits-short-float))
|
||||
(defun bits-short-float (bits)
|
||||
(declare (ignorable bits))
|
||||
#+mezzano
|
||||
(mezzano.extensions:ieee-binary16-to-short-float bits)
|
||||
#+ (or ecl sbcl cmucl allegro ccl
|
||||
(and 64-bit lispworks))
|
||||
|
||||
(let ((sign (ldb (byte 1 15) bits))
|
||||
(exp (ldb (byte 5 10) bits))
|
||||
(sig (ldb (byte 10 0) bits)))
|
||||
(if (= exp 31)
|
||||
(cond
|
||||
((not (zerop sig))
|
||||
;; NaNs
|
||||
(bits-single-float
|
||||
(logior (ash sign 31)
|
||||
(ash #xff 23)
|
||||
;; store in high-bit to preserve quiet/signalling
|
||||
(ash sig 13))))
|
||||
;; infinities
|
||||
((zerop sign)
|
||||
single-float-positive-infinity)
|
||||
(t
|
||||
single-float-negative-infinity))
|
||||
(cond
|
||||
((= 0 exp sig)
|
||||
;; +- 0
|
||||
(if (zerop sign) 0s0 -0s0))
|
||||
((zerop exp)
|
||||
;; denormals -> single floats
|
||||
(let ((d (- 11 (integer-length sig))))
|
||||
(setf exp (- -14 d))
|
||||
(setf sig (ldb (byte 11 0) (ash sig (1+ d))))
|
||||
(bits-single-float
|
||||
(logior (ash sign 31)
|
||||
(ash (+ exp 127) 23)
|
||||
(ash sig #.(- 23 11))))))
|
||||
(t
|
||||
;; normal numbers
|
||||
(bits-single-float
|
||||
(logior (ash sign 31)
|
||||
(ash (+ exp #.(+ 127 -15)) 23)
|
||||
(ash sig #.(- 23 10))))))))
|
||||
#- (or mezzano ecl sbcl cmucl allegro ccl (and 64-bit lispworks))
|
||||
(progn bits (error "Implementation not supported.")))
|
||||
|
||||
(declaim (ftype (function (T) single-float) bits-single-float))
|
||||
(defun bits-single-float (bits)
|
||||
#+abcl
|
||||
(system:make-single-float bits)
|
||||
#+allegro
|
||||
(excl:shorts-to-single-float (ldb (byte 16 16) bits) (ldb (byte 16 0) bits))
|
||||
#+ccl
|
||||
(ccl::host-single-float-from-unsigned-byte-32 bits)
|
||||
#+clasp
|
||||
(ext:bits-to-single-float bits)
|
||||
#+cmucl
|
||||
(flet ((s32 (x)
|
||||
(logior x (- (mask-field (byte 1 31) x))) ))
|
||||
(kernel:make-single-float (s32 bits)))
|
||||
#+ecl
|
||||
(si:bits-single-float bits)
|
||||
#+lispworks
|
||||
(let ((v (sys:make-typed-aref-vector 4)))
|
||||
(declare (optimize speed (float 0) (safety 0)))
|
||||
(declare (dynamic-extent v))
|
||||
(setf (sys:typed-aref '(unsigned-byte 32) v 0) bits)
|
||||
(sys:typed-aref 'single-float v 0))
|
||||
#+mezzano
|
||||
(mezzano.extensions:ieee-binary32-to-single-float bits)
|
||||
#+sbcl
|
||||
(sb-kernel:make-single-float
|
||||
(sb-c::mask-signed-field 32 (the (unsigned-byte 32) bits)))
|
||||
#-(or abcl allegro ccl clasp cmucl ecl lispworks mezzano sbcl)
|
||||
(progn bits (error "Implementation not supported.")))
|
||||
|
||||
(declaim (ftype (function (T) double-float) bits-double-float))
|
||||
(defun bits-double-float (bits)
|
||||
#+abcl
|
||||
(system:make-double-float bits)
|
||||
#+allegro
|
||||
(excl:shorts-to-double-float
|
||||
(ldb (byte 16 48) bits) (ldb (byte 16 32) bits) (ldb (byte 16 16) bits) (ldb (byte 16 0) bits))
|
||||
#+ccl
|
||||
(ccl::double-float-from-bits (ldb (byte 32 32) bits) (ldb (byte 32 0) bits))
|
||||
#+clasp
|
||||
(ext:bits-to-double-float bits)
|
||||
#+cmucl
|
||||
(flet ((s32 (x)
|
||||
(logior x (- (mask-field (byte 1 31) x))) ))
|
||||
(kernel:make-double-float (s32 (ldb (byte 32 32) bits))
|
||||
(ldb (byte 32 0) bits)))
|
||||
#+ecl
|
||||
(si:bits-double-float bits)
|
||||
#+lispworks
|
||||
(let ((v (sys:make-typed-aref-vector 8)))
|
||||
(declare (optimize speed (float 0) (safety 0)))
|
||||
(declare (dynamic-extent v))
|
||||
#+x86-64 (setf (sys:typed-aref '(unsigned-byte 64) v 0) bits)
|
||||
#-x86-64 (setf (sys:typed-aref '(unsigned-byte 32) v 0) (ldb (byte 32 0) bits)
|
||||
(sys:typed-aref '(unsigned-byte 32) v 4) (ldb (byte 32 32) bits))
|
||||
(sys:typed-aref 'double-float v 0))
|
||||
#+mezzano
|
||||
(mezzano.extensions:ieee-binary64-to-double-float bits)
|
||||
#+sbcl
|
||||
(sb-kernel:make-double-float
|
||||
(sb-c::mask-signed-field 32 (ldb (byte 32 32) (the (unsigned-byte 64) bits)))
|
||||
(ldb (byte 32 0) bits))
|
||||
#-(or abcl allegro ccl clasp cmucl ecl lispworks mezzano sbcl)
|
||||
(progn bits (error "Implementation not supported.")))
|
||||
|
||||
(declaim (ftype (function (T) long-float) bits-long-float))
|
||||
(defun bits-long-float (bits)
|
||||
(declare (ignore bits))
|
||||
(error "Implementation not supported."))
|
||||
5
examples/meshtastic/hacks/readme.md
Normal file
5
examples/meshtastic/hacks/readme.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
Before applying this patch, please install latest ECL from development branch
|
||||
(as of May 2023).
|
||||
|
||||
Just copy **float-features** from Quicklisp under
|
||||
`~/quicklisp/local-projects/` and apply patch (or copy `float-features.lisp`).
|
||||
31
examples/meshtastic/i18n/readme.md
Normal file
31
examples/meshtastic/i18n/readme.md
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
|
||||
Translations
|
||||
------------
|
||||
|
||||
Wrap all strings which need to be translated in either `(tr "")` (Lisp files)
|
||||
or `qsTr("")` (QML files).
|
||||
|
||||
* compile app (either desktop or mobile, you may need `touch ../app.asd` to
|
||||
force recompilation of all files); this will generate a dummy file `tr.h`,
|
||||
containing all Lisp strings to translate
|
||||
|
||||
* run Qt command `lupdate` (here: Spanish, French) for creating the translation
|
||||
source files from both Lisp and QML strings:
|
||||
```
|
||||
lupdate ../app.pro -ts es.ts fr.ts
|
||||
```
|
||||
* translate all `*.ts` files using **Qt Linguist**
|
||||
|
||||
* run Qt command `lrelease` to create compiled translation files:
|
||||
```
|
||||
lrelease es.ts fr.ts
|
||||
```
|
||||
* run respective `qmake` again (destop/mobile) in order to include all `*.qm`
|
||||
files (compiled translations)
|
||||
|
||||
* next time you compile the app, the translation files will be included as
|
||||
resources in the executable
|
||||
|
||||
Now when you launch the app, the translation file matching your system locale
|
||||
setting of your platform (see `QLocale`) will be loaded, see `QTranslator` in
|
||||
[main.cpp](../../../src/cpp/main.cpp).
|
||||
23
examples/meshtastic/lisp/cl-protobufs/LICENSE
Normal file
23
examples/meshtastic/lisp/cl-protobufs/LICENSE
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
The MIT License
|
||||
|
||||
Copyright (c) 2012-2020 Google LLC
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use, copy,
|
||||
modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
890
examples/meshtastic/lisp/cl-protobufs/buffers.lisp
Normal file
890
examples/meshtastic/lisp/cl-protobufs/buffers.lisp
Normal file
|
|
@ -0,0 +1,890 @@
|
|||
;;; Copyright 2012-2020 Google LLC
|
||||
;;;
|
||||
;;; Use of this source code is governed by an MIT-style
|
||||
;;; license that can be found in the LICENSE file or at
|
||||
;;; https://opensource.org/licenses/MIT.
|
||||
|
||||
(in-package #:cl-protobufs.implementation)
|
||||
|
||||
;;; This file provides a stream-like abstraction, a BUFFER, that Protobuf serialization
|
||||
;;; logic can use to perform a one-pass traversal of the input object tree such that
|
||||
;;; all variable-length pieces are properly length-prefixed but without having to
|
||||
;;; precompute lengths. This differs from the C implementation of serialization,
|
||||
;;; which (by default) requires a pre-pass to compute the lengths for all constituent
|
||||
;;; variable-length pieces such as strings and sub-messages.
|
||||
|
||||
(eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(defparameter $optimize-buffering *optimize-fast-unsafe*)) ; NOLINT
|
||||
|
||||
(deftype array-index ()
|
||||
#+sbcl 'sb-int:index
|
||||
#-sbcl `(integer 0 ,(1- array-total-size-limit)))
|
||||
|
||||
;; A BUFFER is a linked list of blocks (vectors) of unsigned-byte.
|
||||
;; It can more-or-less be thought of as a string-output-stream that accepts
|
||||
;; (UNSIGNED-BYTE n) as the element-type, instead of character, and which
|
||||
;; allows replacement of previously written bytes. CONCATENATE-BLOCKS
|
||||
;; is the analogous operation to GET-OUTPUT-STREAM-STRING. It produces a
|
||||
;; single vector of all bytes that were written.
|
||||
;; This structure has subtypes for 8-bit octets and 32-bit words.
|
||||
(defstruct (buffer (:constructor nil))
|
||||
;; The current block
|
||||
(block nil :type (simple-array * 1))
|
||||
;; Index into current block at which next element may be written.
|
||||
;; The block is full when index is equal to (LENGTH BLOCK).
|
||||
(index 0 :type (unsigned-byte 28))
|
||||
;; The entire list of blocks
|
||||
(chain nil :type cons)
|
||||
;; The cons cell whose car is BLOCK. This slot acts primarily
|
||||
;; to optimize nconc onto CHAIN. It is not necessarily the last
|
||||
;; cons in CHAIN, but usually it is.
|
||||
(next nil :type cons)
|
||||
;; Zero-based absolute position of the first element of this block in
|
||||
;; the overall output. Updated only when assigning a new BLOCK.
|
||||
(%block-absolute-start 0 :type array-index))
|
||||
|
||||
(defmethod print-object ((self buffer) stream)
|
||||
(print-unreadable-object (self stream :type t :identity t)))
|
||||
|
||||
;; BUFFER-SAP is a macro because it makes little sense to write a function
|
||||
;; that returns a pointer to something that can go stale on you.
|
||||
;; Otherwise any extraction of a SAP from the buffer would be reliable only
|
||||
;; within the scope of a WITHOUT-GCING or WITH-PINNED-OBJECTS.
|
||||
;; It would work as an inline function, but this forces the right behavior.
|
||||
#+sbcl
|
||||
(defmacro buffer-sap (buffer)
|
||||
`(sb-sys:vector-sap (buffer-block ,buffer)))
|
||||
|
||||
(defun-inline buffer-block-capacity (buffer)
|
||||
(declare (optimize (safety 0)))
|
||||
(length (buffer-block buffer)))
|
||||
|
||||
(defun-inline buffer-absolute-position (buffer)
|
||||
(i+ (buffer-%block-absolute-start buffer)
|
||||
(buffer-index buffer)))
|
||||
|
||||
(defun make-buffer (constructor block)
|
||||
(let ((chain (list block)))
|
||||
(funcall (the function constructor) block chain chain)))
|
||||
|
||||
(deftype octet-type () '(unsigned-byte 8))
|
||||
(deftype word-buffer-block-type () '(simple-array (unsigned-byte 32) 1))
|
||||
|
||||
(defstruct (word-buffer (:include buffer (block nil :type word-buffer-block-type))
|
||||
(:constructor %make-word-buffer (block chain next))))
|
||||
|
||||
(defun make-word-buffer (size)
|
||||
(declare (array-index size))
|
||||
(make-buffer #'%make-word-buffer
|
||||
(make-array size :element-type '(unsigned-byte 32))))
|
||||
|
||||
(defstruct (octet-buffer (:include buffer
|
||||
(block nil :type (simple-array octet-type 1)))
|
||||
(:constructor %make-octet-buffer (block chain next)))
|
||||
;; The collection of backpatches is itself a word buffer
|
||||
(backpatches (make-word-buffer 10))
|
||||
;; When copying a fixed-size wire-level primitive that crosses a block boundary,
|
||||
;; use the scratchpad first, then copy two subsequences of octets.
|
||||
(scratchpad (make-array 8 :element-type '(unsigned-byte 8)))
|
||||
(n-gap-bytes 0 :type fixnum)
|
||||
(target nil) ; the destination of these octets, a STREAM typically
|
||||
;; The BUFFER can also pretend to be stream by implementing CHAR-OUT
|
||||
;; and STRING-OUT methods. The buffer and stream point to each other.
|
||||
;; The stream is created only if needed. No support for non-SBCL Lisps.
|
||||
#+sbcl
|
||||
(stream nil :type (or null sb-kernel:ansi-stream))
|
||||
;; The library does not use this slot, but applications may.
|
||||
;; Because the structure type gets frozen (below) it is impolite/incorrect
|
||||
;; to create subtypes of it having additional slots.
|
||||
(userdata))
|
||||
|
||||
;; This declaration asserts that there wil not be further descendant types,
|
||||
;; and promises to the compiler that TYPEP on the two buffer subtypes
|
||||
;; need only be a simple EQ check.
|
||||
#+sbcl
|
||||
(declaim (sb-ext:freeze-type word-buffer octet-buffer))
|
||||
|
||||
(defun make-octet-buffer (size &key userdata target)
|
||||
(declare (array-index size))
|
||||
(let ((b (make-buffer #'%make-octet-buffer
|
||||
(make-array size :element-type 'octet-type))))
|
||||
(setf (octet-buffer-userdata b) userdata
|
||||
(octet-buffer-target b) target)
|
||||
b))
|
||||
|
||||
;; Allocate but do not link in a new block of at least MIN-SIZE, which can be zero
|
||||
;; for the default growth rate of 1.5x the previous allocation.
|
||||
;; A clever way to make an array of the right kind would be to use introspection
|
||||
;; on the TYPE of the CURRENT-BLOCK slot. But clever = slow, so use ETYPECASE instead.
|
||||
(defun new-block (buffer min-size)
|
||||
(declare (array-index min-size))
|
||||
;; For testing the algorithm without growth of buffers - to make it more likely that
|
||||
;; data will span buffers - the new-capacity could be (max min-size 128) or similar.
|
||||
;; It must never be smaller than the largest primitive type though.
|
||||
(let* ((old-capacity (buffer-block-capacity buffer))
|
||||
(new-capacity
|
||||
(max min-size
|
||||
(min (+ old-capacity (ash old-capacity 1)) 100000))))
|
||||
(etypecase buffer
|
||||
(word-buffer (make-array new-capacity :element-type '(unsigned-byte 32)))
|
||||
(octet-buffer (make-array new-capacity :element-type 'octet-type)))))
|
||||
|
||||
;; After having ensured sufficient space, the "FAST-" output algorithms can avoid
|
||||
;; allocating blocks, but might have to advance the block pointer with ADVANCE-BLOCK.
|
||||
;; This gets called exponentially less often as block size is automatically grown,
|
||||
;; so dot not benefit from being inlined.
|
||||
;; Note that this DOES NOT set the 'current-index' slot to 0.
|
||||
(declaim (ftype (function (buffer) (values (simple-array octet-type 1) &optional))
|
||||
advance-block))
|
||||
(defun advance-block (buffer)
|
||||
(declare #.$optimize-buffering)
|
||||
;; this INCF generates 6 instructions instead of 1. wth?
|
||||
(incf (buffer-%block-absolute-start buffer)
|
||||
(length (buffer-block buffer)))
|
||||
(let ((tail (cdr (buffer-next buffer))))
|
||||
(setf (buffer-next buffer) tail
|
||||
(buffer-block buffer) (car tail))))
|
||||
|
||||
;; Create a new block such that there will be at least N bytes available in
|
||||
;; total across the current and new block, given that BUFFER-ENSURE-SPACE [q.v.]
|
||||
;; has already decided there is not presently enough space.
|
||||
;; The new block's size is the greater of the defecit or the standard growth
|
||||
;; amount. If there is zero space in the current block, the new block is set
|
||||
;; as the current block, otherwise it is not.
|
||||
;; Return true if all data will fit in the current block; NIL otherwise.
|
||||
(declaim (ftype (function (t t) (values t &optional)) %buffer-ensure-space))
|
||||
(defun %buffer-ensure-space (buffer n)
|
||||
(declare ((and fixnum unsigned-byte) n) #.$optimize-buffering)
|
||||
(let* ((capacity (buffer-block-capacity buffer))
|
||||
(space-remaining (- capacity (buffer-index buffer)))
|
||||
(defecit (the fixnum (- n space-remaining))))
|
||||
;; There might already be a next-block. This can happen if previous write asked
|
||||
;; for more space than existed in the current block, but subsequently didn't
|
||||
;; use any space in the new block. That block can be smaller than what is
|
||||
;; needed now, but don't drop it - push a new next-block in front.
|
||||
(unless (and (cdr (buffer-next buffer))
|
||||
(>= (length (the vector (second (buffer-next buffer)))) defecit))
|
||||
(rplacd (buffer-next buffer)
|
||||
(cons (new-block buffer defecit) (cdr (buffer-next buffer)))))
|
||||
(when (zerop space-remaining)
|
||||
(advance-block buffer)
|
||||
;; 0 serves as a generalized T, meaining all N bytes fit in one block
|
||||
(setf (buffer-index buffer) 0))))
|
||||
|
||||
;; Guarantee that BUFFER has room for at least N more elements (words or octets)
|
||||
;; considering its current block and possibly one new block.
|
||||
;; If all N elements fit into the current block, return true, else return NIL.
|
||||
;; If exactly at the end of a block, the return value will be true because
|
||||
;; the next block will contain all N bytes.
|
||||
;; This inlined wrapper punts to the general case if available space is inadequate.
|
||||
;;
|
||||
(defun-inline buffer-ensure-space (buffer n)
|
||||
(declare ((and fixnum unsigned-byte) n) #.$optimize-buffering)
|
||||
(or (>= (- (buffer-block-capacity buffer) (buffer-index buffer)) n)
|
||||
(%buffer-ensure-space buffer n)))
|
||||
|
||||
;; A SERIALIZED-PROTOBUF is the result of serializing in the one-pass algorithm
|
||||
;; and then squashing out any of the gaps that were left by allocating length
|
||||
;; prefixes in their largest possible size but not using all bytes.
|
||||
;;
|
||||
(defstruct (serialized-protobuf
|
||||
(:constructor make-serialized-protobuf
|
||||
(blocks total-length final-block-length)))
|
||||
blocks
|
||||
total-length
|
||||
final-block-length)
|
||||
(defmethod print-object ((self serialized-protobuf) stream)
|
||||
(declare (stream stream))
|
||||
(print-unreadable-object (self stream :type t)
|
||||
(format stream "~D byte~:P" (serialized-protobuf-total-length self))))
|
||||
|
||||
(declaim (ftype (function (t t) (values t &optional))
|
||||
word-out octet-out)
|
||||
(inline word-out))
|
||||
|
||||
;; Define OCTET-OUT and WORD-OUT on the respective buffer types.
|
||||
(macrolet
|
||||
((define-emitter (name buffer-type element-type)
|
||||
`(defun ,name (buffer val)
|
||||
(declare (,buffer-type buffer) #.$optimize-buffering)
|
||||
(let* ((block (buffer-block buffer))
|
||||
(index (buffer-index buffer))
|
||||
(capacity (length block)))
|
||||
;; Structure's slot type isn't enough to provide type information
|
||||
;; because of a later setq.
|
||||
(declare ((simple-array ,element-type 1) block))
|
||||
(when (>= index capacity)
|
||||
(incf (buffer-%block-absolute-start buffer) capacity)
|
||||
(setf block
|
||||
;; see if space was pre-allocated
|
||||
(cond ((cdr (buffer-next buffer))
|
||||
(pop (buffer-next buffer))
|
||||
(car (buffer-next buffer)))
|
||||
(t
|
||||
(let* ((next (new-block buffer 0))
|
||||
(cell (list next)))
|
||||
(setf (cdr (buffer-next buffer)) cell
|
||||
(buffer-next buffer) cell)
|
||||
next)))
|
||||
(buffer-block buffer) block
|
||||
index 0))
|
||||
(setf (aref block index) val
|
||||
(buffer-index buffer) (1+ index))))))
|
||||
(define-emitter word-out word-buffer (unsigned-byte 32))
|
||||
(define-emitter octet-out octet-buffer octet-type))
|
||||
|
||||
(defun %fast-octet-out (buffer val)
|
||||
(let ((block (advance-block buffer)))
|
||||
(setf (aref block 0) val
|
||||
(buffer-index buffer) 1)))
|
||||
|
||||
;; Perform OCTET-OUT, but if the current block can hold no more,
|
||||
;; assume existence of a pre-made next block.
|
||||
(defun-inline fast-octet-out (buffer val)
|
||||
(declare (octet-buffer buffer) #.$optimize-buffering)
|
||||
(let* ((block (buffer-block buffer))
|
||||
(index (buffer-index buffer)))
|
||||
(declare ((simple-array octet-type 1) block))
|
||||
(if (i< index (length block))
|
||||
(setf (aref block index) val (buffer-index buffer) (1+ index))
|
||||
(%fast-octet-out buffer val)))) ; punt
|
||||
|
||||
;; Rapidly copy all of OCTETS into BUFFER as if by FAST-OCTET-OUT.
|
||||
;; Space must have been ensured so that at most one additional block beyond
|
||||
;; the current-block is needed.
|
||||
;;
|
||||
(defun fast-octets-out (buffer octets
|
||||
&aux (input-length (length octets)))
|
||||
(declare (octet-buffer buffer) (optimize (safety 0))
|
||||
((simple-array octet-type 1) octets)
|
||||
((unsigned-byte 32) input-length))
|
||||
(unless (zerop input-length)
|
||||
(let* ((block (buffer-block buffer))
|
||||
(index (buffer-index buffer))
|
||||
(available-space (- (length block) index)))
|
||||
(declare ((simple-array octet-type 1) block))
|
||||
;; ENSURE-SPACE always leaves room for at least 1 octet in the current block,
|
||||
;; and even if it left zero this code would still be correct.
|
||||
(let ((n (min available-space input-length)))
|
||||
(replace block octets :start1 index)
|
||||
(incf index n)
|
||||
(decf input-length n))
|
||||
(when (plusp input-length)
|
||||
;; There is more input. This can only happen if the block's
|
||||
;; capacity was reached.
|
||||
;; The starting index of the source of the copy is the number
|
||||
;; of bytes that were already written into the first block.
|
||||
(replace (advance-block buffer) octets
|
||||
:start2 available-space)
|
||||
;; The ending index in the current block is whatever was just
|
||||
;; copied, since the starting index for writing was 0.
|
||||
(setq index input-length))
|
||||
(setf (buffer-index buffer) index))))
|
||||
|
||||
;; Bind ITER to an iterator over WORD-BUFFER in the manner of standard
|
||||
;; WITH-{mumble}-ITERATOR macros. Each time ITER is invoked, the next
|
||||
;; buffer element will be returned, or NIL if no more remain.
|
||||
(defmacro with-word-buffer-iterator ((iterator-name word-buffer) &body body)
|
||||
(with-gensyms (buffer block more-blocks input-pointer input-limit)
|
||||
`(let* ((,buffer, word-buffer)
|
||||
(,block ,(coerce #() 'word-buffer-block-type))
|
||||
;; if the current block's index is 0, then no blocks were used at all
|
||||
(,more-blocks (unless (zerop (buffer-index ,buffer))
|
||||
(buffer-chain ,buffer)))
|
||||
(,input-pointer 0)
|
||||
(,input-limit 0))
|
||||
(declare (word-buffer-block-type ,block)
|
||||
(array-index ,input-pointer ,input-limit))
|
||||
(macrolet
|
||||
((,iterator-name ()
|
||||
`(locally
|
||||
(declare (optimize (safety 0)))
|
||||
(when (or (i< ,',input-pointer ,',input-limit)
|
||||
(when ,',more-blocks
|
||||
(setq ,',block (pop ,',more-blocks)
|
||||
,',input-limit
|
||||
(if ,',more-blocks
|
||||
(length ,',block)
|
||||
(buffer-index ,',buffer))
|
||||
,',input-pointer 0)))
|
||||
(aref ,',block (prog1 ,',input-pointer (incf ,',input-pointer)))))))
|
||||
,@body))))
|
||||
|
||||
;; Put blank space into an octet buffer so that later we can go back and
|
||||
;; patch a length-prefix in.
|
||||
;; Return fives values: absolute stream position, the cons cell pointing
|
||||
;; to the block in which the first octet would be written, and the index to
|
||||
;; that octet, and a pointer to the block in the buffer of deletions that
|
||||
;; will be performed on finalization, and a pointer into that block.
|
||||
;; Multiple values avoid consing anything to represent saved buffer locations.
|
||||
(declaim (ftype (function (t) (values t t t t t &optional))
|
||||
emit-placeholder))
|
||||
(defun emit-placeholder (buffer)
|
||||
(declare #.$optimize-buffering)
|
||||
;; ABS-POS doesn't change even if BUFFER-ENSURE-SPACE advances a block
|
||||
;; so the first two bindings are actually order-insensitive,
|
||||
;; but the capturing of BUFFER-NEXT must occur after ENSURE-SPACE.
|
||||
;; A length-prefix placeholder reserves 4 octets which is enough to represent
|
||||
;; a 28-bit integer (the other bit of each octet being the "more-to-go" flag).
|
||||
;; Given the suggested message size limit of a few megabytes, this is fine.
|
||||
(symbol-macrolet ((reserve-bytes 4))
|
||||
(let ((within-block-p (buffer-ensure-space buffer reserve-bytes))
|
||||
(abs-pos (buffer-absolute-position buffer))
|
||||
(blocks (buffer-next buffer))
|
||||
(index (buffer-index buffer)))
|
||||
(setf (buffer-index buffer)
|
||||
(if within-block-p
|
||||
(+ index reserve-bytes)
|
||||
(let ((available-space (- (buffer-block-capacity buffer) index)))
|
||||
(advance-block buffer)
|
||||
(- reserve-bytes available-space))))
|
||||
;; A place is reserved in the deletion buffer to hold a pointer to
|
||||
;; the place in the octet buffer that will probably be squeezed out.
|
||||
;; This is done now, so that indices stored are monotonic.
|
||||
;; Were that not done, and backpatching recorded deletion markers
|
||||
;; only at the time of making the patch, the deletion markers would
|
||||
;; not be in ascending order - they would have a "treelike" appearance
|
||||
;; based on the order in which submessages were completed.
|
||||
(let ((patch-buffer (octet-buffer-backpatches buffer)))
|
||||
(word-out patch-buffer 0)
|
||||
(values abs-pos blocks index
|
||||
(buffer-block patch-buffer)
|
||||
(1- (buffer-index patch-buffer)))))))
|
||||
|
||||
;; Patch VAL into the octet buffer by changing the contents of VAL's block at
|
||||
;; the specified indices using 'varint' encoding, and also record a pointer
|
||||
;; to the range of octets which were reserved for VAL but not consumed by it.
|
||||
;; Return the number of bytes used to store VAL.
|
||||
(declaim (ftype (function (t t t t t t t) (values fixnum &optional))
|
||||
backpatch-varint))
|
||||
(defun backpatch-varint (val buffer abs-pos blocks index pointer-block pointer-index)
|
||||
(declare #.$optimize-buffering)
|
||||
(declare (type (unsigned-byte 32) val)
|
||||
((simple-array (unsigned-byte 32) 1) pointer-block)
|
||||
(array-index index pointer-index))
|
||||
(let* ((block (first blocks)) (limit (length block)) (count 0))
|
||||
(declare ((simple-array octet-type 1) block) (fixnum count))
|
||||
;; Seven bits at a time, least significant bits first
|
||||
(loop do (let ((bits (ildb (byte 7 0) val)))
|
||||
(declare (octet-type bits))
|
||||
(setq val (iash val -7))
|
||||
(when (>= index limit)
|
||||
;; This doesn't bother updating LIMIT to its "proper" new value.
|
||||
;; It can't possibly be any smaller than a varint.
|
||||
(setf index 0 block (second blocks)))
|
||||
(setf (aref block index) (ilogior bits (if (i= val 0) 0 128)))
|
||||
(iincf index)
|
||||
(incf count))
|
||||
until (i= val 0))
|
||||
;; Record the location of the backpatch so that the unused bytes can be
|
||||
;; squashed out later. This is done even if all 4 bytes were used,
|
||||
;; because a place was aleady reserved in the word-buffer for this backpatch.
|
||||
(cond ((<= count 4)
|
||||
;; Encode the deletion using 2 bits for the deletion count (0 .. 3)
|
||||
;; ORed with the index at which to delete shifted left 2 bits.
|
||||
(let ((gap (i- 4 count)))
|
||||
(setf (aref pointer-block pointer-index)
|
||||
(ilogior (ash (i+ abs-pos count) 2) gap))
|
||||
(incf (octet-buffer-n-gap-bytes buffer) gap)))
|
||||
((> count 4)
|
||||
(protobuf-error "Backpatch failure on ~S" buffer)))
|
||||
count))
|
||||
|
||||
;; Execute BODY, capturing the state of BUFFER at the start, and *unless* a nonlocal
|
||||
;; exit occurs, restore the state of the buffer prior to executing the body
|
||||
;; and return no value.
|
||||
(defmacro with-bookmark ((buffer) &body body)
|
||||
(with-gensyms (block index next abs-pos)
|
||||
`(let ((,block (buffer-block ,buffer))
|
||||
(,index (buffer-index ,buffer))
|
||||
(,next (buffer-next ,buffer))
|
||||
(,abs-pos (buffer-%block-absolute-start ,buffer)))
|
||||
,@body
|
||||
(setf (buffer-block ,buffer) ,block
|
||||
(buffer-index ,buffer) ,index
|
||||
(buffer-next ,buffer) ,next
|
||||
(buffer-%block-absolute-start ,buffer) ,abs-pos)
|
||||
(values))))
|
||||
|
||||
;; Reserve space for a uint32 prior to the start of a variable-length subsequence
|
||||
;; of buffer, and also reserve space in the backpatch buffer to point to the space
|
||||
;; in the data buffer where unused reserved bytes should be squashed out.
|
||||
(defmacro with-placeholder ((buffer &key position) &body body)
|
||||
(let* ((name "PLACEHOLDER")
|
||||
(abs
|
||||
(or position
|
||||
(make-symbol (concatenate 'string name "-OCTET-POSITION"))))
|
||||
(blocks (make-symbol (concatenate 'string name "-OCTET-BLOCKS")))
|
||||
(index (make-symbol (concatenate 'string name "-OCTET-INDEX")))
|
||||
(pointer-block (make-symbol (concatenate 'string name "-POINTER-BLOCK")))
|
||||
(pointer-index (make-symbol (concatenate 'string name "-POINTER-INDEX"))))
|
||||
`(multiple-value-bind (,abs ,blocks ,index ,pointer-block ,pointer-index)
|
||||
(emit-placeholder ,buffer)
|
||||
(macrolet ((backpatch (value)
|
||||
`(backpatch-varint ,value
|
||||
,',buffer ,',abs ,',blocks ,',index
|
||||
,',pointer-block ,',pointer-index)))
|
||||
,@body))))
|
||||
|
||||
;; A simple wrapper on REPLACE. This function is used only in one place.
|
||||
;; It shouldn't be needed, but small copies using REPLACE are slower than a loop.
|
||||
;; It turns out that a foreign call to memmove would be faster for 80 bytes or more.
|
||||
(defun-inline fast-replace (destination destination-index
|
||||
source source-index count)
|
||||
(declare (array-index destination-index count)
|
||||
((simple-array octet-type 1) destination source))
|
||||
(let ((limit (the array-index (+ destination-index count))))
|
||||
(if (< count 40)
|
||||
(loop (setf (aref destination destination-index) (aref source source-index))
|
||||
(incf source-index)
|
||||
(when (eql (incf destination-index) limit) (return)))
|
||||
(replace destination source
|
||||
:start1 destination-index :end1 limit
|
||||
:start2 source-index))))
|
||||
|
||||
(defvar **empty-word-buffer** (make-word-buffer 0))
|
||||
|
||||
;; Given an octet-buffer BUFFER, squeeze out any octets which "do not exist" in
|
||||
;; the virtual octet sequence so they no also longer exist in the physical sequence.
|
||||
;; After this operation, BUFFER will be ready for direct consumption, such as
|
||||
;; by a client or a compression algorithm or file storage.
|
||||
(defun compactify-blocks (buffer)
|
||||
(declare #.$optimize-buffering)
|
||||
;; OUTPUT and INPUT refer to the same block chain, namely the blocks
|
||||
;; that currently exist in BUFFER.
|
||||
(let* ((input-block-chain (buffer-chain buffer))
|
||||
(output-block-chain input-block-chain)
|
||||
;; Output blocks are not popped off the chain until
|
||||
;; advancing beyond the current block. This way the tail
|
||||
;; can be smashed to NIL when reaching the end of input.
|
||||
(output-block (car output-block-chain))
|
||||
(output-index 0)
|
||||
;; Setting INPUT-BLOCK now is only for type-correctness of the
|
||||
;; initial value. It will be set again immediately before reading
|
||||
(input-block (car input-block-chain))
|
||||
(input-index 0) ; block-relative index
|
||||
(input-position 0) ; absolute
|
||||
(deletion-point 0)
|
||||
(deletion-length 0))
|
||||
(declare ((simple-array octet-type 1) output-block input-block)
|
||||
(array-index output-index input-index input-position))
|
||||
;; Drop any pre-allocated but unused block in the input chain.
|
||||
(when (cdr (buffer-next buffer))
|
||||
(assert (eq (buffer-block buffer) (car (buffer-next buffer))))
|
||||
(rplacd (buffer-next buffer) nil))
|
||||
|
||||
;; The reason for deferring this POP 'til after the preceding "drop"
|
||||
;; is that if there were exactly two input blocks, one used and one not
|
||||
;; used at all, INPUT-BLOCK-CHAIN should become NIL.
|
||||
(setq input-block (pop input-block-chain))
|
||||
(with-word-buffer-iterator
|
||||
(deletion-point-getter (octet-buffer-backpatches buffer))
|
||||
(labels
|
||||
((find-next-deletion-point ()
|
||||
;; If the deletion point is one at which no bytes should be deleted -
|
||||
;; probably impossible as it means a submessage length took >21 bits
|
||||
;; (= 4 bytes) to encode - skip until finding somewhere to delete,
|
||||
;; or else finding that there are no further deletion points.
|
||||
(let ((word (deletion-point-getter)))
|
||||
(if (not word)
|
||||
(setq deletion-point most-positive-fixnum deletion-length 0)
|
||||
(let ((n-bytes (logand (the fixnum word) #b11)))
|
||||
(if (zerop n-bytes)
|
||||
(find-next-deletion-point)
|
||||
(setq deletion-point (ash word -2)
|
||||
deletion-length n-bytes))))))
|
||||
(next-output-block ()
|
||||
(setq output-block-chain (cdr output-block-chain)
|
||||
output-block (car output-block-chain)
|
||||
output-index 0)
|
||||
(length output-block))
|
||||
(copy-to-output (count)
|
||||
(declare ((and fixnum unsigned-byte) count))
|
||||
(when (zerop count)
|
||||
(return-from copy-to-output))
|
||||
(let ((space-available (- (length output-block) output-index)))
|
||||
(declare (array-index count space-available))
|
||||
;; See if the output needs to be advanced to the next block.
|
||||
(when (zerop space-available)
|
||||
(setq space-available (next-output-block)))
|
||||
;; Avoid copying until the earlist point at which bytes need to move.
|
||||
;; This rapidly skips over blocks that contain only fixed-length data
|
||||
;; provided they are the first blocks in the serialized output.
|
||||
;; Not likely, but happens.
|
||||
(when (and (eq output-block input-block)
|
||||
(eql output-index input-index))
|
||||
(incf output-index count)
|
||||
(incf input-index count)
|
||||
(return-from copy-to-output))
|
||||
;; A chunk of input can span more than one block of output due to
|
||||
;; variable-length blocks.
|
||||
(loop
|
||||
(let ((stride (min count space-available)))
|
||||
;; COUNT and SPACE-AVAILABLE are both positive,
|
||||
;; so this will copy at least one octet.
|
||||
(fast-replace output-block output-index
|
||||
input-block input-index stride)
|
||||
(incf output-index stride)
|
||||
(incf input-index stride)
|
||||
(if (eql (decf count stride) 0) (return)))
|
||||
(when (zerop (setq space-available
|
||||
(- (length output-block) output-index)))
|
||||
(setq space-available (next-output-block))))))
|
||||
(compute-input-block-length ()
|
||||
;; Only the final block is possibly shorter than its allocated length.
|
||||
;; The others are as long as allocated, each larger than its predecessor.
|
||||
(if input-block-chain
|
||||
(length input-block)
|
||||
(buffer-index buffer))))
|
||||
(declare (inline next-output-block compute-input-block-length))
|
||||
(prog ((block-length (compute-input-block-length))
|
||||
(total-deletion-count 0))
|
||||
(declare (array-index block-length total-deletion-count))
|
||||
tippytop
|
||||
(find-next-deletion-point)
|
||||
top
|
||||
(let* ((remaining-length (- block-length input-index))
|
||||
(n-bytes-to-copy
|
||||
(min remaining-length (- deletion-point input-position))))
|
||||
(copy-to-output n-bytes-to-copy)
|
||||
(incf input-position n-bytes-to-copy)) ; absolute
|
||||
(when (eql input-index block-length)
|
||||
(unless input-block-chain
|
||||
(rplacd output-block-chain nil) ; terminate the list
|
||||
;; Free the unnecessary word-buffer blocks. Also makes additional calls
|
||||
;; to COMPACTIFY on this buffer do nothing, which seems reasonable.
|
||||
(setf (octet-buffer-backpatches buffer) **empty-word-buffer**)
|
||||
(return (make-serialized-protobuf
|
||||
(buffer-chain buffer)
|
||||
(- input-position total-deletion-count)
|
||||
output-index)))
|
||||
(setq input-block (pop input-block-chain)
|
||||
block-length (compute-input-block-length)
|
||||
input-index 0)
|
||||
(go top))
|
||||
;; now we must be at a deletion point
|
||||
(unless (and (= input-position deletion-point) (plusp deletion-length))
|
||||
(protobuf-error "Octet buffer compaction bug"))
|
||||
(let ((remaining-length (- block-length input-index)))
|
||||
(if (>= remaining-length deletion-length)
|
||||
(incf input-index deletion-length) ; easy case
|
||||
;; Skip remainder of this block and start of one more. Deleted ranges
|
||||
;; never span more than 2 blocks since deletion-length <= 3
|
||||
;; and blocks are much larger than 3 octets.
|
||||
(setq input-block (pop input-block-chain)
|
||||
block-length (compute-input-block-length)
|
||||
input-index (- deletion-length remaining-length))))
|
||||
(incf input-position deletion-length)
|
||||
(incf total-deletion-count deletion-length)
|
||||
(go tippytop))))))
|
||||
|
||||
(defun reset-buffer-chain (buffer chain)
|
||||
"Make BUFFER have CHAIN as its list of octet arrays"
|
||||
(setf (buffer-block buffer) (car chain)
|
||||
(buffer-index buffer) 0
|
||||
(buffer-chain buffer) chain
|
||||
(buffer-next buffer) chain
|
||||
(buffer-%block-absolute-start buffer) 0)
|
||||
;; Zero-fill, or not. This should depend on SAFETY and/or DEBUG,
|
||||
;; but there is no way to discover the current policy
|
||||
;; without using implementation-specific code.
|
||||
#+nil
|
||||
(dolist (block chain)
|
||||
(fill block 0)))
|
||||
|
||||
(defun force-to-stream (buffer)
|
||||
"Write the octets currently in BUFFER to its target stream,
|
||||
and rewind BUFFER so that it is empty."
|
||||
;; Before COMPACTIFY-BLOCKS messes up the chain, copy it.
|
||||
;; Then compactify and copy to the target stream.
|
||||
(let ((chain (copy-list (buffer-chain buffer)))
|
||||
(backpatch-chain (buffer-chain (octet-buffer-backpatches buffer)))
|
||||
(stream (the stream (octet-buffer-target buffer))))
|
||||
(flet ((out-block (block length)
|
||||
(write-sequence block stream :start 0 :end length)))
|
||||
(declare (dynamic-extent #'out-block))
|
||||
(call-with-each-block #'out-block (compactify-blocks buffer)))
|
||||
(reset-buffer-chain buffer chain)
|
||||
(setf (octet-buffer-n-gap-bytes buffer) 0)
|
||||
;; Heuristically resize the backpatch buffer, trying to avoid subsequent expansion
|
||||
;; Ideally we would do this only only on the *next* attempted use of the buffer,
|
||||
;; but that's not as easy as just sizing up now, even if no further write will occur.
|
||||
;; The worst-case is when the backpatch buffer is never needed again,
|
||||
;; but was nonetheless resized to be larger. But that's probably not common.
|
||||
(let ((backpatches (octet-buffer-backpatches buffer)))
|
||||
(reset-buffer-chain
|
||||
backpatches
|
||||
(if (cdr backpatch-chain)
|
||||
(list (new-block backpatches
|
||||
(loop for block in backpatch-chain
|
||||
sum (length block))))
|
||||
backpatch-chain)))))
|
||||
|
||||
;; Given either a SERIALIZED-PROTOBUF or a BUFFER, return the concatenation
|
||||
;; of all BLOCKS. You probably don't want to do this on an uncompacted BUFFER.
|
||||
;; That usually makes no sense in any scenario other than debugging.
|
||||
(defun concatenate-blocks (buffer)
|
||||
(multiple-value-bind (total-length blocks)
|
||||
(etypecase buffer
|
||||
(serialized-protobuf
|
||||
(values (serialized-protobuf-total-length buffer)
|
||||
(serialized-protobuf-blocks buffer)))
|
||||
(buffer
|
||||
(values (loop for (block . rest) on (buffer-chain buffer)
|
||||
sum (if rest (length (the (simple-array * 1) block))
|
||||
(buffer-index buffer))
|
||||
fixnum)
|
||||
(buffer-chain buffer))))
|
||||
(declare (array-index total-length))
|
||||
(let ((result (make-array total-length :element-type 'octet-type))
|
||||
(index 0))
|
||||
(declare (array-index index))
|
||||
(dolist (block blocks result)
|
||||
(replace result (the (simple-array octet-type 1) block) :start1 index)
|
||||
(incf index (length (the (simple-array * 1) block)))))))
|
||||
|
||||
;; Given a BUFFER or a SERIALIZED-PROTOBUF, call FUNCTION once with each
|
||||
;; block, passing it also the effective length of the block.
|
||||
(defun call-with-each-block (function buffer)
|
||||
(etypecase buffer
|
||||
(serialized-protobuf
|
||||
(let ((blocks (serialized-protobuf-blocks buffer)))
|
||||
(loop
|
||||
(let ((block (car blocks)))
|
||||
(funcall function block
|
||||
(if (cdr blocks)
|
||||
(length (the (simple-array * 1) block))
|
||||
(serialized-protobuf-final-block-length buffer))))
|
||||
(pop blocks)
|
||||
(if (null blocks) (return)))))
|
||||
(buffer
|
||||
(let ((blocks (buffer-chain buffer)))
|
||||
(loop
|
||||
(let ((block (car blocks)))
|
||||
(funcall function block
|
||||
(if (cdr blocks)
|
||||
(length (the (simple-array * 1) block))
|
||||
(buffer-index buffer))))
|
||||
(pop blocks)
|
||||
(if (null blocks) (return)))))))
|
||||
|
||||
;;;
|
||||
|
||||
#+sbcl
|
||||
(declaim (sb-ext:maybe-inline encode-uint32))
|
||||
(macrolet ((define-varint-encoder (name reserve-bytes lisp-type
|
||||
&optional (expr 'input))
|
||||
`(progn
|
||||
(declaim (ftype (function (,lisp-type buffer)
|
||||
(values (integer 1 ,(or reserve-bytes 5)) &optional))
|
||||
,name))
|
||||
(defun ,name (input buffer &aux (val ,expr))
|
||||
(declare (type ,lisp-type input)
|
||||
(type (unsigned-byte ,(second lisp-type)) val))
|
||||
;; The locally declare gives us optimizations inside the locally
|
||||
;; but leaves the typechecking in the function.
|
||||
(locally
|
||||
(declare #.$optimize-buffering)
|
||||
,@(when reserve-bytes
|
||||
`((buffer-ensure-space buffer ,reserve-bytes)))
|
||||
(let ((n 0))
|
||||
(declare (fixnum n))
|
||||
(loop (let ((bits (ldb (byte 7 0) val)))
|
||||
(setq val (ash val -7))
|
||||
(fast-octet-out buffer
|
||||
(ilogior bits (if (i= val 0) 0 128)))
|
||||
(iincf n))
|
||||
(when (eql val 0) (return n)))))))))
|
||||
|
||||
(define-varint-encoder encode-uint32 5 (unsigned-byte 32))
|
||||
(define-varint-encoder encode-uint64 10 (unsigned-byte 64))
|
||||
|
||||
;; It is best to keep all occurrences of (LDB (BYTE 64 0) ...) out of calling code
|
||||
;; because that forces boxing in many cases, and even it if doesn't create a new bignum,
|
||||
;; it causes generic arithmetic routines to be used.
|
||||
;; Hiding the LDB operation inside a primitive encoder is better for efficiency.
|
||||
(define-varint-encoder encode-int64 10 (signed-byte 64)
|
||||
;; On SBCL the LOGAND compiles to nothing.
|
||||
#+sbcl (logand input sb-vm::most-positive-word)
|
||||
#-sbcl (ldb (byte 64 0) input))
|
||||
|
||||
;; FAST-ENCODE simply omits the call to ENSURE-SPACE and might not be worth keeping
|
||||
(define-varint-encoder fast-encode-uint32 nil (unsigned-byte 32)))
|
||||
|
||||
(define-compiler-macro encode-uint32 (&whole form val buffer)
|
||||
(let (encoded-length)
|
||||
(if (and (typep val 'fixnum) (i<= (setq encoded-length (length32 val)) 2))
|
||||
(let ((low7 (logand val #x7F)))
|
||||
(case encoded-length
|
||||
(1 `(progn (octet-out ,buffer ,low7)
|
||||
1))
|
||||
(2 `(progn (octet-out2 ,buffer ,(logior #x80 low7) ,(ldb (byte 7 7) val))
|
||||
2))))
|
||||
form)))
|
||||
|
||||
;; For encoding an object tag + wire-type, we can compile-time convert ENCODE-UINT32
|
||||
;; into a few OCTET-OUT calls. I'll only do this for 1 and 2-octet writes though,
|
||||
;; which is enough for field-indices up to (2^14)-1.
|
||||
(defun octet-out2 (buffer first second)
|
||||
(octet-out buffer first)
|
||||
(octet-out buffer second))
|
||||
|
||||
;;;
|
||||
|
||||
;; A BUFFER does not, in general, interact through a stream interface
|
||||
;; (WRITE-BYTE, WRITE-SEQUENCE) however there is some support in SBCL
|
||||
;; for treating it as though it were a character output stream.
|
||||
;; In general it is faster to use OCTET-OUT, however a stream produces
|
||||
;; less garbage if the alternative would be to call WRITE-TO-STRING on
|
||||
;; something and serialize the resultant string. The buffer can do this
|
||||
;; for you as long as you only write ASCII characters, because the
|
||||
;; stream mode does not have a UTF-8 encoder. (It could, but doesn't)
|
||||
|
||||
#+sbcl
|
||||
(progn
|
||||
(defstruct (octet-output-stream
|
||||
(:conc-name octet-stream-)
|
||||
;; Maybe Todo: supply a BOUT (byte-out) handler function.
|
||||
(:include sb-kernel:ansi-stream
|
||||
;; "OUT" is the old slot name, "COUT" is the modern name
|
||||
(#.(if (find-symbol "ANSI-STREAM-OUT" "SB-KERNEL") 'out 'cout)
|
||||
#'octet-stream-char-out)
|
||||
(sout #'octet-stream-string-out))
|
||||
(:constructor make-octet-output-stream (buffer)))
|
||||
;; How many characters should the character producer be permitted to write
|
||||
;; before we complain about a protocol error.
|
||||
(space-available 0 :type fixnum)
|
||||
(buffer nil :type octet-buffer))
|
||||
|
||||
(defun protocol-error (stream)
|
||||
(protobuf-error "Octet stream protocol error on ~S" stream))
|
||||
|
||||
(defun octet-stream-char-out (stream character)
|
||||
;; A streamified BUFFER accept only ASCII characters (for now).
|
||||
;; This is more of a sanity-check than a limitation, and it's a mild
|
||||
;; limitation if that- the ENCODE-STRING protobuf serializer performs
|
||||
;; encoding and doesn't use its BUFFER as a stream. It uses OCTETS-OUT.
|
||||
(unless (<= (char-code character) 127)
|
||||
(protocol-error stream))
|
||||
(octet-out (octet-stream-buffer stream) (char-code character)))
|
||||
|
||||
(defun octet-stream-limited-char-out (stream character)
|
||||
(cond ((or (zerop (octet-stream-space-available stream))
|
||||
(> (char-code character) 127))
|
||||
(protocol-error stream))
|
||||
(t
|
||||
(decf (octet-stream-space-available stream))
|
||||
(octet-out (octet-stream-buffer stream) (char-code character)))))
|
||||
|
||||
(macrolet ((ansi-stream-char-out-method (x)
|
||||
`(,(or (find-symbol "ANSI-STREAM-COUT" "SB-KERNEL")
|
||||
(find-symbol "ANSI-STREAM-OUT" "SB-KERNEL"))
|
||||
,x)))
|
||||
(defun octet-stream-string-out (stream string start end)
|
||||
(declare (string string) (array-index start end))
|
||||
(let ((f (ansi-stream-char-out-method stream)))
|
||||
(sb-kernel:with-array-data ((string string) (start start) (end end))
|
||||
(loop for i fixnum from start below end
|
||||
do (funcall f stream (char string i))))))
|
||||
|
||||
(defun %get-buffer-stream (buffer)
|
||||
(or (octet-buffer-stream buffer)
|
||||
(setf (octet-buffer-stream buffer) (make-octet-output-stream buffer))))
|
||||
|
||||
(declaim (ftype (function (buffer) (values stream &optional))
|
||||
get-unlimited-buffer-stream get-tiny-buffer-stream)
|
||||
(ftype (function (buffer fixnum) (values stream &optional))
|
||||
get-bounded-buffer-stream))
|
||||
|
||||
;; Return a stream that accepts any number of characters.
|
||||
;; A placeholder must already have been reserved for the length prefix.
|
||||
(defun get-unlimited-buffer-stream (buffer)
|
||||
(let ((stream (%get-buffer-stream buffer)))
|
||||
;; Setting the space to 0 ensures we can't call the 'limited'
|
||||
;; char out function without getting an obvious failure.
|
||||
(setf (octet-stream-space-available stream) 0
|
||||
(ansi-stream-char-out-method stream) #'octet-stream-char-out)
|
||||
stream))
|
||||
|
||||
;; Return a stream that accepts a tiny string. 1 byte is reserved for the length.
|
||||
(defun get-tiny-buffer-stream (buffer)
|
||||
(buffer-ensure-space buffer 128) ; 1 byte prefix, <= 127 string characters
|
||||
(fast-octet-out buffer 0) ; easy way to leave a 1-byte space
|
||||
(let ((stream (%get-buffer-stream buffer)))
|
||||
(setf (octet-stream-space-available stream) 127
|
||||
(ansi-stream-char-out-method stream) #'octet-stream-limited-char-out)
|
||||
stream))
|
||||
|
||||
;; Return a stream that accepts a known-length string. The length gets encoded first.
|
||||
(defun get-bounded-buffer-stream (buffer n-chars)
|
||||
(encode-uint32 n-chars buffer) ; emit the variable-length length prefix
|
||||
(let ((stream (%get-buffer-stream buffer)))
|
||||
(setf (octet-stream-space-available stream) n-chars
|
||||
(ansi-stream-char-out-method stream) #'octet-stream-limited-char-out)
|
||||
stream))
|
||||
)
|
||||
|
||||
;; WITH-BUFFER-AS-STREAM binds STREAM to a character output stream that when written to
|
||||
;; places ASCII characters into BUFFER. There are three cases, listed here
|
||||
;; in order from most efficient to least efficient:
|
||||
;; 1. (WITH-BUFFER-AS-STREAM (stream buffer :length n)
|
||||
;; Length specified as an integer N (evaluated at runtime) will encode a prefix of N
|
||||
;; then accept N characters. Writing anything other than exactly N will signal an eror.
|
||||
;; 2. (WITH-BUFFER-AS-STREAM (stream buffer :length :TINY)
|
||||
;; Length specified as the literal symbol :TINY will leave a 1-byte gap for a prefix.
|
||||
;; (... :length N) where N runtime evaluates to the keyword :TINY is not legal.
|
||||
;; Between 0 and 127 characters may be written, and the prefix will be modified accordingly.
|
||||
;; An error will be signaled if more than 127 characters are written.
|
||||
;; 3. (WITH-BUFFER-AS-STREAM (stream buffer) ...)
|
||||
;; No length specified will leave a 4-byte placeholder for an arbitrary length and
|
||||
;; backpatch it in. This relies on buffer compactification in the same way as does
|
||||
;; writing of an unknown-length submessage.
|
||||
|
||||
;; In all cases, non-ASCII characters are rejected.
|
||||
;; If TAG is supplied, it is encoded prior to the encoding of the string data.
|
||||
;; This macro should be used for effect, not value - its return value is undefined.
|
||||
|
||||
(defmacro with-buffer-as-stream ((stream-var buffer &key length (tag nil tag-p))
|
||||
&body body &environment env)
|
||||
(with-gensyms (start-pos start-block start-index)
|
||||
`(progn
|
||||
,@(if tag-p `((encode-uint32 ,tag ,buffer)))
|
||||
,(cond ((not length) ; most general
|
||||
`(with-placeholder (,buffer :position ,start-pos)
|
||||
(let ((,stream-var (get-unlimited-buffer-stream ,buffer)))
|
||||
,@body)
|
||||
(backpatch
|
||||
(i- (buffer-absolute-position ,buffer)
|
||||
;; Buffer's absolute pos was marked at the first octet of the
|
||||
;; placeholder for the varint.
|
||||
;; Actual number of chars written is 4 less than that.
|
||||
,start-pos 4))))
|
||||
((eq length :tiny)
|
||||
`(let ((,stream-var (get-tiny-buffer-stream ,buffer))
|
||||
(,start-block (octet-buffer-block ,buffer))
|
||||
(,start-index (1- (buffer-index ,buffer))))
|
||||
,@body
|
||||
(locally
|
||||
,@(when (sb-c:policy env (= safety 0))
|
||||
`((declare (optimize (sb-c::insert-array-bounds-checks 0)))))
|
||||
(setf (aref ,start-block ,start-index)
|
||||
(i- 127 (octet-stream-space-available ,stream-var))))))
|
||||
(t
|
||||
`(let ((,stream-var (get-bounded-buffer-stream ,buffer ,length)))
|
||||
,@body
|
||||
,@(when (sb-c:policy env (> safety 0))
|
||||
;; The stream will croak upon trying to write >LENGTH chars.
|
||||
;; With safety, ensure *exactly* that many were written.
|
||||
`((unless (zerop (octet-stream-space-available ,stream-var))
|
||||
(protocol-error ,stream-var))))))))))
|
||||
|
||||
) ; end of #+sbcl (PROGN ...)
|
||||
|
||||
;; The portable implementation of WITH-BUFFER-AS-STREAM
|
||||
#-sbcl
|
||||
(defmacro with-buffer-as-stream ((stream-var buffer &key length) &body body)
|
||||
(declare (ignore length))
|
||||
`(let ((,stream-var (make-string-output-stream)))
|
||||
,@body
|
||||
(encode-string (get-output-stream-string ,stream-var)
|
||||
,buffer)))
|
||||
48
examples/meshtastic/lisp/cl-protobufs/conditions.lisp
Normal file
48
examples/meshtastic/lisp/cl-protobufs/conditions.lisp
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
;;; Copyright 2012-2020 Google LLC
|
||||
;;;
|
||||
;;; Use of this source code is governed by an MIT-style
|
||||
;;; license that can be found in the LICENSE file or at
|
||||
;;; https://opensource.org/licenses/MIT.
|
||||
|
||||
(in-package #:cl-protobufs.implementation)
|
||||
|
||||
|
||||
(define-condition protobuf-error (simple-error)
|
||||
()
|
||||
(:documentation
|
||||
"Supertype of all errors explicitly signaled by cl-protobufs.
|
||||
As a subtype of simple-error this accepts :format-control and
|
||||
:format-argumens init keywords."))
|
||||
|
||||
(defun protobuf-error (format-control &rest format-arguments)
|
||||
"Signal a protobuf-error using FORMAT-CONTROL and FORMAT-ARGUMENTS to
|
||||
construct the error message."
|
||||
(error 'protobuf-error
|
||||
:format-control format-control
|
||||
:format-arguments format-arguments))
|
||||
|
||||
(define-condition unknown-type (protobuf-error)
|
||||
()
|
||||
(:documentation
|
||||
"Indicates that a non-protobuf object was encountered where a protobuf type
|
||||
(message,enum, scalar etc.) was expected."))
|
||||
|
||||
(define-condition unknown-field-type (unknown-type)
|
||||
()
|
||||
(:documentation
|
||||
"Indicates that an object that isn't a protocol buffer type was encountered
|
||||
while printing, parsing, serializing, or otherwise processing a protocol
|
||||
buffer object."))
|
||||
|
||||
(define-condition unknown-field (protobuf-error)
|
||||
()
|
||||
(:documentation
|
||||
"An unknown field was encountered when parsing a text proto."))
|
||||
|
||||
(defun unknown-field-type (type field object)
|
||||
"Signal an unknown-field-type error for TYPE in relation to FIELD. OBJECT
|
||||
is usually the protobuf message being printed or serialized, or the descriptor
|
||||
being parsed."
|
||||
(error 'unknown-field-type
|
||||
:format-control "unknown field type ~S for field ~S in ~S"
|
||||
:format-arguments (list type field object)))
|
||||
1621
examples/meshtastic/lisp/cl-protobufs/define-proto.lisp
Normal file
1621
examples/meshtastic/lisp/cl-protobufs/define-proto.lisp
Normal file
File diff suppressed because it is too large
Load diff
680
examples/meshtastic/lisp/cl-protobufs/json.lisp
Normal file
680
examples/meshtastic/lisp/cl-protobufs/json.lisp
Normal file
|
|
@ -0,0 +1,680 @@
|
|||
;;; Copyright 2020 Google LLC
|
||||
;;;
|
||||
;;; Use of this source code is governed by an MIT-style
|
||||
;;; license that can be found in the LICENSE file or at
|
||||
;;; https://opensource.org/licenses/MIT.
|
||||
|
||||
(defpackage #:cl-protobufs.json
|
||||
(:use #:cl
|
||||
#:cl-protobufs
|
||||
#:cl-protobufs.implementation)
|
||||
;; Shadow fmt from cl-protobufs text-format.
|
||||
(:shadow #:fmt)
|
||||
(:export #:print-json
|
||||
#:parse-json
|
||||
#:fmt)
|
||||
(:local-nicknames
|
||||
(#:pi #:cl-protobufs.implementation)
|
||||
(#:google #:cl-protobufs.google.protobuf)
|
||||
(#:wkt #:cl-protobufs.well-known-types)))
|
||||
|
||||
(in-package #:cl-protobufs.json)
|
||||
|
||||
;;; This file implements the protobuf JSON parser and printer.
|
||||
;;; The exported symbols are parse-json and print-json.
|
||||
|
||||
(defun print-json (object &key (pretty-print-p t) (stream *standard-output*)
|
||||
(camel-case-p t) (numeric-enums-p nil))
|
||||
"Prints a protocol buffer message to a stream in JSON format. The parameters
|
||||
CAMEL-CASE-P and NUMERIC-ENUMS-P implement optional JSON printing options:
|
||||
https://developers.google.com/protocol-buffers/docs/proto3#json_options.
|
||||
|
||||
Parameters:
|
||||
OBJECT: The protocol buffer message to print.
|
||||
PRETTY-PRINT-P: When true, generate line breaks and other human readable output
|
||||
in the json. When false, replace line breaks with spaces.
|
||||
STREAM: The stream to print to.
|
||||
CAMEL-CASE-P: If true print proto field names in camelCase.
|
||||
NUMERIC-ENUMS-P: If true, use enum numeric values rather than names."
|
||||
(print-json-impl object (when pretty-print-p 0) stream camel-case-p numeric-enums-p
|
||||
nil))
|
||||
|
||||
(defun print-json-impl (object indent stream camel-case-p numeric-enums-p
|
||||
spliced-p)
|
||||
"Prints a protocol buffer message to a stream in JSON format.
|
||||
Parameters:
|
||||
OBJECT: The protocol buffer message to print.
|
||||
INDENT: Indent the output by INDENT spaces. If INDENT is NIL, then the
|
||||
output will not be pretty-printed.
|
||||
STREAM: The stream to print to.
|
||||
CAMEL-CASE-P: If true print proto field names in camelCase.
|
||||
NUMERIC-ENUMS-P: If true, use enum numeric values rather than names.
|
||||
SPLICED-P: Prints a protocol buffer object inside of the printing
|
||||
of another protocol buffer object as if they were spliced
|
||||
together. Currently only happens while printing a well-known-type.
|
||||
This happens because we have to print the well-known-type metadata.
|
||||
Example using Any well known type:
|
||||
{
|
||||
\"url\": \"type.googleapis.com/google.protobuf.Struct\",
|
||||
contained-proto
|
||||
}"
|
||||
(let* ((type (type-of object))
|
||||
(message (find-message-descriptor type :error-p t)))
|
||||
;; If TYPE has a special JSON mapping, use that.
|
||||
(when (special-json-p type)
|
||||
(print-special-json object type stream indent camel-case-p numeric-enums-p)
|
||||
(return-from print-json-impl))
|
||||
(unless spliced-p
|
||||
(format stream "{")
|
||||
(when indent (format stream "~%")))
|
||||
;; Boolean that tracks if a field is printed. Used for printing commas
|
||||
;; correctly. If this object is spliced into an existing JSON object, then
|
||||
;; a field has been already printed, so always print a comma.
|
||||
(let ((field-printed spliced-p))
|
||||
(dolist (field (proto-fields message))
|
||||
(when (if (eq (slot-value field 'pi::kind) :extends)
|
||||
(has-extension object (slot-value field 'external-field-name))
|
||||
(has-field object (slot-value field 'pi::external-field-name)))
|
||||
(let* ((name (if camel-case-p
|
||||
(pi::proto-json-name field)
|
||||
(proto-name field)))
|
||||
(type (proto-class field))
|
||||
(value
|
||||
(if (eq (slot-value field 'pi::kind) :extends)
|
||||
(get-extension object (slot-value field 'pi::external-field-name))
|
||||
(proto-slot-value object (slot-value field 'pi::external-field-name)))))
|
||||
(if field-printed
|
||||
(format stream ",")
|
||||
(setf field-printed t))
|
||||
(if indent
|
||||
(format stream "~&~V,0T\"~A\": " (+ indent 2) name)
|
||||
(format stream "\"~A\":" name))
|
||||
(if (not (eq (proto-label field) :repeated))
|
||||
(print-field-to-json value type (and indent (+ indent 2))
|
||||
stream camel-case-p numeric-enums-p)
|
||||
(let (repeated-printed)
|
||||
(format stream "[")
|
||||
(pi::doseq (v value)
|
||||
(if repeated-printed
|
||||
(format stream ",")
|
||||
(setf repeated-printed t))
|
||||
(when indent (format stream "~&~V,0T" (+ indent 4)))
|
||||
(print-field-to-json v type (and indent (+ indent 4))
|
||||
stream camel-case-p numeric-enums-p))
|
||||
(if indent
|
||||
(format stream "~&~V,0T]" (+ indent 2))
|
||||
(format stream "]")))))))
|
||||
(dolist (oneof (pi::proto-oneofs message))
|
||||
(let* ((oneof-data (slot-value object (pi::oneof-descriptor-internal-name oneof)))
|
||||
(set-field (pi::oneof-set-field oneof-data)))
|
||||
(when set-field
|
||||
(let* ((field-desc (aref (pi::oneof-descriptor-fields oneof) set-field))
|
||||
(type (proto-class field-desc))
|
||||
(value (pi::oneof-value oneof-data))
|
||||
(name (if camel-case-p
|
||||
(pi::proto-json-name field-desc)
|
||||
(proto-name field-desc))))
|
||||
(if field-printed
|
||||
(format stream ",")
|
||||
(setf field-printed t))
|
||||
(if indent
|
||||
(format stream "~&~V,0T\"~A\": " (+ indent 2) name)
|
||||
(format stream "\"~A\":" name))
|
||||
(print-field-to-json value type (and indent (+ indent 2))
|
||||
stream camel-case-p numeric-enums-p))))))
|
||||
(if indent
|
||||
(format stream "~&~V,0T}" indent)
|
||||
(format stream "}"))))
|
||||
|
||||
(defun print-field-to-json (value type indent stream camel-case-p numeric-enums-p)
|
||||
"Print a field to JSON format.
|
||||
|
||||
Parameters:
|
||||
VALUE: The value held by the field
|
||||
TYPE: The proto-class slot of the field.
|
||||
INDENT: If non-nil, the amount to indent when pretty-printing.
|
||||
STREAM: The stream to print to.
|
||||
CAMEL-CASE-P: Passed recursively to PRINT-JSON.
|
||||
NUMERIC-ENUMS-P: Passed recursively to PRINT-ENUM-TO-JSON and PRINT-JSON."
|
||||
(let ((descriptor (or (find-message-descriptor type)
|
||||
(find-enum-descriptor type)
|
||||
(find-map-descriptor type))))
|
||||
(cond
|
||||
((pi::scalarp type)
|
||||
(print-scalar-to-json value type stream))
|
||||
((typep descriptor 'pi::message-descriptor)
|
||||
(print-json-impl value indent stream camel-case-p numeric-enums-p nil))
|
||||
((typep descriptor 'pi::enum-descriptor)
|
||||
(print-enum-to-json value type stream numeric-enums-p))
|
||||
((typep descriptor 'pi::map-descriptor)
|
||||
(print-map-to-json value descriptor indent
|
||||
stream camel-case-p numeric-enums-p)))))
|
||||
|
||||
(defun print-scalar-to-json (value type stream)
|
||||
"Print scalar VALUE of type TYPE to STREAM."
|
||||
(ecase type
|
||||
((int32 fixed32 uint32 sfixed32 sint32)
|
||||
(format stream "~D" value))
|
||||
((int64 fixed64 uint64 sfixed64 sint64)
|
||||
(format stream "\"~D\"" value))
|
||||
((float double-float)
|
||||
(format stream "~F" value))
|
||||
((string)
|
||||
(format stream "\"~A\"" value))
|
||||
((boolean)
|
||||
(format stream "~A" (if value "true" "false")))
|
||||
((byte-vector)
|
||||
(format stream "\"~A\"" (cl-base64:usb8-array-to-base64-string value)))
|
||||
((keyword)
|
||||
(format stream "\"~A\"" value))
|
||||
((symbol)
|
||||
(let ((*package* (find-package "COMMON-LISP")))
|
||||
(format stream "\"~S\"" value)))))
|
||||
|
||||
(defun print-enum-to-json (value type stream numeric-enums-p)
|
||||
"Print an enum VALUE of type TYPE to STREAM. If NUMERIC-ENUMS-P, then print the enums value
|
||||
rather than its name."
|
||||
(when (eql type 'google:null-value)
|
||||
(format stream "null")
|
||||
(return-from print-enum-to-json))
|
||||
(if numeric-enums-p
|
||||
(format stream "~D" (enum-keyword-to-int type value))
|
||||
(format stream "\"~A\"" (pi::enum-name->proto value))))
|
||||
|
||||
(defun print-map-to-json (value map-descriptor indent stream camel-case-p numeric-enums-p)
|
||||
"Print a map type to JSON.
|
||||
|
||||
Parameters:
|
||||
VALUE: The hash-table to print.
|
||||
MAP-DESCRIPTOR: The map-descriptor of the map.
|
||||
INDENT: If non-nil, the amount to indent when pretty-printing.
|
||||
STREAM: The stream to print to.
|
||||
CAMEL-CASE-P, NUMERIC-ENUMS-P: passed recursively to PRINT-FIELD-TO-JSON."
|
||||
(format stream "{")
|
||||
(when indent (format stream "~%"))
|
||||
(let ((pair-printed nil))
|
||||
(loop for k being the hash-key of value using (hash-value v)
|
||||
do (if pair-printed
|
||||
(format stream ",")
|
||||
(setf pair-printed t))
|
||||
(if indent
|
||||
(format stream "~&~V,0T\"~A\": " (+ indent 2) k)
|
||||
(format stream "\"~A\":" (write-to-string k)))
|
||||
(print-field-to-json v (pi::proto-value-type map-descriptor)
|
||||
(and indent (+ indent 2)) stream camel-case-p numeric-enums-p)))
|
||||
(if indent
|
||||
(format stream "~&~V,0T}" indent)
|
||||
(format stream "}")))
|
||||
|
||||
;;; Parse objects that were serialized using JSON format.
|
||||
|
||||
;;; TODO(cgay): replace all assertions here with something that signals a
|
||||
;;; subtype of protobuf-error and shows current stream position.
|
||||
|
||||
(defun parse-json (type
|
||||
&key (stream *standard-input*) ignore-unknown-fields-p)
|
||||
"Parses JSON text into a protobuf messsage of type TYPE.
|
||||
|
||||
Parameters:
|
||||
TYPE: The object type as a symbol.
|
||||
STREAM: The stream to read from.
|
||||
IGNORE-UNKNOWN-FIELDS-P: If true, then skip fields which are not defined in the
|
||||
message TYPE descriptor. Otherwise, throw an error."
|
||||
(declare (type symbol type))
|
||||
(let ((message (find-message-descriptor type :error-p t)))
|
||||
(parse-json-impl message stream ignore-unknown-fields-p nil)))
|
||||
|
||||
(defun parse-json-impl (msg-desc stream ignore-unknown-fields-p spliced-p)
|
||||
"Parse a JSON formatted message with descriptor MSG-DESC from STREAM. If IGNORE-UNKNOWN-FIELDS-P
|
||||
is true, then skip fields which are not defined in MSG-DESC. Otherwise, throw an error. If
|
||||
SPLICED-P is true, then do not attempt to parse an opening bracket."
|
||||
(declare (type message-descriptor msg-desc))
|
||||
(let ((object (funcall (pi::get-constructor-name
|
||||
(or (pi::proto-alias-for msg-desc)
|
||||
(proto-class msg-desc)))))
|
||||
;; Repeated slot names, tracks which slots need to be nreversed.
|
||||
(rslots ()))
|
||||
(when (special-json-p (proto-class msg-desc))
|
||||
(return-from parse-json-impl
|
||||
(parse-special-json (proto-class msg-desc)
|
||||
stream
|
||||
ignore-unknown-fields-p)))
|
||||
(unless spliced-p
|
||||
(pi::expect-char stream #\{))
|
||||
(loop
|
||||
(let* ((name (pi::parse-string stream))
|
||||
(field (or (find-field-descriptor msg-desc name)
|
||||
(find-field-descriptor-by-json-name msg-desc name)))
|
||||
(type (and field (proto-class field)))
|
||||
(slot (and field (pi::proto-external-field-name field))))
|
||||
(pi::expect-char stream #\:)
|
||||
(if (null field)
|
||||
;; If FIELD is null, then we assume that MSG-DESC describes a
|
||||
;; different version of the proto on the wire which doesn't
|
||||
;; have FIELD, and continue,
|
||||
(if ignore-unknown-fields-p
|
||||
(skip-json-value stream)
|
||||
(error 'unknown-field-type
|
||||
:format-control "unknown field ~S encountered in message ~S"
|
||||
:format-arguments (list name msg-desc)))
|
||||
(let (val error-p null-p)
|
||||
(cond
|
||||
((eql (peek-char nil stream nil) #\n)
|
||||
(pi::expect-token-or-string stream "null")
|
||||
(setf null-p t))
|
||||
((eq (proto-label field) :repeated)
|
||||
(pi::expect-char stream #\[)
|
||||
(loop
|
||||
(multiple-value-bind (data err)
|
||||
(parse-value-from-json type :stream stream
|
||||
:ignore-unknown-fields-p ignore-unknown-fields-p)
|
||||
(if err
|
||||
(setf error-p t)
|
||||
(push data val)))
|
||||
(if (eql (peek-char nil stream nil) #\,)
|
||||
(pi::expect-char stream #\,)
|
||||
(return)))
|
||||
(pi::expect-char stream #\]))
|
||||
(t (multiple-value-setq (val error-p)
|
||||
(parse-value-from-json type
|
||||
:stream stream
|
||||
:ignore-unknown-fields-p ignore-unknown-fields-p))))
|
||||
(cond
|
||||
(null-p nil)
|
||||
(error-p
|
||||
(unknown-field-type type field msg-desc)
|
||||
(return-from parse-json-impl))
|
||||
((eq (pi::proto-kind field) :map)
|
||||
(dolist (pair val)
|
||||
(setf (gethash (car pair) (proto-slot-value object slot))
|
||||
(cdr pair))))
|
||||
(t
|
||||
(when slot
|
||||
(setf (proto-slot-value object slot) val)
|
||||
(when (eq (proto-label field) :repeated)
|
||||
(pushnew slot rslots))))))))
|
||||
(if (eql (peek-char nil stream nil) #\,)
|
||||
(pi::expect-char stream #\,)
|
||||
(progn
|
||||
(pi::expect-char stream #\})
|
||||
(dolist (slot rslots)
|
||||
(setf (proto-slot-value object slot)
|
||||
(nreverse (proto-slot-value object slot))))
|
||||
(return-from parse-json-impl object))))))
|
||||
|
||||
(defun parse-value-from-json (type &key (stream *standard-input*) ignore-unknown-fields-p)
|
||||
"Parse a single JSON value of type TYPE from STREAM. IGNORE-UNKNOWN-FIELDS-P is passed
|
||||
to recursive calls to PARSE-JSON-IMPL."
|
||||
(let ((desc (or (find-message-descriptor type)
|
||||
(find-enum-descriptor type)
|
||||
(find-map-descriptor type))))
|
||||
(cond ((pi::scalarp type)
|
||||
(case type
|
||||
((float) (pi::parse-float stream))
|
||||
((double-float) (pi::parse-double stream :append-d0 t))
|
||||
((string) (pi::parse-string stream))
|
||||
((boolean)
|
||||
(let ((token (pi::parse-token stream)))
|
||||
(cond ((string= token "true") t)
|
||||
((string= token "false") nil)
|
||||
;; Parsing failed, return T as a second
|
||||
;; value to indicate a failure.
|
||||
(t (values nil t)))))
|
||||
((byte-vector)
|
||||
(cl-base64:base64-string-to-usb8-array (pi::parse-string stream)))
|
||||
(otherwise
|
||||
(if (eql (peek-char nil stream nil) #\")
|
||||
(let (ret)
|
||||
(pi::expect-char stream #\")
|
||||
(setf ret (pi::parse-signed-int stream))
|
||||
(pi::expect-char stream #\")
|
||||
ret)
|
||||
(pi::parse-signed-int stream)))))
|
||||
((typep desc 'pi::message-descriptor)
|
||||
(parse-json-impl desc stream ignore-unknown-fields-p nil))
|
||||
((typep desc 'pi::enum-descriptor)
|
||||
(multiple-value-bind (name type-parsed)
|
||||
(pi::parse-token-or-string stream)
|
||||
;; special handling for well known enum NullValue.
|
||||
(when (eql type 'google:null-value)
|
||||
(if (string= name "null")
|
||||
(return-from parse-value-from-json :null-value)
|
||||
(protobuf-error
|
||||
"~S is not a valid keyword for well-known enum NullValue" name)))
|
||||
(let ((enum (if (eql type-parsed 'symbol)
|
||||
;; If the parsed type is a symbol, then the enum was printed
|
||||
;; as an integer. Otherwise, it is a string which names a
|
||||
;; keyword.
|
||||
(find (parse-integer name) (pi::enum-descriptor-values desc)
|
||||
:key #'pi::enum-value-descriptor-value)
|
||||
(find (pi::keywordify name)
|
||||
(pi::enum-descriptor-values desc)
|
||||
:key #'pi::enum-value-descriptor-name))))
|
||||
(and enum (pi::enum-value-descriptor-name enum)))))
|
||||
;; In the case of maps, return a list of key-value pairs.
|
||||
((typep desc 'pi::map-descriptor)
|
||||
(pi::expect-char stream #\{)
|
||||
(loop with pairs = ()
|
||||
with key-type = (pi::proto-key-type desc)
|
||||
with val-type = (pi::proto-value-type desc)
|
||||
for pair = (cons nil nil)
|
||||
do (setf (car pair)
|
||||
(if (eql key-type 'string)
|
||||
(pi::parse-string stream)
|
||||
(parse-integer (pi::parse-string stream))))
|
||||
(pi::expect-char stream #\:)
|
||||
(setf (cdr pair) (parse-value-from-json val-type :stream stream))
|
||||
(push pair pairs)
|
||||
(if (eql (peek-char nil stream nil) #\,)
|
||||
(pi::expect-char stream #\,)
|
||||
(progn
|
||||
(pi::expect-char stream #\})
|
||||
(return pairs)))))
|
||||
(t (values nil t)))))
|
||||
|
||||
(defun skip-json-value (stream)
|
||||
"Skip a single JSON value in STREAM. This can
|
||||
be either an array, object, or primitive."
|
||||
(pi::skip-whitespace stream)
|
||||
(case (peek-char nil stream nil)
|
||||
((#\{) (skip-json-object stream))
|
||||
((#\[) (skip-json-array stream))
|
||||
(t (pi::parse-token-or-string stream))))
|
||||
|
||||
(defun skip-json-array (stream)
|
||||
"Skip a JSON array in STREAM."
|
||||
(pi::expect-char stream #\[)
|
||||
(loop do (skip-json-value stream)
|
||||
(if (eql (peek-char nil stream nil) #\,)
|
||||
(pi::expect-char stream #\,)
|
||||
(return)))
|
||||
(pi::skip-whitespace stream)
|
||||
(pi::expect-char stream #\]))
|
||||
|
||||
(defun skip-json-object (stream)
|
||||
"Skip a JSON object in STREAM."
|
||||
(pi::expect-char stream #\{)
|
||||
(loop do (pi::parse-string stream)
|
||||
(pi::expect-char stream #\:)
|
||||
(skip-json-value stream)
|
||||
(if (eql (peek-char nil stream nil) #\,)
|
||||
(pi::expect-char stream #\,)
|
||||
(return)))
|
||||
(pi::skip-whitespace stream)
|
||||
(pi::expect-char stream #\}))
|
||||
|
||||
(defun find-field-descriptor-by-json-name (msg-desc name)
|
||||
"Return the field-descriptor with json-name NAME in MSG-DESC."
|
||||
(or (find name (proto-fields msg-desc) :key #'pi::proto-json-name :test #'string=)
|
||||
(loop for oneof in (pi::proto-oneofs msg-desc)
|
||||
thereis (find name (pi::oneof-descriptor-fields oneof)
|
||||
:key #'pi::proto-json-name
|
||||
:test #'string=))))
|
||||
|
||||
;; Special JSON mappings for well known types below
|
||||
|
||||
(defun special-json-p (type)
|
||||
"Check if the message TYPE has a special JSON mapping."
|
||||
(member type '(google:any
|
||||
google:timestamp
|
||||
google:duration
|
||||
google:struct
|
||||
google:value
|
||||
google:field-mask
|
||||
google:list-value
|
||||
google:bool-value
|
||||
google:string-value
|
||||
google:bytes-value
|
||||
google:double-value
|
||||
google:float-value
|
||||
google:int32-value
|
||||
google:int64-value
|
||||
google:u-int32-value
|
||||
google:u-int64-value)))
|
||||
|
||||
(defun wrapper-message->type (type)
|
||||
"For a well known wrapper type TYPE, return the type being wrapped."
|
||||
(ecase type
|
||||
((google:bool-value) 'boolean)
|
||||
((google:string-value) 'string)
|
||||
((google:bytes-value) 'byte-vector)
|
||||
((google:double-value) 'double-float)
|
||||
((google:float-value) 'float)
|
||||
((google:int32-value) 'int32)
|
||||
((google:int64-value) 'int64)
|
||||
((google:u-int32-value) 'uint32)
|
||||
((google:u-int64-value) 'uint64)))
|
||||
|
||||
|
||||
(defun print-special-json (object type stream indent camel-case-p numeric-enums-p)
|
||||
"For an OBJECT whose TYPE is a well-known type, print the object's special JSON mapping
|
||||
to STREAM. INDENT, CAMEL-CASE-P, and NUMERIC-ENUMS-P are passed recursively to
|
||||
PRINT-JSON-IMPL for any types."
|
||||
(declare (type symbol type))
|
||||
(case type
|
||||
((google:any)
|
||||
(let ((url (google:any.type-url object))
|
||||
(packed-message (wkt:unpack-any object)))
|
||||
(format stream "{")
|
||||
(if indent
|
||||
(format stream "~&~V,0T\"url\": \"~A\"" (+ indent 2) url)
|
||||
(format stream "\"url\": \"~A\"" url))
|
||||
(if (special-json-p (type-of packed-message))
|
||||
;; special handling for nested special json mapping within an ANY.
|
||||
(progn
|
||||
(if indent
|
||||
(format stream ",~&~V,0T\"value\": " (+ indent 2))
|
||||
(format stream ",\"value\":"))
|
||||
(print-special-json packed-message (type-of packed-message) stream
|
||||
(and indent (+ indent 2)) camel-case-p numeric-enums-p)
|
||||
(if indent
|
||||
(format stream "~&~V,0T}" indent)
|
||||
(format stream "}")))
|
||||
(print-json-impl packed-message indent stream camel-case-p
|
||||
numeric-enums-p t))))
|
||||
((google:timestamp)
|
||||
(let* ((nsec (google:timestamp.nanos object))
|
||||
(timestamp (local-time:unix-to-timestamp
|
||||
(google:timestamp.seconds object)
|
||||
:nsec nsec))
|
||||
(prefix '((:year 4) #\- (:month 2) #\- (:day 2) #\T
|
||||
(:hour 2) #\: (:min 2) #\: (:sec 2)))
|
||||
(suffix '(:gmt-offset-or-z))
|
||||
(format (cond ((= nsec 0) (append prefix suffix))
|
||||
((= (mod nsec 1000000) 0) (append prefix '(#\. (:msec 3)) suffix))
|
||||
((= (mod nsec 1000) 0) (append prefix '(#\. (:usec 6)) suffix))
|
||||
(t (append prefix '(#\. (:nsec 9)) suffix)))))
|
||||
(format stream "~S" (local-time:format-timestring
|
||||
nil timestamp
|
||||
:format format
|
||||
:timezone local-time:+utc-zone+))))
|
||||
((google:duration)
|
||||
(let ((seconds (google:duration.seconds object))
|
||||
(nanos (google:duration.nanos object)))
|
||||
(assert (eql (signum seconds) (signum nanos)))
|
||||
(format stream "\"~D.~V,VDs\"" seconds 9 #\0 (abs nanos))))
|
||||
((google:field-mask)
|
||||
(let ((paths (google:field-mask.paths object)))
|
||||
(format stream "\"~{~a~^,~}\"" (mapcar (lambda (name)
|
||||
(pi::camel-case-but-one name '(#\_)))
|
||||
paths))))
|
||||
((google:struct)
|
||||
(let ((field (pi::%find-field-descriptor (find-message-descriptor type) 'google::%fields)))
|
||||
(print-map-to-json (google:fields object) (find-map-descriptor (proto-class field))
|
||||
indent stream camel-case-p numeric-enums-p)))
|
||||
((google:list-value)
|
||||
(format stream "[")
|
||||
(loop for print-comma-p = nil then t
|
||||
for value in (google:values object)
|
||||
do (when print-comma-p (format stream ","))
|
||||
(when indent (format stream "~&~V,0T" (+ 2 indent)))
|
||||
(print-field-to-json value 'google:value (and indent (+ indent 2))
|
||||
stream camel-case-p numeric-enums-p))
|
||||
(if indent
|
||||
(format stream "~&~V,0T]" indent)
|
||||
(format stream "]")))
|
||||
((google:value)
|
||||
(let* ((oneof-data (slot-value object 'google::%kind))
|
||||
;; The wkt Value consists of a single oneof, so the first oneof in the
|
||||
;; descriptor's list is the one we are looking for.
|
||||
(oneof-desc (first (pi::proto-oneofs (find-message-descriptor type))))
|
||||
(set-field (pi::oneof-set-field oneof-data)))
|
||||
(assert set-field ()
|
||||
"Message ~S must have a set 'kind' oneof as it has well-known-type 'Value'." object)
|
||||
(let* ((field (aref (pi::oneof-descriptor-fields oneof-desc)
|
||||
(pi::oneof-set-field oneof-data)))
|
||||
(value (pi::oneof-value oneof-data)))
|
||||
(print-field-to-json value (proto-class field)
|
||||
indent stream camel-case-p numeric-enums-p))))
|
||||
;; Otherwise, TYPE is a wrapper type.
|
||||
(t (if object
|
||||
(print-scalar-to-json (google:value object)
|
||||
(wrapper-message->type type)
|
||||
stream)
|
||||
(format stream "null")))))
|
||||
|
||||
(defun parse-special-json (type stream ignore-unknown-fields-p)
|
||||
"Parse a well known type TYPE from STREAM. IGNORE-UNKNOWN-FIELDS-P is passed to recursive
|
||||
calls to PARSE-JSON-IMPL."
|
||||
;; If the stream starts with 'n', then the data is NULL. In which case, return NIL.
|
||||
;; In all cases except the `Value` well-known-type, we return NIL. However, if TYPE is
|
||||
;; GOOGLE:VALUE, then we return the wrapper enum that represents null as per the spec.
|
||||
(when (eql (peek-char nil stream nil) #\n)
|
||||
(pi::expect-token-or-string stream "null")
|
||||
(return-from parse-special-json
|
||||
(and (eql type 'google:value)
|
||||
(google:make-value :null-value :null-value))))
|
||||
(case type
|
||||
((google:any)
|
||||
(pi::expect-char stream #\{)
|
||||
(pi::expect-token-or-string stream "url")
|
||||
(pi::expect-char stream #\:)
|
||||
(let* ((type-url (pi::parse-string stream))
|
||||
(type (wkt::resolve-type-url type-url)))
|
||||
(pi::expect-char stream #\,)
|
||||
(if (not (special-json-p type))
|
||||
;; Parse the remaining elements in the object into a new message, then pack that message.
|
||||
(wkt:pack-any
|
||||
(parse-json-impl (find-message-descriptor type :error-p t)
|
||||
stream ignore-unknown-fields-p t))
|
||||
;; If URL names a well-known-type, then the next element in the object has key "VALUE",
|
||||
;; and the value is the special JSON format. Parse that and close the object.
|
||||
(let (ret)
|
||||
(pi::expect-token-or-string stream "value")
|
||||
(pi::expect-char stream #\:)
|
||||
(setf ret (parse-special-json type stream ignore-unknown-fields-p))
|
||||
(pi::expect-char stream #\})
|
||||
(wkt:pack-any ret)))))
|
||||
|
||||
((google:timestamp)
|
||||
(let* ((timestring (pi::parse-string stream))
|
||||
(timestamp (local-time:parse-rfc3339-timestring timestring)))
|
||||
(google:make-timestamp
|
||||
:seconds (local-time:timestamp-to-unix timestamp)
|
||||
:nanos (local-time:nsec-of timestamp))))
|
||||
|
||||
;; Durations can feasibly have 64-bit seconds place, so parsing a float/double is lossy.
|
||||
((google:duration)
|
||||
(pi::expect-char stream #\")
|
||||
(let ((seconds (pi::parse-signed-int stream)))
|
||||
(ecase (peek-char nil stream nil)
|
||||
;; Duration has no decimal component.
|
||||
((#\s)
|
||||
(pi::expect-char stream #\s)
|
||||
(pi::expect-char stream #\")
|
||||
(google:make-duration :seconds seconds))
|
||||
((#\.)
|
||||
(pi::expect-char stream #\.)
|
||||
;; Parse the decimal part of the string, and convert to nanoseconds.
|
||||
(let ((remainder (pi::parse-token stream)))
|
||||
(assert (eql (char remainder (1- (length remainder))) #\s)
|
||||
nil "Duration string ~S.~A does end with \"s\"" seconds remainder)
|
||||
(pi::expect-char stream #\")
|
||||
(let* ((decimals (subseq remainder 0 (1- (length remainder))))
|
||||
;; If there are more than 9 decimal points, trim to length 9.
|
||||
(decimals (if (< 9 (length decimals))
|
||||
(subseq decimals 0 10)
|
||||
decimals))
|
||||
(dec-length (length decimals)))
|
||||
(google:make-duration
|
||||
:seconds seconds
|
||||
;; Nanoseconds are in the range 0 through 999,999,999. Pad the decimal string
|
||||
;; with 0s to make the string have total length 9.
|
||||
;; Lastly, multiply by the sign of SECONDS, as NANOS and and SECONDS must
|
||||
;; have the same sign.
|
||||
:nanos (* (if (= 0 seconds) 1 (signum seconds))
|
||||
(parse-integer (concatenate 'string
|
||||
decimals
|
||||
(make-string (- 9 dec-length)
|
||||
:initial-element #\0)))))))))))
|
||||
|
||||
;; Field masks are in the form \"camelCasePath1,path2,path3\". We need to first split,
|
||||
;; then convert to proto field name format (lowercase, separated by underscore).
|
||||
((google:field-mask)
|
||||
(let ((camel-case-paths (pi::split-string (pi::parse-string stream)
|
||||
:separators '(#\,))))
|
||||
(google:make-field-mask
|
||||
:paths (mapcar (lambda (path) (nstring-downcase (pi::uncamel-case path #\_)))
|
||||
camel-case-paths))))
|
||||
|
||||
((google:struct)
|
||||
(pi::expect-char stream #\{)
|
||||
(loop with ret = (google:make-struct)
|
||||
for key = (pi::parse-string stream)
|
||||
do (pi::expect-char stream #\:)
|
||||
(setf (google:struct.fields-gethash key ret)
|
||||
(parse-special-json 'google:value stream ignore-unknown-fields-p))
|
||||
(if (eql (peek-char nil stream nil) #\,)
|
||||
(pi::expect-char stream #\,)
|
||||
(progn
|
||||
(pi::expect-char stream #\})
|
||||
(return ret)))))
|
||||
|
||||
((google:list-value)
|
||||
(pi::expect-char stream #\[)
|
||||
(loop with ret = (google:make-list-value)
|
||||
do (multiple-value-bind (data err)
|
||||
(parse-value-from-json 'google:value
|
||||
:stream stream
|
||||
:ignore-unknown-fields-p ignore-unknown-fields-p)
|
||||
(if err
|
||||
(error "Error while parsing well known type VALUE from JSON format.")
|
||||
(push data (google:list-value.values ret))))
|
||||
(if (eql (peek-char nil stream nil) #\,)
|
||||
(pi::expect-char stream #\,)
|
||||
(progn
|
||||
(pi::expect-char stream #\])
|
||||
(return ret)))))
|
||||
|
||||
((google:value)
|
||||
(case (peek-char nil stream nil)
|
||||
((#\{) (google:make-value
|
||||
:struct-value (parse-special-json 'google:struct stream ignore-unknown-fields-p)))
|
||||
((#\[) (google:make-value
|
||||
:list-value (parse-special-json 'google:list-value stream ignore-unknown-fields-p)))
|
||||
((#\") (google:make-value :string-value (pi::parse-string stream)))
|
||||
((#\t)
|
||||
(pi::expect-token-or-string stream "true")
|
||||
(google:make-value :bool-value t))
|
||||
((#\f)
|
||||
(pi::expect-token-or-string stream "false")
|
||||
(google:make-value :bool-value nil))
|
||||
;; Otherwise, the value has type double.
|
||||
(t (google:make-value :number-value (pi::parse-double stream :append-d0 t)))))
|
||||
|
||||
;; Otherwise, the well known type is a wrapper type.
|
||||
(t (let ((object (funcall (pi::get-constructor-name type)))
|
||||
(value (parse-value-from-json (wrapper-message->type type) :stream stream)))
|
||||
(setf (google:value object) value)
|
||||
object))))
|
||||
|
||||
(defun fmt (stream proto colon-p at-sign-p &optional width &rest other-args)
|
||||
"Format command for protobufs
|
||||
~/cl-protobufs.json:fmt/ emits a non-pretty-printed protobuf of PROTO to STREAM.
|
||||
~@/cl-protobufs.json:fmt/ emits a pretty-printed protobuf of PROTO to STREAM.
|
||||
COLON-P and AT-SIGN-P are the usual for format directives.
|
||||
WIDTH and OTHER-ARGS are ignored."
|
||||
(declare (ignore width))
|
||||
(cond (other-args (error "FORMAT directive ~~/cl-protobufs.json:fmt/ takes only one argument."))
|
||||
(colon-p (error "FORMAT directive ~~/cl-protobufs.json:fmt/ does not take colons."))
|
||||
(t (print-json proto :stream stream :pretty-print-p at-sign-p))))
|
||||
326
examples/meshtastic/lisp/cl-protobufs/message-api.lisp
Normal file
326
examples/meshtastic/lisp/cl-protobufs/message-api.lisp
Normal file
|
|
@ -0,0 +1,326 @@
|
|||
;;; Copyright 2012-2020 Google LLC
|
||||
;;;
|
||||
;;; Use of this source code is governed by an MIT-style
|
||||
;;; license that can be found in the LICENSE file or at
|
||||
;;; https://opensource.org/licenses/MIT.
|
||||
|
||||
(in-package #:cl-protobufs.implementation)
|
||||
|
||||
(defun object-initialized-p (object message)
|
||||
"Check if OBJECT with proto-message MESSAGE is initialized.
|
||||
The definition of initialized is all required-fields are set."
|
||||
(loop for field in (proto-fields message)
|
||||
when (eq (proto-label field) :required)
|
||||
do (when (= (bit (slot-value object '%%is-set)
|
||||
(proto-field-offset field))
|
||||
0)
|
||||
(return-from object-initialized-p nil))
|
||||
when (and (member (proto-kind field) '(:message :group :extends))
|
||||
(or (eq (proto-label field) :repeated)
|
||||
(= (bit (slot-value object '%%is-set)
|
||||
(proto-field-offset field))
|
||||
1)))
|
||||
do (let ((lisp-type (proto-class field))
|
||||
(field-value (slot-value object (proto-internal-field-name field))))
|
||||
(when (and (not (keywordp lisp-type))
|
||||
(find-message-descriptor lisp-type))
|
||||
(doseq (msg (if (eq (proto-label field) :repeated)
|
||||
field-value
|
||||
(list field-value)))
|
||||
(unless (object-initialized-p msg (find-message-descriptor lisp-type))
|
||||
(return-from object-initialized-p nil))))))
|
||||
t)
|
||||
|
||||
(defun is-initialized (object)
|
||||
"Returns true if all of the fields of OBJECT are initialized."
|
||||
(let* ((class (type-of object))
|
||||
(desc (find-message-descriptor class :error-p t)))
|
||||
(object-initialized-p object desc)))
|
||||
|
||||
(defun map-field-equal (map-1 map-2 map-descriptor exact)
|
||||
"Returns true if two maps with the same map-descriptor are equal.
|
||||
Parameters:
|
||||
MAP-1: The first map to compare.
|
||||
MAP-2: The second map to compare.
|
||||
MAP-DESCRIPTOR: The map descriptor for the two maps.
|
||||
EXACT: If true consider the messages to be equal
|
||||
only if the same fields have been explicitly set."
|
||||
(and (= (hash-table-count map-1)
|
||||
(hash-table-count map-2))
|
||||
|
||||
(loop for key being the hash-keys of map-1
|
||||
using (hash-value map-1-value)
|
||||
for map-2-value = (gethash key map-2)
|
||||
always
|
||||
(if (or (scalarp (proto-value-type map-descriptor))
|
||||
(find-enum-descriptor (proto-value-type map-descriptor)))
|
||||
(scalar-field-equal map-1-value
|
||||
map-2-value)
|
||||
(proto-equal map-1-value
|
||||
map-2-value
|
||||
:exact exact)))))
|
||||
|
||||
(defun oneof-field-equal (oneof-1 oneof-2 oneof-descriptor exact)
|
||||
"Returns true if two maps with the same map-descriptor are equal.
|
||||
Parameters:
|
||||
ONEOF-1: The first oneof to compare.
|
||||
ONEOF-2: The second oneof to compare.
|
||||
ONEOF-DESCRIPTOR: The oneof descriptor for the two oneofs.
|
||||
EXACT: If true consider the messages to be equal
|
||||
only if the same fields have been explicitly set."
|
||||
(let ((set-field-1 (oneof-set-field oneof-1))
|
||||
(set-field-2 (oneof-set-field oneof-2)))
|
||||
|
||||
;; Check if one of the fields aren't set.
|
||||
(unless (and set-field-1 set-field-2)
|
||||
(return-from oneof-field-equal
|
||||
(not (or set-field-1 set-field-2))))
|
||||
|
||||
;; Check the same field is set.
|
||||
(unless (eql (oneof-set-field oneof-1)
|
||||
(oneof-set-field oneof-2))
|
||||
(return-from oneof-field-equal nil))
|
||||
|
||||
;; Check for field equality.
|
||||
(let* ((lisp-type
|
||||
(proto-class
|
||||
(aref (oneof-descriptor-fields oneof-descriptor)
|
||||
set-field-1))))
|
||||
(if (or (scalarp lisp-type)
|
||||
(find-enum-descriptor lisp-type))
|
||||
(scalar-field-equal (oneof-value oneof-1)
|
||||
(oneof-value oneof-2))
|
||||
(proto-equal (oneof-value oneof-1)
|
||||
(oneof-value oneof-2)
|
||||
:exact exact)))))
|
||||
|
||||
(defun non-bool-field-equal (field-1 field-2 field-descriptor exact)
|
||||
"Returns true if two proto-fields which aren't bools or oneofs are equal.
|
||||
Parameters:
|
||||
FIELD-1: The first field to compare.
|
||||
FIELD-2: The second field to compare.
|
||||
FIELD-DESCRIPTOR: The field descriptor for the two fields.
|
||||
EXACT: If true consider the messages to be equal
|
||||
only if the same fields have been explicitly set."
|
||||
(declare (type field-descriptor field-descriptor))
|
||||
(let ((lisp-type (proto-class field-descriptor)))
|
||||
(assert (not (eql lisp-type 'boolean)))
|
||||
|
||||
(unless (and field-1 field-2)
|
||||
(return-from non-bool-field-equal
|
||||
(not (or field-1 field-2))))
|
||||
|
||||
(when (or (scalarp lisp-type)
|
||||
(find-enum-descriptor lisp-type))
|
||||
(return-from non-bool-field-equal
|
||||
(scalar-field-equal field-1 field-2)))
|
||||
|
||||
(when (eql (proto-kind field-descriptor) :map)
|
||||
(return-from non-bool-field-equal
|
||||
(map-field-equal field-1
|
||||
field-2
|
||||
(find-map-descriptor lisp-type)
|
||||
exact))))
|
||||
|
||||
(if (proto-container field-descriptor)
|
||||
(and (= (length field-1) (length field-2))
|
||||
(every (lambda (x y) (proto-equal x y :exact exact))
|
||||
field-1 field-2))
|
||||
(proto-equal field-1 field-2 :exact exact)))
|
||||
|
||||
(defun scalar-field-equal (object-1 object-2)
|
||||
"Check if two objects with scalar type are equal.
|
||||
Parameters:
|
||||
OBJECT-1: The first scalar object.
|
||||
OBJECT-2: The second scalar object."
|
||||
(typecase object-1
|
||||
(string (string= object-1 object-2))
|
||||
(byte-vector (equalp object-1 object-2))
|
||||
((or list vector)
|
||||
(and (= (length object-1) (length object-2))
|
||||
(every #'scalar-field-equal object-1 object-2)))
|
||||
(t (eql object-1 object-2))))
|
||||
|
||||
(defun proto-equal (message-1 message-2 &key exact)
|
||||
"Check if MESSAGE-1 and MESSAGE-2 are the same. By default two messages are equal if calling the
|
||||
getter on each field would retrieve the same value. This means that a message with a field
|
||||
explicitly set to the default value is considered the same as a message with that field not set.
|
||||
If EXACT is true the messages are considered equal only if the same fields have been explicitly
|
||||
set."
|
||||
(let* ((class-1 (type-of message-1))
|
||||
(desc (find-message-descriptor class-1)))
|
||||
(and
|
||||
;; Check the messages are the same.
|
||||
desc
|
||||
(eq (type-of message-2) class-1)
|
||||
|
||||
;; Check same fields are set if exact is specified.
|
||||
(or (not exact)
|
||||
(equalp (slot-value message-1 '%%is-set)
|
||||
(slot-value message-2 '%%is-set)))
|
||||
|
||||
;; Bool values are stored in a vector.
|
||||
(or (not (slot-exists-p message-1 '%%bool-values))
|
||||
(equalp (slot-value message-1 '%%bool-values)
|
||||
(slot-value message-2 '%%bool-values)))
|
||||
|
||||
;; oneofs
|
||||
(loop for oneof in (proto-oneofs desc)
|
||||
for slot-value-1
|
||||
= (slot-value message-1 (oneof-descriptor-internal-name oneof))
|
||||
for slot-value-2
|
||||
= (slot-value message-2 (oneof-descriptor-internal-name oneof))
|
||||
always (oneof-field-equal slot-value-1 slot-value-2 oneof exact))
|
||||
|
||||
;; regular fields
|
||||
(loop for field in (proto-fields desc)
|
||||
for lisp-type = (proto-class field)
|
||||
for boolp = (eq lisp-type 'boolean)
|
||||
for slot-value-1
|
||||
= (unless boolp
|
||||
(slot-value message-1 (proto-internal-field-name field)))
|
||||
for slot-value-2
|
||||
= (unless boolp
|
||||
(slot-value message-2 (proto-internal-field-name field)))
|
||||
always (or boolp
|
||||
(non-bool-field-equal slot-value-1 slot-value-2 field exact))))))
|
||||
|
||||
(defgeneric clear (object)
|
||||
(:documentation
|
||||
"Initialize all fields of OBJECT to their default values."))
|
||||
|
||||
(defun-inline has-field (object field)
|
||||
"Check if OBJECT has FIELD set."
|
||||
(funcall (field-accessors-has (get field (type-of object)))
|
||||
object))
|
||||
|
||||
(defun-inline clear-field (object field)
|
||||
"Check if OBJECT has FIELD set."
|
||||
(funcall (field-accessors-clear (get field (type-of object)))
|
||||
object))
|
||||
|
||||
(defun-inline proto-slot-value (object slot)
|
||||
"Get the value of a field in a protobuf object.
|
||||
Parameters:
|
||||
OBJECT: The protobuf object.
|
||||
SLOT: The slot in object to retrieve the value from."
|
||||
(funcall (field-accessors-get (get slot (type-of object)))
|
||||
object))
|
||||
|
||||
(defun-inline (setf proto-slot-value) (value object slot)
|
||||
"Set the value of a field in a protobuf object.
|
||||
Parameters:
|
||||
VALUE: The new value for the field.
|
||||
OBJECT: The protobuf object.
|
||||
SLOT: The slot in object to retrieve the value from."
|
||||
(funcall (fdefinition (field-accessors-set (get slot (type-of object))))
|
||||
value
|
||||
object))
|
||||
|
||||
(defgeneric encoded-field (object field-name)
|
||||
(:documentation
|
||||
"Returns the encoded value of the field FIELD-NAME, or signals
|
||||
protobuf-error if the field doesn't exist. For repeated fields, returns a
|
||||
list of the encoded values, which may contain NILs.")
|
||||
(:method ((object structure-object) slot)
|
||||
(let* ((class (type-of object))
|
||||
(message (find-message-descriptor class :error-p t))
|
||||
(field (find slot (proto-fields message) :key #'proto-external-field-name)))
|
||||
(unless field
|
||||
(let* ((lisp-package (or (symbol-package class)
|
||||
(protobuf-error "Lisp package not found for message ~A"
|
||||
(proto-name message))))
|
||||
(lazy-slot (intern (nstring-upcase (format nil "%~A" slot))
|
||||
lisp-package)))
|
||||
(setf field (%find-field-descriptor message lazy-slot))
|
||||
(when field
|
||||
(setf slot lazy-slot))))
|
||||
(unless field
|
||||
(protobuf-error "There is no protobuf field with the name ~S" slot))
|
||||
(let ((value (slot-value object (proto-internal-field-name field))))
|
||||
(if (eq (proto-label field) :repeated)
|
||||
(map 'list #'proto-%%bytes value)
|
||||
(proto-%%bytes value))))))
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defun merge-from (from-message to-message)
|
||||
"Merge messages.
|
||||
Taken from https://github.com/protocolbuffers/protobuf-go/blob/master/proto/merge.go:
|
||||
Populated scalar fields in FROM-MESSAGE are copied to TO-MESSAGE, while populated
|
||||
singular messages in FROM-MESSAGE are merged into TO-MESSAGE by recursively calling Merge.
|
||||
The elements of every list field in FROM-MESSAGE is appended to the corresponded
|
||||
list fields in TO-MESSAGE. The entries of every map field in FROM-MESSAGE is copied into
|
||||
the corresponding map field in TO-MESSAGE, possibly replacing existing entries."
|
||||
(labels ((create-message-of-same-type (message)
|
||||
(let ((class (find-class (type-of message))))
|
||||
(funcall (get-constructor-name
|
||||
(class-name class)))))
|
||||
(copy-message (message)
|
||||
(let ((new-message (create-message-of-same-type message)))
|
||||
(merge-from message new-message)
|
||||
new-message))
|
||||
(concatenate-repeated-field (from-field to-field field-container field-type field-kind)
|
||||
(if (eq field-container :vector)
|
||||
(let ((new-vector (make-array `(,(+ (length from-field)
|
||||
(length to-field)))
|
||||
:element-type field-type
|
||||
:adjustable t
|
||||
:fill-pointer (+ (length from-field)
|
||||
(length to-field)))))
|
||||
(loop for i from 0
|
||||
for el across to-field
|
||||
do
|
||||
(setf (aref new-vector i) el))
|
||||
(loop for i from (length to-field)
|
||||
for el across from-field
|
||||
do
|
||||
(setf (aref new-vector i)
|
||||
(if (member field-kind '(:message :group))
|
||||
(copy-message el)
|
||||
el)))
|
||||
new-vector)
|
||||
(append to-field (mapcar (if (member field-kind '(:message :group))
|
||||
#'copy-message
|
||||
#'identity)
|
||||
from-field)))))
|
||||
|
||||
(let* ((class (type-of from-message))
|
||||
(desc (find-message-descriptor class)))
|
||||
;; Check the messages are the same.
|
||||
(and desc (eq (type-of to-message) class)
|
||||
|
||||
(loop :for field-desc :in (proto-fields desc)
|
||||
:for field-name = (proto-external-field-name field-desc)
|
||||
:for from-field-value = (proto-slot-value from-message field-name)
|
||||
:when (has-field from-message field-name)
|
||||
:do
|
||||
(cond
|
||||
((eq (proto-label field-desc) :repeated)
|
||||
(setf (proto-slot-value to-message field-name)
|
||||
(concatenate-repeated-field from-field-value
|
||||
(proto-slot-value to-message field-name)
|
||||
(proto-container field-desc)
|
||||
(proto-type field-desc)
|
||||
(proto-kind field-desc))))
|
||||
((member (proto-kind field-desc) '(:message :group))
|
||||
(if (has-field to-message field-name)
|
||||
(merge-from from-field-value
|
||||
(proto-slot-value to-message field-name))
|
||||
(setf (proto-slot-value to-message field-name)
|
||||
(copy-message from-field-value))))
|
||||
|
||||
((eq (proto-kind field-desc) :map)
|
||||
(loop with map-descriptor = (find-map-descriptor (proto-class field-desc))
|
||||
with to-hash-map = (proto-slot-value to-message field-name)
|
||||
for key being the hash-keys of from-field-value
|
||||
using (hash-value from-value)
|
||||
do
|
||||
(setf (gethash key to-hash-map)
|
||||
(if (eq (proto-value-kind map-descriptor) :message)
|
||||
(copy-message from-value)
|
||||
from-value))))
|
||||
|
||||
(t (setf (proto-slot-value to-message field-name)
|
||||
from-field-value))))))))
|
||||
714
examples/meshtastic/lisp/cl-protobufs/model-classes.lisp
Normal file
714
examples/meshtastic/lisp/cl-protobufs/model-classes.lisp
Normal file
|
|
@ -0,0 +1,714 @@
|
|||
;;; Copyright 2012-2020 Google LLC
|
||||
;;;
|
||||
;;; Use of this source code is governed by an MIT-style
|
||||
;;; license that can be found in the LICENSE file or at
|
||||
;;; https://opensource.org/licenses/MIT.
|
||||
|
||||
(in-package #:cl-protobufs.implementation)
|
||||
|
||||
;;; Classes to represent the objects in a .proto file.
|
||||
|
||||
|
||||
(defvar *file-descriptors* (make-hash-table :test #'equal)
|
||||
"A global table mapping names to file-descriptor objects.")
|
||||
|
||||
(defun find-file-descriptor (name)
|
||||
"Find a file-descriptor for the given name. Returns nil if not found.
|
||||
Parameters:
|
||||
NAME: A string, symbol, or pathname."
|
||||
(values (gethash name *file-descriptors*)))
|
||||
|
||||
(defun add-file-descriptor (pathname symbol)
|
||||
"Register the file-descriptor named by SYMBOL under the key PATHNAME.
|
||||
Intended for use by protoc-gen-cl-pb."
|
||||
(setf (gethash pathname *file-descriptors*) (find-file-descriptor symbol)))
|
||||
|
||||
(defstruct message
|
||||
"All protobuf message objects extend this type. Note that some fields that
|
||||
logically belong here, such as %%bool-values, are conditionally added to the
|
||||
generated message defstructs in the `define-message' macro, to avoid using
|
||||
memory for their slots when they're not needed."
|
||||
;; %%skipped-bytes will contain all of the bytes we couldn't
|
||||
;; identify when we tried to deserialize a proto but will
|
||||
;; add to the serialized bytes for the proto if we serialize it.
|
||||
;; See https://developers.google.com/protocol-buffers/docs/overview#updating
|
||||
(%%skipped-bytes nil :type (or null byte-vector)))
|
||||
|
||||
|
||||
;;; Descriptor classes -- These classes taken together represent the contents of a .proto file.
|
||||
|
||||
(defclass abstract-descriptor () ()
|
||||
(:documentation
|
||||
"Base class of all protobuf descriptor classes, which describe the contents of .proto files."))
|
||||
|
||||
|
||||
;; It would be nice if most of the slots had only reader functions, but
|
||||
;; that makes writing the protobuf parser a good deal more complicated.
|
||||
(defclass descriptor (abstract-descriptor)
|
||||
;; The Lisp name for the type of this object.
|
||||
((class :type symbol
|
||||
:accessor proto-class
|
||||
:initarg :class
|
||||
:initform nil)
|
||||
;; The (unqualified) protobuf name for this enum, message, etc
|
||||
(name :type (or null string)
|
||||
:reader proto-name
|
||||
:initarg :name
|
||||
:initform nil)
|
||||
;; The fully qualified name, e.g., "proto2.MessageSet"
|
||||
(qual-name :type string
|
||||
:accessor proto-qualified-name
|
||||
:initarg :qualified-name
|
||||
:initform "")
|
||||
(options :type (list-of option-descriptor)
|
||||
:accessor proto-options
|
||||
:initarg :options
|
||||
:initform ()))
|
||||
(:documentation
|
||||
"Shared attributes for protobuf message descriptors."))
|
||||
|
||||
(defun find-qualified-name (name protos
|
||||
&key (proto-key #'proto-name) (full-key #'proto-qualified-name)
|
||||
relative-to)
|
||||
"Find something by its string name, first doing a simple name match,
|
||||
and, if that fails, exhaustively searching qualified names."
|
||||
(declare (ignore relative-to))
|
||||
(or (find name protos :key proto-key :test #'string=)
|
||||
;;--- This needs more sophisticated search, e.g., relative to current namespace
|
||||
(find name protos :key full-key :test #'string=)))
|
||||
|
||||
|
||||
(defclass file-descriptor (descriptor)
|
||||
((syntax :type (member :proto2 :proto3 :editions)
|
||||
:accessor proto-syntax
|
||||
:initarg :syntax)
|
||||
(edition :type (or null string)
|
||||
:accessor proto-edition
|
||||
:initarg :edition)
|
||||
(package :type (or null string)
|
||||
:accessor proto-package-name
|
||||
:initarg :package
|
||||
:initform nil)
|
||||
(imports :type (list-of string) ; the names of schemas to be imported
|
||||
:accessor proto-imports
|
||||
:initarg :imports
|
||||
:initform ()))
|
||||
(:documentation
|
||||
"Model class to describe a protobuf file, sometimes referred to as a schema."))
|
||||
|
||||
(defmethod make-load-form ((file-desc file-descriptor) &optional environment)
|
||||
(with-slots (class) file-desc
|
||||
(multiple-value-bind (constructor initializer)
|
||||
(make-load-form-saving-slots file-desc :environment environment)
|
||||
(values `(or (gethash ',class *file-descriptors*) ,constructor)
|
||||
`(unless (gethash ',class *file-descriptors*)
|
||||
(record-file-descriptor ,file-desc :symbol ',class)
|
||||
,initializer)))))
|
||||
|
||||
(defun record-file-descriptor (descriptor &key symbol)
|
||||
"Record DESCRIPTOR in the global schema hash table under the key SYMBOL.
|
||||
The generated code also stores the schema in this hash table using the
|
||||
file pathname as the key."
|
||||
(declare (type file-descriptor descriptor))
|
||||
(let ((symbol (or symbol (proto-class descriptor))))
|
||||
(setf (gethash symbol *file-descriptors*) descriptor)))
|
||||
|
||||
(defmethod print-object ((file-desc file-descriptor) stream)
|
||||
(if *print-escape*
|
||||
(print-unreadable-object (file-desc stream :type t :identity t)
|
||||
(format stream "~@[~S~]~@[ (package ~A)~]"
|
||||
(proto-class file-desc)
|
||||
(proto-package-name file-desc)))
|
||||
(format stream "~S" (proto-class file-desc))))
|
||||
|
||||
;; find-* functions for finding different proto meta-objects
|
||||
|
||||
(defvar *messages* (make-hash-table :test 'eq)
|
||||
"Map from the protobuf message name symbol to the message-descriptor instance. If there is an
|
||||
'extends' instance this will be the last (largest) defined extended version of the
|
||||
message-descriptor.")
|
||||
|
||||
(defvar *qualified-messages* (make-hash-table :test 'equal)
|
||||
"Map from the proto-qualified-name of a message (a string) to its Lisp type symbol.")
|
||||
|
||||
(defun-inline find-message-descriptor (type &key error-p)
|
||||
"Return the message-descriptor named by TYPE (a symbol), or nil. If ERROR-P
|
||||
is true then signal protobuf-error instead of returning nil."
|
||||
(or (gethash type *messages*)
|
||||
(when error-p
|
||||
(protobuf-error "~S does not name a protobuf message type" type))))
|
||||
|
||||
(defun-inline find-message-by-qualified-name (qualified-name &key error-p)
|
||||
"Return the protobuf message symbol named by QUALIFIED-NAME, or nil. For
|
||||
definition of QUALIFIED-NAME see qual-name slot on message-descriptor.
|
||||
If ERROR-P is true then signal protobuf-error instead of returning nil."
|
||||
(or (gethash qualified-name *qualified-messages*)
|
||||
(when error-p
|
||||
(protobuf-error "~S does not name a protobuf message type" qualified-name))))
|
||||
|
||||
(defstruct (map-descriptor (:conc-name proto-))
|
||||
"Describes a protobuf map."
|
||||
;; The Lisp type of the key.
|
||||
(key-type nil)
|
||||
;; The Lisp type of the value.
|
||||
(value-type nil)
|
||||
(value-kind nil :type (member :scalar :message :enum)))
|
||||
|
||||
;; Delete these compatibility shims on next major release.
|
||||
(defun-inline map-key-type (desc) (proto-key-type desc))
|
||||
(defun-inline map-value-type (desc) (proto-value-type desc))
|
||||
(defun-inline map-value-kind (desc) (proto-value-kind desc))
|
||||
|
||||
(defmethod make-load-form ((m map-descriptor) &optional environment)
|
||||
(make-load-form-saving-slots m :environment environment))
|
||||
|
||||
(defvar *map-descriptors* (make-hash-table :test 'eq)
|
||||
"Maps map names (symbols) to map-descriptor instances.")
|
||||
|
||||
(defun-inline find-map-descriptor (type)
|
||||
"Return a map-descriptor instance named by TYPE (a symbol)."
|
||||
(gethash type *map-descriptors*))
|
||||
|
||||
(defvar *enum-descriptors* (make-hash-table :test 'eq)
|
||||
"Maps enum names (symbols) to enum-descriptor instances.")
|
||||
|
||||
(defun-inline find-enum-descriptor (type)
|
||||
"Return a enum-descriptor instance named by TYPE (a symbol)."
|
||||
(gethash type *enum-descriptors*))
|
||||
|
||||
|
||||
;; We accept and store any option, but only act on a few: default, packed,
|
||||
;; optimize_for, lisp_name, lisp_alias
|
||||
(defclass option-descriptor (abstract-descriptor)
|
||||
;; The name of the option, for example "lisp_name".
|
||||
((name :type string
|
||||
:reader proto-name
|
||||
:initarg :name)
|
||||
;; The (untyped) value
|
||||
(value :accessor proto-value
|
||||
:initarg :value
|
||||
:initform nil)
|
||||
;; Optional Lisp type, one of string, integer, float, symbol (for now).
|
||||
(type :type (or null symbol)
|
||||
:reader proto-type
|
||||
:initarg :type
|
||||
:initform 'string))
|
||||
(:documentation
|
||||
"Model class to describe a protobuf option, i.e., a key/value pair."))
|
||||
|
||||
(defmethod make-load-form ((o option-descriptor) &optional environment)
|
||||
(make-load-form-saving-slots o :environment environment))
|
||||
|
||||
(defmethod print-object ((o option-descriptor) stream)
|
||||
(if *print-escape*
|
||||
(print-unreadable-object (o stream :type t :identity t)
|
||||
(format stream "~A~@[ = ~S~]" (proto-name o) (proto-value o)))
|
||||
(format stream "~A" (proto-name o))))
|
||||
|
||||
(defun make-option (name value &optional (type 'string))
|
||||
(check-type name string)
|
||||
(make-instance 'option-descriptor
|
||||
:name name :value value :type type))
|
||||
|
||||
(defun find-option (desc name)
|
||||
"Given a protobuf descriptor DESC and the NAME of an option, returns the
|
||||
value of the option and its Lisp type, otherwise NIL."
|
||||
(declare (type descriptor desc) (type string name))
|
||||
(let ((option (find name (proto-options desc) :key #'proto-name :test #'option-name=)))
|
||||
(when option
|
||||
(values (proto-value option) (proto-type option)))))
|
||||
|
||||
(defgeneric remove-options (descriptor &rest names)
|
||||
(:documentation
|
||||
"Given a protobuf descriptor (schema, message, enum, etc) and a set of option names,
|
||||
remove all of those options from the set of options in the descriptor."))
|
||||
|
||||
(defmethod remove-options ((desc descriptor) &rest names)
|
||||
(dolist (name names (proto-options desc))
|
||||
(let ((option (find name (proto-options desc) :key #'proto-name :test #'option-name=)))
|
||||
(when option
|
||||
(setf (proto-options desc) (remove option (proto-options desc)))))))
|
||||
|
||||
(defmethod remove-options ((options list) &rest names)
|
||||
(dolist (name names options)
|
||||
(let ((option (find name options :key #'proto-name :test #'option-name=)))
|
||||
(when option
|
||||
;; This does not side-effect the list of options
|
||||
(setq options (remove option options))))))
|
||||
|
||||
(defun option-name= (name1 name2)
|
||||
(let* ((name1 (string name1))
|
||||
(name2 (string name2))
|
||||
(start1 (if (eql (char name1 0) #\() 1 0))
|
||||
(start2 (if (eql (char name2 0) #\() 1 0))
|
||||
(end1 (if (eql (char name1 0) #\() (- (length name1) 1) (length name1)))
|
||||
(end2 (if (eql (char name2 0) #\() (- (length name2) 1) (length name2))))
|
||||
(string= name1 name2 :start1 start1 :end1 end1 :start2 start2 :end2 end2)))
|
||||
|
||||
(defstruct enum-descriptor
|
||||
"Describes a protobuf enum."
|
||||
;; The symbol naming the Lisp type for this enum.
|
||||
(class nil :type symbol)
|
||||
;; The string naming the protobuf type for this enum.
|
||||
(name nil :type string)
|
||||
;; The name and integer value of each enum element.
|
||||
(values nil :type (list-of enum-value-descriptor)))
|
||||
|
||||
(defmethod make-load-form ((e enum-descriptor) &optional environment)
|
||||
(make-load-form-saving-slots e :environment environment))
|
||||
|
||||
(defstruct enum-value-descriptor
|
||||
"The model class that represents a protobuf enum key/value pair."
|
||||
;; The keyword symbol corresponding to the enum value key.
|
||||
;; Note that the API uses "keyword-to-int" and "int-to-keyword".
|
||||
;; Let's make this match that at some point.
|
||||
(name nil :type keyword)
|
||||
(value nil :type sfixed32))
|
||||
|
||||
(defmethod make-load-form ((desc enum-value-descriptor) &optional environment)
|
||||
(make-load-form-saving-slots desc :environment environment))
|
||||
|
||||
(defun enum-keywords (enum-type)
|
||||
"Returns all keywords that belong to the given ENUM-TYPE."
|
||||
(let ((expansion (type-expand enum-type)))
|
||||
(check-type expansion (cons (eql member) list))
|
||||
(rest expansion)))
|
||||
|
||||
;; An object describing a Protobufs message. Confusingly most local variables that hold
|
||||
;; instances of this struct are named MESSAGE, but the C API makes it clear that
|
||||
;; a Message is not its descriptor.
|
||||
;; This would have been far less confusing if it sounded more obviously like a 'descriptor'
|
||||
;; and not the contents of the message per se.
|
||||
(defclass message-descriptor (descriptor)
|
||||
(
|
||||
;; Use this if you want to make this message descriptor an alias for an existing Lisp type.
|
||||
(alias :type (or null symbol)
|
||||
:accessor proto-alias-for
|
||||
:initarg :alias-for
|
||||
:initform nil)
|
||||
;; All fields for this message, including local ones and extended ones.
|
||||
;; This does NOT include fields that are inside of a oneof. These field descriptors can
|
||||
;; be accessed via the FIELDS slot in each oneof-descriptor stored in the ONEOFS slot.
|
||||
(fields :type (list-of field-descriptor)
|
||||
:accessor proto-fields
|
||||
:initarg :fields
|
||||
:initform ())
|
||||
;; A list of all oneof descriptors defined in this message.
|
||||
(oneofs :type (list-of oneof-descriptor)
|
||||
:accessor proto-oneofs
|
||||
:initarg :oneofs
|
||||
:initform ())
|
||||
;; The FIELDS slot (more or less) as a vector. If the index space is dense,
|
||||
;; the vector is accessed by field index, otherwise it requires linear scan.
|
||||
;; TODO(dougk): sparse indices can do better than linear scan.
|
||||
(field-vect :type vector
|
||||
:accessor proto-field-vect)
|
||||
;; The extended fields defined in this message.
|
||||
(extended-fields :type (list-of field-descriptor)
|
||||
:accessor proto-extended-fields
|
||||
:initform ())
|
||||
(extensions :type (list-of extension-descriptor)
|
||||
:accessor proto-extensions
|
||||
:initarg :extensions
|
||||
:initform ())
|
||||
;; :message is an ordinary message
|
||||
;; :extends is an 'extends' to an existing message
|
||||
(message-type :type (member :message :extends)
|
||||
:accessor proto-message-type
|
||||
:initarg :message-type
|
||||
:initform :message))
|
||||
(:documentation
|
||||
"Describes a protobuf message."))
|
||||
|
||||
(defmethod make-load-form ((msg-desc message-descriptor) &optional environment)
|
||||
(with-slots (class message-type alias) msg-desc
|
||||
(multiple-value-bind (constructor initializer)
|
||||
(make-load-form-saving-slots msg-desc :environment environment)
|
||||
(values (if (eq message-type :extends)
|
||||
constructor
|
||||
`(let ((msg-desc ,constructor))
|
||||
(record-protobuf-object ',message-type msg-desc :message)
|
||||
msg-desc))
|
||||
initializer))))
|
||||
|
||||
(defmethod print-object ((msg-desc message-descriptor) stream)
|
||||
(if *print-escape*
|
||||
(print-unreadable-object (msg-desc stream :type t :identity t)
|
||||
(format stream "~S~@[ (alias for ~S)~]~@[ (group~*)~]~@[ (extended~*)~]"
|
||||
(proto-class msg-desc)
|
||||
(and (slot-boundp msg-desc 'alias)
|
||||
(proto-alias-for msg-desc))
|
||||
(and (slot-boundp msg-desc 'message-type)
|
||||
(eq (proto-message-type msg-desc) :group))
|
||||
(and (slot-boundp msg-desc 'message-type)
|
||||
(eq (proto-message-type msg-desc) :extends))))
|
||||
(format stream "~S" (proto-class msg-desc))))
|
||||
|
||||
;; Extensions protocol
|
||||
(defgeneric get-extension (object slot)
|
||||
(:documentation
|
||||
"Returns the value of the extended slot SLOT in OBJECT."))
|
||||
|
||||
(defgeneric set-extension (object slot value)
|
||||
(:documentation
|
||||
"Sets the value of the extended slot SLOT to VALUE in OBJECT."))
|
||||
|
||||
(defgeneric has-extension (object slot)
|
||||
(:documentation
|
||||
"Returns true iff there is an extended slot named SLOT in OBJECT.")
|
||||
;; It's an error to call {get,set,clear}-extension on a non-extendable object.
|
||||
(:method ((object standard-object) slot)
|
||||
(declare (ignore slot))
|
||||
nil))
|
||||
|
||||
(defgeneric clear-extension (object slot)
|
||||
(:documentation
|
||||
"Clears the value of the extended slot SLOT from OBJECT."))
|
||||
|
||||
(defconstant $empty-default 'empty-default
|
||||
"The marker used in 'proto-default' used to indicate that there is no default value.")
|
||||
|
||||
;; Describes a field within a message.
|
||||
;;--- Support the 'deprecated' option (have serialization ignore such fields?)
|
||||
(defclass field-descriptor (descriptor)
|
||||
;; :group means this is a message-typed field but it should be serialized as
|
||||
;; a group. What does nil mean here? Needs a comment.
|
||||
((kind :type (member :message :group :extends :enum :map :scalar nil)
|
||||
:accessor proto-kind
|
||||
:initarg :kind)
|
||||
(type :type (or null symbol)
|
||||
:accessor proto-type
|
||||
:initarg :type)
|
||||
(label :type (member :required :optional :repeated)
|
||||
:accessor proto-label
|
||||
:initarg :label)
|
||||
;; TODO(cgay): rename to field-number and proto-field-number. Why be coy?
|
||||
(index :type field-number
|
||||
:accessor proto-index
|
||||
:initarg :index)
|
||||
;; Offset into the is-set bit vector. nil for members of a oneof.
|
||||
(field-offset :type (or null field-number)
|
||||
:accessor proto-field-offset
|
||||
:initarg :field-offset)
|
||||
;; If this field is contained in a oneof, this holds the order of this field
|
||||
;; as it was defined in the oneof. This slot is nil if and only if the field
|
||||
;; is not part of a oneof.
|
||||
(oneof-offset :type (or null field-number)
|
||||
:accessor proto-oneof-offset
|
||||
:initarg :oneof-offset
|
||||
:initform nil)
|
||||
;; The name of the slot holding the field value.
|
||||
;; TODO(cgay): there's no deep reason we must have internal and external field names. It's a
|
||||
;; historical artifact that can probably be removed once the QPX protobuf code has been updated.
|
||||
(internal-field-name :type (or null symbol)
|
||||
:accessor proto-internal-field-name
|
||||
:initarg :internal-field-name
|
||||
:initform nil)
|
||||
(external-field-name
|
||||
:type (or null symbol) ; The Lisp slot holding the value within an object
|
||||
:accessor proto-external-field-name ; this also serves as the Lisp field name
|
||||
:initarg :external-field-name
|
||||
:initform nil)
|
||||
(json-name ; The key to use when printing this field to JSON.
|
||||
:type string ; This is pulled directly from protoc output.
|
||||
:accessor proto-json-name
|
||||
:initarg :json-name)
|
||||
(default :accessor proto-default ; Default value (untyped), pulled out of the options
|
||||
:initarg :default
|
||||
:initform $empty-default)
|
||||
(packed :type boolean ; Packed, pulled out of the options
|
||||
:accessor proto-packed
|
||||
:initarg :packed
|
||||
:initform nil)
|
||||
(container :accessor proto-container ; If the field is repeated, this specifies the
|
||||
:type (member nil :vector :list) ; container type. If not, this field is nil.
|
||||
:initarg :container
|
||||
:initform nil)
|
||||
(lazy :type boolean ; Lazy, pulled out of the options
|
||||
:accessor proto-lazy-p
|
||||
:initarg :lazy
|
||||
:initform nil)
|
||||
(bool-index :type (or null integer) ; For non-repeated boolean fields only, the
|
||||
:accessor proto-bool-index ; index into the bit-vector of boolean field values.
|
||||
:initarg :bool-index
|
||||
:initform nil))
|
||||
(:documentation
|
||||
"The model class that represents one field within a Protobufs message."))
|
||||
|
||||
(defmethod initialize-instance :after ((field field-descriptor) &rest initargs)
|
||||
(declare (ignore initargs))
|
||||
(unless (and (plusp (proto-index field))
|
||||
(not (<= 19000 (proto-index field) 19999)))
|
||||
(protobuf-error
|
||||
"Protobuf field indexes must be positive and not between 19000 and 19999 (inclusive)")))
|
||||
|
||||
(defmethod make-load-form ((f field-descriptor) &optional environment)
|
||||
(make-load-form-saving-slots f :environment environment))
|
||||
|
||||
(defmethod print-object ((f field-descriptor) stream)
|
||||
(if *print-escape*
|
||||
(print-unreadable-object (f stream :type t :identity t)
|
||||
(format stream "~S :: ~S = ~D~@[ (group~*)~]~@[ (extended~*)~]"
|
||||
(proto-internal-field-name f)
|
||||
(proto-class f)
|
||||
(proto-index f)
|
||||
(eq (proto-kind f) :group)
|
||||
(eq (proto-kind f) :extends)))
|
||||
(format stream "~S" (proto-internal-field-name f))))
|
||||
|
||||
(defmethod proto-slot ((field field-descriptor))
|
||||
(proto-internal-field-name field))
|
||||
|
||||
(defmethod (setf proto-slot) (slot (field field-descriptor))
|
||||
(setf (proto-value field) slot))
|
||||
|
||||
(defclass extension-descriptor (abstract-descriptor)
|
||||
;; The start of the extension range.
|
||||
((from :type field-number
|
||||
:accessor proto-extension-from
|
||||
:initarg :from)
|
||||
;; The end of the extension range, inclusive.
|
||||
(to :type field-number
|
||||
:accessor proto-extension-to
|
||||
:initarg :to))
|
||||
(:documentation
|
||||
"The model class that represents an extension range within a protobuf message."))
|
||||
|
||||
;;; TODO(cgay): this is unused. Were there plans for it?
|
||||
(defvar *extension-descriptors* nil "Extension descriptors.")
|
||||
|
||||
(defmethod make-load-form ((e extension-descriptor) &optional environment)
|
||||
(declare (ignore environment))
|
||||
(let ((from (and (slot-boundp e 'from) (proto-extension-from e)))
|
||||
(to (and (slot-boundp e 'to) (proto-extension-to e))))
|
||||
`(or (cdr (assoc '(,from . ,to) *extension-descriptors* :test #'equal))
|
||||
(let ((obj (make-instance 'extension-descriptor
|
||||
,@(and from `(:from ,from))
|
||||
,@(and to `(:to ,to)))))
|
||||
(push (cons '(,from . ,to) obj) *extension-descriptors*)
|
||||
obj))))
|
||||
|
||||
(defmethod print-object ((e extension-descriptor) stream)
|
||||
(print-unreadable-object (e stream :type t :identity t)
|
||||
(format stream "~D - ~D"
|
||||
(proto-extension-from e) (proto-extension-to e))))
|
||||
|
||||
(defvar *service-descriptors* (make-hash-table)
|
||||
"Maps service names (symbols) to service-descriptor instances.")
|
||||
|
||||
(defun find-service-descriptor (name)
|
||||
"Return a service-descriptor instance named by NAME (a symbol)."
|
||||
(gethash name *service-descriptors*))
|
||||
|
||||
(defclass service-descriptor (descriptor)
|
||||
((methods :type (list-of method-descriptor)
|
||||
:accessor proto-methods
|
||||
:initarg :methods
|
||||
:initform ())
|
||||
;; The pathname of the protobuf the service is defined in.
|
||||
(location :type (or null pathname)
|
||||
:accessor proto-source-location
|
||||
:initarg :source-location
|
||||
:initform nil))
|
||||
(:documentation "Model class to describe a protobuf service."))
|
||||
|
||||
(defmethod make-load-form ((s service-descriptor) &optional environment)
|
||||
(make-load-form-saving-slots s :environment environment))
|
||||
|
||||
(defmethod print-object ((s service-descriptor) stream)
|
||||
(if *print-escape*
|
||||
(print-unreadable-object (s stream :type t :identity t)
|
||||
(format stream "~S" (proto-name s)))
|
||||
(format stream "~S" (proto-name s))))
|
||||
|
||||
(defgeneric find-method-descriptor (service name)
|
||||
(:documentation
|
||||
"Given a protobuf service-descriptor and a method name,
|
||||
returns the protobuf method having that name."))
|
||||
|
||||
(defun record-protobuf-object (symbol descriptor type)
|
||||
"Record the protobuf-metaobject DESCRIPTOR named by SYMBOL in the
|
||||
hash-table indicated by TYPE. Also sets the default constructor on the symbol
|
||||
if we are not in SBCL."
|
||||
;; No need to record an extension, it's already been recorded
|
||||
(ecase type
|
||||
(:enum (setf (gethash symbol *enum-descriptors*) descriptor))
|
||||
(:message
|
||||
(setf (gethash symbol *messages*) descriptor)
|
||||
#-sbcl
|
||||
(setf (get symbol :default-constructor)
|
||||
(intern (nstring-upcase (format nil "%MAKE-~A" symbol))
|
||||
(symbol-package symbol)))
|
||||
(when (and (slot-boundp descriptor 'qual-name) (proto-qualified-name descriptor))
|
||||
(setf (gethash (proto-qualified-name descriptor) *qualified-messages*)
|
||||
(proto-class descriptor))))
|
||||
(:map (setf (gethash symbol *map-descriptors*) descriptor))
|
||||
(:service (setf (gethash symbol *service-descriptors*) descriptor))))
|
||||
|
||||
(defmethod find-method-descriptor ((service service-descriptor) (name symbol))
|
||||
(find name (proto-methods service) :key #'proto-class))
|
||||
|
||||
(defmethod find-method-descriptor ((service service-descriptor) (name string))
|
||||
(find-qualified-name name (proto-methods service)))
|
||||
|
||||
(defmethod find-method-descriptor ((service service-descriptor) (index integer))
|
||||
(find index (proto-methods service) :key #'proto-index))
|
||||
|
||||
|
||||
(defclass method-descriptor (descriptor)
|
||||
;; Name of the Stubby service for which this is a method.
|
||||
((service-name :type string
|
||||
:accessor proto-service-name
|
||||
:initarg :service-name)
|
||||
(client-fn :type symbol
|
||||
:accessor proto-client-stub
|
||||
:initarg :client-stub)
|
||||
(server-fn :type symbol
|
||||
:accessor proto-server-stub
|
||||
:initarg :server-stub)
|
||||
;; TODO(jgodbout): Fix internally and delete.
|
||||
(old-server-fn :type symbol
|
||||
:accessor proto-old-server-stub
|
||||
:initarg :old-server-stub)
|
||||
;; Lisp name of the input parameter, which must be a message or extension.
|
||||
(itype :type symbol
|
||||
:accessor proto-input-type
|
||||
:initarg :input-type)
|
||||
;; Protobuf name of the input parameter. (Fully qualified?)
|
||||
(iname :type (or null string)
|
||||
:accessor proto-input-name
|
||||
:initarg :input-name
|
||||
:initform nil)
|
||||
(istreaming :type boolean ; For stubby4-style streaming.
|
||||
:accessor proto-input-streaming-p
|
||||
:initarg :input-streaming
|
||||
:initform nil)
|
||||
;; Lisp name of the output parameter, which must be a message or extension.
|
||||
(otype :type symbol
|
||||
:accessor proto-output-type
|
||||
:initarg :output-type)
|
||||
;; Protobuf name of the output parameter. (Fully qualified?)
|
||||
(oname :type (or null string)
|
||||
:accessor proto-output-name
|
||||
:initarg :output-name
|
||||
:initform nil)
|
||||
(ostreaming :type boolean ; For stubby4-style streaming.
|
||||
:accessor proto-output-streaming-p
|
||||
:initarg :output-streaming
|
||||
:initform nil)
|
||||
(stype :type (or symbol null) ; The Lisp type name of
|
||||
:accessor proto-streams-type ; the "streams" type.
|
||||
:initarg :streams-type
|
||||
:initform nil)
|
||||
(sname :type (or null string) ; The Protobufs name of the
|
||||
:accessor proto-streams-name ; "streams" type.
|
||||
:initarg :streams-name
|
||||
:initform nil)
|
||||
(index :type (unsigned-byte 32) ; An identifying index for this method.
|
||||
:accessor proto-index ; (used by the RPC implementation)
|
||||
:initarg :index))
|
||||
(:documentation
|
||||
"Model class to describe one method in a protobuf service."))
|
||||
|
||||
(defmethod make-load-form ((m method-descriptor) &optional environment)
|
||||
(make-load-form-saving-slots m :environment environment))
|
||||
|
||||
(defmethod print-object ((m method-descriptor) stream)
|
||||
(if *print-escape*
|
||||
(print-unreadable-object (m stream :type t :identity t)
|
||||
(format stream "~S (~S) => (~S)"
|
||||
(proto-class m)
|
||||
(and (slot-boundp m 'itype) (proto-input-type m))
|
||||
(and (slot-boundp m 'otype) (proto-output-type m))))
|
||||
(format stream "~S" (proto-class m))))
|
||||
|
||||
(defstruct oneof
|
||||
"Stores data for a oneof slot."
|
||||
;; Value of the currently set field in the oneof. Only the one (untyped) slot
|
||||
;; is needed to store the oneof's current value.
|
||||
(value nil)
|
||||
;; Indicates which field is set in the oneof. If nil, then nothing is set in
|
||||
;; the oneof. If a number, say N, then the N-th field in the oneof is set.
|
||||
(set-field nil :type (or null (unsigned-byte 32))))
|
||||
|
||||
(defstruct oneof-descriptor
|
||||
"Describes a oneof"
|
||||
;; Indicates whether the oneof is synthetic. A synthetic oneof is a oneof
|
||||
;; created by protoc in order to create has-* functions for proto3 optional
|
||||
;; fields. Special accessors (the clear, has, and case functions) are not
|
||||
;; created for synthetic oneofs.
|
||||
(synthetic-p nil :type boolean)
|
||||
;; One field-descriptor for each field in the one-of, in order.
|
||||
(fields nil :type simple-vector)
|
||||
;; A symbol naming the oneof field.
|
||||
(external-name nil :type symbol)
|
||||
;; The external name, but with '%' prepended.
|
||||
(internal-name nil :type symbol))
|
||||
|
||||
(defmethod make-load-form ((o oneof-descriptor) &optional environment)
|
||||
(make-load-form-saving-slots o :environment environment))
|
||||
|
||||
(defun %find-field-descriptor (desc internal-field-name)
|
||||
"Like find-field-descriptor, but looks in DESC for INTERNAL-FIELD-NAME
|
||||
instead of the external field name."
|
||||
(or (find internal-field-name (proto-fields desc)
|
||||
:key #'proto-internal-field-name)
|
||||
(loop for oneof in (proto-oneofs desc)
|
||||
thereis (find internal-field-name (oneof-descriptor-fields oneof)
|
||||
:key #'proto-internal-field-name))))
|
||||
|
||||
;;; TODO(cgay): looks like relative-to is for searching relative to a current
|
||||
;;; namespace and isn't implemented yet.
|
||||
(defgeneric find-field-descriptor (desc id &optional relative-to)
|
||||
(:documentation
|
||||
"Given a message-descriptor DESC and a field ID, returns the
|
||||
field-descriptor having that ID. ID may be the symbol naming the
|
||||
field, the field name (string), or the field number."))
|
||||
|
||||
(defmethod find-field-descriptor ((desc message-descriptor) (name symbol)
|
||||
&optional relative-to)
|
||||
(declare (ignore relative-to))
|
||||
(or (find name (proto-fields desc) :key #'proto-external-field-name)
|
||||
(loop for oneof in (proto-oneofs desc)
|
||||
thereis (find name (oneof-descriptor-fields oneof)
|
||||
:key #'proto-external-field-name))))
|
||||
|
||||
(defmethod find-field-descriptor ((desc message-descriptor) (name string)
|
||||
&optional relative-to)
|
||||
(or (find-qualified-name name (proto-fields desc)
|
||||
:relative-to (or relative-to desc))
|
||||
(loop for oneof in (proto-oneofs desc)
|
||||
thereis (find-qualified-name name (oneof-descriptor-fields oneof)
|
||||
:relative-to (or relative-to desc)))))
|
||||
|
||||
(defmethod find-field-descriptor ((desc message-descriptor) (index integer)
|
||||
&optional relative-to)
|
||||
(declare (ignore relative-to))
|
||||
(or (find index (proto-fields desc) :key #'proto-index)
|
||||
(loop for oneof in (proto-oneofs desc)
|
||||
thereis (find index (oneof-descriptor-fields oneof)
|
||||
:key #'proto-index))))
|
||||
|
||||
(defgeneric set-method-do-not-deserialize-input (method)
|
||||
(:documentation
|
||||
"Sets a service METHOD to indicate that its input should not be deserialized prior to calling its
|
||||
server function.")
|
||||
(:method ((method method-descriptor))
|
||||
(setf (proto-input-type method) nil)))
|
||||
|
||||
(defgeneric make-qualified-name (parent-desc name)
|
||||
(:documentation
|
||||
"Given a parent file-descriptor or message-descriptor and a name,
|
||||
generate a fully qualified name string for the name."))
|
||||
|
||||
(defmethod make-qualified-name ((parent-desc file-descriptor) name)
|
||||
"Make a qualified name for NAME by prepending the package name from PARENT-DESC and a '.'."
|
||||
(let* ((parent-name (proto-package-name parent-desc)))
|
||||
(if parent-name
|
||||
(strcat parent-name "." name)
|
||||
name)))
|
||||
|
||||
(defmethod make-qualified-name ((parent-desc message-descriptor) name)
|
||||
"Make a qualified name for NAME by prepending the message name from PARENT-DESC and a '.'."
|
||||
(let* ((parent-qual-name (proto-qualified-name parent-desc)))
|
||||
(strcat parent-qual-name "." name)))
|
||||
303
examples/meshtastic/lisp/cl-protobufs/parser.lisp
Normal file
303
examples/meshtastic/lisp/cl-protobufs/parser.lisp
Normal file
|
|
@ -0,0 +1,303 @@
|
|||
;;; Copyright 2012-2020 Google LLC
|
||||
;;;
|
||||
;;; Use of this source code is governed by an MIT-style
|
||||
;;; license that can be found in the LICENSE file or at
|
||||
;;; https://opensource.org/licenses/MIT.
|
||||
|
||||
(in-package #:cl-protobufs.implementation)
|
||||
|
||||
;;; Text parsing utilities
|
||||
|
||||
(defun-inline proto-whitespace-char-p (ch)
|
||||
(declare #.*optimize-fast-unsafe*)
|
||||
(and ch (member ch '(#\space #\tab #\return #\newline))))
|
||||
|
||||
(defun-inline proto-hash-char-p (ch)
|
||||
(declare #.*optimize-fast-unsafe*)
|
||||
(and ch (eq ch #\#)))
|
||||
|
||||
(defun-inline proto-eol-char-p (ch)
|
||||
(declare #.*optimize-fast-unsafe*)
|
||||
(and ch (member ch '(#\return #\newline))))
|
||||
|
||||
(defun-inline proto-token-char-p (ch)
|
||||
(declare #.*optimize-fast-unsafe*)
|
||||
(and ch (or (alpha-char-p ch)
|
||||
(digit-char-p ch)
|
||||
(member ch '(#\_ #\.)))))
|
||||
|
||||
(defun skip-whitespace-comments-and-chars (stream &key chars)
|
||||
"Skip all whitespace characters, text-format comments and elements of CHARS
|
||||
are coming up in the STREAM."
|
||||
(loop for ch = (peek-char nil stream nil)
|
||||
until (or (null ch)
|
||||
(and (not (proto-whitespace-char-p ch))
|
||||
(not (proto-hash-char-p ch))
|
||||
(not (if (listp chars)
|
||||
(member ch chars)
|
||||
(eql ch chars)))))
|
||||
do
|
||||
(if (proto-hash-char-p ch)
|
||||
(read-line stream nil)
|
||||
(read-char stream nil))))
|
||||
|
||||
(defun skip-whitespace (stream)
|
||||
"Skip all the whitespace characters that are coming up in the stream."
|
||||
(loop for ch = (peek-char nil stream nil)
|
||||
until (or (null ch) (not (proto-whitespace-char-p ch)))
|
||||
do
|
||||
(read-char stream nil)))
|
||||
|
||||
(defun expect-matching-end (stream start-char)
|
||||
"Expect that the starting block element START-CHAR matches the next element
|
||||
in the STREAM which should end the block, signal an error if there's no match.
|
||||
The return value is the character that was eaten."
|
||||
(let ((end-char (peek-char nil stream nil)))
|
||||
(unless (or (and (eq start-char #\{)
|
||||
(eq end-char #\}))
|
||||
(and (eq start-char #\<)
|
||||
(eq end-char #\>)))
|
||||
(protobuf-error "Started with ~S ended with ~S at position ~D"
|
||||
start-char end-char (file-position stream))))
|
||||
(read-char stream))
|
||||
|
||||
(defun expect-char (stream char &optional chars within)
|
||||
"Expect to see 'char' as the next character in the stream; signal an error if it's not there.
|
||||
Then skip all of the following whitespace.
|
||||
The return value is the character that was eaten."
|
||||
(let (ch)
|
||||
(if (if (listp char)
|
||||
(member (peek-char nil stream nil) char)
|
||||
(eql (peek-char nil stream nil) char))
|
||||
(setq ch (read-char stream))
|
||||
(protobuf-error "No ~S found~@[ within '~A'~] at position ~D"
|
||||
char within (file-position stream)))
|
||||
(maybe-skip-chars stream chars)
|
||||
ch))
|
||||
|
||||
(defun expect-token-or-string (stream string)
|
||||
"Expect to see STRING as the next string in STREAM, as parsed by PARSE-TOKEN-OR-STRING.
|
||||
Signal an error if not present, and return the parsed string."
|
||||
(let ((str (parse-token-or-string stream)))
|
||||
(skip-whitespace stream)
|
||||
(if (string= str string)
|
||||
str
|
||||
(error "No ~S found at position ~D" string (file-position stream)))))
|
||||
|
||||
(defun maybe-skip-chars (stream chars)
|
||||
"Skip some optional characters in the stream,
|
||||
then skip all of the following whitespace."
|
||||
(skip-whitespace-comments-and-chars stream)
|
||||
(when chars
|
||||
(loop
|
||||
(let ((ch (peek-char nil stream nil)))
|
||||
(when (or (null ch) (not (member ch chars)))
|
||||
(skip-whitespace-comments-and-chars stream)
|
||||
(return-from maybe-skip-chars)))
|
||||
(read-char stream))))
|
||||
|
||||
|
||||
;;--- Collect the comment so we can attach it to its associated object
|
||||
(defun maybe-skip-comments (stream)
|
||||
"If what appears next in the stream is a comment, skip it and any following comments,
|
||||
then skip any following whitespace."
|
||||
(loop
|
||||
(let ((ch (peek-char nil stream nil)))
|
||||
(unless (eql ch #\/)
|
||||
(return-from maybe-skip-comments))
|
||||
(read-char stream)
|
||||
(case (peek-char nil stream nil)
|
||||
((#\/)
|
||||
(skip-line-comment stream))
|
||||
((#\*)
|
||||
(skip-block-comment stream))
|
||||
((nil)
|
||||
(skip-whitespace stream)
|
||||
(return-from maybe-skip-comments))
|
||||
(otherwise
|
||||
(protobuf-error "Found '/' at position ~D to start a comment, but no following '/' or '*'"
|
||||
(file-position stream)))))))
|
||||
|
||||
(defun skip-line-comment (stream)
|
||||
"Skip to the end of a line comment, that is, to the end of the line.
|
||||
Then skip any following whitespace."
|
||||
(loop for ch = (read-char stream nil)
|
||||
until (or (null ch) (proto-eol-char-p ch)))
|
||||
(skip-whitespace stream))
|
||||
|
||||
(defun skip-block-comment (stream)
|
||||
"Skip to the end of a block comment, that is, until a '*/' is seen.
|
||||
Then skip any following whitespace."
|
||||
(loop for ch = (read-char stream nil)
|
||||
do (cond ((null ch)
|
||||
(protobuf-error "Premature end of file while skipping block comment"))
|
||||
((and (eql ch #\*)
|
||||
(eql (peek-char nil stream nil) #\/))
|
||||
(read-char stream nil)
|
||||
(return))))
|
||||
(skip-whitespace stream))
|
||||
|
||||
|
||||
(defun parse-token (stream &optional additional-chars)
|
||||
"Parse the next token in the stream, then skip following whitespace/comments.
|
||||
The returned value is the token."
|
||||
(maybe-skip-comments stream)
|
||||
(when (let ((ch (peek-char nil stream nil)))
|
||||
(or (proto-token-char-p ch) (member ch additional-chars)))
|
||||
(loop for ch = (read-char stream nil)
|
||||
for ch1 = (peek-char nil stream nil)
|
||||
collect ch into token
|
||||
until (or (null ch1)
|
||||
(and (not (proto-token-char-p ch1))
|
||||
(not (member ch1 additional-chars))))
|
||||
finally (progn
|
||||
(skip-whitespace stream)
|
||||
(maybe-skip-comments stream)
|
||||
(return (coerce token 'string))))))
|
||||
|
||||
(defun parse-parenthesized-token (stream)
|
||||
"Parse the next token in the stream, then skip the following whitespace.
|
||||
The token might be surrounded by parentheses.
|
||||
The returned value is the token."
|
||||
(let ((left (peek-char nil stream nil)))
|
||||
(when (eql left #\()
|
||||
(read-char stream))
|
||||
(when (proto-token-char-p (peek-char nil stream nil))
|
||||
(loop for ch = (read-char stream nil)
|
||||
for ch1 = (peek-char nil stream nil)
|
||||
collect ch into token
|
||||
until (or (null ch1) (not (proto-token-char-p ch1)))
|
||||
finally (progn
|
||||
(skip-whitespace stream)
|
||||
(when (eql left #\()
|
||||
(expect-char stream #\)))
|
||||
(return (coerce token 'string)))))))
|
||||
|
||||
(defun parse-token-or-string (stream)
|
||||
(if (eql (peek-char nil stream nil) #\")
|
||||
(values (parse-string stream) 'string)
|
||||
(values (parse-token stream) 'symbol)))
|
||||
|
||||
(defun parse-string (stream)
|
||||
"Parse the next quoted string in the stream, then skip the following whitespace.
|
||||
The returned value is the string, without the quotation marks."
|
||||
(let ((ch0 (read-char stream nil)))
|
||||
(unless (member ch0 '(#\' #\"))
|
||||
(protobuf-error "Starting string character ~c should be \' or \"." ch0))
|
||||
(loop for ch = (read-char stream nil)
|
||||
until (or (null ch) (char= ch ch0))
|
||||
when (eql ch #\\)
|
||||
do (setq ch (unescape-char stream))
|
||||
collect ch into string
|
||||
finally (progn
|
||||
(skip-whitespace-comments-and-chars stream)
|
||||
(if (eql (peek-char nil stream nil) ch0)
|
||||
;; If the next character is a quote character, that means
|
||||
;; we should go parse another string and concatenate it
|
||||
(return (strcat (coerce string 'string) (parse-string stream)))
|
||||
(return (coerce string 'string)))))))
|
||||
|
||||
(defun unescape-char (stream)
|
||||
"Parse the next \"escaped\" character from the stream."
|
||||
(let ((ch (read-char stream nil)))
|
||||
(assert (not (null ch)) ()
|
||||
"End of stream reached while reading escaped character")
|
||||
(case ch
|
||||
((#\x)
|
||||
;; Two hex digits
|
||||
(let* ((d1 (digit-char-p (read-char stream) 16))
|
||||
(d2 (digit-char-p (read-char stream) 16)))
|
||||
(code-char (+ (* d1 16) d2))))
|
||||
((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
|
||||
(if (not (digit-char-p (peek-char nil stream nil)))
|
||||
#\null
|
||||
;; Three octal digits
|
||||
(let* ((d1 (digit-char-p ch 8))
|
||||
(d2 (digit-char-p (read-char stream) 8))
|
||||
(d3 (digit-char-p (read-char stream) 8)))
|
||||
(code-char (+ (* d1 64) (* d2 8) d3)))))
|
||||
((#\t) #\tab)
|
||||
((#\n) #\newline)
|
||||
((#\r) #\return)
|
||||
((#\f) #\page)
|
||||
((#\b) #\backspace)
|
||||
((#\a) #\bell)
|
||||
((#\e) #\esc)
|
||||
(otherwise ch))))
|
||||
|
||||
(defun escape-char (ch)
|
||||
"The inverse of 'unescape-char', for printing."
|
||||
(if (and (standard-char-p ch) (graphic-char-p ch))
|
||||
ch
|
||||
(case ch
|
||||
((#\null) "\\0")
|
||||
((#\tab) "\\t")
|
||||
((#\newline) "\\n")
|
||||
((#\return) "\\r")
|
||||
((#\page) "\\f")
|
||||
((#\backspace) "\\b")
|
||||
((#\bell) "\\a")
|
||||
((#\esc) "\\e")
|
||||
(otherwise
|
||||
(format nil "\\x~2,'0X" (char-code ch))))))
|
||||
|
||||
(defun parse-signed-int (stream)
|
||||
"Parse the next token in the stream as an integer, then skip the following whitespace.
|
||||
The returned value is the integer."
|
||||
(let* ((sign (if (eql (peek-char nil stream nil) #\-)
|
||||
(progn (read-char stream) -1)
|
||||
1))
|
||||
(int (parse-unsigned-int stream)))
|
||||
(* int sign)))
|
||||
|
||||
(defun parse-unsigned-int (stream)
|
||||
"Parse the next token in the stream as an integer, then skip the following whitespace.
|
||||
The returned value is the integer."
|
||||
(when (digit-char-p (peek-char nil stream nil))
|
||||
(loop for ch = (read-char stream nil)
|
||||
for ch1 = (peek-char nil stream nil)
|
||||
collect ch into token
|
||||
until (or (null ch1) (and (not (digit-char-p ch1)) (not (eql ch #\x))))
|
||||
finally (progn
|
||||
(skip-whitespace stream)
|
||||
(let ((token (coerce token 'string)))
|
||||
(if (starts-with token "0x")
|
||||
(let ((*read-base* 16))
|
||||
(return (parse-integer (subseq token 2))))
|
||||
(return (parse-integer token))))))))
|
||||
|
||||
(defun parse-float (stream)
|
||||
"Parse the next token in the STREAM as a float, then skip the following whitespace.
|
||||
The returned value is the float."
|
||||
(let ((number (parse-number stream)))
|
||||
(when number
|
||||
(coerce number 'float))))
|
||||
|
||||
(defun parse-double (stream &key append-d0)
|
||||
"Parse the next token in the STREAM as a double, then skip the following whitespace.
|
||||
If APPEND-D0 is true, then append 'd0' to the parsed number before attempting to convert
|
||||
to a double. This is necessary in order to parse doubles from the stream which do not
|
||||
already have the 'd0' suffix. The returned value is the double-float."
|
||||
(let ((number (parse-number stream :append-d0 append-d0)))
|
||||
(when number
|
||||
(coerce number 'double-float))))
|
||||
|
||||
(defun parse-number (stream &key append-d0)
|
||||
"Parse a number from STREAM. If APPEND-D0 is true, append \"d0\"
|
||||
to the end of the parsed numerical string."
|
||||
(when (let ((ch (peek-char nil stream nil)))
|
||||
(or (digit-char-p ch) (member ch '(#\- #\+ #\.))))
|
||||
(let ((token (parse-token stream '(#\- #\+ #\.))))
|
||||
(when token
|
||||
(skip-whitespace-comments-and-chars stream)
|
||||
(if append-d0
|
||||
(parse-numeric-string (concatenate 'string token "d0"))
|
||||
(parse-numeric-string token))))))
|
||||
|
||||
(defun parse-numeric-string (string)
|
||||
(cond ((starts-with string "0x")
|
||||
(parse-integer (subseq string 2) :radix 16))
|
||||
((starts-with string "-0x")
|
||||
(- (parse-integer (subseq string 3) :radix 16)))
|
||||
(t
|
||||
(read-from-string string))))
|
||||
187
examples/meshtastic/lisp/cl-protobufs/pkgdcl.lisp
Normal file
187
examples/meshtastic/lisp/cl-protobufs/pkgdcl.lisp
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
;;; Copyright 2012-2020 Google LLC
|
||||
;;;
|
||||
;;; Use of this source code is governed by an MIT-style
|
||||
;;; license that can be found in the LICENSE file or at
|
||||
;;; https://opensource.org/licenses/MIT.
|
||||
|
||||
(in-package "CL-USER")
|
||||
|
||||
(defpackage #:cl-protobufs
|
||||
(:use)
|
||||
(:export
|
||||
;; Base type for all message instances.
|
||||
#:message
|
||||
|
||||
;; Message field types and related definitions.
|
||||
#:int32
|
||||
#:int64
|
||||
#:uint32
|
||||
#:uint64
|
||||
#:sint32
|
||||
#:sint64
|
||||
#:fixed32
|
||||
#:fixed64
|
||||
#:sfixed32
|
||||
#:sfixed64
|
||||
#:list-of
|
||||
#:vector-of
|
||||
#:byte-vector
|
||||
#:make-byte-vector
|
||||
|
||||
;; Enumerations
|
||||
#:enum-keywords
|
||||
#:enum-int-to-keyword
|
||||
#:enum-keyword-to-int
|
||||
|
||||
;; Serialization to/from various formats
|
||||
|
||||
;; Binary format
|
||||
#:serialize-to-stream
|
||||
#:serialize-to-bytes
|
||||
#:deserialize-from-stream
|
||||
#:deserialize-from-bytes
|
||||
#:make-message-with-bytes
|
||||
#:set-method-do-not-deserialize-input
|
||||
|
||||
;; JSON
|
||||
#:parse-json
|
||||
#:print-json
|
||||
|
||||
;; Text format - not well specified, prefer json or binary
|
||||
#:parse-text-format
|
||||
#:print-text-format
|
||||
#:fmt
|
||||
|
||||
;; Descriptors -- descriptors contain all the information parsed from .proto
|
||||
;; files and may be looked up by the symbol naming a protobuf message, enum,
|
||||
;; etc. For most use cases it's not necessary to deal with descriptors
|
||||
;; directly; just access the protos through the generated code APIs and a
|
||||
;; few other generic APIs above. The descriptor APIs are mostly intended for
|
||||
;; writing code that deals with arbitrary protos when the types aren't known
|
||||
;; in advance.
|
||||
|
||||
#:abstract-descriptor
|
||||
#:descriptor
|
||||
#:enum-descriptor
|
||||
#:enum-value-descriptor
|
||||
#:extension-descriptor
|
||||
#:field-descriptor
|
||||
#:file-descriptor
|
||||
#:map-descriptor
|
||||
#:message-descriptor
|
||||
#:method-descriptor
|
||||
#:option-descriptor
|
||||
#:service-descriptor
|
||||
|
||||
;; Descriptor lookup
|
||||
#:find-enum-descriptor
|
||||
#:find-field-descriptor
|
||||
#:find-file-descriptor
|
||||
#:find-map-descriptor
|
||||
#:find-message-descriptor
|
||||
#:find-method-descriptor
|
||||
#:find-service-descriptor
|
||||
|
||||
;; descriptor accessors
|
||||
#:enum-descriptor-class
|
||||
#:enum-descriptor-name
|
||||
#:enum-descriptor-values
|
||||
|
||||
;; The map-* versions are deprecated, to be removed in release 4.0.
|
||||
#:proto-key-type #:map-key-type
|
||||
#:proto-value-kind #:map-value-kind
|
||||
#:proto-value-type #:map-value-type
|
||||
|
||||
#:oneof-descriptor-fields
|
||||
#:oneof-descriptor-name
|
||||
#:oneof-descriptor-synthetic-p
|
||||
#:proto-class
|
||||
#:proto-client-stub
|
||||
#:proto-container
|
||||
#:proto-default
|
||||
#:proto-edition
|
||||
#:proto-external-field-name
|
||||
#:proto-fields
|
||||
#:proto-imports
|
||||
#:proto-index
|
||||
#:proto-input-name
|
||||
#:proto-input-streaming-p
|
||||
#:proto-input-type
|
||||
#:proto-internal-field-name
|
||||
#:proto-kind
|
||||
#:proto-label
|
||||
#:proto-methods
|
||||
#:proto-name
|
||||
#:proto-oneofs
|
||||
#:proto-options
|
||||
#:proto-output-name
|
||||
#:proto-output-streaming-p
|
||||
#:proto-output-type
|
||||
#:proto-package-name
|
||||
#:proto-qualified-name
|
||||
#:proto-server-stub
|
||||
#:proto-old-server-stub
|
||||
#:proto-service-name
|
||||
#:proto-source-location
|
||||
#:proto-streams-name
|
||||
#:proto-streams-type
|
||||
#:proto-type
|
||||
#:proto-value
|
||||
|
||||
#:find-option ; finds an option, not a descriptor
|
||||
|
||||
;; Conditions
|
||||
#:protobuf-error
|
||||
#:unknown-type
|
||||
#:unknown-field-type
|
||||
|
||||
;; Extensions
|
||||
#:get-extension
|
||||
#:set-extension
|
||||
#:has-extension
|
||||
#:clear-extension
|
||||
|
||||
;; The Python "compatibility" API
|
||||
#:is-initialized
|
||||
#:proto-equal
|
||||
#:clear
|
||||
#:has-field
|
||||
#:proto-slot-value
|
||||
#:encoded-field
|
||||
#:merge-from
|
||||
|
||||
;; For RPC stubs
|
||||
;; An RPC library supporting the client functions defined in
|
||||
;; `define-service` should bind these.
|
||||
#:*rpc-call-function*
|
||||
#:*rpc-streaming-client-function*))
|
||||
|
||||
(defpackage #:cl-protobufs.implementation
|
||||
(:use :common-lisp :cl-protobufs)
|
||||
(:import-from :alexandria #:define-constant)
|
||||
(:export
|
||||
;; Exported for use by generated code. These shouldn't be called directly.
|
||||
#:define-schema
|
||||
#:define-enum
|
||||
#:define-map
|
||||
#:define-oneof
|
||||
#:define-message
|
||||
#:define-extend
|
||||
#:define-extension
|
||||
#:define-service
|
||||
|
||||
#:add-file-descriptor
|
||||
|
||||
;; TODO(cgay): These should be removed or moved to the interface package, as
|
||||
;; appropriate.
|
||||
#:encode-double
|
||||
#:encode-string
|
||||
#:encode-uint32
|
||||
#:make-deserializer
|
||||
#:make-serializer
|
||||
#:make-tag
|
||||
|
||||
#:serialize-scalar
|
||||
|
||||
;; For ASDF
|
||||
#:validate-imports))
|
||||
1471
examples/meshtastic/lisp/cl-protobufs/serialize.lisp
Normal file
1471
examples/meshtastic/lisp/cl-protobufs/serialize.lisp
Normal file
File diff suppressed because it is too large
Load diff
379
examples/meshtastic/lisp/cl-protobufs/text-format.lisp
Normal file
379
examples/meshtastic/lisp/cl-protobufs/text-format.lisp
Normal file
|
|
@ -0,0 +1,379 @@
|
|||
;;; Copyright 2012-2020 Google LLC
|
||||
;;;
|
||||
;;; Use of this source code is governed by an MIT-style
|
||||
;;; license that can be found in the LICENSE file or at
|
||||
;;; https://opensource.org/licenses/MIT.
|
||||
|
||||
(in-package #:cl-protobufs.implementation)
|
||||
|
||||
;;; This file implements the protobuf Text Format parser and printer.
|
||||
;;; The exported symbols are parse-text-format and print-text-format.
|
||||
|
||||
(defun print-text-format (object &key
|
||||
(stream *standard-output*)
|
||||
(pretty-print-p t))
|
||||
"Prints a protocol buffer message to a stream.
|
||||
Parameters:
|
||||
OBJECT: The protocol buffer message to print.
|
||||
STREAM: The stream to print to.
|
||||
PRETTY-PRINT-P: When true, generate line breaks and other human readable output
|
||||
in the text format. When false, replace line breaks with spaces."
|
||||
(print-text-format-impl object :stream stream
|
||||
:pretty-print-p pretty-print-p))
|
||||
|
||||
(defun print-text-format-impl (object &key
|
||||
(indent 0)
|
||||
(stream *standard-output*)
|
||||
(pretty-print-p t))
|
||||
"Prints a protocol buffer message to a stream.
|
||||
Parameters:
|
||||
OBJECT: The protocol buffer message to print.
|
||||
INDENT: Indent the output by INDENT spaces. Only used for pretty-printing.
|
||||
STREAM: The stream to print to.
|
||||
PRETTY-PRINT-P: When true, generate line breaks and other human readable output
|
||||
in the text format. When false, replace line breaks with spaces."
|
||||
(let* ((type (type-of object))
|
||||
(message (find-message-descriptor type :error-p t)))
|
||||
(dolist (field (proto-fields message))
|
||||
(when (if (eq (slot-value field 'kind) :extends)
|
||||
(has-extension object (slot-value field 'external-field-name))
|
||||
(has-field object (slot-value field 'external-field-name)))
|
||||
(let* ((value
|
||||
(if (eq (slot-value field 'kind) :extends)
|
||||
(get-extension object (slot-value field 'external-field-name))
|
||||
(proto-slot-value object (slot-value field 'external-field-name)))))
|
||||
(if (eq (proto-label field) :repeated)
|
||||
(doseq (val value)
|
||||
(print-field val
|
||||
(proto-class field)
|
||||
(proto-name field)
|
||||
:indent indent
|
||||
:stream stream
|
||||
:pretty-print-p pretty-print-p))
|
||||
(print-field value
|
||||
(proto-class field)
|
||||
(proto-name field)
|
||||
:indent indent
|
||||
:stream stream
|
||||
:pretty-print-p pretty-print-p)))))
|
||||
(dolist (oneof (proto-oneofs message))
|
||||
(let* ((oneof-data (slot-value object (oneof-descriptor-internal-name oneof)))
|
||||
(set-field (oneof-set-field oneof-data)))
|
||||
(when set-field
|
||||
(let ((field-desc (aref (oneof-descriptor-fields oneof) set-field)))
|
||||
(print-field (oneof-value oneof-data)
|
||||
(proto-class field-desc)
|
||||
(proto-name field-desc)
|
||||
:indent indent
|
||||
:stream stream
|
||||
:pretty-print-p pretty-print-p)))))
|
||||
nil))
|
||||
|
||||
(defun print-field (value type name
|
||||
&key (indent 0) (stream *standard-output*) (pretty-print-p t))
|
||||
"Print the text format of a single field which is not repeated.
|
||||
Parameters:
|
||||
VALUE: The value in the field to print.
|
||||
TYPE: The protobuf type to print. This is obtained from
|
||||
the PROTO-CLASS slot in the field-descriptor.
|
||||
NAME: The name of the field. This is printed before the value.
|
||||
INDENT: If supplied, indent the text by INDENT spaces.
|
||||
STREAM: The stream to output to.
|
||||
PRINT-NAME: Whether or not to print the name of the field.
|
||||
PRETTY-PRINT-P: When true, print newlines and indentation."
|
||||
;; If VALUE is NIL and the type is not boolean, there is nothing to do.
|
||||
(unless (or value (eq type 'boolean) (eq type 'symbol))
|
||||
(return-from print-field nil))
|
||||
(let (desc)
|
||||
(cond
|
||||
((scalarp type)
|
||||
(print-scalar value type name stream
|
||||
(and pretty-print-p indent)))
|
||||
((typep (setq desc (or (find-message-descriptor type)
|
||||
(find-enum-descriptor type)
|
||||
(find-map-descriptor type)))
|
||||
'message-descriptor)
|
||||
(print-message-brace t name pretty-print-p indent stream)
|
||||
(print-text-format-impl value :indent (+ indent 2)
|
||||
:stream stream
|
||||
:pretty-print-p pretty-print-p)
|
||||
(print-message-brace nil name pretty-print-p indent stream))
|
||||
((typep desc 'enum-descriptor)
|
||||
(print-enum value desc name stream (and pretty-print-p indent)))
|
||||
((typep desc 'map-descriptor)
|
||||
(loop for k being the hash-keys of value using (hash-value v)
|
||||
do (if pretty-print-p
|
||||
(format stream "~&~V,0T~A { " indent name)
|
||||
(format stream "~A { " name))
|
||||
(print-scalar k (proto-key-type desc) "key" stream nil)
|
||||
(print-field v (proto-value-type desc) "value"
|
||||
:stream stream
|
||||
:pretty-print-p nil)
|
||||
(format stream "}")
|
||||
(when pretty-print-p
|
||||
(format stream "~%"))))
|
||||
;; This case only happens when the user specifies a custom type and
|
||||
;; doesn't support it above.
|
||||
(t
|
||||
(error 'unknown-type
|
||||
:format-control "unknown type ~S, while printing non-repeated field ~S"
|
||||
:format-arguments (list type name))))))
|
||||
|
||||
(defun print-scalar (val type name stream indent)
|
||||
"Print scalar value to stream
|
||||
Parameters:
|
||||
VAL: The data for the value to print.
|
||||
TYPE: The type of val.
|
||||
NAME: The name to print before the value. If nil, then no
|
||||
name will be printed.
|
||||
STREAM: The stream to print to.
|
||||
INDENT: Either a number or nil.
|
||||
- If indent is a number, indent this print
|
||||
by (+ indent 2) and write a newline at
|
||||
the end.
|
||||
- If indent is nil, then do not indent and
|
||||
do not write a newline."
|
||||
(when (or val (eq type 'boolean) (eq type 'symbol))
|
||||
(when indent
|
||||
(format stream "~&~V,0T" indent))
|
||||
(when name
|
||||
(format stream "~A: " name))
|
||||
(ecase type
|
||||
((int32 uint32 int64 uint64 sint32 sint64 fixed32 sfixed32 fixed64 sfixed64)
|
||||
(format stream "~D" val))
|
||||
((string)
|
||||
;; TODO(cgay): This should be the inverse of parse-string.
|
||||
(format stream "\"~A\"" val))
|
||||
((byte-vector)
|
||||
(format stream "~S" val))
|
||||
((boolean)
|
||||
(format stream "~A" (if val "true" "false")))
|
||||
((float double-float)
|
||||
(format stream "~D" val))
|
||||
;; A few of our homegrown types
|
||||
((symbol)
|
||||
(format stream "\"~A\"" (lisp-symbol-string val)))
|
||||
((date time datetime timestamp)
|
||||
(format stream "~D" val)))
|
||||
(if indent
|
||||
(format stream "~%")
|
||||
(format stream " "))))
|
||||
|
||||
(defun print-enum (val enum name stream indent)
|
||||
"Print enum to stream
|
||||
|
||||
Parameters:
|
||||
VAL: The enum value.
|
||||
ENUM: The enum descriptor.
|
||||
NAME: The name to print before the value. If NIL, no name will be printed.
|
||||
STREAM: The stream to print to.
|
||||
INDENT: Either a number or nil.
|
||||
- If indent is a number, indent this print
|
||||
by (+ indent 2) and write a newline at
|
||||
the end.
|
||||
- If indent is nil, then do not indent and
|
||||
do not write a newline."
|
||||
(when val
|
||||
(when indent
|
||||
(format stream "~&~V,0T" indent))
|
||||
(when name
|
||||
(format stream "~A: " name))
|
||||
(let* ((e (find (keywordify val)
|
||||
(enum-descriptor-values enum)
|
||||
:key #'enum-value-descriptor-name))
|
||||
(value (and e (enum-value-descriptor-name e)))
|
||||
(proto-keyword-value (substitute #\_ #\- (string value))))
|
||||
(format stream "~A" proto-keyword-value)
|
||||
(if indent
|
||||
(format stream "~%")
|
||||
(format stream " ")))))
|
||||
|
||||
(defun print-message-brace (opening-p name pretty-print-p indent stream)
|
||||
"Print either the opening NAME { or closing }.
|
||||
|
||||
Parameters:
|
||||
OPENING-P: Is this an opening or closing brace.
|
||||
NAME: The name to print before the value. If NIL, no name will be printed.
|
||||
PRETTY-PRINT-P: When true, print newlines and indentation.
|
||||
INDENT: A set indentation to print to. Used only for pretty-print.
|
||||
STREAM: The stream to print to."
|
||||
(if opening-p
|
||||
(if pretty-print-p
|
||||
(format stream "~&~V,0T~A {~%" indent name)
|
||||
(format stream "~A { " name))
|
||||
(if pretty-print-p
|
||||
(format stream "~&~V,0T}~%" indent)
|
||||
(format stream "} "))))
|
||||
|
||||
;;; Parse objects that were serialized using the text format
|
||||
|
||||
(defun parse-text-format (type &key (stream *standard-input*))
|
||||
"Parses an object in stream STREAM of type TYPE written in text format."
|
||||
(declare (type symbol type)
|
||||
(type stream stream))
|
||||
(let ((message (find-message-descriptor type :error-p t)))
|
||||
(parse-text-format-impl message :stream stream)))
|
||||
|
||||
;;; TODO(cgay): replace all assertions here with something that signals a
|
||||
;;; subtype of protobuf-error and shows current stream position.
|
||||
|
||||
(defun parse-text-format-impl
|
||||
(msg-desc &key (stream *standard-input*))
|
||||
"Parse a protobuf message with descriptor MSG-DESC from STREAM. This method
|
||||
returns the parsed object."
|
||||
(declare (type message-descriptor msg-desc))
|
||||
(let ((object (funcall (get-constructor-name
|
||||
(or (proto-alias-for msg-desc)
|
||||
(proto-class msg-desc)))))
|
||||
;; Repeated slot names, tracks which slots need to be nreversed.
|
||||
(rslots ()))
|
||||
(loop
|
||||
(skip-whitespace-comments-and-chars stream)
|
||||
(when (or (not (peek-char nil stream nil))
|
||||
(eql (peek-char nil stream nil) #\})
|
||||
(eql (peek-char nil stream nil) #\>))
|
||||
;; We should respect the order of slots as
|
||||
;; they were in the message.
|
||||
(dolist (slot rslots)
|
||||
(setf (proto-slot-value object slot)
|
||||
(nreverse (proto-slot-value object slot))))
|
||||
(return-from parse-text-format-impl object))
|
||||
(let* ((name (parse-token stream))
|
||||
(field (and name (find-field-descriptor msg-desc name)))
|
||||
(type (and field (proto-class field)))
|
||||
(slot (and field (proto-external-field-name field)))
|
||||
(repeated-p (and field (eql :repeated (proto-label field)))))
|
||||
(if (null field)
|
||||
(error 'unknown-field
|
||||
:format-control "unknown field ~S, while parsing message of type ~A"
|
||||
:format-arguments (list name msg-desc))
|
||||
(multiple-value-bind (val error-p)
|
||||
(parse-field type :stream stream :repeated-p repeated-p)
|
||||
(cond
|
||||
(error-p
|
||||
(unknown-field-type type field msg-desc))
|
||||
(repeated-p
|
||||
;; If slot is NIL, then this field doesn't exist in the message
|
||||
;; so we skip it.
|
||||
(when slot
|
||||
(pushnew slot rslots)
|
||||
;; Brief note on val: VAL should be a list.
|
||||
;; In the case of repeated symbol slot, we may have
|
||||
;; symbol: nil
|
||||
;; in which case we want the symbol nil, which happens to
|
||||
;; also be a list... since for a repeated field foo
|
||||
;; foo: # no value defined for foo
|
||||
;; is invalid, we aren't going to have collisions.
|
||||
(if (and (listp val) val)
|
||||
(dolist (el val)
|
||||
(push el (proto-slot-value object slot)))
|
||||
(push val (proto-slot-value object slot)))))
|
||||
((eq (proto-kind field) :map)
|
||||
(dolist (pair val)
|
||||
(setf (gethash (car pair) (proto-slot-value object slot))
|
||||
(cdr pair))))
|
||||
(t
|
||||
(when slot
|
||||
(setf (proto-slot-value object slot) val))))))))))
|
||||
|
||||
(defun parse-field (type &key (stream *standard-input*) repeated-p)
|
||||
"Parse data of type TYPE from STREAM. This function returns
|
||||
the object parsed. We need to know if hte field is REPEATED-P.
|
||||
If the parsing fails, the function will
|
||||
return T as a second value."
|
||||
(let ((desc (or (find-message-descriptor type)
|
||||
(find-enum-descriptor type)
|
||||
(find-map-descriptor type))))
|
||||
(flet ((parse-message ()
|
||||
(skip-whitespace-comments-and-chars stream)
|
||||
(let ((start-char (expect-char stream '(#\{ #\<))))
|
||||
(prog1
|
||||
(parse-text-format-impl (find-message-descriptor type) :stream stream)
|
||||
(skip-whitespace-comments-and-chars stream)
|
||||
(expect-matching-end stream start-char))))
|
||||
(parse-scalar ()
|
||||
(case type
|
||||
((float) (parse-float stream))
|
||||
((double-float) (parse-double stream))
|
||||
((string) (parse-string stream))
|
||||
((symbol) (make-lisp-symbol (parse-string stream) t))
|
||||
((boolean) (let ((token (parse-token stream)))
|
||||
(cond ((string= token "true") t)
|
||||
((string= token "false") nil)
|
||||
;; Parsing failed, so return T as
|
||||
;; a second value to indicate a
|
||||
;; failure.
|
||||
(t (values nil t)))))
|
||||
(otherwise (parse-signed-int stream))))
|
||||
(parse (parse-function)
|
||||
(when (eql (peek-char nil stream nil) #\:)
|
||||
(read-char stream))
|
||||
(skip-whitespace-comments-and-chars stream)
|
||||
(if (and repeated-p
|
||||
(eq (peek-char nil stream nil) #\[))
|
||||
(progn
|
||||
(read-char stream)
|
||||
(skip-whitespace-comments-and-chars stream :chars #\,)
|
||||
(let ((element-list (loop until (eq (peek-char nil stream nil) #\])
|
||||
collect (funcall parse-function)
|
||||
do
|
||||
(skip-whitespace-comments-and-chars stream :chars #\,))))
|
||||
(read-char stream)
|
||||
element-list))
|
||||
(funcall parse-function))))
|
||||
|
||||
(cond ((scalarp type)
|
||||
(parse #'parse-scalar))
|
||||
((typep desc 'message-descriptor)
|
||||
(parse #'parse-message))
|
||||
((typep desc 'enum-descriptor)
|
||||
(expect-char stream #\:)
|
||||
(let* ((name (parse-token stream))
|
||||
(enum (find (keywordify name) (enum-descriptor-values desc)
|
||||
:key #'enum-value-descriptor-name)))
|
||||
(and enum (enum-value-descriptor-name enum))))
|
||||
((typep desc 'map-descriptor)
|
||||
(let ((key-type (proto-key-type desc))
|
||||
(val-type (proto-value-type desc)))
|
||||
(flet ((parse-map-entry (key-type val-type stream)
|
||||
(let (key val)
|
||||
(expect-char stream #\{)
|
||||
(assert (string= "key" (parse-token stream)))
|
||||
(setf key (parse-field key-type :stream stream))
|
||||
(skip-whitespace-comments-and-chars stream)
|
||||
(assert (string= "value" (parse-token stream)))
|
||||
(setf val (parse-field val-type :stream stream))
|
||||
(skip-whitespace-comments-and-chars stream)
|
||||
(expect-char stream #\})
|
||||
(cons key val))))
|
||||
(case (peek-char nil stream nil)
|
||||
((#\:)
|
||||
(expect-char stream #\:)
|
||||
(expect-char stream #\[)
|
||||
(loop
|
||||
with pairs = ()
|
||||
do (skip-whitespace-comments-and-chars stream)
|
||||
(push (parse-map-entry key-type val-type stream)
|
||||
pairs)
|
||||
(if (eql (peek-char nil stream nil) #\,)
|
||||
(read-char stream)
|
||||
(progn
|
||||
(skip-whitespace-comments-and-chars stream)
|
||||
(expect-char stream #\])
|
||||
(return pairs)))))
|
||||
(t
|
||||
(skip-whitespace-comments-and-chars stream)
|
||||
(list (parse-map-entry key-type val-type stream)))))))
|
||||
;; Parsing failed, return t as a second vlaue to indicate failure.
|
||||
(t (values nil t))))))
|
||||
|
||||
(defun fmt (stream proto colon-p at-sign-p &optional width &rest other-args)
|
||||
"Format command for protobufs
|
||||
~/cl-protobufs:fmt/ emits a non-pretty-printed protobuf of PROTO to STREAM.
|
||||
~@/cl-protobufs:fmt/ emits a pretty-printed protobuf of PROTO to STREAM.
|
||||
COLON-P and AT-SIGN-P are the usual for format directives.
|
||||
WIDTH and OTHER-ARGS is ignored."
|
||||
(declare (ignore width))
|
||||
(cond (other-args (error "FORMAT directive ~~/cl-protobufs:fmt/ takes only one argument."))
|
||||
(colon-p (error "FORMAT directive ~~/cl-protobufs:fmt/ does not take colons."))
|
||||
(t (print-text-format proto :stream stream :pretty-print-p at-sign-p))))
|
||||
642
examples/meshtastic/lisp/cl-protobufs/utilities.lisp
Normal file
642
examples/meshtastic/lisp/cl-protobufs/utilities.lisp
Normal file
|
|
@ -0,0 +1,642 @@
|
|||
;;; Copyright 2012-2020 Google LLC
|
||||
;;;
|
||||
;;; Use of this source code is governed by an MIT-style
|
||||
;;; license that can be found in the LICENSE file or at
|
||||
;;; https://opensource.org/licenses/MIT.
|
||||
|
||||
(in-package #:cl-protobufs.implementation)
|
||||
|
||||
|
||||
|
||||
;;; Optimized fixnum arithmetic
|
||||
|
||||
;;; By default we optimize select portions of cl-protobufs code that need to be
|
||||
;;; very fast by using *optimize-fast-unsafe*. Serialization is the primary
|
||||
;;; example. Use (PUSHNEW :DBG *FEATURES*) to turn this off during development.
|
||||
;;; Doing so has exposed bugs in the past.
|
||||
|
||||
(eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
|
||||
(defparameter *optimize-fast-unsafe*
|
||||
#+dbg '(optimize (speed 1) (safety 3) (debug 3))
|
||||
#-dbg '(optimize (speed 3) (safety 0) (debug 0))
|
||||
"Compiler optimization settings for fast, unsafe, hard-to-debug code.")
|
||||
|
||||
) ; eval-when
|
||||
|
||||
|
||||
(defmacro defun-inline (name arglist &body body)
|
||||
"Define an inline function with NAME, ARGLIST, and BODY."
|
||||
`(progn (declaim (inline ,name))
|
||||
(defun ,name ,arglist ,@body)))
|
||||
|
||||
|
||||
(defmacro i+ (&rest fixnums)
|
||||
"Do fixnum addition on FIXNUMS."
|
||||
`(the fixnum (+ ,@(loop for n in fixnums collect `(the fixnum ,n)))))
|
||||
|
||||
(defmacro i- (&rest fixnums)
|
||||
"Do fixnum subtraction on FIXNUMS."
|
||||
`(the fixnum (- ,@(loop for n in fixnums collect `(the fixnum ,n)))))
|
||||
|
||||
(defmacro i* (&rest fixnums)
|
||||
"Do fixnum multiplication on FIXNUMS."
|
||||
`(the fixnum (* ,@(loop for n in fixnums collect `(the fixnum ,n)))))
|
||||
|
||||
(defmacro i= (&rest fixnums)
|
||||
"Check FIXNUMS for equality."
|
||||
`(= ,@(loop for n in fixnums collect `(the fixnum ,n))))
|
||||
|
||||
(defmacro i< (&rest fixnums)
|
||||
"Check that FIXNUMS are monotonically increasing left to right."
|
||||
`(< ,@(loop for n in fixnums collect `(the fixnum ,n))))
|
||||
|
||||
(defmacro i<= (&rest fixnums)
|
||||
"Check that FIXNUMS are not decreasing, left to right."
|
||||
`(<= ,@(loop for n in fixnums collect `(the fixnum ,n))))
|
||||
|
||||
(defmacro i> (&rest fixnums)
|
||||
"Check that FIXNUMS are monotonically decreasing, left to right."
|
||||
`(> ,@(loop for n in fixnums collect `(the fixnum ,n))))
|
||||
|
||||
(defmacro i>= (&rest fixnums)
|
||||
"Check that FIXNUMS are not increasing, left to right."
|
||||
`(>= ,@(loop for n in fixnums collect `(the fixnum ,n))))
|
||||
|
||||
(defmacro iash (value count)
|
||||
"Shift VALUE left by COUNT places, preserving sign. Negative COUNT shifts right."
|
||||
`(the fixnum (ash (the fixnum ,value) (the fixnum ,count))))
|
||||
|
||||
(defmacro ilogior (&rest fixnums)
|
||||
"Return the bit-wise or of FIXNUMS."
|
||||
(if (cdr fixnums)
|
||||
`(the fixnum (logior (the fixnum ,(car fixnums))
|
||||
,(if (cddr fixnums)
|
||||
`(ilogior ,@(cdr fixnums))
|
||||
`(the fixnum ,(second fixnums)))))
|
||||
`(the fixnum ,(car fixnums))))
|
||||
|
||||
(defmacro ilogand (&rest fixnums)
|
||||
"Return the bit-wise and of FIXNUMS."
|
||||
(if (cdr fixnums)
|
||||
`(the fixnum (logand (the fixnum ,(car fixnums))
|
||||
,(if (cddr fixnums)
|
||||
`(ilogand ,@(cdr fixnums))
|
||||
`(the fixnum ,(second fixnums)))))
|
||||
`(the fixnum ,(car fixnums))))
|
||||
|
||||
(define-modify-macro iincf (&optional (delta 1)) i+)
|
||||
(define-modify-macro idecf (&optional (delta 1)) i-)
|
||||
|
||||
(defmacro ildb (bytespec value)
|
||||
"Extract the specified BYTESPEC from VALUE, and right justify result."
|
||||
`(the fixnum (ldb ,bytespec (the fixnum ,value))))
|
||||
|
||||
|
||||
;;; String utilities
|
||||
|
||||
(defun starts-with (string prefix)
|
||||
"Returns true if STRING matches PREFIX (case insensitive)."
|
||||
(and (i>= (length string) (length prefix))
|
||||
(string-equal string prefix :end1 (length prefix))
|
||||
prefix))
|
||||
|
||||
(defun ends-with (string suffix)
|
||||
"Returns true if STRING matches SUFFIX (case insensitive)."
|
||||
(let ((string-len (length string))
|
||||
(suffix-len (length suffix)))
|
||||
(and (i>= string-len suffix-len)
|
||||
(string-equal string suffix :start1 (i- string-len suffix-len))
|
||||
suffix)))
|
||||
|
||||
(defun strcat (&rest strings)
|
||||
"Return the concatenation of STRINGS. If no arguments are passed, the empty string is returned."
|
||||
(declare (dynamic-extent strings))
|
||||
(let ((result (apply #'concatenate 'string strings)))
|
||||
(if (and (not (typep result 'base-string))
|
||||
(every (lambda (x) (typep x 'base-char)) result))
|
||||
(coerce result 'base-string)
|
||||
result)))
|
||||
|
||||
(defun camel-case (string &optional (separators '(#\-)))
|
||||
"Convert STRING to camel-case by splitting on any of the SEPARATORS and then joining back together
|
||||
after capitalizing each part.
|
||||
Ex: (camel-case \"camel-case\") => \"CamelCase\""
|
||||
(let ((words (split-string string :separators separators)))
|
||||
(format nil "~{~@(~A~)~}" words)))
|
||||
|
||||
(defun camel-case-but-one (string &optional (separators '(#\-)))
|
||||
"Convert STRING to camel-case by splitting on any of the SEPARATORS and then joining back
|
||||
together after capitalizing all except the first part.
|
||||
Ex: (camel-case-but-one \"camel-case\") => \"camelCase\""
|
||||
(let ((words (split-string string :separators separators)))
|
||||
(format nil "~(~A~)~{~@(~A~)~}" (car words) (cdr words))))
|
||||
|
||||
|
||||
;; NB: uncamel-case is not reversible, i.e., it is lossy w.r.t. the original name.
|
||||
;; (uncamel-case "CamelCase") => "CAMEL-CASE"
|
||||
;; (uncamel-case "TCPConnection") => "TCP-CONNECTION"
|
||||
;; (uncamel-case "NewTCPConnection") => "NEW-TCP-CONNECTION"
|
||||
;; (uncamel-case "new_RPC_LispService") => "NEW-RPC-LISP-SERVICE"
|
||||
;; (uncamel-case "RPC_LispServiceRequest_get_request") => "RPC-LISP-SERVICE-REQUEST-GET-REQUEST"
|
||||
;; (uncamel-case "TCP2Name3") => "TCP2-NAME3"
|
||||
(defun uncamel-case (name &optional (separator #\-))
|
||||
"Convert NAME from camel-case to a SEPARATOR-separated string."
|
||||
;; We need a whole state machine to get this right
|
||||
(labels ((uncamel (chars state result)
|
||||
(let ((ch (first chars)))
|
||||
(cond ((null chars)
|
||||
result)
|
||||
((upper-case-p ch)
|
||||
(uncamel (rest chars) 'upper
|
||||
(case state
|
||||
((upper)
|
||||
;; "TCPConnection" => "TCP-CONNECTION"
|
||||
(if (and (second chars) (lower-case-p (second chars)))
|
||||
(list* ch separator result)
|
||||
(cons ch result)))
|
||||
((lower digit) (list* ch separator result))
|
||||
(otherwise (cons ch result)))))
|
||||
((lower-case-p ch)
|
||||
(uncamel (rest chars) 'lower
|
||||
(cons (char-upcase ch) result)))
|
||||
((digit-char-p ch)
|
||||
(uncamel (rest chars) 'digit
|
||||
(cons ch result)))
|
||||
((or (eql ch #\-) (eql ch #\_))
|
||||
(uncamel (rest chars) 'dash
|
||||
(cons #\- result)))
|
||||
((eql ch #\.)
|
||||
(uncamel (rest chars) 'dot
|
||||
(cons #\. result)))
|
||||
(t
|
||||
(protobuf-error "Invalid name character: ~S" ch))))))
|
||||
(strcat (nreverse (uncamel (concatenate 'list name) nil ())))))
|
||||
|
||||
(defun split-string (line &key (start 0) (end (length line)) (separators '(#\-)))
|
||||
"Split LINE at each of the characters in SEPARATORS starting at START and ending before END.
|
||||
Returns a list strings, with empty strings removed.
|
||||
Ex: (split-string \"-a-b\") => (\"a\" \"b\")"
|
||||
(unless (i= start end)
|
||||
(loop for this fixnum = start then (i+ next 1)
|
||||
for next fixnum = (or (position-if #'(lambda (ch) (member ch separators)) line
|
||||
:start this :end end)
|
||||
end)
|
||||
for piece = (string-right-trim '(#\space) (subseq line this next))
|
||||
when (not (i= (length piece) 0))
|
||||
collect piece
|
||||
until (i>= next end))))
|
||||
|
||||
;;; Managing symbols
|
||||
|
||||
(defmacro with-gensyms ((&rest bindings) &body body)
|
||||
"Bind each symbol in BINDINGS to a gensym'd symbol containing its name."
|
||||
`(let ,(mapcar #'(lambda (b) `(,b (gensym ,(string b))))
|
||||
bindings)
|
||||
,@body))
|
||||
|
||||
(defun lisp-symbol-string (symbol)
|
||||
"Returns the string used as the wire format for SYMBOL."
|
||||
(case symbol
|
||||
((t) "T")
|
||||
((nil) "NIL")
|
||||
(:t ":T")
|
||||
(:nil ":NIL")
|
||||
(otherwise
|
||||
(if (keywordp symbol)
|
||||
(symbol-name symbol)
|
||||
(format nil "~A:~A"
|
||||
(let ((package (symbol-package symbol)))
|
||||
(if package (package-name package) "#"))
|
||||
(symbol-name symbol))))))
|
||||
|
||||
(defun make-lisp-symbol (input-string &optional check-bad-chars)
|
||||
"Intern the symbol described by INPUT-STRING. If INPUT-STRING is
|
||||
\"nil\" or \"t\" then return nil or t. If string has no colon
|
||||
return a keyword symbol.
|
||||
Otherwise, STRING should be of the form 'package:string' and the symbol
|
||||
PACKAGE::STRING is returned.
|
||||
If CHECK-BAD-CHARS is specified, disallow strings with more than one colon
|
||||
or strings that have certain other bad characters."
|
||||
(let ((string (string-upcase input-string)))
|
||||
(cond
|
||||
((string= string "T") T)
|
||||
((string= string "NIL") NIL)
|
||||
(t
|
||||
(when check-bad-chars
|
||||
(let* ((bad-chars `(#\' #\\ #\"))
|
||||
(bad-char (find-if #'(lambda (x) (member x bad-chars)) string)))
|
||||
(when bad-char
|
||||
(protobuf-error "Invalid symbol character ~S in ~S" bad-char input-string))))
|
||||
(let ((pos (position #\: string))
|
||||
symbol-name
|
||||
package-name)
|
||||
(if pos
|
||||
(setq symbol-name (subseq string (1+ pos))
|
||||
package-name (if (= pos 0) "KEYWORD" (subseq string 0 pos)))
|
||||
(setq symbol-name string
|
||||
package-name "KEYWORD"))
|
||||
(when (and check-bad-chars
|
||||
(find #\: symbol-name))
|
||||
(protobuf-error "Invalid symbol character ~S in ~S" #\: input-string))
|
||||
(if (string= package-name "#")
|
||||
(make-symbol symbol-name)
|
||||
(let ((package (or (find-package package-name)
|
||||
(make-package package-name :use ()))))
|
||||
;; Discard 2nd value from intern so that this function returns only 1 value.
|
||||
(values (intern symbol-name package)))))))))
|
||||
|
||||
(defun qualified-symbol-name (symbol)
|
||||
"Return a string representing SYMBOL qualified with its package name."
|
||||
(let* ((*package* (find-package :keyword)))
|
||||
(prin1-to-string symbol)))
|
||||
|
||||
(defun fintern (format-string &rest format-args)
|
||||
"Interns a new symbol in the current package. The symbol name is the result of applying #'format
|
||||
to FORMAT-STRING and FORMAT-ARGS."
|
||||
(declare (dynamic-extent format-args))
|
||||
(intern (nstring-upcase (apply #'format nil format-string format-args))))
|
||||
|
||||
(defun kintern (format-string &rest format-args)
|
||||
"Interns a new symbol in the keyword package. The symbol name is the result of applying 'format to
|
||||
FORMAT-STRING and FORMAT-ARGS."
|
||||
(declare (dynamic-extent format-args))
|
||||
(intern (nstring-upcase (apply #'format nil format-string format-args)) "KEYWORD"))
|
||||
|
||||
(defun keywordify (x)
|
||||
"Given a symbol designator X, returns a keyword symbol whose name is (symbol-name X).
|
||||
If X is nil, returns nil."
|
||||
(check-type x (or string symbol null))
|
||||
(cond ((null x) nil)
|
||||
((keywordp x) x)
|
||||
((symbolp x) (keywordify (symbol-name x)))
|
||||
((zerop (length x)) nil)
|
||||
((string-not-equal x "nil")
|
||||
(intern (substitute #\- #\_ (string-upcase x)) (find-package "KEYWORD")))))
|
||||
|
||||
(defun join-intern (&rest symbols)
|
||||
"Given SYMBOLS, return a symbol made by joining the symbol names with a dot, e.g.
|
||||
SYMBOL1.SYMBOL2.SYMBOL3. The resulting symbol is interned in the package of the first symbol."
|
||||
(when symbols
|
||||
(intern (format nil "~{~A~^.~}" symbols)
|
||||
(symbol-package (first symbols)))))
|
||||
|
||||
;;; Collectors, etc
|
||||
|
||||
(defun proto-slot-function-name (proto-type slot function-type)
|
||||
"Create function names for proto fields given their slot name.
|
||||
Arguments:
|
||||
PROTO-TYPE: The symbol naming a protobuf message, group, etc.
|
||||
SLOT: The symbol naming a protobuf field.
|
||||
FUNCTION-TYPE: The type of function name to retrieve:
|
||||
This can be :has, :get, or :clear (for normal fields).
|
||||
This can also be :map-get or :map-rem for the special map functions.
|
||||
Finally, it can be :case for the special oneof function."
|
||||
(declare (type symbol proto-type slot)
|
||||
(type (member :has :internal-has :get :clear :map-get :map-rem
|
||||
:case :push :length-of :nth)
|
||||
function-type))
|
||||
(let ((f-symbol (ecase function-type
|
||||
(:has 'has)
|
||||
(:internal-has '%%has)
|
||||
(:clear 'clear)
|
||||
(:get nil)
|
||||
(:map-get 'gethash)
|
||||
(:map-rem 'remhash)
|
||||
(:case 'case)
|
||||
(:push 'push)
|
||||
(:length-of 'length-of)
|
||||
(:nth 'nth))))
|
||||
(cond ((member f-symbol '(gethash remhash case))
|
||||
(intern (nstring-upcase (format nil "~a.~a-~a"
|
||||
(symbol-name proto-type)
|
||||
(symbol-name slot)
|
||||
f-symbol))
|
||||
(symbol-package proto-type)))
|
||||
(f-symbol
|
||||
(intern (nstring-upcase (format nil "~a.~a-~a"
|
||||
(symbol-name proto-type)
|
||||
f-symbol
|
||||
(symbol-name slot)))
|
||||
(symbol-package proto-type)))
|
||||
(t
|
||||
(intern (nstring-upcase (format nil "~a.~a"
|
||||
(symbol-name proto-type)
|
||||
(symbol-name slot)))
|
||||
(symbol-package proto-type))))))
|
||||
|
||||
;;; TODO(cgay): use ace.core.collect when that works on CCL and ABCL.
|
||||
(defmacro with-collectors ((&rest collection-descriptions) &body body)
|
||||
"COLLECTION-DESCRIPTIONS is a list of clauses of the form (collection function).
|
||||
The body can call 'function' to add a value to the corresponding 'collection'. Elements are added
|
||||
to the ends of the lists, in constant time. Example:
|
||||
(with-collectors ((numbers collect-number))
|
||||
... (collect-number n) ...)"
|
||||
(let ((let-bindings ())
|
||||
(flet-bindings ())
|
||||
(dynamic-extents ())
|
||||
(vobj '#:OBJECT))
|
||||
(dolist (description collection-descriptions)
|
||||
(destructuring-bind (place name) description
|
||||
(let ((vtail (make-symbol (format nil "~A-TAIL" place))))
|
||||
(setq dynamic-extents
|
||||
(nconc dynamic-extents `(#',name)))
|
||||
(setq let-bindings
|
||||
(nconc let-bindings
|
||||
`((,place ())
|
||||
(,vtail nil))))
|
||||
(setq flet-bindings
|
||||
(nconc flet-bindings
|
||||
`((,name (,vobj)
|
||||
(setq ,vtail (if ,vtail
|
||||
(setf (cdr ,vtail) (list ,vobj))
|
||||
(setf ,place (list ,vobj)))))))))))
|
||||
`(let (,@let-bindings)
|
||||
(flet (,@flet-bindings)
|
||||
,@(and dynamic-extents
|
||||
`((declare (dynamic-extent ,@dynamic-extents))))
|
||||
,@body))))
|
||||
|
||||
(defmacro dovector ((var vector &optional result) &body body)
|
||||
"Like DOLIST, but iterates over VECTOR binding VAR to each successive element.
|
||||
Returns RESULT."
|
||||
`(when ,vector
|
||||
(loop for ,var across ,vector
|
||||
do (progn ,@body)
|
||||
finally (return ,result))))
|
||||
|
||||
(defmacro doseq ((var sequence &optional result) &body body)
|
||||
"Iterates over SEQUENCE, binding VAR to each element in turn. Uses DOLIST or DOVECTOR depending on
|
||||
the type of the sequence. In optimized code, this turns out to be faster than (map () #'f
|
||||
sequence). Returns RESULT."
|
||||
(with-gensyms (vseq vbody)
|
||||
`(flet ((,vbody (,var) ,@body))
|
||||
(let ((,vseq ,sequence))
|
||||
(if (vectorp ,vseq)
|
||||
(dovector (,var ,vseq ,result)
|
||||
(,vbody ,var))
|
||||
(dolist (,var ,vseq ,result)
|
||||
(,vbody ,var)))))))
|
||||
|
||||
|
||||
(defmacro appendf (place tail)
|
||||
"Append TAIL to the list given by PLACE, then set the PLACE to the new list."
|
||||
`(setf ,place (append ,place ,tail)))
|
||||
|
||||
|
||||
;;; Types
|
||||
|
||||
;; A parameterized list type for repeated fields. The elements aren't type-checked.
|
||||
(deftype list-of (type)
|
||||
(if (eq type nil) ; a list that cannot have any element (element-type nil) is null
|
||||
'null
|
||||
'list))
|
||||
|
||||
;; A parameterized vector type for repeated fields. The elements aren't type-checked.
|
||||
(deftype vector-of (type)
|
||||
(if (eq type nil) ; an array that cannot have any element (element-type nil) is of size 0
|
||||
'(array * (0))
|
||||
'(array * (*)))) ; a 1-dimensional array of any type
|
||||
|
||||
;;; This can't be simple-vector because #() is used as the default in some places. Fix it.
|
||||
;;; This corresponds to the :bytes protobuf type.
|
||||
(deftype byte-vector () '(array (unsigned-byte 8) (*)))
|
||||
|
||||
(defun make-byte-vector (size &key adjustable)
|
||||
"Make a byte vector of length SIZE, optionally ADJUSTABLE."
|
||||
(make-array size :element-type '(unsigned-byte 8)
|
||||
:adjustable adjustable))
|
||||
|
||||
(defconstant +field-number-bits+ 29
|
||||
"Number of bits in a field number.")
|
||||
|
||||
(defconstant +max-field-number+ (- (ash 1 +field-number-bits+) 1)
|
||||
"Maximum field number is 2^29 - 1")
|
||||
|
||||
(deftype field-number () `(integer 0 ,+max-field-number+))
|
||||
|
||||
;; The protobuf integer types
|
||||
(deftype int32 () '(signed-byte 32))
|
||||
(deftype int64 () '(signed-byte 64))
|
||||
(deftype uint32 () '(unsigned-byte 32))
|
||||
(deftype uint64 () '(unsigned-byte 64))
|
||||
(deftype sint32 () '(signed-byte 32))
|
||||
(deftype sint64 () '(signed-byte 64))
|
||||
(deftype fixed32 () '(unsigned-byte 32))
|
||||
(deftype fixed64 () '(unsigned-byte 64))
|
||||
(deftype sfixed32 () '(signed-byte 32))
|
||||
(deftype sfixed64 () '(signed-byte 64))
|
||||
|
||||
(defun fixed-width-integer-type-p (type)
|
||||
"Check whether TYPE can be serialized in a fixed number of bits."
|
||||
(member type '(fixed32 fixed64 sfixed32 sfixed64)))
|
||||
|
||||
(defun zigzag-encoded-type-p (type)
|
||||
"Check whether TYPE should be zigzag encoded on the wire."
|
||||
(member type '(sint32 sint64)))
|
||||
|
||||
(defun type-expand (type)
|
||||
"Convert TYPE into an equivalent type, removing all references to derived types."
|
||||
#+(or abcl xcl) (system::expand-deftype type)
|
||||
#+allegro (excl:normalize-type type :default type)
|
||||
#+ccl (ccl::type-expand type)
|
||||
#+clisp (ext:type-expand type)
|
||||
#+cmu (kernel:type-expand type)
|
||||
#+(or ecl mkcl) (si::expand-deftype type)
|
||||
#+lispworks (type:expand-user-type type)
|
||||
#+sbcl (sb-ext:typexpand type)
|
||||
#-(or abcl allegro ccl clisp cmu ecl lispworks mkcl sbcl xcl) type)
|
||||
|
||||
|
||||
;;; Code generation utilities
|
||||
|
||||
(defparameter *proto-name-separators* '(#\- #\_ #\/ #\space)
|
||||
"List of characters to use when splitting Lisp names apart to convert to protobuf names.")
|
||||
|
||||
(defparameter *camel-case-field-names* nil
|
||||
"If true, generate camelCase field names, otherwise generate snake_case field names.")
|
||||
|
||||
(defun find-proto-package (name)
|
||||
"Find a package named NAME, using various heuristics."
|
||||
(typecase name
|
||||
((or string symbol)
|
||||
;; Try looking under the given name and the all-uppercase name.
|
||||
(or (find-package (string name))
|
||||
(find-package (string-upcase (string name)))))
|
||||
(cons
|
||||
;; If 'name' is a list, it's actually a fully-qualified path.
|
||||
(or (find-proto-package (first name))
|
||||
(find-proto-package (format nil "~{~A~^.~}" name))))))
|
||||
|
||||
;; "class-name" -> "ClassName", ("ClassName")
|
||||
;; "outer-class.inner-class" -> "InnerClass", ("OuterClass" "InnerClass")
|
||||
;;; TODO(cgay): this would be more appropriately named lisp-name->proto-name.
|
||||
(defun class-name->proto (lisp-type-name)
|
||||
"Returns the protobuf message or enum name (a string) associated with
|
||||
LISP-TYPE-NAME (a symbol or string)."
|
||||
(let* ((full-path (split-string (string lisp-type-name) :separators '(#\.)))
|
||||
(name-part (first (last full-path))))
|
||||
(remove-if-not #'alphanumericp (camel-case name-part *proto-name-separators*))))
|
||||
|
||||
;; "enum-value" -> "ENUM_VALUE", ("ENUM_VALUE")
|
||||
;; "class-name.enum-value" -> "ENUM_VALUE", ("ClassName" "ENUM_VALUE")
|
||||
(defun enum-name->proto (enum-value-name &optional prefix)
|
||||
"Returns the protobuf enum value name associated with the Lisp ENUM-VALUE-NAME (a string).
|
||||
Strip PREFIX from the returned name, if supplied."
|
||||
(let* ((xs (split-string (string enum-value-name) :separators '(#\.)))
|
||||
(nx (string-upcase (car (last xs))))
|
||||
(nx (if (and prefix (starts-with nx prefix))
|
||||
(subseq nx (length prefix))
|
||||
nx))
|
||||
;; Keep underscores, they are standard separators in Protobufs enum names.
|
||||
(name (remove-if-not #'(lambda (x) (or (alphanumericp x) (eql x #\_)))
|
||||
(format nil "~{~A~^_~}"
|
||||
(split-string nx :separators *proto-name-separators*)))))
|
||||
name))
|
||||
|
||||
;; "slot-name" -> "slot_name", ("slot_name") or "slotName", ("slotName")
|
||||
;; "class-name.slot-name" -> "Class.slot_name", ("ClassName" "slot_name")
|
||||
(defun slot-name->proto (slot-name)
|
||||
"Returns the protobuf field name associated with a Lisp SLOT-NAME (a string)."
|
||||
(let* ((xs (split-string (string slot-name) :separators '(#\.)))
|
||||
(nx (string-downcase (car (last xs))))
|
||||
(name (if *camel-case-field-names*
|
||||
(remove-if-not #'alphanumericp
|
||||
(camel-case-but-one (format nil "~A" nx) *proto-name-separators*))
|
||||
;; Keep underscores, they are standard separators in Protobufs field names.
|
||||
(remove-if-not #'(lambda (x) (or (alphanumericp x) (eql x #\_)))
|
||||
(format nil "~{~A~^_~}"
|
||||
(split-string nx :separators *proto-name-separators*))))))
|
||||
name))
|
||||
|
||||
;; "foo.bar.Baz" -> 'FOO.BAR::BAZ
|
||||
;; "foo_bar.bar.Baz" -> 'FOO-BAR.BAR::BAZ
|
||||
(defun proto-to-class (proto-name &key (add-cl-protobufs t))
|
||||
"Turn a proto name into a Lisp structure class name.
|
||||
Parameters:
|
||||
PROTO-NAME: A proto name will have a package seperated with '.', all
|
||||
in lower case. The class name will be uppercase first, possibility
|
||||
with '.'.
|
||||
ADD-CL-PROTOBUFS: If true prepend 'CL-PROTOBUFS.' to the expected package
|
||||
name found in proto name."
|
||||
(let* ((first-upcase-position
|
||||
(position-if #'upper-case-p proto-name))
|
||||
(name
|
||||
(subseq proto-name first-upcase-position))
|
||||
(package
|
||||
(substitute
|
||||
#\- #\_
|
||||
(string-upcase (subseq proto-name 0
|
||||
(1- first-upcase-position))))))
|
||||
(when add-cl-protobufs
|
||||
(setf package (concatenate 'string "CL-PROTOBUFS." package)))
|
||||
(proto->class-name name package)))
|
||||
|
||||
;; "ClassName" -> 'class-name
|
||||
;; "cl-user.ClassName" -> 'cl-user::class-name
|
||||
;; "cl-user.OuterClass.InnerClass" -> 'cl-user::outer-class.inner-class
|
||||
(defun proto->class-name (proto-name &optional package)
|
||||
"Returns a Lisp type name (a symbol) for the protobuf message named PROTO-NAME.
|
||||
PROTO-NAME is a dotted string naming a proto message type, e.g., 'package.OuterClass.InnerClass'.
|
||||
If PACKAGE is non-nil and PROTO-NAME doesn't contain any dots the returned symbol is interned
|
||||
into PACKAGE, otherwise an uninterned symbol in the current package is returned."
|
||||
(let* ((full-path
|
||||
(split-string (substitute #\- #\_ (uncamel-case proto-name))
|
||||
:separators '(#\.)))
|
||||
(top-level (first full-path))
|
||||
(path-from-top (rest full-path))
|
||||
(path-part (butlast full-path))
|
||||
(name-part (last full-path))
|
||||
(pkg1 (when path-from-top (find-proto-package top-level)))
|
||||
;; TODO(dlroxe) Next line is faithful to original implementation, but
|
||||
;; TODO(dlroxe) s/path-part/name-part would make more sense to me.
|
||||
(pkgn (when path-from-top (find-proto-package path-part)))
|
||||
(package (or pkg1 pkgn package))
|
||||
(name (nstring-upcase
|
||||
(format nil "~{~A~^.~}" (cond (pkg1 path-from-top)
|
||||
(pkgn name-part)
|
||||
(t full-path))))))
|
||||
(if package
|
||||
(intern name package)
|
||||
(make-symbol name))))
|
||||
|
||||
;; "ENUM_VALUE" -> :enum-value
|
||||
;; "cl-user.ENUM_VALUE" -> :enum-value
|
||||
;; "cl-user.OuterClass.ENUM_VALUE" -> :enum-value
|
||||
(defun proto->enum-name (enum-name)
|
||||
"Returns a Lisp enum value (a keyword symbol) for the protobuf enum value named ENUM-NAME.
|
||||
ENUM-NAME is a dotted string naming a proto enum value, e.g., 'package.OuterClass.ENUM_VALUE'."
|
||||
(let* ((xs (split-string (substitute #\- #\_ (uncamel-case enum-name))
|
||||
:separators '(#\.)))
|
||||
(pkg1 (and (cdr xs) (find-proto-package (first xs))))
|
||||
(pkgn (and (cdr xs) (find-proto-package (butlast xs)))))
|
||||
(kintern (format nil "~{~A~^.~}" (cond (pkg1 (cdr xs))
|
||||
(pkgn (last xs))
|
||||
(t xs))))))
|
||||
|
||||
;; "slot_name" or "slotName" -> 'slot-name
|
||||
;; "cl-user.slot_name" or "cl-user.slotName" -> 'cl-user::slot-name
|
||||
;; "cl-user.OuterClass.slot_name" -> 'cl-user::outer-class.slot-name
|
||||
;; TODO(cgay): Can package default to *package* now that we've gotten rid of *protobuf-package*?
|
||||
;; What's the use case for returning an uninterned symbol?
|
||||
(defun proto->slot-name (field-name &optional package)
|
||||
"Returns a Lisp slot name (a symbol) for the protobuf field named FIELD-NAME.
|
||||
FIELD-NAME is a dotted string naming a proto message field, e.g.,
|
||||
'package.OuterClass.field_name'. If PACKAGE is non-nil and FIELD-NAME doesn't contain any dots
|
||||
the returned symbol is interned into PACKAGE, otherwise an uninterned symbol in the current
|
||||
package is returned."
|
||||
(let* ((xs (split-string (substitute #\- #\_ (uncamel-case field-name))
|
||||
:separators '(#\.)))
|
||||
(pkg1 (and (cdr xs) (find-proto-package (first xs))))
|
||||
(pkgn (and (cdr xs) (find-proto-package (butlast xs))))
|
||||
(package (or pkg1 pkgn package))
|
||||
(name (nstring-upcase
|
||||
(format nil "~{~A~^.~}" (cond (pkg1 (cdr xs))
|
||||
(pkgn (last xs))
|
||||
(t xs))))))
|
||||
(if package
|
||||
(intern name package)
|
||||
(make-symbol name))))
|
||||
|
||||
(defun scalarp (type)
|
||||
"Returns true if the given protobuf type TYPE is a scalar type. Scalar
|
||||
types are defined by the protobuf documentation. The cl-protobufs specific
|
||||
type `symbol' is included as a scalar type, as it is treated as a synonym
|
||||
to the `string' type. This is because symbols are transmitted as strings,
|
||||
which are scalars, and then converted based on the lisp_type of the field.
|
||||
|
||||
https://developers.google.com/protocol-buffers/docs/proto#scalar "
|
||||
(member type '(double-float float int32 int64 uint32 uint64 sint32
|
||||
sint32 sint64 fixed32 fixed64 sfixed32 sfixed64
|
||||
boolean string byte-vector symbol)))
|
||||
|
||||
(defun packed-type-p (type)
|
||||
"Returns true if the given protobuf TYPE can use a packed field."
|
||||
(check-type type symbol)
|
||||
(not (null (member type '(int32 int64 uint32 uint64 sint32 sint64
|
||||
fixed32 fixed64 sfixed32 sfixed64
|
||||
boolean float double-float)))))
|
||||
|
||||
;;; Warnings
|
||||
|
||||
(define-condition protobufs-warning (warning simple-condition) ())
|
||||
|
||||
(defun protobufs-warn (format-control &rest format-arguments)
|
||||
"Signal a protobufs-warning condition using FORMAT-CONTROL and FORMAT-ARGUMENTS
|
||||
to generate the warning message."
|
||||
(warn 'protobufs-warning ; NOLINT
|
||||
:format-control format-control
|
||||
:format-arguments format-arguments))
|
||||
|
||||
|
||||
#-(or allegro lispworks)
|
||||
(defmacro without-redefinition-warnings (() &body body) ; lint: disable=MISSING-DOCUMENTATION
|
||||
`(progn ,@body))
|
||||
|
||||
#+allegro
|
||||
(defmacro without-redefinition-warnings (() &body body) ; lint: disable=MISSING-DOCUMENTATION
|
||||
`(excl:without-redefinition-warnings ,@body))
|
||||
|
||||
#+lispworks
|
||||
(defmacro without-redefinition-warnings (() &body body) ; lint: disable=MISSING-DOCUMENTATION
|
||||
`(let ((dspec:*redefinition-action* :quiet)) ,@body))
|
||||
73
examples/meshtastic/lisp/cl-protobufs/well-known-types.lisp
Normal file
73
examples/meshtastic/lisp/cl-protobufs/well-known-types.lisp
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
;;; Copyright 2020 Google LLC
|
||||
;;;
|
||||
;;; Use of this source code is governed by an MIT-style
|
||||
;;; license that can be found in the LICENSE file or at
|
||||
;;; https://opensource.org/licenses/MIT.
|
||||
|
||||
;;; Functions for working with well known type
|
||||
|
||||
(defpackage #:cl-protobufs.well-known-types
|
||||
(:use #:cl
|
||||
#:cl-protobufs)
|
||||
(:local-nicknames (#:pi #:cl-protobufs.implementation))
|
||||
(:export #:unpack-any
|
||||
#:pack-any))
|
||||
|
||||
(in-package #:cl-protobufs.well-known-types)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; Any
|
||||
;;;
|
||||
;;; google.protobuf.Any is a protobuf message that contains
|
||||
;;; another message in its serialized form along with a type URL
|
||||
;;; that may be used to decode the contained message.
|
||||
;;;
|
||||
;;; Example:
|
||||
;;;
|
||||
;;; message MessageWithAny {
|
||||
;;; google.protobuf.Any my_field = 1;
|
||||
;;; }
|
||||
;;;
|
||||
;;; message Internal {
|
||||
;;; int64 internal_field = 1;
|
||||
;;; }
|
||||
;;;
|
||||
;;; To make a MessageWithAny containing an Internal message:
|
||||
;;;
|
||||
;;; (let* ((a (make-message-with-any :my-field
|
||||
;;; (pack-any (make-internal
|
||||
;;; :internal-field 1))))
|
||||
;;; (ret (unpack-any (message-with-any.my-field a))))
|
||||
;;; (proto-equal ret (make-internal :internal-field 1))) ; => t
|
||||
;;;
|
||||
;;; The Any .proto file can be found:
|
||||
;;; https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto
|
||||
;;;
|
||||
|
||||
(defun resolve-type-url (type-url)
|
||||
"Given a string TYPE-URL, find and return the Lisp type that it names. If no
|
||||
message is found, signal an error."
|
||||
(assert (find #\/ type-url :from-end t) ()
|
||||
"Could not find / inside of type-url.")
|
||||
(let* ((type-part-of-url (subseq type-url (1+ (position #\/ type-url :from-end t)))))
|
||||
(pi::find-message-by-qualified-name type-part-of-url :error-p t)))
|
||||
|
||||
(defun unpack-any (any-message)
|
||||
"Given an Any message decode the contained message and return it.
|
||||
Parameters:
|
||||
ANY-MESSAGE: The message to unpack."
|
||||
(let* ((type (resolve-type-url (cl-protobufs.google.protobuf:any.type-url any-message)))
|
||||
(value (cl-protobufs.google.protobuf:any.value any-message)))
|
||||
(deserialize-from-bytes type (subseq value 0))))
|
||||
|
||||
(defun pack-any (message &key (base-url "type.googleapis.com"))
|
||||
"Create an Any message containing MESSAGE.
|
||||
Parameters:
|
||||
MESSAGE: The messag to pack.
|
||||
BASE-URL: The base part of the URL without the final '/'."
|
||||
(let* ((m (cl-protobufs:find-message-descriptor (type-of message))))
|
||||
(cl-protobufs.google.protobuf:make-any
|
||||
;; This should either use a URL library or manually deal with the trailing
|
||||
;; slash correctly.
|
||||
:type-url (pi::strcat base-url "/" (pi::proto-qualified-name m))
|
||||
:value (serialize-to-bytes message))))
|
||||
1195
examples/meshtastic/lisp/cl-protobufs/wire-format.lisp
Normal file
1195
examples/meshtastic/lisp/cl-protobufs/wire-format.lisp
Normal file
File diff suppressed because it is too large
Load diff
9
examples/meshtastic/lisp/main.lisp
Normal file
9
examples/meshtastic/lisp/main.lisp
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
(in-package :app)
|
||||
|
||||
(defun ini ()
|
||||
(qt:ini)
|
||||
(msg:load-messages)
|
||||
(q> |visible| ui:*hour-glass* nil) ; shown during Lisp startup
|
||||
(q> |playing| ui:*busy* t)) ; shown during BLE setup
|
||||
|
||||
(qlater 'ini)
|
||||
44
examples/meshtastic/lisp/messages.lisp
Normal file
44
examples/meshtastic/lisp/messages.lisp
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
(in-package :msg)
|
||||
|
||||
(defvar *messages* nil)
|
||||
(defvar *message-id* 0)
|
||||
(defvar *states* '(:not-received :out-there :received))
|
||||
|
||||
(defun add-message (message &optional loading)
|
||||
"Adds passed MESSAGE (a PLIST) to both the QML item model and *MESSAGES*.
|
||||
The model keys are:
|
||||
:m-text :m-sender :m-timestamp :m-id :m-ack-state"
|
||||
(qjs |addMessage| ui:*messages* message)
|
||||
(unless loading
|
||||
(push message *messages*)
|
||||
(qlater 'save-messages)))
|
||||
|
||||
(defun change-state (state id)
|
||||
(let ((i-state (position state *states*)))
|
||||
(qjs |changeState| ui:*messages*
|
||||
i-state id)
|
||||
(dolist (msg *messages*)
|
||||
(when (eql (getf msg :m-id) id) ; EQL: might be NIL
|
||||
(setf (getf msg :m-ack-state) i-state)
|
||||
(return))))
|
||||
(qlater 'save-messages))
|
||||
|
||||
(defvar *file* (merge-pathnames "data/messages.exp"))
|
||||
|
||||
(defun load-messages ()
|
||||
"Loads *MESSAGES* which can directly be passed to the QML item model."
|
||||
(when (probe-file *file*)
|
||||
(with-open-file (s *file*)
|
||||
(setf *messages* (read s)))
|
||||
(dolist (msg (reverse *messages*))
|
||||
(setf *message-id* (max (or (getf msg :m-id) 0)
|
||||
*message-id*))
|
||||
(add-message msg t))))
|
||||
|
||||
(defun save-messages ()
|
||||
"Saves *MESSAGES* by simply printing them into a file."
|
||||
(ensure-directories-exist *file*)
|
||||
(with-open-file (s *file* :direction :output :if-exists :supersede)
|
||||
(let ((*print-pretty* nil))
|
||||
(prin1 *messages* s))))
|
||||
|
||||
38
examples/meshtastic/lisp/package.lisp
Normal file
38
examples/meshtastic/lisp/package.lisp
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
(defpackage :app
|
||||
(:use :cl :qml)
|
||||
(:export))
|
||||
|
||||
(defpackage :radio
|
||||
(:use :cl :qml)
|
||||
(:local-nicknames (:pr :cl-protobufs)
|
||||
(:me :cl-protobufs.meshtastic))
|
||||
(:export
|
||||
#:*channels*
|
||||
#:*config-lora*
|
||||
#:*my-node-info*
|
||||
#:*node-infos*
|
||||
#:*primary-channel*
|
||||
#:*reading*
|
||||
#:*ready*
|
||||
#:*received*
|
||||
#:*region*
|
||||
#:*remote-node*
|
||||
#:start-config
|
||||
#:read-radio
|
||||
#:received-from-radio
|
||||
#:receiving-done
|
||||
#:send-message
|
||||
#:send-to-radio
|
||||
#:set-ready))
|
||||
|
||||
(defpackage :messages
|
||||
(:nicknames :msg)
|
||||
(:use :cl :qml)
|
||||
(:export
|
||||
#:*messages*
|
||||
#:*message-id*
|
||||
#:*states*
|
||||
#:add-message
|
||||
#:change-state
|
||||
#:load-messages
|
||||
#:save-messages))
|
||||
39
examples/meshtastic/lisp/proto/cl-proto/any.lisp
Normal file
39
examples/meshtastic/lisp/proto/cl-proto/any.lisp
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
;;; any.proto.lisp
|
||||
;;;
|
||||
;;; Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
|
||||
(cl:in-package #:common-lisp-user)
|
||||
|
||||
#+sbcl
|
||||
(cl:progn
|
||||
(cl:eval-when (:compile-toplevel) (sb-ext:restrict-compiler-policy 'cl:debug 0 1))
|
||||
(cl:declaim (cl:optimize (sb-c:store-coverage-data 0))))
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(cl:unless (cl:find-package "CL-PROTOBUFS.GOOGLE.PROTOBUF")
|
||||
(cl:defpackage "CL-PROTOBUFS.GOOGLE.PROTOBUF" (:use)
|
||||
(:local-nicknames (#:pi #:cl-protobufs.implementation)))))
|
||||
|
||||
(cl:in-package "CL-PROTOBUFS.GOOGLE.PROTOBUF")
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(pi:define-schema 'any
|
||||
:syntax :proto3
|
||||
|
||||
:package "google.protobuf")
|
||||
)
|
||||
|
||||
|
||||
;;; Top-Level messages
|
||||
|
||||
(pi:define-message any
|
||||
()
|
||||
;; Fields
|
||||
(type-url
|
||||
:index 1 :type cl:string :kind :scalar :label (:optional) :json-name "typeUrl")
|
||||
(value
|
||||
:index 2 :type cl-protobufs:byte-vector :kind :scalar :label (:optional) :json-name "value"))
|
||||
|
||||
(cl:export '(any
|
||||
type-url
|
||||
value))
|
||||
89
examples/meshtastic/lisp/proto/cl-proto/api.lisp
Normal file
89
examples/meshtastic/lisp/proto/cl-proto/api.lisp
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
;;; api.proto.lisp
|
||||
;;;
|
||||
;;; Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
|
||||
(cl:in-package #:common-lisp-user)
|
||||
|
||||
#+sbcl
|
||||
(cl:progn
|
||||
(cl:eval-when (:compile-toplevel) (sb-ext:restrict-compiler-policy 'cl:debug 0 1))
|
||||
(cl:declaim (cl:optimize (sb-c:store-coverage-data 0))))
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(cl:unless (cl:find-package "CL-PROTOBUFS.GOOGLE.PROTOBUF")
|
||||
(cl:defpackage "CL-PROTOBUFS.GOOGLE.PROTOBUF" (:use)
|
||||
(:local-nicknames (#:pi #:cl-protobufs.implementation)))))
|
||||
|
||||
(cl:in-package "CL-PROTOBUFS.GOOGLE.PROTOBUF")
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(pi:define-schema 'api
|
||||
:syntax :proto3
|
||||
|
||||
:package "google.protobuf"))
|
||||
|
||||
;;; Top-Level messages
|
||||
|
||||
(pi:define-message api
|
||||
()
|
||||
;; Fields
|
||||
(name
|
||||
:index 1 :type cl:string :kind :scalar :label (:optional) :json-name "name")
|
||||
(methods
|
||||
:index 2 :type method :kind :message :label (:repeated :list) :json-name "methods")
|
||||
(options
|
||||
:index 3 :type cl-protobufs.google.protobuf::option :kind :message :label (:repeated :list) :json-name "options")
|
||||
(version
|
||||
:index 4 :type cl:string :kind :scalar :label (:optional) :json-name "version")
|
||||
(source-context
|
||||
:index 5 :type cl-protobufs.google.protobuf::source-context :kind :message :label (:optional) :json-name "sourceContext")
|
||||
(mixins
|
||||
:index 6 :type mixin :kind :message :label (:repeated :list) :json-name "mixins")
|
||||
(syntax
|
||||
:index 7 :type cl-protobufs.google.protobuf::syntax :kind :enum :label (:optional) :json-name "syntax" :default :syntax-proto2))
|
||||
|
||||
(pi:define-message method
|
||||
()
|
||||
;; Fields
|
||||
(name
|
||||
:index 1 :type cl:string :kind :scalar :label (:optional) :json-name "name")
|
||||
(request-type-url
|
||||
:index 2 :type cl:string :kind :scalar :label (:optional) :json-name "requestTypeUrl")
|
||||
(request-streaming
|
||||
:index 3 :type cl:boolean :kind :scalar :label (:optional) :json-name "requestStreaming")
|
||||
(response-type-url
|
||||
:index 4 :type cl:string :kind :scalar :label (:optional) :json-name "responseTypeUrl")
|
||||
(response-streaming
|
||||
:index 5 :type cl:boolean :kind :scalar :label (:optional) :json-name "responseStreaming")
|
||||
(options
|
||||
:index 6 :type cl-protobufs.google.protobuf::option :kind :message :label (:repeated :list) :json-name "options")
|
||||
(syntax
|
||||
:index 7 :type cl-protobufs.google.protobuf::syntax :kind :enum :label (:optional) :json-name "syntax" :default :syntax-proto2))
|
||||
|
||||
(pi:define-message mixin
|
||||
()
|
||||
;; Fields
|
||||
(name
|
||||
:index 1 :type cl:string :kind :scalar :label (:optional) :json-name "name")
|
||||
(root
|
||||
:index 2 :type cl:string :kind :scalar :label (:optional) :json-name "root"))
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(pi:add-file-descriptor #P"api.proto" 'api)
|
||||
)
|
||||
|
||||
(cl:export '(api
|
||||
method
|
||||
methods
|
||||
mixin
|
||||
mixins
|
||||
name
|
||||
options
|
||||
request-streaming
|
||||
request-type-url
|
||||
response-streaming
|
||||
response-type-url
|
||||
root
|
||||
source-context
|
||||
syntax
|
||||
version))
|
||||
591
examples/meshtastic/lisp/proto/cl-proto/descriptor.lisp
Normal file
591
examples/meshtastic/lisp/proto/cl-proto/descriptor.lisp
Normal file
|
|
@ -0,0 +1,591 @@
|
|||
;;; descriptor.proto.lisp
|
||||
;;;
|
||||
;;; Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
|
||||
(cl:in-package #:common-lisp-user)
|
||||
|
||||
#+sbcl
|
||||
(cl:progn
|
||||
(cl:eval-when (:compile-toplevel) (sb-ext:restrict-compiler-policy 'cl:debug 0 1))
|
||||
(cl:declaim (cl:optimize (sb-c:store-coverage-data 0))))
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(cl:unless (cl:find-package "CL-PROTOBUFS.GOOGLE.PROTOBUF")
|
||||
(cl:defpackage "CL-PROTOBUFS.GOOGLE.PROTOBUF" (:use)
|
||||
(:local-nicknames (#:pi #:cl-protobufs.implementation)))))
|
||||
|
||||
(cl:in-package "CL-PROTOBUFS.GOOGLE.PROTOBUF")
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(pi:define-schema 'descriptor
|
||||
:package "google.protobuf")
|
||||
)
|
||||
|
||||
|
||||
;;; Top-Level messages
|
||||
|
||||
(pi:define-message file-descriptor-set
|
||||
()
|
||||
;; Fields
|
||||
(file
|
||||
:index 1 :type file-descriptor-proto :kind :message :label (:repeated :list) :json-name "file"))
|
||||
|
||||
(pi:define-message file-descriptor-proto
|
||||
()
|
||||
;; Fields
|
||||
(name
|
||||
:index 1 :type cl:string :kind :scalar :label (:optional) :json-name "name")
|
||||
(package
|
||||
:index 2 :type cl:string :kind :scalar :label (:optional) :json-name "package")
|
||||
(dependency
|
||||
:index 3 :type cl:string :kind :scalar :label (:repeated :list) :json-name "dependency")
|
||||
(public-dependency
|
||||
:index 10 :type cl-protobufs:int32 :kind :scalar :label (:repeated :list) :json-name "publicDependency")
|
||||
(weak-dependency
|
||||
:index 11 :type cl-protobufs:int32 :kind :scalar :label (:repeated :list) :json-name "weakDependency")
|
||||
(message-type
|
||||
:index 4 :type descriptor-proto :kind :message :label (:repeated :list) :json-name "messageType")
|
||||
(enum-type
|
||||
:index 5 :type enum-descriptor-proto :kind :message :label (:repeated :list) :json-name "enumType")
|
||||
(service
|
||||
:index 6 :type service-descriptor-proto :kind :message :label (:repeated :list) :json-name "service")
|
||||
(extension
|
||||
:index 7 :type field-descriptor-proto :kind :message :label (:repeated :list) :json-name "extension")
|
||||
(options
|
||||
:index 8 :type file-options :kind :message :label (:optional) :json-name "options")
|
||||
(source-code-info
|
||||
:index 9 :type source-code-info :kind :message :label (:optional) :json-name "sourceCodeInfo")
|
||||
(syntax
|
||||
:index 12 :type cl:string :kind :scalar :label (:optional) :json-name "syntax"))
|
||||
|
||||
(pi:define-message descriptor-proto
|
||||
()
|
||||
;; Nested messages
|
||||
|
||||
(pi:define-message descriptor-proto.extension-range
|
||||
()
|
||||
;; Fields
|
||||
(start
|
||||
:index 1 :type cl-protobufs:int32 :kind :scalar :label (:optional) :json-name "start")
|
||||
(end
|
||||
:index 2 :type cl-protobufs:int32 :kind :scalar :label (:optional) :json-name "end")
|
||||
(options
|
||||
:index 3 :type extension-range-options :kind :message :label (:optional) :json-name "options"))
|
||||
|
||||
(pi:define-message descriptor-proto.reserved-range
|
||||
()
|
||||
;; Fields
|
||||
(start
|
||||
:index 1 :type cl-protobufs:int32 :kind :scalar :label (:optional) :json-name "start")
|
||||
(end
|
||||
:index 2 :type cl-protobufs:int32 :kind :scalar :label (:optional) :json-name "end"))
|
||||
;; Fields
|
||||
(name
|
||||
:index 1 :type cl:string :kind :scalar :label (:optional) :json-name "name")
|
||||
(field
|
||||
:index 2 :type field-descriptor-proto :kind :message :label (:repeated :list) :json-name "field")
|
||||
(extension
|
||||
:index 6 :type field-descriptor-proto :kind :message :label (:repeated :list) :json-name "extension")
|
||||
(nested-type
|
||||
:index 3 :type descriptor-proto :kind :message :label (:repeated :list) :json-name "nestedType")
|
||||
(enum-type
|
||||
:index 4 :type enum-descriptor-proto :kind :message :label (:repeated :list) :json-name "enumType")
|
||||
(extension-range
|
||||
:index 5 :type descriptor-proto.extension-range :kind :message :label (:repeated :list) :json-name "extensionRange")
|
||||
(oneof-decl
|
||||
:index 8 :type oneof-descriptor-proto :kind :message :label (:repeated :list) :json-name "oneofDecl")
|
||||
(options
|
||||
:index 7 :type message-options :kind :message :label (:optional) :json-name "options")
|
||||
(reserved-range
|
||||
:index 9 :type descriptor-proto.reserved-range :kind :message :label (:repeated :list) :json-name "reservedRange")
|
||||
(reserved-name
|
||||
:index 10 :type cl:string :kind :scalar :label (:repeated :list) :json-name "reservedName"))
|
||||
|
||||
(pi:define-message extension-range-options
|
||||
()
|
||||
;; Fields
|
||||
(uninterpreted-option
|
||||
:index 999 :type uninterpreted-option :kind :message :label (:repeated :list) :json-name "uninterpretedOption")
|
||||
;; Extension ranges
|
||||
(pi:define-extension 1000 536870911))
|
||||
|
||||
(pi:define-message field-descriptor-proto
|
||||
()
|
||||
;; Nested enums
|
||||
|
||||
(pi:define-enum field-descriptor-proto.type
|
||||
()
|
||||
(:type-double :index 1)
|
||||
(:type-float :index 2)
|
||||
(:type-int64 :index 3)
|
||||
(:type-uint64 :index 4)
|
||||
(:type-int32 :index 5)
|
||||
(:type-fixed64 :index 6)
|
||||
(:type-fixed32 :index 7)
|
||||
(:type-bool :index 8)
|
||||
(:type-string :index 9)
|
||||
(:type-group :index 10)
|
||||
(:type-message :index 11)
|
||||
(:type-bytes :index 12)
|
||||
(:type-uint32 :index 13)
|
||||
(:type-enum :index 14)
|
||||
(:type-sfixed32 :index 15)
|
||||
(:type-sfixed64 :index 16)
|
||||
(:type-sint32 :index 17)
|
||||
(:type-sint64 :index 18))
|
||||
|
||||
(pi:define-enum field-descriptor-proto.label
|
||||
()
|
||||
(:label-optional :index 1)
|
||||
(:label-required :index 2)
|
||||
(:label-repeated :index 3))
|
||||
;; Fields
|
||||
(name
|
||||
:index 1 :type cl:string :kind :scalar :label (:optional) :json-name "name")
|
||||
(number
|
||||
:index 3 :type cl-protobufs:int32 :kind :scalar :label (:optional) :json-name "number")
|
||||
(label
|
||||
:index 4 :type field-descriptor-proto.label :kind :enum :label (:optional) :json-name "label" :default :label-optional)
|
||||
(type
|
||||
:index 5 :type field-descriptor-proto.type :kind :enum :label (:optional) :json-name "type" :default :type-double)
|
||||
(type-name
|
||||
:index 6 :type cl:string :kind :scalar :label (:optional) :json-name "typeName")
|
||||
(extendee
|
||||
:index 2 :type cl:string :kind :scalar :label (:optional) :json-name "extendee")
|
||||
(default-value
|
||||
:index 7 :type cl:string :kind :scalar :label (:optional) :json-name "defaultValue")
|
||||
(oneof-index
|
||||
:index 9 :type cl-protobufs:int32 :kind :scalar :label (:optional) :json-name "oneofIndex")
|
||||
(json-name
|
||||
:index 10 :type cl:string :kind :scalar :label (:optional) :json-name "jsonName")
|
||||
(options
|
||||
:index 8 :type field-options :kind :message :label (:optional) :json-name "options")
|
||||
(proto3-optional
|
||||
:index 17 :type cl:boolean :kind :scalar :label (:optional) :json-name "proto3Optional"))
|
||||
|
||||
(pi:define-message oneof-descriptor-proto
|
||||
()
|
||||
;; Fields
|
||||
(name
|
||||
:index 1 :type cl:string :kind :scalar :label (:optional) :json-name "name")
|
||||
(options
|
||||
:index 2 :type oneof-options :kind :message :label (:optional) :json-name "options"))
|
||||
|
||||
(pi:define-message enum-descriptor-proto
|
||||
()
|
||||
;; Nested messages
|
||||
|
||||
(pi:define-message enum-descriptor-proto.enum-reserved-range
|
||||
()
|
||||
;; Fields
|
||||
(start
|
||||
:index 1 :type cl-protobufs:int32 :kind :scalar :label (:optional) :json-name "start")
|
||||
(end
|
||||
:index 2 :type cl-protobufs:int32 :kind :scalar :label (:optional) :json-name "end"))
|
||||
;; Fields
|
||||
(name
|
||||
:index 1 :type cl:string :kind :scalar :label (:optional) :json-name "name")
|
||||
(value
|
||||
:index 2 :type enum-value-descriptor-proto :kind :message :label (:repeated :list) :json-name "value")
|
||||
(options
|
||||
:index 3 :type enum-options :kind :message :label (:optional) :json-name "options")
|
||||
(reserved-range
|
||||
:index 4 :type enum-descriptor-proto.enum-reserved-range :kind :message :label (:repeated :list) :json-name "reservedRange")
|
||||
(reserved-name
|
||||
:index 5 :type cl:string :kind :scalar :label (:repeated :list) :json-name "reservedName"))
|
||||
|
||||
(pi:define-message enum-value-descriptor-proto
|
||||
()
|
||||
;; Fields
|
||||
(name
|
||||
:index 1 :type cl:string :kind :scalar :label (:optional) :json-name "name")
|
||||
(number
|
||||
:index 2 :type cl-protobufs:int32 :kind :scalar :label (:optional) :json-name "number")
|
||||
(options
|
||||
:index 3 :type enum-value-options :kind :message :label (:optional) :json-name "options"))
|
||||
|
||||
(pi:define-message service-descriptor-proto
|
||||
()
|
||||
;; Fields
|
||||
(name
|
||||
:index 1 :type cl:string :kind :scalar :label (:optional) :json-name "name")
|
||||
(method
|
||||
:index 2 :type method-descriptor-proto :kind :message :label (:repeated :list) :json-name "method")
|
||||
(options
|
||||
:index 3 :type service-options :kind :message :label (:optional) :json-name "options"))
|
||||
|
||||
(pi:define-message method-descriptor-proto
|
||||
()
|
||||
;; Fields
|
||||
(name
|
||||
:index 1 :type cl:string :kind :scalar :label (:optional) :json-name "name")
|
||||
(input-type
|
||||
:index 2 :type cl:string :kind :scalar :label (:optional) :json-name "inputType")
|
||||
(output-type
|
||||
:index 3 :type cl:string :kind :scalar :label (:optional) :json-name "outputType")
|
||||
(options
|
||||
:index 4 :type method-options :kind :message :label (:optional) :json-name "options")
|
||||
(client-streaming
|
||||
:index 5 :type cl:boolean :kind :scalar :label (:optional) :json-name "clientStreaming" :default cl:nil)
|
||||
(server-streaming
|
||||
:index 6 :type cl:boolean :kind :scalar :label (:optional) :json-name "serverStreaming" :default cl:nil))
|
||||
|
||||
(pi:define-message file-options
|
||||
()
|
||||
;; Nested enums
|
||||
|
||||
(pi:define-enum file-options.optimize-mode
|
||||
()
|
||||
(:speed :index 1)
|
||||
(:code-size :index 2)
|
||||
(:lite-runtime :index 3))
|
||||
;; Fields
|
||||
(java-package
|
||||
:index 1 :type cl:string :kind :scalar :label (:optional) :json-name "javaPackage")
|
||||
(java-outer-classname
|
||||
:index 8 :type cl:string :kind :scalar :label (:optional) :json-name "javaOuterClassname")
|
||||
(java-multiple-files
|
||||
:index 10 :type cl:boolean :kind :scalar :label (:optional) :json-name "javaMultipleFiles" :default cl:nil)
|
||||
(java-generate-equals-and-hash
|
||||
:index 20 :type cl:boolean :kind :scalar :label (:optional) :json-name "javaGenerateEqualsAndHash")
|
||||
(java-string-check-utf8
|
||||
:index 27 :type cl:boolean :kind :scalar :label (:optional) :json-name "javaStringCheckUtf8" :default cl:nil)
|
||||
(optimize-for
|
||||
:index 9 :type file-options.optimize-mode :kind :enum :label (:optional) :json-name "optimizeFor" :default :speed)
|
||||
(go-package
|
||||
:index 11 :type cl:string :kind :scalar :label (:optional) :json-name "goPackage")
|
||||
(cc-generic-services
|
||||
:index 16 :type cl:boolean :kind :scalar :label (:optional) :json-name "ccGenericServices" :default cl:nil)
|
||||
(java-generic-services
|
||||
:index 17 :type cl:boolean :kind :scalar :label (:optional) :json-name "javaGenericServices" :default cl:nil)
|
||||
(py-generic-services
|
||||
:index 18 :type cl:boolean :kind :scalar :label (:optional) :json-name "pyGenericServices" :default cl:nil)
|
||||
(php-generic-services
|
||||
:index 42 :type cl:boolean :kind :scalar :label (:optional) :json-name "phpGenericServices" :default cl:nil)
|
||||
(deprecated
|
||||
:index 23 :type cl:boolean :kind :scalar :label (:optional) :json-name "deprecated" :default cl:nil)
|
||||
(cc-enable-arenas
|
||||
:index 31 :type cl:boolean :kind :scalar :label (:optional) :json-name "ccEnableArenas" :default cl:t)
|
||||
(objc-class-prefix
|
||||
:index 36 :type cl:string :kind :scalar :label (:optional) :json-name "objcClassPrefix")
|
||||
(csharp-namespace
|
||||
:index 37 :type cl:string :kind :scalar :label (:optional) :json-name "csharpNamespace")
|
||||
(swift-prefix
|
||||
:index 39 :type cl:string :kind :scalar :label (:optional) :json-name "swiftPrefix")
|
||||
(php-class-prefix
|
||||
:index 40 :type cl:string :kind :scalar :label (:optional) :json-name "phpClassPrefix")
|
||||
(php-namespace
|
||||
:index 41 :type cl:string :kind :scalar :label (:optional) :json-name "phpNamespace")
|
||||
(php-metadata-namespace
|
||||
:index 44 :type cl:string :kind :scalar :label (:optional) :json-name "phpMetadataNamespace")
|
||||
(ruby-package
|
||||
:index 45 :type cl:string :kind :scalar :label (:optional) :json-name "rubyPackage")
|
||||
(uninterpreted-option
|
||||
:index 999 :type uninterpreted-option :kind :message :label (:repeated :list) :json-name "uninterpretedOption")
|
||||
;; Extension ranges
|
||||
(pi:define-extension 1000 536870911))
|
||||
|
||||
(pi:define-message message-options
|
||||
()
|
||||
;; Fields
|
||||
(message-set-wire-format
|
||||
:index 1 :type cl:boolean :kind :scalar :label (:optional) :json-name "messageSetWireFormat" :default cl:nil)
|
||||
(no-standard-descriptor-accessor
|
||||
:index 2 :type cl:boolean :kind :scalar :label (:optional) :json-name "noStandardDescriptorAccessor" :default cl:nil)
|
||||
(deprecated
|
||||
:index 3 :type cl:boolean :kind :scalar :label (:optional) :json-name "deprecated" :default cl:nil)
|
||||
(map-entry
|
||||
:index 7 :type cl:boolean :kind :scalar :label (:optional) :json-name "mapEntry")
|
||||
(uninterpreted-option
|
||||
:index 999 :type uninterpreted-option :kind :message :label (:repeated :list) :json-name "uninterpretedOption")
|
||||
;; Extension ranges
|
||||
(pi:define-extension 1000 536870911))
|
||||
|
||||
(pi:define-message field-options
|
||||
()
|
||||
;; Nested enums
|
||||
|
||||
(pi:define-enum field-options.c-type
|
||||
()
|
||||
(:string :index 0)
|
||||
(:cord :index 1)
|
||||
(:string-piece :index 2))
|
||||
|
||||
(pi:define-enum field-options.js-type
|
||||
(:name "JSType")
|
||||
(:js-normal :index 0)
|
||||
(:js-string :index 1)
|
||||
(:js-number :index 2))
|
||||
;; Fields
|
||||
(ctype
|
||||
:index 1 :type field-options.c-type :kind :enum :label (:optional) :json-name "ctype" :default :string)
|
||||
(packed
|
||||
:index 2 :type cl:boolean :kind :scalar :label (:optional) :json-name "packed")
|
||||
(jstype
|
||||
:index 6 :type field-options.js-type :kind :enum :label (:optional) :json-name "jstype" :default :js-normal)
|
||||
(lazy
|
||||
:index 5 :type cl:boolean :kind :scalar :label (:optional) :json-name "lazy" :default cl:nil)
|
||||
(deprecated
|
||||
:index 3 :type cl:boolean :kind :scalar :label (:optional) :json-name "deprecated" :default cl:nil)
|
||||
(weak
|
||||
:index 10 :type cl:boolean :kind :scalar :label (:optional) :json-name "weak" :default cl:nil)
|
||||
(uninterpreted-option
|
||||
:index 999 :type uninterpreted-option :kind :message :label (:repeated :list) :json-name "uninterpretedOption")
|
||||
;; Extension ranges
|
||||
(pi:define-extension 1000 536870911))
|
||||
|
||||
(pi:define-message oneof-options
|
||||
()
|
||||
;; Fields
|
||||
(uninterpreted-option
|
||||
:index 999 :type uninterpreted-option :kind :message :label (:repeated :list) :json-name "uninterpretedOption")
|
||||
;; Extension ranges
|
||||
(pi:define-extension 1000 536870911))
|
||||
|
||||
(pi:define-message enum-options
|
||||
()
|
||||
;; Fields
|
||||
(allow-alias
|
||||
:index 2 :type cl:boolean :kind :scalar :label (:optional) :json-name "allowAlias")
|
||||
(deprecated
|
||||
:index 3 :type cl:boolean :kind :scalar :label (:optional) :json-name "deprecated" :default cl:nil)
|
||||
(uninterpreted-option
|
||||
:index 999 :type uninterpreted-option :kind :message :label (:repeated :list) :json-name "uninterpretedOption")
|
||||
;; Extension ranges
|
||||
(pi:define-extension 1000 536870911))
|
||||
|
||||
(pi:define-message enum-value-options
|
||||
()
|
||||
;; Fields
|
||||
(deprecated
|
||||
:index 1 :type cl:boolean :kind :scalar :label (:optional) :json-name "deprecated" :default cl:nil)
|
||||
(uninterpreted-option
|
||||
:index 999 :type uninterpreted-option :kind :message :label (:repeated :list) :json-name "uninterpretedOption")
|
||||
;; Extension ranges
|
||||
(pi:define-extension 1000 536870911))
|
||||
|
||||
(pi:define-message service-options
|
||||
()
|
||||
;; Fields
|
||||
(deprecated
|
||||
:index 33 :type cl:boolean :kind :scalar :label (:optional) :json-name "deprecated" :default cl:nil)
|
||||
(uninterpreted-option
|
||||
:index 999 :type uninterpreted-option :kind :message :label (:repeated :list) :json-name "uninterpretedOption")
|
||||
;; Extension ranges
|
||||
(pi:define-extension 1000 536870911))
|
||||
|
||||
(pi:define-message method-options
|
||||
()
|
||||
;; Nested enums
|
||||
|
||||
(pi:define-enum method-options.idempotency-level
|
||||
()
|
||||
(:idempotency-unknown :index 0)
|
||||
(:no-side-effects :index 1)
|
||||
(:idempotent :index 2))
|
||||
;; Fields
|
||||
(deprecated
|
||||
:index 33 :type cl:boolean :kind :scalar :label (:optional) :json-name "deprecated" :default cl:nil)
|
||||
(idempotency-level
|
||||
:index 34 :type method-options.idempotency-level :kind :enum :label (:optional) :json-name "idempotencyLevel" :default :idempotency-unknown)
|
||||
(uninterpreted-option
|
||||
:index 999 :type uninterpreted-option :kind :message :label (:repeated :list) :json-name "uninterpretedOption")
|
||||
;; Extension ranges
|
||||
(pi:define-extension 1000 536870911))
|
||||
|
||||
(pi:define-message uninterpreted-option
|
||||
()
|
||||
;; Nested messages
|
||||
|
||||
(pi:define-message uninterpreted-option.name-part
|
||||
()
|
||||
;; Fields
|
||||
(name-part
|
||||
:index 1 :type cl:string :kind :scalar :label (:required) :json-name "namePart")
|
||||
(is-extension
|
||||
:index 2 :type cl:boolean :kind :scalar :label (:required) :json-name "isExtension"))
|
||||
;; Fields
|
||||
(name
|
||||
:index 2 :type uninterpreted-option.name-part :kind :message :label (:repeated :list) :json-name "name")
|
||||
(identifier-value
|
||||
:index 3 :type cl:string :kind :scalar :label (:optional) :json-name "identifierValue")
|
||||
(positive-int-value
|
||||
:index 4 :type cl-protobufs:uint64 :kind :scalar :label (:optional) :json-name "positiveIntValue")
|
||||
(negative-int-value
|
||||
:index 5 :type cl-protobufs:int64 :kind :scalar :label (:optional) :json-name "negativeIntValue")
|
||||
(double-value
|
||||
:index 6 :type cl:double-float :kind :scalar :label (:optional) :json-name "doubleValue")
|
||||
(string-value
|
||||
:index 7 :type cl-protobufs:byte-vector :kind :scalar :label (:optional) :json-name "stringValue")
|
||||
(aggregate-value
|
||||
:index 8 :type cl:string :kind :scalar :label (:optional) :json-name "aggregateValue"))
|
||||
|
||||
(pi:define-message source-code-info
|
||||
()
|
||||
;; Nested messages
|
||||
|
||||
(pi:define-message source-code-info.location
|
||||
()
|
||||
;; Fields
|
||||
(path
|
||||
:index 1 :type cl-protobufs:int32 :kind :scalar :label (:repeated :list) :json-name "path" :packed cl:t)
|
||||
(span
|
||||
:index 2 :type cl-protobufs:int32 :kind :scalar :label (:repeated :list) :json-name "span" :packed cl:t)
|
||||
(leading-comments
|
||||
:index 3 :type cl:string :kind :scalar :label (:optional) :json-name "leadingComments")
|
||||
(trailing-comments
|
||||
:index 4 :type cl:string :kind :scalar :label (:optional) :json-name "trailingComments")
|
||||
(leading-detached-comments
|
||||
:index 6 :type cl:string :kind :scalar :label (:repeated :list) :json-name "leadingDetachedComments"))
|
||||
;; Fields
|
||||
(location
|
||||
:index 1 :type source-code-info.location :kind :message :label (:repeated :list) :json-name "location"))
|
||||
|
||||
(pi:define-message generated-code-info
|
||||
()
|
||||
;; Nested messages
|
||||
|
||||
(pi:define-message generated-code-info.annotation
|
||||
()
|
||||
;; Fields
|
||||
(path
|
||||
:index 1 :type cl-protobufs:int32 :kind :scalar :label (:repeated :list) :json-name "path" :packed cl:t)
|
||||
(source-file
|
||||
:index 2 :type cl:string :kind :scalar :label (:optional) :json-name "sourceFile")
|
||||
(begin
|
||||
:index 3 :type cl-protobufs:int32 :kind :scalar :label (:optional) :json-name "begin")
|
||||
(end
|
||||
:index 4 :type cl-protobufs:int32 :kind :scalar :label (:optional) :json-name "end"))
|
||||
;; Fields
|
||||
(annotation
|
||||
:index 1 :type generated-code-info.annotation :kind :message :label (:repeated :list) :json-name "annotation"))
|
||||
|
||||
|
||||
(cl:export '(aggregate-value
|
||||
allow-alias
|
||||
annotation
|
||||
begin
|
||||
cc-enable-arenas
|
||||
cc-generic-services
|
||||
client-streaming
|
||||
csharp-namespace
|
||||
ctype
|
||||
default-value
|
||||
dependency
|
||||
deprecated
|
||||
descriptor
|
||||
descriptor-proto
|
||||
descriptor-proto.extension-range
|
||||
descriptor-proto.reserved-range
|
||||
double-value
|
||||
end
|
||||
enum-descriptor-proto
|
||||
enum-descriptor-proto.enum-reserved-range
|
||||
enum-options
|
||||
enum-type
|
||||
enum-value-descriptor-proto
|
||||
enum-value-options
|
||||
extendee
|
||||
extension
|
||||
extension-range
|
||||
extension-range-options
|
||||
field
|
||||
field-descriptor-proto
|
||||
field-descriptor-proto.label
|
||||
field-descriptor-proto.label-int-to-keyword
|
||||
field-descriptor-proto.label-keyword-to-int
|
||||
field-descriptor-proto.type
|
||||
field-descriptor-proto.type-int-to-keyword
|
||||
field-descriptor-proto.type-keyword-to-int
|
||||
field-options
|
||||
field-options.c-type
|
||||
field-options.c-type-int-to-keyword
|
||||
field-options.c-type-keyword-to-int
|
||||
field-options.js-type
|
||||
field-options.js-type-int-to-keyword
|
||||
field-options.js-type-keyword-to-int
|
||||
file
|
||||
file-descriptor-proto
|
||||
file-descriptor-set
|
||||
file-options
|
||||
file-options.optimize-mode
|
||||
file-options.optimize-mode-int-to-keyword
|
||||
file-options.optimize-mode-keyword-to-int
|
||||
generated-code-info
|
||||
generated-code-info.annotation
|
||||
go-package
|
||||
idempotency-level
|
||||
identifier-value
|
||||
input-type
|
||||
is-extension
|
||||
java-generate-equals-and-hash
|
||||
java-generic-services
|
||||
java-multiple-files
|
||||
java-outer-classname
|
||||
java-package
|
||||
java-string-check-utf8
|
||||
json-name
|
||||
jstype
|
||||
label
|
||||
lazy
|
||||
leading-comments
|
||||
leading-detached-comments
|
||||
location
|
||||
map-entry
|
||||
message-options
|
||||
message-set-wire-format
|
||||
message-type
|
||||
method
|
||||
method-descriptor-proto
|
||||
method-options
|
||||
method-options.idempotency-level
|
||||
method-options.idempotency-level-int-to-keyword
|
||||
method-options.idempotency-level-keyword-to-int
|
||||
name
|
||||
name-part
|
||||
negative-int-value
|
||||
nested-type
|
||||
no-standard-descriptor-accessor
|
||||
number
|
||||
objc-class-prefix
|
||||
oneof-decl
|
||||
oneof-descriptor-proto
|
||||
oneof-index
|
||||
oneof-options
|
||||
optimize-for
|
||||
options
|
||||
output-type
|
||||
package
|
||||
packed
|
||||
path
|
||||
php-class-prefix
|
||||
php-generic-services
|
||||
php-metadata-namespace
|
||||
php-namespace
|
||||
positive-int-value
|
||||
proto3-optional
|
||||
public-dependency
|
||||
py-generic-services
|
||||
reserved-name
|
||||
reserved-range
|
||||
ruby-package
|
||||
server-streaming
|
||||
service
|
||||
service-descriptor-proto
|
||||
service-options
|
||||
source-code-info
|
||||
source-code-info.location
|
||||
source-file
|
||||
span
|
||||
start
|
||||
string-value
|
||||
swift-prefix
|
||||
syntax
|
||||
trailing-comments
|
||||
type
|
||||
type-name
|
||||
uninterpreted-option
|
||||
uninterpreted-option.name-part
|
||||
value
|
||||
weak
|
||||
weak-dependency))
|
||||
40
examples/meshtastic/lisp/proto/cl-proto/duration.lisp
Normal file
40
examples/meshtastic/lisp/proto/cl-proto/duration.lisp
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
;;; duration.proto.lisp
|
||||
;;;
|
||||
;;; Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
|
||||
(cl:in-package #:common-lisp-user)
|
||||
|
||||
#+sbcl
|
||||
(cl:progn
|
||||
(cl:eval-when (:compile-toplevel) (sb-ext:restrict-compiler-policy 'cl:debug 0 1))
|
||||
(cl:declaim (cl:optimize (sb-c:store-coverage-data 0))))
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(cl:unless (cl:find-package "CL-PROTOBUFS.GOOGLE.PROTOBUF")
|
||||
(cl:defpackage "CL-PROTOBUFS.GOOGLE.PROTOBUF" (:use)
|
||||
(:local-nicknames (#:pi #:cl-protobufs.implementation)))))
|
||||
|
||||
(cl:in-package "CL-PROTOBUFS.GOOGLE.PROTOBUF")
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(pi:define-schema 'duration
|
||||
:syntax :proto3
|
||||
|
||||
:package "google.protobuf")
|
||||
)
|
||||
|
||||
|
||||
;;; Top-Level messages
|
||||
|
||||
(pi:define-message duration
|
||||
()
|
||||
;; Fields
|
||||
(seconds
|
||||
:index 1 :type cl-protobufs:int64 :kind :scalar :label (:optional) :json-name "seconds")
|
||||
(nanos
|
||||
:index 2 :type cl-protobufs:int32 :kind :scalar :label (:optional) :json-name "nanos"))
|
||||
|
||||
|
||||
(cl:export '(duration
|
||||
nanos
|
||||
seconds))
|
||||
33
examples/meshtastic/lisp/proto/cl-proto/empty.lisp
Normal file
33
examples/meshtastic/lisp/proto/cl-proto/empty.lisp
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
;;; empty.proto.lisp
|
||||
;;;
|
||||
;;; Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
|
||||
(cl:in-package #:common-lisp-user)
|
||||
|
||||
#+sbcl
|
||||
(cl:progn
|
||||
(cl:eval-when (:compile-toplevel) (sb-ext:restrict-compiler-policy 'cl:debug 0 1))
|
||||
(cl:declaim (cl:optimize (sb-c:store-coverage-data 0))))
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(cl:unless (cl:find-package "CL-PROTOBUFS.GOOGLE.PROTOBUF")
|
||||
(cl:defpackage "CL-PROTOBUFS.GOOGLE.PROTOBUF" (:use)
|
||||
(:local-nicknames (#:pi #:cl-protobufs.implementation)))))
|
||||
|
||||
(cl:in-package "CL-PROTOBUFS.GOOGLE.PROTOBUF")
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(pi:define-schema 'empty
|
||||
:syntax :proto3
|
||||
|
||||
:package "google.protobuf")
|
||||
)
|
||||
|
||||
|
||||
;;; Top-Level messages
|
||||
|
||||
(pi:define-message empty
|
||||
())
|
||||
|
||||
|
||||
(cl:export '(empty))
|
||||
38
examples/meshtastic/lisp/proto/cl-proto/field-mask.lisp
Normal file
38
examples/meshtastic/lisp/proto/cl-proto/field-mask.lisp
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
;;; field_mask.proto.lisp
|
||||
;;;
|
||||
;;; Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
|
||||
(cl:in-package #:common-lisp-user)
|
||||
|
||||
#+sbcl
|
||||
(cl:progn
|
||||
(cl:eval-when (:compile-toplevel) (sb-ext:restrict-compiler-policy 'cl:debug 0 1))
|
||||
(cl:declaim (cl:optimize (sb-c:store-coverage-data 0))))
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(cl:unless (cl:find-package "CL-PROTOBUFS.GOOGLE.PROTOBUF")
|
||||
(cl:defpackage "CL-PROTOBUFS.GOOGLE.PROTOBUF" (:use)
|
||||
(:local-nicknames (#:pi #:cl-protobufs.implementation)))))
|
||||
|
||||
(cl:in-package "CL-PROTOBUFS.GOOGLE.PROTOBUF")
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(pi:define-schema 'field_mask
|
||||
:syntax :proto3
|
||||
|
||||
:package "google.protobuf")
|
||||
)
|
||||
|
||||
|
||||
;;; Top-Level messages
|
||||
|
||||
(pi:define-message field-mask
|
||||
()
|
||||
;; Fields
|
||||
(paths
|
||||
:index 1 :type cl:string :kind :scalar :label (:repeated :list) :json-name "paths"))
|
||||
|
||||
|
||||
(cl:export '(field-mask
|
||||
field_mask
|
||||
paths))
|
||||
38
examples/meshtastic/lisp/proto/cl-proto/source-context.lisp
Normal file
38
examples/meshtastic/lisp/proto/cl-proto/source-context.lisp
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
;;; source_context.proto.lisp
|
||||
;;;
|
||||
;;; Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
|
||||
(cl:in-package #:common-lisp-user)
|
||||
|
||||
#+sbcl
|
||||
(cl:progn
|
||||
(cl:eval-when (:compile-toplevel) (sb-ext:restrict-compiler-policy 'cl:debug 0 1))
|
||||
(cl:declaim (cl:optimize (sb-c:store-coverage-data 0))))
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(cl:unless (cl:find-package "CL-PROTOBUFS.GOOGLE.PROTOBUF")
|
||||
(cl:defpackage "CL-PROTOBUFS.GOOGLE.PROTOBUF" (:use)
|
||||
(:local-nicknames (#:pi #:cl-protobufs.implementation)))))
|
||||
|
||||
(cl:in-package "CL-PROTOBUFS.GOOGLE.PROTOBUF")
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(pi:define-schema 'source_context
|
||||
:syntax :proto3
|
||||
|
||||
:package "google.protobuf")
|
||||
)
|
||||
|
||||
|
||||
;;; Top-Level messages
|
||||
|
||||
(pi:define-message source-context
|
||||
()
|
||||
;; Fields
|
||||
(file-name
|
||||
:index 1 :type cl:string :kind :scalar :label (:optional) :json-name "fileName"))
|
||||
|
||||
|
||||
(cl:export '(file-name
|
||||
source-context
|
||||
source_context))
|
||||
83
examples/meshtastic/lisp/proto/cl-proto/struct.lisp
Normal file
83
examples/meshtastic/lisp/proto/cl-proto/struct.lisp
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
;;; struct.proto.lisp
|
||||
;;;
|
||||
;;; Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
|
||||
(cl:in-package #:common-lisp-user)
|
||||
|
||||
#+sbcl
|
||||
(cl:progn
|
||||
(cl:eval-when (:compile-toplevel) (sb-ext:restrict-compiler-policy 'cl:debug 0 1))
|
||||
(cl:declaim (cl:optimize (sb-c:store-coverage-data 0))))
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(cl:unless (cl:find-package "CL-PROTOBUFS.GOOGLE.PROTOBUF")
|
||||
(cl:defpackage "CL-PROTOBUFS.GOOGLE.PROTOBUF" (:use)
|
||||
(:local-nicknames (#:pi #:cl-protobufs.implementation)))))
|
||||
|
||||
(cl:in-package "CL-PROTOBUFS.GOOGLE.PROTOBUF")
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(pi:define-schema 'struct
|
||||
:syntax :proto3
|
||||
|
||||
:package "google.protobuf")
|
||||
)
|
||||
|
||||
|
||||
;;; Top-Level enums
|
||||
|
||||
(pi:define-enum null-value
|
||||
()
|
||||
(:null-value :index 0))
|
||||
|
||||
;;; Top-Level messages
|
||||
|
||||
(pi:define-message struct
|
||||
()
|
||||
;; Nested messages
|
||||
;; Fields
|
||||
(pi:define-map fields
|
||||
:key-type cl:string
|
||||
:value-type value
|
||||
:json-name "fields"
|
||||
:value-kind :message
|
||||
:index 1))
|
||||
|
||||
(pi:define-message value
|
||||
()
|
||||
;; Fields
|
||||
(pi:define-oneof kind ()
|
||||
(null-value
|
||||
:index 1 :type null-value :kind :enum :label (:optional) :json-name "nullValue" :default :null-value)
|
||||
(number-value
|
||||
:index 2 :type cl:double-float :kind :scalar :label (:optional) :json-name "numberValue")
|
||||
(string-value
|
||||
:index 3 :type cl:string :kind :scalar :label (:optional) :json-name "stringValue")
|
||||
(bool-value
|
||||
:index 4 :type cl:boolean :kind :scalar :label (:optional) :json-name "boolValue")
|
||||
(struct-value
|
||||
:index 5 :type struct :kind :message :label (:optional) :json-name "structValue")
|
||||
(list-value
|
||||
:index 6 :type list-value :kind :message :label (:optional) :json-name "listValue")))
|
||||
|
||||
(pi:define-message list-value
|
||||
()
|
||||
;; Fields
|
||||
(values
|
||||
:index 1 :type value :kind :message :label (:repeated :list) :json-name "values"))
|
||||
|
||||
|
||||
(cl:export '(bool-value
|
||||
fields
|
||||
key
|
||||
list-value
|
||||
null-value
|
||||
null-value-int-to-keyword
|
||||
null-value-keyword-to-int
|
||||
number-value
|
||||
string-value
|
||||
struct
|
||||
struct-value
|
||||
struct.fields-entry
|
||||
value
|
||||
values))
|
||||
40
examples/meshtastic/lisp/proto/cl-proto/timestamp.lisp
Normal file
40
examples/meshtastic/lisp/proto/cl-proto/timestamp.lisp
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
;;; timestamp.proto.lisp
|
||||
;;;
|
||||
;;; Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
|
||||
(cl:in-package #:common-lisp-user)
|
||||
|
||||
#+sbcl
|
||||
(cl:progn
|
||||
(cl:eval-when (:compile-toplevel) (sb-ext:restrict-compiler-policy 'cl:debug 0 1))
|
||||
(cl:declaim (cl:optimize (sb-c:store-coverage-data 0))))
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(cl:unless (cl:find-package "CL-PROTOBUFS.GOOGLE.PROTOBUF")
|
||||
(cl:defpackage "CL-PROTOBUFS.GOOGLE.PROTOBUF" (:use)
|
||||
(:local-nicknames (#:pi #:cl-protobufs.implementation)))))
|
||||
|
||||
(cl:in-package "CL-PROTOBUFS.GOOGLE.PROTOBUF")
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(pi:define-schema 'timestamp
|
||||
:syntax :proto3
|
||||
|
||||
:package "google.protobuf")
|
||||
)
|
||||
|
||||
|
||||
;;; Top-Level messages
|
||||
|
||||
(pi:define-message timestamp
|
||||
()
|
||||
;; Fields
|
||||
(seconds
|
||||
:index 1 :type cl-protobufs:int64 :kind :scalar :label (:optional) :json-name "seconds")
|
||||
(nanos
|
||||
:index 2 :type cl-protobufs:int32 :kind :scalar :label (:optional) :json-name "nanos"))
|
||||
|
||||
|
||||
(cl:export '(nanos
|
||||
seconds
|
||||
timestamp))
|
||||
168
examples/meshtastic/lisp/proto/cl-proto/type.lisp
Normal file
168
examples/meshtastic/lisp/proto/cl-proto/type.lisp
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
;;; type.proto.lisp
|
||||
;;;
|
||||
;;; Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
|
||||
(cl:in-package #:common-lisp-user)
|
||||
|
||||
#+sbcl
|
||||
(cl:progn
|
||||
(cl:eval-when (:compile-toplevel) (sb-ext:restrict-compiler-policy 'cl:debug 0 1))
|
||||
(cl:declaim (cl:optimize (sb-c:store-coverage-data 0))))
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(cl:unless (cl:find-package "CL-PROTOBUFS.GOOGLE.PROTOBUF")
|
||||
(cl:defpackage "CL-PROTOBUFS.GOOGLE.PROTOBUF" (:use)
|
||||
(:local-nicknames (#:pi #:cl-protobufs.implementation)))))
|
||||
|
||||
(cl:in-package "CL-PROTOBUFS.GOOGLE.PROTOBUF")
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(pi:define-schema 'type
|
||||
:syntax :proto3
|
||||
|
||||
:package "google.protobuf"))
|
||||
;;:import '("any.proto"
|
||||
;; "source_context.proto")
|
||||
|
||||
|
||||
;;; Top-Level enums
|
||||
|
||||
(pi:define-enum syntax
|
||||
()
|
||||
(:syntax-proto2 :index 0)
|
||||
(:syntax-proto3 :index 1))
|
||||
|
||||
;;; Top-Level messages
|
||||
|
||||
(pi:define-message type
|
||||
()
|
||||
;; Fields
|
||||
(name
|
||||
:index 1 :type cl:string :kind :scalar :label (:optional) :json-name "name")
|
||||
(fields
|
||||
:index 2 :type field :kind :message :label (:repeated :list) :json-name "fields")
|
||||
(oneofs
|
||||
:index 3 :type cl:string :kind :scalar :label (:repeated :list) :json-name "oneofs")
|
||||
(options
|
||||
:index 4 :type option :kind :message :label (:repeated :list) :json-name "options")
|
||||
(source-context
|
||||
:index 5 :type cl-protobufs.google.protobuf::source-context :kind :message :label (:optional) :json-name "sourceContext")
|
||||
(syntax
|
||||
:index 6 :type syntax :kind :enum :label (:optional) :json-name "syntax" :default :syntax-proto2))
|
||||
|
||||
(pi:define-message field
|
||||
()
|
||||
;; Nested enums
|
||||
|
||||
(pi:define-enum field.kind
|
||||
()
|
||||
(:type-unknown :index 0)
|
||||
(:type-double :index 1)
|
||||
(:type-float :index 2)
|
||||
(:type-int64 :index 3)
|
||||
(:type-uint64 :index 4)
|
||||
(:type-int32 :index 5)
|
||||
(:type-fixed64 :index 6)
|
||||
(:type-fixed32 :index 7)
|
||||
(:type-bool :index 8)
|
||||
(:type-string :index 9)
|
||||
(:type-group :index 10)
|
||||
(:type-message :index 11)
|
||||
(:type-bytes :index 12)
|
||||
(:type-uint32 :index 13)
|
||||
(:type-enum :index 14)
|
||||
(:type-sfixed32 :index 15)
|
||||
(:type-sfixed64 :index 16)
|
||||
(:type-sint32 :index 17)
|
||||
(:type-sint64 :index 18))
|
||||
|
||||
(pi:define-enum field.cardinality
|
||||
()
|
||||
(:cardinality-unknown :index 0)
|
||||
(:cardinality-optional :index 1)
|
||||
(:cardinality-required :index 2)
|
||||
(:cardinality-repeated :index 3))
|
||||
;; Fields
|
||||
(kind
|
||||
:index 1 :type field.kind :kind :enum :label (:optional) :json-name "kind" :default :type-unknown)
|
||||
(cardinality
|
||||
:index 2 :type field.cardinality :kind :enum :label (:optional) :json-name "cardinality" :default :cardinality-unknown)
|
||||
(number
|
||||
:index 3 :type cl-protobufs:int32 :kind :scalar :label (:optional) :json-name "number")
|
||||
(name
|
||||
:index 4 :type cl:string :kind :scalar :label (:optional) :json-name "name")
|
||||
(type-url
|
||||
:index 6 :type cl:string :kind :scalar :label (:optional) :json-name "typeUrl")
|
||||
(oneof-index
|
||||
:index 7 :type cl-protobufs:int32 :kind :scalar :label (:optional) :json-name "oneofIndex")
|
||||
(packed
|
||||
:index 8 :type cl:boolean :kind :scalar :label (:optional) :json-name "packed")
|
||||
(options
|
||||
:index 9 :type option :kind :message :label (:repeated :list) :json-name "options")
|
||||
(json-name
|
||||
:index 10 :type cl:string :kind :scalar :label (:optional) :json-name "jsonName")
|
||||
(default-value
|
||||
:index 11 :type cl:string :kind :scalar :label (:optional) :json-name "defaultValue"))
|
||||
|
||||
(pi:define-message enum
|
||||
()
|
||||
;; Fields
|
||||
(name
|
||||
:index 1 :type cl:string :kind :scalar :label (:optional) :json-name "name")
|
||||
(enumvalue
|
||||
:index 2 :type enum-value :kind :message :label (:repeated :list) :json-name "enumvalue")
|
||||
(options
|
||||
:index 3 :type option :kind :message :label (:repeated :list) :json-name "options")
|
||||
(source-context
|
||||
:index 4 :type cl-protobufs.google.protobuf::source-context :kind :message :label (:optional) :json-name "sourceContext")
|
||||
(syntax
|
||||
:index 5 :type syntax :kind :enum :label (:optional) :json-name "syntax" :default :syntax-proto2))
|
||||
|
||||
(pi:define-message enum-value
|
||||
()
|
||||
;; Fields
|
||||
(name
|
||||
:index 1 :type cl:string :kind :scalar :label (:optional) :json-name "name")
|
||||
(number
|
||||
:index 2 :type cl-protobufs:int32 :kind :scalar :label (:optional) :json-name "number")
|
||||
(options
|
||||
:index 3 :type option :kind :message :label (:repeated :list) :json-name "options"))
|
||||
|
||||
(pi:define-message option
|
||||
()
|
||||
;; Fields
|
||||
(name
|
||||
:index 1 :type cl:string :kind :scalar :label (:optional) :json-name "name")
|
||||
(value
|
||||
:index 2 :type cl-protobufs.google.protobuf::any :kind :message :label (:optional) :json-name "value"))
|
||||
|
||||
|
||||
(cl:export '(cardinality
|
||||
default-value
|
||||
enum
|
||||
enum-value
|
||||
enumvalue
|
||||
field
|
||||
field.cardinality
|
||||
field.cardinality-int-to-keyword
|
||||
field.cardinality-keyword-to-int
|
||||
field.kind
|
||||
field.kind-int-to-keyword
|
||||
field.kind-keyword-to-int
|
||||
fields
|
||||
json-name
|
||||
kind
|
||||
name
|
||||
number
|
||||
oneof-index
|
||||
oneofs
|
||||
option
|
||||
options
|
||||
packed
|
||||
source-context
|
||||
syntax
|
||||
syntax-int-to-keyword
|
||||
syntax-keyword-to-int
|
||||
type
|
||||
type-url
|
||||
value))
|
||||
94
examples/meshtastic/lisp/proto/cl-proto/wrappers.lisp
Normal file
94
examples/meshtastic/lisp/proto/cl-proto/wrappers.lisp
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
;;; wrappers.proto.lisp
|
||||
;;;
|
||||
;;; Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
|
||||
(cl:in-package #:common-lisp-user)
|
||||
|
||||
#+sbcl
|
||||
(cl:progn
|
||||
(cl:eval-when (:compile-toplevel) (sb-ext:restrict-compiler-policy 'cl:debug 0 1))
|
||||
(cl:declaim (cl:optimize (sb-c:store-coverage-data 0))))
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(cl:unless (cl:find-package "CL-PROTOBUFS.GOOGLE.PROTOBUF")
|
||||
(cl:defpackage "CL-PROTOBUFS.GOOGLE.PROTOBUF" (:use)
|
||||
(:local-nicknames (#:pi #:cl-protobufs.implementation)))))
|
||||
|
||||
(cl:in-package "CL-PROTOBUFS.GOOGLE.PROTOBUF")
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(pi:define-schema 'wrappers
|
||||
:syntax :proto3
|
||||
|
||||
:package "google.protobuf")
|
||||
)
|
||||
|
||||
|
||||
;;; Top-Level messages
|
||||
|
||||
(pi:define-message double-value
|
||||
()
|
||||
;; Fields
|
||||
(value
|
||||
:index 1 :type cl:double-float :kind :scalar :label (:optional) :json-name "value"))
|
||||
|
||||
(pi:define-message float-value
|
||||
()
|
||||
;; Fields
|
||||
(value
|
||||
:index 1 :type cl:float :kind :scalar :label (:optional) :json-name "value"))
|
||||
|
||||
(pi:define-message int64-value
|
||||
()
|
||||
;; Fields
|
||||
(value
|
||||
:index 1 :type cl-protobufs:int64 :kind :scalar :label (:optional) :json-name "value"))
|
||||
|
||||
(pi:define-message u-int64-value
|
||||
()
|
||||
;; Fields
|
||||
(value
|
||||
:index 1 :type cl-protobufs:uint64 :kind :scalar :label (:optional) :json-name "value"))
|
||||
|
||||
(pi:define-message int32-value
|
||||
()
|
||||
;; Fields
|
||||
(value
|
||||
:index 1 :type cl-protobufs:int32 :kind :scalar :label (:optional) :json-name "value"))
|
||||
|
||||
(pi:define-message u-int32-value
|
||||
()
|
||||
;; Fields
|
||||
(value
|
||||
:index 1 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "value"))
|
||||
|
||||
(pi:define-message bool-value
|
||||
()
|
||||
;; Fields
|
||||
(value
|
||||
:index 1 :type cl:boolean :kind :scalar :label (:optional) :json-name "value"))
|
||||
|
||||
(pi:define-message string-value
|
||||
()
|
||||
;; Fields
|
||||
(value
|
||||
:index 1 :type cl:string :kind :scalar :label (:optional) :json-name "value"))
|
||||
|
||||
(pi:define-message bytes-value
|
||||
()
|
||||
;; Fields
|
||||
(value
|
||||
:index 1 :type cl-protobufs:byte-vector :kind :scalar :label (:optional) :json-name "value"))
|
||||
|
||||
|
||||
(cl:export '(bool-value
|
||||
bytes-value
|
||||
double-value
|
||||
float-value
|
||||
int32-value
|
||||
int64-value
|
||||
string-value
|
||||
u-int32-value
|
||||
u-int64-value
|
||||
value
|
||||
wrappers))
|
||||
180
examples/meshtastic/lisp/proto/meshtastic/admin.lisp
Normal file
180
examples/meshtastic/lisp/proto/meshtastic/admin.lisp
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
;;; meshtastic/admin.proto.lisp
|
||||
;;;
|
||||
;;; Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
|
||||
(cl:in-package #:common-lisp-user)
|
||||
|
||||
#+sbcl
|
||||
(cl:progn
|
||||
(cl:eval-when (:compile-toplevel) (sb-ext:restrict-compiler-policy 'cl:debug 0 1))
|
||||
(cl:declaim (cl:optimize (sb-c:store-coverage-data 0))))
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(cl:unless (cl:find-package "CL-PROTOBUFS.MESHTASTIC")
|
||||
(cl:defpackage "CL-PROTOBUFS.MESHTASTIC" (:use)
|
||||
(:local-nicknames (#:pi #:cl-protobufs.implementation)))))
|
||||
|
||||
(cl:in-package "CL-PROTOBUFS.MESHTASTIC")
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(pi:define-schema 'admin
|
||||
:syntax :proto3
|
||||
|
||||
:package "meshtastic"
|
||||
:import '(;;"meshtastic/channel.proto"
|
||||
;;"meshtastic/config.proto"
|
||||
;;"meshtastic/mesh.proto"
|
||||
;;"meshtastic/module_config.proto"
|
||||
;;"meshtastic/connection_status.proto"
|
||||
))
|
||||
)
|
||||
|
||||
|
||||
;;; Top-Level messages
|
||||
|
||||
(pi:define-message admin-message
|
||||
()
|
||||
;; Nested enums
|
||||
|
||||
(pi:define-enum admin-message.config-type
|
||||
()
|
||||
(:device-config :index 0)
|
||||
(:position-config :index 1)
|
||||
(:power-config :index 2)
|
||||
(:network-config :index 3)
|
||||
(:display-config :index 4)
|
||||
(:lora-config :index 5)
|
||||
(:bluetooth-config :index 6))
|
||||
|
||||
(pi:define-enum admin-message.module-config-type
|
||||
()
|
||||
(:mqtt-config :index 0)
|
||||
(:serial-config :index 1)
|
||||
(:extnotif-config :index 2)
|
||||
(:storeforward-config :index 3)
|
||||
(:rangetest-config :index 4)
|
||||
(:telemetry-config :index 5)
|
||||
(:cannedmsg-config :index 6)
|
||||
(:audio-config :index 7)
|
||||
(:remotehardware-config :index 8))
|
||||
;; Fields
|
||||
(pi:define-oneof payload-variant ()
|
||||
(get-channel-request
|
||||
:index 1 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "getChannelRequest")
|
||||
(get-channel-response
|
||||
:index 2 :type cl-protobufs.meshtastic::channel :kind :message :label (:optional) :json-name "getChannelResponse")
|
||||
(get-owner-request
|
||||
:index 3 :type cl:boolean :kind :scalar :label (:optional) :json-name "getOwnerRequest")
|
||||
(get-owner-response
|
||||
:index 4 :type cl-protobufs.meshtastic::user :kind :message :label (:optional) :json-name "getOwnerResponse")
|
||||
(get-config-request
|
||||
:index 5 :type admin-message.config-type :kind :enum :label (:optional) :json-name "getConfigRequest" :default :device-config)
|
||||
(get-config-response
|
||||
:index 6 :type cl-protobufs.meshtastic::config :kind :message :label (:optional) :json-name "getConfigResponse")
|
||||
(get-module-config-request
|
||||
:index 7 :type admin-message.module-config-type :kind :enum :label (:optional) :json-name "getModuleConfigRequest" :default :mqtt-config)
|
||||
(get-module-config-response
|
||||
:index 8 :type cl-protobufs.meshtastic::module-config :kind :message :label (:optional) :json-name "getModuleConfigResponse")
|
||||
(get-canned-message-module-messages-request
|
||||
:index 10 :type cl:boolean :kind :scalar :label (:optional) :json-name "getCannedMessageModuleMessagesRequest")
|
||||
(get-canned-message-module-messages-response
|
||||
:index 11 :type cl:string :kind :scalar :label (:optional) :json-name "getCannedMessageModuleMessagesResponse")
|
||||
(get-device-metadata-request
|
||||
:index 12 :type cl:boolean :kind :scalar :label (:optional) :json-name "getDeviceMetadataRequest")
|
||||
(get-device-metadata-response
|
||||
:index 13 :type cl-protobufs.meshtastic::device-metadata :kind :message :label (:optional) :json-name "getDeviceMetadataResponse")
|
||||
(get-ringtone-request
|
||||
:index 14 :type cl:boolean :kind :scalar :label (:optional) :json-name "getRingtoneRequest")
|
||||
(get-ringtone-response
|
||||
:index 15 :type cl:string :kind :scalar :label (:optional) :json-name "getRingtoneResponse")
|
||||
(get-device-connection-status-request
|
||||
:index 16 :type cl:boolean :kind :scalar :label (:optional) :json-name "getDeviceConnectionStatusRequest")
|
||||
(get-device-connection-status-response
|
||||
:index 17 :type cl-protobufs.meshtastic::device-connection-status :kind :message :label (:optional) :json-name "getDeviceConnectionStatusResponse")
|
||||
(set-ham-mode
|
||||
:index 18 :type ham-parameters :kind :message :label (:optional) :json-name "setHamMode")
|
||||
(set-owner
|
||||
:index 32 :type cl-protobufs.meshtastic::user :kind :message :label (:optional) :json-name "setOwner")
|
||||
(set-channel
|
||||
:index 33 :type cl-protobufs.meshtastic::channel :kind :message :label (:optional) :json-name "setChannel")
|
||||
(set-config
|
||||
:index 34 :type cl-protobufs.meshtastic::config :kind :message :label (:optional) :json-name "setConfig")
|
||||
(set-module-config
|
||||
:index 35 :type cl-protobufs.meshtastic::module-config :kind :message :label (:optional) :json-name "setModuleConfig")
|
||||
(set-canned-message-module-messages
|
||||
:index 36 :type cl:string :kind :scalar :label (:optional) :json-name "setCannedMessageModuleMessages")
|
||||
(set-ringtone-message
|
||||
:index 37 :type cl:string :kind :scalar :label (:optional) :json-name "setRingtoneMessage")
|
||||
(begin-edit-settings
|
||||
:index 64 :type cl:boolean :kind :scalar :label (:optional) :json-name "beginEditSettings")
|
||||
(commit-edit-settings
|
||||
:index 65 :type cl:boolean :kind :scalar :label (:optional) :json-name "commitEditSettings")
|
||||
(reboot-ota-seconds
|
||||
:index 95 :type cl-protobufs:int32 :kind :scalar :label (:optional) :json-name "rebootOtaSeconds")
|
||||
(exit-simulator
|
||||
:index 96 :type cl:boolean :kind :scalar :label (:optional) :json-name "exitSimulator")
|
||||
(reboot-seconds
|
||||
:index 97 :type cl-protobufs:int32 :kind :scalar :label (:optional) :json-name "rebootSeconds")
|
||||
(shutdown-seconds
|
||||
:index 98 :type cl-protobufs:int32 :kind :scalar :label (:optional) :json-name "shutdownSeconds")
|
||||
(factory-reset
|
||||
:index 99 :type cl-protobufs:int32 :kind :scalar :label (:optional) :json-name "factoryReset")
|
||||
(nodedb-reset
|
||||
:index 100 :type cl-protobufs:int32 :kind :scalar :label (:optional) :json-name "nodedbReset")))
|
||||
|
||||
(pi:define-message ham-parameters
|
||||
()
|
||||
;; Fields
|
||||
(call-sign
|
||||
:index 1 :type cl:string :kind :scalar :label (:optional) :json-name "callSign")
|
||||
(tx-power
|
||||
:index 2 :type cl-protobufs:int32 :kind :scalar :label (:optional) :json-name "txPower")
|
||||
(frequency
|
||||
:index 3 :type cl:float :kind :scalar :label (:optional) :json-name "frequency")
|
||||
(short-name
|
||||
:index 4 :type cl:string :kind :scalar :label (:optional) :json-name "shortName"))
|
||||
|
||||
(cl:export '(admin
|
||||
admin-message
|
||||
admin-message.config-type
|
||||
admin-message.config-type-int-to-keyword
|
||||
admin-message.config-type-keyword-to-int
|
||||
admin-message.module-config-type
|
||||
admin-message.module-config-type-int-to-keyword
|
||||
admin-message.module-config-type-keyword-to-int
|
||||
begin-edit-settings
|
||||
call-sign
|
||||
commit-edit-settings
|
||||
exit-simulator
|
||||
factory-reset
|
||||
frequency
|
||||
get-canned-message-module-messages-request
|
||||
get-canned-message-module-messages-response
|
||||
get-channel-request
|
||||
get-channel-response
|
||||
get-config-request
|
||||
get-config-response
|
||||
get-device-connection-status-request
|
||||
get-device-connection-status-response
|
||||
get-device-metadata-request
|
||||
get-device-metadata-response
|
||||
get-module-config-request
|
||||
get-module-config-response
|
||||
get-owner-request
|
||||
get-owner-response
|
||||
get-ringtone-request
|
||||
get-ringtone-response
|
||||
ham-parameters
|
||||
nodedb-reset
|
||||
reboot-ota-seconds
|
||||
reboot-seconds
|
||||
set-canned-message-module-messages
|
||||
set-channel
|
||||
set-config
|
||||
set-ham-mode
|
||||
set-module-config
|
||||
set-owner
|
||||
set-ringtone-message
|
||||
short-name
|
||||
shutdown-seconds
|
||||
tx-power))
|
||||
43
examples/meshtastic/lisp/proto/meshtastic/apponly.lisp
Normal file
43
examples/meshtastic/lisp/proto/meshtastic/apponly.lisp
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
;;; meshtastic/apponly.proto.lisp
|
||||
;;;
|
||||
;;; Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
|
||||
(cl:in-package #:common-lisp-user)
|
||||
|
||||
#+sbcl
|
||||
(cl:progn
|
||||
(cl:eval-when (:compile-toplevel) (sb-ext:restrict-compiler-policy 'cl:debug 0 1))
|
||||
(cl:declaim (cl:optimize (sb-c:store-coverage-data 0))))
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(cl:unless (cl:find-package "CL-PROTOBUFS.MESHTASTIC")
|
||||
(cl:defpackage "CL-PROTOBUFS.MESHTASTIC" (:use)
|
||||
(:local-nicknames (#:pi #:cl-protobufs.implementation)))))
|
||||
|
||||
(cl:in-package "CL-PROTOBUFS.MESHTASTIC")
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(pi:define-schema 'apponly
|
||||
:syntax :proto3
|
||||
|
||||
:package "meshtastic"
|
||||
:import '(;;"meshtastic/channel.proto"
|
||||
;;"meshtastic/config.proto"
|
||||
))
|
||||
)
|
||||
|
||||
|
||||
;;; Top-Level messages
|
||||
|
||||
(pi:define-message channel-set
|
||||
()
|
||||
;; Fields
|
||||
(settings
|
||||
:index 1 :type cl-protobufs.meshtastic::channel-settings :kind :message :label (:repeated :list) :json-name "settings")
|
||||
(lora-config
|
||||
:index 2 :type cl-protobufs.meshtastic::config.lo-ra-config :kind :message :label (:optional) :json-name "loraConfig"))
|
||||
|
||||
(cl:export '(apponly
|
||||
channel-set
|
||||
lora-config
|
||||
settings))
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
;;; meshtastic/cannedmessages.proto.lisp
|
||||
;;;
|
||||
;;; Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
|
||||
(cl:in-package #:common-lisp-user)
|
||||
|
||||
#+sbcl
|
||||
(cl:progn
|
||||
(cl:eval-when (:compile-toplevel) (sb-ext:restrict-compiler-policy 'cl:debug 0 1))
|
||||
(cl:declaim (cl:optimize (sb-c:store-coverage-data 0))))
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(cl:unless (cl:find-package "CL-PROTOBUFS.MESHTASTIC")
|
||||
(cl:defpackage "CL-PROTOBUFS.MESHTASTIC" (:use)
|
||||
(:local-nicknames (#:pi #:cl-protobufs.implementation)))))
|
||||
|
||||
(cl:in-package "CL-PROTOBUFS.MESHTASTIC")
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(pi:define-schema 'cannedmessages
|
||||
:syntax :proto3
|
||||
|
||||
:package "meshtastic")
|
||||
)
|
||||
|
||||
|
||||
;;; Top-Level messages
|
||||
|
||||
(pi:define-message canned-message-module-config
|
||||
()
|
||||
;; Fields
|
||||
(messages
|
||||
:index 1 :type cl:string :kind :scalar :label (:optional) :json-name "messages"))
|
||||
|
||||
(cl:export '(canned-message-module-config
|
||||
cannedmessages
|
||||
messages))
|
||||
75
examples/meshtastic/lisp/proto/meshtastic/channel.lisp
Normal file
75
examples/meshtastic/lisp/proto/meshtastic/channel.lisp
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
;;; meshtastic/channel.proto.lisp
|
||||
;;;
|
||||
;;; Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
|
||||
(cl:in-package #:common-lisp-user)
|
||||
|
||||
#+sbcl
|
||||
(cl:progn
|
||||
(cl:eval-when (:compile-toplevel) (sb-ext:restrict-compiler-policy 'cl:debug 0 1))
|
||||
(cl:declaim (cl:optimize (sb-c:store-coverage-data 0))))
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(cl:unless (cl:find-package "CL-PROTOBUFS.MESHTASTIC")
|
||||
(cl:defpackage "CL-PROTOBUFS.MESHTASTIC" (:use)
|
||||
(:local-nicknames (#:pi #:cl-protobufs.implementation)))))
|
||||
|
||||
(cl:in-package "CL-PROTOBUFS.MESHTASTIC")
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(pi:define-schema 'channel
|
||||
:syntax :proto3
|
||||
|
||||
:package "meshtastic")
|
||||
)
|
||||
|
||||
|
||||
;;; Top-Level messages
|
||||
|
||||
(pi:define-message channel-settings
|
||||
()
|
||||
;; Fields
|
||||
(channel-num
|
||||
:index 1 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "channelNum")
|
||||
(psk
|
||||
:index 2 :type cl-protobufs:byte-vector :kind :scalar :label (:optional) :json-name "psk")
|
||||
(name
|
||||
:index 3 :type cl:string :kind :scalar :label (:optional) :json-name "name")
|
||||
(id
|
||||
:index 4 :type cl-protobufs:fixed32 :kind :scalar :label (:optional) :json-name "id")
|
||||
(uplink-enabled
|
||||
:index 5 :type cl:boolean :kind :scalar :label (:optional) :json-name "uplinkEnabled")
|
||||
(downlink-enabled
|
||||
:index 6 :type cl:boolean :kind :scalar :label (:optional) :json-name "downlinkEnabled"))
|
||||
|
||||
(pi:define-message channel
|
||||
()
|
||||
;; Nested enums
|
||||
|
||||
(pi:define-enum channel.role
|
||||
()
|
||||
(:disabled :index 0)
|
||||
(:primary :index 1)
|
||||
(:secondary :index 2))
|
||||
;; Fields
|
||||
(index
|
||||
:index 1 :type cl-protobufs:int32 :kind :scalar :label (:optional) :json-name "index")
|
||||
(settings
|
||||
:index 2 :type channel-settings :kind :message :label (:optional) :json-name "settings")
|
||||
(role
|
||||
:index 3 :type channel.role :kind :enum :label (:optional) :json-name "role" :default :disabled))
|
||||
|
||||
(cl:export '(channel
|
||||
channel-num
|
||||
channel-settings
|
||||
channel.role
|
||||
channel.role-int-to-keyword
|
||||
channel.role-keyword-to-int
|
||||
downlink-enabled
|
||||
id
|
||||
index
|
||||
name
|
||||
psk
|
||||
role
|
||||
settings
|
||||
uplink-enabled))
|
||||
56
examples/meshtastic/lisp/proto/meshtastic/clientonly.lisp
Normal file
56
examples/meshtastic/lisp/proto/meshtastic/clientonly.lisp
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
;;; meshtastic/clientonly.proto.lisp
|
||||
;;;
|
||||
;;; Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
|
||||
(cl:in-package #:common-lisp-user)
|
||||
|
||||
#+sbcl
|
||||
(cl:progn
|
||||
(cl:eval-when (:compile-toplevel) (sb-ext:restrict-compiler-policy 'cl:debug 0 1))
|
||||
(cl:declaim (cl:optimize (sb-c:store-coverage-data 0))))
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(cl:unless (cl:find-package "CL-PROTOBUFS.MESHTASTIC")
|
||||
(cl:defpackage "CL-PROTOBUFS.MESHTASTIC" (:use)
|
||||
(:local-nicknames (#:pi #:cl-protobufs.implementation)))))
|
||||
|
||||
(cl:in-package "CL-PROTOBUFS.MESHTASTIC")
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(pi:define-schema 'clientonly
|
||||
:syntax :proto3
|
||||
|
||||
:package "meshtastic"
|
||||
:import '(;;"meshtastic/localonly.proto"
|
||||
))
|
||||
)
|
||||
|
||||
|
||||
;;; Top-Level messages
|
||||
|
||||
(pi:define-message device-profile
|
||||
()
|
||||
;; Fields
|
||||
(pi:define-oneof -long-name (:synthetic-p t)
|
||||
(long-name
|
||||
:index 1 :type cl:string :kind :scalar :label (:optional) :json-name "longName"))
|
||||
(pi:define-oneof -short-name (:synthetic-p t)
|
||||
(short-name
|
||||
:index 2 :type cl:string :kind :scalar :label (:optional) :json-name "shortName"))
|
||||
(pi:define-oneof -channel-url (:synthetic-p t)
|
||||
(channel-url
|
||||
:index 3 :type cl:string :kind :scalar :label (:optional) :json-name "channelUrl"))
|
||||
(pi:define-oneof -config (:synthetic-p t)
|
||||
(config
|
||||
:index 4 :type cl-protobufs.meshtastic::local-config :kind :message :label (:optional) :json-name "config"))
|
||||
(pi:define-oneof -module-config (:synthetic-p t)
|
||||
(module-config
|
||||
:index 5 :type cl-protobufs.meshtastic::local-module-config :kind :message :label (:optional) :json-name "moduleConfig")))
|
||||
|
||||
(cl:export '(channel-url
|
||||
clientonly
|
||||
config
|
||||
device-profile
|
||||
long-name
|
||||
module-config
|
||||
short-name))
|
||||
436
examples/meshtastic/lisp/proto/meshtastic/config.lisp
Normal file
436
examples/meshtastic/lisp/proto/meshtastic/config.lisp
Normal file
|
|
@ -0,0 +1,436 @@
|
|||
;;; meshtastic/config.proto.lisp
|
||||
;;;
|
||||
;;; Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
|
||||
(cl:in-package #:common-lisp-user)
|
||||
|
||||
#+sbcl
|
||||
(cl:progn
|
||||
(cl:eval-when (:compile-toplevel) (sb-ext:restrict-compiler-policy 'cl:debug 0 1))
|
||||
(cl:declaim (cl:optimize (sb-c:store-coverage-data 0))))
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(cl:unless (cl:find-package "CL-PROTOBUFS.MESHTASTIC")
|
||||
(cl:defpackage "CL-PROTOBUFS.MESHTASTIC" (:use)
|
||||
(:local-nicknames (#:pi #:cl-protobufs.implementation)))))
|
||||
|
||||
(cl:in-package "CL-PROTOBUFS.MESHTASTIC")
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(pi:define-schema 'config
|
||||
:syntax :proto3
|
||||
|
||||
:package "meshtastic")
|
||||
)
|
||||
|
||||
|
||||
;;; Top-Level messages
|
||||
|
||||
(pi:define-message config
|
||||
()
|
||||
;; Nested messages
|
||||
|
||||
(pi:define-message config.device-config
|
||||
()
|
||||
;; Nested enums
|
||||
|
||||
(pi:define-enum config.device-config.role
|
||||
()
|
||||
(:client :index 0)
|
||||
(:client-mute :index 1)
|
||||
(:router :index 2)
|
||||
(:router-client :index 3)
|
||||
(:repeater :index 4)
|
||||
(:tracker :index 5)
|
||||
(:sensor :index 6))
|
||||
|
||||
(pi:define-enum config.device-config.rebroadcast-mode
|
||||
()
|
||||
(:all :index 0)
|
||||
(:all-skip-decoding :index 1)
|
||||
(:local-only :index 2))
|
||||
;; Fields
|
||||
(role
|
||||
:index 1 :type config.device-config.role :kind :enum :label (:optional) :json-name "role" :default :client)
|
||||
(serial-enabled
|
||||
:index 2 :type cl:boolean :kind :scalar :label (:optional) :json-name "serialEnabled")
|
||||
(debug-log-enabled
|
||||
:index 3 :type cl:boolean :kind :scalar :label (:optional) :json-name "debugLogEnabled")
|
||||
(button-gpio
|
||||
:index 4 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "buttonGpio")
|
||||
(buzzer-gpio
|
||||
:index 5 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "buzzerGpio")
|
||||
(rebroadcast-mode
|
||||
:index 6 :type config.device-config.rebroadcast-mode :kind :enum :label (:optional) :json-name "rebroadcastMode" :default :all)
|
||||
(node-info-broadcast-secs
|
||||
:index 7 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "nodeInfoBroadcastSecs")
|
||||
(double-tap-as-button-press
|
||||
:index 8 :type cl:boolean :kind :scalar :label (:optional) :json-name "doubleTapAsButtonPress"))
|
||||
|
||||
(pi:define-message config.position-config
|
||||
()
|
||||
;; Nested enums
|
||||
|
||||
(pi:define-enum config.position-config.position-flags
|
||||
()
|
||||
(:unset :index 0)
|
||||
(:altitude :index 1)
|
||||
(:altitude-msl :index 2)
|
||||
(:geoidal-separation :index 4)
|
||||
(:dop :index 8)
|
||||
(:hvdop :index 16)
|
||||
(:satinview :index 32)
|
||||
(:seq-no :index 64)
|
||||
(:timestamp :index 128)
|
||||
(:heading :index 256)
|
||||
(:speed :index 512))
|
||||
;; Fields
|
||||
(position-broadcast-secs
|
||||
:index 1 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "positionBroadcastSecs")
|
||||
(position-broadcast-smart-enabled
|
||||
:index 2 :type cl:boolean :kind :scalar :label (:optional) :json-name "positionBroadcastSmartEnabled")
|
||||
(fixed-position
|
||||
:index 3 :type cl:boolean :kind :scalar :label (:optional) :json-name "fixedPosition")
|
||||
(gps-enabled
|
||||
:index 4 :type cl:boolean :kind :scalar :label (:optional) :json-name "gpsEnabled")
|
||||
(gps-update-interval
|
||||
:index 5 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "gpsUpdateInterval")
|
||||
(gps-attempt-time
|
||||
:index 6 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "gpsAttemptTime")
|
||||
(position-flags
|
||||
:index 7 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "positionFlags")
|
||||
(rx-gpio
|
||||
:index 8 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "rxGpio")
|
||||
(tx-gpio
|
||||
:index 9 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "txGpio")
|
||||
(broadcast-smart-minimum-distance
|
||||
:index 10 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "broadcastSmartMinimumDistance")
|
||||
(broadcast-smart-minimum-interval-secs
|
||||
:index 11 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "broadcastSmartMinimumIntervalSecs"))
|
||||
|
||||
(pi:define-message config.power-config
|
||||
()
|
||||
;; Fields
|
||||
(is-power-saving
|
||||
:index 1 :type cl:boolean :kind :scalar :label (:optional) :json-name "isPowerSaving")
|
||||
(on-battery-shutdown-after-secs
|
||||
:index 2 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "onBatteryShutdownAfterSecs")
|
||||
(adc-multiplier-override
|
||||
:index 3 :type cl:float :kind :scalar :label (:optional) :json-name "adcMultiplierOverride")
|
||||
(wait-bluetooth-secs
|
||||
:index 4 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "waitBluetoothSecs")
|
||||
(mesh-sds-timeout-secs
|
||||
:index 5 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "meshSdsTimeoutSecs")
|
||||
(sds-secs
|
||||
:index 6 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "sdsSecs")
|
||||
(ls-secs
|
||||
:index 7 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "lsSecs")
|
||||
(min-wake-secs
|
||||
:index 8 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "minWakeSecs"))
|
||||
|
||||
(pi:define-message config.network-config
|
||||
()
|
||||
;; Nested enums
|
||||
|
||||
(pi:define-enum config.network-config.address-mode
|
||||
()
|
||||
(:dhcp :index 0)
|
||||
(:static :index 1))
|
||||
;; Nested messages
|
||||
|
||||
(pi:define-message config.network-config.ip-v4-config
|
||||
()
|
||||
;; Fields
|
||||
(ip
|
||||
:index 1 :type cl-protobufs:fixed32 :kind :scalar :label (:optional) :json-name "ip")
|
||||
(gateway
|
||||
:index 2 :type cl-protobufs:fixed32 :kind :scalar :label (:optional) :json-name "gateway")
|
||||
(subnet
|
||||
:index 3 :type cl-protobufs:fixed32 :kind :scalar :label (:optional) :json-name "subnet")
|
||||
(dns
|
||||
:index 4 :type cl-protobufs:fixed32 :kind :scalar :label (:optional) :json-name "dns"))
|
||||
;; Fields
|
||||
(wifi-enabled
|
||||
:index 1 :type cl:boolean :kind :scalar :label (:optional) :json-name "wifiEnabled")
|
||||
(wifi-ssid
|
||||
:index 3 :type cl:string :kind :scalar :label (:optional) :json-name "wifiSsid")
|
||||
(wifi-psk
|
||||
:index 4 :type cl:string :kind :scalar :label (:optional) :json-name "wifiPsk")
|
||||
(ntp-server
|
||||
:index 5 :type cl:string :kind :scalar :label (:optional) :json-name "ntpServer")
|
||||
(eth-enabled
|
||||
:index 6 :type cl:boolean :kind :scalar :label (:optional) :json-name "ethEnabled")
|
||||
(address-mode
|
||||
:index 7 :type config.network-config.address-mode :kind :enum :label (:optional) :json-name "addressMode" :default :dhcp)
|
||||
(ipv4-config
|
||||
:index 8 :type config.network-config.ip-v4-config :kind :message :label (:optional) :json-name "ipv4Config")
|
||||
(rsyslog-server
|
||||
:index 9 :type cl:string :kind :scalar :label (:optional) :json-name "rsyslogServer"))
|
||||
|
||||
(pi:define-message config.display-config
|
||||
()
|
||||
;; Nested enums
|
||||
|
||||
(pi:define-enum config.display-config.gps-coordinate-format
|
||||
()
|
||||
(:dec :index 0)
|
||||
(:dms :index 1)
|
||||
(:utm :index 2)
|
||||
(:mgrs :index 3)
|
||||
(:olc :index 4)
|
||||
(:osgr :index 5))
|
||||
|
||||
(pi:define-enum config.display-config.display-units
|
||||
()
|
||||
(:metric :index 0)
|
||||
(:imperial :index 1))
|
||||
|
||||
(pi:define-enum config.display-config.oled-type
|
||||
()
|
||||
(:oled-auto :index 0)
|
||||
(:oled-ssd1306 :index 1)
|
||||
(:oled-sh1106 :index 2)
|
||||
(:oled-sh1107 :index 3))
|
||||
|
||||
(pi:define-enum config.display-config.display-mode
|
||||
()
|
||||
(:default :index 0)
|
||||
(:twocolor :index 1)
|
||||
(:inverted :index 2)
|
||||
(:color :index 3))
|
||||
;; Fields
|
||||
(screen-on-secs
|
||||
:index 1 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "screenOnSecs")
|
||||
(gps-format
|
||||
:index 2 :type config.display-config.gps-coordinate-format :kind :enum :label (:optional) :json-name "gpsFormat" :default :dec)
|
||||
(auto-screen-carousel-secs
|
||||
:index 3 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "autoScreenCarouselSecs")
|
||||
(compass-north-top
|
||||
:index 4 :type cl:boolean :kind :scalar :label (:optional) :json-name "compassNorthTop")
|
||||
(flip-screen
|
||||
:index 5 :type cl:boolean :kind :scalar :label (:optional) :json-name "flipScreen")
|
||||
(units
|
||||
:index 6 :type config.display-config.display-units :kind :enum :label (:optional) :json-name "units" :default :metric)
|
||||
(oled
|
||||
:index 7 :type config.display-config.oled-type :kind :enum :label (:optional) :json-name "oled" :default :oled-auto)
|
||||
(displaymode
|
||||
:index 8 :type config.display-config.display-mode :kind :enum :label (:optional) :json-name "displaymode" :default :default)
|
||||
(heading-bold
|
||||
:index 9 :type cl:boolean :kind :scalar :label (:optional) :json-name "headingBold")
|
||||
(wake-on-tap-or-motion
|
||||
:index 10 :type cl:boolean :kind :scalar :label (:optional) :json-name "wakeOnTapOrMotion"))
|
||||
|
||||
(pi:define-message config.lo-ra-config
|
||||
()
|
||||
;; Nested enums
|
||||
|
||||
(pi:define-enum config.lo-ra-config.region-code
|
||||
()
|
||||
(:unset :index 0)
|
||||
(:us :index 1)
|
||||
(:eu-433 :index 2)
|
||||
(:eu-868 :index 3)
|
||||
(:cn :index 4)
|
||||
(:jp :index 5)
|
||||
(:anz :index 6)
|
||||
(:kr :index 7)
|
||||
(:tw :index 8)
|
||||
(:ru :index 9)
|
||||
(:in :index 10)
|
||||
(:nz-865 :index 11)
|
||||
(:th :index 12)
|
||||
(:lora-24 :index 13)
|
||||
(:ua-433 :index 14)
|
||||
(:ua-868 :index 15))
|
||||
|
||||
(pi:define-enum config.lo-ra-config.modem-preset
|
||||
()
|
||||
(:long-fast :index 0)
|
||||
(:long-slow :index 1)
|
||||
(:very-long-slow :index 2)
|
||||
(:medium-slow :index 3)
|
||||
(:medium-fast :index 4)
|
||||
(:short-slow :index 5)
|
||||
(:short-fast :index 6)
|
||||
(:long-moderate :index 7))
|
||||
;; Fields
|
||||
(use-preset
|
||||
:index 1 :type cl:boolean :kind :scalar :label (:optional) :json-name "usePreset")
|
||||
(modem-preset
|
||||
:index 2 :type config.lo-ra-config.modem-preset :kind :enum :label (:optional) :json-name "modemPreset" :default :long-fast)
|
||||
(bandwidth
|
||||
:index 3 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "bandwidth")
|
||||
(spread-factor
|
||||
:index 4 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "spreadFactor")
|
||||
(coding-rate
|
||||
:index 5 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "codingRate")
|
||||
(frequency-offset
|
||||
:index 6 :type cl:float :kind :scalar :label (:optional) :json-name "frequencyOffset")
|
||||
(region
|
||||
:index 7 :type config.lo-ra-config.region-code :kind :enum :label (:optional) :json-name "region" :default :unset)
|
||||
(hop-limit
|
||||
:index 8 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "hopLimit")
|
||||
(tx-enabled
|
||||
:index 9 :type cl:boolean :kind :scalar :label (:optional) :json-name "txEnabled")
|
||||
(tx-power
|
||||
:index 10 :type cl-protobufs:int32 :kind :scalar :label (:optional) :json-name "txPower")
|
||||
(channel-num
|
||||
:index 11 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "channelNum")
|
||||
(override-duty-cycle
|
||||
:index 12 :type cl:boolean :kind :scalar :label (:optional) :json-name "overrideDutyCycle")
|
||||
(sx126x-rx-boosted-gain
|
||||
:index 13 :type cl:boolean :kind :scalar :label (:optional) :json-name "sx126xRxBoostedGain")
|
||||
(override-frequency
|
||||
:index 14 :type cl:float :kind :scalar :label (:optional) :json-name "overrideFrequency")
|
||||
(ignore-incoming
|
||||
:index 103 :type cl-protobufs:uint32 :kind :scalar :label (:repeated :list) :json-name "ignoreIncoming"))
|
||||
|
||||
(pi:define-message config.bluetooth-config
|
||||
()
|
||||
;; Nested enums
|
||||
|
||||
(pi:define-enum config.bluetooth-config.pairing-mode
|
||||
()
|
||||
(:random-pin :index 0)
|
||||
(:fixed-pin :index 1)
|
||||
(:no-pin :index 2))
|
||||
;; Fields
|
||||
(enabled
|
||||
:index 1 :type cl:boolean :kind :scalar :label (:optional) :json-name "enabled")
|
||||
(mode
|
||||
:index 2 :type config.bluetooth-config.pairing-mode :kind :enum :label (:optional) :json-name "mode" :default :random-pin)
|
||||
(fixed-pin
|
||||
:index 3 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "fixedPin"))
|
||||
;; Fields
|
||||
(pi:define-oneof payload-variant ()
|
||||
(device
|
||||
:index 1 :type config.device-config :kind :message :label (:optional) :json-name "device")
|
||||
(position
|
||||
:index 2 :type config.position-config :kind :message :label (:optional) :json-name "position")
|
||||
(power
|
||||
:index 3 :type config.power-config :kind :message :label (:optional) :json-name "power")
|
||||
(network
|
||||
:index 4 :type config.network-config :kind :message :label (:optional) :json-name "network")
|
||||
(display
|
||||
:index 5 :type config.display-config :kind :message :label (:optional) :json-name "display")
|
||||
(lora
|
||||
:index 6 :type config.lo-ra-config :kind :message :label (:optional) :json-name "lora")
|
||||
(bluetooth
|
||||
:index 7 :type config.bluetooth-config :kind :message :label (:optional) :json-name "bluetooth")))
|
||||
|
||||
(cl:export '(adc-multiplier-override
|
||||
address-mode
|
||||
auto-screen-carousel-secs
|
||||
bandwidth
|
||||
bluetooth
|
||||
broadcast-smart-minimum-distance
|
||||
broadcast-smart-minimum-interval-secs
|
||||
button-gpio
|
||||
buzzer-gpio
|
||||
channel-num
|
||||
coding-rate
|
||||
compass-north-top
|
||||
config
|
||||
config.bluetooth-config
|
||||
config.bluetooth-config.pairing-mode
|
||||
config.bluetooth-config.pairing-mode-int-to-keyword
|
||||
config.bluetooth-config.pairing-mode-keyword-to-int
|
||||
config.device-config
|
||||
config.device-config.rebroadcast-mode
|
||||
config.device-config.rebroadcast-mode-int-to-keyword
|
||||
config.device-config.rebroadcast-mode-keyword-to-int
|
||||
config.device-config.role
|
||||
config.device-config.role-int-to-keyword
|
||||
config.device-config.role-keyword-to-int
|
||||
config.display-config
|
||||
config.display-config.display-mode
|
||||
config.display-config.display-mode-int-to-keyword
|
||||
config.display-config.display-mode-keyword-to-int
|
||||
config.display-config.display-units
|
||||
config.display-config.display-units-int-to-keyword
|
||||
config.display-config.display-units-keyword-to-int
|
||||
config.display-config.gps-coordinate-format
|
||||
config.display-config.gps-coordinate-format-int-to-keyword
|
||||
config.display-config.gps-coordinate-format-keyword-to-int
|
||||
config.display-config.oled-type
|
||||
config.display-config.oled-type-int-to-keyword
|
||||
config.display-config.oled-type-keyword-to-int
|
||||
config.lo-ra-config
|
||||
config.lo-ra-config.modem-preset
|
||||
config.lo-ra-config.modem-preset-int-to-keyword
|
||||
config.lo-ra-config.modem-preset-keyword-to-int
|
||||
config.lo-ra-config.region-code
|
||||
config.lo-ra-config.region-code-int-to-keyword
|
||||
config.lo-ra-config.region-code-keyword-to-int
|
||||
config.network-config
|
||||
config.network-config.address-mode
|
||||
config.network-config.address-mode-int-to-keyword
|
||||
config.network-config.address-mode-keyword-to-int
|
||||
config.network-config.ip-v4-config
|
||||
config.position-config
|
||||
config.position-config.position-flags
|
||||
config.position-config.position-flags-int-to-keyword
|
||||
config.position-config.position-flags-keyword-to-int
|
||||
config.power-config
|
||||
debug-log-enabled
|
||||
device
|
||||
display
|
||||
displaymode
|
||||
dns
|
||||
double-tap-as-button-press
|
||||
enabled
|
||||
eth-enabled
|
||||
fixed-pin
|
||||
fixed-position
|
||||
flip-screen
|
||||
frequency-offset
|
||||
gateway
|
||||
gps-attempt-time
|
||||
gps-enabled
|
||||
gps-format
|
||||
gps-update-interval
|
||||
heading-bold
|
||||
hop-limit
|
||||
ignore-incoming
|
||||
ip
|
||||
ipv4-config
|
||||
is-power-saving
|
||||
lora
|
||||
ls-secs
|
||||
mesh-sds-timeout-secs
|
||||
min-wake-secs
|
||||
mode
|
||||
modem-preset
|
||||
network
|
||||
node-info-broadcast-secs
|
||||
ntp-server
|
||||
oled
|
||||
on-battery-shutdown-after-secs
|
||||
override-duty-cycle
|
||||
override-frequency
|
||||
position
|
||||
position-broadcast-secs
|
||||
position-broadcast-smart-enabled
|
||||
position-flags
|
||||
power
|
||||
rebroadcast-mode
|
||||
region
|
||||
role
|
||||
rsyslog-server
|
||||
rx-gpio
|
||||
screen-on-secs
|
||||
sds-secs
|
||||
serial-enabled
|
||||
spread-factor
|
||||
subnet
|
||||
sx126x-rx-boosted-gain
|
||||
tx-enabled
|
||||
tx-gpio
|
||||
tx-power
|
||||
units
|
||||
use-preset
|
||||
wait-bluetooth-secs
|
||||
wake-on-tap-or-motion
|
||||
wifi-enabled
|
||||
wifi-psk
|
||||
wifi-ssid))
|
||||
110
examples/meshtastic/lisp/proto/meshtastic/connection-status.lisp
Normal file
110
examples/meshtastic/lisp/proto/meshtastic/connection-status.lisp
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
;;; meshtastic/connection_status.proto.lisp
|
||||
;;;
|
||||
;;; Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
|
||||
(cl:in-package #:common-lisp-user)
|
||||
|
||||
#+sbcl
|
||||
(cl:progn
|
||||
(cl:eval-when (:compile-toplevel) (sb-ext:restrict-compiler-policy 'cl:debug 0 1))
|
||||
(cl:declaim (cl:optimize (sb-c:store-coverage-data 0))))
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(cl:unless (cl:find-package "CL-PROTOBUFS.MESHTASTIC")
|
||||
(cl:defpackage "CL-PROTOBUFS.MESHTASTIC" (:use)
|
||||
(:local-nicknames (#:pi #:cl-protobufs.implementation)))))
|
||||
|
||||
(cl:in-package "CL-PROTOBUFS.MESHTASTIC")
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(pi:define-schema 'connection_status
|
||||
:syntax :proto3
|
||||
|
||||
:package "meshtastic")
|
||||
)
|
||||
|
||||
|
||||
;;; Top-Level messages
|
||||
|
||||
(pi:define-message device-connection-status
|
||||
()
|
||||
;; Fields
|
||||
(pi:define-oneof -wifi (:synthetic-p t)
|
||||
(wifi
|
||||
:index 1 :type wifi-connection-status :kind :message :label (:optional) :json-name "wifi"))
|
||||
(pi:define-oneof -ethernet (:synthetic-p t)
|
||||
(ethernet
|
||||
:index 2 :type ethernet-connection-status :kind :message :label (:optional) :json-name "ethernet"))
|
||||
(pi:define-oneof -bluetooth (:synthetic-p t)
|
||||
(bluetooth
|
||||
:index 3 :type bluetooth-connection-status :kind :message :label (:optional) :json-name "bluetooth"))
|
||||
(pi:define-oneof -serial (:synthetic-p t)
|
||||
(serial
|
||||
:index 4 :type serial-connection-status :kind :message :label (:optional) :json-name "serial")))
|
||||
|
||||
(pi:define-message wifi-connection-status
|
||||
()
|
||||
;; Fields
|
||||
(status
|
||||
:index 1 :type network-connection-status :kind :message :label (:optional) :json-name "status")
|
||||
(ssid
|
||||
:index 2 :type cl:string :kind :scalar :label (:optional) :json-name "ssid")
|
||||
(rssi
|
||||
:index 3 :type cl-protobufs:int32 :kind :scalar :label (:optional) :json-name "rssi"))
|
||||
|
||||
(pi:define-message ethernet-connection-status
|
||||
()
|
||||
;; Fields
|
||||
(status
|
||||
:index 1 :type network-connection-status :kind :message :label (:optional) :json-name "status"))
|
||||
|
||||
(pi:define-message network-connection-status
|
||||
()
|
||||
;; Fields
|
||||
(ip-address
|
||||
:index 1 :type cl-protobufs:fixed32 :kind :scalar :label (:optional) :json-name "ipAddress")
|
||||
(is-connected
|
||||
:index 2 :type cl:boolean :kind :scalar :label (:optional) :json-name "isConnected")
|
||||
(is-mqtt-connected
|
||||
:index 3 :type cl:boolean :kind :scalar :label (:optional) :json-name "isMqttConnected")
|
||||
(is-syslog-connected
|
||||
:index 4 :type cl:boolean :kind :scalar :label (:optional) :json-name "isSyslogConnected"))
|
||||
|
||||
(pi:define-message bluetooth-connection-status
|
||||
()
|
||||
;; Fields
|
||||
(pin
|
||||
:index 1 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "pin")
|
||||
(rssi
|
||||
:index 2 :type cl-protobufs:int32 :kind :scalar :label (:optional) :json-name "rssi")
|
||||
(is-connected
|
||||
:index 3 :type cl:boolean :kind :scalar :label (:optional) :json-name "isConnected"))
|
||||
|
||||
(pi:define-message serial-connection-status
|
||||
()
|
||||
;; Fields
|
||||
(baud
|
||||
:index 1 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "baud")
|
||||
(is-connected
|
||||
:index 2 :type cl:boolean :kind :scalar :label (:optional) :json-name "isConnected"))
|
||||
|
||||
(cl:export '(baud
|
||||
bluetooth
|
||||
bluetooth-connection-status
|
||||
connection_status
|
||||
device-connection-status
|
||||
ethernet
|
||||
ethernet-connection-status
|
||||
ip-address
|
||||
is-connected
|
||||
is-mqtt-connected
|
||||
is-syslog-connected
|
||||
network-connection-status
|
||||
pin
|
||||
rssi
|
||||
serial
|
||||
serial-connection-status
|
||||
ssid
|
||||
status
|
||||
wifi
|
||||
wifi-connection-status))
|
||||
113
examples/meshtastic/lisp/proto/meshtastic/deviceonly.lisp
Normal file
113
examples/meshtastic/lisp/proto/meshtastic/deviceonly.lisp
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
;;; meshtastic/deviceonly.proto.lisp
|
||||
;;;
|
||||
;;; Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
|
||||
(cl:in-package #:common-lisp-user)
|
||||
|
||||
#+sbcl
|
||||
(cl:progn
|
||||
(cl:eval-when (:compile-toplevel) (sb-ext:restrict-compiler-policy 'cl:debug 0 1))
|
||||
(cl:declaim (cl:optimize (sb-c:store-coverage-data 0))))
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(cl:unless (cl:find-package "CL-PROTOBUFS.MESHTASTIC")
|
||||
(cl:defpackage "CL-PROTOBUFS.MESHTASTIC" (:use)
|
||||
(:local-nicknames (#:pi #:cl-protobufs.implementation)))))
|
||||
|
||||
(cl:in-package "CL-PROTOBUFS.MESHTASTIC")
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(pi:define-schema 'deviceonly
|
||||
:syntax :proto3
|
||||
|
||||
:package "meshtastic"
|
||||
:import '(;;"meshtastic/channel.proto"
|
||||
;;"meshtastic/localonly.proto"
|
||||
;;"meshtastic/mesh.proto"
|
||||
))
|
||||
)
|
||||
|
||||
|
||||
;;; Top-Level enums
|
||||
|
||||
(pi:define-enum screen-fonts
|
||||
()
|
||||
(:font-small :index 0)
|
||||
(:font-medium :index 1)
|
||||
(:font-large :index 2))
|
||||
|
||||
;;; Top-Level messages
|
||||
|
||||
(pi:define-message device-state
|
||||
()
|
||||
;; Fields
|
||||
(my-node
|
||||
:index 2 :type cl-protobufs.meshtastic::my-node-info :kind :message :label (:optional) :json-name "myNode")
|
||||
(owner
|
||||
:index 3 :type cl-protobufs.meshtastic::user :kind :message :label (:optional) :json-name "owner")
|
||||
(node-db
|
||||
:index 4 :type cl-protobufs.meshtastic::node-info :kind :message :label (:repeated :list) :json-name "nodeDb")
|
||||
(receive-queue
|
||||
:index 5 :type cl-protobufs.meshtastic::mesh-packet :kind :message :label (:repeated :list) :json-name "receiveQueue")
|
||||
(version
|
||||
:index 8 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "version")
|
||||
(rx-text-message
|
||||
:index 7 :type cl-protobufs.meshtastic::mesh-packet :kind :message :label (:optional) :json-name "rxTextMessage")
|
||||
(no-save
|
||||
:index 9 :type cl:boolean :kind :scalar :label (:optional) :json-name "noSave")
|
||||
(did-gps-reset
|
||||
:index 11 :type cl:boolean :kind :scalar :label (:optional) :json-name "didGpsReset"))
|
||||
|
||||
(pi:define-message channel-file
|
||||
()
|
||||
;; Fields
|
||||
(channels
|
||||
:index 1 :type cl-protobufs.meshtastic::channel :kind :message :label (:repeated :list) :json-name "channels")
|
||||
(version
|
||||
:index 2 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "version"))
|
||||
|
||||
(pi:define-message oem-store
|
||||
(
|
||||
:name "OEMStore")
|
||||
;; Fields
|
||||
(oem-icon-width
|
||||
:index 1 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "oemIconWidth")
|
||||
(oem-icon-height
|
||||
:index 2 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "oemIconHeight")
|
||||
(oem-icon-bits
|
||||
:index 3 :type cl-protobufs:byte-vector :kind :scalar :label (:optional) :json-name "oemIconBits")
|
||||
(oem-font
|
||||
:index 4 :type screen-fonts :kind :enum :label (:optional) :json-name "oemFont" :default :font-small)
|
||||
(oem-text
|
||||
:index 5 :type cl:string :kind :scalar :label (:optional) :json-name "oemText")
|
||||
(oem-aes-key
|
||||
:index 6 :type cl-protobufs:byte-vector :kind :scalar :label (:optional) :json-name "oemAesKey")
|
||||
(oem-local-config
|
||||
:index 7 :type cl-protobufs.meshtastic::local-config :kind :message :label (:optional) :json-name "oemLocalConfig")
|
||||
(oem-local-module-config
|
||||
:index 8 :type cl-protobufs.meshtastic::local-module-config :kind :message :label (:optional) :json-name "oemLocalModuleConfig"))
|
||||
|
||||
(cl:export '(channel-file
|
||||
channels
|
||||
device-state
|
||||
deviceonly
|
||||
did-gps-reset
|
||||
my-node
|
||||
no-save
|
||||
node-db
|
||||
oem-aes-key
|
||||
oem-font
|
||||
oem-icon-bits
|
||||
oem-icon-height
|
||||
oem-icon-width
|
||||
oem-local-config
|
||||
oem-local-module-config
|
||||
oem-store
|
||||
oem-text
|
||||
owner
|
||||
receive-queue
|
||||
rx-text-message
|
||||
screen-fonts
|
||||
screen-fonts-int-to-keyword
|
||||
screen-fonts-keyword-to-int
|
||||
version))
|
||||
95
examples/meshtastic/lisp/proto/meshtastic/localonly.lisp
Normal file
95
examples/meshtastic/lisp/proto/meshtastic/localonly.lisp
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
;;; meshtastic/localonly.proto.lisp
|
||||
;;;
|
||||
;;; Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
|
||||
(cl:in-package #:common-lisp-user)
|
||||
|
||||
#+sbcl
|
||||
(cl:progn
|
||||
(cl:eval-when (:compile-toplevel) (sb-ext:restrict-compiler-policy 'cl:debug 0 1))
|
||||
(cl:declaim (cl:optimize (sb-c:store-coverage-data 0))))
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(cl:unless (cl:find-package "CL-PROTOBUFS.MESHTASTIC")
|
||||
(cl:defpackage "CL-PROTOBUFS.MESHTASTIC" (:use)
|
||||
(:local-nicknames (#:pi #:cl-protobufs.implementation)))))
|
||||
|
||||
(cl:in-package "CL-PROTOBUFS.MESHTASTIC")
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(pi:define-schema 'localonly
|
||||
:syntax :proto3
|
||||
|
||||
:package "meshtastic"
|
||||
:import '(;;"meshtastic/config.proto"
|
||||
;;"meshtastic/module_config.proto"
|
||||
))
|
||||
)
|
||||
|
||||
|
||||
;;; Top-Level messages
|
||||
|
||||
(pi:define-message local-config
|
||||
()
|
||||
;; Fields
|
||||
(device
|
||||
:index 1 :type cl-protobufs.meshtastic::config.device-config :kind :message :label (:optional) :json-name "device")
|
||||
(position
|
||||
:index 2 :type cl-protobufs.meshtastic::config.position-config :kind :message :label (:optional) :json-name "position")
|
||||
(power
|
||||
:index 3 :type cl-protobufs.meshtastic::config.power-config :kind :message :label (:optional) :json-name "power")
|
||||
(network
|
||||
:index 4 :type cl-protobufs.meshtastic::config.network-config :kind :message :label (:optional) :json-name "network")
|
||||
(display
|
||||
:index 5 :type cl-protobufs.meshtastic::config.display-config :kind :message :label (:optional) :json-name "display")
|
||||
(lora
|
||||
:index 6 :type cl-protobufs.meshtastic::config.lo-ra-config :kind :message :label (:optional) :json-name "lora")
|
||||
(bluetooth
|
||||
:index 7 :type cl-protobufs.meshtastic::config.bluetooth-config :kind :message :label (:optional) :json-name "bluetooth")
|
||||
(version
|
||||
:index 8 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "version"))
|
||||
|
||||
(pi:define-message local-module-config
|
||||
()
|
||||
;; Fields
|
||||
(mqtt
|
||||
:index 1 :type cl-protobufs.meshtastic::module-config.mqtt-config :kind :message :label (:optional) :json-name "mqtt")
|
||||
(serial
|
||||
:index 2 :type cl-protobufs.meshtastic::module-config.serial-config :kind :message :label (:optional) :json-name "serial")
|
||||
(external-notification
|
||||
:index 3 :type cl-protobufs.meshtastic::module-config.external-notification-config :kind :message :label (:optional) :json-name "externalNotification")
|
||||
(store-forward
|
||||
:index 4 :type cl-protobufs.meshtastic::module-config.store-forward-config :kind :message :label (:optional) :json-name "storeForward")
|
||||
(range-test
|
||||
:index 5 :type cl-protobufs.meshtastic::module-config.range-test-config :kind :message :label (:optional) :json-name "rangeTest")
|
||||
(telemetry
|
||||
:index 6 :type cl-protobufs.meshtastic::module-config.telemetry-config :kind :message :label (:optional) :json-name "telemetry")
|
||||
(canned-message
|
||||
:index 7 :type cl-protobufs.meshtastic::module-config.canned-message-config :kind :message :label (:optional) :json-name "cannedMessage")
|
||||
(audio
|
||||
:index 9 :type cl-protobufs.meshtastic::module-config.audio-config :kind :message :label (:optional) :json-name "audio")
|
||||
(remote-hardware
|
||||
:index 10 :type cl-protobufs.meshtastic::module-config.remote-hardware-config :kind :message :label (:optional) :json-name "remoteHardware")
|
||||
(version
|
||||
:index 8 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "version"))
|
||||
|
||||
(cl:export '(audio
|
||||
bluetooth
|
||||
canned-message
|
||||
device
|
||||
display
|
||||
external-notification
|
||||
local-config
|
||||
local-module-config
|
||||
localonly
|
||||
lora
|
||||
mqtt
|
||||
network
|
||||
position
|
||||
power
|
||||
range-test
|
||||
remote-hardware
|
||||
serial
|
||||
store-forward
|
||||
telemetry
|
||||
version))
|
||||
624
examples/meshtastic/lisp/proto/meshtastic/mesh.lisp
Normal file
624
examples/meshtastic/lisp/proto/meshtastic/mesh.lisp
Normal file
|
|
@ -0,0 +1,624 @@
|
|||
;;; meshtastic/mesh.proto.lisp
|
||||
;;;
|
||||
;;; Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
|
||||
(cl:in-package #:common-lisp-user)
|
||||
|
||||
#+sbcl
|
||||
(cl:progn
|
||||
(cl:eval-when (:compile-toplevel) (sb-ext:restrict-compiler-policy 'cl:debug 0 1))
|
||||
(cl:declaim (cl:optimize (sb-c:store-coverage-data 0))))
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(cl:unless (cl:find-package "CL-PROTOBUFS.MESHTASTIC")
|
||||
(cl:defpackage "CL-PROTOBUFS.MESHTASTIC" (:use)
|
||||
(:local-nicknames (#:pi #:cl-protobufs.implementation)))))
|
||||
|
||||
(cl:in-package "CL-PROTOBUFS.MESHTASTIC")
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(pi:define-schema 'mesh
|
||||
:syntax :proto3
|
||||
|
||||
:package "meshtastic"
|
||||
:import '(;;"meshtastic/channel.proto"
|
||||
;;"meshtastic/config.proto"
|
||||
;;"meshtastic/module_config.proto"
|
||||
;;"meshtastic/portnums.proto"
|
||||
;;"meshtastic/telemetry.proto"
|
||||
;;"meshtastic/xmodem.proto"
|
||||
))
|
||||
)
|
||||
|
||||
|
||||
;;; Top-Level enums
|
||||
|
||||
(pi:define-enum hardware-model
|
||||
()
|
||||
(:unset :index 0)
|
||||
(:tlora-v2 :index 1)
|
||||
(:tlora-v1 :index 2)
|
||||
(:tlora-v2-1-1p6 :index 3)
|
||||
(:tbeam :index 4)
|
||||
(:heltec-v2-0 :index 5)
|
||||
(:tbeam-v0p7 :index 6)
|
||||
(:t-echo :index 7)
|
||||
(:tlora-v1-1p3 :index 8)
|
||||
(:rak4631 :index 9)
|
||||
(:heltec-v2-1 :index 10)
|
||||
(:heltec-v1 :index 11)
|
||||
(:lilygo-tbeam-s3-core :index 12)
|
||||
(:rak11200 :index 13)
|
||||
(:nano-g1 :index 14)
|
||||
(:tlora-v2-1-1p8 :index 15)
|
||||
(:tlora-t3-s3 :index 16)
|
||||
(:nano-g1-explorer :index 17)
|
||||
(:station-g1 :index 25)
|
||||
(:lora-relay-v1 :index 32)
|
||||
(:nrf52840dk :index 33)
|
||||
(:ppr :index 34)
|
||||
(:genieblocks :index 35)
|
||||
(:nrf52-unknown :index 36)
|
||||
(:portduino :index 37)
|
||||
(:android-sim :index 38)
|
||||
(:diy-v1 :index 39)
|
||||
(:nrf52840-pca10059 :index 40)
|
||||
(:dr-dev :index 41)
|
||||
(:m5stack :index 42)
|
||||
(:heltec-v3 :index 43)
|
||||
(:heltec-wsl-v3 :index 44)
|
||||
(:betafpv-2400-tx :index 45)
|
||||
(:betafpv-900-nano-tx :index 46)
|
||||
(:private-hw :index 255))
|
||||
|
||||
(pi:define-enum constants
|
||||
()
|
||||
(:zero :index 0)
|
||||
(:data-payload-len :index 237))
|
||||
|
||||
(pi:define-enum critical-error-code
|
||||
()
|
||||
(:none :index 0)
|
||||
(:tx-watchdog :index 1)
|
||||
(:sleep-enter-wait :index 2)
|
||||
(:no-radio :index 3)
|
||||
(:unspecified :index 4)
|
||||
(:ublox-unit-failed :index 5)
|
||||
(:no-axp192 :index 6)
|
||||
(:invalid-radio-setting :index 7)
|
||||
(:transmit-failed :index 8)
|
||||
(:brownout :index 9)
|
||||
(:sx1262-failure :index 10)
|
||||
(:radio-spi-bug :index 11))
|
||||
|
||||
;;; Top-Level messages
|
||||
|
||||
(pi:define-message position
|
||||
()
|
||||
;; Nested enums
|
||||
|
||||
(pi:define-enum position.loc-source
|
||||
()
|
||||
(:loc-unset :index 0)
|
||||
(:loc-manual :index 1)
|
||||
(:loc-internal :index 2)
|
||||
(:loc-external :index 3))
|
||||
|
||||
(pi:define-enum position.alt-source
|
||||
()
|
||||
(:alt-unset :index 0)
|
||||
(:alt-manual :index 1)
|
||||
(:alt-internal :index 2)
|
||||
(:alt-external :index 3)
|
||||
(:alt-barometric :index 4))
|
||||
;; Fields
|
||||
(latitude-i
|
||||
:index 1 :type cl-protobufs:sfixed32 :kind :scalar :label (:optional) :json-name "latitudeI")
|
||||
(longitude-i
|
||||
:index 2 :type cl-protobufs:sfixed32 :kind :scalar :label (:optional) :json-name "longitudeI")
|
||||
(altitude
|
||||
:index 3 :type cl-protobufs:int32 :kind :scalar :label (:optional) :json-name "altitude")
|
||||
(time
|
||||
:index 4 :type cl-protobufs:fixed32 :kind :scalar :label (:optional) :json-name "time")
|
||||
(location-source
|
||||
:index 5 :type position.loc-source :kind :enum :label (:optional) :json-name "locationSource" :default :loc-unset)
|
||||
(altitude-source
|
||||
:index 6 :type position.alt-source :kind :enum :label (:optional) :json-name "altitudeSource" :default :alt-unset)
|
||||
(timestamp
|
||||
:index 7 :type cl-protobufs:fixed32 :kind :scalar :label (:optional) :json-name "timestamp")
|
||||
(timestamp-millis-adjust
|
||||
:index 8 :type cl-protobufs:int32 :kind :scalar :label (:optional) :json-name "timestampMillisAdjust")
|
||||
(altitude-hae
|
||||
:index 9 :type cl-protobufs:sint32 :kind :scalar :label (:optional) :json-name "altitudeHae")
|
||||
(altitude-geoidal-separation
|
||||
:index 10 :type cl-protobufs:sint32 :kind :scalar :label (:optional) :json-name "altitudeGeoidalSeparation")
|
||||
(pdop
|
||||
:index 11 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "PDOP")
|
||||
(hdop
|
||||
:index 12 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "HDOP")
|
||||
(vdop
|
||||
:index 13 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "VDOP")
|
||||
(gps-accuracy
|
||||
:index 14 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "gpsAccuracy")
|
||||
(ground-speed
|
||||
:index 15 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "groundSpeed")
|
||||
(ground-track
|
||||
:index 16 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "groundTrack")
|
||||
(fix-quality
|
||||
:index 17 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "fixQuality")
|
||||
(fix-type
|
||||
:index 18 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "fixType")
|
||||
(sats-in-view
|
||||
:index 19 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "satsInView")
|
||||
(sensor-id
|
||||
:index 20 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "sensorId")
|
||||
(next-update
|
||||
:index 21 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "nextUpdate")
|
||||
(seq-number
|
||||
:index 22 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "seqNumber"))
|
||||
|
||||
(pi:define-message user
|
||||
()
|
||||
;; Fields
|
||||
(id
|
||||
:index 1 :type cl:string :kind :scalar :label (:optional) :json-name "id")
|
||||
(long-name
|
||||
:index 2 :type cl:string :kind :scalar :label (:optional) :json-name "longName")
|
||||
(short-name
|
||||
:index 3 :type cl:string :kind :scalar :label (:optional) :json-name "shortName")
|
||||
(macaddr
|
||||
:index 4 :type cl-protobufs:byte-vector :kind :scalar :label (:optional) :json-name "macaddr")
|
||||
(hw-model
|
||||
:index 5 :type hardware-model :kind :enum :label (:optional) :json-name "hwModel" :default :unset)
|
||||
(is-licensed
|
||||
:index 6 :type cl:boolean :kind :scalar :label (:optional) :json-name "isLicensed"))
|
||||
|
||||
(pi:define-message route-discovery
|
||||
()
|
||||
;; Fields
|
||||
(route
|
||||
:index 1 :type cl-protobufs:fixed32 :kind :scalar :label (:repeated :list) :json-name "route"))
|
||||
|
||||
(pi:define-message routing
|
||||
()
|
||||
;; Nested enums
|
||||
|
||||
(pi:define-enum routing.error
|
||||
()
|
||||
(:none :index 0)
|
||||
(:no-route :index 1)
|
||||
(:got-nak :index 2)
|
||||
(:timeout :index 3)
|
||||
(:no-interface :index 4)
|
||||
(:max-retransmit :index 5)
|
||||
(:no-channel :index 6)
|
||||
(:too-large :index 7)
|
||||
(:no-response :index 8)
|
||||
(:duty-cycle-limit :index 9)
|
||||
(:bad-request :index 32)
|
||||
(:not-authorized :index 33))
|
||||
;; Fields
|
||||
(pi:define-oneof variant ()
|
||||
(route-request
|
||||
:index 1 :type route-discovery :kind :message :label (:optional) :json-name "routeRequest")
|
||||
(route-reply
|
||||
:index 2 :type route-discovery :kind :message :label (:optional) :json-name "routeReply")
|
||||
(error-reason
|
||||
:index 3 :type routing.error :kind :enum :label (:optional) :json-name "errorReason" :default :none)))
|
||||
|
||||
(pi:define-message data
|
||||
()
|
||||
;; Fields
|
||||
(portnum
|
||||
:index 1 :type cl-protobufs.meshtastic::port-num :kind :enum :label (:optional) :json-name "portnum" :default :unknown-app)
|
||||
(payload
|
||||
:index 2 :type cl-protobufs:byte-vector :kind :scalar :label (:optional) :json-name "payload")
|
||||
(want-response
|
||||
:index 3 :type cl:boolean :kind :scalar :label (:optional) :json-name "wantResponse")
|
||||
(dest
|
||||
:index 4 :type cl-protobufs:fixed32 :kind :scalar :label (:optional) :json-name "dest")
|
||||
(source
|
||||
:index 5 :type cl-protobufs:fixed32 :kind :scalar :label (:optional) :json-name "source")
|
||||
(request-id
|
||||
:index 6 :type cl-protobufs:fixed32 :kind :scalar :label (:optional) :json-name "requestId")
|
||||
(reply-id
|
||||
:index 7 :type cl-protobufs:fixed32 :kind :scalar :label (:optional) :json-name "replyId")
|
||||
(emoji
|
||||
:index 8 :type cl-protobufs:fixed32 :kind :scalar :label (:optional) :json-name "emoji"))
|
||||
|
||||
(pi:define-message waypoint
|
||||
()
|
||||
;; Fields
|
||||
(id
|
||||
:index 1 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "id")
|
||||
(latitude-i
|
||||
:index 2 :type cl-protobufs:sfixed32 :kind :scalar :label (:optional) :json-name "latitudeI")
|
||||
(longitude-i
|
||||
:index 3 :type cl-protobufs:sfixed32 :kind :scalar :label (:optional) :json-name "longitudeI")
|
||||
(expire
|
||||
:index 4 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "expire")
|
||||
(locked-to
|
||||
:index 5 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "lockedTo")
|
||||
(name
|
||||
:index 6 :type cl:string :kind :scalar :label (:optional) :json-name "name")
|
||||
(description
|
||||
:index 7 :type cl:string :kind :scalar :label (:optional) :json-name "description")
|
||||
(icon
|
||||
:index 8 :type cl-protobufs:fixed32 :kind :scalar :label (:optional) :json-name "icon"))
|
||||
|
||||
(pi:define-message mesh-packet
|
||||
()
|
||||
;; Nested enums
|
||||
|
||||
(pi:define-enum mesh-packet.priority
|
||||
()
|
||||
(:unset :index 0)
|
||||
(:min :index 1)
|
||||
(:background :index 10)
|
||||
(:default :index 64)
|
||||
(:reliable :index 70)
|
||||
(:ack :index 120)
|
||||
(:max :index 127))
|
||||
|
||||
(pi:define-enum mesh-packet.delayed
|
||||
()
|
||||
(:no-delay :index 0)
|
||||
(:delayed-broadcast :index 1)
|
||||
(:delayed-direct :index 2))
|
||||
;; Fields
|
||||
(pi:define-oneof payload-variant ()
|
||||
(decoded
|
||||
:index 4 :type data :kind :message :label (:optional) :json-name "decoded")
|
||||
(encrypted
|
||||
:index 5 :type cl-protobufs:byte-vector :kind :scalar :label (:optional) :json-name "encrypted"))
|
||||
(from
|
||||
:index 1 :type cl-protobufs:fixed32 :kind :scalar :label (:optional) :json-name "from")
|
||||
(to
|
||||
:index 2 :type cl-protobufs:fixed32 :kind :scalar :label (:optional) :json-name "to")
|
||||
(channel
|
||||
:index 3 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "channel")
|
||||
(id
|
||||
:index 6 :type cl-protobufs:fixed32 :kind :scalar :label (:optional) :json-name "id")
|
||||
(rx-time
|
||||
:index 7 :type cl-protobufs:fixed32 :kind :scalar :label (:optional) :json-name "rxTime")
|
||||
(rx-snr
|
||||
:index 8 :type cl:float :kind :scalar :label (:optional) :json-name "rxSnr")
|
||||
(hop-limit
|
||||
:index 9 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "hopLimit")
|
||||
(want-ack
|
||||
:index 10 :type cl:boolean :kind :scalar :label (:optional) :json-name "wantAck")
|
||||
(priority
|
||||
:index 11 :type mesh-packet.priority :kind :enum :label (:optional) :json-name "priority" :default :unset)
|
||||
(rx-rssi
|
||||
:index 12 :type cl-protobufs:int32 :kind :scalar :label (:optional) :json-name "rxRssi")
|
||||
(delayed
|
||||
:index 13 :type mesh-packet.delayed :kind :enum :label (:optional) :json-name "delayed" :default :no-delay))
|
||||
|
||||
(pi:define-message node-info
|
||||
()
|
||||
;; Fields
|
||||
(num
|
||||
:index 1 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "num")
|
||||
(user
|
||||
:index 2 :type user :kind :message :label (:optional) :json-name "user")
|
||||
(position
|
||||
:index 3 :type position :kind :message :label (:optional) :json-name "position")
|
||||
(snr
|
||||
:index 4 :type cl:float :kind :scalar :label (:optional) :json-name "snr")
|
||||
(last-heard
|
||||
:index 5 :type cl-protobufs:fixed32 :kind :scalar :label (:optional) :json-name "lastHeard")
|
||||
(device-metrics
|
||||
:index 6 :type cl-protobufs.meshtastic::device-metrics :kind :message :label (:optional) :json-name "deviceMetrics")
|
||||
(channel
|
||||
:index 7 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "channel"))
|
||||
|
||||
(pi:define-message my-node-info
|
||||
()
|
||||
;; Fields
|
||||
(my-node-num
|
||||
:index 1 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "myNodeNum")
|
||||
(has-gps
|
||||
:index 2 :type cl:boolean :kind :scalar :label (:optional) :json-name "hasGps")
|
||||
(max-channels
|
||||
:index 3 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "maxChannels")
|
||||
(firmware-version
|
||||
:index 4 :type cl:string :kind :scalar :label (:optional) :json-name "firmwareVersion")
|
||||
(error-code
|
||||
:index 5 :type critical-error-code :kind :enum :label (:optional) :json-name "errorCode" :default :none)
|
||||
(error-address
|
||||
:index 6 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "errorAddress")
|
||||
(error-count
|
||||
:index 7 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "errorCount")
|
||||
(reboot-count
|
||||
:index 8 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "rebootCount")
|
||||
(bitrate
|
||||
:index 9 :type cl:float :kind :scalar :label (:optional) :json-name "bitrate")
|
||||
(message-timeout-msec
|
||||
:index 10 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "messageTimeoutMsec")
|
||||
(min-app-version
|
||||
:index 11 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "minAppVersion")
|
||||
(air-period-tx
|
||||
:index 12 :type cl-protobufs:uint32 :kind :scalar :label (:repeated :list) :json-name "airPeriodTx")
|
||||
(air-period-rx
|
||||
:index 13 :type cl-protobufs:uint32 :kind :scalar :label (:repeated :list) :json-name "airPeriodRx")
|
||||
(has-wifi
|
||||
:index 14 :type cl:boolean :kind :scalar :label (:optional) :json-name "hasWifi")
|
||||
(channel-utilization
|
||||
:index 15 :type cl:float :kind :scalar :label (:optional) :json-name "channelUtilization")
|
||||
(air-util-tx
|
||||
:index 16 :type cl:float :kind :scalar :label (:optional) :json-name "airUtilTx"))
|
||||
|
||||
(pi:define-message log-record
|
||||
()
|
||||
;; Nested enums
|
||||
|
||||
(pi:define-enum log-record.level
|
||||
()
|
||||
(:unset :index 0)
|
||||
(:critical :index 50)
|
||||
(:error :index 40)
|
||||
(:warning :index 30)
|
||||
(:info :index 20)
|
||||
(:debug :index 10)
|
||||
(:trace :index 5))
|
||||
;; Fields
|
||||
(message
|
||||
:index 1 :type cl:string :kind :scalar :label (:optional) :json-name "message")
|
||||
(time
|
||||
:index 2 :type cl-protobufs:fixed32 :kind :scalar :label (:optional) :json-name "time")
|
||||
(source
|
||||
:index 3 :type cl:string :kind :scalar :label (:optional) :json-name "source")
|
||||
(level
|
||||
:index 4 :type log-record.level :kind :enum :label (:optional) :json-name "level" :default :unset))
|
||||
|
||||
(pi:define-message queue-status
|
||||
()
|
||||
;; Fields
|
||||
(res
|
||||
:index 1 :type cl-protobufs:int32 :kind :scalar :label (:optional) :json-name "res")
|
||||
(free
|
||||
:index 2 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "free")
|
||||
(maxlen
|
||||
:index 3 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "maxlen")
|
||||
(mesh-packet-id
|
||||
:index 4 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "meshPacketId"))
|
||||
|
||||
(pi:define-message from-radio
|
||||
()
|
||||
;; Fields
|
||||
(pi:define-oneof payload-variant ()
|
||||
(packet
|
||||
:index 2 :type mesh-packet :kind :message :label (:optional) :json-name "packet")
|
||||
(my-info
|
||||
:index 3 :type my-node-info :kind :message :label (:optional) :json-name "myInfo")
|
||||
(node-info
|
||||
:index 4 :type node-info :kind :message :label (:optional) :json-name "nodeInfo")
|
||||
(config
|
||||
:index 5 :type cl-protobufs.meshtastic::config :kind :message :label (:optional) :json-name "config")
|
||||
(log-record
|
||||
:index 6 :type log-record :kind :message :label (:optional) :json-name "logRecord")
|
||||
(config-complete-id
|
||||
:index 7 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "configCompleteId")
|
||||
(rebooted
|
||||
:index 8 :type cl:boolean :kind :scalar :label (:optional) :json-name "rebooted")
|
||||
(module-config
|
||||
:index 9 :type cl-protobufs.meshtastic::module-config :kind :message :label (:optional) :json-name "moduleConfig")
|
||||
(channel
|
||||
:index 10 :type cl-protobufs.meshtastic::channel :kind :message :label (:optional) :json-name "channel")
|
||||
(queue-status
|
||||
:index 11 :type queue-status :kind :message :label (:optional) :json-name "queueStatus")
|
||||
(xmodem-packet
|
||||
:index 12 :type cl-protobufs.meshtastic::x-modem :kind :message :label (:optional) :json-name "xmodemPacket")
|
||||
(metadata
|
||||
:index 13 :type device-metadata :kind :message :label (:optional) :json-name "metadata"))
|
||||
(id
|
||||
:index 1 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "id"))
|
||||
|
||||
(pi:define-message to-radio
|
||||
()
|
||||
;; Fields
|
||||
(pi:define-oneof payload-variant ()
|
||||
(packet
|
||||
:index 1 :type mesh-packet :kind :message :label (:optional) :json-name "packet")
|
||||
(want-config-id
|
||||
:index 3 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "wantConfigId")
|
||||
(disconnect
|
||||
:index 4 :type cl:boolean :kind :scalar :label (:optional) :json-name "disconnect")
|
||||
(xmodem-packet
|
||||
:index 5 :type cl-protobufs.meshtastic::x-modem :kind :message :label (:optional) :json-name "xmodemPacket")))
|
||||
|
||||
(pi:define-message compressed
|
||||
()
|
||||
;; Fields
|
||||
(portnum
|
||||
:index 1 :type cl-protobufs.meshtastic::port-num :kind :enum :label (:optional) :json-name "portnum" :default :unknown-app)
|
||||
(data
|
||||
:index 2 :type cl-protobufs:byte-vector :kind :scalar :label (:optional) :json-name "data"))
|
||||
|
||||
(pi:define-message neighbor-info
|
||||
()
|
||||
;; Fields
|
||||
(node-id
|
||||
:index 1 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "nodeId")
|
||||
(last-sent-by-id
|
||||
:index 2 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "lastSentById")
|
||||
(neighbors
|
||||
:index 3 :type neighbor :kind :message :label (:repeated :list) :json-name "neighbors"))
|
||||
|
||||
(pi:define-message neighbor
|
||||
()
|
||||
;; Fields
|
||||
(node-id
|
||||
:index 1 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "nodeId")
|
||||
(snr
|
||||
:index 2 :type cl:float :kind :scalar :label (:optional) :json-name "snr"))
|
||||
|
||||
(pi:define-message device-metadata
|
||||
()
|
||||
;; Fields
|
||||
(firmware-version
|
||||
:index 1 :type cl:string :kind :scalar :label (:optional) :json-name "firmwareVersion")
|
||||
(device-state-version
|
||||
:index 2 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "deviceStateVersion")
|
||||
(can-shutdown
|
||||
:index 3 :type cl:boolean :kind :scalar :label (:optional) :json-name "canShutdown")
|
||||
(has-wifi
|
||||
:index 4 :type cl:boolean :kind :scalar :label (:optional) :json-name "hasWifi")
|
||||
(has-bluetooth
|
||||
:index 5 :type cl:boolean :kind :scalar :label (:optional) :json-name "hasBluetooth")
|
||||
(has-ethernet
|
||||
:index 6 :type cl:boolean :kind :scalar :label (:optional) :json-name "hasEthernet")
|
||||
(role
|
||||
:index 7 :type cl-protobufs.meshtastic::config.device-config.role :kind :enum :label (:optional) :json-name "role" :default :client)
|
||||
(position-flags
|
||||
:index 8 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "positionFlags")
|
||||
(hw-model
|
||||
:index 9 :type hardware-model :kind :enum :label (:optional) :json-name "hwModel" :default :unset))
|
||||
|
||||
(cl:export '(air-period-rx
|
||||
air-period-tx
|
||||
air-util-tx
|
||||
altitude
|
||||
altitude-geoidal-separation
|
||||
altitude-hae
|
||||
altitude-source
|
||||
bitrate
|
||||
can-shutdown
|
||||
channel
|
||||
channel-utilization
|
||||
compressed
|
||||
config
|
||||
config-complete-id
|
||||
constants
|
||||
constants-int-to-keyword
|
||||
constants-keyword-to-int
|
||||
critical-error-code
|
||||
critical-error-code-int-to-keyword
|
||||
critical-error-code-keyword-to-int
|
||||
data
|
||||
decoded
|
||||
delayed
|
||||
description
|
||||
dest
|
||||
device-metadata
|
||||
device-metrics
|
||||
device-state-version
|
||||
disconnect
|
||||
emoji
|
||||
encrypted
|
||||
error-address
|
||||
error-code
|
||||
error-count
|
||||
error-reason
|
||||
expire
|
||||
firmware-version
|
||||
fix-quality
|
||||
fix-type
|
||||
free
|
||||
from
|
||||
from-radio
|
||||
gps-accuracy
|
||||
ground-speed
|
||||
ground-track
|
||||
hardware-model
|
||||
hardware-model-int-to-keyword
|
||||
hardware-model-keyword-to-int
|
||||
has-bluetooth
|
||||
has-ethernet
|
||||
has-gps
|
||||
has-wifi
|
||||
hdop
|
||||
hop-limit
|
||||
hw-model
|
||||
icon
|
||||
id
|
||||
is-licensed
|
||||
last-heard
|
||||
last-sent-by-id
|
||||
latitude-i
|
||||
level
|
||||
location-source
|
||||
locked-to
|
||||
log-record
|
||||
log-record.level
|
||||
log-record.level-int-to-keyword
|
||||
log-record.level-keyword-to-int
|
||||
long-name
|
||||
longitude-i
|
||||
macaddr
|
||||
max-channels
|
||||
maxlen
|
||||
mesh
|
||||
mesh-packet
|
||||
mesh-packet-id
|
||||
mesh-packet.delayed
|
||||
mesh-packet.delayed-int-to-keyword
|
||||
mesh-packet.delayed-keyword-to-int
|
||||
mesh-packet.priority
|
||||
mesh-packet.priority-int-to-keyword
|
||||
mesh-packet.priority-keyword-to-int
|
||||
message
|
||||
message-timeout-msec
|
||||
metadata
|
||||
min-app-version
|
||||
module-config
|
||||
my-info
|
||||
my-node-info
|
||||
my-node-num
|
||||
name
|
||||
neighbor
|
||||
neighbor-info
|
||||
neighbors
|
||||
next-update
|
||||
node-id
|
||||
node-info
|
||||
num
|
||||
packet
|
||||
payload
|
||||
pdop
|
||||
portnum
|
||||
position
|
||||
position-flags
|
||||
position.alt-source
|
||||
position.alt-source-int-to-keyword
|
||||
position.alt-source-keyword-to-int
|
||||
position.loc-source
|
||||
position.loc-source-int-to-keyword
|
||||
position.loc-source-keyword-to-int
|
||||
priority
|
||||
queue-status
|
||||
reboot-count
|
||||
rebooted
|
||||
reply-id
|
||||
request-id
|
||||
res
|
||||
role
|
||||
route
|
||||
route-discovery
|
||||
route-reply
|
||||
route-request
|
||||
routing
|
||||
routing.error
|
||||
routing.error-int-to-keyword
|
||||
routing.error-keyword-to-int
|
||||
rx-rssi
|
||||
rx-snr
|
||||
rx-time
|
||||
sats-in-view
|
||||
sensor-id
|
||||
seq-number
|
||||
short-name
|
||||
snr
|
||||
source
|
||||
time
|
||||
timestamp
|
||||
timestamp-millis-adjust
|
||||
to
|
||||
to-radio
|
||||
user
|
||||
vdop
|
||||
want-ack
|
||||
want-config-id
|
||||
want-response
|
||||
waypoint
|
||||
xmodem-packet))
|
||||
357
examples/meshtastic/lisp/proto/meshtastic/module-config.lisp
Normal file
357
examples/meshtastic/lisp/proto/meshtastic/module-config.lisp
Normal file
|
|
@ -0,0 +1,357 @@
|
|||
;;; meshtastic/module_config.proto.lisp
|
||||
;;;
|
||||
;;; Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
|
||||
(cl:in-package #:common-lisp-user)
|
||||
|
||||
#+sbcl
|
||||
(cl:progn
|
||||
(cl:eval-when (:compile-toplevel) (sb-ext:restrict-compiler-policy 'cl:debug 0 1))
|
||||
(cl:declaim (cl:optimize (sb-c:store-coverage-data 0))))
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(cl:unless (cl:find-package "CL-PROTOBUFS.MESHTASTIC")
|
||||
(cl:defpackage "CL-PROTOBUFS.MESHTASTIC" (:use)
|
||||
(:local-nicknames (#:pi #:cl-protobufs.implementation)))))
|
||||
|
||||
(cl:in-package "CL-PROTOBUFS.MESHTASTIC")
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(pi:define-schema 'module_config
|
||||
:syntax :proto3
|
||||
|
||||
:package "meshtastic")
|
||||
)
|
||||
|
||||
|
||||
;;; Top-Level messages
|
||||
|
||||
(pi:define-message module-config
|
||||
()
|
||||
;; Nested messages
|
||||
|
||||
(pi:define-message module-config.mqtt-config
|
||||
(
|
||||
:name "MQTTConfig")
|
||||
;; Fields
|
||||
(enabled
|
||||
:index 1 :type cl:boolean :kind :scalar :label (:optional) :json-name "enabled")
|
||||
(address
|
||||
:index 2 :type cl:string :kind :scalar :label (:optional) :json-name "address")
|
||||
(username
|
||||
:index 3 :type cl:string :kind :scalar :label (:optional) :json-name "username")
|
||||
(password
|
||||
:index 4 :type cl:string :kind :scalar :label (:optional) :json-name "password")
|
||||
(encryption-enabled
|
||||
:index 5 :type cl:boolean :kind :scalar :label (:optional) :json-name "encryptionEnabled")
|
||||
(json-enabled
|
||||
:index 6 :type cl:boolean :kind :scalar :label (:optional) :json-name "jsonEnabled")
|
||||
(tls-enabled
|
||||
:index 7 :type cl:boolean :kind :scalar :label (:optional) :json-name "tlsEnabled")
|
||||
(root
|
||||
:index 8 :type cl:string :kind :scalar :label (:optional) :json-name "root"))
|
||||
|
||||
(pi:define-message module-config.remote-hardware-config
|
||||
()
|
||||
;; Fields
|
||||
(enabled
|
||||
:index 1 :type cl:boolean :kind :scalar :label (:optional) :json-name "enabled"))
|
||||
|
||||
(pi:define-message module-config.audio-config
|
||||
()
|
||||
;; Nested enums
|
||||
|
||||
(pi:define-enum module-config.audio-config.audio-baud
|
||||
(:name "Audio_Baud")
|
||||
(:codec2-default :index 0)
|
||||
(:codec2-3200 :index 1)
|
||||
(:codec2-2400 :index 2)
|
||||
(:codec2-1600 :index 3)
|
||||
(:codec2-1400 :index 4)
|
||||
(:codec2-1300 :index 5)
|
||||
(:codec2-1200 :index 6)
|
||||
(:codec2-700 :index 7)
|
||||
(:codec2-700b :index 8))
|
||||
;; Fields
|
||||
(codec2-enabled
|
||||
:index 1 :type cl:boolean :kind :scalar :label (:optional) :json-name "codec2Enabled")
|
||||
(ptt-pin
|
||||
:index 2 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "pttPin")
|
||||
(bitrate
|
||||
:index 3 :type module-config.audio-config.audio-baud :kind :enum :label (:optional) :json-name "bitrate" :default :codec2-default)
|
||||
(i2s-ws
|
||||
:index 4 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "i2sWs")
|
||||
(i2s-sd
|
||||
:index 5 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "i2sSd")
|
||||
(i2s-din
|
||||
:index 6 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "i2sDin")
|
||||
(i2s-sck
|
||||
:index 7 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "i2sSck"))
|
||||
|
||||
(pi:define-message module-config.serial-config
|
||||
()
|
||||
;; Nested enums
|
||||
|
||||
(pi:define-enum module-config.serial-config.serial-baud
|
||||
(:name "Serial_Baud")
|
||||
(:baud-default :index 0)
|
||||
(:baud-110 :index 1)
|
||||
(:baud-300 :index 2)
|
||||
(:baud-600 :index 3)
|
||||
(:baud-1200 :index 4)
|
||||
(:baud-2400 :index 5)
|
||||
(:baud-4800 :index 6)
|
||||
(:baud-9600 :index 7)
|
||||
(:baud-19200 :index 8)
|
||||
(:baud-38400 :index 9)
|
||||
(:baud-57600 :index 10)
|
||||
(:baud-115200 :index 11)
|
||||
(:baud-230400 :index 12)
|
||||
(:baud-460800 :index 13)
|
||||
(:baud-576000 :index 14)
|
||||
(:baud-921600 :index 15))
|
||||
|
||||
(pi:define-enum module-config.serial-config.serial-mode
|
||||
(:name "Serial_Mode")
|
||||
(:default :index 0)
|
||||
(:simple :index 1)
|
||||
(:proto :index 2)
|
||||
(:textmsg :index 3)
|
||||
(:nmea :index 4))
|
||||
;; Fields
|
||||
(enabled
|
||||
:index 1 :type cl:boolean :kind :scalar :label (:optional) :json-name "enabled")
|
||||
(echo
|
||||
:index 2 :type cl:boolean :kind :scalar :label (:optional) :json-name "echo")
|
||||
(rxd
|
||||
:index 3 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "rxd")
|
||||
(txd
|
||||
:index 4 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "txd")
|
||||
(baud
|
||||
:index 5 :type module-config.serial-config.serial-baud :kind :enum :label (:optional) :json-name "baud" :default :baud-default)
|
||||
(timeout
|
||||
:index 6 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "timeout")
|
||||
(mode
|
||||
:index 7 :type module-config.serial-config.serial-mode :kind :enum :label (:optional) :json-name "mode" :default :default))
|
||||
|
||||
(pi:define-message module-config.external-notification-config
|
||||
()
|
||||
;; Fields
|
||||
(enabled
|
||||
:index 1 :type cl:boolean :kind :scalar :label (:optional) :json-name "enabled")
|
||||
(output-ms
|
||||
:index 2 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "outputMs")
|
||||
(output
|
||||
:index 3 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "output")
|
||||
(output-vibra
|
||||
:index 8 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "outputVibra")
|
||||
(output-buzzer
|
||||
:index 9 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "outputBuzzer")
|
||||
(active
|
||||
:index 4 :type cl:boolean :kind :scalar :label (:optional) :json-name "active")
|
||||
(alert-message
|
||||
:index 5 :type cl:boolean :kind :scalar :label (:optional) :json-name "alertMessage")
|
||||
(alert-message-vibra
|
||||
:index 10 :type cl:boolean :kind :scalar :label (:optional) :json-name "alertMessageVibra")
|
||||
(alert-message-buzzer
|
||||
:index 11 :type cl:boolean :kind :scalar :label (:optional) :json-name "alertMessageBuzzer")
|
||||
(alert-bell
|
||||
:index 6 :type cl:boolean :kind :scalar :label (:optional) :json-name "alertBell")
|
||||
(alert-bell-vibra
|
||||
:index 12 :type cl:boolean :kind :scalar :label (:optional) :json-name "alertBellVibra")
|
||||
(alert-bell-buzzer
|
||||
:index 13 :type cl:boolean :kind :scalar :label (:optional) :json-name "alertBellBuzzer")
|
||||
(use-pwm
|
||||
:index 7 :type cl:boolean :kind :scalar :label (:optional) :json-name "usePwm")
|
||||
(nag-timeout
|
||||
:index 14 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "nagTimeout"))
|
||||
|
||||
(pi:define-message module-config.store-forward-config
|
||||
()
|
||||
;; Fields
|
||||
(enabled
|
||||
:index 1 :type cl:boolean :kind :scalar :label (:optional) :json-name "enabled")
|
||||
(heartbeat
|
||||
:index 2 :type cl:boolean :kind :scalar :label (:optional) :json-name "heartbeat")
|
||||
(records
|
||||
:index 3 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "records")
|
||||
(history-return-max
|
||||
:index 4 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "historyReturnMax")
|
||||
(history-return-window
|
||||
:index 5 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "historyReturnWindow"))
|
||||
|
||||
(pi:define-message module-config.range-test-config
|
||||
()
|
||||
;; Fields
|
||||
(enabled
|
||||
:index 1 :type cl:boolean :kind :scalar :label (:optional) :json-name "enabled")
|
||||
(sender
|
||||
:index 2 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "sender")
|
||||
(save
|
||||
:index 3 :type cl:boolean :kind :scalar :label (:optional) :json-name "save"))
|
||||
|
||||
(pi:define-message module-config.telemetry-config
|
||||
()
|
||||
;; Fields
|
||||
(device-update-interval
|
||||
:index 1 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "deviceUpdateInterval")
|
||||
(environment-update-interval
|
||||
:index 2 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "environmentUpdateInterval")
|
||||
(environment-measurement-enabled
|
||||
:index 3 :type cl:boolean :kind :scalar :label (:optional) :json-name "environmentMeasurementEnabled")
|
||||
(environment-screen-enabled
|
||||
:index 4 :type cl:boolean :kind :scalar :label (:optional) :json-name "environmentScreenEnabled")
|
||||
(environment-display-fahrenheit
|
||||
:index 5 :type cl:boolean :kind :scalar :label (:optional) :json-name "environmentDisplayFahrenheit")
|
||||
(air-quality-enabled
|
||||
:index 6 :type cl:boolean :kind :scalar :label (:optional) :json-name "airQualityEnabled")
|
||||
(air-quality-interval
|
||||
:index 7 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "airQualityInterval"))
|
||||
|
||||
(pi:define-message module-config.canned-message-config
|
||||
()
|
||||
;; Nested enums
|
||||
|
||||
(pi:define-enum module-config.canned-message-config.input-event-char
|
||||
()
|
||||
(:none :index 0)
|
||||
(:up :index 17)
|
||||
(:down :index 18)
|
||||
(:left :index 19)
|
||||
(:right :index 20)
|
||||
(:select :index 10)
|
||||
(:back :index 27)
|
||||
(:cancel :index 24))
|
||||
;; Fields
|
||||
(rotary1-enabled
|
||||
:index 1 :type cl:boolean :kind :scalar :label (:optional) :json-name "rotary1Enabled")
|
||||
(inputbroker-pin-a
|
||||
:index 2 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "inputbrokerPinA")
|
||||
(inputbroker-pin-b
|
||||
:index 3 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "inputbrokerPinB")
|
||||
(inputbroker-pin-press
|
||||
:index 4 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "inputbrokerPinPress")
|
||||
(inputbroker-event-cw
|
||||
:index 5 :type module-config.canned-message-config.input-event-char :kind :enum :label (:optional) :json-name "inputbrokerEventCw" :default :none)
|
||||
(inputbroker-event-ccw
|
||||
:index 6 :type module-config.canned-message-config.input-event-char :kind :enum :label (:optional) :json-name "inputbrokerEventCcw" :default :none)
|
||||
(inputbroker-event-press
|
||||
:index 7 :type module-config.canned-message-config.input-event-char :kind :enum :label (:optional) :json-name "inputbrokerEventPress" :default :none)
|
||||
(updown1-enabled
|
||||
:index 8 :type cl:boolean :kind :scalar :label (:optional) :json-name "updown1Enabled")
|
||||
(enabled
|
||||
:index 9 :type cl:boolean :kind :scalar :label (:optional) :json-name "enabled")
|
||||
(allow-input-source
|
||||
:index 10 :type cl:string :kind :scalar :label (:optional) :json-name "allowInputSource")
|
||||
(send-bell
|
||||
:index 11 :type cl:boolean :kind :scalar :label (:optional) :json-name "sendBell"))
|
||||
;; Fields
|
||||
(pi:define-oneof payload-variant ()
|
||||
(mqtt
|
||||
:index 1 :type module-config.mqtt-config :kind :message :label (:optional) :json-name "mqtt")
|
||||
(serial
|
||||
:index 2 :type module-config.serial-config :kind :message :label (:optional) :json-name "serial")
|
||||
(external-notification
|
||||
:index 3 :type module-config.external-notification-config :kind :message :label (:optional) :json-name "externalNotification")
|
||||
(store-forward
|
||||
:index 4 :type module-config.store-forward-config :kind :message :label (:optional) :json-name "storeForward")
|
||||
(range-test
|
||||
:index 5 :type module-config.range-test-config :kind :message :label (:optional) :json-name "rangeTest")
|
||||
(telemetry
|
||||
:index 6 :type module-config.telemetry-config :kind :message :label (:optional) :json-name "telemetry")
|
||||
(canned-message
|
||||
:index 7 :type module-config.canned-message-config :kind :message :label (:optional) :json-name "cannedMessage")
|
||||
(audio
|
||||
:index 8 :type module-config.audio-config :kind :message :label (:optional) :json-name "audio")
|
||||
(remote-hardware
|
||||
:index 9 :type module-config.remote-hardware-config :kind :message :label (:optional) :json-name "remoteHardware")))
|
||||
|
||||
(cl:export '(active
|
||||
address
|
||||
air-quality-enabled
|
||||
air-quality-interval
|
||||
alert-bell
|
||||
alert-bell-buzzer
|
||||
alert-bell-vibra
|
||||
alert-message
|
||||
alert-message-buzzer
|
||||
alert-message-vibra
|
||||
allow-input-source
|
||||
audio
|
||||
baud
|
||||
bitrate
|
||||
canned-message
|
||||
codec2-enabled
|
||||
device-update-interval
|
||||
echo
|
||||
enabled
|
||||
encryption-enabled
|
||||
environment-display-fahrenheit
|
||||
environment-measurement-enabled
|
||||
environment-screen-enabled
|
||||
environment-update-interval
|
||||
external-notification
|
||||
heartbeat
|
||||
history-return-max
|
||||
history-return-window
|
||||
i2s-din
|
||||
i2s-sck
|
||||
i2s-sd
|
||||
i2s-ws
|
||||
inputbroker-event-ccw
|
||||
inputbroker-event-cw
|
||||
inputbroker-event-press
|
||||
inputbroker-pin-a
|
||||
inputbroker-pin-b
|
||||
inputbroker-pin-press
|
||||
json-enabled
|
||||
mode
|
||||
module-config
|
||||
module-config.audio-config
|
||||
module-config.audio-config.audio-baud
|
||||
module-config.audio-config.audio-baud-int-to-keyword
|
||||
module-config.audio-config.audio-baud-keyword-to-int
|
||||
module-config.canned-message-config
|
||||
module-config.canned-message-config.input-event-char
|
||||
module-config.canned-message-config.input-event-char-int-to-keyword
|
||||
module-config.canned-message-config.input-event-char-keyword-to-int
|
||||
module-config.external-notification-config
|
||||
module-config.mqtt-config
|
||||
module-config.range-test-config
|
||||
module-config.remote-hardware-config
|
||||
module-config.serial-config
|
||||
module-config.serial-config.serial-baud
|
||||
module-config.serial-config.serial-baud-int-to-keyword
|
||||
module-config.serial-config.serial-baud-keyword-to-int
|
||||
module-config.serial-config.serial-mode
|
||||
module-config.serial-config.serial-mode-int-to-keyword
|
||||
module-config.serial-config.serial-mode-keyword-to-int
|
||||
module-config.store-forward-config
|
||||
module-config.telemetry-config
|
||||
module_config
|
||||
mqtt
|
||||
nag-timeout
|
||||
output
|
||||
output-buzzer
|
||||
output-ms
|
||||
output-vibra
|
||||
password
|
||||
ptt-pin
|
||||
range-test
|
||||
records
|
||||
remote-hardware
|
||||
root
|
||||
rotary1-enabled
|
||||
rxd
|
||||
save
|
||||
send-bell
|
||||
sender
|
||||
serial
|
||||
store-forward
|
||||
telemetry
|
||||
timeout
|
||||
tls-enabled
|
||||
txd
|
||||
updown1-enabled
|
||||
use-pwm
|
||||
username))
|
||||
45
examples/meshtastic/lisp/proto/meshtastic/mqtt.lisp
Normal file
45
examples/meshtastic/lisp/proto/meshtastic/mqtt.lisp
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
;;; meshtastic/mqtt.proto.lisp
|
||||
;;;
|
||||
;;; Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
|
||||
(cl:in-package #:common-lisp-user)
|
||||
|
||||
#+sbcl
|
||||
(cl:progn
|
||||
(cl:eval-when (:compile-toplevel) (sb-ext:restrict-compiler-policy 'cl:debug 0 1))
|
||||
(cl:declaim (cl:optimize (sb-c:store-coverage-data 0))))
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(cl:unless (cl:find-package "CL-PROTOBUFS.MESHTASTIC")
|
||||
(cl:defpackage "CL-PROTOBUFS.MESHTASTIC" (:use)
|
||||
(:local-nicknames (#:pi #:cl-protobufs.implementation)))))
|
||||
|
||||
(cl:in-package "CL-PROTOBUFS.MESHTASTIC")
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(pi:define-schema 'mqtt
|
||||
:syntax :proto3
|
||||
|
||||
:package "meshtastic"
|
||||
:import '(;;"meshtastic/mesh.proto"
|
||||
))
|
||||
)
|
||||
|
||||
|
||||
;;; Top-Level messages
|
||||
|
||||
(pi:define-message service-envelope
|
||||
()
|
||||
;; Fields
|
||||
(packet
|
||||
:index 1 :type cl-protobufs.meshtastic::mesh-packet :kind :message :label (:optional) :json-name "packet")
|
||||
(channel-id
|
||||
:index 2 :type cl:string :kind :scalar :label (:optional) :json-name "channelId")
|
||||
(gateway-id
|
||||
:index 3 :type cl:string :kind :scalar :label (:optional) :json-name "gatewayId"))
|
||||
|
||||
(cl:export '(channel-id
|
||||
gateway-id
|
||||
mqtt
|
||||
packet
|
||||
service-envelope))
|
||||
58
examples/meshtastic/lisp/proto/meshtastic/portnums.lisp
Normal file
58
examples/meshtastic/lisp/proto/meshtastic/portnums.lisp
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
;;; meshtastic/portnums.proto.lisp
|
||||
;;;
|
||||
;;; Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
|
||||
(cl:in-package #:common-lisp-user)
|
||||
|
||||
#+sbcl
|
||||
(cl:progn
|
||||
(cl:eval-when (:compile-toplevel) (sb-ext:restrict-compiler-policy 'cl:debug 0 1))
|
||||
(cl:declaim (cl:optimize (sb-c:store-coverage-data 0))))
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(cl:unless (cl:find-package "CL-PROTOBUFS.MESHTASTIC")
|
||||
(cl:defpackage "CL-PROTOBUFS.MESHTASTIC" (:use)
|
||||
(:local-nicknames (#:pi #:cl-protobufs.implementation)))))
|
||||
|
||||
(cl:in-package "CL-PROTOBUFS.MESHTASTIC")
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(pi:define-schema 'portnums
|
||||
:syntax :proto3
|
||||
|
||||
:package "meshtastic")
|
||||
)
|
||||
|
||||
|
||||
;;; Top-Level enums
|
||||
|
||||
(pi:define-enum port-num
|
||||
()
|
||||
(:unknown-app :index 0)
|
||||
(:text-message-app :index 1)
|
||||
(:remote-hardware-app :index 2)
|
||||
(:position-app :index 3)
|
||||
(:nodeinfo-app :index 4)
|
||||
(:routing-app :index 5)
|
||||
(:admin-app :index 6)
|
||||
(:text-message-compressed-app :index 7)
|
||||
(:waypoint-app :index 8)
|
||||
(:audio-app :index 9)
|
||||
(:reply-app :index 32)
|
||||
(:ip-tunnel-app :index 33)
|
||||
(:serial-app :index 64)
|
||||
(:store-forward-app :index 65)
|
||||
(:range-test-app :index 66)
|
||||
(:telemetry-app :index 67)
|
||||
(:zps-app :index 68)
|
||||
(:simulator-app :index 69)
|
||||
(:traceroute-app :index 70)
|
||||
(:neighborinfo-app :index 71)
|
||||
(:private-app :index 256)
|
||||
(:atak-forwarder :index 257)
|
||||
(:max :index 511))
|
||||
|
||||
(cl:export '(port-num
|
||||
port-num-int-to-keyword
|
||||
port-num-keyword-to-int
|
||||
portnums))
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
;;; meshtastic/remote_hardware.proto.lisp
|
||||
;;;
|
||||
;;; Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
|
||||
(cl:in-package #:common-lisp-user)
|
||||
|
||||
#+sbcl
|
||||
(cl:progn
|
||||
(cl:eval-when (:compile-toplevel) (sb-ext:restrict-compiler-policy 'cl:debug 0 1))
|
||||
(cl:declaim (cl:optimize (sb-c:store-coverage-data 0))))
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(cl:unless (cl:find-package "CL-PROTOBUFS.MESHTASTIC")
|
||||
(cl:defpackage "CL-PROTOBUFS.MESHTASTIC" (:use)
|
||||
(:local-nicknames (#:pi #:cl-protobufs.implementation)))))
|
||||
|
||||
(cl:in-package "CL-PROTOBUFS.MESHTASTIC")
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(pi:define-schema 'remote_hardware
|
||||
:syntax :proto3
|
||||
|
||||
:package "meshtastic")
|
||||
)
|
||||
|
||||
|
||||
;;; Top-Level messages
|
||||
|
||||
(pi:define-message hardware-message
|
||||
()
|
||||
;; Nested enums
|
||||
|
||||
(pi:define-enum hardware-message.type
|
||||
()
|
||||
(:unset :index 0)
|
||||
(:write-gpios :index 1)
|
||||
(:watch-gpios :index 2)
|
||||
(:gpios-changed :index 3)
|
||||
(:read-gpios :index 4)
|
||||
(:read-gpios-reply :index 5))
|
||||
;; Fields
|
||||
(type
|
||||
:index 1 :type hardware-message.type :kind :enum :label (:optional) :json-name "type" :default :unset)
|
||||
(gpio-mask
|
||||
:index 2 :type cl-protobufs:uint64 :kind :scalar :label (:optional) :json-name "gpioMask")
|
||||
(gpio-value
|
||||
:index 3 :type cl-protobufs:uint64 :kind :scalar :label (:optional) :json-name "gpioValue"))
|
||||
|
||||
(cl:export '(gpio-mask
|
||||
gpio-value
|
||||
hardware-message
|
||||
hardware-message.type
|
||||
hardware-message.type-int-to-keyword
|
||||
hardware-message.type-keyword-to-int
|
||||
remote_hardware
|
||||
type))
|
||||
38
examples/meshtastic/lisp/proto/meshtastic/rtttl.lisp
Normal file
38
examples/meshtastic/lisp/proto/meshtastic/rtttl.lisp
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
;;; meshtastic/rtttl.proto.lisp
|
||||
;;;
|
||||
;;; Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
|
||||
(cl:in-package #:common-lisp-user)
|
||||
|
||||
#+sbcl
|
||||
(cl:progn
|
||||
(cl:eval-when (:compile-toplevel) (sb-ext:restrict-compiler-policy 'cl:debug 0 1))
|
||||
(cl:declaim (cl:optimize (sb-c:store-coverage-data 0))))
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(cl:unless (cl:find-package "CL-PROTOBUFS.MESHTASTIC")
|
||||
(cl:defpackage "CL-PROTOBUFS.MESHTASTIC" (:use)
|
||||
(:local-nicknames (#:pi #:cl-protobufs.implementation)))))
|
||||
|
||||
(cl:in-package "CL-PROTOBUFS.MESHTASTIC")
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(pi:define-schema 'rtttl
|
||||
:syntax :proto3
|
||||
|
||||
:package "meshtastic")
|
||||
)
|
||||
|
||||
|
||||
;;; Top-Level messages
|
||||
|
||||
(pi:define-message rtttl-config
|
||||
(
|
||||
:name "RTTTLConfig")
|
||||
;; Fields
|
||||
(ringtone
|
||||
:index 1 :type cl:string :kind :scalar :label (:optional) :json-name "ringtone"))
|
||||
|
||||
(cl:export '(ringtone
|
||||
rtttl
|
||||
rtttl-config))
|
||||
128
examples/meshtastic/lisp/proto/meshtastic/storeforward.lisp
Normal file
128
examples/meshtastic/lisp/proto/meshtastic/storeforward.lisp
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
;;; meshtastic/storeforward.proto.lisp
|
||||
;;;
|
||||
;;; Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
|
||||
(cl:in-package #:common-lisp-user)
|
||||
|
||||
#+sbcl
|
||||
(cl:progn
|
||||
(cl:eval-when (:compile-toplevel) (sb-ext:restrict-compiler-policy 'cl:debug 0 1))
|
||||
(cl:declaim (cl:optimize (sb-c:store-coverage-data 0))))
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(cl:unless (cl:find-package "CL-PROTOBUFS.MESHTASTIC")
|
||||
(cl:defpackage "CL-PROTOBUFS.MESHTASTIC" (:use)
|
||||
(:local-nicknames (#:pi #:cl-protobufs.implementation)))))
|
||||
|
||||
(cl:in-package "CL-PROTOBUFS.MESHTASTIC")
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(pi:define-schema 'storeforward
|
||||
:syntax :proto3
|
||||
|
||||
:package "meshtastic")
|
||||
)
|
||||
|
||||
|
||||
;;; Top-Level messages
|
||||
|
||||
(pi:define-message store-and-forward
|
||||
()
|
||||
;; Nested enums
|
||||
|
||||
(pi:define-enum store-and-forward.request-response
|
||||
()
|
||||
(:unset :index 0)
|
||||
(:router-error :index 1)
|
||||
(:router-heartbeat :index 2)
|
||||
(:router-ping :index 3)
|
||||
(:router-pong :index 4)
|
||||
(:router-busy :index 5)
|
||||
(:router-history :index 6)
|
||||
(:router-stats :index 7)
|
||||
(:client-error :index 64)
|
||||
(:client-history :index 65)
|
||||
(:client-stats :index 66)
|
||||
(:client-ping :index 67)
|
||||
(:client-pong :index 68)
|
||||
(:client-abort :index 106))
|
||||
;; Nested messages
|
||||
|
||||
(pi:define-message store-and-forward.statistics
|
||||
()
|
||||
;; Fields
|
||||
(messages-total
|
||||
:index 1 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "messagesTotal")
|
||||
(messages-saved
|
||||
:index 2 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "messagesSaved")
|
||||
(messages-max
|
||||
:index 3 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "messagesMax")
|
||||
(up-time
|
||||
:index 4 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "upTime")
|
||||
(requests
|
||||
:index 5 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "requests")
|
||||
(requests-history
|
||||
:index 6 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "requestsHistory")
|
||||
(heartbeat
|
||||
:index 7 :type cl:boolean :kind :scalar :label (:optional) :json-name "heartbeat")
|
||||
(return-max
|
||||
:index 8 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "returnMax")
|
||||
(return-window
|
||||
:index 9 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "returnWindow"))
|
||||
|
||||
(pi:define-message store-and-forward.history
|
||||
()
|
||||
;; Fields
|
||||
(history-messages
|
||||
:index 1 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "historyMessages")
|
||||
(window
|
||||
:index 2 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "window")
|
||||
(last-request
|
||||
:index 3 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "lastRequest"))
|
||||
|
||||
(pi:define-message store-and-forward.heartbeat
|
||||
()
|
||||
;; Fields
|
||||
(period
|
||||
:index 1 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "period")
|
||||
(secondary
|
||||
:index 2 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "secondary"))
|
||||
;; Fields
|
||||
(pi:define-oneof variant ()
|
||||
(stats
|
||||
:index 2 :type store-and-forward.statistics :kind :message :label (:optional) :json-name "stats")
|
||||
(history
|
||||
:index 3 :type store-and-forward.history :kind :message :label (:optional) :json-name "history")
|
||||
(heartbeat
|
||||
:index 4 :type store-and-forward.heartbeat :kind :message :label (:optional) :json-name "heartbeat")
|
||||
(empty
|
||||
:index 5 :type cl:boolean :kind :scalar :label (:optional) :json-name "empty"))
|
||||
(rr
|
||||
:index 1 :type store-and-forward.request-response :kind :enum :label (:optional) :json-name "rr" :default :unset))
|
||||
|
||||
(cl:export '(empty
|
||||
heartbeat
|
||||
history
|
||||
history-messages
|
||||
last-request
|
||||
messages-max
|
||||
messages-saved
|
||||
messages-total
|
||||
period
|
||||
requests
|
||||
requests-history
|
||||
return-max
|
||||
return-window
|
||||
rr
|
||||
secondary
|
||||
stats
|
||||
store-and-forward
|
||||
store-and-forward.heartbeat
|
||||
store-and-forward.history
|
||||
store-and-forward.request-response
|
||||
store-and-forward.request-response-int-to-keyword
|
||||
store-and-forward.request-response-keyword-to-int
|
||||
store-and-forward.statistics
|
||||
storeforward
|
||||
up-time
|
||||
window))
|
||||
145
examples/meshtastic/lisp/proto/meshtastic/telemetry.lisp
Normal file
145
examples/meshtastic/lisp/proto/meshtastic/telemetry.lisp
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
;;; meshtastic/telemetry.proto.lisp
|
||||
;;;
|
||||
;;; Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
|
||||
(cl:in-package #:common-lisp-user)
|
||||
|
||||
#+sbcl
|
||||
(cl:progn
|
||||
(cl:eval-when (:compile-toplevel) (sb-ext:restrict-compiler-policy 'cl:debug 0 1))
|
||||
(cl:declaim (cl:optimize (sb-c:store-coverage-data 0))))
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(cl:unless (cl:find-package "CL-PROTOBUFS.MESHTASTIC")
|
||||
(cl:defpackage "CL-PROTOBUFS.MESHTASTIC" (:use)
|
||||
(:local-nicknames (#:pi #:cl-protobufs.implementation)))))
|
||||
|
||||
(cl:in-package "CL-PROTOBUFS.MESHTASTIC")
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(pi:define-schema 'telemetry
|
||||
:syntax :proto3
|
||||
|
||||
:package "meshtastic")
|
||||
)
|
||||
|
||||
|
||||
;;; Top-Level enums
|
||||
|
||||
(pi:define-enum telemetry-sensor-type
|
||||
()
|
||||
(:sensor-unset :index 0)
|
||||
(:bme280 :index 1)
|
||||
(:bme680 :index 2)
|
||||
(:mcp9808 :index 3)
|
||||
(:ina260 :index 4)
|
||||
(:ina219 :index 5)
|
||||
(:bmp280 :index 6)
|
||||
(:shtc3 :index 7)
|
||||
(:lps22 :index 8)
|
||||
(:qmc6310 :index 9)
|
||||
(:qmi8658 :index 10)
|
||||
(:qmc5883l :index 11)
|
||||
(:sht31 :index 12)
|
||||
(:pmsa003i :index 13))
|
||||
|
||||
;;; Top-Level messages
|
||||
|
||||
(pi:define-message device-metrics
|
||||
()
|
||||
;; Fields
|
||||
(battery-level
|
||||
:index 1 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "batteryLevel")
|
||||
(voltage
|
||||
:index 2 :type cl:float :kind :scalar :label (:optional) :json-name "voltage")
|
||||
(channel-utilization
|
||||
:index 3 :type cl:float :kind :scalar :label (:optional) :json-name "channelUtilization")
|
||||
(air-util-tx
|
||||
:index 4 :type cl:float :kind :scalar :label (:optional) :json-name "airUtilTx"))
|
||||
|
||||
(pi:define-message environment-metrics
|
||||
()
|
||||
;; Fields
|
||||
(temperature
|
||||
:index 1 :type cl:float :kind :scalar :label (:optional) :json-name "temperature")
|
||||
(relative-humidity
|
||||
:index 2 :type cl:float :kind :scalar :label (:optional) :json-name "relativeHumidity")
|
||||
(barometric-pressure
|
||||
:index 3 :type cl:float :kind :scalar :label (:optional) :json-name "barometricPressure")
|
||||
(gas-resistance
|
||||
:index 4 :type cl:float :kind :scalar :label (:optional) :json-name "gasResistance")
|
||||
(voltage
|
||||
:index 5 :type cl:float :kind :scalar :label (:optional) :json-name "voltage")
|
||||
(current
|
||||
:index 6 :type cl:float :kind :scalar :label (:optional) :json-name "current"))
|
||||
|
||||
(pi:define-message air-quality-metrics
|
||||
()
|
||||
;; Fields
|
||||
(pm10-standard
|
||||
:index 1 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "pm10Standard")
|
||||
(pm25-standard
|
||||
:index 2 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "pm25Standard")
|
||||
(pm100-standard
|
||||
:index 3 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "pm100Standard")
|
||||
(pm10-environmental
|
||||
:index 4 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "pm10Environmental")
|
||||
(pm25-environmental
|
||||
:index 5 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "pm25Environmental")
|
||||
(pm100-environmental
|
||||
:index 6 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "pm100Environmental")
|
||||
(particles-03um
|
||||
:index 7 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "particles03um")
|
||||
(particles-05um
|
||||
:index 8 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "particles05um")
|
||||
(particles-10um
|
||||
:index 9 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "particles10um")
|
||||
(particles-25um
|
||||
:index 10 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "particles25um")
|
||||
(particles-50um
|
||||
:index 11 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "particles50um")
|
||||
(particles-100um
|
||||
:index 12 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "particles100um"))
|
||||
|
||||
(pi:define-message telemetry
|
||||
()
|
||||
;; Fields
|
||||
(pi:define-oneof variant ()
|
||||
(device-metrics
|
||||
:index 2 :type device-metrics :kind :message :label (:optional) :json-name "deviceMetrics")
|
||||
(environment-metrics
|
||||
:index 3 :type environment-metrics :kind :message :label (:optional) :json-name "environmentMetrics")
|
||||
(air-quality-metrics
|
||||
:index 4 :type air-quality-metrics :kind :message :label (:optional) :json-name "airQualityMetrics"))
|
||||
(time
|
||||
:index 1 :type cl-protobufs:fixed32 :kind :scalar :label (:optional) :json-name "time"))
|
||||
|
||||
(cl:export '(air-quality-metrics
|
||||
air-util-tx
|
||||
barometric-pressure
|
||||
battery-level
|
||||
channel-utilization
|
||||
current
|
||||
device-metrics
|
||||
environment-metrics
|
||||
gas-resistance
|
||||
particles-03um
|
||||
particles-05um
|
||||
particles-100um
|
||||
particles-10um
|
||||
particles-25um
|
||||
particles-50um
|
||||
pm10-environmental
|
||||
pm10-standard
|
||||
pm100-environmental
|
||||
pm100-standard
|
||||
pm25-environmental
|
||||
pm25-standard
|
||||
relative-humidity
|
||||
telemetry
|
||||
telemetry-sensor-type
|
||||
telemetry-sensor-type-int-to-keyword
|
||||
telemetry-sensor-type-keyword-to-int
|
||||
temperature
|
||||
time
|
||||
voltage))
|
||||
61
examples/meshtastic/lisp/proto/meshtastic/xmodem.lisp
Normal file
61
examples/meshtastic/lisp/proto/meshtastic/xmodem.lisp
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
;;; meshtastic/xmodem.proto.lisp
|
||||
;;;
|
||||
;;; Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
|
||||
(cl:in-package #:common-lisp-user)
|
||||
|
||||
#+sbcl
|
||||
(cl:progn
|
||||
(cl:eval-when (:compile-toplevel) (sb-ext:restrict-compiler-policy 'cl:debug 0 1))
|
||||
(cl:declaim (cl:optimize (sb-c:store-coverage-data 0))))
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(cl:unless (cl:find-package "CL-PROTOBUFS.MESHTASTIC")
|
||||
(cl:defpackage "CL-PROTOBUFS.MESHTASTIC" (:use)
|
||||
(:local-nicknames (#:pi #:cl-protobufs.implementation)))))
|
||||
|
||||
(cl:in-package "CL-PROTOBUFS.MESHTASTIC")
|
||||
|
||||
(cl:eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(pi:define-schema 'xmodem
|
||||
:syntax :proto3
|
||||
|
||||
:package "meshtastic")
|
||||
)
|
||||
|
||||
|
||||
;;; Top-Level messages
|
||||
|
||||
(pi:define-message x-modem
|
||||
()
|
||||
;; Nested enums
|
||||
|
||||
(pi:define-enum x-modem.control
|
||||
()
|
||||
(:nul :index 0)
|
||||
(:soh :index 1)
|
||||
(:stx :index 2)
|
||||
(:eot :index 4)
|
||||
(:ack :index 6)
|
||||
(:nak :index 21)
|
||||
(:can :index 24)
|
||||
(:ctrlz :index 26))
|
||||
;; Fields
|
||||
(control
|
||||
:index 1 :type x-modem.control :kind :enum :label (:optional) :json-name "control" :default :nul)
|
||||
(seq
|
||||
:index 2 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "seq")
|
||||
(crc16
|
||||
:index 3 :type cl-protobufs:uint32 :kind :scalar :label (:optional) :json-name "crc16")
|
||||
(buffer
|
||||
:index 4 :type cl-protobufs:byte-vector :kind :scalar :label (:optional) :json-name "buffer"))
|
||||
|
||||
(cl:export '(buffer
|
||||
control
|
||||
crc16
|
||||
seq
|
||||
x-modem
|
||||
x-modem.control
|
||||
x-modem.control-int-to-keyword
|
||||
x-modem.control-keyword-to-int
|
||||
xmodem))
|
||||
34
examples/meshtastic/lisp/qml-reload/auto-reload.lisp
Normal file
34
examples/meshtastic/lisp/qml-reload/auto-reload.lisp
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
;;; trivial QML auto reload during development (desktop only), see:
|
||||
;;;
|
||||
;;; lqml run.lisp -auto
|
||||
|
||||
(in-package :qml-user)
|
||||
|
||||
(defvar *dir* *load-truename*)
|
||||
|
||||
(defun qml:view-status-changed (status)
|
||||
(when (= 1 status)
|
||||
(load (merge-pathnames "on-reloaded" *dir*))))
|
||||
|
||||
(let ((secs 0)
|
||||
files)
|
||||
(defun watch-files ()
|
||||
(flet ((repeat ()
|
||||
(qsingle-shot 500 'watch-files)))
|
||||
(unless files
|
||||
(dolist (file (directory (merge-pathnames "../../qml/**/*.qml" *dir*)))
|
||||
(push file files)))
|
||||
(let ((curr 0))
|
||||
(dolist (file files)
|
||||
(let ((date (file-write-date file)))
|
||||
(unless date ; might be NIL while saving
|
||||
(return-from watch-files (repeat)))
|
||||
(incf curr date)))
|
||||
(when (/= secs curr)
|
||||
(unless (zerop secs)
|
||||
(qml:reload))
|
||||
(setf secs curr)))
|
||||
(repeat))))
|
||||
|
||||
(watch-files)
|
||||
|
||||
5
examples/meshtastic/lisp/qml-reload/on-reloaded.lisp
Normal file
5
examples/meshtastic/lisp/qml-reload/on-reloaded.lisp
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
;;; this file will be loaded every time QML has been reloaded
|
||||
|
||||
(in-package :qml-user)
|
||||
|
||||
|
||||
19
examples/meshtastic/lisp/qt.lisp
Normal file
19
examples/meshtastic/lisp/qt.lisp
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
(defpackage :qt
|
||||
(:use :cl :qml)
|
||||
(:export
|
||||
#:*ble*
|
||||
#:ini
|
||||
#:read*
|
||||
#:write*))
|
||||
|
||||
(in-package :qt)
|
||||
|
||||
(defvar *ble* nil)
|
||||
|
||||
(defun ini ()
|
||||
(setf *ble*
|
||||
#+qt-plugin (qload-c++ "cpp/qt")
|
||||
#-qt-plugin (qfind-child nil "QT"))
|
||||
(let ((*package* (find-package :qt)))
|
||||
(define-qt-wrappers *ble*)))
|
||||
|
||||
184
examples/meshtastic/lisp/radio.lisp
Normal file
184
examples/meshtastic/lisp/radio.lisp
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
(in-package :radio)
|
||||
|
||||
(defvar *region* :eu-868) ; Europe 868 MHz
|
||||
|
||||
(defvar *primary-channel* nil)
|
||||
(defvar *channels* nil)
|
||||
(defvar *my-node-info* nil)
|
||||
(defvar *node-infos* nil)
|
||||
(defvar *config-lora* nil)
|
||||
|
||||
;;; header
|
||||
|
||||
(defun lsb (size)
|
||||
(ldb (byte 8 0) size))
|
||||
|
||||
(defun msb (size)
|
||||
(ldb (byte 8 8) size))
|
||||
|
||||
(defun header (size)
|
||||
(vector #x94 #xc3 (msb size) (lsb size)))
|
||||
|
||||
;;; ini/send/receive
|
||||
|
||||
(defvar *config-id* 0)
|
||||
(defvar *notify-id* nil)
|
||||
(defvar *ready* nil)
|
||||
(defvar *reading* nil)
|
||||
(defvar *received* nil)
|
||||
|
||||
(defun to-bytes (list)
|
||||
(make-array (length list)
|
||||
:element-type '(unsigned-byte 8)
|
||||
:initial-contents list))
|
||||
|
||||
(defun set-ready (&optional (ready t)) ; called from Qt
|
||||
(setf *ready* ready)
|
||||
(when ready
|
||||
(qlater 'start-config))
|
||||
(values))
|
||||
|
||||
(defun start-config ()
|
||||
#+android
|
||||
(ensure-permissions :access-coarse-location) ; needed for BLE
|
||||
(when *ready*
|
||||
(incf *config-id*)
|
||||
(send-to-radio
|
||||
(me:make-to-radio :want-config-id *config-id*))))
|
||||
|
||||
(defun send-message (text)
|
||||
"Sends TEXT to radio and adds it to QML item model."
|
||||
(incf msg:*message-id*)
|
||||
(send-to-radio
|
||||
(me:make-to-radio
|
||||
:packet (me:make-mesh-packet
|
||||
:from (me:num *my-node-info*)
|
||||
:to (me:num (first *node-infos*)) ; assumes just 2 radios (for now)
|
||||
:id msg:*message-id*
|
||||
:want-ack t
|
||||
:decoded (me:make-data
|
||||
:portnum :text-message-app
|
||||
:payload (babel:string-to-octets text)))))
|
||||
(msg:add-message
|
||||
(list :m-text text
|
||||
:m-sender (me:short-name (me:user *my-node-info*))
|
||||
:m-timestamp (timestamp-to-string)
|
||||
:m-id msg:*message-id*
|
||||
:m-ack-state (position :not-received msg:*states*))))
|
||||
|
||||
(defun read-radio ()
|
||||
"Triggers a read on the radio. Will call RECEIVED-FROM-RADIO on success."
|
||||
(qrun* (qt:read* qt:*ble*)))
|
||||
|
||||
(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))))
|
||||
|
||||
(defun received-from-radio (bytes &optional notified) ; called from Qt
|
||||
(if notified
|
||||
(progn
|
||||
(setf *notify-id* bytes)
|
||||
(read-radio))
|
||||
(let ((from-radio (pr:deserialize-from-bytes 'me:from-radio bytes)))
|
||||
(setf *reading* t)
|
||||
(pr:print-json from-radio)
|
||||
(push from-radio *received*)))
|
||||
(values))
|
||||
|
||||
(defun receiving-done ()
|
||||
(setf *reading* nil)
|
||||
(process-received))
|
||||
|
||||
(defun node-to-name (num)
|
||||
(dolist (info *node-infos*)
|
||||
(when (= num (me:num info))
|
||||
(return (me:short-name (me:user info))))))
|
||||
|
||||
(defun timestamp-to-string (&optional (secs (get-universal-time)))
|
||||
(multiple-value-bind (_ m h)
|
||||
(decode-universal-time secs)
|
||||
(format nil "~D:~2,'0D" h m)))
|
||||
|
||||
(defun process-received ()
|
||||
"Walks *RECEIVED* FROM-RADIOs and saves relevant data."
|
||||
(setf *received* (nreverse *received*))
|
||||
(dolist (struct *received*)
|
||||
(cond ((me:from-radio.has-packet struct)
|
||||
(let* ((packet (me:from-radio.packet struct))
|
||||
(decoded (me:decoded packet))
|
||||
(payload (me:payload decoded)))
|
||||
(case (me:portnum decoded)
|
||||
;; text-message
|
||||
(:text-message-app
|
||||
(msg:add-message
|
||||
(list :m-text (babel:octets-to-string payload)
|
||||
:m-sender (node-to-name (me:from packet))
|
||||
:m-timestamp (timestamp-to-string))))
|
||||
;; for :m-ack-state (acknowledgement state)
|
||||
(:routing-app
|
||||
(msg:change-state (case (me:routing.error-reason
|
||||
(pr:deserialize-from-bytes 'me:routing payload))
|
||||
(:none :received))
|
||||
(me:request-id decoded))))))
|
||||
;; my-info
|
||||
((me:from-radio.has-my-info struct)
|
||||
(setf *my-node-info* (me:my-node-num (me:my-info struct))))
|
||||
;; node-info
|
||||
((me:from-radio.has-node-info struct)
|
||||
(let ((info (me:node-info struct)))
|
||||
(if (eql *my-node-info* (me:num info))
|
||||
(setf *my-node-info* info)
|
||||
(push info *node-infos*))))
|
||||
;; channel
|
||||
((me:from-radio.has-channel struct)
|
||||
(let ((channel (me:channel struct)))
|
||||
(if (eql :primary (me:role channel))
|
||||
(setf *primary-channel* channel)
|
||||
(push channel *channels*))))
|
||||
;; config lora
|
||||
((me:from-radio.has-config struct)
|
||||
(let ((config (me:config struct)))
|
||||
(when (me:config.has-lora config)
|
||||
(setf *config-lora* (me:lora config)))))
|
||||
;; config-complete-id
|
||||
((me:from-radio.has-config-complete-id struct)
|
||||
(when (= *config-id* (me:config-complete-id struct))
|
||||
(qlater 'config-device)
|
||||
(q> |myName| ui:*view*
|
||||
(me:short-name (me:user *my-node-info*)))
|
||||
(q> |playing| ui:*busy* nil)
|
||||
(qlog :config-complete *config-id*)))))
|
||||
(setf *received* nil))
|
||||
|
||||
(defun send-admin (admin-message)
|
||||
(send-to-radio
|
||||
(me:make-to-radio
|
||||
:packet (me:make-mesh-packet
|
||||
:id (incf msg:*message-id*)
|
||||
:want-ack t
|
||||
:decoded (me:make-data
|
||||
:portnum :admin-app
|
||||
:payload (pr:serialize-to-bytes admin-message)
|
||||
:want-response t)))))
|
||||
|
||||
(defun config-device ()
|
||||
"Absolut minimum necessary for sending text messages."
|
||||
;; lora settings
|
||||
(send-admin
|
||||
(me:make-admin-message
|
||||
:set-config (me:make-config
|
||||
:lora (me:make-config.lo-ra-config
|
||||
:use-preset t
|
||||
:region *region*
|
||||
:hop-limit 3
|
||||
:tx-enabled t))))
|
||||
;; channel settings
|
||||
(send-admin
|
||||
(me:make-admin-message
|
||||
:set-channel (me:make-channel
|
||||
:settings (me:make-channel-settings :psk (to-bytes (list 1)))
|
||||
:role :primary))))
|
||||
17
examples/meshtastic/lisp/ui-vars.lisp
Normal file
17
examples/meshtastic/lisp/ui-vars.lisp
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
;;; keep sorted to recognize eventual name clashes
|
||||
|
||||
(defpackage ui
|
||||
(:use :cl)
|
||||
(:export
|
||||
#:*busy*
|
||||
#:*hour-glass*
|
||||
#:*messages*
|
||||
#:*view*))
|
||||
|
||||
(in-package :ui)
|
||||
|
||||
(defparameter *busy* "busy")
|
||||
(defparameter *hour-glass* "hour_glass")
|
||||
(defparameter *messages* "messages")
|
||||
(defparameter *view* "view")
|
||||
|
||||
125
examples/meshtastic/make.lisp
Normal file
125
examples/meshtastic/make.lisp
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
;;; check target
|
||||
|
||||
(defvar *32bit* (<= most-positive-fixnum (expt 2 32)))
|
||||
|
||||
(let ((arg (first (ext:command-args))))
|
||||
(mapc (lambda (name feature)
|
||||
(when (search name arg)
|
||||
(pushnew feature *features*)))
|
||||
(list "/ecl-android" "/ecl-ios")
|
||||
(list :android :ios)))
|
||||
|
||||
#+(or android ios)
|
||||
(pushnew :mobile *features*)
|
||||
|
||||
;;; copy ECL 'encodings/' (mobile only)
|
||||
|
||||
(defun cc (&rest args)
|
||||
(apply 'concatenate 'string args))
|
||||
|
||||
#+mobile
|
||||
(defvar *assets* #+android "../platforms/android/assets/lib/"
|
||||
#+ios "../platforms/ios/assets/Library/")
|
||||
|
||||
#+mobile
|
||||
(defun shell (command)
|
||||
(ext:run-program "sh" (list "-c" command)))
|
||||
|
||||
#+mobile
|
||||
(unless (probe-file (cc *assets* "encodings"))
|
||||
(ensure-directories-exist *assets*)
|
||||
(let ((lib (cc (ext:getenv #+android (if *32bit* "ECL_ANDROID_32" "ECL_ANDROID")
|
||||
#+ios "ECL_IOS")
|
||||
"/lib/ecl-*/")))
|
||||
(shell (cc "cp -r " lib "encodings " *assets*))))
|
||||
|
||||
;;; compile ASDF system
|
||||
|
||||
(require :asdf)
|
||||
(require :cmp)
|
||||
|
||||
(push (merge-pathnames "../")
|
||||
asdf:*central-registry*)
|
||||
|
||||
(setf *default-pathname-defaults*
|
||||
(truename (merge-pathnames "../../../"))) ; LQML root
|
||||
|
||||
(defvar *current*
|
||||
(let ((name (namestring *load-truename*)))
|
||||
(subseq name
|
||||
(length (namestring *default-pathname-defaults*))
|
||||
(1+ (position #\/ name :from-end t)))))
|
||||
|
||||
(dolist (file (list "package" "x" "ecl-ext" "ini" "qml")) ; load LQML symbols
|
||||
(load (merge-pathnames file "src/lisp/")))
|
||||
|
||||
(progn
|
||||
(defvar cl-user::*tr-path* (truename (cc *current* "i18n/")))
|
||||
(load "src/lisp/tr"))
|
||||
|
||||
#-mobile
|
||||
(asdf:make-build "app"
|
||||
:monolithic t
|
||||
:type :static-library
|
||||
:move-here (cc *current* "build/tmp/")
|
||||
:init-name "ini_app")
|
||||
|
||||
#+mobile
|
||||
(progn
|
||||
(pushnew :interpreter *features*)
|
||||
(defvar *asdf-system* "app")
|
||||
(defvar *ql-libs* (cc *current* "ql-libs.lisp"))
|
||||
(defvar *init-name* "ini_app")
|
||||
(defvar *library-path* (format nil "~Abuild-~A/tmp/"
|
||||
*current*
|
||||
#+android "android"
|
||||
#+ios "ios"))
|
||||
(defvar *require* (list :ecl-curl))
|
||||
(load "platforms/shared/make"))
|
||||
|
||||
;;; rename lib
|
||||
|
||||
(let* ((from #-mobile (cc *current* (format nil "build/tmp/app--all-systems.~A"
|
||||
#+msvc "lib"
|
||||
#-msvc "a"))
|
||||
#+mobile (cc *library-path* "app--all-systems.a"))
|
||||
(to #+msvc "app.lib"
|
||||
#-msvc "libapp.a")
|
||||
(to* #-mobile (cc *current* "build/tmp/" to)
|
||||
#+mobile (cc *library-path* to)))
|
||||
(when (probe-file to*)
|
||||
(delete-file to*))
|
||||
(rename-file from to))
|
||||
|
||||
;;; build 'cl-protobufs.fas' (slow on mobile, will be loaded in background)
|
||||
|
||||
#|
|
||||
#-mobile
|
||||
(asdf:make-build "my-cl-protobufs"
|
||||
:monolithic t
|
||||
:type :fasl
|
||||
:move-here (cc *current* "build/tmp/"))
|
||||
|
||||
#+mobile
|
||||
(progn
|
||||
(pushnew :interpreter *features*)
|
||||
(defvar *asdf-system* "my-cl-protobufs")
|
||||
(defvar *ql-libs* (cc *current* "ql-libs.lisp"))
|
||||
(defvar *build-type* :fasl)
|
||||
(defvar *library-path* (format nil "~Abuild-~A/tmp/"
|
||||
*current*
|
||||
#+android "android"
|
||||
#+ios "ios"))
|
||||
(load "platforms/shared/make"))
|
||||
|
||||
;;; rename lib
|
||||
|
||||
(let* ((from #-mobile (cc *current* "build/tmp/my-cl-protobufs--all-systems.fasb")
|
||||
#+mobile (cc *library-path* "my-cl-protobufs--all-systems.fasb"))
|
||||
(to "cl-protobufs.fas")
|
||||
(to* #-mobile (cc *current* "build/tmp/" to)
|
||||
#+mobile (cc *library-path* to)))
|
||||
(when (probe-file to*)
|
||||
(delete-file to*))
|
||||
(rename-file from to))
|
||||
|#
|
||||
3
examples/meshtastic/mkdirs.sh
Executable file
3
examples/meshtastic/mkdirs.sh
Executable file
|
|
@ -0,0 +1,3 @@
|
|||
mkdir build
|
||||
mkdir build-android
|
||||
mkdir build-ios
|
||||
62
examples/meshtastic/my-cl-protobufs.asd
Normal file
62
examples/meshtastic/my-cl-protobufs.asd
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
;;; This is included here because we don't want to depend on the
|
||||
;;; 'protoc-gen-cl-pb' executable, so we include the already generated lisp
|
||||
;;; files from the proto files of both cl-protobufs and meshtastic.
|
||||
;;;
|
||||
;;; So, this is just the strict minimum necessary for being able to serialize
|
||||
;;; and deserialize meshtastic data.
|
||||
|
||||
(defsystem :my-cl-protobufs
|
||||
:serial t
|
||||
:depends-on (:closer-mop
|
||||
:babel
|
||||
:alexandria
|
||||
:trivial-garbage
|
||||
:cl-base64
|
||||
:local-time
|
||||
:float-features)
|
||||
:components ((:file "lisp/cl-protobufs/pkgdcl")
|
||||
(:file "lisp/cl-protobufs/utilities")
|
||||
(:file "lisp/cl-protobufs/model-classes")
|
||||
(:file "lisp/cl-protobufs/conditions")
|
||||
(:file "lisp/cl-protobufs/parser")
|
||||
(:file "lisp/cl-protobufs/define-proto")
|
||||
(:file "lisp/cl-protobufs/buffers")
|
||||
(:file "lisp/cl-protobufs/text-format")
|
||||
(:file "lisp/cl-protobufs/wire-format")
|
||||
;; cl-proto
|
||||
(:file "lisp/proto/cl-proto/any")
|
||||
(:file "lisp/proto/cl-proto/source-context")
|
||||
(:file "lisp/proto/cl-proto/type")
|
||||
(:file "lisp/proto/cl-proto/api")
|
||||
(:file "lisp/proto/cl-proto/duration")
|
||||
(:file "lisp/proto/cl-proto/empty")
|
||||
(:file "lisp/proto/cl-proto/field-mask")
|
||||
(:file "lisp/proto/cl-proto/struct")
|
||||
(:file "lisp/proto/cl-proto/timestamp")
|
||||
(:file "lisp/proto/cl-proto/wrappers")
|
||||
;;(:file "lisp/proto/cl-proto/descriptor") ; not used
|
||||
;; meshtastic
|
||||
(:file "lisp/proto/meshtastic/xmodem")
|
||||
(:file "lisp/proto/meshtastic/telemetry")
|
||||
(:file "lisp/proto/meshtastic/portnums")
|
||||
(:file "lisp/proto/meshtastic/module-config")
|
||||
(:file "lisp/proto/meshtastic/config")
|
||||
(:file "lisp/proto/meshtastic/channel")
|
||||
(:file "lisp/proto/meshtastic/connection-status")
|
||||
(:file "lisp/proto/meshtastic/admin")
|
||||
(:file "lisp/proto/meshtastic/mesh")
|
||||
(:file "lisp/proto/meshtastic/storeforward")
|
||||
(:file "lisp/proto/meshtastic/apponly")
|
||||
(:file "lisp/proto/meshtastic/localonly")
|
||||
(:file "lisp/proto/meshtastic/clientonly")
|
||||
(:file "lisp/proto/meshtastic/deviceonly")
|
||||
(:file "lisp/proto/meshtastic/remote-hardware")
|
||||
(:file "lisp/proto/meshtastic/cannedmessages")
|
||||
(:file "lisp/proto/meshtastic/mqtt")
|
||||
(:file "lisp/proto/meshtastic/rtttl")
|
||||
;;
|
||||
(:file "lisp/cl-protobufs/serialize")
|
||||
(:file "lisp/cl-protobufs/well-known-types")
|
||||
(:file "lisp/cl-protobufs/message-api")
|
||||
(:file "lisp/cl-protobufs/json")))
|
||||
|
||||
81
examples/meshtastic/platforms/android/AndroidManifest.xml
Normal file
81
examples/meshtastic/platforms/android/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
<?xml version="1.0"?>
|
||||
<manifest package="org.qtproject.example.meshtastic" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.0" android:versionCode="1" android:installLocation="auto">
|
||||
<!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
|
||||
Remove the comment if you do not require these default permissions. -->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.BLUETOOTH"/>
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
|
||||
<!-- The following comment will be replaced upon deployment with default features based on the dependencies of the application.
|
||||
Remove the comment if you do not require these default features. -->
|
||||
|
||||
|
||||
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
|
||||
<application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="cl-meshtastic" android:extractNativeLibs="true">
|
||||
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density" android:name="org.qtproject.qt5.android.bindings.QtActivity" android:label="cl-meshtastic" android:screenOrientation="unspecified" android:launchMode="singleTop" android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
<!-- Application arguments -->
|
||||
<!-- meta-data android:name="android.app.arguments" android:value="arg1 arg2 arg3"/ -->
|
||||
<!-- Application arguments -->
|
||||
<meta-data android:name="android.app.lib_name" android:value="app"/>
|
||||
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
|
||||
<meta-data android:name="android.app.repository" android:value="default"/>
|
||||
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
|
||||
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
|
||||
<!-- Deploy Qt libs as part of package -->
|
||||
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="1"/>
|
||||
<!-- Run with local libs -->
|
||||
<meta-data android:name="android.app.use_local_qt_libs" android:value="1"/>
|
||||
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
|
||||
<meta-data android:name="android.app.load_local_libs_resource_id" android:resource="@array/load_local_libs"/>
|
||||
<meta-data android:name="android.app.load_local_jars" android:value="jar/QtAndroid.jar:jar/QtAndroidExtras.jar:jar/QtAndroidBluetooth.jar:jar/QtAndroidBearer.jar"/>
|
||||
<meta-data android:name="android.app.static_init_classes" android:value="org.qtproject.qt5.android.bluetooth.QtBluetoothBroadcastReceiver"/>
|
||||
<!-- Used to specify custom system library path to run with local system libs -->
|
||||
<!-- <meta-data android:name="android.app.system_libs_prefix" android:value="/system/lib/"/> -->
|
||||
<!-- Messages maps -->
|
||||
<meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/>
|
||||
<meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/>
|
||||
<meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
|
||||
<meta-data android:value="@string/unsupported_android_version" android:name="android.app.unsupported_android_version"/>
|
||||
<!-- Messages maps -->
|
||||
<!-- Splash screen -->
|
||||
<!-- Orientation-specific (portrait/landscape) data is checked first. If not available for current orientation,
|
||||
then android.app.splash_screen_drawable. For best results, use together with splash_screen_sticky and
|
||||
use hideSplashScreen() with a fade-out animation from Qt Android Extras to hide the splash screen when you
|
||||
are done populating your window with content. -->
|
||||
<!-- meta-data android:name="android.app.splash_screen_drawable_portrait" android:resource="@drawable/logo_portrait" / -->
|
||||
<!-- meta-data android:name="android.app.splash_screen_drawable_landscape" android:resource="@drawable/logo_landscape" / -->
|
||||
<!-- meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/logo"/ -->
|
||||
<!-- meta-data android:name="android.app.splash_screen_sticky" android:value="true"/ -->
|
||||
<!-- Splash screen -->
|
||||
<!-- Background running -->
|
||||
<!-- Warning: changing this value to true may cause unexpected crashes if the
|
||||
application still try to draw after
|
||||
"applicationStateChanged(Qt::ApplicationSuspended)"
|
||||
signal is sent! -->
|
||||
<meta-data android:name="android.app.background_running" android:value="false"/>
|
||||
<!-- Background running -->
|
||||
<!-- auto screen scale factor -->
|
||||
<meta-data android:name="android.app.auto_screen_scale_factor" android:value="false"/>
|
||||
<!-- auto screen scale factor -->
|
||||
<!-- extract android style -->
|
||||
<!-- available android:values :
|
||||
* default - In most cases this will be the same as "full", but it can also be something else if needed, e.g., for compatibility reasons
|
||||
* full - useful QWidget & Quick Controls 1 apps
|
||||
* minimal - useful for Quick Controls 2 apps, it is much faster than "full"
|
||||
* none - useful for apps that don't use any of the above Qt modules
|
||||
-->
|
||||
<meta-data android:name="android.app.extract_android_style" android:value="default"/>
|
||||
<!-- extract android style -->
|
||||
</activity>
|
||||
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
4
examples/meshtastic/ql-libs.lisp
Normal file
4
examples/meshtastic/ql-libs.lisp
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
;;; define here how to load eventual, not yet installed dependencies
|
||||
;;; (for cross-compiling only)
|
||||
;;; e.g. (ql:quickload :alexandria)
|
||||
|
||||
BIN
examples/meshtastic/qml/fonts/Hack-Bold.ttf
Normal file
BIN
examples/meshtastic/qml/fonts/Hack-Bold.ttf
Normal file
Binary file not shown.
BIN
examples/meshtastic/qml/fonts/Hack-Regular.ttf
Normal file
BIN
examples/meshtastic/qml/fonts/Hack-Regular.ttf
Normal file
Binary file not shown.
BIN
examples/meshtastic/qml/fonts/tahoma.ttf
Normal file
BIN
examples/meshtastic/qml/fonts/tahoma.ttf
Normal file
Binary file not shown.
BIN
examples/meshtastic/qml/img/busy.gif
Normal file
BIN
examples/meshtastic/qml/img/busy.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
BIN
examples/meshtastic/qml/img/busy.png
Normal file
BIN
examples/meshtastic/qml/img/busy.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.9 KiB |
BIN
examples/meshtastic/qml/img/logo.png
Normal file
BIN
examples/meshtastic/qml/img/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
BIN
examples/meshtastic/qml/img/semaphore.gif
Normal file
BIN
examples/meshtastic/qml/img/semaphore.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 534 B |
BIN
examples/meshtastic/qml/img/send.png
Normal file
BIN
examples/meshtastic/qml/img/send.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
197
examples/meshtastic/qml/main.qml
Normal file
197
examples/meshtastic/qml/main.qml
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Window 2.15
|
||||
|
||||
Item {
|
||||
id: main
|
||||
objectName: "main"
|
||||
width: 300
|
||||
height: 500
|
||||
|
||||
function availableHeight() {
|
||||
var h = Math.round(Qt.inputMethod.keyboardRectangle.y /
|
||||
((Qt.platform.os === "android") ? Screen.devicePixelRatio : 1))
|
||||
return (h === 0) ? main.height : h
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "#e5d8bd"
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: view
|
||||
objectName: "view"
|
||||
width: parent.width
|
||||
height: availableHeight() - rectEdit.height - 3
|
||||
anchors.margins: 3
|
||||
spacing: 3
|
||||
delegate: messageDelegate
|
||||
model: messages
|
||||
|
||||
property string myName
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: messages
|
||||
objectName: "messages"
|
||||
|
||||
function addMessage(message) {
|
||||
append(message)
|
||||
view.positionViewAtEnd()
|
||||
}
|
||||
|
||||
function changeState(state, id) {
|
||||
for (var i = count - 1; i >= 0; i--) {
|
||||
if (get(i).mId === id) {
|
||||
setProperty(i, "mAckState", state)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: messageDelegate
|
||||
|
||||
Item {
|
||||
id: delegate
|
||||
width: Math.max(text.contentWidth, rowSender.width + 4 * text.padding) + 2 * text.padding
|
||||
height: text.contentHeight + 2 * text.padding + sender.contentHeight
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: (mSender === view.myName) ? "#f2f2f2" : "#ffffcc"
|
||||
radius: 12
|
||||
border.width: 0
|
||||
border.color: "#dc1128"
|
||||
|
||||
Row {
|
||||
id: rowSender
|
||||
padding: text.padding
|
||||
spacing: padding
|
||||
|
||||
AnimatedImage {
|
||||
id: semaphore
|
||||
playing: false
|
||||
y: 2
|
||||
width: 8
|
||||
height: width
|
||||
source: "img/semaphore.gif"
|
||||
currentFrame: mAckState
|
||||
visible: (sender.text === view.myName)
|
||||
}
|
||||
|
||||
Text {
|
||||
id: sender
|
||||
font.pixelSize: 10
|
||||
font.bold: true
|
||||
font.family: fontMono.name
|
||||
color: "#8B0000"
|
||||
text: mSender
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
id: timestamp
|
||||
x: delegate.width - contentWidth - text.padding
|
||||
y: text.padding
|
||||
font.pixelSize: 10
|
||||
font.family: fontText.name
|
||||
color: "#505050"
|
||||
text: mTimestamp
|
||||
}
|
||||
|
||||
Text {
|
||||
id: text
|
||||
y: sender.contentHeight
|
||||
width: main.width
|
||||
padding: 5
|
||||
wrapMode: Text.Wrap
|
||||
font.pixelSize: 18
|
||||
font.family: fontText.name
|
||||
color: "#303030"
|
||||
text: mText
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: rectEdit
|
||||
anchors.bottom: parent.bottom
|
||||
width: parent.width
|
||||
height: edit.paintedHeight + 14
|
||||
border.width: 2
|
||||
border.color: edit.focus ? "#228ae3" : "#c0c0c0"
|
||||
radius: 12
|
||||
|
||||
TextArea {
|
||||
id: edit
|
||||
anchors.fill: parent
|
||||
textFormat: TextEdit.PlainText
|
||||
font.pixelSize: 18
|
||||
font.family: fontText.name
|
||||
selectionColor: "#228ae3"
|
||||
selectedTextColor: "white"
|
||||
wrapMode: TextEdit.Wrap
|
||||
textMargin: 0
|
||||
placeholderText: qsTr("message")
|
||||
}
|
||||
|
||||
Image {
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.top
|
||||
anchors.margins: 3
|
||||
width: 38
|
||||
height: width
|
||||
source: "img/send.png"
|
||||
visible: edit.focus
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
edit.focus = Qt.NoFocus
|
||||
Lisp.call("radio:send-message", edit.text)
|
||||
edit.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// busy image / animation
|
||||
|
||||
Item { // shown while loading app (slow...)
|
||||
anchors.fill: parent
|
||||
objectName: "hour_glass"
|
||||
|
||||
Image {
|
||||
anchors.centerIn: parent
|
||||
source: "img/busy.png"
|
||||
}
|
||||
|
||||
Text {
|
||||
width: parent.width
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: main.height / 4
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pixelSize: 20
|
||||
text: qsTr("Loading app...\n(make take a while)")
|
||||
}
|
||||
}
|
||||
|
||||
AnimatedImage { // shown during config
|
||||
objectName: "busy"
|
||||
anchors.centerIn: parent
|
||||
width: 42
|
||||
height: width
|
||||
z: 10
|
||||
source: "img/busy.gif"
|
||||
visible: playing
|
||||
playing: false
|
||||
}
|
||||
|
||||
FontLoader { id: fontText; source: "fonts/tahoma.ttf" }
|
||||
FontLoader { id: fontMono; source: "fonts/Hack-Regular.ttf" }
|
||||
FontLoader { id: fontMono2; source: "fonts/Hack-Bold.ttf" }
|
||||
}
|
||||
137
examples/meshtastic/readme-build.md
Normal file
137
examples/meshtastic/readme-build.md
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
|
||||
Special requirements
|
||||
--------------------
|
||||
|
||||
This example requires both latest ECL from development branch (as of May 2023)
|
||||
plus the hack described in [hacks](hacks/).
|
||||
|
||||
See also notes in [my-cl-protobufs.asd](my-cl-protobufs.asd).
|
||||
|
||||
You will also need **uiop** installed under e.g. `~/quicklisp/local-projects/`
|
||||
(see ASDF sources).
|
||||
|
||||
|
||||
|
||||
Prepare
|
||||
-------
|
||||
|
||||
If you use Qt versions prior to 5.15, you need to adapt the QML module version
|
||||
to your minor Qt version in all files under `qml/`.
|
||||
|
||||
Example: Qt5.**12** => import QtQuick 2.**12**.
|
||||
|
||||
The version number can be omitted in Qt6.
|
||||
|
||||
|
||||
Note
|
||||
----
|
||||
|
||||
Every Lisp file under `lisp/` and every qml/image/font/whatever file under
|
||||
`qml/` is added **automatically** to your Qt project file (both for re-compile
|
||||
and adding to resources included in the executable).
|
||||
|
||||
So, you only need to **manually** care about the usual ASDF project files in
|
||||
`app.asd`.
|
||||
|
||||
But -- *of course* -- you still need to run the respective **qmake** command
|
||||
every time you add new files to the project, because the automation is all
|
||||
defined in `app.pro`.
|
||||
|
||||
|
||||
Run desktop
|
||||
-----------
|
||||
```
|
||||
$ lqml run.lisp
|
||||
```
|
||||
|
||||
|
||||
Build desktop app
|
||||
-----------------
|
||||
```
|
||||
$ cd build
|
||||
|
||||
$ qmake ..
|
||||
$ make
|
||||
```
|
||||
|
||||
|
||||
Build android APK
|
||||
-----------------
|
||||
```
|
||||
$ cd build-android
|
||||
|
||||
$ qmake-android ..
|
||||
$ make apk
|
||||
|
||||
$ ./install-run.sh
|
||||
```
|
||||
Log note: for showing only your own messages, see `log.sh`.
|
||||
|
||||
|
||||
|
||||
Build iOS app
|
||||
-------------
|
||||
|
||||
**Important notes**: the Qt Xcode integration is not perfect, which means: when
|
||||
you include asset files (like in example `swank-server`), they may not be
|
||||
copied to the build directory, the first time you build the app.
|
||||
|
||||
So, it's a good idea to run `qmake-ios` again if there are any startup problems
|
||||
of the app (like asset files not found when launching).
|
||||
|
||||
The first build after a `qmake-ios` will almost always fail (missing build
|
||||
files): don't worry, just run 'Build' (from Xcode) again, and the missing files
|
||||
will be created.
|
||||
|
||||
You also need to check the console from where you launched `./xcode.sh` for
|
||||
eventual errors compiling the Lisp code.
|
||||
```
|
||||
$ cd build-ios
|
||||
|
||||
$ qmake-ios ..
|
||||
|
||||
$ ./xcode.sh
|
||||
```
|
||||
The script above first cross-compiles the Lisp code, then opens **Xcode**.
|
||||
|
||||
Please note (important):
|
||||
|
||||
* before building the app, go to 'Build Settings' / 'Build Options' and set
|
||||
**Enable Bitcode** to **No**
|
||||
|
||||
* if it complains about missing source files when first hitting the 'Run'
|
||||
button, just hit the 'Run' button again (and they will be generated)
|
||||
|
||||
* using latest Xcode, it may complain about the 'Legacy Build System'; just go
|
||||
to 'File' / 'Project Settings' and select 'New Build System'
|
||||
|
||||
* you only need to run `qmake-ios` again after you added/removed files to the
|
||||
project; after every `qmake-ios`, the above steps need to be repeated
|
||||
|
||||
If you cross-compiled ECL for the simulator, it should work there too, but this
|
||||
is currently only tested on **Intel**.
|
||||
|
||||
Simulator note: to show the virtual keyboard, use `cmd-k`.
|
||||
|
||||
|
||||
|
||||
Notes
|
||||
-----
|
||||
|
||||
You will note that sometimes a change of a single Lisp file won't recompile
|
||||
that file on the next `make`; in those cases, just do something like
|
||||
`touch ../app.asd` to force recompilation of everything.
|
||||
|
||||
For conditions during Qt event processing, a fallback restart is added at
|
||||
startup (needed in e.g. Slime).
|
||||
|
||||
If you don't want this, define the following in `app.pro`:
|
||||
```
|
||||
DEFINES += NO_QT_RESTART
|
||||
```
|
||||
|
||||
|
||||
Translations (i18n)
|
||||
-------------------
|
||||
|
||||
Please see [i18n/readme](i18n/readme.md).
|
||||
75
examples/meshtastic/readme.md
Normal file
75
examples/meshtastic/readme.md
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
|
||||
Info
|
||||
----
|
||||
|
||||
Please note: this is **WIP!**. It's only a 'proof-of-concept' version.
|
||||
|
||||
Eventually it will (hopefully) catch up with the official app versions.
|
||||
|
||||
|
||||
|
||||
Technical notes
|
||||
---------------
|
||||
|
||||
This app uses both **BLE** (bluetooth low energy) and the **protobufs**
|
||||
serialization library version 3.
|
||||
|
||||
For BLE you need to build the plugin in `cpp/` first (written in Qt5).
|
||||
|
||||
The cl-protobufs library is included here because the official version doesn't
|
||||
work without the C++ plugin installed (which we don't need here). So I made
|
||||
some small adaptions and included all generated proto Lisp files in order to be
|
||||
independent.
|
||||
|
||||
Unfortunately cl-protobufs loads very slowly on mobile (and conses hugely
|
||||
during startup). On an older phone and a cold startup this may take up to 30
|
||||
seconds. On newer phones and warm startup it should 'only' take around 10
|
||||
seconds (which seems acceptable).
|
||||
|
||||
You will see a json output of all data sent/received. It simply uses the
|
||||
`print-json` convenience function from cl-protobufs.
|
||||
|
||||
|
||||
|
||||
Tested
|
||||
------
|
||||
|
||||
Currently tested on Linux, macOS, android. The macOS version shows an exception
|
||||
during BLE ini, but works nevertheless.
|
||||
|
||||
The iOS version doesn't currently work yet (WIP).
|
||||
|
||||
|
||||
|
||||
How to use cl-meshtastic
|
||||
------------------------
|
||||
|
||||
You currently need 2 meshtastic radio devices, both should be running before
|
||||
you start the app. Both bluetooth and location needs to be enabled (coarse
|
||||
location permission is required on android for BLE to work).
|
||||
|
||||
Pairing might sometimes require some playing around. If it asks for a PIN and
|
||||
your device doesn't have a display (like the RAK starter kit), just use
|
||||
`123456`.
|
||||
|
||||
On Linux you might need to restart the bluetooth service if you want to pair
|
||||
a different device (after already pairing a first one).
|
||||
|
||||
|
||||
|
||||
Run
|
||||
---
|
||||
```
|
||||
lqml run.lisp
|
||||
```
|
||||
Optionally pass `-slime` to start a Swank server, and connect from Emacs with
|
||||
`M-x slime-connect`.
|
||||
|
||||
During development you can pass `-auto`, which will reload all QML files after
|
||||
you made a change to any of them and saved it. For re-initialization after
|
||||
reloading, file `lisp/qml-reload/on-reloaded` will be loaded.
|
||||
|
||||
Closing the window quits the app. If you try to kill it with `ctrl-c`, you need
|
||||
an additional `ctrl-d` to exit from ECL. To quit from Slime, do `(qq)` which is
|
||||
short for `(qquit)`.
|
||||
|
||||
38
examples/meshtastic/run.lisp
Normal file
38
examples/meshtastic/run.lisp
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
(in-package :qml-user)
|
||||
|
||||
(pushnew :qt-plugin *features*)
|
||||
|
||||
(require :asdf)
|
||||
|
||||
(push (merge-pathnames "./")
|
||||
asdf:*central-registry*)
|
||||
|
||||
(asdf:load-system :uiop)
|
||||
(asdf:load-system :trivial-package-local-nicknames)
|
||||
|
||||
;; may take very long on mobile devices
|
||||
(time (asdf:load-system :my-cl-protobufs))
|
||||
|
||||
(push :depends-loaded *features*)
|
||||
|
||||
(asdf:operate 'asdf:load-source-op :app)
|
||||
|
||||
(qset *quick-view*
|
||||
|x| 75
|
||||
|y| 75)
|
||||
|
||||
(defun option (name)
|
||||
(find name (ext:command-args) :test 'search))
|
||||
|
||||
;;; trivial auto reload of all QML files after saving any change
|
||||
|
||||
(when (option "-auto")
|
||||
(load "lisp/qml-reload/auto-reload"))
|
||||
|
||||
;;; for Slime after copying 'lqml-start-swank.lisp' from LQML sources
|
||||
;;; to your Slime directory, which is assumed to be '~/slime/'
|
||||
|
||||
(when (option "-slime")
|
||||
(load "~/slime/lqml-start-swank") ; for 'slime-connect' from Emacs
|
||||
(qlater (lambda () (in-package :radio))))
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue