Program Listing for File cuecontroller.cpp

Return to documentation for file (src/cue/cuecontroller.cpp)

/*
 * CueController - Class for converting PixelMaestro commands to and from serialized strings.
 */

#include "cuecontroller.h"
#include "animationcuehandler.h"
#include "canvascuehandler.h"
#include "maestrocuehandler.h"
#include "sectioncuehandler.h"
#include "showcuehandler.h"

namespace PixelMaestro {

    CueController::CueController(Maestro& maestro, uint32_t buffer_size) : maestro_(maestro) {
        this->buffer_size_ = buffer_size;
        this->buffer_ = new uint8_t[buffer_size] {0};
    }

    uint8_t* CueController::assemble(uint32_t payload_size) {
        /*
         * Final Cue has the following form: [ID] [Checksum] [Size] [Payload]
         *
         * [ID] is a set of pre-defined characters marking the start of a Cue.
         * [Size] is the size of the payload.
         * [Checksum] is a value generated for error detection.
         * [Payload] contains the actual command with parameters.
         */

        for (uint8_t i = 0; i < (uint8_t)Byte::ChecksumByte; i++) {
            buffer_[i] = id_[i];
        }

        IntByteConvert size(payload_size);
        buffer_[(uint8_t)Byte::SizeByte1] = size.converted_0;
        buffer_[(uint8_t)Byte::SizeByte2] = size.converted_1;
        buffer_[(uint8_t)Byte::SizeByte3] = size.converted_2;
        buffer_[(uint8_t)Byte::SizeByte4] = size.converted_3;

        buffer_[(uint8_t)Byte::ChecksumByte] = checksum(buffer_, payload_size);

        return buffer_;
    }

    uint8_t CueController::checksum(const uint8_t* cue, uint32_t cue_size) {
        uint32_t sum = 0;
        for (uint32_t i = 0; i < cue_size; i++) {

            // Make sure we don't include the checksum in its own calculation
            if (i != (uint8_t)Byte::ChecksumByte) {
                sum += cue[i];
            }
        }

        return (sum % 256);
    }

    CueHandler& CueController::enable_animation_cue_handler() {
        uint8_t handler = (uint8_t)Handler::AnimationCueHandler;
        if (handlers_[handler] == nullptr) {
            handlers_[handler] = new AnimationCueHandler(*this);
        }
        return *handlers_[handler];
    }

    CueHandler& CueController::enable_canvas_cue_handler() {
        uint8_t handler = (uint8_t)Handler::CanvasCueHandler;
        if (handlers_[handler] == nullptr) {
            handlers_[handler] = new CanvasCueHandler(*this);
        }
        return *handlers_[handler];
    }

    CueHandler& CueController::enable_maestro_cue_handler() {
        uint8_t handler = (uint8_t)Handler::MaestroCueHandler;
        if (handlers_[handler] == nullptr) {
            handlers_[handler] = new MaestroCueHandler(*this);
        }
        return *handlers_[handler];
    }

    CueHandler& CueController::enable_section_cue_handler() {
        uint8_t handler = (uint8_t)Handler::SectionCueHandler;
        if (handlers_[handler] == nullptr) {
            handlers_[handler] = new SectionCueHandler(*this);
        }
        return *handlers_[handler];
    }

    CueHandler& CueController::enable_show_cue_handler() {
        uint8_t handler = (uint8_t)Handler::ShowCueHandler;
        if (handlers_[handler] == nullptr) {
            handlers_[handler] = new ShowCueHandler(*this);
        }
        return *handlers_[handler];
    }

    uint8_t* CueController::get_buffer() const {
        return buffer_;
    }

    uint32_t CueController::get_cue_size() const {
        return IntByteConvert::byte_to_uint32(&buffer_[(uint8_t)CueController::Byte::SizeByte1]);
    }

    uint32_t CueController::get_cue_size(uint8_t *cue) const {
        return (IntByteConvert::byte_to_uint32(&cue[(uint8_t)CueController::Byte::SizeByte1]));
    }

    CueHandler* CueController::get_handler(Handler handler) const {
        return handlers_[(uint8_t)handler];
    }

