<commit_before>// Version: $Id$
//
//

// Commentary:
//
//

// Change Log:
//
//

// Code:

// /////////////////////////////////////////////////////////////////
// dtkDistributedGraph implementation
// /////////////////////////////////////////////////////////////////

inline dtkDistributedGraph::dtkDistributedGraph(dtkDistributedWorker *worker) : dtkDistributedContainer(worker)
{
    m_edges      = NULL;
    m_vertices   = NULL;
    m_edge_count = NULL;

}

inline dtkDistributedGraph::dtkDistributedGraph(const qlonglong& vertex_count, dtkDistributedWorker *worker) : dtkDistributedContainer(vertex_count, worker)
{
    m_edges = NULL;
    this->initialize();
}

inline dtkDistributedGraph::~dtkDistributedGraph(void)
{
    if (m_vertices)
        delete m_vertices;
    if (m_edge_count)
        delete m_edge_count;
    if (m_edges)
        delete m_edges;
}

inline qlonglong dtkDistributedGraph::vertexCount(void) const
{
    return this->size();
}

inline qlonglong dtkDistributedGraph::edgeCount(void) const
{
    return m_edge_count->at(this->wid());
}

inline void dtkDistributedGraph::initialize(void)
{
    qDebug() << "initialize graph with " << vertexCount() << "vertexes" << this->wid();

    m_mapper->setMapping(vertexCount(), m_comm->size());

    dtkDistributedMapper *mapper = new dtkDistributedMapper;
    mapper->setMapping(vertexCount(), m_comm->size());
    mapper->setMap(vertexCount() + 1, m_comm->size());
    m_vertices = new dtkDistributedArray<qlonglong>(vertexCount() + 1, this->worker(), mapper);

    m_edge_count = new dtkDistributedArray<qlonglong>(m_comm->size(), this->worker());
    for (qint32 i = 0; i< m_comm->size(); i++) {
        if (i == this->wid()) {
            m_edge_count->setAt(i,0);
        }
    }
}

inline void dtkDistributedGraph::build(void)
{
    this->m_comm->barrier();

    qlonglong edge_count = 0;
    for (qlonglong i = 0; i < m_comm->size(); ++i) {
        edge_count += m_edge_count->at(i);
    }

    dtkDistributedMapper *mapper = new dtkDistributedMapper;
    mapper->initMap(edge_count, m_comm->size());

    qlonglong offset = 0;
    for (qlonglong i = 0; i < m_comm->size(); ++i) {
        mapper->setMap(offset ,i);
        offset += m_edge_count->at(i);
    }

    m_edges = new dtkDistributedArray<qlonglong>(edge_count, this->worker(), mapper);

    EdgeMap::const_iterator it  = m_map.cbegin();
    EdgeMap::const_iterator end = m_map.cend();

    offset = mapper->firstIndex(this->wid());
    qlonglong index = 0;
    for (; it != end; ++it) {
        m_vertices->setAt(it.key(), offset);
        offset += (*it).count();
        EdgeList::const_iterator nit  = (*it).begin();
        EdgeList::const_iterator nend = (*it).end();
        for(;nit != nend; ++nit) {
            m_edges->begin()[index] = *nit;
            ++index;
        }
    }

    this->m_comm->barrier();
    if (this->wid() == this->m_comm->size() - 1) {
        m_vertices->setAt(m_vertices->size() - 1, offset);
    }

    this->m_comm->barrier();
}

inline bool dtkDistributedGraph::read2(const QString& filename)
{
    QFile file(filename);
    QTextStream in(&file);
    qlonglong edges_count = 0;
    QTime time;

    if (this->wid() == 0) {
        time.start();

        if(filename.isEmpty() || (!file.exists())) {
            qWarning() << "input file is empty/does not exist" << filename << "Current dir is" << QDir::currentPath();
            return false;
        }
        if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
            return false;

        QStringList header = in.readLine().split(' ');
        m_size = header.first().toLongLong();
        if (m_size  == 0) {
            qWarning() << "Can't parse size of the graph" << filename;
            return false;
        }
        edges_count = header.at(1).toLongLong();
        if (edges_count  == 0) {
            qWarning() << "Can't parse the number of edges" << filename;
            return false;
        }

    }
    m_comm->broadcast(&m_size, 1, 0);
    m_comm->broadcast(&edges_count, 1, 0);

    this->initialize();
    m_comm->barrier();

    m_edges = new dtkDistributedArray<qlonglong>(2 * edges_count, this->worker());

    if (this->wid() == 0) {

        QString line;
        QStringList edges;

        QVector<qlonglong> v_vec; v_vec.reserve(2 * m_vertices->mapper()->countMax());
        QVector<qlonglong> e_vec; e_vec.reserve(2 * m_edges->mapper()->countMax());

        qlonglong e_gid = 0;
        qlonglong v_gid = 0;

        qlonglong e_local_count = 0;
        qlonglong v_local_count = 0;

        qlonglong owner = 0;
        qlonglong v_first_gid = m_vertices->mapper()->firstIndex(owner);
        qlonglong v_last_gid  = m_vertices->mapper()->lastIndex(owner);
        qlonglong e_first_gid = 0;

        while (!in.atEnd()) {
            line = in.readLine().trimmed();
            edges = line.split(' ');
            if (line.isEmpty() || line.at(0) == '#'){
                qDebug() << "skip line" << line;
                continue;
            }
            v_vec << e_gid;
            for (qlonglong i = 0; i < edges.size(); ++i) {
                qlonglong val = edges.at(i).toLongLong();
                if (val < 1 || val > m_size ) {
                    qWarning() << "bad vertice id in graph for edge" << val << v_gid;
                    continue;
                }
                e_vec << val-1;
                ++e_gid;
                ++e_local_count;
            }
            ++v_gid;
            if(v_gid > v_last_gid || in.atEnd()) {
                if(in.atEnd()) {
                    v_vec << e_gid;
                    m_vertices->setAt(v_first_gid, v_vec.data(), v_vec.size());
                    m_edges->setAt(e_first_gid, e_vec.data(), e_vec.size());
                    m_edge_count->setAt(owner, e_local_count);

                } else {
                    m_vertices->setAt(v_first_gid, v_vec.data(), v_vec.size());
                    m_edges->setAt(e_first_gid, e_vec.data(), e_vec.size());
                    m_edge_count->setAt(owner, e_local_count);

                    ++owner;
                    e_local_count = 0;
                    v_first_gid = v_gid;
                    v_last_gid  = m_vertices->mapper()->lastIndex(owner);
                    e_first_gid = e_gid;
                    v_vec.clear();
                    v_vec.reserve(2 * m_vertices->mapper()->countMax());
                    e_vec.clear();
                    e_vec.reserve(2 * m_edges->mapper()->countMax());
                }
            }
        }
    }

    m_comm->barrier();

    qDebug() << "remap";
    dtkDistributedMapper *mapper = new dtkDistributedMapper;
    mapper->initMap(2 * edges_count, m_comm->size());

    qlonglong offset = 0;
    for (qlonglong i = 0; i < m_comm->size(); ++i) {
        mapper->setMap(offset ,i);
        offset += m_edge_count->at(i);
    }
    m_edges->remap(mapper);
    if (this->wid() == 0) {
        qDebug() << "read and remap done in "<< time.elapsed() << "ms";
    }

    return true;
}

// read graph from file as described in
// http://people.sc.fsu.edu/~jburkardt/data/metis_graph/metis_graph.html

inline bool dtkDistributedGraph::read(const QString& filename)
{
    QFile file(filename);
    QTextStream in(&file);
    qlonglong edges_count = 0;
    QTime time;
    if (this->wid() == 0) {
        time.start();

        if(filename.isEmpty() || (!file.exists())) {
            qWarning() << "input file is empty/does not exist" << filename << "Current dir is" << QDir::currentPath();
            return false;
        }
        if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
            return false;

        QStringList header = in.readLine().split(' ');
        m_size = header.first().toLongLong();
        if (m_size  == 0) {
            qWarning() << "Can't parse size of the graph" << filename;
            return false;
        }
        edges_count = header.at(1).toLongLong();
        if (edges_count  == 0) {
            qWarning() << "Can't parse the number of edges" << filename;
            return false;
        }

    }
    m_comm->broadcast(&m_size, 1, 0);
    m_comm->broadcast(&edges_count, 1, 0);

    this->initialize();
    m_comm->barrier();

    m_edges = new dtkDistributedArray<qlonglong>(2 * edges_count, this->worker());

    if (this->wid() == 0) {

        qlonglong index              = 0;
        qlonglong current_edge_count = 0;
        qlonglong current_owner      = 0;
        qlonglong current_vertice    = 0;
        qlonglong temp_owner;

        QString line;
        QStringList edges;

        qlonglong owner = 0;
        qlonglong next_index = m_edges->mapper()->lastIndex(owner);

        qlonglong max_vert_size = m_vertices->mapper()->countMax();
        qlonglong max_edge_size = m_edges->mapper()->countMax();

        qlonglong *v_array = new qlonglong[max_vert_size];
        qlonglong *e_array = new qlonglong[max_edge_size];

        qlonglong val;
        qlonglong v_pos = 0;
        qlonglong e_pos = 0;
        while (!in.atEnd()) {
            line = in.readLine().trimmed();
            edges = line.split(' ');
            if (line.isEmpty() || line.at(0) == '#'){
                qDebug() << "skip line" << line;
                continue;
            }
            v_array[v_pos] = index;
            for (qlonglong i = 0; i < edges.size(); ++i) {
                val = edges.at(i).toLongLong();
                if (val < 1 || val > m_size ) {
                    qWarning() << "bad vertice id in graph for edge" << val << current_vertice;
                    continue;
                }
                e_array[e_pos] = val-1;
                ++e_pos;
                ++index;
                ++current_edge_count;
                if (index > next_index) {
                    m_edges->setAt(m_edges->mapper()->firstIndex(owner), e_array, e_pos);
                    owner++;
                    e_pos = 0;
                    if (owner <  m_comm->size()) {
                        next_index = m_edges->mapper()->lastIndex(owner);
                    }
                }
            }

            ++current_vertice;
            ++v_pos;

            temp_owner = m_vertices->mapper()->owner(current_vertice, current_owner);
            if (temp_owner != current_owner) {
                m_edge_count->setAt(current_owner, current_edge_count);
                m_vertices->setAt(m_vertices->mapper()->firstIndex(current_owner), v_array, v_pos);
                current_owner = temp_owner;
                current_edge_count = 0;
                v_pos = 0;
            }
        }
        v_array[v_pos] = index; ++v_pos;
        m_vertices->setAt(m_vertices->mapper()->firstIndex(m_vertices->mapper()->owner(current_vertice, current_owner)), v_array, v_pos);
        delete v_array;
        delete e_array;

    }

    m_comm->barrier();

    if (this->wid() == 0) {
        qDebug() << "read done in "<< time.elapsed() << "ms";
        time.restart();
    }
    dtkDistributedMapper *mapper = new dtkDistributedMapper;
    mapper->initMap(2 * edges_count, m_comm->size());

    qlonglong offset = 0;
    for (qlonglong i = 0; i < m_comm->size(); ++i) {
        mapper->setMap(offset ,i);
        offset += m_edge_count->at(i);
    }
    m_edges->remap(mapper);
    if (this->wid() == 0) {
        qDebug() << "remap done in "<< time.elapsed() << "ms";
    }

    return true;
}

inline void dtkDistributedGraph::appendEdge(qlonglong from, qlonglong to)
{
    qint32 wid = this->wid();
    qint32 from_owner = static_cast<qint32>(m_mapper->owner(from, wid));
    qint32 to_owner = static_cast<qint32>(m_mapper->owner(to, wid));

    if ((wid != from_owner) && (wid != to_owner))
        return;

    qlonglong edge_counter = m_edge_count->at(this->wid());

    if (wid == from_owner) {
        m_map[from].append(to);
        ++edge_counter;
    }
    if (wid == to_owner) {
        m_map[to].append(from);
        ++edge_counter;
    }
    m_edge_count->setAt(wid, edge_counter);
}

inline qlonglong dtkDistributedGraph::edgeCount(qlonglong vertex_id) const
{
    qlonglong start = m_vertices->at(vertex_id);
    return (m_vertices->at(vertex_id + 1) - start);
}

inline dtkDistributedGraph::Neighbours dtkDistributedGraph::neighbours(const qlonglong& vertex_id) const
{
    qlonglong n_start = m_vertices->at(vertex_id);
    qlonglong size = m_vertices->at(vertex_id + 1) - n_start;

    return Neighbours(m_edges, n_start, size);
}

dtkDistributedGraph::const_iterator dtkDistributedGraph::cbegin(void) const
{
    return const_iterator(this, this->m_mapper->firstIndex(this->wid()));
}

dtkDistributedGraph::const_iterator dtkDistributedGraph::cend(void) const
{
    return const_iterator(this, this->m_mapper->lastIndex(this->wid()) + 1);
}

dtkDistributedGraph::const_iterator dtkDistributedGraph::begin(void) const
{
    return const_iterator(this, this->m_mapper->firstIndex(this->wid()));
}

dtkDistributedGraph::const_iterator dtkDistributedGraph::end(void) const
{
    return const_iterator(this, this->m_mapper->lastIndex(this->wid()) + 1);
}

//
// dtkDistributedGraph.tpp ends here
<commit_msg>avoid infinite loop in threads with rank > 0 if read fails<commit_after>// Version: $Id$
//
//

// Commentary:
//
//

// Change Log:
//
//

// Code:

// /////////////////////////////////////////////////////////////////
// dtkDistributedGraph implementation
// /////////////////////////////////////////////////////////////////

inline dtkDistributedGraph::dtkDistributedGraph(dtkDistributedWorker *worker) : dtkDistributedContainer(worker)
{
    m_edges      = NULL;
    m_vertices   = NULL;
    m_edge_count = NULL;

}

inline dtkDistributedGraph::dtkDistributedGraph(const qlonglong& vertex_count, dtkDistributedWorker *worker) : dtkDistributedContainer(vertex_count, worker)
{
    m_edges = NULL;
    this->initialize();
}

inline dtkDistributedGraph::~dtkDistributedGraph(void)
{
    if (m_vertices)
        delete m_vertices;
    if (m_edge_count)
        delete m_edge_count;
    if (m_edges)
        delete m_edges;
}

inline qlonglong dtkDistributedGraph::vertexCount(void) const
{
    return this->size();
}

inline qlonglong dtkDistributedGraph::edgeCount(void) const
{
    return m_edge_count->at(this->wid());
}

inline void dtkDistributedGraph::initialize(void)
{
    qDebug() << "initialize graph with " << vertexCount() << "vertexes" << this->wid();

    m_mapper->setMapping(vertexCount(), m_comm->size());

    dtkDistributedMapper *mapper = new dtkDistributedMapper;
    mapper->setMapping(vertexCount(), m_comm->size());
    mapper->setMap(vertexCount() + 1, m_comm->size());
    m_vertices = new dtkDistributedArray<qlonglong>(vertexCount() + 1, this->worker(), mapper);

    m_edge_count = new dtkDistributedArray<qlonglong>(m_comm->size(), this->worker());
    for (qint32 i = 0; i< m_comm->size(); i++) {
        if (i == this->wid()) {
            m_edge_count->setAt(i,0);
        }
    }
}

inline void dtkDistributedGraph::build(void)
{
    this->m_comm->barrier();

    qlonglong edge_count = 0;
    for (qlonglong i = 0; i < m_comm->size(); ++i) {
        edge_count += m_edge_count->at(i);
    }

    dtkDistributedMapper *mapper = new dtkDistributedMapper;
    mapper->initMap(edge_count, m_comm->size());

    qlonglong offset = 0;
    for (qlonglong i = 0; i < m_comm->size(); ++i) {
        mapper->setMap(offset ,i);
        offset += m_edge_count->at(i);
    }

    m_edges = new dtkDistributedArray<qlonglong>(edge_count, this->worker(), mapper);

    EdgeMap::const_iterator it  = m_map.cbegin();
    EdgeMap::const_iterator end = m_map.cend();

    offset = mapper->firstIndex(this->wid());
    qlonglong index = 0;
    for (; it != end; ++it) {
        m_vertices->setAt(it.key(), offset);
        offset += (*it).count();
        EdgeList::const_iterator nit  = (*it).begin();
        EdgeList::const_iterator nend = (*it).end();
        for(;nit != nend; ++nit) {
            m_edges->begin()[index] = *nit;
            ++index;
        }
    }

    this->m_comm->barrier();
    if (this->wid() == this->m_comm->size() - 1) {
        m_vertices->setAt(m_vertices->size() - 1, offset);
    }

    this->m_comm->barrier();
}

inline bool dtkDistributedGraph::read2(const QString& filename)
{
    QFile file(filename);
    QTextStream in(&file);
    qlonglong edges_count = 0;
    QTime time;

    if (this->wid() == 0) {
        time.start();

        if(filename.isEmpty() || (!file.exists())) {
            qWarning() << "input file is empty/does not exist" << filename << "Current dir is" << QDir::currentPath();
            return false;
        }
        if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
            return false;

        QStringList header = in.readLine().split(' ');
        m_size = header.first().toLongLong();
        if (m_size  == 0) {
            qWarning() << "Can't parse size of the graph" << filename;
            return false;
        }
        edges_count = header.at(1).toLongLong();
        if (edges_count  == 0) {
            qWarning() << "Can't parse the number of edges" << filename;
            return false;
        }

    }
    m_comm->broadcast(&m_size, 1, 0);
    m_comm->broadcast(&edges_count, 1, 0);

    this->initialize();
    m_comm->barrier();

    m_edges = new dtkDistributedArray<qlonglong>(2 * edges_count, this->worker());

    if (this->wid() == 0) {

        QString line;
        QStringList edges;

        QVector<qlonglong> v_vec; v_vec.reserve(2 * m_vertices->mapper()->countMax());
        QVector<qlonglong> e_vec; e_vec.reserve(2 * m_edges->mapper()->countMax());

        qlonglong e_gid = 0;
        qlonglong v_gid = 0;

        qlonglong e_local_count = 0;
        qlonglong v_local_count = 0;

        qlonglong owner = 0;
        qlonglong v_first_gid = m_vertices->mapper()->firstIndex(owner);
        qlonglong v_last_gid  = m_vertices->mapper()->lastIndex(owner);
        qlonglong e_first_gid = 0;

        while (!in.atEnd()) {
            line = in.readLine().trimmed();
            edges = line.split(' ');
            if (line.isEmpty() || line.at(0) == '#'){
                qDebug() << "skip line" << line;
                continue;
            }
            v_vec << e_gid;
            for (qlonglong i = 0; i < edges.size(); ++i) {
                qlonglong val = edges.at(i).toLongLong();
                if (val < 1 || val > m_size ) {
                    qWarning() << "bad vertice id in graph for edge" << val << v_gid;
                    continue;
                }
                e_vec << val-1;
                ++e_gid;
                ++e_local_count;
            }
            ++v_gid;
            if(v_gid > v_last_gid || in.atEnd()) {
                if(in.atEnd()) {
                    v_vec << e_gid;
                    m_vertices->setAt(v_first_gid, v_vec.data(), v_vec.size());
                    m_edges->setAt(e_first_gid, e_vec.data(), e_vec.size());
                    m_edge_count->setAt(owner, e_local_count);

                } else {
                    m_vertices->setAt(v_first_gid, v_vec.data(), v_vec.size());
                    m_edges->setAt(e_first_gid, e_vec.data(), e_vec.size());
                    m_edge_count->setAt(owner, e_local_count);

                    ++owner;
                    e_local_count = 0;
                    v_first_gid = v_gid;
                    v_last_gid  = m_vertices->mapper()->lastIndex(owner);
                    e_first_gid = e_gid;
                    v_vec.clear();
                    v_vec.reserve(2 * m_vertices->mapper()->countMax());
                    e_vec.clear();
                    e_vec.reserve(2 * m_edges->mapper()->countMax());
                }
            }
        }
    }

    m_comm->barrier();

    qDebug() << "remap";
    dtkDistributedMapper *mapper = new dtkDistributedMapper;
    mapper->initMap(2 * edges_count, m_comm->size());

    qlonglong offset = 0;
    for (qlonglong i = 0; i < m_comm->size(); ++i) {
        mapper->setMap(offset ,i);
        offset += m_edge_count->at(i);
    }
    m_edges->remap(mapper);
    if (this->wid() == 0) {
        qDebug() << "read and remap done in "<< time.elapsed() << "ms";
    }

    return true;
}

// read graph from file as described in
// http://people.sc.fsu.edu/~jburkardt/data/metis_graph/metis_graph.html

inline bool dtkDistributedGraph::read(const QString& filename)
{
    QFile file(filename);
    QTextStream in(&file);
    qlonglong edges_count = 0;
    QTime time;

    if (this->wid() == 0) {
        time.start();

        if(filename.isEmpty() || (!file.exists())) {
            qWarning() << "input file is empty/does not exist" << filename << "Current dir is" << QDir::currentPath();
            m_size = -1;
        } else if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
            m_size = -1;
        } else {

            QStringList header = in.readLine().split(' ');
            m_size = header.first().toLongLong();
            if (m_size  == 0) {
                qWarning() << "Can't parse size of the graph" << filename;
                m_size = -1;
            } else {
                edges_count = header.at(1).toLongLong();
                if (edges_count  == 0) {
                    qWarning() << "Can't parse the number of edges" << filename;
                    m_size = -1;
                }
            }
        }
    }
    m_comm->broadcast(&m_size, 1, 0);

    if (m_size < 0)
        return false;

    m_comm->broadcast(&edges_count, 1, 0);

    this->initialize();
    m_comm->barrier();

    m_edges = new dtkDistributedArray<qlonglong>(2 * edges_count, this->worker());

    if (this->wid() == 0) {

        qlonglong index              = 0;
        qlonglong current_edge_count = 0;
        qlonglong current_owner      = 0;
        qlonglong current_vertice    = 0;
        qlonglong temp_owner;

        QString line;
        QStringList edges;

        qlonglong owner = 0;
        qlonglong next_index = m_edges->mapper()->lastIndex(owner);

        qlonglong max_vert_size = m_vertices->mapper()->countMax();
        qlonglong max_edge_size = m_edges->mapper()->countMax();

        qlonglong *v_array = new qlonglong[max_vert_size];
        qlonglong *e_array = new qlonglong[max_edge_size];

        qlonglong val;
        qlonglong v_pos = 0;
        qlonglong e_pos = 0;
        while (!in.atEnd()) {
            line = in.readLine().trimmed();
            edges = line.split(' ');
            if (line.isEmpty() || line.at(0) == '#'){
                qDebug() << "skip line" << line;
                continue;
            }
            v_array[v_pos] = index;
            for (qlonglong i = 0; i < edges.size(); ++i) {
                val = edges.at(i).toLongLong();
                if (val < 1 || val > m_size ) {
                    qWarning() << "bad vertice id in graph for edge" << val << current_vertice;
                    continue;
                }
                e_array[e_pos] = val-1;
                ++e_pos;
                ++index;
                ++current_edge_count;
                if (index > next_index) {
                    m_edges->setAt(m_edges->mapper()->firstIndex(owner), e_array, e_pos);
                    owner++;
                    e_pos = 0;
                    if (owner <  m_comm->size()) {
                        next_index = m_edges->mapper()->lastIndex(owner);
                    }
                }
            }

            ++current_vertice;
            ++v_pos;

            temp_owner = m_vertices->mapper()->owner(current_vertice, current_owner);
            if (temp_owner != current_owner) {
                m_edge_count->setAt(current_owner, current_edge_count);
                m_vertices->setAt(m_vertices->mapper()->firstIndex(current_owner), v_array, v_pos);
                current_owner = temp_owner;
                current_edge_count = 0;
                v_pos = 0;
            }
        }
        v_array[v_pos] = index; ++v_pos;
        m_vertices->setAt(m_vertices->mapper()->firstIndex(m_vertices->mapper()->owner(current_vertice, current_owner)), v_array, v_pos);
        delete v_array;
        delete e_array;

    }

    m_comm->barrier();

    if (this->wid() == 0) {
        qDebug() << "read done in "<< time.elapsed() << "ms";
        time.restart();
    }
    dtkDistributedMapper *mapper = new dtkDistributedMapper;
    mapper->initMap(2 * edges_count, m_comm->size());

    qlonglong offset = 0;
    for (qlonglong i = 0; i < m_comm->size(); ++i) {
        mapper->setMap(offset ,i);
        offset += m_edge_count->at(i);
    }
    m_edges->remap(mapper);
    if (this->wid() == 0) {
        qDebug() << "remap done in "<< time.elapsed() << "ms";
    }

    return true;
}

inline void dtkDistributedGraph::appendEdge(qlonglong from, qlonglong to)
{
    qint32 wid = this->wid();
    qint32 from_owner = static_cast<qint32>(m_mapper->owner(from, wid));
    qint32 to_owner = static_cast<qint32>(m_mapper->owner(to, wid));

    if ((wid != from_owner) && (wid != to_owner))
        return;

    qlonglong edge_counter = m_edge_count->at(this->wid());

    if (wid == from_owner) {
        m_map[from].append(to);
        ++edge_counter;
    }
    if (wid == to_owner) {
        m_map[to].append(from);
        ++edge_counter;
    }
    m_edge_count->setAt(wid, edge_counter);
}

inline qlonglong dtkDistributedGraph::edgeCount(qlonglong vertex_id) const
{
    qlonglong start = m_vertices->at(vertex_id);
    return (m_vertices->at(vertex_id + 1) - start);
}

inline dtkDistributedGraph::Neighbours dtkDistributedGraph::neighbours(const qlonglong& vertex_id) const
{
    qlonglong n_start = m_vertices->at(vertex_id);
    qlonglong size = m_vertices->at(vertex_id + 1) - n_start;

    return Neighbours(m_edges, n_start, size);
}

dtkDistributedGraph::const_iterator dtkDistributedGraph::cbegin(void) const
{
    return const_iterator(this, this->m_mapper->firstIndex(this->wid()));
}

dtkDistributedGraph::const_iterator dtkDistributedGraph::cend(void) const
{
    return const_iterator(this, this->m_mapper->lastIndex(this->wid()) + 1);
}

dtkDistributedGraph::const_iterator dtkDistributedGraph::begin(void) const
{
    return const_iterator(this, this->m_mapper->firstIndex(this->wid()));
}

dtkDistributedGraph::const_iterator dtkDistributedGraph::end(void) const
{
    return const_iterator(this, this->m_mapper->lastIndex(this->wid()) + 1);
}

//
// dtkDistributedGraph.tpp ends here
<|endoftext|>


<commit_before>#pragma once

#include <duke/engine/Timeline.hpp>

namespace duke {

Ranges getMediaRanges(const Timeline &timeline);
bool contains(const Ranges &range, size_t frame);

struct TrackMediaFrameIterator {
	TrackMediaFrameIterator() = default;
	TrackMediaFrameIterator(const TrackMediaFrameIterator&) = default;
	TrackMediaFrameIterator(const Timeline * pTimeline, size_t currentFrame);

	void reset(const Timeline * pTimeline, size_t currentFrame);

	void clear();
	MediaFrameReference next();
	bool empty() const;
private:
	std::vector<MediaFrameReference> m_References;
};

enum class IterationMode
	: unsigned char {
		FORWARD, BACKWARD, PINGPONG
};

struct FrameIterator {
	FrameIterator(const Ranges *pMediaRanges, size_t initialFrame, IterationMode mode = IterationMode::FORWARD);

	FrameIterator& setMaxIterations(size_t maxIterations);

	void clear();
	size_t next();
	bool empty() const;
private:
	size_t findNext(size_t) const;
	size_t findPrevious(size_t) const;

	const Ranges *m_pMediaRanges;
	IterationMode m_Mode;
	size_t m_Forward, m_Backward, m_FramesToGo, m_SpanCount;
	bool m_bForward;
};

struct TimelineIterator {
	TimelineIterator();
	TimelineIterator(const Timeline * pTimeline, const Ranges *pMediaRanges, size_t currentFrame, IterationMode mode = IterationMode::FORWARD);

	inline void setMaxFrameIterations(size_t maxIterations) {
		m_FrameIterator.setMaxIterations(maxIterations);
	}

	void clear();
	MediaFrameReference next();
	bool empty();
private:
	const Timeline * m_pTimeline;
	FrameIterator m_FrameIterator;
	TrackMediaFrameIterator m_TrackIterator;
};

} /* namespace duke */
<commit_msg>Adding documentation<commit_after>#pragma once

#include <duke/engine/Timeline.hpp>

namespace duke {

/**
 * Return all the clip's ranges sorted by start time
 */
Ranges getMediaRanges(const Timeline &timeline);
bool contains(const Ranges &range, size_t frame);

struct TrackMediaFrameIterator {
	TrackMediaFrameIterator() = default;
	TrackMediaFrameIterator(const TrackMediaFrameIterator&) = default;
	TrackMediaFrameIterator(const Timeline * pTimeline, size_t currentFrame);

	void reset(const Timeline * pTimeline, size_t currentFrame);

	void clear();
	MediaFrameReference next();
	bool empty() const;
private:
	std::vector<MediaFrameReference> m_References;
};

enum class IterationMode
	: unsigned char {
		FORWARD, BACKWARD, PINGPONG
};

struct FrameIterator {
	FrameIterator(const Ranges *pMediaRanges, size_t initialFrame, IterationMode mode = IterationMode::FORWARD);

	FrameIterator& setMaxIterations(size_t maxIterations);

	void clear();
	size_t next();
	bool empty() const;
private:
	size_t findNext(size_t) const;
	size_t findPrevious(size_t) const;

	const Ranges *m_pMediaRanges;
	IterationMode m_Mode;
	size_t m_Forward, m_Backward, m_FramesToGo, m_SpanCount;
	bool m_bForward;
};

struct TimelineIterator {
	TimelineIterator();
	TimelineIterator(const Timeline * pTimeline, const Ranges *pMediaRanges, size_t currentFrame, IterationMode mode = IterationMode::FORWARD);

