Meeting Notes
Lunar Knights Control Systems

API Designing 10/03/2021

Agenda #

  1. Recap
  2. C in Python Demo
  3. Previous control systems work
  4. Design Discussions
  5. Preparing for Next Week

C in Python Demo #

For low-level programming we will use C/C++; however, for higher tasks (such as the dashboard), we will use Python.

Dependencies #

Some C compiler such as gcc or clang. Your system probably already has one installed, but if not look up C compiler install instructions for your machine.

Python 3: https://www.python.org/downloads/

Install the cffi package for python: python3 -m pip install cffi

Sample Shared C Library #

We will use the foreign function interface (FFI) to call C functions in Python. Let's first create a C library.

Consider a header file such as:

void hello(void);
int adder(int x, int y);

And an implementation:

void hello(void) {
	printf("Hello from C!\n");
}

int adder(int x, int y) {
	return x + y;
}

Compile the demo shared library:

gcc -shared -o libdemo.so -fPIC src/demo.c

ctypes module

Documentation: https://docs.python.org/3/library/ctypes.html

from ctypes import CDLL
lib = CDLL('./libdemo.so')
lib.hello()
print('result', lib.adder(2, 3))

Pros: included in the standard Python modules

Cons: requires casting Python to C types for many data types

cffi module

Documentation: https://cffi.readthedocs.io/

from cffi import FFI

ffi = FFI()
ffi.cdef('''
void hello(void);
int adder(int x, int y);
''')

lib = ffi.dlopen('./libdemo.so')

lib.hello()

print('result', lib.adder(2, 3))

Pros: easier to deal with types

Cons: requires an external import

Sample C Extensions module #

Documentation: https://docs.python.org/3/extending/extending.html

The source c file should look like this:

static PyObject *hello(PyObject *self, PyObject *args) {
	printf("Hello from C!\n");
	return Py_None;
}

static PyObject *adder(PyObject *self, PyObject *args) {
	int x, y;
	if (!PyArg_ParseTuple(args, "ii", &x, &y)) return NULL;

	return PyLong_FromLong(x + y);
}

static PyMethodDef DemoMethod[] = {
	{"hello", hello, METH_VARARGS, "hello"},
	{"adder", adder, METH_VARARGS, "adder"},
	{NULL, NULL, 0, NULL}
};

static struct PyModuleDef demomod = {
	PyModuleDef_HEAD_INIT,
	"demo",
	NULL,
	-1,
	DemoMethod
};

PyMODINIT_FUNC PyInit_demo(void) {
	return PyModule_Create(&demomod);
}

To build this module create a setup.py file:

from distutils.core import setup, Extension

module = Extension('demo', sources=['src/demomod.c'])

setup(name='Demo Module',
    version='0.1',
    description='Demo extension',
	ext_modules=[module])

Compile the module with:

python3 setup.py build --build-lib=.

Now we can use the C extension in python:

import demo

demo.hello()
print('result', demo.adder(2, 3))

Run the demos #

python3 ctypes_demo.py
python3 cffi_demo.py
python3 cext_demo.py

Each script should produce an output similar to:

Hello from C!
Python: 5

Run python3 speed_demo.py to see the speed of each method.

Sample output:

Loading ctypes.CDLL took 0.012s
Loading cffi.FFI took 0.045s
Loading c extension took 0.004s

Running adder for 20,000,000 iterations...
ctypes took 3.146s
cffi took 2.720s
cext took 1.384s

Previous control systems work #

You had to be there, but we demonstrated the previous robot dashboard after connecting to the raspberry pi. We were able to init the CAN bus and get the currents from each TalonSRX.

Design Discussion #

We will mostly be working on tasks in parallel. In order to do this effectively, we need to define the explicit API between each layer. Here is an example of what this API might look like between the C, Python, and Web layers. We need to make some design decisions in regard to what the outward facing API is for each layer before we start heavy implementation.

liblk: C library
	hardware:
		- Talon.cpp
			- set_power(float) -> void
			- get_current(void) -> float
			- get_ticks(void) -> float
		- Stepper.cpp
			- step(void) -> void
			- step_by(int) -> void
			- get_steps() -> int
		- IMU.cpp
			- get_heading() -> float
			- get_yaw() -> float
			- get_pitch() -> float
	robot:
		- Base.cpp
			- turn(float) -> void
			- travel(float) -> void
		- Intake.cpp
		- Depo.cpp
		- Robot.cpp
			- init(void) -> void
			- start_heartbeat() -> void
			- stop_all() -> void
	utils:
		- Logger.cpp

lkpy: Python C extension module
	- lkpymodule.cpp
		- create_robot(void) -> Robot
		- kill(void) -> boolean
		- get_status(void) -> Status

web api:
	POST:
		- /gamepad {input_id: value}
		- /kill
	GET:
		- /dashboard
		- /sensor?id=

Preparing for Next Week #

Besides thinking about some API design choices, here are some concepts you may want to look at in preparing for the implementation phase. Some of these resources will also help us make choices about the design.

C/C++ Stuff #

Some design choices to be made here are:

Python Stuff #

Some design choices to be made here are:

Web Stuff #

Some design choices to be made here are: