Skip to content

Latest commit

 

History

History
149 lines (101 loc) · 8.35 KB

README.md

File metadata and controls

149 lines (101 loc) · 8.35 KB

libezsp developper information

Source code organisation

This repository is organised following hexagonal achitecture conventions. This means that the core code of the library (domain) is isolated (inside folder src/ezsp) from the code that allows this library to make use of external resources or library (spi) and from translation code that makes information available from this library to other software (api).

  • src/ezsp contains most of the library code (EZSP/ASH encoding/decoding and protocol sequence implementation)
  • src/spi contains the adapters for platform-specific implementations of timers and serial port access
  • src/spi/serial contains the concrete implementations of an adapter to read/write on the serial port for Linux and Windows (using libserialcpp)
  • src/spi/cppthreads contains the concrete implementation of timers using C 11 threads
  • src/spi/mock-uart contains a framework to emulate a serial port for unit testing
  • src/spi/raritan contains the concrete implementations of adapters for the Raritan framework (using selectors and an event-driven main loop)
  • src/spi/console contains the concrete implementations of a console logger
  • src/spi/aes contains the concrete implementations of a basic AES encryption/decryption
  • example contains the code for a sample demo program to read and report sensor values from a zigbee network
  • tests contains code for automatic unit testing

SPI connectors implement a concrete implementation that complies to a specified interface declared in interface headers (.h header files which filename starts with a capital I). This allows dependency inversion paradigm, where the connector depends on the library, rather than the library depends on the underlying connectors. Connectors then become interchangeable, and we use this method a lot to abstract the library from the services it uses (a version of these services are using the Raritan framework, another version is independent from the Raritan framework and can run outside of it).

Coding style

C code should be formatted using astyle --style=java --indent=tab --break-closing-brackets to ensure homogeneous code style and indentation. This means identation should use tabs, and opening curly braces should be placed at the end of the previous line like most java-formatted code.

SPI interface definition

  • include/spi/ITimer.h and include/spi/ITimerBuilder.h

    ITimer.h: Abstract interface to which must conforms implementations of classes that handle timed callbacks ITimerBuilder.h: Abstract interface to which must conforms implementations of builder classes that generate ITimer-derived objects

    Concrete Builder API for each framework:

    • include/spi/TimerBuilder.h
    • src/spi/TimerBuilder.cpp A TimerBuilder will generate the appropriate ITimer-derived object (for example a RaritanTimer or a CppThreadsTimer) for the platform where it runs, thus avoiding platform-specific code.

    Framework implementation: Concrete implementation in the raritan framework:

    • src/spi/raritan/RaritanTimer.{h,cpp}

    Concrete implementation using C threads:

    • src/spi/cppthreads/CppThreadsTimer.{h,cpp}
  • include/spi/Logger.h

    Generic singleton logger

  • include/spi/ILogger.h

    Abstract interface to which must conforms implementations of classes that log debug/warning/error messages

    Framework implementation: Concrete implementation in the raritan framework:

    • src/spi/raritan/RaritanLogger.{h,cpp}

    Concrete implementation that outputs messages on the system console:

    • src/spi/console/ConsoleLogger.{h,cpp}
  • include/spi/IAsyncDataInputObserver.h

    Abstract interface to which must conforms implementations of classes that get asynchronous notifications on incoming data This is an exception, it is not a DIP implementation, the concrete implementation lies inside the applicative code (inside the library)

  • include/spi/IUartDriver.h and include/spi/IUartDriverBuilder.h

    IUartDriver.h: Abstract interface to which must conforms concrete implementations of classes that manipulate UARTs IUartDriverBuilder.h: Abstract interface to which must conforms implementations of builder classes that generate IUartDriver-derived objects

    Concrete Builder API for each framework:

    • include/spi/UartDriverBuilder.h
    • src/spi/UartDriverBuilder.cpp A UartDriverBuilder will generate the appropriate IUartDriver-derived object (for example a RaritanUartDriver or a SerialUartDriver) for the platform where it runs, thus avoiding platform-specific code.

    Framework implementation: Concrete implementation in the raritan framework:

    • src/spi/raritan/RaritanUartDriver.{h,cpp}

    Concrete implementation using libserial:

    • src/spi/serial/SerialUartDriver.{h,cpp}

    Concrete implementation of a robotized emulated serial port for unit testing:

    • src/spi/mock-uart/MockUartDriver.{h,cpp}
  • include/spi/IAes.h

    Abstract interface to which must conforms implementations of classes that encrypt/decrypt using AES

    Concrete Builder API for each framework:

    • include/spi/IAesBuilder.h
    • src/spi/IAesBuilder.cpp A IasBuilder will generate the appropriate IAes-derived object (for example a CAes) for the platform where it runs, thus avoiding platform-specific code.

    Framework implementation: Concrete implementation using a custom AES code:

    • src/spi/aes/custom-aes/custom-aes.{h,cpp}

Generic rules for coding

All dependency on an external service should be implemented as an SPI (Service Provider Interface), as dictated by Hexagonal Architeture.

  • C 11 is supported and C 11 features are to be preferred over home-made equivalent using earlier versions of the standard C . For example, you should use std::thread, constexpr, std::move that is available in C 11. C 14-specific and above (C 17, C 20 etc.) language and STL features should be avoided (see the related Wikipedia page).

  • All headers that will be required for compilation by a client code of the library should be placed inside the folder include/. In that folder, we will distinguish between headers for the libezsp itself (domain), located in include/ezsp/ and the includes related to SPIs located in include/spi/.

  • All #include directives used inside the headers files located under include/ should be done using named header include (using angle brackets < and >).

  • All classes that should be accessible from the outside should prefix the class name with the macro LIBEXPORT (defined in include/ezsp/export.h. Most probably, all classes and functions in header files located in under include/ should thus declare themselves using LIBEXPORT.

  • All header files located under include/ should use #ifdef, #define, #endif preprocessor conditions in order not to double-declare content when included more than once (or they could also use #pragma once, but this is less portable).

Indentation

Indentation of the code should match what is enforced by the following command:

astyle --style=java --indent=tab --break-closing-brackets

Generating doxygen documentation

In order to generate the source code documentation out of the doxygen tags contained inside the code, run the following command:

cd /path/to/libezsp
doxygen Doxyfile.dev

The resulting doc will be available via a browser by opening `~/libezsp/doc/html/index.html

Running automated unit tests

Unit tests are located under tests, and are grouped by families in common .cpp files.

Unit tests can be run inside the cpputest framework (requires installing cpputest) or as a standalone executable (using the lightweight unit test utilities located in src/tests/TestHarness.h). When run as standalone, an executable named test_runner will be compiled and will contain all the code to run for automated unit testing.

In order to both compile and run unit tests, using -DUSE_CPPTHREADS=ON -DUSE_GCOV=y -DUSE_SERIALCPP=OFF -DUSE_MOCKSERIAL=ON options when running cmake:

cmake -DUSE_CPPTHREADS=ON -DUSE_GCOV=y -DUSE_SERIALCPP=OFF -DUSE_MOCKSERIAL=ON .
make
./tests/gptest

If all tests pass, the above command will succeed with exit code 0.

Note that this is what travis runs when performing coverage check (see .travis.yml).