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:
- All Observers observe packets regardless of origin.
- Origin of packets from the Pico’s perspective includes packets received from the car, packets received from the rpi. The Pico can also generate packet for writing (to either UART), but these are not received by observers on the Pico.
- A two-way firewall can be implemented, following the pattern of the knob-click. The Pico can have a white-list or black-list of messages that the Pi is allowed to send to the vehicle network, preventing security risks such as the pi sending a door unlock command through a remote hack.