	inline void setMaxFrameIterations(size_t maxIterations) {
		m_FrameIterator.setMaxIterations(maxIterations);
	}

	void clear();
	MediaFrameReference next();
	bool empty();
private:
	const Timeline * m_pTimeline;
	FrameIterator m_FrameIterator;
	TrackMediaFrameIterator m_TrackIterator;
};

} /* namespace duke */
<|endoftext|>


<commit_before>//----------------------------------------------------------------------------
/// \file  visit_encode_size.hpp
//----------------------------------------------------------------------------
/// \brief A class implementing size encoder visitor.
//----------------------------------------------------------------------------
// Copyright (c) 2010 Serge Aleynikov <saleyn@gmail.com>
// Created: 2010-09-20
//----------------------------------------------------------------------------
/*
***** BEGIN LICENSE BLOCK *****

Copyright 2010 Serge Aleynikov <saleyn at gmail dot com>

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

***** END LICENSE BLOCK *****
*/
#ifndef _IMPL_VISIT_ENCODE_SIZE_HPP_
#define _IMPL_VISIT_ENCODE_SIZE_HPP_

#include <eixx/marshal/visit.hpp>
#include <ei.h>

namespace eixx {
namespace marshal {

template <typename Alloc>
struct visit_eterm_encode_size_calc
    : public static_visitor<visit_eterm_encode_size_calc<Alloc>, size_t> {

    size_t operator()(bool   a) const { return 3 + (a ? 4 : 5); }
    size_t operator()(double a) const { return 9; }
    size_t operator()(long   a) const { int n = 0; ei_encode_longlong(NULL, &n, a); return n; }

    template <typename T>
    size_t operator()(const T& a) const { return a.encode_size(); }
};

} // namespace eixx
} // namespace eixx

#endif // _IMPL_VISIT_ENCODE_SIZE_HPP_
<commit_msg>Do not rely on past guess about encoding size<commit_after>//----------------------------------------------------------------------------
/// \file  visit_encode_size.hpp
//----------------------------------------------------------------------------
/// \brief A class implementing size encoder visitor.
//----------------------------------------------------------------------------
// Copyright (c) 2010 Serge Aleynikov <saleyn@gmail.com>
// Created: 2010-09-20
//----------------------------------------------------------------------------
/*
***** BEGIN LICENSE BLOCK *****

Copyright 2010 Serge Aleynikov <saleyn at gmail dot com>

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

***** END LICENSE BLOCK *****
*/
#ifndef _IMPL_VISIT_ENCODE_SIZE_HPP_
#define _IMPL_VISIT_ENCODE_SIZE_HPP_

#include <eixx/marshal/visit.hpp>
#include <ei.h>

namespace eixx {
namespace marshal {

template <typename Alloc>
struct visit_eterm_encode_size_calc
    : public static_visitor<visit_eterm_encode_size_calc<Alloc>, size_t> {

    size_t operator()(bool   a) const { int n = 0; ei_encode_boolean(NULL, &n, a); return n; }
    size_t operator()(double a) const { int n = 0; ei_encode_double(NULL, &n, a); return n; }
    size_t operator()(long   a) const { int n = 0; ei_encode_longlong(NULL, &n, a); return n; }

    template <typename T>
    size_t operator()(const T& a) const { return a.encode_size(); }
};

} // namespace eixx
} // namespace eixx

#endif // _IMPL_VISIT_ENCODE_SIZE_HPP_
<|endoftext|>


<commit_before>#include "epiphansdk_video_source.h"

namespace gg
{

VideoSourceEpiphanSDK::VideoSourceEpiphanSDK(
    const std::string device_id, V2U_INT32 colour_space)
    : IVideoSource(),
      _frame_grabber(nullptr),
      _flags(0),
      _roi(nullptr)
{
    FrmGrab_Init();

    _frame_grabber = FrmGrabLocal_OpenSN(device_id.c_str());
    if (not _frame_grabber)
    {
        // TODO - exception GiftGrab#42
        std::cerr << "Could not open " << device_id << std::endl;
        return;
    }

    if (colour_space != V2U_GRABFRAME_FORMAT_I420 and colour_space != V2U_GRABFRAME_FORMAT_BGR24)
    {
        // TODO - exception GiftGrab#42
        std::cerr << "Colour space " << colour_space << " not supported" << std::endl;
        return;
    }
    _flags |= colour_space;
}

VideoSourceEpiphanSDK::~VideoSourceEpiphanSDK()
{
    if (_frame_grabber)
    {
        FrmGrab_Release(_frame_grabber, _buffer);
        FrmGrab_Close(_frame_grabber);
    }
    FrmGrab_Deinit();
}

bool VideoSourceEpiphanSDK::get_frame_dimensions(int & width, int & height)
{
    if (not _roi)
    {
        VideoFrame_I420 frame;
        if (not get_frame(frame)) return false;
        _roi = new V2URect;
        _roi->x = 0;
        _roi->y = 0;
        _roi->width = frame.cols();
        _roi->height = frame.rows();
    }
    width = _roi->width;
    height = _roi->height;
    return true;
}

bool VideoSourceEpiphanSDK::get_frame(VideoFrame_BGRA & frame)
{
    // TODO
    return false;
}

bool VideoSourceEpiphanSDK::get_frame(VideoFrame_I420 & frame)
{
    _buffer = FrmGrab_Frame(_frame_grabber, _flags, _roi);
    if (_buffer)
    {
        frame = VideoFrame_I420(
                    static_cast<unsigned char*>(_buffer->pixbuf), _buffer->imagelen,
                    _buffer->crop.width, _buffer->crop.height,
                    false
                    );
        return true;
    }
    else
        return false;
}

double VideoSourceEpiphanSDK::get_frame_rate()
{
    // TODO
    return -1;
}

void VideoSourceEpiphanSDK::set_sub_frame(int x, int y, int width, int height)
{
    // TODO
}

void VideoSourceEpiphanSDK::get_full_frame()
{
    // TODO
}

}
<commit_msg>Issue #31: delete'ing _roi in destructor<commit_after>#include "epiphansdk_video_source.h"

namespace gg
{

VideoSourceEpiphanSDK::VideoSourceEpiphanSDK(
    const std::string device_id, V2U_INT32 colour_space)
    : IVideoSource(),
      _frame_grabber(nullptr),
      _flags(0),
      _roi(nullptr)
{
    FrmGrab_Init();

    _frame_grabber = FrmGrabLocal_OpenSN(device_id.c_str());
    if (not _frame_grabber)
    {
        // TODO - exception GiftGrab#42
        std::cerr << "Could not open " << device_id << std::endl;
        return;
    }

    if (colour_space != V2U_GRABFRAME_FORMAT_I420 and colour_space != V2U_GRABFRAME_FORMAT_BGR24)
    {
        // TODO - exception GiftGrab#42
        std::cerr << "Colour space " << colour_space << " not supported" << std::endl;
        return;
    }
    _flags |= colour_space;
}

VideoSourceEpiphanSDK::~VideoSourceEpiphanSDK()
{
    if (_frame_grabber)
    {
        FrmGrab_Release(_frame_grabber, _buffer);
        FrmGrab_Close(_frame_grabber);
    }
    FrmGrab_Deinit();
    delete _roi;
}

bool VideoSourceEpiphanSDK::get_frame_dimensions(int & width, int & height)
{
    if (not _roi)
    {
        VideoFrame_I420 frame;
        if (not get_frame(frame)) return false;
        _roi = new V2URect;
        _roi->x = 0;
        _roi->y = 0;
        _roi->width = frame.cols();
        _roi->height = frame.rows();
    }
    width = _roi->width;
    height = _roi->height;
    return true;
}

bool VideoSourceEpiphanSDK::get_frame(VideoFrame_BGRA & frame)
{
    // TODO
    return false;
}

bool VideoSourceEpiphanSDK::get_frame(VideoFrame_I420 & frame)
{
    _buffer = FrmGrab_Frame(_frame_grabber, _flags, _roi);
    if (_buffer)
    {
        frame = VideoFrame_I420(
                    static_cast<unsigned char*>(_buffer->pixbuf), _buffer->imagelen,
                    _buffer->crop.width, _buffer->crop.height,
                    false
                    );
        return true;
    }
    else
        return false;
}

double VideoSourceEpiphanSDK::get_frame_rate()
{
    // TODO
    return -1;
}

void VideoSourceEpiphanSDK::set_sub_frame(int x, int y, int width, int height)
{
    // TODO
}

void VideoSourceEpiphanSDK::get_full_frame()
{
    // TODO
}

}
<|endoftext|>


<commit_before>#include "consts.h"
#include "circuits/element.h"
#include "circuits/element.h"
#include <iostream>
#include <vector>
#include <string>

using namespace std;

void printSummary(int numNodes, int numVariables, int numElements){
    /* Monta o sistema nodal modificado */
    cout << "O circuito tem " << numNodes << " nos, " << numVariables << " variaveis e " << numElements << " elementos" << endl;
    cout << endl;
}

void applyStamps(int numElements, int numVariables, vector<Element> netlist, double Yn[MAX_NODES+1][MAX_NODES+2]){
    double g;
    char tipo;
    for (int i=1; i<=numElements; i++) {
        tipo=netlist[i].getType();
        if (tipo=='R') {
            g=1/netlist[i].valor;
            Yn[netlist[i].a][netlist[i].a]+=g;
            Yn[netlist[i].b][netlist[i].b]+=g;
            Yn[netlist[i].a][netlist[i].b]-=g;
            Yn[netlist[i].b][netlist[i].a]-=g;
        }
        else if (tipo=='G') {
            g=netlist[i].valor;
            Yn[netlist[i].a][netlist[i].c]+=g;
            Yn[netlist[i].b][netlist[i].d]+=g;
            Yn[netlist[i].a][netlist[i].d]-=g;
            Yn[netlist[i].b][netlist[i].c]-=g;
        }
        else if (tipo=='I') {
            g=netlist[i].valor;
            Yn[netlist[i].a][numVariables+1]-=g;
            Yn[netlist[i].b][numVariables+1]+=g;
        }
        else if (tipo=='V') {
            Yn[netlist[i].a][netlist[i].x]+=1;
            Yn[netlist[i].b][netlist[i].x]-=1;
            Yn[netlist[i].x][netlist[i].a]-=1;
            Yn[netlist[i].x][netlist[i].b]+=1;
            Yn[netlist[i].x][numVariables+1]-=netlist[i].valor;
        }
        else if (tipo=='E') {
            g=netlist[i].valor;
            Yn[netlist[i].a][netlist[i].x]+=1;
            Yn[netlist[i].b][netlist[i].x]-=1;
            Yn[netlist[i].x][netlist[i].a]-=1;
            Yn[netlist[i].x][netlist[i].b]+=1;
            Yn[netlist[i].x][netlist[i].c]+=g;
            Yn[netlist[i].x][netlist[i].d]-=g;
        }
        else if (tipo=='F') {
            g=netlist[i].valor;
            Yn[netlist[i].a][netlist[i].x]+=g;
            Yn[netlist[i].b][netlist[i].x]-=g;
            Yn[netlist[i].c][netlist[i].x]+=1;
            Yn[netlist[i].d][netlist[i].x]-=1;
            Yn[netlist[i].x][netlist[i].c]-=1;
            Yn[netlist[i].x][netlist[i].d]+=1;
        }
        else if (tipo=='H') {
            g=netlist[i].valor;
            Yn[netlist[i].a][netlist[i].y]+=1;
            Yn[netlist[i].b][netlist[i].y]-=1;
            Yn[netlist[i].c][netlist[i].x]+=1;
            Yn[netlist[i].d][netlist[i].x]-=1;
            Yn[netlist[i].y][netlist[i].a]-=1;
            Yn[netlist[i].y][netlist[i].b]+=1;
            Yn[netlist[i].x][netlist[i].c]-=1;
            Yn[netlist[i].x][netlist[i].d]+=1;
            Yn[netlist[i].y][netlist[i].x]+=g;
        }
        else if (tipo=='O') {
            Yn[netlist[i].a][netlist[i].x]+=1;
            Yn[netlist[i].b][netlist[i].x]-=1;
            Yn[netlist[i].x][netlist[i].c]+=1;
            Yn[netlist[i].x][netlist[i].d]-=1;
        }

#ifdef DEBUG
        /* Opcional: Mostra o sistema apos a montagem da estampa */
        cout << "Sistema apos a estampa de " << netlist[i].name << endl;
        print(numVariables, Yn);
#endif

    }
}

void printSolution(int numVariables, int numNodes, double Yn[MAX_NODES+1][MAX_NODES+2], vector<string> lista){
    string txt;
    cout << "Solucao:" << endl;
    txt = "Tensao";
    for (int i=1; i<=numVariables; i++) {
        if (i==numNodes+1)
            txt = "Corrente";
        cout << txt << " " << lista[i] << ": " << Yn[i][numVariables+1] << endl;
    }
}
<commit_msg>Translates printSummary function<commit_after>#include "consts.h"
#include "circuits/element.h"
#include "circuits/element.h"
#include <iostream>
#include <vector>
#include <string>

using namespace std;

void printSummary(int numNodes, int numVariables, int numElements){
    /* Monta o sistema nodal modificado */
    cout << "Total nodes: " << numNodes << endl;
    cout << "Total variables: " << numVariables << endl;
    cout << "Total elements: " << numElements << endl;
    cout << endl;
}

void applyStamps(int numElements, int numVariables, vector<Element> netlist, double Yn[MAX_NODES+1][MAX_NODES+2]){
    double g;
    char tipo;
    for (int i=1; i<=numElements; i++) {
        tipo=netlist[i].getType();
        if (tipo=='R') {
            g=1/netlist[i].valor;
            Yn[netlist[i].a][netlist[i].a]+=g;
            Yn[netlist[i].b][netlist[i].b]+=g;
            Yn[netlist[i].a][netlist[i].b]-=g;
            Yn[netlist[i].b][netlist[i].a]-=g;
        }
        else if (tipo=='G') {
            g=netlist[i].valor;
            Yn[netlist[i].a][netlist[i].c]+=g;
            Yn[netlist[i].b][netlist[i].d]+=g;
            Yn[netlist[i].a][netlist[i].d]-=g;
            Yn[netlist[i].b][netlist[i].c]-=g;
        }
        else if (tipo=='I') {
            g=netlist[i].valor;
            Yn[netlist[i].a][numVariables+1]-=g;
            Yn[netlist[i].b][numVariables+1]+=g;
        }
        else if (tipo=='V') {
            Yn[netlist[i].a][netlist[i].x]+=1;
            Yn[netlist[i].b][netlist[i].x]-=1;
            Yn[netlist[i].x][netlist[i].a]-=1;
            Yn[netlist[i].x][netlist[i].b]+=1;
            Yn[netlist[i].x][numVariables+1]-=netlist[i].valor;
        }
        else if (tipo=='E') {
            g=netlist[i].valor;
            Yn[netlist[i].a][netlist[i].x]+=1;
            Yn[netlist[i].b][netlist[i].x]-=1;
            Yn[netlist[i].x][netlist[i].a]-=1;
            Yn[netlist[i].x][netlist[i].b]+=1;
            Yn[netlist[i].x][netlist[i].c]+=g;
            Yn[netlist[i].x][netlist[i].d]-=g;
        }
        else if (tipo=='F') {
            g=netlist[i].valor;
            Yn[netlist[i].a][netlist[i].x]+=g;
            Yn[netlist[i].b][netlist[i].x]-=g;
            Yn[netlist[i].c][netlist[i].x]+=1;
            Yn[netlist[i].d][netlist[i].x]-=1;
            Yn[netlist[i].x][netlist[i].c]-=1;
            Yn[netlist[i].x][netlist[i].d]+=1;
        }
        else if (tipo=='H') {
            g=netlist[i].valor;
            Yn[netlist[i].a][netlist[i].y]+=1;
            Yn[netlist[i].b][netlist[i].y]-=1;
            Yn[netlist[i].c][netlist[i].x]+=1;
            Yn[netlist[i].d][netlist[i].x]-=1;
            Yn[netlist[i].y][netlist[i].a]-=1;
            Yn[netlist[i].y][netlist[i].b]+=1;
            Yn[netlist[i].x][netlist[i].c]-=1;
            Yn[netlist[i].x][netlist[i].d]+=1;
            Yn[netlist[i].y][netlist[i].x]+=g;
        }
        else if (tipo=='O') {
            Yn[netlist[i].a][netlist[i].x]+=1;
            Yn[netlist[i].b][netlist[i].x]-=1;
            Yn[netlist[i].x][netlist[i].c]+=1;
            Yn[netlist[i].x][netlist[i].d]-=1;
        }

#ifdef DEBUG
        /* Opcional: Mostra o sistema apos a montagem da estampa */
        cout << "Sistema apos a estampa de " << netlist[i].name << endl;
        print(numVariables, Yn);
#endif

    }
}

void printSolution(int numVariables, int numNodes, double Yn[MAX_NODES+1][MAX_NODES+2], vector<string> lista){
    string txt;
    cout << "Solucao:" << endl;
    txt = "Tensao";
    for (int i=1; i<=numVariables; i++) {
        if (i==numNodes+1)
            txt = "Corrente";
        cout << txt << " " << lista[i] << ": " << Yn[i][numVariables+1] << endl;
    }
}
<|endoftext|>


<commit_before>/************************************************************************
  This file is part of Freekick.

  Freekick is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  Freekick is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU Affero General Public License for more details.

  You should have received a copy of the GNU Affero General Public License
  along with Freekick.  If not, see <http://www.gnu.org/licenses/>.

  Copyright Antti Salonen, 2008
**************************************************************************/

#include <cstdlib>

#include <iostream>
#include <exception>
#include <fstream>
#include <csignal>

#include <boost/thread/thread.hpp>
#include <boost/bind.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/exception.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

#include "addutil/Exception.h"

#include "MatchStatus.h"
#include "Network.h"

// https://svn.boost.org/trac/boost/ticket/2156: first boost/asio.hpp (Network.h), then curses.h
#include <curses.h>

#include "messages/InitialDataRequest.h"

using namespace freekick::match;
using namespace freekick::match::client;

// TODO: make this less ugly
void run_network(Network* n)
{
    n->run();
}

namespace freekick { namespace match { namespace client { namespace curses {
static void cleanup(int sig)
{
    nl();
    endwin();
    exit(0);
}

using namespace std;

class Curses_Engine
{
public:
    Curses_Engine(boost::shared_ptr<MatchStatus> ms, Network* n)
        : m_matchstatus(ms),
          m_network(n)
    {
        initscr();
        use_default_colors();
        keypad(stdscr, TRUE);
        nonl();
        cbreak();
        noecho();
        start_color();

        init_pair(1, COLOR_GREEN,   COLOR_BLACK);
        init_pair(2, COLOR_CYAN,    COLOR_BLACK);
        init_pair(3, COLOR_WHITE,   COLOR_BLACK);

        getmaxyx (stdscr, m_max_y, m_max_x);

        set_fps(10.0f);
        m_max_pitch_x = m_max_x - 1;
        m_max_pitch_y = m_max_y - 3 - 1;
        m_pitch_width = m_matchstatus->getPitchWidth();
        m_pitch_length = m_matchstatus->getPitchLength();
        if (m_pitch_width == 0.0f) throw std::runtime_error("pitch width = 0.0f");
        if (m_pitch_length == 0.0f) throw std::runtime_error("pitch length = 0.0f");

        m_playermap_home = m_matchstatus->getPlayers(Home);
        m_playermap_away = m_matchstatus->getPlayers(Away);
    }

    void set_fps(float f)
    {
        if(f <= 0.0f) return;
        m_fps = f;
        m_frametime = 1.0f / m_fps;
        m_sleep_time = m_frametime * 1000000;
    }

    bool render()
    {
        clear();
        attrset(COLOR_PAIR(1));
        BOOST_FOREACH(boost::shared_ptr<MatchPlayer> p, m_playermap_home)
        {
            int y = pitch_x_coord_to_screen(p->getPosition().x);
            int x = pitch_z_coord_to_screen(p->getPosition().z);
            mvaddstr(y, x, p->getNumberString().c_str());
        }
        attrset(COLOR_PAIR(2));
        BOOST_FOREACH(boost::shared_ptr<MatchPlayer> p, m_playermap_away)
        {
            int y = pitch_x_coord_to_screen(p->getPosition().x);
            int x = pitch_z_coord_to_screen(p->getPosition().z);
            mvaddstr(y, x, p->getNumberString().c_str());
        }

        attrset(COLOR_PAIR(3));
        {
            int y = pitch_x_coord_to_screen(m_matchstatus->getBall()->getPosition().x);
            int x = pitch_z_coord_to_screen(m_matchstatus->getBall()->getPosition().z);
            mvaddch(y, x, 'o');
        }

        attrset(COLOR_PAIR(0));
        mvaddch(0, 0, '1');
        mvaddch(0, m_max_pitch_x, '2');
        mvaddch(m_max_pitch_y, 0, '3');
        mvaddch(m_max_pitch_y, m_max_pitch_x, '4');
        refresh();
        return true;
    }

    void render_loop()
    {
        using namespace boost::posix_time;
        bool cont = true;
        float time_left;
        while (cont)
        {
            ptime before_time(microsec_clock::local_time());
            cont = render();
            ptime after_time(microsec_clock::local_time());

            time_period diff_time(before_time, after_time);
            time_duration diff_dur = diff_time.length();
            long us_diff = diff_dur.total_microseconds();
            time_left = m_sleep_time - us_diff;
            if(time_left > 0)
                boost::this_thread::sleep(microseconds(time_left));
            else
            {
                std::cerr << "render_loop: overrun by " << -time_left << " microseconds\n";
            }
        }
    }

    void run()
    {
        render_loop();
    }

    int pitch_x_coord_to_screen(float x)
    {
        return /*m_max_pitch_y -*/ ((x / m_pitch_width) * m_max_pitch_y);
    }

    int pitch_z_coord_to_screen(float z)
    {
        return (z / m_pitch_length) * m_max_pitch_x;
    }

private:
    boost::shared_ptr<MatchStatus> m_matchstatus;
    Network* m_network;

    std::vector <boost::shared_ptr<MatchPlayer> > m_playermap_home;
    std::vector <boost::shared_ptr<MatchPlayer> > m_playermap_away;

    int m_max_y;
    int m_max_x;
    float m_fps;
    float m_frametime;
    float m_sleep_time;

    int m_max_pitch_y;
    int m_max_pitch_x;
    float m_pitch_width;
    float m_pitch_length;
};

void run_game(boost::shared_ptr<MatchStatus> ms, Network* n)
{
    using namespace std;

    signal(SIGINT, cleanup);
    Curses_Engine ce(ms, n);
    ce.run();
    cleanup(0);
}
            } } } }

using namespace freekick::match::client::curses;

int main(int argc, char** argv)
{
    try
    {
        addutil::network::IP_Connection conn;
        conn.ip_address = "127.0.0.1";
        conn.port = "32105";
        Network* network;
        boost::shared_ptr<MatchStatus> status;
        std::cerr << "Freekick Curses client starting" << std::endl;
        try
        {
            network = new Network(conn, false);
            boost::thread network_thread(boost::bind(&run_network, network));
            boost::this_thread::sleep(boost::posix_time::milliseconds(1000));  // TODO: make timeouts configurable?
            if(!network->is_connected())
            {
                std::cerr << "Network::Network: timeout while connecting";
                return 1;
            }
            network->sendMessage(freekick::match::messages::InitialDataRequest());
            boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
            status.reset(network->getMatchStatus());
            if(status.get() == 0)
            {
                std::string err("Network::Network: no match status created.\n");
                std::cerr << err;
                return 1;
            }
            else std::cerr << "Network::Network: Success.\n";
        }
        catch(...)
        {
            std::cerr << "Network connection failed; exiting.\n";
            return 1;
        }
        if(status.get() == 0) { std::cerr << "Received invalid match status?\n"; return 1; }

        run_game(status, network);
        std::cout << "Shutting down client.\n";
        cleanup(0);

        delete network;
    }
    catch (boost::exception& e)
    {
	addutil::output_boost_exception(e);
    }
    catch (std::exception& e)
    {
        std::cerr << "A std::exception has occurred: " << e.what() << std::endl;
    }
    catch (...)
    {
        std::cerr << "Unknown exception has occurred." << std::endl;
    }
    cleanup(0);

    return 0;
}
<commit_msg>ncurses: minor update<commit_after>/************************************************************************
  This file is part of Freekick.

  Freekick is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  Freekick is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU Affero General Public License for more details.

  You should have received a copy of the GNU Affero General Public License
  along with Freekick.  If not, see <http://www.gnu.org/licenses/>.

  Copyright Antti Salonen, 2008
**************************************************************************/

#include <cstdlib>

#include <iostream>
#include <exception>
#include <fstream>
#include <csignal>

#include <boost/thread/thread.hpp>
#include <boost/bind.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/exception.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

#include "addutil/Exception.h"

#include "MatchStatus.h"
#include "Network.h"

// https://svn.boost.org/trac/boost/ticket/2156: first boost/asio.hpp (Network.h), then curses.h
#include <curses.h>

#include "messages/InitialDataRequest.h"

using namespace freekick::match;
using namespace freekick::match::client;

// TODO: make this less ugly
void run_network(Network* n)
{
    n->run();
}

namespace freekick { namespace match { namespace client { namespace curses {
static void cleanup(int sig)
{
    nl();
    endwin();
    exit(0);
}

using namespace std;

class Curses_Engine
{
public:
    Curses_Engine(boost::shared_ptr<MatchStatus> ms, Network* n)
        : m_matchstatus(ms),
          m_network(n)
    {
        initscr();
        use_default_colors();
        keypad(stdscr, TRUE);
        nonl();
        cbreak();
        noecho();
        start_color();

        init_pair(1, COLOR_GREEN,   COLOR_BLACK);
        init_pair(2, COLOR_CYAN,    COLOR_BLACK);
        init_pair(3, COLOR_WHITE,   COLOR_BLACK);

        getmaxyx (stdscr, m_max_y, m_max_x);

        set_fps(10.0f);
        m_max_pitch_x = m_max_x - 1;
        m_max_pitch_y = m_max_y - 3 - 1;
        m_pitch_width = m_matchstatus->getPitchWidth();
        m_pitch_length = m_matchstatus->getPitchLength();
        if (m_pitch_width == 0.0f) throw std::runtime_error("pitch width = 0.0f");
        if (m_pitch_length == 0.0f) throw std::runtime_error("pitch length = 0.0f");

        m_playermap_home = m_matchstatus->getPlayers(Home);
        m_playermap_away = m_matchstatus->getPlayers(Away);
    }

    void set_fps(float f)
    {
        if(f <= 0.0f) return;
        m_fps = f;
        m_frametime = 1.0f / m_fps;
        m_sleep_time = m_frametime * 1000000;
    }

    bool render()
    {
        clear();
        attrset(COLOR_PAIR(1));
        BOOST_FOREACH(boost::shared_ptr<MatchPlayer> p, m_playermap_home)
        {
            int y = pitch_x_coord_to_screen(p->getPosition().x);
            int x = pitch_z_coord_to_screen(p->getPosition().z);
            mvaddstr(y, x, p->getNumberString().c_str());
        }
        attrset(COLOR_PAIR(2));
        BOOST_FOREACH(boost::shared_ptr<MatchPlayer> p, m_playermap_away)
        {
            int y = pitch_x_coord_to_screen(p->getPosition().x);
            int x = pitch_z_coord_to_screen(p->getPosition().z);
            mvaddstr(y, x, p->getNumberString().c_str());
        }

        attrset(COLOR_PAIR(3));
        {
            int y = pitch_x_coord_to_screen(m_matchstatus->getBall()->getPosition().x);
            int x = pitch_z_coord_to_screen(m_matchstatus->getBall()->getPosition().z);
            mvaddch(y, x, 'o');
        }

        attrset(COLOR_PAIR(0));
        mvaddch(0, 0, '1');
        mvaddch(0, m_max_pitch_x, '2');
        mvaddch(m_max_pitch_y, 0, '3');
        mvaddch(m_max_pitch_y, m_max_pitch_x, '4');
        refresh();
        return true;
    }

    void render_loop()
    {
        using namespace boost::posix_time;
        bool cont = true;
        float time_left;
        while (cont)
        {
            ptime before_time(microsec_clock::local_time());
            cont = render();
            ptime after_time(microsec_clock::local_time());

            time_period diff_time(before_time, after_time);
            time_duration diff_dur = diff_time.length();
            long us_diff = diff_dur.total_microseconds();
            time_left = m_sleep_time - us_diff;
            if(time_left > 0)
                boost::this_thread::sleep(microseconds(time_left));
            else
            {
                std::cerr << "render_loop: overrun by " << -time_left << " microseconds\n";
            }
        }
    }

    void run()
    {
        render_loop();
    }

    int pitch_x_coord_to_screen(float x)
    {
        return /*m_max_pitch_y -*/ ((x / m_pitch_width) * m_max_pitch_y);
    }

    int pitch_z_coord_to_screen(float z)
    {
        return (z / m_pitch_length) * m_max_pitch_x;
    }

private:
    boost::shared_ptr<MatchStatus> m_matchstatus;
    Network* m_network;

    std::vector <boost::shared_ptr<MatchPlayer> > m_playermap_home;
    std::vector <boost::shared_ptr<MatchPlayer> > m_playermap_away;

    int m_max_y;
    int m_max_x;
    float m_fps;
    float m_frametime;
    float m_sleep_time;

    int m_max_pitch_y;
    int m_max_pitch_x;
    float m_pitch_width;
    float m_pitch_length;
};

void run_game(boost::shared_ptr<MatchStatus> ms, Network* n)
{
    using namespace std;

    signal(SIGINT, cleanup);
    Curses_Engine ce(ms, n);
    ce.run();
    cleanup(0);
}
            } } } }

using namespace freekick::match::client::curses;

