Return to Home Page

Output Writers

All of the output writers are structured in an “API surface” of classes with public methods on them.

They all inherit from BaseOutputWriter which allows subclasses to schedule outgoing packets with the DmaManager, as well as centralize the PicoToPi serialization logic.

namespace pico {
    namespace ibus {
        namespace output {
            namespace writer {

                class BaseOutputWriter {

                protected:

                    virtual std::string getTag() = 0;

                    virtual std::shared_ptr<dma::IDmaManager> getDmaManager() = 0;

                    //Puts the packet on a queue somewhere for writing.
                    //or, just blocks till it's done writing?
                    void schedulePacketForWrite(pico::ibus::data::IbusPacket ibusPacket);

                    void schedulePicoToPiMessageForWrite(messages::PicoToPiMessage message);
                };

            } // pico
        } // ibus
    } // output
} // writer

Observers and Writers working together

A good example of an Observer and a Writer working together is the UartForwarderWriter and the UartForwarderObserver.

The UartForwarderWriter is straight-forward:

namespace pico::ibus::output::writer {
    UartForwarderWriter::UartForwarderWriter(
            std::shared_ptr<logger::BaseLogger> logger,
            std::shared_ptr<dma::IDmaManager> dmaManager
            ) {
        this->logger = logger;
        this->dmaManager = dmaManager;
    }

    std::shared_ptr<dma::IDmaManager> UartForwarderWriter::getDmaManager() {
        return dmaManager;
    }

    void UartForwarderWriter::forwardPacketToCar(data::IbusPacket packet) {
        getDmaManager()->cpu0scheduleOutgoingMessage(packet);
    }

    void UartForwarderWriter::forwardPacketToPi(data::IbusPacket packet) {
        getDmaManager()->cpu0scheduleOutgoingProbeOnlyMessage(packet);
    }

} // writer

The UartForwarderObserver has some more logic:

namespace pico::ibus::observers {
    UartForwarderObserver::UartForwarderObserver(
            std::shared_ptr<pico::logger::BaseLogger> logger,
            std::shared_ptr<pico::ibus::topology::BusTopologyManager> busTopologyManager,
            std::shared_ptr<pico::ibus::output::writer::UartForwarderWriter> writer,
            std::shared_ptr<pico::hardware::videoSwitch::VideoSwitch> videoSwitch) {
        this->logger = logger;
        this->busTopologyManager = busTopologyManager;
        this->writer = writer;
        this->videoSwitch = videoSwitch;
    }

    void UartForwarderObserver::onNewPacket(std::shared_ptr<pico::ibus::data::IbusPacket> iBusPacket) {
        if (busTopologyManager->getBusToplogy() == topology::BusTopology::SLED_NO_PI) {
            return;
        }

        switch (iBusPacket->getPacketSource()) {
            case data::NOT_SET:
                logger->wtf(getTag(), "We observed a packet that did not have a source, not forwarding");
                logger->wtf(getTag(), iBusPacket->toString());
                break;
            case data::FROM_CAR:
                if (shouldForwardPacketToCar(iBusPacket)) {
                    writer->forwardPacketToPi(*iBusPacket);
                }
                break;
            case data::FROM_PI:
                writer->forwardPacketToCar(*iBusPacket);
                break;
        }
    }

    bool UartForwarderObserver::shouldForwardPacketToCar(std::shared_ptr<pico::ibus::data::IbusPacket> iBusPacket) {
        //Don't forward the packet if it's a knob turn, knob click, and the video source != rpi.
        bool isKnobTurnOrClick = false;
        if (iBusPacket->getSourceDevice() == data::IbusDeviceEnum::BOARDMONITOR_BUTTONS &&
            iBusPacket->getDestinationDevice() == data::NAV_VIDEOMODULE) {

            if (iBusPacket->getDataLength() >= 2) {
                //PushData = 0x48, 0x05
                if (iBusPacket->getRawPacket()[data::IbusPacket::DATA_START + 0] == 0x48) {
                    if (iBusPacket->getRawPacket()[data::IbusPacket::DATA_START + 1] == 0x05) {
                        isKnobTurnOrClick = true;
                    }
                }
                if (iBusPacket->getRawPacket()[data::IbusPacket::DATA_START + 0] == 0x49) {
                    uint8_t rotation = iBusPacket->getRawPacket()[data::IbusPacket::DATA_START + 1];
                    if (rotation - 0x0 >= 1 && rotation - 0x00 <= 9) {
                        isKnobTurnOrClick = true;
                    }

                    if (rotation - 0x80 >= 1 && rotation - 0x80 <= 9) {
                        isKnobTurnOrClick = true;
                    }
                }
            }
        }

        if (isKnobTurnOrClick && videoSwitch->getPreviousVideoSource() != hardware::videoSwitch::VideoSource::PI) {
            return false;
        }

        return true;
    }
} // observers

This highlights the following properties of the Observer archiecture:

Return to Top