void HostCommunication::packetSent(int index, const Message &m) { const Component *src = m.sender; MicroFlo::PortId srcPort = m.senderPort; if (!src) { return; } uint8_t cmd[MICROFLO_CMD_SIZE] = { GraphCmdPacketSent, src->id(), (uint8_t)srcPort, m.target->id(), (uint8_t)m.targetPort, (uint8_t)m.pkg.type(), 0, 0 }; if (m.pkg.isData()) { if (m.pkg.isBool()) { cmd[6] = m.pkg.asBool(); } else if (m.pkg.isNumber()){ // FIXME: truncates const int i = m.pkg.asInteger(); cmd[6] = i>>0; cmd[7] = i>>8; } else if (m.pkg.isVoid()) { // Nothing needs doing } else { // FIXME: support all types MICROFLO_DEBUG(this, DebugLevelError, DebugNotImplemented); } }
void HostCommunication::setup(Network *net, HostTransport *t) { network = net; transport = t; MICROFLO_DEBUG(this, DebugLevelInfo, DebugProgramStart); network->setNotificationHandler(this); }
void Network::connectSubgraph(bool isOutput, MicroFlo::NodeId subgraphNode, MicroFlo::PortId subgraphPort, MicroFlo::NodeId childNode, MicroFlo::PortId childPort) { #ifdef MICROFLO_ENABLE_SUBGRAPHS MICROFLO_RETURN_IF_FAIL(MICROFLO_VALID_NODEID(subgraphNode) && MICROFLO_VALID_NODEID(childNode), notificationHandler, DebugLevelError, DebugSubGraphConnectInvalidNodes); Component *comp = nodes[subgraphNode]; Component *child = nodes[childNode]; MICROFLO_ASSERT(comp->component() == MicroFlo::IdSubGraph && child->parentNodeId >= Network::firstNodeId, notificationHandler, DebugLevelError, DebugSubGraphConnectNotASubgraph); SubGraph *subgraph = (SubGraph *)comp; if (isOutput) { subgraph->connectOutport(subgraphPort, child, childPort); } else { subgraph->connectInport(subgraphPort, child, childPort); } if (notificationHandler) { notificationHandler->subgraphConnected(isOutput, subgraphNode, subgraphPort, childNode, childPort); } #else MICROFLO_DEBUG(this, DebugLevelError, DebugNotSupported); #endif }
virtual void PinSetPullup(MicroFlo::PinId pin, IO::PullupMode mode) { if (mode == IO::PullNone) { } else { MICROFLO_DEBUG(debug, DebugLevelError, DebugIoOperationNotImplemented); } }
// Timer virtual long TimerCurrentMs() { timespec current_time; if (clock_gettime(CLOCK_MONOTONIC, ¤t_time) != 0) { MICROFLO_DEBUG(debug, DebugLevelError, DebugIoFailure); } timespec since_start = timespec_diff(start_time, current_time); return (since_start.tv_sec*1000)+(since_start.tv_nsec/1000000); }
virtual void PinSetPullup(MicroFlo::PinId pin, IO::PullupMode mode) { if (mode == IO::PullNone) { digitalWrite(pin, LOW); } else if (mode == IO::PullUp) { digitalWrite(pin, HIGH); } else { MICROFLO_DEBUG(debug, DebugLevelError, DebugIoOperationNotImplemented); } }
// Assumes GPIO is set up as input bool gpio_read(int number){ std::string path = SYS_GPIO_BASE + "gpio" + std::to_string(number) + "/value"; std::string res = read_sys_file(path); if (res.empty()) { MICROFLO_DEBUG(debug, DebugLevelError, DebugIoFailure); } res = rtrim(res); return res == "1"; }
// Pin config virtual void PinSetMode(MicroFlo::PinId pin, IO::PinMode mode) { if (!write_sys_file(SYS_GPIO_BASE+"export", std::to_string(pin))) { MICROFLO_DEBUG(debug, DebugLevelError, DebugIoFailure); return; } if (mode == IO::InputPin) { if (!write_sys_file(SYS_GPIO_BASE+"gpio"+std::to_string(pin)+"/direction", "in")) { MICROFLO_DEBUG(debug, DebugLevelError, DebugIoFailure); } } else if (mode == IO::OutputPin) { if (write_sys_file(SYS_GPIO_BASE+"gpio"+std::to_string(pin)+"/direction", "out")) { MICROFLO_DEBUG(debug, DebugLevelError, DebugIoFailure); } } else { MICROFLO_DEBUG(debug, DebugLevelError, DebugIoOperationNotImplemented); } }
void HostCommunication::parseByte(char b) { buffer[currentByte++] = b; if (state == ParseHeader) { MICROFLO_DEBUG(this, DebugLevelVeryDetailed, DebugParseHeader); if (currentByte == sizeof(MICROFLO_GRAPH_MAGIC)) { if (memcmp(buffer, MICROFLO_GRAPH_MAGIC, sizeof(MICROFLO_GRAPH_MAGIC)) == 0) { MICROFLO_DEBUG(this, DebugLevelDetailed, DebugMagicMatched); const uint8_t cmd[] = { GraphCmdCommunicationOpen }; transport->sendCommand(cmd, sizeof(cmd)); state = ParseCmd; } else { MICROFLO_DEBUG(this, DebugLevelError, DebugMagicMismatch); state = Invalid; } currentByte = 0; } } else if (state == ParseCmd) { MICROFLO_DEBUG(this, DebugLevelVeryDetailed, DebugParseCommand); if (currentByte == MICROFLO_CMD_SIZE) { if (memcmp(buffer, MICROFLO_GRAPH_MAGIC, sizeof(MICROFLO_GRAPH_MAGIC)) == 0) { MICROFLO_DEBUG(this, DebugLevelDetailed, DebugMagicMatched); const uint8_t cmd[] = { GraphCmdCommunicationOpen }; transport->sendCommand(cmd, sizeof(cmd)); // already in ParseCmd state } else { parseCmd(); } currentByte = 0; } } else if (state == LookForHeader) { MICROFLO_DEBUG(this, DebugLevelVeryDetailed, DebugParseLookForHeader); if (b == MICROFLO_GRAPH_MAGIC[0]) { state = ParseHeader; buffer[0] = b; currentByte = 1; } else { currentByte = 0; } } else if (state == Invalid) { MICROFLO_DEBUG(this, DebugLevelError, DebugParserInvalidState); // try to recover currentByte = 0; state = LookForHeader; } else { MICROFLO_DEBUG(this, DebugLevelError,DebugParserUnknownState); // try to recover currentByte = 0; state = LookForHeader; } }
// Pin config virtual void PinSetMode(MicroFlo::PinId pin, IO::PinMode mode) { MAP_SysCtlPeripheralEnable(peripheral(pin)); if (mode == IO::InputPin) { MAP_GPIOPinTypeGPIOInput(portBase(pin), pinMask(pin)); } else if (mode == IO::OutputPin) { MAP_GPIOPinTypeGPIOOutput(portBase(pin), pinMask(pin)); } else { MICROFLO_DEBUG(debug, DebugLevelError, DebugIoOperationNotImplemented); } }
virtual void setIoValue(const uint8_t *buf, uint8_t len) { IoType type = (IoType)buf[1]; if (type == IoTypeAnalog) { // TEMP: implement MICROFLO_DEBUG(debug, DebugLevelError, DebugUnknownIoType); } else if (type == IoTypeDigital) { const uint8_t pin = buf[2]; const bool val = buf[3]; if (pin < DIGITAL_PINS) { digitalInputs[pin] = val; const uint8_t b[] = { GraphCmdSetIoValueCompleted, buf[1], buf[2], buf[3], buf[4] }; transport->sendCommand(b, sizeof(b)); } else { MICROFLO_DEBUG(debug, DebugLevelError, DebugIoInvalidValueSet); } } else if (type == IoTypeTimeMs) { // Perhaps also support incrementing? timeMs = readLong(buf+2); const uint8_t b[] = { GraphCmdSetIoValueCompleted, buf[1], buf[2], buf[3], buf[4] }; transport->sendCommand(b, sizeof(b)); } else { MICROFLO_DEBUG(debug, DebugLevelError, DebugUnknownIoType); } }
LinuxIO() { if (clock_gettime(CLOCK_MONOTONIC, &start_time) != 0) { MICROFLO_DEBUG(debug, DebugLevelError, DebugIoFailure); } }
// Assumes GPIO is set up as output void gpio_write(int number, bool value){ std::string path = SYS_GPIO_BASE + "gpio" + std::to_string(number) + "/value"; if (!write_sys_file(path, value ? "1" : "0")) { MICROFLO_DEBUG(debug, DebugLevelError, DebugIoFailure); } }
virtual void AttachExternalInterrupt(uint8_t interrupt, IO::Interrupt::Mode mode, IOInterruptFunction func, void *user) { MICROFLO_DEBUG(debug, DebugLevelError, DebugIoOperationNotImplemented); }
// Timer virtual long TimerCurrentMs() { MICROFLO_DEBUG(debug, DebugLevelError, DebugIoOperationNotImplemented); return 0; }
virtual void PwmWrite(MicroFlo::PinId pin, long dutyPercent) { MICROFLO_DEBUG(debug, DebugLevelError, DebugIoOperationNotImplemented); }
// Digital virtual void DigitalWrite(MicroFlo::PinId pin, bool val) { MICROFLO_DEBUG(debug, DebugLevelError, DebugIoOperationNotImplemented); }
virtual unsigned char SerialRead(uint8_t serialDevice) { MICROFLO_DEBUG(debug, DebugLevelError, DebugIoOperationNotImplemented); return '\0'; }
// Serial // TODO: support serial virtual void SerialBegin(uint8_t serialDevice, int baudrate) { MICROFLO_DEBUG(debug, DebugLevelError, DebugIoOperationNotImplemented); }
void HostCommunication::parseCmd() { GraphCmd cmd = (GraphCmd)buffer[0]; if (cmd == GraphCmdEnd) { MICROFLO_DEBUG(this, DebugLevelDetailed, DebugEndOfTransmission); const uint8_t cmd[] = { GraphCmdTransmissionEnded }; transport->sendCommand(cmd, sizeof(cmd)); state = LookForHeader; } else if (cmd == GraphCmdReset) { network->reset(); } else if (cmd == GraphCmdStartNetwork) { network->start(); } else if (cmd == GraphCmdCreateComponent) { MICROFLO_DEBUG(this, DebugLevelDetailed, DebugComponentCreateStart); Component *c = createComponent((MicroFlo::ComponentId)buffer[1]); MICROFLO_DEBUG(this, DebugLevelDetailed, DebugComponentCreateEnd); network->addNode(c, buffer[2]); } else if (cmd == GraphCmdConnectNodes) { MICROFLO_DEBUG(this, DebugLevelDetailed, DebugConnectNodesStart); network->connect(buffer[1], buffer[3], buffer[2], buffer[4]); } else if (cmd == GraphCmdSendPacket) { const Msg packetType = (Msg)buffer[3]; Packet p; if (packetType == MsgBracketStart || packetType == MsgBracketEnd || packetType == MsgVoid) { p = Packet(packetType); } else if (packetType == MsgInteger) { const long val = buffer[4] + ((long)(buffer[5])<<8) + ((long)(buffer[6])<<16) + ((long)(buffer[7])<<24); p = Packet(val); } else if (packetType == MsgByte) { p = Packet(buffer[4]); } else if (packetType == MsgBoolean) { p = Packet(!(buffer[4] == 0)); } if (p.isValid()) { network->sendMessageId(buffer[1], buffer[2], p); const uint8_t cmd[] = { GraphCmdSendPacketDone, buffer[1], buffer[2], (uint8_t)packetType }; transport->sendCommand(cmd, sizeof(cmd)); } else { MICROFLO_DEBUG(this, DebugLevelError, DebugParserUnknownPacketType); } } else if (cmd == GraphCmdConfigureDebug) { network->setDebugLevel((DebugLevel)buffer[1]); } else if (cmd == GraphCmdSubscribeToPort) { network->subscribeToPort(buffer[1], buffer[2], (bool)buffer[3]); } else if (cmd == GraphCmdConnectSubgraphPort) { #ifdef MICROFLO_ENABLE_SUBGRAPHS // FIXME: validate const bool isOutput = (unsigned int)buffer[1]; const int subgraphNode = (unsigned int)buffer[2]; const int subgraphPort = (unsigned int)buffer[3]; const int childNode = (unsigned int)buffer[4]; const int childPort = (unsigned int)buffer[5]; network->connectSubgraph(isOutput, subgraphNode, subgraphPort, childNode, childPort); #else MICROFLO_DEBUG(this, DebugLevelError, DebugNotSupported); #endif } else if (cmd == GraphCmdPing) { const uint8_t cmd[] = { GraphCmdPong, cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6], cmd[7] }; transport->sendCommand(cmd, sizeof(cmd)); } else if (cmd == GraphCmdSetIoValue) { network->setIoValue(buffer, MICROFLO_CMD_SIZE); } else if (cmd >= GraphCmdInvalid) { MICROFLO_ASSERT(memcmp(buffer, MICROFLO_GRAPH_MAGIC, sizeof(MICROFLO_GRAPH_MAGIC)) == 0, this, DebugLevelError, DebugParserInvalidCommand); } else { MICROFLO_DEBUG(this, DebugLevelError, DebugParserUnknownCommand); } }
virtual long SerialDataAvailable(uint8_t serialDevice) { MICROFLO_DEBUG(debug, DebugLevelError, DebugIoOperationNotImplemented); return 0; }
virtual bool DigitalRead(MicroFlo::PinId pin) { MICROFLO_DEBUG(debug, DebugLevelError, DebugIoOperationNotImplemented); return false; }
virtual void SerialWrite(uint8_t serialDevice, unsigned char b) { MICROFLO_DEBUG(debug, DebugLevelError, DebugIoOperationNotImplemented); }
virtual void PinSetPullup(MicroFlo::PinId pin, IO::PullupMode mode) { // TODO: support pullup/pulldown config on common boards like RPi // Not exposed in sysfs, need to prod registers directly. // http://elinux.org/RPi_Low-level_peripherals#GPIO_Pull_Up.2FPull_Down_Register_Example MICROFLO_DEBUG(debug, DebugLevelError, DebugIoOperationNotImplemented); }
// Analog virtual long AnalogRead(MicroFlo::PinId pin) { MICROFLO_DEBUG(debug, DebugLevelError, DebugIoOperationNotImplemented); return 0; }
// Pin config virtual void PinSetMode(MicroFlo::PinId pin, IO::PinMode mode) { MICROFLO_DEBUG(debug, DebugLevelError, DebugIoOperationNotImplemented); }