Program Listing for File microparcel.h

Return to documentation for file (include/microparcel/microparcel.h)

#ifndef MICROPARCEL_H
#define MICROPARCEL_H

#include <cstdint>
#include <cstring>
#include <limits>
#include <numeric>
#include <iterator>

namespace microparcel{
    template <uint8_t Size>
    class Message{
        public:
            static const uint8_t kSize = Size;
            template <typename T, uint8_t Offset, uint8_t Bitsize>
            inline T get(){
                //check consistency
                static_assert(std::numeric_limits<T>::digits <= 16, "Can't get data larger than uint16_t");
                static_assert(Bitsize <= 16, "Bit size is bigger than 16");
                static_assert(Bitsize > 0, "Bit size can't be zero");

                static_assert(std::numeric_limits<T>::digits >= Bitsize, "the return type can't handle Bitsize");
                static_assert((Offset + Bitsize) <= 8 * Size, "Bitsize+Offset is out of range");

                static_assert((Bitsize <= 8) or ((Bitsize > 8) and (Offset & 0x3) == 0), "Offset must be a multiple of 8 for >8 bit bitfield");

                // for <= 8bits:
                if(Bitsize <= 8){
                    // on one byte
                    if((Offset & 0x7) + Bitsize <= 8){
                        uint8_t mask = (1<<Bitsize) - 1;
                        uint8_t byte_idx = Offset >> 3;
                        uint8_t byte_shift = Offset & 0x7;
                        return (data[byte_idx] >> byte_shift) & mask;
                    }

                    else{
                        uint8_t mask = (1<<Bitsize) - 1;
                        uint8_t byte_idx = Offset >> 3;
                        uint8_t byte_shift = Offset & 0x7;
                        uint8_t mask_lsb = mask & ( (1 << (8 - byte_shift)) - 1);
                        uint8_t mask_msb = mask >> (8 - byte_shift);

                        uint8_t lsb_part = (data[byte_idx] >> byte_shift) & mask_lsb;
                        uint8_t msb_part = data[byte_idx+1] & mask_msb;

                        return lsb_part | (msb_part << (8 - byte_shift));
                    }
                }

                // for >8bits
                else{
                    uint16_t mask = (1<<Bitsize) - 1;
                    uint16_t byte_idx = Offset >> 3;

                    return (data[byte_idx] & (uint8_t)(mask&0xFF)) | ((data[byte_idx+1] & (uint8_t)(mask>>8)) << 8);
                }
            };

            template <typename T, uint8_t Offset, uint8_t Bitsize>
            inline void set(T field){
                //check consistency
                static_assert(std::numeric_limits<T>::digits <= 16, "Can't get data larger than uint16_t");
                static_assert(Bitsize <= 16, "Bit size is bigger than 16");
                static_assert(Bitsize > 0, "Bit size can't be zero");

                static_assert(std::numeric_limits<T>::digits >= Bitsize, "the return type can't handle Bitsize");
                static_assert((Offset + Bitsize) <= 8 * Size, "Bitsize+Offset is out of range");

                static_assert((Bitsize <= 8) or ((Bitsize > 8) and (Offset & 0x3) == 0), "Offset must be a multiple of 8 for >8 bit bitfield");

                // for <= 8bits:
                if(Bitsize <= 8){
                    // on one byte
                    if((Offset & 0x7) + Bitsize <= 8){
                        uint8_t mask = (1<<Bitsize) - 1;
                        uint8_t byte_idx = Offset >> 3;
                        uint8_t byte_shift = Offset & 0x7;
                        data[byte_idx] &= ~(mask << byte_shift);
                        data[byte_idx] |= (field & mask) << byte_shift;
                        return;
                    }

                    else{
                        uint8_t mask = (1<<Bitsize) - 1;
                        uint8_t byte_idx = Offset >> 3;
                        uint8_t lsb_byte_shift = Offset & 0x7;
                        uint8_t msb_byte_shift = 8 - lsb_byte_shift;
                        uint8_t mask_lsb = mask & ( (1 << msb_byte_shift) - 1);
                        uint8_t mask_msb = mask >> msb_byte_shift;

                        // lsb
                        data[byte_idx] &= ~( mask_lsb << lsb_byte_shift );
                        data[byte_idx] |= (field & mask_lsb) << lsb_byte_shift;
                        // msb
                        data[byte_idx+1] &= ~mask_msb;
                        data[byte_idx+1] |= (field >> msb_byte_shift) & mask_msb;
                        return; // lsb_part | (msb_part << (8 - byte_shift));
                    }
                }

                // for >8bits
                else{
                    uint16_t mask = (1<<Bitsize) - 1;
                    uint8_t byte_idx = Offset >> 3;

                    data[byte_idx] &= ~(mask & 0xFF);
                    data[byte_idx] |= (field & 0xFF);

                    data[byte_idx+1] &= ~(mask >> 8);
                    data[byte_idx+1] |= (field >> 8);
                }
            };

