void PlayDrillCommandClass::LogSteps() const {
    const Step* pStep= NULL;
    
    int index= 1;
    
    for (int i= 0; i < m_Steps.getUsedElems(); ++i)
    {
        pStep= m_Steps[i];
        LOGF3("Step [", i+1, "]");
        pStep->log(F(""));
        
    }
}
void PlayDrillCommandClass::bumperOff()
{
	LOGF("bumperOff");
	
#if USE_RF24
	for (int id= RF24_BUMPER_ID_BASE; id < RF24NodeId(RF24_BUMPER_ID_BASE + MAX_BUMPER); ++ id)
	{
		if (g_Rf24Master.isDeviceConnected( RF24NodeId(id) ))
		{
			LOGF3("Off(", id, ")");
			g_Rf24Master.sendOff(RF24NodeId(id));
		}
	}
#endif
}
void PlayDrillCommandClass::bumperOn(bool resetCounter)
{
	LOGF("bumperOn");
	
#if USE_RF24
	
	// TODO: improve communication with bumper
	for (int id= RF24_BUMPER_ID_BASE; id < RF24NodeId(RF24_BUMPER_ID_BASE + MAX_BUMPER); ++ id)
	{
		if (g_Rf24Master.isDeviceConnected( RF24NodeId(id) ))
		{
			LOGF3("On(", id, ")");
			g_Rf24Master.sendOn(RF24NodeId(id), resetCounter);
		}	
	}
#endif
}
void KineticCountingSemaphore_Give(KineticCountingSemaphore * const sem) // SIGNAL
{
    KINETIC_ASSERT(sem != NULL);
    pthread_mutex_lock(&sem->mutex);

    if (sem->count == 0 && sem->num_waiting > 0) {
        pthread_cond_signal(&sem->available);
    }

    uint32_t before = sem->count++;
    uint32_t after = sem->count;
    uint32_t waiting = sem->num_waiting;

    pthread_mutex_unlock(&sem->mutex);

    LOGF3("Concurrent ops throttle -- GIVE: %u => %u (waiting=%u)", before, after, waiting);
    KINETIC_ASSERT(sem->max >= after);
}
Beispiel #5
0
KineticStatus bus_to_kinetic_status(bus_send_status_t const status)
{
    KineticStatus res = KINETIC_STATUS_INVALID;

    switch(status) {
    // TODO scrutinize all these mappings
    case BUS_SEND_SUCCESS:
        res = KINETIC_STATUS_SUCCESS;
        break;
    case BUS_SEND_TX_TIMEOUT_NOTIFYING_LISTENER:
    case BUS_SEND_TX_TIMEOUT:
        res = KINETIC_STATUS_SOCKET_TIMEOUT;
        break;
    case BUS_SEND_TX_FAILURE:
        res = KINETIC_STATUS_SOCKET_ERROR;
        break;
    case BUS_SEND_RX_TIMEOUT:
        res = KINETIC_STATUS_OPERATION_TIMEDOUT;
        break;
    case BUS_SEND_RX_FAILURE:
        res = KINETIC_STATUS_SOCKET_ERROR;
        break;
    case BUS_SEND_BAD_RESPONSE:
        res = KINETIC_STATUS_SOCKET_ERROR;
        break;
    case BUS_SEND_UNREGISTERED_SOCKET:
        res = KINETIC_STATUS_SOCKET_ERROR;
        break;
    case BUS_SEND_RX_TIMEOUT_EXPECT:
        res = KINETIC_STATUS_OPERATION_TIMEDOUT;
        break;
    case BUS_SEND_UNDEFINED:
    default:
    {
        LOGF0("bus_to_kinetic_status: UNMATCHED %d", status);
        KINETIC_ASSERT(false);
        return KINETIC_STATUS_INVALID;
    }
    }

    LOGF3("bus_to_kinetic_status: mapping status %d => %d",
          status, res);
    return res;
}
void KineticCountingSemaphore_Take(KineticCountingSemaphore * const sem) // WAIT
{
    KINETIC_ASSERT(sem != NULL);
    pthread_mutex_lock(&sem->mutex);

    sem->num_waiting++;

    while (sem->count == 0) {
        pthread_cond_wait(&sem->available, &sem->mutex);
    }

    sem->num_waiting--;

    uint32_t before = sem->count--;
    uint32_t after = sem->count;
    uint32_t waiting = sem->num_waiting;

    pthread_mutex_unlock(&sem->mutex);

    LOGF3("Concurrent ops throttle -- TAKE: %u => %u (waiting=%u)", before, after, waiting);
}
KineticStatus KineticSocket_Write(int socket, ByteBuffer* src)
{
    LOGF3("Writing %zu bytes to socket...", src->bytesUsed);
    for (unsigned int bytesSent = 0; bytesSent < src->bytesUsed;) {
        int bytesRemaining = src->bytesUsed - bytesSent;
        int status = write(socket, &src->array.data[bytesSent], bytesRemaining);
        if (status == -1 &&
            ((errno == EINTR) || (errno == EAGAIN) || (errno == EWOULDBLOCK))) {
            LOG2("Write interrupted. retrying...");
            continue;
        }
        else if (status <= 0) {
            LOGF0("Failed to write to socket! status=%d, errno=%d\n", status, errno);
            return KINETIC_STATUS_SOCKET_ERROR;
        }
        else {
            bytesSent += status;
            LOGF2("Wrote %d bytes (%d of %zu sent)", status, bytesSent, src->bytesUsed);
        }
    }
    LOG3("Socket write completed successfully");
    return KINETIC_STATUS_SUCCESS;
}
int KineticSocket_Connect(const char* host, int port, bool nonBlocking)
{
    char port_str[32];
    struct addrinfo hints;
    struct addrinfo* ai_result = NULL;
    struct addrinfo* ai = NULL;
    socket99_result result;

    // Setup server address info
    socket99_config cfg = {
        .host = (char*)host,
        .port = port,
        .nonblocking = nonBlocking,
    };
    sprintf(port_str, "%d", port);

    // Open socket
    LOGF0("Connecting to %s:%d", host, port);
    if (!socket99_open(&cfg, &result)) {
        LOGF0("Failed to open socket connection with host: status %d, errno %d",
             result.status, result.saved_errno);
        return KINETIC_SOCKET_DESCRIPTOR_INVALID;
    }

    // Configure the socket
    socket99_set_hints(&cfg, &hints);
    if (getaddrinfo(cfg.host, port_str, &hints, &ai_result) != 0) {
        LOGF0("Failed to get socket address info: errno %d", errno);
        close(result.fd);
        return KINETIC_SOCKET_DESCRIPTOR_INVALID;
    }

    for (ai = ai_result; ai != NULL; ai = ai->ai_next) {
        int setsockopt_result;
        int buffer_size = KINETIC_OBJ_SIZE;

#if defined(SO_NOSIGPIPE) && !defined(__APPLE__)
        // On BSD-like systems we can set SO_NOSIGPIPE on the socket to
        // prevent it from sending a PIPE signal and bringing down the whole
        // application if the server closes the socket forcibly
        int enable = 1;
        setsockopt_result = setsockopt(result.fd,
                                       SOL_SOCKET, SO_NOSIGPIPE,
                                       &enable, sizeof(enable));
        // Allow ENOTSOCK because it allows tests to use pipes instead of
        // real sockets
        if (setsockopt_result != 0 && setsockopt_result != ENOTSOCK) {
            LOG0("Failed to set SO_NOSIGPIPE on socket");
            continue;
        }
#endif

        // Increase send buffer to KINETIC_OBJ_SIZE
        // Note: OS allocates 2x this value for its overhead
        setsockopt_result = setsockopt(result.fd,
                                       SOL_SOCKET, SO_SNDBUF,
                                       &buffer_size, sizeof(buffer_size));
        if (setsockopt_result == -1) {
            LOG0("Error setting socket send buffer size");
            continue;
        }

        // Increase receive buffer to KINETIC_OBJ_SIZE
        // Note: OS allocates 2x this value for its overheadbuffer_size
        setsockopt_result = setsockopt(result.fd,
                                       SOL_SOCKET, SO_RCVBUF,
                                       &buffer_size, sizeof(buffer_size));
        if (setsockopt_result == -1) {
            LOG0("Error setting socket receive buffer size");
            continue;
        }

        break;
    }

    freeaddrinfo(ai_result);

    if (ai == NULL || result.fd == KINETIC_SOCKET_DESCRIPTOR_INVALID) {
        // we went through all addresses without finding one we could bind to
        LOGF0("Could not connect to %s:%d", host, port);
        return KINETIC_SOCKET_DESCRIPTOR_INVALID;
    }
    else {
        LOGF1("Successfully connected to %s:%d (fd=%d)", host, port, result.fd);
        return result.fd;
    }
}

void KineticSocket_Close(int socket)
{
    if (socket == -1) {
        LOG1("Not connected so no cleanup needed");
    }
    else {
        LOGF0("Closing socket with fd=%d", socket);
        if (close(socket) == 0) {
            LOG2("Socket closed successfully");
        }
        else {
            LOGF0("Error closing socket file descriptor!"
                 " (fd=%d, errno=%d, desc='%s')",
                 socket, errno, strerror(errno));
        }
    }
}

KineticWaitStatus KineticSocket_WaitUntilDataAvailable(int socket, int timeout)
{
    if (socket < 0) {return -1;}
    struct pollfd fd = {
        .fd = socket,
        .events = POLLIN,
        .revents = 0,
    };

    int res = poll(&fd, 1, timeout);

    if (res > 0) {
        //if (fd.revents & POLLHUP) // hung up
        if (fd.revents & POLLIN)
        {
            return KINETIC_WAIT_STATUS_DATA_AVAILABLE;
        }
        else
        {
            return KINETIC_WAIT_STATUS_FATAL_ERROR;
        }
    }
    else if (res == 0)
    {
        return KINETIC_WAIT_STATUS_TIMED_OUT;
    }
    else if ((errno & (EAGAIN | EINTR)) != 0)
    {
        return KINETIC_WAIT_STATUS_RETRYABLE_ERROR;
    }
    else
    {
        return KINETIC_WAIT_STATUS_FATAL_ERROR;
    }
}

int KineticSocket_DataBytesAvailable(int socket)
{
    if (socket < 0) {return -1;}
    int count = -1;
    ioctl(socket, FIONREAD, &count);
    return count;
}

KineticStatus KineticSocket_Read(int socket, ByteBuffer* dest, size_t len)
{
    LOGF2("Reading %zd bytes into buffer @ 0x%zX from fd=%d",
         len, (size_t)dest->array.data, socket);

    KineticStatus status = KINETIC_STATUS_INVALID;

    // Read "up to" the allocated number of bytes into dest buffer
    size_t bytesToReadIntoBuffer = len;
    if (dest->array.len < len) {
        bytesToReadIntoBuffer = dest->array.len;
    }
    while (dest->bytesUsed < bytesToReadIntoBuffer) {
        int opStatus;
        fd_set readSet;
        struct timeval timeout;

        // Time out after 5 seconds
        timeout.tv_sec = 5;
        timeout.tv_usec = 0;

        FD_ZERO(&readSet);
        FD_SET(socket, &readSet);
        opStatus = select(socket + 1, &readSet, NULL, NULL, &timeout);

        if (opStatus < 0) { // Error occurred
            LOGF0("Failed waiting to read from socket!"
                 " status=%d, errno=%d, desc='%s'",
                 opStatus, errno, strerror(errno));
            return KINETIC_STATUS_SOCKET_ERROR;
        }
        else if (opStatus == 0) { // Timeout occurred
            LOG0("Timed out waiting for socket data to arrive!");
            return KINETIC_STATUS_SOCKET_TIMEOUT;
        }
        else if (opStatus > 0) { // Data available to read
            // The socket is ready for reading
            opStatus = read(socket,
                            &dest->array.data[dest->bytesUsed],
                            dest->array.len - dest->bytesUsed);
            // Retry if no data yet...
            if (opStatus == -1 &&
                ((errno == EINTR) ||
                 (errno == EAGAIN) ||
                 (errno == EWOULDBLOCK)
                )) {
                continue;
            }
            else if (opStatus <= 0) {
                LOGF0("Failed to read from socket!"
                     " status=%d, errno=%d, desc='%s'",
                     opStatus, errno, strerror(errno));
                return KINETIC_STATUS_SOCKET_ERROR;
            }
            else {
                dest->bytesUsed += opStatus;
                LOGF3("Received %d bytes (%zd of %zd)",
                     opStatus, dest->bytesUsed, len);
            }
        }
    }

    // Flush any remaining data, in case of a truncated read w/short dest buffer
    if (dest->bytesUsed < len) {
        bool abortFlush = false;

        uint8_t* discardedBytes = malloc(len - dest->bytesUsed);
        if (discardedBytes == NULL) {
            LOG0("Failed allocating a socket read discard buffer!");
            abortFlush = true;
            status = KINETIC_STATUS_MEMORY_ERROR;
        }

        while (!abortFlush && dest->bytesUsed < len) {
            int opStatus;
            fd_set readSet;
            struct timeval timeout;
            size_t remainingLen = len - dest->bytesUsed;

            // Time out after 5 seconds
            timeout.tv_sec = 5;
            timeout.tv_usec = 0;

            FD_ZERO(&readSet);
            FD_SET(socket, &readSet);
            opStatus = select(socket + 1, &readSet, NULL, NULL, &timeout);

            if (opStatus < 0) { // Error occurred
                LOGF0("Failure trying to flush read socket data!"
                     " status=%d, errno=%d, desc='%s'",
                     status, errno, strerror(errno));
                abortFlush = true;
                status = KINETIC_STATUS_SOCKET_ERROR;
                continue;
            }
            else if (opStatus == 0) { // Timeout occurred
                LOG0("Timed out waiting to flush socket data!");
                abortFlush = true;
                status = KINETIC_STATUS_SOCKET_TIMEOUT;
                continue;
            }
            else if (opStatus > 0) { // Data available to read
                // The socket is ready for reading
                opStatus = read(socket, discardedBytes, remainingLen);
                // Retry if no data yet...
                if (opStatus == -1 &&
                    ((errno == EINTR) ||
                     (errno == EAGAIN) ||
                     (errno == EWOULDBLOCK)
                    )) {
                    continue;
                }
                else if (opStatus <= 0) {
                    LOGF0("Failed to read from socket while flushing!"
                         " status=%d, errno=%d, desc='%s'",
                         opStatus, errno, strerror(errno));
                    abortFlush = true;
                    status = KINETIC_STATUS_SOCKET_ERROR;
                }
                else {
                    dest->bytesUsed += opStatus;
                    LOGF3("Flushed %d bytes from socket read pipe (%zd of %zd)",
                         opStatus, dest->bytesUsed, len);
                }
            }
        }

        // Free up dynamically allocated memory before returning
        if (discardedBytes != NULL) {
            free(discardedBytes);
        }

        // Report any error that occurred during socket flush
        if (abortFlush) {
            LOG0("Socket read pipe flush aborted!");
            assert(status == KINETIC_STATUS_SUCCESS);
            return status;
        }

        // Report truncation of data for any variable length byte arrays
        LOGF1("Socket read buffer was truncated due to buffer overrun!"
             " received=%zu, copied=%zu",
             len, dest->array.len);
        return KINETIC_STATUS_BUFFER_OVERRUN;
    }
    LOGF3("Received %zd of %zd bytes requested", dest->bytesUsed, len);
    return KINETIC_STATUS_SUCCESS;
}

KineticStatus KineticSocket_ReadProtobuf(int socket, KineticPDU* pdu)
{
    size_t bytesToRead = pdu->header.protobufLength;
    LOGF2("Reading %zd bytes of protobuf", bytesToRead);

    uint8_t* packed = (uint8_t*)malloc(bytesToRead);
    if (packed == NULL) {
        LOG0("Failed allocating memory for protocol buffer");
        return KINETIC_STATUS_MEMORY_ERROR;
    }

    ByteBuffer recvBuffer = ByteBuffer_Create(packed, bytesToRead, 0);
    KineticStatus status = KineticSocket_Read(socket, &recvBuffer, bytesToRead);

    if (status != KINETIC_STATUS_SUCCESS) {
        LOG0("Protobuf read failed!");
        free(packed);
        return status;
    }
    else {
        pdu->proto = KineticProto_Message__unpack(
                         NULL, recvBuffer.bytesUsed, recvBuffer.array.data);
    }

    free(packed);

    if (pdu->proto == NULL) {
        pdu->protobufDynamicallyExtracted = false;
        LOG0("Error unpacking incoming Kinetic protobuf message!");
        return KINETIC_STATUS_DATA_ERROR;
    }
    else {
        pdu->protobufDynamicallyExtracted = true;
        LOG3("Protobuf unpacked successfully!");
        return KINETIC_STATUS_SUCCESS;
    }
}
void PlayDrillCommandClass::execute()
{
    if (IsExecuting())
    {
        LOGF("SKIPPING PlayDrill command");
        return;
    }

    parse();
    
    LogSteps();
    
    StopCommandClass::reset();
    PauseCommandClass::reset();
    resetInterruptCounter();
	
    LOGF("=========== Initializing Bumper ==========");
    
	bumperOn(true/*resetCounter*/);
    
    LOGF("=========== Play Drill ==========");

    s_pExecutedDrill = this;
    
    bool bContinue= true;
    char pref[10];
    
    while (true)
    {
        // handle incoming commands
        executeCommands();
        
        if (StopCommandClass::IsStopped())
        {
            m_execData.error= false;
            m_execData.feedData.result=MotorHelperClass::FR_NONE;
            break;
        }
        else if (PauseCommandClass::IsPaused())
        {
            static unsigned long    s_LedSetTime= 0;
            static bool             s_yellowLed= false;
            unsigned long currentTime= millis();
            
            if (currentTime - s_LedSetTime >= 250) {
                if (s_yellowLed) {
                    Helper::setLedColor(0,0,255); // turn the RGB LED blue
                }
                else {
                    Helper::setLedColor(255,215,0); // turn the RGB LED yellow
                }
                s_LedSetTime= currentTime;
                s_yellowLed= !s_yellowLed;
            }
            g_motorHelper.stopMotorsIfIdle();
        }
        else
        {
            Step* pStep= NULL;
            
            for (int stepIndex= 0; stepIndex < m_Steps.getUsedElems(); ++stepIndex)
            {
                if (m_execData.nextStepIndex > 0) {
                    // goto
                    stepIndex= m_execData.nextStepIndex - 1; // 1-based
                    m_execData.nextStepIndex= -1;
                }
                pStep= m_Steps[stepIndex];
                
                LOGF3("============ Executing Step [", stepIndex+1, F("] ============"));
                
                bContinue= pStep->execute(m_execData);
                
                // handle incoming commands
                executeCommands();
                
                if (StopCommandClass::IsStopped())
                {
                    m_execData.error= false;
                    m_execData.feedData.result=MotorHelperClass::FR_NONE;
                    bContinue= false;
                }
                else if (PauseCommandClass::IsPaused())
                {
                    g_motorHelper.stopMotorsIfIdle();
                    break;
                }
                
                if (!bContinue)
                    break;
					
				g_TaskQueue.executeReadyTasks();               
            }
            
            if (!bContinue)
                break;
        }
        
    } // EOF while(true)
    
    g_motorHelper.stopMotors();
    s_pExecutedDrill = NULL;
	
	bumperOff();
    
    g_serialCom.fireEvent(EVENT_EXECUTE_STOPPED);
    
    // in case of error no sound
    if (!m_execData.error)
    {
        Helper::playSound(Helper::SND_FINISHED);
    }
    
}