int main(int argc, char** argv)
{
    try
    {
        addutil::network::IP_Connection conn;
        conn.ip_address = "127.0.0.1";
        conn.port = "32105";
        Network* network;
        boost::shared_ptr<MatchStatus> status;
        std::cerr << "Freekick Curses client starting" << std::endl;
        try
        {
            network = new Network(conn, false);
            boost::thread network_thread(boost::bind(&run_network, network));
            boost::this_thread::sleep(boost::posix_time::milliseconds(1000));  // TODO: make timeouts configurable?
            if(!network->is_connected())
            {
                std::cerr << "Network::Network: timeout while connecting";
                return 1;
            }
            network->sendMessage(freekick::match::messages::InitialDataRequest());
            boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
            status = network->getMatchStatus();
            if(status.get() == 0)
            {
                std::string err("Network::Network: no match status created.\n");
                std::cerr << err;
                return 1;
            }
            else std::cerr << "Network::Network: Success.\n";
        }
        catch(...)
        {
            std::cerr << "Network connection failed; exiting.\n";
            return 1;
        }
        if(status.get() == 0) { std::cerr << "Received invalid match status?\n"; return 1; }

        run_game(status, network);
        std::cout << "Shutting down client.\n";
        cleanup(0);

        delete network;
    }
    catch (boost::exception& e)
    {
	addutil::output_boost_exception(e);
    }
    catch (std::exception& e)
    {
        std::cerr << "A std::exception has occurred: " << e.what() << std::endl;
    }
    catch (...)
    {
        std::cerr << "Unknown exception has occurred." << std::endl;
    }
    cleanup(0);

    return 0;
}
<|endoftext|>


<commit_before>/** @copyright
 * Copyright (c) 2017 Stuart W Baker
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are  permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * @file MCP2515Can.cxx
 * This file implements the CAN driver for the MCP2515 CAN Controller.
 *
 * @author Stuart W. Baker
 * @date 3 January 2017
 */

#include "MCP2515Can.hxx"

#include <fcntl.h>

const MCP2515Can::MCP2515Baud MCP2515Can::baudTable[] =
{
    /* 20 MHz clock source
     * TQ = (2 * BRP) / freq = (2 * 4) / 20 MHz = 400 nsec
     * Baud = 125 kHz
     * bit time = 1 / 125 kHz = 8 usec = 20 TQ
     * SyncSeg = 1 TQ
     * PropSeg = 7 TQ
     * PS1 = 8 TQ
     * PS2 = 4 TQ
     * sample time = (1 TQ + 7 TQ + 8 TQ) / 20 TQ = 80%
     * SJW = PS2 - 1 = 4 - 1 = 3
     * SJW = 3 * 400 nsec = 1.2 usec
     */
    {20000000, 125000, {(4 - 1), (2 - 1)},
                       {(7 - 1), (8 - 1), 0, 1},
                       {(4 - 1), 0, 0}},
    /* 8 MHz clock source
     * TQ = (2 * BRP) / freq = (2 * 2) / 8 MHz = 500 nsec
     * Baud = 125 kHz
     * bit time = 1 / 125 kHz = 8 usec = 16 TQ
     * SyncSeg = 1 TQ
     * PropSeg = 4 TQ
     * PS1 = 8 TQ
     * PS2 = 3 TQ
     * sample time = (1 TQ + 4 TQ + 8 TQ) / 16 TQ = 81.25%
     * SJW = PS2 - 1 = 3 - 1 = 2
     * SJW = 2 * 500 nsec = 1 usec
     */
    {8000000, 125000, {(2 - 1), (2 - 1)},
                      {(4 - 1), (8 - 1), 0, 1},
                      {(3 - 1), 0, 0}}
};

/*
 * init()
 */
void MCP2515Can::init(const char *spi_name, uint32_t freq, uint32_t baud)
{
    spi = ::open(spi_name, O_RDWR);
    HASSERT(spi >= 0);

    /* configure SPI bus settings */
    uint8_t spi_mode = SPI_MODE_0;
    uint8_t spi_bpw = 8;
    uint32_t spi_max_speed_hz = freq / 4;
    ::ioctl(spi, SPI_IOC_WR_MODE, &spi_mode);
    ::ioctl(spi, SPI_IOC_WR_BITS_PER_WORD, &spi_bpw);
    ::ioctl(spi, SPI_IOC_WR_MAX_SPEED_HZ, &spi_max_speed_hz);

    /* reset device */
    reset();

    /* wait until device is in configuration mode */
    while ((register_read(CANSTAT) & 0xE0) != 0x80);

    /* find valid timing settings for the requested frequency and buad rates */
    for (size_t i = 0; i < (sizeof(baudTable) / sizeof(baudTable[0])); ++i)
    {
        if (baudTable[i].freq == freq && baudTable[i].baud == baud)
        {
            register_write(CNF1, baudTable[i].cnf1.data);
            register_write(CNF2, baudTable[i].cnf2.data);
            register_write(CNF3, baudTable[i].cnf3.data);

            /* setup RX Buf 0 and 1 to receive any message */
            register_write(RXB0CTRL, 0x60);
            register_write(RXB1CTRL, 0x60);

            /* enable error and receive interrupts */
            register_write(CANINTE, MERR | ERRI | RX1I | RX0I);

            /* put the device into normal operation mode */
            register_write(CANCTRL, 0x00);

            /* wait until device is in normal mode */
            while ((register_read(CANSTAT) & 0xE0) != 0x00);

            return;
        }
    }

    /* unsupported frequency */
    HASSERT(0);
}

/*
 * enable()
 */
void MCP2515Can::enable()
{
    /* there is a mutex lock above us, so the following sequence is atomic */
    if (!is_created())
    {
        /* start the thread */
        /** @todo make this the highest possible thread priority */
        start(name, 0, 1024);
    }

    /* reset device */
    //reset();

    interrupt_enable();
}

/*
 * disable()
 */
void MCP2515Can::disable()
{
    interrupt_disable();

    register_write(TXB0CTRL, 0x00);
    register_write(TXB1CTRL, 0x00);
    portENTER_CRITICAL();
    /* flush out any transmit data in the pipleline */
    txBuf->flush();
    txPending = 0;
    portEXIT_CRITICAL();
}

/*
 * tx_msg_locked()
 */
void MCP2515Can::tx_msg_locked()
{
    /* the node lock_ will be locked by the caller */
    if (txPending < 3)
    {
        struct can_frame *can_frame;

        /* find an empty buffer */
        int index = 0;
        if (txPending & 0x1)
        {
            /* buffer 0 already in use */
            index = 1;
        }

        portENTER_CRITICAL();
        if (txBuf->data_read_pointer(&can_frame))
        {
            Buffer tx_buf(can_frame);
            txBuf->consume(1);
            txBuf->signal_condition();
            portEXIT_CRITICAL();

            txPending |= (0x1 << index);

            /* bump up priority of the other buffer so it will transmit first
             * if it is pending
             */
            bit_modify(index == 0 ? TXB1CTRL : TXB0CTRL, 0x01, 0x03);
            /* load the tranmsit buffer */
            buffer_write(index, tx_buf.get_payload());
            /* request to send at lowest priority */
            bit_modify(index == 0 ? TXB0CTRL : TXB1CTRL, 0x08, 0x0B);
            /* enable transmit interrupt */
            bit_modify(CANINTE, TX0I << index, TX0I << index);
        }
        else
        {
            portEXIT_CRITICAL();
        }
    }
}

/*
 * rx_msg()
 */
void MCP2515Can::rx_msg(int index)
{
    Buffer rx_buf;
    buffer_read(index, rx_buf.get_payload());
    struct can_frame *can_frame;

    portENTER_CRITICAL();
    if (rxBuf->data_write_pointer(&can_frame))
    {
        rx_buf.build_struct_can_frame(can_frame);
        rxBuf->advance(1);
        ++numReceivedPackets_;
        rxBuf->signal_condition();
    }
    else
    {
        /* receive overrun occured */
        ++overrunCount;
    }
    portEXIT_CRITICAL();
}

/*
 * entry()
 */
void *MCP2515Can::entry()
{
    for ( ; /* forever */ ; )
    {
#if MCP2515_DEBUG
        int result = sem.timedwait(SEC_TO_NSEC(1));
        lock_.lock();

        if (result != 0)
        {
            spi_ioc_transfer xfer[2];
            memset(xfer, 0, sizeof(xfer));
            uint8_t wr_data[2] = {READ, 0};
            xfer[0].tx_buf = (unsigned long)wr_data;
            xfer[0].len = sizeof(wr_data);
            xfer[1].rx_buf = (unsigned long)regs;
            xfer[1].len = sizeof(regs);
            xfer[1].cs_change = 1;
            ::ioctl(spi, SPI_IOC_MESSAGE(2), xfer);
            lock_.unlock();
            continue;
        }
#else
        sem.wait();
        lock_.lock();
#endif
        /* read status flags */
        uint8_t canintf = register_read(CANINTF);

        if (canintf & MERR)
        {
            /* message error interrupt active */
        }
        if (canintf & ERRI)
        {
            /* error interrupt active */
            register_write(TXB0CTRL, 0x00);
            register_write(TXB1CTRL, 0x00);

            portENTER_CRITICAL();
            ++softErrorCount;
            /* flush out any transmit data in the pipleline */
            txBuf->flush();
            txBuf->signal_condition();
            portEXIT_CRITICAL();

            txPending = 0;
        }
        if (canintf & RX0I)
        {
            /* receive interrupt active */
            rx_msg(0);
        }

        if (txPending)
        {
            /* transmit interrupt active and transmission complete */
            if (canintf & TX0I)
            {
                txPending &= ~0x1;
                bit_modify(CANINTE, 0, TX0I);
                bit_modify(CANINTF, 0, TX0I);
                ++numTransmittedPackets_;
            }
            if (canintf & TX1I)
            {
                txPending &= ~0x2;
                bit_modify(CANINTE, 0, TX1I);
                bit_modify(CANINTF, 0, TX1I);
                ++numTransmittedPackets_;
            }

            tx_msg_locked();
        }

        /* Refresh status flags just in case RX1 buffer became active
         * before we could finish reading out RX0 buffer.  This ussually
         * won't happen because we should be able to respond to incoming
         * messages fast enough to only use RX0 buffer.
         */
        canintf = register_read(CANINTF);
        if (canintf & RX1I)
        {
            /* receive interrupt active */
            rx_msg(1);
        }
        lock_.unlock();

        interrupt_enable();
    }

    return NULL;
}

