ArnLib  4.0.x
Active Registry Network
ArnRpc.cpp
Go to the documentation of this file.
1 // Copyright (C) 2010-2022 Michael Wiklund.
2 // All rights reserved.
3 // Contact: arnlib@wiklunden.se
4 //
5 // This file is part of the ArnLib - Active Registry Network.
6 // Parts of ArnLib depend on Qt and/or other libraries that have their own
7 // licenses. Usage of these other libraries is subject to their respective
8 // license agreements.
9 //
10 // GNU Lesser General Public License Usage
11 // This file may be used under the terms of the GNU Lesser General Public
12 // License version 2.1 as published by the Free Software Foundation and
13 // appearing in the file LICENSE_LGPL.txt included in the packaging of this
14 // file. In addition, as a special exception, you may use the rights described
15 // in the Nokia Qt LGPL Exception version 1.1, included in the file
16 // LGPL_EXCEPTION.txt in this package.
17 //
18 // GNU General Public License Usage
19 // Alternatively, this file may be used under the terms of the GNU General Public
20 // License version 3.0 as published by the Free Software Foundation and appearing
21 // in the file LICENSE_GPL.txt included in the packaging of this file.
22 //
23 // Other Usage
24 // Alternatively, this file may be used in accordance with the terms and conditions
25 // contained in a signed written agreement between you and Michael Wiklund.
26 //
27 // This program is distributed in the hope that it will be useful, but WITHOUT ANY
28 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
29 // PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
30 //
31 
32 #include "ArnInc/ArnRpc.hpp"
33 #include "private/ArnRpc_p.hpp"
34 #include "ArnInc/ArnM.hpp"
35 #include "ArnInc/XStringMap.hpp"
36 #include "ArnInc/ArnLib.hpp"
37 #include "ArnInc/ArnCompat.hpp"
38 #include <QMetaType>
39 #include <QMetaMethod>
40 #include <QTimer>
41 #include <QDataStream>
42 #include <QVariant>
43 #include <QDebug>
44 
45 using Arn::XStringMap;
46 
47 #define RPC_STORAGE_NAME "_ArnRpcStorage"
48 
49 ArnRpc::RpcTypeInfo ArnRpc::_rpcTypeInfoTab[] = {
50 // rpcTypeName qtTypeName typeId
51  {"int", "int", QMetaType::Int},
52  {"uint", "uint", QMetaType::UInt},
53  {"int64", "qint64", QMetaType::LongLong},
54  {"uint64", "quint64", QMetaType::ULongLong},
55  {"bool", "bool", QMetaType::Bool},
56  {"float", "float", QMetaType::Float},
57  {"double", "double", QMetaType::Double},
58  {"bytes", "QByteArray", QMetaType::QByteArray},
59  {"date", "QDate", QMetaType::QDate},
60  {"time", "QTime", QMetaType::QTime},
61  {"datetime", "QDateTime", QMetaType::QDateTime},
62  {"list", "QStringList", QMetaType::QStringList},
63  {"string", "QString", QMetaType::QString},
64  {"", "", 0} // End marker & Null case
65 };
66 
67 
69 class ArnRpcReceiverStorage : public QObject
70 {
71 public:
72  ArnRpc* _rpcSender;
73 
74  explicit ArnRpcReceiverStorage( QObject* parent) :
75  QObject( parent)
76  {
77  _rpcSender = 0;
78  }
79 };
80 
81 
82 class ArnDynamicSignals : public QObject
83 {
84 public:
85  explicit ArnDynamicSignals( ArnRpc* rpc);
86  int qt_metacall( QMetaObject::Call call, int id, void **arguments);
87  bool addSignal( QObject *sender, int signalId, const QByteArray& funcName);
88 
89 private:
90  struct Slot {
91  QByteArray funcName;
92  QList<QByteArray> typeNames;
93  QList<QByteArray> parNames;
94  ArnRpc::Invoke invokeFlags;
95  };
96  QList<Slot> _slotTab;
97 
98  int _slotIdCount;
99  ArnRpc* _rpc;
100 };
101 
102 
103 ArnDynamicSignals::ArnDynamicSignals( ArnRpc* rpc) :
104  QObject( rpc)
105 {
106  _slotIdCount = QObject::staticMetaObject.methodCount();
107  _rpc = rpc;
108 }
109 
110 
111 int ArnDynamicSignals::qt_metacall( QMetaObject::Call call, int id, void **arguments)
112 {
113  if (Arn::debugRPC) qDebug() << "Rpc metaCall-1: call=" << call << " id=" << id;
114 
115  id = QObject::qt_metacall( call, id, arguments);
116  if ((id < 0) || (call != QMetaObject::InvokeMetaMethod))
117  return id;
118 
119  Q_ASSERT(id < _slotTab.size());
120 
121  MQGenericArgument args[8];
122  const Slot& slot = _slotTab.at( id);
123  int argNum = slot.typeNames.size();
124 
125  for (int i = 0; i < argNum; ++i) {
126  args[i] = MQGenericArgument( slot.typeNames.at(i).constData(),
127  slot.parNames.at(i).constData(),
128  arguments[i + 1]);
129  }
130 
131  if (Arn::debugRPC) qDebug() << "Rpc metaCall-2: call=" << call << " id=" << id
132  << " func=" << slot.funcName << " flags=" << slot.invokeFlags;
133 
134  _rpc->invoke( QString::fromLatin1( slot.funcName),
135  slot.invokeFlags,
136  args[0],
137  args[1],
138  args[2],
139  args[3],
140  args[4],
141  args[5],
142  args[6],
143  args[7]);
144 
145  return -1;
146 }
147 
148 
149 bool ArnDynamicSignals::addSignal( QObject *sender, int signalId, const QByteArray& funcName)
150 {
151  const QMetaObject* metaObject = sender->metaObject();
152  QMetaMethod method = metaObject->method( signalId);
153 
154  Slot slot;
155  slot.funcName = funcName;
156  slot.typeNames = method.parameterTypes();
157  slot.parNames = method.parameterNames();
158  ArnRpc::Invoke ivf;
159  ivf.set( ivf.NoQueue, QByteArray( method.tag()).contains("no_queue"));
160  slot.invokeFlags = ivf;
161  _slotTab += slot;
162 
163  bool status = QMetaObject::connect( sender, signalId, this, _slotIdCount);
164  ++_slotIdCount;
165  return status;
166 }
168 
169 
170 
171 ArnRpcPrivate::ArnRpcPrivate()
172 {
173  _pipe = arnNullptr;
174  _receiver = arnNullptr;
175  _receiverStorage = arnNullptr;
176  _receiverMethodsParam = arnNullptr;
177  _convVariantPar = false;
178  _isIncludeSender = false;
179  _dynamicSignals = arnNullptr;
180  _isHeartBeatOk = true;
181  _timerHeartBeatSend = new QTimer;
182  _timerHeartBeatCheck = new QTimer;
183 }
184 
185 
186 ArnRpcPrivate::~ArnRpcPrivate()
187 {
188  delete _timerHeartBeatSend;
189  delete _timerHeartBeatCheck;
190 }
191 
192 
193 void ArnRpc::init()
194 {
195  Q_D(ArnRpc);
196 
197  d->_dynamicSignals = new ArnDynamicSignals( this);
198 
199  connect( d->_timerHeartBeatSend, SIGNAL(timeout()), this, SLOT(timeoutHeartBeatSend()));
200  connect( d->_timerHeartBeatCheck, SIGNAL(timeout()), this, SLOT(timeoutHeartBeatCheck()));
201 }
202 
203 
204 ArnRpc::ArnRpc( QObject* parent)
205  : QObject( parent)
206  , d_ptr( new ArnRpcPrivate)
207 {
208  init();
209 }
210 
211 
212 ArnRpc::ArnRpc( ArnRpcPrivate& dd, QObject* parent)
213  : QObject( parent)
214  , d_ptr( &dd)
215 {
216  init();
217 }
218 
219 
221 {
222  delete d_ptr;
223 }
224 
225 
226 QString ArnRpc::pipePath() const
227 {
228  Q_D(const ArnRpc);
229 
230  if (!d->_pipe) return QString();
231 
232  return d->_pipe->path();
233 }
234 
235 
236 bool ArnRpc::open( const QString& pipePath)
237 {
238  Q_D(ArnRpc);
239 
240  setPipe( arnNullptr); // Remove any existing pipe
241 
243  QString path = pipePath;
244  if (d->_mode.is( Mode::Provider) != Arn::isProviderPath( path))
245  path = Arn::twinPath( path);
246 
247  ArnPipe* pipe = new ArnPipe;
248  if (!d->_mode.is( Mode::Provider))
249  pipe->setMaster();
250  if (d->_mode.is( Mode::AutoDestroy))
251  pipe->setAutoDestroy();
252 
253  bool stat;
254  if (d->_mode.is( Mode::UuidPipe))
255  stat = pipe->openUuid( path);
256  else
257  stat = pipe->open( path);
258 
259  if (stat) {
260  pipe->setSendSeq( d->_mode.is( Mode::SendSequence));
261  pipe->setCheckSeq( d->_mode.is( Mode::CheckSequence));
262  }
263  else {
264  delete pipe;
265  return false;
266  }
267 
268  setPipe( pipe);
269  return true;
270 }
271 
272 
274 {
275  Q_D(ArnRpc);
276 
277  if (d->_pipe) {
278  if (Arn::debugRPC) qDebug() << "Rpc delete pipe: path=" << d->_pipe->path();
279  d->_pipe->deleteLater();
280  }
281  d->_pipe = pipe;
282  if (d->_pipe) {
283  d->_pipe->setParent( this);
284  connect( d->_pipe, SIGNAL(changed(QByteArray)), this, SLOT(pipeInput(QByteArray)),
285  Qt::QueuedConnection);
286  connect( d->_pipe, SIGNAL(arnLinkDestroyed()), this, SLOT(destroyPipe()));
287  connect( d->_pipe, SIGNAL(outOfSequence()), this, SIGNAL(outOfSequence()));
288  }
289 }
290 
291 
293 {
294  Q_D(const ArnRpc);
295 
296  return d->_pipe;
297 }
298 
299 
300 bool ArnRpc::setReceiver( QObject* receiver, bool useTrackRpcSender)
301 {
302  Q_D(ArnRpc);
303 
304  bool stat = true;
305 
306  d->_receiver = receiver;
307  deleteReceiverMethodsParam();
308  d->_receiverStorage = arnNullptr; // Default: don't need be able to track rpcSender
309  if (!receiver) return stat;
310 
311  bool isSameThread = receiver->thread() == this->thread();
312  if (useTrackRpcSender && isSameThread) {
313  d->_receiverStorage = d->_receiver->findChild<ArnRpcReceiverStorage*>( RPC_STORAGE_NAME);
314  if (!d->_receiverStorage) {
315  d->_receiverStorage = new ArnRpcReceiverStorage( d->_receiver);
316  d->_receiverStorage->setObjectName( RPC_STORAGE_NAME);
317  }
318  }
319  else if (useTrackRpcSender) {
320  errorLog( QString(tr("Can't track rpcSender to receiver in other thread")),
322  stat = false;
323  }
324 
325  return stat;
326 }
327 
328 
329 QObject*ArnRpc::receiver() const
330 {
331  Q_D(const ArnRpc);
332 
333  return d->_receiver;
334 }
335 
336 
337 void ArnRpc::setMethodPrefix( const QString& prefix)
338 {
339  Q_D(ArnRpc);
340 
341  d->_methodPrefix = prefix.toLatin1();
342  deleteReceiverMethodsParam();
343 }
344 
345 
346 QString ArnRpc::methodPrefix() const
347 {
348  Q_D(const ArnRpc);
349 
350  return d->_methodPrefix;
351 }
352 
353 
354 bool ArnRpc::isConvVariantPar() const
355 {
356  Q_D(const ArnRpc);
357 
358  return d->_convVariantPar;
359 }
360 
361 
362 void ArnRpc::setConvVariantPar( bool convVariantPar)
363 {
364  Q_D(ArnRpc);
365 
366  d->_convVariantPar = convVariantPar;
367 }
368 
369 
371 {
372  Q_D(ArnRpc);
373 
374  d->_isIncludeSender = v;
375 }
376 
377 
379 {
380  Q_D(ArnRpc);
381 
382  d->_mode = mode;
383 }
384 
385 
387 {
388  Q_D(const ArnRpc);
389 
390  return d->_mode;
391 }
392 
393 
395 {
396  Q_D(ArnRpc);
397 
398  if (time == 0)
399  d->_timerHeartBeatSend->stop();
400  else
401  d->_timerHeartBeatSend->start( time * 1000);
402 }
403 
404 
406 {
407  Q_D(const ArnRpc);
408 
409  return d->_timerHeartBeatSend->interval() / 1000;
410 }
411 
412 
414 {
415  Q_D(ArnRpc);
416 
417  if (time == 0)
418  d->_timerHeartBeatCheck->stop();
419  else
420  d->_timerHeartBeatCheck->start( time * 1000);
421 }
422 
423 
425 {
426  Q_D(const ArnRpc);
427 
428  return d->_timerHeartBeatCheck->interval() / 1000;
429 }
430 
431 
433 {
434  Q_D(const ArnRpc);
435 
436  return d->_isHeartBeatOk;
437 }
438 
439 
440 void ArnRpc::addSenderSignals( QObject* sender, const QString& prefix)
441 {
442  Q_D(ArnRpc);
443 
444  const QMetaObject* metaObject = sender->metaObject();
445  int methodCount = metaObject->methodCount();
446  QByteArray methodSignCompHead;
447  int methodIdCompHead = -1;
448  for (int methodId = 0; methodId < methodCount; ++methodId) {
449  QMetaMethod method = metaObject->method( methodId);
450  if (method.methodType() != QMetaMethod::Signal) continue;
451 
452  QByteArray methodSign = methodSignature( method);
453  if (!methodSign.startsWith( prefix.toLatin1())) continue;
454 
455  QByteArray methodName = methodSign.left( methodSign.indexOf('('));
456  QByteArray funcName = methodName.mid( prefix.size());
457 
458  QByteArray methodSignComp = methodSign;
459  methodSignComp.chop(1); // Remove last ")"
460 
461  if (!d->_mode.is( Mode::NoDefaultArgs) // When using Default args ...
462  && methodSignCompHead.startsWith( methodSignComp) // Starts with same signatur ...
463  && hasSameParamNames( method, metaObject->method( methodIdCompHead))) // and same param names
464  continue; // Skip it, to prohibit multiple rpc calls.
465 
466  methodSignCompHead = methodSignComp;
467  methodIdCompHead = methodId;
468  d->_dynamicSignals->addSignal( sender, methodId, funcName);
469  }
470 }
471 
472 
474 {
475  Q_D(ArnRpc);
476 
477  if (!d->_receiverStorage) return arnNullptr;
478 
479  return d->_receiverStorage->_rpcSender;
480 }
481 
482 
483 ArnRpc* ArnRpc::rpcSender( QObject *receiver)
484 {
485  if (!receiver) return arnNullptr;
486 
487  ArnRpcReceiverStorage* recStore = receiver->findChild<ArnRpcReceiverStorage*>( RPC_STORAGE_NAME);
488  if (!recStore) return arnNullptr;
489 
490  return recStore->_rpcSender;
491 }
492 
493 
494 bool ArnRpc::invoke( const QString& funcName,
495  MQGenericArgument arg1,
496  MQGenericArgument arg2,
497  MQGenericArgument arg3,
498  MQGenericArgument arg4,
499  MQGenericArgument arg5,
500  MQGenericArgument arg6,
501  MQGenericArgument arg7,
502  MQGenericArgument arg8)
503 {
504  Q_D(ArnRpc);
505 
506  if (!d->_pipe || !d->_pipe->isOpen()) {
507  errorLog( QString(tr("Pipe not open")),
509  return false;
510  }
511 
512  XStringMap xsmCall;
513  xsmCall.add("", funcName.toLatin1());
514 
515  int nArg = 0;
516  bool stat = true; // Default ok
517  stat &= xsmAddArg( xsmCall, arg1, 1, nArg);
518  stat &= xsmAddArg( xsmCall, arg2, 2, nArg);
519  stat &= xsmAddArg( xsmCall, arg3, 3, nArg);
520  stat &= xsmAddArg( xsmCall, arg4, 4, nArg);
521  stat &= xsmAddArg( xsmCall, arg5, 5, nArg);
522  stat &= xsmAddArg( xsmCall, arg6, 6, nArg);
523  stat &= xsmAddArg( xsmCall, arg7, 7, nArg);
524  stat &= xsmAddArg( xsmCall, arg8, 8, nArg);
525 
526  if (stat) {
527  *d->_pipe = xsmCall.toXString();
528  }
529  return stat;
530 }
531 
532 
533 bool ArnRpc::invoke( const QString& funcName,
534  Invoke invokeFlags,
535  MQGenericArgument arg1,
536  MQGenericArgument arg2,
537  MQGenericArgument arg3,
538  MQGenericArgument arg4,
539  MQGenericArgument arg5,
540  MQGenericArgument arg6,
541  MQGenericArgument arg7,
542  MQGenericArgument arg8)
543 {
544  Q_D(ArnRpc);
545 
546  if (!d->_pipe || !d->_pipe->isOpen()) {
547  errorLog( QString(tr("Pipe not open")),
549  return false;
550  }
551 
552  XStringMap xsmCall;
553  xsmCall.add("", funcName.toLatin1());
554 
555  int nArg = 0;
556  bool stat = true; // Default ok
557  stat &= xsmAddArg( xsmCall, arg1, 1, nArg);
558  stat &= xsmAddArg( xsmCall, arg2, 2, nArg);
559  stat &= xsmAddArg( xsmCall, arg3, 3, nArg);
560  stat &= xsmAddArg( xsmCall, arg4, 4, nArg);
561  stat &= xsmAddArg( xsmCall, arg5, 5, nArg);
562  stat &= xsmAddArg( xsmCall, arg6, 6, nArg);
563  stat &= xsmAddArg( xsmCall, arg7, 7, nArg);
564  stat &= xsmAddArg( xsmCall, arg8, 8, nArg);
565 
566  if (stat) {
567  if (invokeFlags.is( Invoke::NoQueue)) {
568  ARN_RegExp rx("^" + funcName + "\\b");
569  d->_pipe->setValueOverwrite( xsmCall.toXString(), rx);
570  }
571  else
572  *d->_pipe = xsmCall.toXString();
573  }
574  return stat;
575 }
576 
577 
578 bool ArnRpc::xsmAddArg( XStringMap& xsm, const MQGenericArgument& arg, uint index, int& nArg)
579 {
580  Q_D(ArnRpc);
581 
582  if (!arg.name() || !arg.label() || !arg.data()) return true; // Empty arg
583  if (nArg + 1 != int( index)) return true; // Out of seq - Finished
584 
586  QByteArray typeName = arg.name();
587 
588 #if QT_VERSION >= 0x060000
589  QMetaType mType = QMetaType::fromName( typeName);
590  int type = mType.id();
591 #else
592  int type = QMetaType::type( typeName.constData());
593 #endif
594 
595  if (!type) {
596  errorLog( QString(tr("Unknown type:") + typeName.constData()),
598  return false;
599  }
600  const RpcTypeInfo& rpcTypeInfo = typeInfoFromId( type);
601 
603  QByteArray argLabel = arg.label();
604 
606  QByteArray argDataDump;
607  QStringList argDataList;
608  bool isBinaryType = false;
609 #if QT_VERSION >= 0x060000
610  QVariant varArg( mType, arg.data());
611 #else
612  QVariant varArg( type, arg.data());
613 #endif
614  if (type == QMetaType::QStringList) {
615  argDataList = varArg.toStringList();
616  }
617  else if (type == QMetaType::QByteArray) {
618  argDataDump = varArg.toByteArray();
619  }
620  else if (varArg.canConvert( QVariant::String)) {
621  argDataDump = varArg.toString().toUtf8();
622  }
623  else {
624  QDataStream stream( &argDataDump, QIODevice::WriteOnly);
625  stream.setVersion( DATASTREAM_VER);
626  stream << quint8(0); // Spare
627  stream << quint8( DATASTREAM_VER);
628  if (!QMetaType::save( stream, type, arg.data())) {
629  errorLog( QString(tr("Can't export type:") + typeName.constData()),
631  return false;
632  }
633  isBinaryType = true;
634  }
635 
637  QByteArray argKey;
638  if (!rpcTypeInfo.typeId)
639  argKey = (isBinaryType ? "tb<" : "t<") + typeName + ">";
640  else
641  argKey = rpcTypeInfo.rpcTypeName;
642  if (!argLabel.isEmpty()) {
643  if (d->_mode.is( Mode::NamedArg) && rpcTypeInfo.typeId)
644  argKey = argLabel;
645  else if (d->_mode.is( Mode::NamedArg) || d->_mode.is( Mode::NamedTypedArg))
646  argKey = argLabel + ":" + argKey;
647  else
648  argKey += "." + argLabel;
649  }
650 
652  if (type == QMetaType::QStringList) { // Handle list
653  int i = 0;
654  QString argData;
655  if (!argDataList.isEmpty() && !argDataList.at(0).isEmpty()) {
656  argData = argDataList.at(0);
657  ++i;
658  }
659  xsm.add( argKey, argData);
660  // Output each element in list
661  for(; i < argDataList.size(); ++i) {
662  argData = argDataList.at(i);
663  if (argData.isEmpty() || argData.contains( QChar('=')))
664  xsm.add("+", argData);
665  else
666  xsm.add("", argData);
667  }
668  }
669  else {
670  xsm.add( argKey, argDataDump);
671  }
672 
673  ++nArg;
674  return true; // 1 arg added
675 }
676 
677 
678 void ArnRpc::pipeInput( const QByteArray& data)
679 {
680  Q_D(ArnRpc);
681 
682  if (Arn::debugRPC) qDebug() << "Rpc pipeInput: path=" << d->_pipe->path()
683  << " itemId=" << d->_pipe->itemId() << " data=" << data;
684 
685  if (!d->_receiver) {
686  errorLog( QString(tr("Can't invoke method: receiver=0")), ArnError::RpcReceiveError);
687  return;
688  }
689 
691  if (data.startsWith('"')) { // Text is received
692  int endSize = data.endsWith('"') ? (data.size() - 1) : data.size();
693  if (endSize < 1)
694  endSize = 1;
695  emit textReceived( QString::fromUtf8( data.mid( 1, endSize - 1), endSize - 1));
696  return;
697  }
698 
699  XStringMap xsmCall( data);
700 
702  QByteArray rpcFunc = xsmCall.value(0);
703  if (rpcFunc == "$heartbeat") // Built in Heart beat support
704  return funcHeartBeat( xsmCall);
705  if (rpcFunc == "$arg")
706  return funcArg( xsmCall);
707  if (rpcFunc == "$help") // Built in Help
708  return funcHelp( xsmCall);
709 
711  // qDebug() << "rpc pipeInput: data=" << data;
712  QByteArray methodName = d->_methodPrefix + rpcFunc;
713  if (d->_mode.is( Mode::UseDefaultCall)) {
714  setupReceiverMethodsParam(); // Setup searching method data structure
715 
716  int pslotIndex = d->_receiverMethodsParam->methodNames.indexOf( methodName);
717  if (pslotIndex < 0) { // Method not found
718  emit defaultCall( data);
719  return;
720  }
721  }
722 
724  ArgInfo argInfo[21]; // 0..9: Used args, 10..19: Default args, 20: Null arg
725  int argc = 0;
726 
727  if (d->_isIncludeSender) {
728  argInfo[ argc].arg = Q_ARG( ArnRpc*, this);
729  ++argc;
730  }
731 
732  bool stat = true; // Default ok
733  int index = 1; // Start after function name in xsm
734  while (index > 0) {
735  if (index >= xsmCall.size())
736  break; // End of args
737  if (argc > 10) {
738  errorLog( QString(tr("To many args:") + QString::number( argc))
739  + tr(" method=") + methodName.constData(),
741  stat = false;
742  break;
743  }
744  stat = xsmLoadArg( xsmCall, argInfo[ argc], index, methodName);
745  if (!stat) break;
746  ++argc;
747  }
748 
749  char argOrder[10];
750  if (stat) {
751  for (int i = 0; i < 10; ++i) {
752  argOrder[i] = char(i); // Set default order 1 to 1
753  }
754  stat = argLogic( argInfo, argOrder, argc, methodName);
755  for (int i = argc; i < 10; ++i) {
756  argOrder[i] = char(20); // Set unused to null arg
757  }
758  }
759 
760  if (stat) {
761  bool useVarPar = checkConvVarPar( methodName, argc);
762  for (int i = d->_isIncludeSender; i < argc; ++i) {
763  stat = importArgData( argInfo[ int(argOrder[i])], methodName, useVarPar);
764  if (!stat) break;
765  }
766  }
767 
768  if (stat) {
769  if (d->_receiverStorage)
770  d->_receiverStorage->_rpcSender = this;
771  stat = QMetaObject::invokeMethod( d->_receiver,
772  methodName.constData(),
773  Qt::AutoConnection,
774  argInfo[ int(argOrder[0])].arg,
775  argInfo[ int(argOrder[1])].arg,
776  argInfo[ int(argOrder[2])].arg,
777  argInfo[ int(argOrder[3])].arg,
778  argInfo[ int(argOrder[4])].arg,
779  argInfo[ int(argOrder[5])].arg,
780  argInfo[ int(argOrder[6])].arg,
781  argInfo[ int(argOrder[7])].arg,
782  argInfo[ int(argOrder[8])].arg,
783  argInfo[ int(argOrder[9])].arg);
784  if (d->_receiverStorage)
785  d->_receiverStorage->_rpcSender = arnNullptr;
786  if(!stat) {
787  errorLog( QString(tr("Can't invoke method:")) + methodName.constData(),
789  sendText("Can't invoke method, use $help");
790  }
791  }
792 
794  for (int i = d->_isIncludeSender; i < 20; ++i) {
795  ArgInfo& aiSlot = argInfo[i];
796  if (aiSlot.isArgAlloc && aiSlot.arg.data()) {
797  int type = QMetaType::type( aiSlot.arg.name());
798  QMetaType::destroy( type, aiSlot.arg.data());
799  }
800  if (aiSlot.isDataAlloc) {
801  void* data = const_cast<void*>( aiSlot.data);
802  QMetaType::destroy( aiSlot.typeId, data);
803  }
804  }
805 }
806 
807 
808 bool ArnRpc::xsmLoadArg( const XStringMap& xsm, ArgInfo& argInfo, int &index,
809  const QByteArray& methodName)
810 {
811  Q_D(ArnRpc);
812 
814  const QByteArray& typeKey = xsm.keyRef( index); // MW: Why ref &typeKey not working in debugger?
815  QByteArray rpcType;
816  argInfo.isPositional = true; // Default
817  int sepPos = typeKey.indexOf(':');
818  if (sepPos >=0)
819  argInfo.isPositional = false;
820  else
821  sepPos = typeKey.indexOf('.');
822  if (sepPos >=0) {
823  QByteArray t1 = typeKey.left( sepPos);
824  QByteArray t2 = typeKey.mid( sepPos + 1);
825  rpcType = argInfo.isPositional ? t1 : t2;
826  argInfo.name = argInfo.isPositional ? t2 : t1;
827  argInfo.hasType = !rpcType.isEmpty();
828  argInfo.hasName = !argInfo.name.isEmpty();
829  }
830  else {
831  rpcType = typeKey; // Assume type (can also be name)
832  }
833 
835  bool isTypeGen = false;
836  if (rpcType.startsWith("t<"))
837  isTypeGen = true;
838  else if (rpcType.startsWith("tb<")) {
839  isTypeGen = true;
840  argInfo.isBinary = true;
841  }
842 
844  const RpcTypeInfo& rpcTypeInfo = (isTypeGen || (!argInfo.hasType && d->_mode.is( Mode::NamedArg)))
845  ? typeInfoNull() : typeInfoFromRpc( rpcType);
846  bool isListFormat = rpcTypeInfo.typeId == QMetaType::QStringList;
847  if (isTypeGen) {
848  int posStart = rpcType.indexOf('<') + 1;
849  int posEnd = rpcType.lastIndexOf('>');
850  argInfo.qtType = rpcType.mid( posStart, (posEnd < 0 ? -1 : posEnd - posStart));
851  argInfo.typeId = QMetaType::type( argInfo.qtType.constData());
852  argInfo.hasType = true;
853  }
854  else if (rpcTypeInfo.typeId) {
855  argInfo.qtType = rpcTypeInfo.qtTypeName;
856  argInfo.typeId = rpcTypeInfo.typeId;
857  }
858  else if (rpcType.isEmpty() && argInfo.name.isEmpty()) {
859  argInfo.qtType = "QString"; // Default type;
860  argInfo.typeId = QMetaType::QString;
861  }
862  else {
863  if (!argInfo.hasType)
864  rpcType.clear(); // No type given
865  if (!argInfo.hasName) {
866  argInfo.name = typeKey; // This must be a name
867  argInfo.isPositional = false;
868  }
869  }
870 
871  bool possibleNameArg = !d->_mode.is( Mode::OnlyPosArgIn) && !argInfo.name.isEmpty();
872 
874  if ((!argInfo.typeId)
875  && (argInfo.hasType || argInfo.isPositional || !possibleNameArg)) {
876  errorLog( QString(tr("Unknown type:"))
877  + (argInfo.qtType.isEmpty() ? rpcType.constData() : argInfo.qtType.constData())
878  + (argInfo.hasName ? (tr(" name=") + argInfo.name.constData()) : QString())
879  + tr(" method=") + methodName.constData(),
881  return false;
882  }
883 
885  const QByteArray& argDataDump = xsm.valueRef( index);
886  if (isListFormat) { // Handle list (QStringList)
887  QStringList* argDataList = new QStringList;
888  if (!argDataDump.isEmpty())
889  *argDataList += QString::fromUtf8( argDataDump.constData(), argDataDump.size());
890  ++index;
891  while (index < xsm.size()) {
892  const QByteArray& key = xsm.keyRef( index);
893  if ((key != "") && (key != "+"))
894  break;
895  const QByteArray& arg = xsm.valueRef( index);
896  *argDataList += QString::fromUtf8( arg.constData(), arg.size());
897  ++index;
898  }
899  argInfo.data = argDataList;
900  argInfo.dataAsArg = true;
901  argInfo.isDataAlloc = true;
902  }
903  else {
904  ++index;
905  argInfo.data = &argDataDump;
906  }
907 
908  return true;
909 }
910 
911 
912 bool ArnRpc::argLogic( ArgInfo* argInfo, char* argOrder, int& argc, const QByteArray& methodName)
913 {
914  Q_D(ArnRpc);
915 
916  if (d->_mode.is( Mode::OnlyPosArgIn)) return true; // Only allowed call by positional argument
917  if (d->_isIncludeSender) return true; // IncludeSender is deprecated and never namedArg
918 
919  bool isOnlyPositional = true;
920  bool isOnlyNamed = true;
921  for (int i = 0; i < argc; ++i) {
922  if ( argInfo[i].isPositional)
923  isOnlyNamed = false;
924  else
925  isOnlyPositional = false;
926  }
927  if (isOnlyPositional && (argc > 0))
928  return true; // Only positional arguments in call has been used
929 
930  if (!isOnlyNamed) {
931  errorLog( QString(tr("Mixed positional & named arg call not supported, method="))
932  + methodName.constData(),
934  return false;
935  }
936 
937  int methodIndex = argLogicFindMethod( argInfo, argc, methodName);
938  if (methodIndex < 0) return false; // Failed finding a method
939 
940  const QMetaObject* metaObject = d->_receiver->metaObject();
941  QMetaMethod method = metaObject->method( methodIndex);
942  QList<QByteArray> parNames = method.parameterNames();
943  QList<QByteArray> parTypes = method.parameterTypes();
944  int parCount = parNames.size();
945  int defArgIndex = 10;
946  for (int parIndex = 0; parIndex < parCount; ++parIndex) {
947  bool match = false;
948  for (int argIndex = 0; argIndex < argc; ++argIndex) {
949  ArgInfo& aiSlot = argInfo[ argIndex];
950  if (aiSlot.name == parNames.at( parIndex)) { // Found arg name
951  QByteArray parType = parTypes.at( parIndex);
952  if (!aiSlot.qtType.isEmpty()) { // Type already known for arg
953  if ((parType != aiSlot.qtType)
954  && ((parType != "QVariant") || !d->_convVariantPar)) {
955  errorLog( QString(tr("Type mismatch, arg=")) + parNames.at( parIndex)
956  + tr(" in method=") + methodName.constData(),
958  return false;
959  }
960  }
961  else { // Type for arg is defined by the method parameter
962  aiSlot.qtType = parType;
963  aiSlot.typeId = QMetaType::type( aiSlot.qtType.constData());
964 
965  const RpcTypeInfo& typeInfo = typeInfoFromId( aiSlot.typeId);
966  if ((aiSlot.typeId == QMetaType::QVariant) && d->_convVariantPar) {
967  aiSlot.qtType = "QString"; // Will be QString in QVariant
968  aiSlot.typeId = QMetaType::QString;
969  }
970  else if ((!typeInfo.typeId)
971  || typeInfo.typeId == QMetaType::QStringList) {
972  errorLog( QString(tr("Type mandatory, arg=")) + parNames.at( parIndex)
973  + tr(" type=") + aiSlot.qtType.constData()
974  + tr(" in method=") + methodName.constData(),
976  return false;
977  }
978  }
979  argOrder[ parIndex] = char( argIndex);
980  match = true;
981  break;
982  }
983  }
984  if (!match) { // Parameter not given by arg, use default constructor
985  ArgInfo& aiSlot = argInfo[ defArgIndex];
986  aiSlot.name = parNames.at( parIndex);
987  aiSlot.qtType = parTypes.at( parIndex);
988  aiSlot.typeId = QMetaType::type( aiSlot.qtType.constData());
989 #if QT_VERSION >= 0x050000
990  aiSlot.data = QMetaType::create( aiSlot.typeId);
991 #else
992  aiSlot.data = QMetaType::construct( aiSlot.typeId);
993 #endif
994  aiSlot.isDataAlloc = true;
995  aiSlot.dataAsArg = true;
996 
997  argOrder[ parIndex] = char( defArgIndex);
998  ++defArgIndex;
999  }
1000  }
1001  argc = parCount; // New argc will be exactly as number of parameters
1002 
1003  return true;
1004 }
1005 
1006 
1007 int ArnRpc::argLogicFindMethod( const ArnRpc::ArgInfo* argInfo, int argc, const QByteArray& methodName)
1008 {
1009  Q_D(ArnRpc);
1010 
1011  setupReceiverMethodsParam(); // Setup searching method data structure
1012 
1013  int pslotIndex = d->_receiverMethodsParam->methodNames.indexOf( methodName);
1014  if (pslotIndex < 0) {
1015  errorLog( QString(tr("Not found, method=")) + methodName.constData(),
1017  return -1;
1018  }
1019 
1020  const MethodsParam::Params& pslot = d->_receiverMethodsParam->paramTab.at( pslotIndex);
1021 
1022  // Only 1 method with zero parameters, use it
1023  if ((pslot.paramNames.size() == 1) && pslot.paramNames.at(0).isEmpty())
1024  return pslot.methodIdsTab.at(0).at(0);
1025 
1026  int foundArgCount = 0;
1027  QList<int> methodCand = pslot.allMethodIds; // Start with all methods as candidates (same name)
1028  for (int argIndex = 0; argIndex < argc; ++argIndex) {
1029  int parIndex = pslot.paramNames.indexOf( argInfo[ argIndex].name);
1030  if (parIndex < 0) // arg not used at all, Ok (unneeded)
1031  continue;
1032 
1034  ++foundArgCount;
1035 
1037  const QList<int>& methodIds = pslot.methodIdsTab.at( parIndex);
1038  for (int i = 0; i < methodCand.size();) {
1039  if (methodIds.contains( methodCand.at(i))) {
1040  ++i;
1041  continue;
1042  }
1043  methodCand.removeAt(i); // Remove method not using the parameter
1044  }
1045  }
1046 
1047  if (methodCand.isEmpty()) {
1048  errorLog( QString(tr("Not found method with matching parameters, method=")) + methodName.constData(),
1050  return -1;
1051  }
1052 
1053  if (methodCand.size() == 1)
1054  return methodCand.at(0); // Match with exactly 1 method
1055 
1057  const QMetaObject* metaObject = d->_receiver->metaObject();
1058  for (int i = 0; i < methodCand.size();) {
1059  QMetaMethod method = metaObject->method( methodCand.at(i));
1060  if (method.parameterNames().size() == foundArgCount) {
1061  ++i;
1062  continue;
1063  }
1064  methodCand.removeAt(i); // Remove method with not same number of params
1065  }
1066 
1067  if (methodCand.size() == 1)
1068  return methodCand.at(0); // Match with exactly 1 method having same number of params
1069 
1070  errorLog( QString(tr("Many methods with matching parameters, method=")) + methodName.constData(),
1072  return -1;
1073 }
1074 
1075 
1076 void ArnRpc::setupReceiverMethodsParam()
1077 {
1078  Q_D(ArnRpc);
1079 
1080  if (d->_receiverMethodsParam) return; // Already done
1081 
1082  MethodsParam* mpar = new MethodsParam;
1083  d->_receiverMethodsParam = mpar;
1084 
1085  const QMetaObject* metaObject = d->_receiver->metaObject();
1086  int methodCount = metaObject->methodCount();
1087  QByteArray lastMethodName;
1088  for (int methodIndex = 0; methodIndex < methodCount; ++methodIndex) {
1089  QMetaMethod method = metaObject->method( methodIndex);
1090  QByteArray methodSign = methodSignature( method);
1091  if (!methodSign.startsWith( d->_methodPrefix)) continue;
1092 
1094  QByteArray methodName = methodSign.left( methodSign.indexOf('('));
1095  if (methodName != lastMethodName) {
1096  mpar->methodNames += methodName;
1097  mpar->paramTab += MethodsParam::Params();
1098  lastMethodName = methodName;
1099  }
1100  MethodsParam::Params& pslot = mpar->paramTab[ mpar->paramTab.size() - 1];
1101 
1102  pslot.allMethodIds += methodIndex;
1103 
1104  QList<QByteArray> parNames = method.parameterNames();
1105  int parCount = parNames.size();
1106  int parIndexStart = parCount == 0 ? -1 : 0;
1107  for (int parIndex = parIndexStart; parIndex < parCount; ++parIndex) {
1108  // method with no parameters will store using parName="" (parIndex=-1)
1109  const char* parName = parIndex < 0 ? "" : parNames.at( parIndex).constData();
1110  int parI = pslot.paramNames.indexOf( parName);
1111  if (parI < 0) {
1112  pslot.paramNames += parName;
1113  pslot.methodIdsTab += QList<int>();
1114  parI = pslot.paramNames.size() - 1;
1115  }
1116  pslot.methodIdsTab[ parI] += methodIndex;
1117  }
1118  }
1119 }
1120 
1121 
1122 void ArnRpc::deleteReceiverMethodsParam()
1123 {
1124  Q_D(ArnRpc);
1125 
1126  if (!d->_receiverMethodsParam) return; // Already done
1127 
1128  delete d->_receiverMethodsParam;
1129  d->_receiverMethodsParam = arnNullptr;
1130 }
1131 
1132 
1133 bool ArnRpc::hasSameParamNames( const QMetaMethod& method1, const QMetaMethod& method2)
1134 {
1135  QList<QByteArray> parNames1 = method1.parameterNames();
1136  QList<QByteArray> parNames2 = method2.parameterNames();
1137  int parCount = qMin( parNames1.size(), parNames2.size());
1138  for (int i = 0; i < parCount; ++i) {
1139  if (parNames2.at(i) != parNames1.at(i))
1140  return false;
1141  }
1142  return true;
1143 }
1144 
1145 
1146 bool ArnRpc::checkConvVarPar( const QByteArray& methodName, int argc)
1147 {
1148  Q_D(ArnRpc);
1149 
1150  if (!d->_convVariantPar) return false;
1151 
1152  bool useVarPar = false;
1153  setupReceiverMethodsParam(); // Setup searching method data structure
1154 
1155  int pslotIndex = d->_receiverMethodsParam->methodNames.indexOf( methodName);
1156  if (pslotIndex < 0) return false;
1157 
1158  useVarPar = true;
1159  const MethodsParam::Params& pslot = d->_receiverMethodsParam->paramTab.at( pslotIndex);
1160  const QMetaObject* metaObject = d->_receiver->metaObject();
1161  foreach (int methodIndex, pslot.allMethodIds) {
1162  QMetaMethod method = metaObject->method( methodIndex);
1163  QList<QByteArray> typesNames = method.parameterTypes();
1164  if (typesNames.size() != argc) continue;
1165 
1166  foreach (const QByteArray& typeName, typesNames) {
1167  useVarPar &= typeName == "QVariant";
1168  }
1169  }
1170 
1171  return useVarPar;
1172 }
1173 
1174 
1175 bool ArnRpc::importArgData( ArnRpc::ArgInfo& argInfo, const QByteArray& methodName, bool useVarPar)
1176 {
1177  if (argInfo.typeId == QMetaType::QByteArray)
1178  argInfo.dataAsArg = true;
1179 
1180  if (argInfo.dataAsArg) {
1181  argInfo.arg = QGenericArgument( argInfo.qtType.constData(), argInfo.data);
1182  return true;
1183  }
1184 
1185  const QByteArray& argDataDump = *static_cast<const QByteArray*>( argInfo.data);
1186  if (argInfo.isBinary) {
1187  if ((argDataDump.size() < 2) || (argDataDump.at(1) != DATASTREAM_VER)) {
1188  errorLog( QString(tr("Not same DataStream version, method=")) + methodName.constData(),
1190  return false;
1191  }
1192 #if QT_VERSION >= 0x050000
1193  void* argData = QMetaType::create( argInfo.typeId);
1194 #else
1195  void* argData = QMetaType::construct( argInfo.typeId);
1196 #endif
1197  Q_ASSERT( argData);
1198  argInfo.arg = QGenericArgument( argInfo.qtType.constData(), argData); // Assign arg as it has been allocated
1199  argInfo.isArgAlloc = true;
1200  QDataStream stream( argDataDump);
1201  stream.setVersion( DATASTREAM_VER);
1202  stream.skipRawData(2);
1203  if (!QMetaType::load( stream, argInfo.typeId, argData)) {
1204  errorLog( QString(tr("Can't' import bin type:") + argInfo.qtType.constData())
1205  + tr(" method=") + methodName.constData(),
1207  return false;
1208  }
1209  }
1210  else { // Textual type
1211  QVariant varArg( QString::fromUtf8( argDataDump.constData(), argDataDump.size()));
1212  if (!varArg.convert( QVariant::Type( argInfo.typeId))) {
1213  errorLog( QString(tr("Can't' import str type:") + argInfo.qtType.constData())
1214  + tr(" method=") + methodName.constData(),
1216  return false;
1217  }
1218 
1219  void* argData = arnNullptr;
1220  if (useVarPar) {
1221  argData = new QVariant( varArg);
1222  argInfo.typeId = QMetaType::QVariant;
1223  argInfo.qtType = "QVariant";
1224  }
1225  else {
1226 #if QT_VERSION >= 0x050000
1227  argData = QMetaType::create( argInfo.typeId, varArg.constData());
1228 #else
1229  argData = QMetaType::construct( argInfo.typeId, varArg.constData());
1230 #endif
1231  }
1232  Q_ASSERT( argData);
1233  argInfo.arg = QGenericArgument( argInfo.qtType.constData(), argData); // Assign arg as it has been allocated
1234  argInfo.isArgAlloc = true;
1235  }
1236 
1237  return true;
1238 }
1239 
1240 
1241 void ArnRpc::funcHeartBeat( const XStringMap& xsm)
1242 {
1243  Q_D(ArnRpc);
1244 
1245  QByteArray time = xsm.value(1);
1246  if (time == "off") { // Remote turn off heart beat function for both directions
1247  d->_timerHeartBeatSend->stop();
1248  invoke("$heartbeat", MQ_ARG( QString, time, "off1"));
1249  }
1250  if (time == "off1") { // Remote turn off heart beat function for this direction
1251  d->_timerHeartBeatSend->stop();
1252  invoke("$heartbeat", MQ_ARG( QString, time, "0"));
1253  }
1254  else if (time == "0") { // Remote turn off heart beat checking for this direction
1255  d->_timerHeartBeatCheck->stop();
1256  d->_isHeartBeatOk = true;
1257  }
1258  else {
1259  emit heartBeatReceived();
1260  if (!d->_isHeartBeatOk) {
1261  d->_isHeartBeatOk = true;
1262  emit heartBeatChanged( d->_isHeartBeatOk);
1263  }
1264  }
1265 
1266  if (d->_timerHeartBeatCheck->isActive())
1267  d->_timerHeartBeatCheck->start(); // Restart heart beat check timer
1268 }
1269 
1270 
1271 void ArnRpc::funcHelp( const XStringMap& xsm)
1272 {
1273  Q_D(ArnRpc);
1274 
1275  if (!d->_receiver) {
1276  sendText("$help: rpc-receiver = 0");
1277  return;
1278  }
1279 
1280  QByteArray modePar = xsm.value(1);
1281  int flags = -1; // Default is faulty parameter
1282 
1283  if (modePar.isEmpty()) { // Ok, standard
1284  bool useNamedArgHelp = d->_mode.isAny( Mode::AnyNamedArg) & !d->_mode.is( Mode::OnlyPosArgIn);
1285  flags = d->_mode.flagIf( useNamedArgHelp, Mode::NamedArg);
1286  }
1287  else if (modePar.startsWith("p")) { // Positional
1288  flags = 0;
1289  }
1290  else if (modePar.startsWith("n")) { // Named
1291  flags = Mode::NamedArg;
1292  }
1293  else {
1294  sendText("$help: Unknown mode");
1295  }
1296 
1297  if (flags >= 0) {
1298  if (d->_mode.is( Mode::OnlyPosArgIn))
1299  sendText("* Only positional args allowed.");
1300  if (d->_convVariantPar)
1301  sendText("* Type VAR can be any, e.g. string or int.");
1302 
1303  const QMetaObject* metaObject = d->_receiver->metaObject();
1304  int methodIdHead = -1;
1305  int parCountMin = 10;
1306  QByteArray methodNameHead;
1307  QByteArray methodSignHead;
1308  int methodCount = metaObject->methodCount();
1309  for (int methodId = 0; methodId < methodCount; ++methodId) {
1310  QMetaMethod method = metaObject->method(methodId);
1311  QByteArray methodSign = methodSignature( method);
1312  if (!methodSign.startsWith( d->_methodPrefix)) continue;
1313 
1314  methodSign.chop(1); // Remove last ")"
1315  QList<QByteArray> parNames = method.parameterNames();
1316  int parCount = parNames.size();
1317 
1318  if (!d->_mode.is( Mode::NoDefaultArgs) // When using Default args ...
1319  && methodSignHead.startsWith( methodSign) // Starts with same signatur ...
1320  && hasSameParamNames( method, metaObject->method( methodIdHead))) // and same param names
1321  parCountMin = parCount; // Same method with less param
1322  else {
1323  if (methodIdHead >= 0)
1324  funcHelpMethod( metaObject->method( methodIdHead),
1325  methodNameHead, parCountMin, flags);
1326  methodIdHead = methodId;
1327  methodSignHead = methodSign;
1328  methodNameHead = methodSign.left( methodSign.indexOf('('));
1329  parCountMin = parCount;
1330  }
1331  }
1332  if (methodIdHead >= 0)
1333  funcHelpMethod( metaObject->method( methodIdHead), methodNameHead, parCountMin, flags);
1334  }
1335 
1336  sendText("$arg pos|named|typed");
1337  sendText("$heartbeat [`time`|off|off1]");
1338  sendText("$help [pos|named]");
1339 }
1340 
1341 
1342 void ArnRpc::funcHelpMethod( const QMetaMethod &method, const QByteArray& name, int parNumMin, int flags)
1343 {
1344  Q_D(ArnRpc);
1345 
1346  QString line = QString::fromLatin1( name.mid( d->_methodPrefix.size()));
1347 
1348  QList<QByteArray> typesNames = method.parameterTypes();
1349  QList<QByteArray> parNames = method.parameterNames();
1350 
1351  bool wasListType = false;
1352  int parCount = parNames.size();
1353  for (int i = d->_isIncludeSender; i < parCount; ++i) {
1354  QByteArray param;
1355  QByteArray parName = parNames.at(i);
1356  QByteArray typeName = typesNames.at(i);
1357 
1358  int type = QMetaType::type( typeName.constData());
1359  QVariant varPar( type);
1360  bool isBinaryType = (type == 0) || !varPar.canConvert( QVariant::String);
1361  QByteArray rpcType;
1362  const RpcTypeInfo& rpcTypeInfo = typeInfoFromQt( typeName);
1363  bool isList = rpcTypeInfo.typeId == QMetaType::QStringList;
1364  bool isTypeGen = false;
1365  bool isTypeVar = false;
1366  if (rpcTypeInfo.typeId == QMetaType::QString) {
1367  rpcType = wasListType ? rpcTypeInfo.rpcTypeName : "";
1368  }
1369  else if (rpcTypeInfo.typeId) {
1370  rpcType = rpcTypeInfo.rpcTypeName;
1371  }
1372  else if ((type == QMetaType::QVariant) && d->_convVariantPar) {
1373  rpcType = "VAR";
1374  isTypeVar = true;
1375  }
1376  else {
1377  rpcType = (isBinaryType ? "tb<" : "t<") + typeName + ">";
1378  isTypeGen = true;
1379  }
1380 
1381  if ((flags & Mode::NamedArg) && !parName.isEmpty()) {
1382  param += parName;
1383  if (isTypeGen | isTypeVar)
1384  param += ":" + rpcType + "=`data`";
1385  else {
1386  QByteArray rpcTypeVal = rpcType.isEmpty() ? QByteArray("string") : rpcType;
1387  param += "=`" + rpcTypeVal + "`";
1388  }
1389  }
1390  else {
1391  if (!rpcType.isEmpty()) {
1392  param += rpcType;
1393  if (d->_mode.is( Mode::NamedArg) && !isTypeGen)
1394  param += "."; // Force typename
1395  param += "=";
1396  }
1397  param += "`" + parName + "`";
1398  }
1399 
1400  if (i >= parNumMin)
1401  param = "[" + param + "]";
1402  line += " " + QString::fromLatin1( param);
1403 
1404  wasListType = isList;
1405  }
1406  sendText( line);
1407 }
1408 
1409 
1410 void ArnRpc::funcArg( const Arn::XStringMap& xsm)
1411 {
1412  Q_D(ArnRpc);
1413 
1414  QByteArray modePar = xsm.value(1);
1415 
1416  if (modePar.startsWith("p")) { // Positional
1417  d->_mode.set( Mode::NamedArg, false);
1418  d->_mode.set( Mode::NamedTypedArg, false);
1419  }
1420  else if (modePar.startsWith("n")) { // Named
1421  d->_mode.set( Mode::NamedArg, true);
1422  d->_mode.set( Mode::NamedTypedArg, false);
1423  }
1424  else if (modePar.startsWith("t")) { // NamedTyped
1425  d->_mode.set( Mode::NamedArg, false);
1426  d->_mode.set( Mode::NamedTypedArg, true);
1427  }
1428  else {
1429  sendText("$arg: Unknown mode, use $help");
1430  }
1431 }
1432 
1433 
1434 void ArnRpc::destroyPipe()
1435 {
1436  Q_D(ArnRpc);
1437 
1438  if (Arn::debugRPC) qDebug() << "Rpc Destroy pipe: path=" << d->_pipe->path();
1439  emit pipeClosed();
1440  setPipe( arnNullptr);
1441 }
1442 
1443 
1444 void ArnRpc::timeoutHeartBeatSend()
1445 {
1446  Q_D(ArnRpc);
1447 
1448  if (!d->_pipe || !d->_pipe->isOpen()) return;
1449 
1450  invoke("$heartbeat", Invoke::NoQueue,
1451  MQ_ARG( QString, time, QByteArray::number( d->_timerHeartBeatSend->interval() / 1000)));
1452 }
1453 
1454 
1455 void ArnRpc::timeoutHeartBeatCheck()
1456 {
1457  Q_D(ArnRpc);
1458 
1459  if (d->_isHeartBeatOk) {
1460  d->_isHeartBeatOk = false;
1461  emit heartBeatChanged( d->_isHeartBeatOk);
1462  }
1463 }
1464 
1465 
1466 void ArnRpc::sendText( const QString& txt)
1467 {
1468  Q_D(ArnRpc);
1469 
1470  if (d->_pipe)
1471  *d->_pipe = "\"" + txt.toUtf8() + "\"";
1472 }
1473 
1474 
1475 void ArnRpc::errorLog( const QString& errText, ArnError err, void* reference)
1476 {
1477  Q_D(ArnRpc);
1478 
1479  QString idText;
1480  if (d->_pipe) {
1481  idText += " pipe:" + d->_pipe->path();
1482  }
1483  ArnM::errorLog( errText + idText, err, reference);
1484 }
1485 
1486 
1487 void ArnRpc::batchConnect( const QObject *sender, const ARN_RegExp& rgx,
1488  const QObject* receiver, const QString& replace,
1489  Mode mode)
1490 {
1491  QList<QByteArray> signalSignTab;
1492  QList<QByteArray> methodSignLowTab;
1493  if (mode.is( mode.Debug))
1494  qDebug() << "batchConnect: regExp=" << rgx.pattern() << " replace=" << replace;
1495 
1497  const QMetaObject* metaObject = sender->metaObject();
1498  QList<QByteArray> doneMethodSignTab;
1499  int methodCount = metaObject->methodCount();
1500  for (int i = 0; i < methodCount; ++i) {
1501  QMetaMethod method = metaObject->method(i);
1502  if (method.methodType() != QMetaMethod::Signal) continue; // Must be a Signal
1503 
1504  QByteArray signalSign = methodSignature( method);
1505  if (doneMethodSignTab.contains( signalSign)) continue; // Already done (inherited)
1506  doneMethodSignTab += signalSign;
1507 
1508  QString signalName = QString::fromLatin1( signalSign.left( signalSign.indexOf('(')).constData());
1509  QByteArray paramSign = signalSign.mid( signalName.size());
1510  if (rgx.indexIn( signalName) >= 0) { // Match of signal in regExp
1511  QString methodName = replace;
1512  for (int j = 1; j <= rgx.captureCount(); ++j) {
1513  methodName.replace("\\" + QString::number(j), rgx.cap(j));
1514  }
1515  signalSignTab += signalSign;
1516  QByteArray methodSignLow = (methodName.toLatin1() + paramSign).toLower();
1517  methodSignLowTab += methodSignLow;
1518  if (mode.is( mode.Debug))
1519  qDebug() << "batchConnect: try match signal=" << signalSign <<
1520  " method(low)=" << methodSignLow;
1521  }
1522  else if (mode.is( mode.Debug))
1523  qDebug() << "batchConnect: No regExp match on signal=" << signalSign;
1524  }
1525 
1527  metaObject = receiver->metaObject();
1528  methodCount = metaObject->methodCount();
1529  doneMethodSignTab.clear();
1530  QByteArray methodSignCompHead;
1531  int methodIdCompHead = -1;
1532  for (int methodId = 0; methodId < methodCount; ++methodId) {
1533  QMetaMethod method = metaObject->method(methodId);
1534  QByteArray methodSign = methodSignature( method);
1535  if (doneMethodSignTab.contains( methodSign)) continue; // Already done (inherited)
1536  doneMethodSignTab += methodSign;
1537 
1538  QByteArray methodSignComp = methodSign;
1539  methodSignComp.chop(1); // Remove last ")"
1540 
1541  if (!mode.is( Mode::NoDefaultArgs) // When using Default args ...
1542  && methodSignCompHead.startsWith( methodSignComp) // Starts with same signatur ...
1543  && hasSameParamNames( method, metaObject->method( methodIdCompHead))) // and same param names
1544  continue; // Skip it, to prohibit multiple calls.
1545 
1546  methodSignCompHead = methodSignComp;
1547  methodIdCompHead = methodId;
1548 
1549  QByteArray methodSignLow = methodSign.toLower();
1550  int index = methodSignLowTab.indexOf( methodSignLow);
1551  if (index >= 0) { // Match signal to method
1552  const char* methodPrefix = (method.methodType() == QMetaMethod::Signal) ? "2" : "1";
1553  connect( sender, "2" + signalSignTab[ index],
1554  receiver, methodPrefix + methodSign);
1555  if (mode.is( mode.Debug))
1556  qDebug() << "batchConnect: connect signal=" << signalSignTab[ index] <<
1557  " method=" << methodSign;
1558  }
1559  else if (mode.is( mode.Debug))
1560  qDebug() << "batchConnect: No match on method=" << methodSignLow;
1561  }
1562 }
1563 
1564 
1565 QByteArray ArnRpc::methodSignature( const QMetaMethod &method)
1566 {
1567 #if QT_VERSION >= 0x050000
1568  return method.methodSignature();
1569 #else
1570  return QByteArray( method.signature());
1571 #endif
1572 }
1573 
1574 
1575 const ArnRpc::RpcTypeInfo& ArnRpc::typeInfoFromRpc( const QByteArray& rpcTypeName)
1576 {
1577  RpcTypeInfo* typeInfo = _rpcTypeInfoTab;
1578  while (typeInfo->typeId) {
1579  if (rpcTypeName == typeInfo->rpcTypeName)
1580  break;
1581  ++typeInfo;
1582  }
1583 
1584  return *typeInfo;
1585 }
1586 
1587 
1588 const ArnRpc::RpcTypeInfo& ArnRpc::typeInfoFromQt( const QByteArray& qtTypeName)
1589 {
1590  RpcTypeInfo* typeInfo = _rpcTypeInfoTab;
1591  while (typeInfo->typeId) {
1592  if (qtTypeName == typeInfo->qtTypeName)
1593  break;
1594  ++typeInfo;
1595  }
1596 
1597  return *typeInfo;
1598 }
1599 
1600 
1601 const ArnRpc::RpcTypeInfo& ArnRpc::typeInfoFromId( int typeId)
1602 {
1603  RpcTypeInfo* typeInfo = _rpcTypeInfoTab;
1604  while (typeInfo->typeId) {
1605  if (typeId == typeInfo->typeId)
1606  break;
1607  ++typeInfo;
1608  }
1609 
1610  return *typeInfo;
1611 }
1612 
1613 
1614 const ArnRpc::RpcTypeInfo& ArnRpc::typeInfoNull()
1615 {
1616  return _rpcTypeInfoTab[ sizeof(_rpcTypeInfoTab) / sizeof(RpcTypeInfo) - 1]; // Last slot is Null
1617 }
void setMethodPrefix(const QString &prefix)
Definition: ArnRpc.cpp:337
Mode mode() const
Get the mode.
Definition: ArnRpc.cpp:386
bool setReceiver(QObject *receiver, bool useTrackRpcSender=true)
Definition: ArnRpc.cpp:300
bool isHeartBeatOk() const
Get the state of heart beat.
Definition: ArnRpc.cpp:432
When calling out, uses named argument e.g "myFunc count=123".
Definition: ArnRpc.hpp:105
void addSenderSignals(QObject *sender, const QString &prefix)
Definition: ArnRpc.cpp:440
static void batchConnect(const QObject *sender, const ARN_RegExp &rgx, const QObject *receiver, const QString &replace, Mode mode=Mode())
Make batch connection from one senders signals to another receivers slots/signals.
Definition: ArnRpc.cpp:1487
void setIncludeSender(bool v)
Add sender as argument when calling a rpc method.
Definition: ArnRpc.cpp:370
This invoke is not queued, multiple calls to same method might overwrite.
Definition: ArnRpc.hpp:167
void setSendSeq(bool useSendSeq)
Change usage of sending sequence numbers.
Definition: ArnPipe.cpp:138
Container class with string representation for serialized data.
Definition: XStringMap.hpp:107
ArnRpc(QObject *parent=arnNullptr)
Definition: ArnRpc.cpp:204
Only allow calling in with positional argument (typed)
Definition: ArnRpc.hpp:103
~ArnRpc()
Definition: ArnRpc.cpp:220
bool openUuid(const QString &path)
Open a handle to an Arn Pipe Object with a unique uuid name.
Definition: ArnPipe.hpp:90
#define ARN_RegExp
Definition: ArnCompat.hpp:70
const QByteArray & keyRef(int i) const
When receiver method missing, send defaultCall() signal instead of error.
Definition: ArnRpc.hpp:109
void defaultCall(const QByteArray &data)
Signal emitted when receiver method missing.
Remote Procedure Call.
Definition: ArnRpc.hpp:156
QByteArray value(int i, const char *def=arnNullptr) const
Check sequence order information from pipe. Can generate signal outOfSequence().
Definition: ArnRpc.hpp:101
const QByteArray & valueRef(int i) const
#define MQ_ARG(type, label, data)
Similar to Q_ARG but with added argument label (parameter name)
Definition: ArnRpc.hpp:49
bool open(const QString &path)
Open a handle to an Arn Data Object
Definition: ArnItemB.cpp:91
QObject * receiver() const
Definition: ArnRpc.cpp:329
QByteArray toXString() const
void heartBeatReceived()
Signal emitted when Heart beat message is received.
ArnPipe * pipe() const
Get the used pipe
Definition: ArnRpc.cpp:292
void setHeartBeatCheck(int time)
Set max time period for receiving heart beat message.
Definition: ArnRpc.cpp:413
Provider side (opposed to requester)
Definition: ArnRpc.hpp:91
bool debugRPC
Definition: ArnLib.cpp:45
ArnPipe & setMaster()
Set client session sync mode as Master for this ArnItem.
Definition: ArnPipe.hpp:98
QString twinPath(const QString &path)
Get the bidirectional twin to a given path
Definition: Arn.cpp:195
bool invoke(const QString &funcName, MQGenericArgument val0=MQGenericArgument(0), MQGenericArgument val1=MQGenericArgument(), MQGenericArgument val2=MQGenericArgument(), MQGenericArgument val3=MQGenericArgument(), MQGenericArgument val4=MQGenericArgument(), MQGenericArgument val5=MQGenericArgument(), MQGenericArgument val6=MQGenericArgument(), MQGenericArgument val7=MQGenericArgument())
Calls a named remote procedure.
Definition: ArnRpc.cpp:494
QString pipePath() const
Get the path for the used pipe
Definition: ArnRpc.cpp:226
QString methodPrefix() const
Definition: ArnRpc.cpp:346
void setHeartBeatSend(int time)
Set period time for sending heart beat message.
Definition: ArnRpc.cpp:394
ArnRpc * rpcSender()
Definition: ArnRpc.cpp:473
When calling out, uses named argument with type e.g "myFunc count:int=123".
Definition: ArnRpc.hpp:107
const char * label() const
Definition: ArnRpc.hpp:67
Send sequence order information to pipe.
Definition: ArnRpc.hpp:99
Use AutoDestroy for the pipe, i.e. it is closed when tcp/ip is broken.
Definition: ArnRpc.hpp:93
int getHeartBeatCheck() const
Get max time period for receiving heart beat message.
Definition: ArnRpc.cpp:424
Use an unique uuid in the pipe name.
Definition: ArnRpc.hpp:95
ArnItem specialized as a pipe.
Definition: ArnPipe.hpp:64
void textReceived(const QString &text)
Signal emitted when a general text message is received.
void pipeClosed()
Signal emitted when the used pipe is closed.
#define DATASTREAM_VER
Definition: Arn.hpp:39
static void errorLog(QString errText, ArnError err=ArnError::Undef, void *reference=arnNullptr)
Definition: ArnM.cpp:1025
If guarantied no default arguments, full member name overload is ok.
Definition: ArnRpc.hpp:97
XStringMap & add(const char *key, const QByteArray &val)
Similar to QGenericArgument but with added argument label (parameter name)
Definition: ArnRpc.hpp:59
Convenience, combined NamedArg and NamedTypedArg
Definition: ArnRpc.hpp:115
int size() const
Definition: XStringMap.hpp:121
void setPipe(ArnPipe *pipe)
Set pipe for this Rpc.
Definition: ArnRpc.cpp:273
ArnPipe & setAutoDestroy()
Set client session sync mode as AutoDestroy for this ArnItem.
Definition: ArnPipe.hpp:112
bool open(const QString &pipePath)
Definition: ArnRpc.cpp:236
Debug mode, dumping info for the batch connections.
Definition: ArnRpc.hpp:111
void outOfSequence()
Signal emitted when checked sequence order is wrong.
#define RPC_STORAGE_NAME
Definition: ArnRpc.cpp:47
void setCheckSeq(bool useCheckSeq)
Change usage of checking received sequence numbers.
Definition: ArnPipe.cpp:154
void sendText(const QString &txt)
Send a general text message to the other end of the used pipe
Definition: ArnRpc.cpp:1466
int getHeartBeatSend() const
Get period time for sending heart beat message.
Definition: ArnRpc.cpp:405
void heartBeatChanged(bool isOk)
Signal emitted when Heart beat changes state.
void setMode(Mode mode)
Definition: ArnRpc.cpp:378
bool isProviderPath(const QString &path)
Test if path is a provider path
Definition: Arn.cpp:216