            uint8_t data[Size];
    };

    template <uint8_t MsgSize>
    class Frame{
        public:
            static const uint8_t kSOF = 0xAA;
            static const uint8_t FrameSize = MsgSize + 2;

            uint8_t SOF;
            Message<MsgSize> message;
            uint8_t checksum;

    };


    template <uint8_t MsgSize>
    class Parser{
        public:
            using Message_T = Message<MsgSize>;
            using Frame_T = Frame<MsgSize>;

            enum Status{
                eComplete = 0,
                eNotComplete,
                eError
            };

            Parser(): state(idle), status(eNotComplete){}


            Status parse(uint8_t in_byte, Message_T *out_msg){
                switch(state){
                    case idle:
                        // reset the state machine
                        buff_ptr = 0;

                        if(in_byte == Frame_T::kSOF){
                            // first byte is valid;
                            status = eNotComplete;
                            state = busy;

                            buffer[buff_ptr++] = in_byte;
                        }
                        else{
                            status = eError;
                        }

                        break;

                    case busy:
                        buffer[buff_ptr++] = in_byte;

                        // handle the last data byte
                        if(buff_ptr == Frame_T::FrameSize){
                            if(isCheckSumValid()){
                                status = eComplete;
                                std::memcpy(out_msg->data, buffer+1, MsgSize);
                            }

                            else{
                                status = eError;
                            }

                            state = idle; //ready to retrieve new messages
                        }
                        /*else{
                            status = eNotComplete;
                        }*/
                        break;
                }

                return status;
            }


            static Frame_T encode(const Message_T &in_msg){
                Frame_T frame;
                frame.SOF = Frame_T::kSOF;
                frame.message = in_msg;
                frame.checksum = std::accumulate(std::begin(in_msg.data), std::end(in_msg.data), Frame_T::kSOF);

                return frame;
            }

        protected:
            bool isCheckSumValid(){
                uint8_t cs = std::accumulate(std::begin(buffer), std::end(buffer)-1, 0);
                return cs == buffer[Frame_T::FrameSize-1];
            }

        private:
            enum State{
                idle = 0,
                busy
            };

            State state;
            Status status;

            uint8_t buffer[Frame_T::FrameSize];
            uint8_t buff_ptr;
    };



    template <typename Implementation, typename Router, typename MsgType>
    class MsgProcessor: public Router{
        using TParser = microparcel::Parser<MsgType::kSize>;
        using TFrame = typename TParser::Frame_T;

        public:
            void send(const MsgType &inMsg){
                TFrame frame = mParser.encode(inMsg);

                Implementation& underlying = static_cast<Implementation&>(*this);
                underlying.sendFrame(frame);
            }

            // protected void Implementation::sendFrame(const TFrame &inFrame);

            void parse(uint8_t inByte){
                typename TParser::Status status = mParser.parse(inByte, &mMsgRecv);
                if(status == TParser::eComplete){
                    this->process(mMsgRecv);
                }
            }

        private:
            TParser mParser;
            MsgType mMsgRecv;
    };
};

#endif //MICROPARCEL_H