RTC Forums
May 24, 2022, 05:38:20 PM *
Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length
 
   Home   Help Login Register  
Pages: [1]
  Print  
Author Topic: XML-RPC "system.multicall"  (Read 5740 times)
kaju74
Newbie
*
Posts: 25


« on: January 04, 2010, 02:43:58 PM »

Hello.

I need to write a XML-RPC-Server which will be contacted by a hardware-product. The specifications for this (hardware) product requires
the presence of the "system.multicall" support. Does the RTC-Server supports this method and what does this means exactly i.e. what to
do to activate this?

Thank you,
Marc
Logged
D.Tkalcec (RTC)
Administrator
*****
Posts: 1870


« Reply #1 on: January 04, 2010, 05:38:26 PM »

With the RTC SDK you can send any number of remote calls in a single request as well as box remote function calls to any depth, but it will depend on the system you are working with how the data will be received and what you will need to do to process it correctly. Since "system.multicall" isn't part of the XML-RPC standard but an extension of the standard, there is more than one way it can be implemented, but here are the two most commonly used, which should get you going:

1) If clients are calling your Server by sending their remote function calls inside a special "system.multicall" remote function, passing the actual remote function calls as parameters to "system.multicall", you will have to add a "system.multicall" remote function to your Server (using the TRtcFunction component, just like for any other remote function) to be able to process boxed "system.multicall" calls.

The OnExecute event of your "system.multicall" remote function will receive all the results from all boxed remote function calls, once they have been executed using their own TRtcFunction components OnExecute events. For the client to receive the results, you will need to "forward" all received results to the client by doing the following in the OnExecute event of the "system.multicall" function:

Result.asArray:=Data.asArray['params'];

2) If clients are sending multiple calls as an array of remote function calls without boxing them inside a "system.multicall" remote function, you will NOT need a "system.multicall" remote function on the Server. Writing a Server which has to receive multiple remote function calls as an array in a single request when the functions are not "boxed", there is nothing special you need to do. Each remote call sent will trigger its own OnExecute event where you will get data from that specific remote function. But if you want to stay on the safe side and you know your clients are sending multiple calls in a single request, you should implement the "system.multicall" remote function as explained above.

Should you have problems communicating with the system you have to work with, please post the plain XML-RPC data you are receiving and let me know what the system expects as a response.

Best Regards,
Danijel Tkalcec
Logged
kaju74
Newbie
*
Posts: 25


« Reply #2 on: January 05, 2010, 08:46:36 AM »

Hi..

Thank you for this detailled answer...at this moment, I had to find out, what the client tries to request. What is the best way to monitor, what the client tries to request from my xmlrpc-server?

Thank you so much,
Marc
Logged
D.Tkalcec (RTC)
Administrator
*****
Posts: 1870


« Reply #3 on: January 05, 2010, 04:08:09 PM »

Use a TRtcDataProvider component on the Server (linked to TRtcHttpServer, ofcourse). That will give you access to the complete unmodified request.

Best Regards,
Danijel Tkalcec
Logged
kaju74
Newbie
*
Posts: 25


« Reply #4 on: January 06, 2010, 05:28:14 PM »

Hi.

I'll try this today...thank you 8-)

Regards,
Marc
Logged
kaju74
Newbie
*
Posts: 25


« Reply #5 on: January 07, 2010, 10:45:05 AM »

Hi...

I've monitored, what's happend and noticed the following:

The event "BeforeExecute" triggers with the functionname "system.multicall":

Quote
<methodCall>
  <methodName>system.multicall</methodName>
  <params>
    <param>
      <value>
        <array>
     <data>
            <value>
              <struct>
                <member>
                  <name>methodName</name>
                  <value><string>event</string></value>
                </member>
                <member><name>params</name>
                  <value>
                     <array>
                       <data>
                         <value><string>text1</string></value>
                         <value><string>text2</string></value>
                         <value><string>text3</string></value>
                         <value><double>0.00</double></value>
                       </data>
                     </array>
                   </value>
                </member>
              </struct>
            </value>
            <value>
              <struct>
                <member>
                  <name>methodName</name>
                  <value><string>event</string></value>
                </member>
                <member>
                  <name>params</name>
                  <value>
                    <array>
                      <data>
                        <value><string>texta</string></value>
                        <value><string>textb</string></value>
                        <value><string>textc</string></value>
                        <value><boolean>1</boolean></value>
                      </data>
                    </array>
                  </value>
                </member>
              </struct>
            </value>
          </data>
   </array>
      </value>
    </param>
  </params>
</methodCall>

