History

Mostly working send/recv, numeric argument 2 is always zero?

Ada committed 10 months ago

Commit a88e62b1 parent 81c80e72 files · +166 −46Browse files

Showing the changes to interface/src/scripting/OSCScriptingInterface.cpp (1 of 2 files). Show all

Unified · Split

interface/src/scripting/OSCScriptingInterface.cpp+158−34
@@ -12,18 +12,22 @@ · 11 unmodified lines
1212 #include <QLoggingCategory>
1313 #include <QRegularExpression>
1414 #include <QtEndian>
15+#include <QNetworkDatagram>
1516
1617 #include "ScriptEngine.h"
1718 #include "OSCScriptingInterface.h"
1819
1920
20−Q_LOGGING_CATEGORY(osc, "osc")
21+Q_LOGGING_CATEGORY(osc_cat, "osc")
2122
2223 enum OSCTag: char {
2324 Int = 'i',
2425 Float = 'f',
2526 String = 's',
2627 Blob = 'b',
28+ False = 'F',
29+ True = 'T',
30+ Null = 'N',
2731 };
2832
2933 static const QRegularExpression invalidCharacters = QRegularExpression("([ #*,?\\[\\]{}])");
@@ -32,6 +36,9 @@ static const QMap<QChar, QVariant::Type> typeNameMap = { · 2 unmodified lines
3236 { OSCTag::Float, QVariant::Double },
3337 { OSCTag::String, QVariant::String },
3438 { OSCTag::Blob, QVariant::ByteArray },
39+ { OSCTag::False, QVariant::Bool },
40+ { OSCTag::True, QVariant::Bool },
41+ { OSCTag::Null, QVariant::Invalid },
3542 };
3643
3744
@@ -45,34 +52,146 @@ OSCScriptingInterface::OSCScriptingInterface(QObject* parent) : · 7 unmodified lines
4552 {
4653 _socket->bind(QHostAddress(_receiveHost.get()), _receivePort.get());
4754
48− connect(_socket.get(), &QUdpSocket::readyRead, this, &OSCScriptingInterface::receivePacket);
55+ connect(_socket.get(), &QUdpSocket::readyRead, this, &OSCScriptingInterface::readPacket);
4956
50− qCInfo(osc) << "Listening on" << _socket->localAddress();
57+ qCInfo(osc_cat) << "Listening on" << _socket->localAddress();
5158 }
5259
5360 OSCScriptingInterface::~OSCScriptingInterface() {
5461 _socket->close();
55− qCInfo(osc) << "Closed listen socket";
62+ qCInfo(osc_cat) << "Closed listen socket";
5663 }
5764
58−void OSCScriptingInterface::receivePacket() {
59− // TODO
60− emit messageReceived("/test", "", QVariantList());
65+void OSCScriptingInterface::readPacket() {
66+ auto datagram = _socket->receiveDatagram();
67+
68+ if (!datagram.isValid()) { return; }
69+
70+ auto next4 = [](int i) { return i + (4 - i % 4); };
71+
72+ auto data = datagram.data();
73+ int cursor = 0;
74+
75+ QString addr;
76+ QByteArray argTypes;
77+ QVariantList args;
78+
79+ // address
80+ for (int i = 0; (i + cursor) < data.length(); i++) {
81+ if (data[i + cursor] == '\x00') {
82+ addr = QString::fromUtf8(data.constData(), i);
83+ cursor = next4(cursor + i);
84+ break;
85+ }
86+ }
87+
88+ if (addr.isEmpty()) { return; }
89+
90+ // argument types
91+ for (int i = 0; (i + cursor) < data.length(); i++) {
92+ if (data[i + cursor] == '\x00') {
93+ argTypes = QByteArray(data.constData() + cursor, i);
94+ cursor = next4(cursor + i);
95+ break;
96+ }
97+ }
98+
99+ // +1 to exclude the comma
100+ for (int arg = 1; arg < argTypes.length(); arg++) {
101+ switch (argTypes[arg]) {
102+ case OSCTag::Int: {
103+ const char* ptr = data.constData() + cursor;
104+ qint32 tmp = qFromBigEndian<qint32>(ptr);
105+ args.append(QVariantMap {
106+ {"type", QString(OSCTag::Int)},
107+ {"value", tmp},
108+ });
109+ cursor += 4;
110+ } break;
111+
112+ case OSCTag::Float: {
113+ const char* ptr = data.constData() + cursor;
114+ qint32 tmp = qFromBigEndian<qint32>(ptr);
115+ args.append(QVariantMap {
116+ {"type", QString(OSCTag::Float)},
117+ {"value", *reinterpret_cast<float*>(&tmp)},
118+ });
119+ cursor += 4;
120+ } break;
121+
122+ case OSCTag::String: {
123+ QString tmp;
124+
125+ for (int i = 0; i + cursor < data.length(); i++) {
126+ if (data[i + cursor] == '\x00') {
127+ tmp = QString::fromUtf8(data.constData() + cursor, i);
128+ cursor = next4(cursor + i);
129+ break;
130+ }
131+ }
132+
133+ args.append(QVariantMap {
134+ {"type", QString(OSCTag::String)},
135+ {"value", tmp},
136+ });
137+ } break;
138+
139+ case OSCTag::Blob: {
140+ const char* ptr = data.constData() + cursor;
141+ auto len = qFromBigEndian<qint32>(ptr);
142+ cursor += 4;
143+
144+ args.append(QVariantMap {
145+ {"type", QString(OSCTag::Blob)},
146+ {"value", QByteArray(data.constData() + cursor, len)},
147+ });
148+
149+ cursor = next4(cursor + len);
150+ } break;
151+
152+ case OSCTag::False:
153+ args.append(QVariantMap {
154+ {"type", QString(OSCTag::False)},
155+ {"value", false},
156+ });
157+ break;
158+
159+ case OSCTag::True:
160+ args.append(QVariantMap {
161+ {"type", QString(OSCTag::True)},
162+ {"value", true},
163+ });
164+ break;
165+
166+ case OSCTag::Null:
167+ args.append(QVariantMap {
168+ {"type", QString(OSCTag::Null)},
169+ {"value", QVariant()},
170+ });
171+ break;
172+
173+ default:
174+ qCCritical(osc_cat, "Unknown type '%c'", static_cast<char>(argTypes[arg]));
175+ return;
176+ }
177+ }
178+
179+ emit packetReceived(addr, args);
61180 }
62181
63−void OSCScriptingInterface::sendMessage(const QString& address, const QString &argumentTypes, const QVariantList& arguments) {
182+void OSCScriptingInterface::sendPacket(const QString& address, const QVariantList& arguments) {
64183 Q_ASSERT(_socket);
65184
66185 // FIXME: how do you get the script engine???
67186 struct HackRemoveThis {
68187 void raiseException(QString msg) {
69− qCCritical(osc) << msg;
188+ qCCritical(osc_cat) << msg;
70189 }
71190 };
72191
73192 auto scriptEngine = std::make_unique<HackRemoveThis>(); //_scriptManager->engine();
74193
75− if (address.length() < 2 || address[0] != '/') {
194+ if (address.length() < 2 || !address.startsWith('/') || address.endsWith('/')) {
76195 scriptEngine->raiseException("address must be at least two characters and start with '/'");
77196 return;
78197 }
@@ -82,11 +201,6 @@ void OSCScriptingInterface::sendMessage(const QString& address, const QString &a · 3 unmodified lines
82201 return;
83202 }
84203
85− if (argumentTypes.length() != arguments.length()) {
86− scriptEngine->raiseException(QString("argumentTypes.length (%1) does not match arguments.length (%2)").arg(argumentTypes.length()).arg(arguments.length()));
87− return;
88− }
89−
90204 auto pad4 = [](QByteArray& array) {
91205 array.append((4 - array.length() % 4) % 4, 0);
92206 };
@@ -105,19 +219,26 @@ void OSCScriptingInterface::sendMessage(const QString& address, const QString &a · 12 unmodified lines
105219 for (int i = 0; i < arguments.length(); i++) {
106220 auto arg = arguments[i];
107221
108− if (!typeNameMap.contains(argumentTypes[i])) {
109− scriptEngine->raiseException(QString("Unknown type tag '%1'").arg(argumentTypes[i]));
110− return;
222+ QVariant::Type expectedType;
223+ QVariant value;
224+
225+ if (arg.type() == QVariant::Map) {
226+ auto map = arg.toMap();
227+ auto typeName = map.value("type").toString();
228+ if (!typeNameMap.contains(typeName[0])) {
229+ scriptEngine->raiseException(QString("Unknown type '%c' on argument %d").arg(typeName).arg(i));
230+ return;
231+ }
232+ expectedType = typeNameMap[typeName[0]];
233+ value = map.value("value");
234+ } else {
235+ expectedType = arg.type();
236+ value = arg;
111237 }
112238
113− auto expectedType = typeNameMap[argumentTypes[i]];
114− if (
115− expectedType != arg.type() &&
116− // JS doesn't have ints, so allow doubles to be interpreted as ints
117− arg.type() != QVariant::Double
118− ) {
119− scriptEngine->raiseException(QString("Specified argument type '%1' does not match value of type '%2'").arg(QVariant::typeToName(expectedType)).arg(arg.typeName()));
120− return;
239+ if (value.isNull()) {
240+ bytes.append(OSCTag::Null);
241+ continue;
121242 }
122243
123244 switch (expectedType) {
@@ -127,7 +248,7 @@ void OSCScriptingInterface::sendMessage(const QString& address, const QString &a · 3 unmodified lines
127248 auto* ptr = bodyBytes.data() + bodyBytes.length();
128249 bodyBytes.append(4, 0);
129250
130− qToBigEndian(static_cast<qint32>(arg.toDouble()), ptr);
251+ qToBigEndian(static_cast<qint32>(value.toDouble()), ptr);
131252 } break;
132253
133254 case QVariant::Double: {
@@ -137,14 +258,14 @@ void OSCScriptingInterface::sendMessage(const QString& address, const QString &a · 3 unmodified lines
137258 bodyBytes.append(4, 0);
138259
139260 // FIXME: use std::bit_cast instead of this hack once c++20 is in
140− auto tmp = static_cast<float>(arg.toDouble());
261+ auto tmp = static_cast<float>(value.toDouble());
141262 qToBigEndian(*reinterpret_cast<quint32*>(&tmp), ptr);
142263 } break;
143264
144265 case QVariant::String: {
145266 bytes.append(OSCTag::String);
146267
147− auto stringArg = arg.toString();
268+ auto stringArg = value.toString();
148269
149270 if (stringArg.contains(QChar(0))) {
150271 scriptEngine->raiseException("String contains null (0x00) character");
@@ -161,7 +282,7 @@ void OSCScriptingInterface::sendMessage(const QString& address, const QString &a · 10 unmodified lines
161282 case QVariant::ByteArray: {
162283 bytes.append(OSCTag::Blob);
163284
164− auto bytesArg = arg.toByteArray();
285+ auto bytesArg = value.toByteArray();
165286
166287 // write length prefix
167288 auto* ptr = bodyBytes.data() + bodyBytes.length();
@@ -175,10 +296,13 @@ void OSCScriptingInterface::sendMessage(const QString& address, const QString &a · 7 unmodified lines
175296 pad4(bodyBytes);
176297 } break;
177298
178− // unreachable
179− default:
180− Q_ASSERT(false);
299+ case QVariant::Bool:
300+ bytes.append(value.toBool() ? OSCTag::True : OSCTag::False);
181301 break;
302+
303+ default:
304+ scriptEngine->raiseException(QString("Unserializable type %1").arg(value.typeName()));
305+ return;
182306 }
183307 }
184308
@@ -191,5 +315,5 @@ void OSCScriptingInterface::sendMessage(const QString& address, const QString &a · 6 unmodified lines
191315
192316 _socket->writeDatagram(bytes, QHostAddress(_sendHost.get()), _sendPort.get());
193317
194− qCDebug(osc) << QHostAddress(_sendHost.get()) << _sendPort.get() << ":" << bytes.toHex(' ');
318+ qCDebug(osc_cat) << QHostAddress(_sendHost.get()) << _sendPort.get() << ":" << bytes.toHex(' ');
195319 }