I am trying to use PyBind11 to create a C++ Wrapper to access my USB device on Ubuntu 22.04. I have working c++ program that does this .
#include <iostream>
#include "FUTEK_USB_DLL.h"
#include <string.h>
#include <stdio.h>
#include <ctime>
#include <ratio>
#include <chrono>
#include <unistd.h>
#include <fstream>
#include <cmath>
using namespace std;
int main()
{
_FUTEK_USB_DLL::FUTEK_USB_DLL dll;
PVOID deviceHandle ;
BYTE channelNumber = 0;
string sn = dll.Get_Device_Serial_Number(0);
char *serialNumber = new char[sn.length() + 1];
strcpy(serialNumber, sn.c_str());
dll.Open_Device_Connection(serialNumber);
deviceHandle = dll.DeviceHandle;
string boardType = dll.Get_Type_of_Board(deviceHandle);
string firmVersion = dll.Get_Firmware_Version(deviceHandle);
string samplingRate = dll.Get_ADC_Sampling_Rate(deviceHandle, channelNumber);
double offsetValue = stod(dll.Get_Offset_Value(deviceHandle, channelNumber));
double fullScaleLoad = stod(dll.Get_Fullscale_Load(deviceHandle, channelNumber));
double offsetLoad = stod(dll.Get_Offset_Load(deviceHandle, channelNumber));
double fullScaleValue = stod(dll.Get_Fullscale_Value(deviceHandle, channelNumber));
double decimalPoint = stod(dll.Get_Decimal_Point(deviceHandle, channelNumber));
cout << "Get_Offset_Value: " << offsetValue << endl;
cout << "Get_Fullscale_Load: " << fullScaleLoad << endl;
cout << "Get_Offset_Load: " << offsetLoad << endl;
cout << "Get_Fullscale_Value: " << fullScaleValue << endl;
cout << "Get_Decimal_Point: " << decimalPoint << endl;
while(true)
{
double normalDataRequest = stod(dll.Normal_Data_Request(deviceHandle, channelNumber));
//cout << "Normal_Data_Request: " << normalDataRequest << endl;
float torque = (normalDataRequest - offsetValue)*((fullScaleLoad - offsetLoad)/(fullScaleValue-offsetValue)) / pow(10, decimalPoint);
cout << "torque: " << torque << endl;
usleep((unsigned int)500000);
}
dll.Close_Device_Connection(serialNumber);
}
This code uses two external shared libraries that I link when compiling.
all: main.cpp
g++ -D _GLIBCXX_USE_CXX11_ABI=0 -std=c++11 -Wall main.cpp -o main -lFUTEK_USB-x86_64 -lftd2xx
I am now trying to use PyBind11 to be able to call this code from Python. I am new to PyBind11, but I did get the "hello world" example to compile and work from being called from python. My issue is I am not sure how to link these two dependencies (FUTEK_USB and lftd2xx) in my module using CMake. Here is my CMakeLists.txt
cmake_minimum_required(VERSION 3.4)
project(pybind_test)
find_library(futekLib FUTEK_USB-x86_64)
if (${futekLib} STREQUAL futekLib-NOTFOUND)
message(FATAL_ERROR "No FutekLib, sad day")
else()
message(STATUS "futeklib Found! as ${futekLib}")
endif()
find_library(ftd2xxLib ftd2xx)
if (${ftd2xxLib} STREQUAL ftd2xxLib-NOTFOUND)
message(FATAL_ERROR "No ftd2xxLib, sad day")
else()
message(STATUS "ftd2xxLib Found! as ${ftd2xxLib}")
endif()
add_subdirectory(pybind11)
include_directories(thirdparty)
pybind11_add_module(bind_test main.cpp)
target_link_libraries(bind_test PUBLIC ${futekLib} ${ftd2xxLib})
By adding those message statements to my CMakeLists.txt I can confirm that it does find those two libraries. When i build this python module and then try to import it, I keep getting this undefined reference
<ipython-input-1-a9d1c7c9c951> in <module>
----> 1 import bind_test
ImportError: /home/dyno/Downloads/futek_usb_linux_x86_64/FUTEK_USB-x86_64/bind_test.cpython-310-x86_64-linux-gnu.so: undefined symbol: _ZN14_FUTEK_USB_DLL13FUTEK_USB_DLL21Get_ADC_Sampling_RateB5cxx11EPvh
Here is my PyBind11 file im trying to run
#include <pybind11/pybind11.h>
#include <iostream>
#include "FUTEK_USB_DLL.h"
#include <string.h>
#include <stdio.h>
#include <ctime>
#include <ratio>
#include <chrono>
#include <unistd.h>
#include <fstream>
namespace py = pybind11;
using namespace std;
std::string get_torque(){
_FUTEK_USB_DLL::FUTEK_USB_DLL dll;
PVOID deviceHandle ;
BYTE channelNumber = 0;
string sn = dll.Get_Device_Serial_Number(0);
char *serialNumber = new char[sn.length() + 1];
strcpy(serialNumber, sn.c_str());
dll.Open_Device_Connection(serialNumber);
deviceHandle = dll.DeviceHandle;
string boardType = dll.Get_Type_of_Board(deviceHandle);
string firmVersion = dll.Get_Firmware_Version(deviceHandle);
string samplingRate = dll.Get_ADC_Sampling_Rate(deviceHandle, channelNumber);
string adc = dll.Normal_Data_Request(deviceHandle, channelNumber);
return adc;
}
PYBIND11_MODULE(bind_test, handle){
handle.doc()="TEST";
handle.def("python_name", &get_torque);
}
Any help would be greatly appreciated as I am very stuck on this issue. Here is the repo with the full code https://github.com/RavenLabsNH/FutekTorqueTransducer
Related
I am developing a python GUI for one cpp module. My question is, is it possible to get this gui to output to std::cin from cpp ? I tried using Popen but I cant figure out how it works. Also, is there any other way to output values from my python gui into the CPP ?
Example:
CPP
#include <fstream>
#include <sstream>
#include <string>
#include <stdio.h>
//#include <conio.h>
#include <errno.h>
#include <Python.h>
#include <thread>
using namespace std;
void LaunchGui(bool &IsAlive){
FILE* scr;
Py_Initialize();
scr = fopen("gui.py", "r");
PyRun_SimpleFile(scr,"gui.py");
fclose(scr);
Py_Finalize();
IsAlive = false;
}
int main()
{
std::string line;
bool IsAlive{true};
int speed = 1;
std::thread GuiThread(LaunchGui,std::ref(IsAlive));
while(IsAlive){
std::cin >> line;
if(!line.empty() ){
std::cout << line << std::endl;
}
}
GuiThread.join();
return 0;
}
Python GUI
from tkinter import ttk
from subprocess import Popen, PIPE
root = Tk()
root.title('GUI')
root.geometry("800x600")
root.minsize('800','600')
root.maxsize('800','600')
p = Popen(['test'], shell=True, stdout=PIPE, stdin=PIPE)
def Manual(value):
print("From PythonGUI: " + str("send " + value))
output = bytes(str(value), 'UTF-8')
p.stdin.write(output)
Slider = Scale(root ,orient=HORIZONTAL,sliderlength = 25,from_=0,to=100,resolution = 1, label= "Value",font=("Ubuntu", 11),length= 500,command=Manual)
Slider.pack(side= TOP, anchor="w")
root.mainloop()
I'm using swig to connect a C ++ function with my Python libraries. I managed to compile everything and create the .so file. But after having done the import of my C ++ function in python I have some errors on the argv ... maybe I'm wrong to pass the values to it from the outside or maybe when I compiled and created the .i swig file I got something wrong. The C ++ function creates a TCP socket and sends a Hex code to a device that replies with another Hex code, from a terminal with the C ++ code i write:
connect_PE_func 192.168.1.170 600000060
and it works perfectly. and it works perfectly. I would expect a similar syntax once I set my c ++ function via swig in python, type:
answer= connect_PE_func.connect_PE_func("192.168.1.170", 600000060,2)
but I get this error:
test=connect_PE_func.connect_pe_func(["192.168.2.170"],["2600000026"])
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-12-f58f79b27fe5> in <module>
----> 1 test=connect_PE_func.connect_pe_func(["192.168.2.170"],["2600000026"])
~/mqtt_atenapy/C_connect_PE_dev/test_cpython/connect_PE_func.py in connect_pe_func(argv, argc)
64
65 def connect_pe_func(argv, argc):
---> 66 return _connect_PE_func.connect_pe_func(argv, argc)
67
68
TypeError: in method 'connect_pe_func', argument 1 of type 'char *[]'
I attach the code to the files .c .h e .i which I used with swig to get the .so.
thanks.
connect_PE.func.c :
#include <stdio.h>
#include <errno.h>
#include <string>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <cstring>
#include <unistd.h>
#include <iostream>
#include <unistd.h>
#include <sstream>
#include "connect_PE_func.h"
using namespace std;
// to compile gcc connect_PE_func.cpp -lstdc++ -c
char* connect_pe_func(char *argv[],int argc)
{
int sockfd, n;
int connected = 0;
struct sockaddr_in servaddr;
std::string serveraddr = argv[1];
sockfd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = inet_addr(serveraddr.c_str());
servaddr.sin_port = htons(9761);
connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
std::string pref_hex;
std::string hex("0x");
std::string test = argv[2];
size_t numbytes = test.size() / 2;
uint8_t command[numbytes];
for (size_t w = 0, x = 0; w < numbytes; ++w, x += 2)
{
pref_hex = hex + test.substr(x, 2);
cout << pref_hex;
command[w] = stoi(pref_hex, nullptr, 16);
//cout << test.substr(x, 2);
//cout << "\n";
//cout << command[w];
//cout << "\n";
}
//uint8_t command[] = {0x26, 0x00, 0x00, 0x00, 0x26};
int bytes_to_send = sizeof(command);
send(sockfd, command, bytes_to_send, 0);
uint8_t output_command[numbytes];
recv(sockfd, output_command, bytes_to_send, 0);
char test_out[10];
for (size_t w = 0, x = 0; w < numbytes; ++w, x += 2)
{
test_out[x] = (char)output_command[w];
//cout << unsigned(test_out[x]);
}
return test_out;
}
connect_PE_func.h:
// file: connect_PE_func.h
char* connect_pe_func(char *argv[], int argc);
connect_PE_func.i:
/* file: connect_PE_func.i */
%module connect_PE_func
%{
/* Everything in this block will be copied in the wrapper file. We include the C header file necessary to compile the interface
*/
#include "connect_PE_func.h"
// extern char *connect_pe_func(int argc, char *argv[]);
%}
/* list functions to be interfaced: */
char* connect_pe_func(char *argv[], int argc);
You've misplaced a comma func(["192.168.2.170"] , ["2600000026"])
My guess is that you need to pass func(["192.168.2.170", "2600000026"], 2)
I'm not sure why I'm having easier time string searching in program I wrote in python faster than a program I wrote in C++. Is there a trick I'm missing?
Generating Use Case
This is for a single line use case, however in the real use case I care about multiple lines.
#include "tchar.h"
#include "stdio.h"
#include "stdlib.h"
#include <string>
#include <sstream>
#include <iostream>
#include <fstream>
#include <ctime>
using namespace std;
void main(void){
ofstream testfile;
unsigned int line_idx = 0;
testfile.open("testfile.txt");
for(line_idx = 0; line_idx < 50000u; line_idx++)
{
if(line_idx != 43268u )
{
testfile << line_idx << " dontcare" << std::endl;
}
else
{
testfile << line_idx << " care" << std::endl;
}
}
testfile.close();
}
The regular expression
Using regular expression ^(\d*)\s(care)$
The C++ Program takes 13.954 seconds
#include "tchar.h"
#include "stdio.h"
#include "stdlib.h"
#include <string>
#include <sstream>
#include <iostream>
#include <fstream>
#include <ctime>
using namespace std;
void main(void){
double duration;
std::clock_t start;
ifstream testfile("testfile.txt", ios_base::in);
unsigned int line_idx = 0;
bool found = false;
string line;
regex ptrn("^(\\d*)\\s(care)$");
start = std::clock(); /* Debug time */
while (getline(testfile, line))
{
std::smatch matches;
if(regex_search(line, matches, ptrn))
{
found = true;
}
}
testfile.close();
duration = ( std::clock() - start ) / (double) CLOCKS_PER_SEC;
std::cout << "Found? " << (found ? "yes" : "no") << std::endl;
std::cout << " Total time: " << duration << std::endl;
}
Python Program takes 0.02200 seconds
import sys, os # to navigate and open files
import re # to search file
import time # to benchmark
ptrn = re.compile(r'^(\d*)\s(care)$', re.MULTILINE)
start = time.time()
with open('testfile.txt','r') as testfile:
filetext = testfile.read()
matches = re.findall(ptrn, filetext)
print("Found? " + "Yes" if len(matches) == 1 else "No")
end = time.time()
print("Total time", end - start)
Implemented Ratah's recommendation to 8.923
about 5 seconds improvement, by reading file to single string
double duration;
std::clock_t start;
ifstream testfile("testfile.txt", ios_base::in);
unsigned int line_idx = 0;
bool found = false;
string line;
regex ptrn("^(\\d*)\\s(care)$");
std::smatch matches;
start = std::clock(); /* Debug time */
std::string test_str((std::istreambuf_iterator<char>(testfile)),
std::istreambuf_iterator<char>());
if(regex_search(test_str, matches, ptrn))
{
found = true;
}
testfile.close();
duration = ( std::clock() - start ) / (double) CLOCKS_PER_SEC;
std::cout << "Found? " << (found ? "yes" : "no") << std::endl;
std::cout << " Total time: " << duration << std::endl;
After UKMonkey's note, reconfigured project to release which also includes \O2 and brought it down to 0.086 seconds
Thanks to Jean-Francois Fabre, Ratah, UKMonkey
I have a function in c++ that returns vector. I am using BOOST_PYTHON_MODULE to call it from python. I want to send a pointer as input to the c++ function. I was trying to send the pointer as string. I know this is the worst method but some people are using it and it is working fine. For my case it isn't working. I am new in c++.
Working case:
#include <iostream>
#include <string.h>
#include <sstream>
using namespace std;
int main(){
string s1 = "0x7fff3e8aee1c";
stringstream ss;
ss << s1;
cout << "ss : " << ss << endl;
long long unsigned int i;
ss >> hex >> i;
cout << "i : " << i << endl;
int *i_ptr=reinterpret_cast<int *>(i);
cout << "pointer: " << i_ptr << endl;
return 0;
}
My case:
#include "openrave-core.h"
#include "ros/ros.h"
#include "std_msgs/String.h"
#include "valid_grasp_generator/GraspSnapshot.h"
#include <iostream>
#include <vector>
#include <boost/python.hpp>
#include <sstream>
using namespace OpenRAVE;
using namespace std;
boost::python::list get_penetration_depth(string env)
{
vector<double> vec(9,0);
stringstream ss(env);
long long unsigned int i;
ss >> hex >> i;
EnvironmentBasePtr penv = reinterpret_cast<int *>(i);
//code for getting penetration value from the openrave
typename std::vector<double>::iterator iter;
boost::python::list list;
for (iter = vec.begin(); iter != vec.end(); ++iter) {
list.append(*iter);
}
return list;
}
BOOST_PYTHON_MODULE(libdepth_penetration){
using namespace boost::python;
def("get_penetration", get_penetration_depth);
}
Error during catkin_make:
/home/saurabh/catkin_ws/src/valid_grasp_generator/src/depth_penetration.cpp: In function ‘boost::python::list get_penetration_depth(std::string)’:
/home/saurabh/catkin_ws/src/valid_grasp_generator/src/depth_penetration.cpp:37:56: error: conversion from ‘int*’ to non-scalar type ‘OpenRAVE::EnvironmentBasePtr {aka boost::shared_ptr<OpenRAVE::EnvironmentBase>}’ requested
EnvironmentBasePtr penv = reinterpret_cast<int *>(i);
^
make[2]: *** [valid_grasp_generator/CMakeFiles/depth_penetration.dir/src/depth_penetration.cpp.o] Error 1
make[1]: *** [valid_grasp_generator/CMakeFiles/depth_penetration.dir/all] Error 2
make: *** [all] Error 2
Invoking "make -j8 -l8" failed
In your case, you try a wrong conversion between an int to a shared_ptr:
EnvironmentBasePtr penv = reinterpret_cast<int *>(i); // wrong conversion
// where EnvironmentBasePtr is boost::shared_ptr<EnvironmentBase>
For this conversion I suggest using a constructor of shared_ptr and a null deleter. I see 3 possible solutions.
You can define your own null deleter void deleter(void* ptr) {} then :
boost::shared_ptr<EnvironmentBase> penv(reinterpret_cast<EnvironmentBase *>(i),&deleter);
If your boost is 1.55 or less, you can include <boost/serialization/shared_ptr.hpp> then :
boost::shared_ptr<EnvironmentBase> penv(reinterpret_cast<EnvironmentBase *>(i),boost::serialization::null_deleter());
If your boost is 1.56 or more, you can include <boost/core/null_deleter.hpp> then :
boost::shared_ptr<EnvironmentBase> penv(reinterpret_cast<EnvironmentBase *>(i),boost:null_deleter());
The code is
{
char name[MAX_JSON_FIELD];
FILE *fp;
copy_cJSON(name,objs[0]);
if ( (fp= fopen(name, "r")) != 0 )
{
Py_Initialize();
PyRun_SimpleFile(fp, name);
Py_Finalize();
fclose(fp);
}
return(clonestr("return string"));
}
How can I get it to return the output of the python file instead of printing it?
I achieved this using a huge workaround. I made both C and Python read and write into a file. I didn't find a better option yet.
I found an actual solution. It consists of 2 files: "main.c" that opens the script-file "script.py" which compares two strings (here: "Hello" and "Mars") and returns the longer one. I still find it strange that it takes ~20 commands to achieve this, maybe there's a better solution out there.
[main.c]
//compile me with "gcc main.c -I/usr/include/python2.7 -lpython2.7"
//original source: "http://python.haas.homelinux.net/python_kapitel_26_003.htm"
//owner is Peter Kaiser and Johannes Ernesti who published the Book "Python" under Galileo Computing
//Translation from german with many additional (and very unprofessional) comments and slight adaption by Cupacoffee, 17.02.2015.
//bugs, grammar mistakes and wrong explainations are my contribution
#include <Python.h>
int main (int argc, char *argv[])
{
char *result;//This char will receive the return value.
PyObject *module, *func, *prm, *ret;//These are some helping variables i don't understand.
Py_Initialize();
PySys_SetPath(".");//Sets the working path to the current path
module = PyImport_ImportModule("script");//Import of the script-file, note that the actual script name is "script.py"!
if (module != 0)//Asks if the script was loaded at all.
{
func = PyObject_GetAttrString(module, "compare_function");//Opens a function within the python script. Notice that you must use a function within the python script, because otherwise you can't return anything.
prm = Py_BuildValue("(ss)", "Hello", "Mars");//The "(ss)" means two strings are passed (replace with "i" for integer for instance), the "Hello" and "Mars" are the strings i pass to the script.
ret = PyObject_CallObject(func, prm);//Returns some python object i have literally no idea about ...
result = PyString_AsString(ret);// ... but luckily there's a function to cast it back to a c-compatible char*!
printf("The Script decdided that '%s' is longer!",result);
Py_DECREF(module);//cleanup?
Py_DECREF(func);//cleanup?
Py_DECREF(prm);//cleanup?
Py_DECREF(ret);//cleanup?
}
else//No script found
{
printf("Error: No script file named \"script.py\" was found!\n");
}
Py_Finalize();
return 0;
}
[script.py]
def compare_function(a, b):#this function takes 2 parameters, they are strings
return (a if min(a) < min(b) else b)#they get compared and returned to the c-program
Good luck.
*Grumble, took me over 2 hours to format this text so I could post it.*
A friend of mine gave me some code snippets which answer the problem. I didn't want to edit the old post because these two programs are entirely new approaches; One in C and one in C++. Both use the same Python Script.
He also pointed me to the manual page of "system" [Unix: "man system"] and "popen" [Unix: "man popen"]. The second one allows interactive communication, which might be useful later.
[C-File:]
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
int callScript()
{
char *cmd = "./script.py hello world";
return WEXITSTATUS(system(cmd));
}
int main(int argc, char *argv[])
{
printf("main - argc: %d, arguments:\n", argc);
for (int i = 0; i < argc; i++)
printf("\targv[%d]: %s\n", i, argv[i]);
int ret = callScript();
printf("exit code of script %d\n", ret);
return 0;
}
[C++-File:]
#include <string>
#include <sstream>
#include <iostream>
#include <stdlib.h>
#include <sys/wait.h>
int callScript(std::string args)
{
std::string cmd = "./script.py" + args;
int ret = system(cmd.c_str());
return WEXITSTATUS(ret);
}
int main(int argc, char *argv[])
{
std::cout << "main - argc: " << argc << ", arguments:" << std::endl;
std::stringstream args;
for (int i = 0; i < argc; i++)
{
std::cout << "\targv[" << i << "]: " << argv[i] << std::endl;
if (i)
args << " " << argv[i];
}
int ret = callScript(args.str());
std::cout << "exit code of script " << ret << std::endl;
return 0;
}
[Python-Script:]
#!/usr/bin/env python
import sys
def Hello(person):
print "Hello " + person
def PrintArgs(argv):
for arg in argv:
print arg
if __name__ == "__main__":
Hello("World!")
PrintArgs(sys.argv[1:])
sys.exit(2)