    Maestro& CueController::get_maestro() const {
        return maestro_;
    }

    bool CueController::is_blocked(const uint8_t *cue) const {
        for (uint8_t i = 0; i < num_blocked_cues_; i++) {
            BlockedCue& block = blocked_cues_[i];
            if ((cue[(uint8_t)Byte::PayloadByte] == (uint8_t)block.handler) &&
                (cue[(uint8_t)Byte::PayloadByte + 1] == block.action)) {
                return true;
            }
        }
        return false;
    }

    bool CueController::read(uint8_t byte) {
        buffer_[read_index_] = byte;
        read_index_++;

        /*
         * Check to see if we should reset the read index in order to make room for the Cue.
         */
        if (read_index_ > (uint8_t)Byte::IDByte3) {

            /*
             * First, make sure this isn't a list of Show Events.
             * If it is, the index will reset while reading an Event, which is a problem.
             */
            if (!(buffer_[(uint8_t)Byte::PayloadByte] == (uint8_t)Handler::ShowCueHandler && buffer_[(uint8_t)ShowCueHandler::Byte::ActionByte] == (uint8_t)ShowCueHandler::Action::SetEvents)) {

                /*
                 * Next, move the read index to the start of the buffer and write the ID.
                 * Then, start reading after the last ID byte.
                 */
                if (buffer_[read_index_ - (uint8_t)Byte::IDByte3] == id_[(uint8_t)Byte::IDByte1] &&
                    buffer_[read_index_ - (uint8_t)Byte::IDByte2] == id_[(uint8_t)Byte::IDByte2] &&
                    buffer_[read_index_] == id_[(uint8_t)Byte::IDByte3]) {

                    buffer_[(uint8_t)Byte::IDByte1] = id_[(uint8_t)Byte::IDByte1];
                    buffer_[(uint8_t)Byte::IDByte2] = id_[(uint8_t)Byte::IDByte2];
                    buffer_[(uint8_t)Byte::IDByte3] = id_[(uint8_t)Byte::IDByte3];
                    read_index_ = (uint8_t)Byte::IDByte3 + 1;
                    return false;
                }
            }
        }

        /*
         * Check the size of the buffered Cue.
         * If it's valid, we know how far to read the Cue.
         * After reaching the end, run the Cue.
         */
        if (read_index_ >= (uint8_t)Byte::SizeByte4) {
            uint32_t buffered_cue_size = IntByteConvert::byte_to_uint32(&buffer_[(uint8_t)Byte::SizeByte1]);
            if (buffered_cue_size > 0 && read_index_ >= buffered_cue_size) {
                run(buffer_);
                read_index_ = 0;
                return true;
            }
        }

        return false;
    }

    void CueController::run() {
        if (handlers_[buffer_[(uint8_t)Byte::PayloadByte]] != nullptr && !is_blocked(buffer_)) {
            handlers_[buffer_[(uint8_t)Byte::PayloadByte]]->run(buffer_);
        }
    }

    void CueController::run(uint8_t *cue) {
        if (handlers_[cue[(uint8_t)Byte::PayloadByte]] != nullptr && !is_blocked(cue) && validate_header(cue)) {
            handlers_[cue[(uint8_t)Byte::PayloadByte]]->run(cue);
        }
    }

    void CueController::set_blocked_cues(BlockedCue *blocks, uint8_t num_blocks) {
        this->blocked_cues_ = blocks;
        this->num_blocked_cues_ = num_blocks;
    }

    bool CueController::validate_header(uint8_t *cue) {
        // Check the ID
        for (uint8_t i = 0; i < (uint8_t)Byte::ChecksumByte; i++) {
            if (cue[i] != id_[i]) {
                return false;
            }
        }

        // Validate the Checksum
        uint32_t size = IntByteConvert::byte_to_uint32(&cue[(uint8_t)Byte::SizeByte1]);
        return (cue[(uint8_t)Byte::ChecksumByte] == checksum(cue, size));
    }

    CueController::~CueController() {
        delete [] buffer_;
        for (uint8_t i = 0; i < 5; i++) {
            delete handlers_[i];
        }
    }
}