As you see, there are some inside calls to the event-method. I also noticed, that the "event"-method will be triggered without a previous "system.multicall". So, I'm thinking, as soon as there will be more than one call, all methods are embedded inside a multicall. If only ONE method needs to be triggered, it will be received directly by the corresponding function-component (TRtcFunction).

Is it possible to extract the embedded methods inside the "system.multicall" and deligate them to their corrsponding TRtcFunction?

Another question:

I had to implement a method, which has the following syntax:

Array<Description> listDescriptions()

The method will be called, but how to retrieve the array? It could be possible, that this array is very long (~2 MB)...but I don't know how to implement a function instead of a method...?!?!

Best regards,
Marc
Logged
D.Tkalcec (RTC)
Administrator
*****
Posts: 1870


« Reply #6 on: January 07, 2010, 11:46:24 AM »

Looks like the client isn't packing the data the way I though it would. Instead of packing remote functions as <methodcall> elements inside the array, they are sending each remote function as a record having one "methodName" field and a "params" array. This means you will need to re-package the data in the "system.multicall" function and use the "Execute" method of the FunctionGroup component to which your TRtcFunction components are linked in order to pass the data over to your TRtcFunctions OnExecute events and get the results back. I haven't tried this, but I think this function should do the trick:
Quote
procedure ExecMultiCall(Sender:TRtcConnection; FuncGroup:TRtcFunctionGroup;
                                 Param:TRtcFunctionInfo; Result:TRtcValue);
var
  arr : TRtcArray;
  rec : TRtcRecord;
  func : TRtcFunctionInfo;
  methName : String;
  MyData : TRtcValue;
  MyResult : TRtcValueObject;
  i : integer;
begin
if Param.isType['Params']<>rtc_Array then Exit;

arr := Param.asArray['Params']; // "remote functions" array
MyData.newArray; // result will also be an array

// use data from the array to create remote function objects
for i := 0 to arr.Count-1 do
  begin
  if arr.isType=rtc_Record then
    begin
    rec := arr.asRecord;
    methName := rec.asText['methodName'];
    if methName<>'' then
      begin
      // create the function object
      func := MyData.newArray(i).newFunction(methName);
      // *move* the params object as-is to the function object
      func.asObject['params'] := rec.asObject['Params'];
      rec.asObject['params'] := nil;
      end;
    end;
  end;

// Execute all remote functions by using TRtcFunction components
// linked to the FuncGroup function group.
MyResult:=FuncGroup.Execute(Sender,MyData);

// Set the combined result received from Execute() as our result.
if MyResult<>MyData then MyData.Free;
Result.asObject:=MyResult;
end;

After copying the above function into your code, call it from the OnExecute event of your "system.multicall" TRtcFunction component like this:

Quote
begin
ExecMultiCall(Sender, MyFunctionGroup, Data, Result);
end;

MyFunctionGroup is the name of the TRtcFunctionGroup component which you have linked to your TRtcServerModule, to which all your TRtcFunction components are linked.

Please let me know if this works.

As for your 2nd question, I am sorry but I have no clue what you mean by "Array<Description> listDescriptions()". I would also like to ask you to start a new thread if the 2nd question is not directly related to the subject line.

Best Regards,
Danijel Tkalcec
Logged
kaju74
Newbie
*
Posts: 25


« Reply #7 on: January 07, 2010, 12:08:24 PM »

Hi.

Wow....thank you for you quick answer. I'll try this at home this evening and tell you, if it's working 8-) It seems to be a lit bit tricky to work with XMLRPC...8-)))

Well, I'll ask the manufactor how the "listDescription" method should be implemented and create another topic.

Best regards,
Marc
Logged
D.Tkalcec (RTC)
Administrator
*****
Posts: 1870


« Reply #8 on: January 07, 2010, 12:49:47 PM »

XML-RPC is not the problem. The problem is when some wanna-be experts decide to "stretch" the standard beyond its original design and come up with constructs like the one your client is using, instead of doing things the right way. The right way would be to send boxed remote function calls as normal <methodCall> structures, in which case every server implemented correctly would know what to do with it. But now, the structure looks just like an ordinary array with records holding more arrays. And that's plain wrong. But it can be fixed on the server side if you know what "the artist wanted to say".

This is what the above "system.multicall" code *should* have looked like ...

