diff --git a/.github/workflows/UnitTests.yml b/.github/workflows/UnitTests.yml index a99e18c80..0f1daa48b 100644 --- a/.github/workflows/UnitTests.yml +++ b/.github/workflows/UnitTests.yml @@ -13,6 +13,8 @@ jobs: run: (cd tools; make all) - name: Run tools unit tests run: (cd tools; make run_tests) + - name: Build swig extension and run simple test + run: (cd python; make testdocker) Unit_Tests: runs-on: ubuntu-latest diff --git a/python/Makefile b/python/Makefile new file mode 100644 index 000000000..e144ed023 --- /dev/null +++ b/python/Makefile @@ -0,0 +1,124 @@ +# SYNOPSIS: +# +# make [all] - makes everything. +# make TARGET - makes the given target. +# make run_tests - makes everything and runs all test +# make run-% - run specific test file (exclude .py) +# replace % with given test file +# make clean - removes all files generated by make. + +# Please tweak the following variable definitions as needed by your +# project, except GTEST_HEADERS, which you can use in your own targets +# but shouldn't modify. + +ifeq (,$(wildcard ./.dockerenv)) +SWIGTYPE = local +else +SWIGTYPE = global +endif + +# Where to find user code. +SRC_DIR = ../src + +# Where to find test code. +TEST_DIR = ../test + +INCLUDES = -I$(SRC_DIR) -I$(TEST_DIR) +DEFFLAGS = -DUNIT_TEST -DSWIGLIB +# Flags passed to the preprocessor. +# Set Google Test's header directory as a system directory, such that +# the compiler doesn't generate warnings in Google Test headers. +CPPFLAGS += -D_IR_LOCALE_=en-AU -fPIC $(DEFFLAGS) + +# Flags passed to the C++ compiler. +CXXFLAGS += -g -Wall -Wextra -pthread -std=gnu++11 + +PYTHONINCL = $(shell python3-config --includes) + +objects = $(patsubst %.cpp,%,$(wildcard *.cpp)) + +all : _irhvac.so + +library : $(objects) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -shared -Wl,-soname,lib_irhvac.so -o _irhvac.so $(COMMON_OBJ) + +swig : libirhvac_wrap.cxx +ifneq (,$(wildcard /.dockerenv)) + SWIG_LIB=$(shell realpath -qe swig-4.2.0/Lib) ./swig-4.2.0/swig $(INCLUDES) $(DEFFLAGS) -c++ -python libirhvac.i +else + swig $(INCLUDES) $(DEFFLAGS) -c++ -python libirhvac.i +endif + +clean : + rm -f *.o *.pyc *.cxx *.cpp + rm -rf swig-4.2.0 +distclean : + rm -f *.o *.pyc libirhvac_wrap.cxx irhvac.py _irhvac.so + rm -rf swig-4.2.0 + + +test : _irhvac.so + python test_lib.py + +docker : swig-4.2.0/swig _irhvac.so + +testdocker : swig-4.2.0/swig _irhvac.so + python test_lib.py + +# Keep all intermediate files. +.SECONDARY: + +# All the IR protocol object files. +PROTOCOL_OBJS = $(patsubst %.cpp,%.o,$(wildcard $(SRC_DIR)/ir_*.cpp)) +PROTOCOLS = $(patsubst $(SRC_DIR)/%,%,$(PROTOCOL_OBJS)) + +# Common object files +COMMON_OBJ = libirhvac_wrap.o IRutils.o IRtimer.o IRsend.o IRrecv.o IRtext.o IRac.o $(PROTOCOLS) + +# Common dependencies +COMMON_DEPS = $(SRC_DIR)/IRrecv.h $(SRC_DIR)/IRsend.h $(SRC_DIR)/IRtimer.h \ + $(SRC_DIR)/IRutils.h $(SRC_DIR)/IRremoteESP8266.h \ + $(SRC_DIR)/IRtext.h $(SRC_DIR)/i18n.h +# Common test dependencies +COMMON_TEST_DEPS = $(COMMON_DEPS) $(TEST_DIR)/IRsend_test.h + +IRtext.o : $(SRC_DIR)/IRtext.cpp $(SRC_DIR)/IRtext.h $(SRC_DIR)/IRremoteESP8266.h $(SRC_DIR)/i18n.h $(SRC_DIR)/locale/*.h + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(SRC_DIR)/IRtext.cpp + +IRutils.o : $(SRC_DIR)/IRutils.cpp $(SRC_DIR)/IRutils.h $(SRC_DIR)/IRremoteESP8266.h + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(SRC_DIR)/IRutils.cpp + +IRsend.o : $(SRC_DIR)/IRsend.cpp $(SRC_DIR)/IRsend.h $(SRC_DIR)/IRremoteESP8266.h + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(SRC_DIR)/IRsend.cpp + +IRrecv.o : $(SRC_DIR)/IRrecv.cpp $(SRC_DIR)/IRrecv.h $(SRC_DIR)/IRremoteESP8266.h $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(SRC_DIR)/IRrecv.cpp + +libirhvac_wrap.o : libirhvac_wrap.cxx + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) $(PYTHONINCL) -c libirhvac_wrap.cxx + +libirhvac_wrap.cxx : + swig $(INCLUDES) $(DEFFLAGS) -c++ -python libirhvac.i + +_irhvac.so : $(COMMON_OBJ) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -shared -Wl,-soname,lib_irhvac.so -o _irhvac.so $(COMMON_OBJ) + +swig-4.2.0/swig : + curl -s -L -o - http://downloads.sourceforge.net/project/swig/swig/swig-4.2.0/swig-4.2.0.tar.gz | tar xfz - + ( cd swig-4.2.0; ./configure ; make ) +# new specific targets goes above this line + +$(objects) : %: $(COMMON_OBJ) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ + +ir_%.o : $(SRC_DIR)/ir_%.h $(SRC_DIR)/ir_%.cpp $(COMMON_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(SRC_DIR)/ir_$*.cpp + +ir_%.o : $(SRC_DIR)/ir_%.cpp $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(SRC_DIR)/ir_$*.cpp + +%.o : %.cpp $(COMMON_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $*.cpp + +%.o : $(SRC_DIR)/%.cpp $(SRC_DIR)/%.h $(COMMON_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(SRC_DIR)/$*.cpp diff --git a/python/libirhvac.i b/python/libirhvac.i new file mode 100644 index 000000000..7f8ebe569 --- /dev/null +++ b/python/libirhvac.i @@ -0,0 +1,26 @@ +%module (package="pyhvac") irhvac +#define SWIGPYTHON_BUILTIN +%include +%include "stdint.i" +%{ + #include + #include "IRac.h" + #include "IRremoteESP8266.h" + #include "IRsend.h" +%} +%include +%template(VectorOfInt) std::vector; +%typemap(out) std::vector (PyObject* _outer) %{ + // Allocate a PyList object of the requested size. + _outer = PyList_New($1.size()); + // Populate the PyList. PyLong_FromLong converts a C++ "long" to a + // Python PyLong object. + for(int x = 0; x < $1.size(); x++) { + PyList_SetItem(_outer,x,PyLong_FromLong($1[x])); + } + $result = SWIG_Python_AppendOutput($result,_outer); +%} +%include +%include +%include + diff --git a/python/test_lib.py b/python/test_lib.py new file mode 100644 index 000000000..b346c2821 --- /dev/null +++ b/python/test_lib.py @@ -0,0 +1,11 @@ +import irhvac +ac=irhvac.IRac(4) + +ac.next.protocol = irhvac.COOLIX +ac.next.mode = irhvac.opmode_t_kAuto +ac.next.degrees = 24 +ac.sendAc() +assert ac.getTiming() == [4692, 4416, 552, 1656, 552, 552, 552, 1656, 552, 1656, 552, 552, 552, 552, 552, 1656, 552, 552, 552, 552, 552, 1656, 552, 552, 552, 552, 552, 1656, 552, 1656, 552, 552, 552, 1656, 552, 552, 552, 1656, 552, 1656, 552, 1656, 552, 1656, 552, 552, 552, 1656, 552, 1656, 552, 1656, 552, 552, 552, 552, 552, 552, 552, 552, 552, 1656, 552, 552, 552, 552, 552, 1656, 552, 1656, 552, 1656, 552, 552, 552, 552, 552, 552, 552, 552, 552, 552, 552, 552, 552, 552, 552, 552, 552, 1656, 552, 1656, 552, 1656, 552, 1656, 552, 1656, 552, 5244, 4692, 4416, 552, 1656, 552, 552, 552, 1656, 552, 1656, 552, 552, 552, 552, 552, 1656, 552, 552, 552, 552, 552, 1656, 552, 552, 552, 552, 552, 1656, 552, 1656, 552, 552, 552, 1656, 552, 552, 552, 1656, 552, 1656, 552, 1656, 552, 1656, 552, 552, 552, 1656, 552, 1656, 552, 1656, 552, 552, 552, 552, 552, 552, 552, 552, 552, 1656, 552, 552, 552, 552, 552, 1656, 552, 1656, 552, 1656, 552, 552, 552, 552, 552, 552, 552, 552, 552, 552, 552, 552, 552, 552, 552, 552, 552, 1656, 552, 1656, 552, 1656, 552, 1656, 552, 1656, 552, 5244, 100000] +print("Success!") + + diff --git a/src/IRac.cpp b/src/IRac.cpp index 571e31c8e..2e36f609c 100644 --- a/src/IRac.cpp +++ b/src/IRac.cpp @@ -18,6 +18,10 @@ #else using ::roundf; #endif +#ifdef SWIGLIB +#include +extern std::vector timingList; +#endif #include "IRsend.h" #include "IRremoteESP8266.h" #include "IRtext.h" @@ -73,6 +77,7 @@ #ifndef UNIT_TEST #define OUTPUT_DECODE_RESULTS_FOR_UT(ac) #else +#ifndef SWIGLIB /* NOTE: THIS IS NOT A DOXYGEN COMMENT (would require ENABLE_PREPROCESSING-YES) /// If compiling for UT *and* a test receiver @c IRrecv is provided via the /// @c _utReceived param, this injects an "output" gadget @c _lastDecodeResults @@ -98,6 +103,9 @@ } \ } \ } +#else +#define OUTPUT_DECODE_RESULTS_FOR_UT(ac) +#endif // SWIGLIB #endif // UNIT_TEST /// Class constructor @@ -181,6 +189,11 @@ stdAc::state_t IRac::getState(void) { return next; } /// @return A Ptr to a state containing the previously sent settings. stdAc::state_t IRac::getStatePrev(void) { return _prev; } +#ifdef SWIGLIB +std::vector IRac::getTiming(void) { return timingList; } +void IRac::resetTiming(void) { timingList.clear(); } +#endif + /// Is the given protocol supported by the IRac class? /// @param[in] protocol The vendor/protocol type. /// @return true if the protocol is supported by this class, otherwise false. diff --git a/src/IRac.h b/src/IRac.h index e3c261d0d..8e4e13b26 100644 --- a/src/IRac.h +++ b/src/IRac.h @@ -8,6 +8,9 @@ #else #include #endif +#ifdef SWIGLIB +#include +#endif #include "IRremoteESP8266.h" #include "ir_Airton.h" #include "ir_Airwell.h" @@ -107,15 +110,21 @@ class IRac { static String swinghToString(const stdAc::swingh_t swingh); stdAc::state_t getState(void); stdAc::state_t getStatePrev(void); +#ifdef SWIGLIB + std::vector getTiming(void); + void resetTiming(void); +#endif bool hasStateChanged(void); stdAc::state_t next; ///< The state we want the device to be in after we send #ifdef UNIT_TEST +#ifndef SWIGLIB /// @cond IGNORE /// UT-specific /// See @c OUTPUT_DECODE_RESULTS_FOR_UT macro description in IRac.cpp std::shared_ptr _utReceiver = nullptr; std::unique_ptr _lastDecodeResults = nullptr; /// @endcond +#endif #else private: diff --git a/src/IRsend.cpp b/src/IRsend.cpp index 10e440b32..ac9e21301 100644 --- a/src/IRsend.cpp +++ b/src/IRsend.cpp @@ -15,6 +15,10 @@ #endif #include "IRtimer.h" +#ifdef SWIGLIB +std::vector timingList; +#endif + /// Constructor for an IRsend object. /// @param[in] IRsendPin Which GPIO pin to use when sending an IR command. /// @param[in] inverted Optional flag to invert the output. (default = false) @@ -155,6 +159,11 @@ void IRsend::_delayMicroseconds(uint32_t usec) { /// Ref: /// https://www.analysir.com/blog/2017/01/29/updated-esp8266-nodemcu-backdoor-upwm-hack-for-ir-signals/ uint16_t IRsend::mark(uint16_t usec) { +#ifdef SWIGLIB + // std::cout << usec << " "; + timingList.push_back(usec); + return 1; +#else // Handle the simple case of no required frequency modulation. if (!modulation || _dutycycle >= 100) { ledOn(); @@ -185,6 +194,7 @@ uint16_t IRsend::mark(uint16_t usec) { elapsed = usecTimer.elapsed(); // Update & recache the actual elapsed time. } return counter; +#endif } /// Turn the pin (LED) off for a given time. @@ -194,7 +204,12 @@ uint16_t IRsend::mark(uint16_t usec) { void IRsend::space(uint32_t time) { ledOff(); if (time == 0) return; +#ifdef SWIGLIB + // std::cout << time << " "; + timingList.push_back(time); +#else _delayMicroseconds(time); +#endif } /// Calculate & set any offsets to account for execution times during sending. diff --git a/src/IRsend.h b/src/IRsend.h index 38491372a..ef1e42fc0 100644 --- a/src/IRsend.h +++ b/src/IRsend.h @@ -6,6 +6,9 @@ #define __STDC_LIMIT_MACROS #include +#ifdef SWIGLIB +#include +#endif // SWIGLIB #include "IRremoteESP8266.h" // Originally from https://github.com/shirriff/Arduino-IRremote/ @@ -14,8 +17,14 @@ #if TEST || UNIT_TEST #define VIRTUAL virtual +#ifdef SWIGLIB +#define VIRTUALMS +#else +#define VIRTUALMS virtual +#endif // SWIGLIB #else #define VIRTUAL +#define VIRTUALMS #endif // Constants @@ -43,6 +52,11 @@ const uint32_t kDefaultMessageGap = 100000; /// @note Not using "-1" as it may be a valid external temp const float kNoTempValue = -100.0; +#ifdef SWIGLIB +// Global +extern std::vector timingList; +#endif + /// Enumerators and Structures for the Common A/C API. namespace stdAc { /// Common A/C settings for A/C operating modes. @@ -228,6 +242,7 @@ enum argo_ac_remote_model_t { SAC_WREM3 // (2) ARGO WREM3 remote (touch buttons), bit-len vary by cmd }; +#ifndef SWIG // Classes /// Class for sending all basic IR protocols. @@ -241,8 +256,8 @@ class IRsend { void begin(); void enableIROut(uint32_t freq, uint8_t duty = kDutyDefault); VIRTUAL void _delayMicroseconds(uint32_t usec); - VIRTUAL uint16_t mark(uint16_t usec); - VIRTUAL void space(uint32_t usec); + VIRTUALMS uint16_t mark(uint16_t usec); + VIRTUALMS void space(uint32_t usec); int8_t calibrate(uint16_t hz = 38000U); void sendRaw(const uint16_t buf[], const uint16_t len, const uint16_t hz); void sendData(uint16_t onemark, uint32_t onespace, uint16_t zeromark, @@ -917,5 +932,5 @@ class IRsend { const uint16_t repeat, const uint16_t freq); #endif // SEND_SONY }; - +#endif // SWIG #endif // IRSEND_H_