jfjlaros / arduino-simple-rpc

Python client for the Arduino simpleRPC protocol.
MIT License
24 stars 7 forks source link

cannot import class methods #15

Closed drbeefsupreme closed 3 years ago

drbeefsupreme commented 3 years ago

Describe the bug Let me preface this by saying that I have very little experience with C/C++/Python, so this is most likely just me not understanding something rather than a bug.

I am attempting to import methods from a class exported with the simpleRPC libary following the instruction on exporting class methods here from a Teensy 3.6, but the presence of a period "." in the name causes Python to throw a syntax error. I suppose this is because Python doesn't want periods in method names, but this is how C++ refers to methods.

To Reproduce Steps to reproduce the behaviour:

On the Arduino:

Here foo.h is a class header file and bar is a method in foo.

#include <simpleRPC.h>
#include <foo.h>

StreamIO io;
foo foo1;

void setup() {
  Serial.begin(9600);
  io.begin(Serial);
}

void loop() {
  interface(io, pack(&foo1, &foo::bar));
}

Then, on the computer connected to the Arduino via USB Serial, in the python3 shell:

>>> from simple_rpc import Interface
>>> interface = Interface('/dev/ttyACM0')

Traceback
...
File "<string>", line 2
  def foo1.bar():
          ^
SyntaxError: invalid syntax

(the ^ is pointing at the .)

Expected behavior The function is imported correctly.

Additional context I don't think it matters, but in my scenario, foo is a templated class and I'm using a Teensy.

I tried just changing the dot to an underscore and ran into a different issue that might be unrelated, but in any case I'm guessing that doing that will make it try to call the wrong method anyways unless I told it to change the underscore back to a dot when the RPC is made.

The actual code I am running is here: https://github.com/drbeefsupreme/qyron/tree/arduino-rpc

If you have platformio installed you should be able to get it working without too much trouble - just change the platformio.ini file https://github.com/drbeefsupreme/qyron/blob/arduino-rpc/teensy/platformio.ini in the teensy folder to refer to github simpleRPC library rather than the local one (which at time of writing is the same as the github one). But you probably also need to be using a Teensy 3.6 for it to compile.

The actual error message:

>>> interface = Interface('/dev/ttyACM0')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/chyron/.local/share/virtualenvs/rpi-cQqRlRR_/lib/python3.7/site-packages/simple_rpc/simple_rpc.py", line 37, in __init__
    self.open()
  File "/home/chyron/.local/share/virtualenvs/rpi-cQqRlRR_/lib/python3.7/site-packages/simple_rpc/simple_rpc.py", line 120, in open
    self, method['name'], MethodType(make_function(method), self))
  File "/home/chyron/.local/share/virtualenvs/rpi-cQqRlRR_/lib/python3.7/site-packages/simple_rpc/extras.py", line 52, in make_function
    context)
  File "<string>", line 2
    def scrollingLayer1.start(self, a, arg1):
                       ^
SyntaxError: invalid syntax

Any pointers would be appreciated. Thanks!

jfjlaros commented 3 years ago

It seems that the Python code wants to add the function foo.bar to the interface, but this is not a valid function name since it contains a dot (.).

Renaming the function to foo_bar might resolve the issue, e.g., change

pack(&scrollingLayer1, &SMLayerScrolling<RGB_TYPE(COLOR_DEPTH), kScrollingLayerOptions>::start), "scrollingLayer1.start: display text on layer 1. @a: char* @return: none");

to

pack(&scrollingLayer1, &SMLayerScrolling<RGB_TYPE(COLOR_DEPTH), kScrollingLayerOptions>::start), "scrollingLayer1_start: display text on layer 1. @a: char* @return: none");

I suppose it is possible to add a form of nesting to the client, so you can use interface.foo.bar() and interface.foo.baz(). Is this what you are looking for?

drbeefsupreme commented 3 years ago

Sorry for the delay! I did not expect that the name for the function would be pulled from the string given when defining the interface so I never checked that that was the case. Changing the name as you suggested worked - and I discovered that my workaround of changing the name on the Python side seemed to work fine as well, my issue was trying to send text via a "string" rather than b'bytes'.

Thanks!

jfjlaros commented 3 years ago

I see where the confusion comes from. The library extracts the function signature from a function pointer, so we can only deduce the return type and all of its parameter types. This is just enough to build an RPC interface, but with cryptic names only (i.e., functions are named method0, method1, etc. and parameters are named arg0, arg1, etc.). The documentation string is there to provide human readable names.