/*
 * interrupt_handler()
 */
void MCP2515Can::interrupt_handler()
{
    int woken = false;
    interrupt_disable();
    sem.post_from_isr(&woken);
    os_isr_exit_yield_test(woken);
}
<commit_msg>Optimize CAN timing parameters for oscillator tolerance while still meeting the CAN bus length requirements.<commit_after>/** @copyright
 * Copyright (c) 2017 Stuart W Baker
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are  permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * @file MCP2515Can.cxx
 * This file implements the CAN driver for the MCP2515 CAN Controller.
 *
 * @author Stuart W. Baker
 * @date 3 January 2017
 */

#include "MCP2515Can.hxx"

#include <fcntl.h>

const MCP2515Can::MCP2515Baud MCP2515Can::baudTable[] =
{
    /* 20 MHz clock source
     * TQ = (2 * BRP) / freq = (2 * 5) / 20 MHz = 500 nsec
     * Baud = 125 kHz
     * bit time = 1 / 125 kHz = 8 usec = 16 TQ
     * SyncSeg = 1 TQ
     * PropSeg = 7 TQ
     * PS1 = 4 TQ
     * PS2 = 4 TQ
     * sample time = (1 TQ + 7 TQ + 4 TQ) / 16 TQ = 75%
     * SJW = PS2 - 1 = 4 - 1 = 3
     * SJW = 3 * 500 nsec = 1.5 usec
     *
     * Oscillator Tolerance:
     *     4 / (2 * ((13 * 16) - 4)) = 0.980%
     *     3 / (20 * 16) = 0.938%
     *     = 0.938%
     */
    {20000000, 125000, {(5 - 1), (3 - 1)},
                       {(7 - 1), (4 - 1), 0, 1},
                       {(4 - 1), 0, 0}},
    /* 8 MHz clock source
     * TQ = (2 * BRP) / freq = (2 * 2) / 8 MHz = 500 nsec
     * Baud = 125 kHz
     * bit time = 1 / 125 kHz = 8 usec = 16 TQ
     * SyncSeg = 1 TQ
     * PropSeg = 7 TQ
     * PS1 = 4 TQ
     * PS2 = 4 TQ
     * sample time = (1 TQ + 7 TQ + 4 TQ) / 16 TQ = 75%
     * SJW = PS2 - 1 = 4 - 1 = 3
     * SJW = 3 * 500 nsec = 1.5 usec
     *
     * Oscillator Tolerance:
     *     4 / (2 * ((13 * 16) - 4)) = 0.980%
     *     3 / (20 * 16) = 0.938%
     *     = 0.938%
     */
    {8000000, 125000, {(2 - 1), (3 - 1)},
                      {(7 - 1), (4 - 1), 0, 1},
                      {(4 - 1), 0, 0}}
};

/*
 * init()
 */
void MCP2515Can::init(const char *spi_name, uint32_t freq, uint32_t baud)
{
    spi = ::open(spi_name, O_RDWR);
    HASSERT(spi >= 0);

    /* configure SPI bus settings */
    uint8_t spi_mode = SPI_MODE_0;
    uint8_t spi_bpw = 8;
    uint32_t spi_max_speed_hz = freq / 4;
    ::ioctl(spi, SPI_IOC_WR_MODE, &spi_mode);
    ::ioctl(spi, SPI_IOC_WR_BITS_PER_WORD, &spi_bpw);
    ::ioctl(spi, SPI_IOC_WR_MAX_SPEED_HZ, &spi_max_speed_hz);

    /* reset device */
    reset();

    /* wait until device is in configuration mode */
    while ((register_read(CANSTAT) & 0xE0) != 0x80);

    /* find valid timing settings for the requested frequency and buad rates */
    for (size_t i = 0; i < (sizeof(baudTable) / sizeof(baudTable[0])); ++i)
    {
        if (baudTable[i].freq == freq && baudTable[i].baud == baud)
        {
            register_write(CNF1, baudTable[i].cnf1.data);
            register_write(CNF2, baudTable[i].cnf2.data);
            register_write(CNF3, baudTable[i].cnf3.data);

            /* setup RX Buf 0 and 1 to receive any message */
            register_write(RXB0CTRL, 0x60);
            register_write(RXB1CTRL, 0x60);

            /* enable error and receive interrupts */
            register_write(CANINTE, MERR | ERRI | RX1I | RX0I);

            /* put the device into normal operation mode */
            register_write(CANCTRL, 0x00);

            /* wait until device is in normal mode */
            while ((register_read(CANSTAT) & 0xE0) != 0x00);

            return;
        }
    }

    /* unsupported frequency */
    HASSERT(0);
}

/*
 * enable()
 */
void MCP2515Can::enable()
{
    /* there is a mutex lock above us, so the following sequence is atomic */
    if (!is_created())
    {
        /* start the thread */
        /** @todo make this the highest possible thread priority */
        start(name, 0, 1024);
    }

    /* reset device */
    //reset();

    interrupt_enable();
}

/*
 * disable()
 */
void MCP2515Can::disable()
{
    interrupt_disable();

    register_write(TXB0CTRL, 0x00);
    register_write(TXB1CTRL, 0x00);
    portENTER_CRITICAL();
    /* flush out any transmit data in the pipleline */
    txBuf->flush();
    txPending = 0;
    portEXIT_CRITICAL();
}

/*
 * tx_msg_locked()
 */
void MCP2515Can::tx_msg_locked()
{
    /* the node lock_ will be locked by the caller */
    if (txPending < 3)
    {
        struct can_frame *can_frame;

        /* find an empty buffer */
        int index = 0;
        if (txPending & 0x1)
        {
            /* buffer 0 already in use */
            index = 1;
        }

        portENTER_CRITICAL();
        if (txBuf->data_read_pointer(&can_frame))
        {
            Buffer tx_buf(can_frame);
            txBuf->consume(1);
            txBuf->signal_condition();
            portEXIT_CRITICAL();

            txPending |= (0x1 << index);

            /* bump up priority of the other buffer so it will transmit first
             * if it is pending
             */
            bit_modify(index == 0 ? TXB1CTRL : TXB0CTRL, 0x01, 0x03);
            /* load the tranmsit buffer */
            buffer_write(index, tx_buf.get_payload());
            /* request to send at lowest priority */
            bit_modify(index == 0 ? TXB0CTRL : TXB1CTRL, 0x08, 0x0B);
            /* enable transmit interrupt */
            bit_modify(CANINTE, TX0I << index, TX0I << index);
        }
        else
        {
            portEXIT_CRITICAL();
        }
    }
}

/*
 * rx_msg()
 */
void MCP2515Can::rx_msg(int index)
{
    Buffer rx_buf;
    buffer_read(index, rx_buf.get_payload());
    struct can_frame *can_frame;

    portENTER_CRITICAL();
    if (rxBuf->data_write_pointer(&can_frame))
    {
        rx_buf.build_struct_can_frame(can_frame);
        rxBuf->advance(1);
        ++numReceivedPackets_;
        rxBuf->signal_condition();
    }
    else
    {
        /* receive overrun occured */
        ++overrunCount;
    }
    portEXIT_CRITICAL();
}

/*
 * entry()
 */
void *MCP2515Can::entry()
{
    for ( ; /* forever */ ; )
    {
#if MCP2515_DEBUG
        int result = sem.timedwait(SEC_TO_NSEC(1));
        lock_.lock();

        if (result != 0)
        {
            spi_ioc_transfer xfer[2];
            memset(xfer, 0, sizeof(xfer));
            uint8_t wr_data[2] = {READ, 0};
            xfer[0].tx_buf = (unsigned long)wr_data;
            xfer[0].len = sizeof(wr_data);
            xfer[1].rx_buf = (unsigned long)regs;
            xfer[1].len = sizeof(regs);
            xfer[1].cs_change = 1;
            ::ioctl(spi, SPI_IOC_MESSAGE(2), xfer);
            lock_.unlock();
            continue;
        }
#else
        sem.wait();
        lock_.lock();
#endif
        /* read status flags */
        uint8_t canintf = register_read(CANINTF);

        if (canintf & MERR)
        {
            /* message error interrupt active */
        }
        if (canintf & ERRI)
        {
            /* error interrupt active */
            register_write(TXB0CTRL, 0x00);
            register_write(TXB1CTRL, 0x00);

            portENTER_CRITICAL();
            ++softErrorCount;
            /* flush out any transmit data in the pipleline */
            txBuf->flush();
            txBuf->signal_condition();
            portEXIT_CRITICAL();

            txPending = 0;
        }
        if (canintf & RX0I)
        {
            /* receive interrupt active */
            rx_msg(0);
        }

        if (txPending)
        {
            /* transmit interrupt active and transmission complete */
            if (canintf & TX0I)
            {
                txPending &= ~0x1;
                bit_modify(CANINTE, 0, TX0I);
                bit_modify(CANINTF, 0, TX0I);
                ++numTransmittedPackets_;
            }
            if (canintf & TX1I)
            {
                txPending &= ~0x2;
                bit_modify(CANINTE, 0, TX1I);
                bit_modify(CANINTF, 0, TX1I);
                ++numTransmittedPackets_;
            }

            tx_msg_locked();
        }

        /* Refresh status flags just in case RX1 buffer became active
         * before we could finish reading out RX0 buffer.  This ussually
         * won't happen because we should be able to respond to incoming
         * messages fast enough to only use RX0 buffer.
         */
        canintf = register_read(CANINTF);
        if (canintf & RX1I)
        {
            /* receive interrupt active */
            rx_msg(1);
        }
        lock_.unlock();

        interrupt_enable();
    }

    return NULL;
}

/*
 * interrupt_handler()
 */
void MCP2515Can::interrupt_handler()
{
    int woken = false;
    interrupt_disable();
    sem.post_from_isr(&woken);
    os_isr_exit_yield_test(woken);
}
<|endoftext|>