Quote
<methodCall>
  <methodName>system.multicall</methodName>
  <params>
    <param>
      <methodCall>
        <methodName>event</methodName>
        <params>
          <param><string>text1</string></param>
          <param><string>text2</string></param>
          <param><string>text3</string></param>
          <param><double>0.00</double></param>
        </params>
      <methodCall>
    </param>
    <param>
      <methodCall>
        <methodName>event</methodName>
        <params>
          <param><string>texta</string></param>
          <param><string>textb</string></param>
          <param><string>textc</string></param>
          <param><boolean>1</boolean></param>
        </params>
      </methodCall>
    </param>
  </params>
</methodCall>

If you compare this to the XML-RPC code you've posted above (which your client is sending), you will see that it contains all the elements, but its intentions are clear and ... it is shorter (less overhead).

Best Regards,
Danijel Tkalcec
Logged
kaju74
Newbie
*
Posts: 25


« Reply #9 on: January 07, 2010, 08:09:26 PM »

Hi again.

Hmm..I've tryed you "ExecMultiCall"-method with the latest SDK but get a lot of errors if compiling:

Code:
if arr.isType = rtc_Record then

ifType seems to be indexed...

Code:
rec := arr.asRecord;

asRecord seems to be indexed...

Code:
func := MyData.newArray(i).newFunction(methName);

has to much parameters...

Not sure, if I fixed it right...

Regards,
Marc
Logged
D.Tkalcec (RTC)
Administrator
*****
Posts: 1870


« Reply #10 on: January 07, 2010, 08:19:56 PM »

Looks like "BBCode" on the Forums has swallowed function parameters. I've also noticed there is anothe bug which the compiler did not catch.

Anyway ... here is fixed version ...

Code:
procedure ExecMultiCall(Sender:TRtcConnection; FuncGroup:TRtcFunctionGroup;
                                 Param:TRtcFunctionInfo; Result:TRtcValue);
var
  arr : TRtcArray;
  rec : TRtcRecord;
  func : TRtcFunctionInfo;
  methName : String;
  MyData : TRtcArray; // <- TRtcArray instead of TRtcValue
  MyResult : TRtcValueObject;
  i : integer;
begin
if Param.isType['Params']<>rtc_Array then Exit;

arr := Param.asArray['Params']; // "remote functions" array

MyData:=TRtcArray.Create; // create our data object

// use data from the array to create remote function objects
for i := 0 to arr.Count-1 do
  begin
  if arr.isType[i]=rtc_Record then
    begin
    rec := arr.asRecord[i];
    methName := rec.asText['methodName'];
    if methName<>'' then
      begin
      // create the function object
      func := MyData.newFunction(i,methName);
      // *move* the params object as-is to the function object
      func.asObject['params'] := rec.asObject['Params'];
      rec.asObject['params'] := nil;
      end;
    end;
  end;

// Execute all remote functions by using TRtcFunction components
// linked to the FuncGroup function group.
MyResult:=FuncGroup.Execute(Sender,MyData);

// Set the combined result received from Execute() as our result.
if MyResult<>MyData then MyData.Free;
Result.asObject:=MyResult;
end;

PS. Sorry, but I am in the middle of an update for the RTC SDK and can't test if the above code works. Should you bump into any other problems, please let me know.

Best Regards,
Danijel Tkalcec
Logged
kaju74
Newbie
*
Posts: 25


« Reply #11 on: January 07, 2010, 08:27:16 PM »

Hi again.

After replacing Execute with ExecuteData the code compiles fine, but:

Code:
  for i := 0 to arr.Count - 1 do
  begin
    if arr.isType[i] = rtc_Record then
    begin
      rec := arr.asRecord[i];

"if arr.isType = rtc_Record then" won't be called anytime, cause isType seems to be an rtc_Array instead...?!?

Any idea?

Regard,
Marc
Logged
D.Tkalcec (RTC)
Administrator
*****
Posts: 1870


« Reply #12 on: January 07, 2010, 08:37:50 PM »

Oh. You are right. Data inside the remote function is packed inside a <params> *and* inside an <array>, which means the first "arr" assignment (line 2) is wrong and should be ...

arr := Param.asArray['Params']; // "remote functions" array
while arr.isType[0]=rtc_Array do arr:=arr.asArray[0];

This will also work with parameters nested knee deep in arrays ;-)

If you find any other problems, please let me know.

Best Regards,
Danijel Tkalcec
Logged
kaju74
Newbie
*
Posts: 25


« Reply #13 on: January 07, 2010, 10:14:14 PM »

Hi.

Superb...everything works as expected 8-))))) Thank you very much.

Best regards,
Marc
Logged
Pages: [1]
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.21 | SMF © 2015, Simple Machines Valid XHTML 1.0! Valid CSS!
Page created in 0.029 seconds with 17 queries.