<commit_before>
#include "connection.hpp"

Connection::Connection() :
mode(SERVER)
{
    socket.bind(GAME_PORT);
    socket.setBlocking(false);
}

Connection::Connection(Mode mode) :
mode(mode)
{
    socket.bind(GAME_PORT);
    socket.setBlocking(false);
}

Connection::~Connection()
{
}

void Connection::update(sf::Time dt)
{
    if(mode == SERVER)
    {
        // check if clients have timed out
        std::list<sf::Uint32> disconnectQueue;
        for(auto iter = elapsedTimeMap.begin(); iter != elapsedTimeMap.end(); ++iter)
        {
            if(iter->second.getElapsedTime().asMilliseconds() >= CONNECTION_TIMEOUT_MILLISECONDS)
            {
                disconnectQueue.push_front(iter->first);
            }
        }

        for(auto iter = disconnectQueue.begin(); iter != disconnectQueue.end(); ++iter)
        {
#ifndef NDEBUG
            std::cout << "Disconnected " << sf::IpAddress(*iter).toString() << '\n';
#endif
            unregisterConnection(*iter);
        }

        // heartbeat packet
        for(auto tIter = heartbeatTimeMap.begin(); tIter != heartbeatTimeMap.end(); ++tIter)
        {
            tIter->second += dt;
            if(tIter->second.asMilliseconds() >= HEARTBEAT_MILLISECONDS)
            {
                tIter->second = sf::milliseconds(0);
                heartbeat(tIter->first);
            }
        }

        // send packet
        if(!sendPacketQueue.empty())
        {
            auto pInfo = sendPacketQueue.back();
            sendPacketQueue.pop_back();

            heartbeatTimeMap[pInfo.address] = sf::milliseconds(0);

            sentPackets[pInfo.address].push_front(PacketInfo(pInfo.packet, pInfo.ID));
            socket.send(pInfo.packet, sf::IpAddress(pInfo.address), GAME_PORT);
            checkSentPacketsSize(pInfo.address);
        }
        //TODO flow control

        // receive packet
        sf::Packet packet;
        sf::IpAddress address;
        unsigned short remotePort;
        sf::Socket::Status status = socket.receive(packet, address, remotePort);
        if(status == sf::Socket::Done)
        {
            sf::Uint32 protocolID;
            if(!(packet >> protocolID))
                return;

            // check protocol ID
            if(protocolID != GAME_PROTOCOL_ID)
                return;

            sf::Uint32 ID;
            sf::Uint32 sequence;
            sf::Uint32 ack;
            sf::Uint32 ackBitfield;

            if(!(packet >> ID) || !(packet >> sequence) || !(packet >> ack) || !(packet >> ackBitfield))
                return;

            if(ID == network::CONNECT)
            {
                if(IDMap.find(address.toInteger()) == IDMap.end())
                {
#ifndef NDEBUG
                    std::cout << "SERVER: Establishing new connection with " << address.toString() << '\n';
#endif
                    // Establish connection
                    registerConnection(address.toInteger());
                    sf::Packet newPacket;
                    sf::Uint32 sequenceID;
                    preparePacket(newPacket, sequenceID, address);
                    sendPacket(newPacket, sequenceID, address);
                }
                return;
            }
            else if(IDMap.find(address.toInteger()) == IDMap.end())
            {
                // Unknown client not attemping to connect, ignoring
                return;
            }
            else if(ID != IDMap[address.toInteger()])
            {
                // ID and address doesn't match, ignoring
                return;
            }

            // packet is valid
#ifndef NDEBUG
            std::cout << "Valid packet received from " << address.toString() << '\n';
#endif

            sf::Uint32 clientAddress = address.toInteger();

            lookupRtt(clientAddress, ack);

            elapsedTimeMap[clientAddress].restart();
            checkSentPackets(ack, ackBitfield, clientAddress);

            sf::Uint32 diff = 0;
            if(sequence > rSequenceMap[clientAddress])
            {
                diff = sequence - rSequenceMap[clientAddress];
                if(diff <= 0x7FFFFFFF)
                {
                    // sequence is more recent
                    rSequenceMap[clientAddress] = sequence;
                    shiftBitfield(address, diff);
                }
                else
                {
                    // sequence is older packet id, diff requires recalc
                    diff = sequence + (0xFFFFFFFF - rSequenceMap[clientAddress]) + 1;

                    if((ackBitfieldMap[clientAddress] & (0x100000000 >> diff)) != 0x0)
                    {
                        // already received packet
                        return;
                    }
                    ackBitfieldMap[clientAddress] |= (0x100000000 >> diff);
                }
            }
            else if(rSequenceMap[clientAddress] > sequence)
            {
                diff = rSequenceMap[clientAddress] - sequence;
                if(diff > 0x7FFFFFFF)
                {
                    // sequence is more recent, diff requires recalc
                    diff = sequence + (0xFFFFFFFF - rSequenceMap[clientAddress]) + 1;

                    rSequenceMap[clientAddress] = sequence;
                    shiftBitfield(address, diff);
                }
                else
                {
                    // sequence is older packet id
                    if((ackBitfieldMap[clientAddress] & (0x100000000 >> diff)) != 0x0)
                    {
                        // already received packet
                        return;
                    }
                    ackBitfieldMap[clientAddress] |= (0x100000000 >> diff);
                }
            }
            else
            {
                // duplicate packet, ignoring
                return;
            }

            receivedPacket(packet);
        }
    } // if(mode == SERVER)
    else if(mode == CLIENT)
    {
        // connection established
        if(IDMap.size() > 0)
        {
            sf::Uint32 serverAddress = clientSentAddress.toInteger();
            // check if timed out
            sf::Time elapsedTime = elapsedTimeMap[serverAddress].getElapsedTime();
            if(elapsedTime.asMilliseconds() > CONNECTION_TIMEOUT_MILLISECONDS)
            {
                unregisterConnection(serverAddress);
                return;
            }

            // heartbeat
            heartbeatTimeMap[serverAddress] += dt;
            if(heartbeatTimeMap[serverAddress].asMilliseconds() >= HEARTBEAT_MILLISECONDS)
            {
                heartbeatTimeMap[serverAddress] = sf::milliseconds(0);
                heartbeat(serverAddress);
            }

            // send
            if(!sendPacketQueue.empty())
            {
                PacketInfo pInfo = sendPacketQueue.back();
                sendPacketQueue.pop_back();

                sentPackets[serverAddress].push_front(PacketInfo(pInfo.packet, pInfo.ID));
                socket.send(pInfo.packet, sf::IpAddress(pInfo.address), GAME_PORT);
                checkSentPacketsSize(serverAddress);
            }

            //TODO flow control

            // receive
            sf::Packet packet;
            sf::IpAddress address;
            unsigned short port;
            sf::Socket::Status status;
            status = socket.receive(packet, address, port);

            if(status == sf::Socket::Done && address == clientSentAddress)
            {
                sf::Uint32 protocolID;
                if(!(packet >> protocolID))
                    return;

                if(protocolID != GAME_PROTOCOL_ID)
                    return;

                sf::Uint32 ID;
                sf::Uint32 sequence;
                sf::Uint32 ack;
                sf::Uint32 bitfield;

                if(!(packet >> ID) || !(packet >> sequence) || !(packet >> ack) || !(packet >> bitfield))
                    return;

                if(ID != IDMap[serverAddress])
                    return;

                // packet is valid
#ifndef NDEBUG
                std::cout << "Valid packet received from " << address.toString() << '\n';
#endif

                lookupRtt(serverAddress, ack);

                elapsedTimeMap[serverAddress].restart();
                checkSentPackets(ack, bitfield, serverAddress);

                sf::Uint32 diff = 0;
                if(sequence > rSequenceMap[serverAddress])
                {
                    diff = sequence - rSequenceMap[serverAddress];
                    if(diff <= 0x7FFFFFFF)
                    {
                        // sequence is more recent
                        rSequenceMap[serverAddress] = sequence;
                        shiftBitfield(address, diff);
                    }
                    else
                    {
                        // sequence is older packet id, diff requires recalc
                        diff = sequence + (0xFFFFFFFF - rSequenceMap[serverAddress]) + 1;

                        if((ackBitfieldMap[serverAddress] & (0x100000000 >> diff)) != 0x0)
                        {
                            // already received packet
                            return;
                        }
                        ackBitfieldMap[serverAddress] |= (0x100000000 >> diff);
                    }
                }
                else if(rSequenceMap[serverAddress] > sequence)
                {
                    diff = rSequenceMap[serverAddress] - sequence;
                    if(diff > 0x7FFFFFFF)
                    {
                        // sequence is more recent, diff requires recalc
                        diff = sequence + (0xFFFFFFFF - rSequenceMap[serverAddress]) + 1;

                        rSequenceMap[serverAddress] = sequence;
                        shiftBitfield(address, diff);
                    }
                    else
                    {
                        // sequence is older packet id
                        if((ackBitfieldMap[serverAddress] & (0x100000000 >> diff)) != 0x0)
                        {
                            // already received packet
                            return;
                        }
                        ackBitfieldMap[serverAddress] |= (0x100000000 >> diff);
                    }
                }
                else
                {
                    // duplicate packet, ignoring
                    return;
                }

                receivedPacket(packet);

            }
        }
        // connection not yet established
        else
        {
            // receive
            sf::Packet packet;
            sf::IpAddress address;
            unsigned short port;
            sf::Socket::Status status;
            status = socket.receive(packet, address, port);

            if(status == sf::Socket::Done && address == clientSentAddress)
            {
                sf::Uint32 protocolID;
                if(!(packet >> protocolID))
                    return;

                if(protocolID != GAME_PROTOCOL_ID)
                    return;

                sf::Uint32 ID;
                sf::Uint32 sequence;
                sf::Uint32 ack;
                sf::Uint32 bitfield;

                if(!(packet >> ID) || !(packet >> sequence) || !(packet >> ack) || !(packet >> bitfield))
                    return;

                registerConnection(address.toInteger(), ID);
            }
        }
    } // elif(mode == CLIENT)
}

void Connection::connectToServer(sf::IpAddress address)
{
#ifndef NDEBUG
    std::cout << "CLIENT: sending connection request to server at " << address.toString() << '\n';
#endif
    sf::Packet packet;
    packet << (sf::Uint32) GAME_PROTOCOL_ID << (sf::Uint32) network::CONNECT << (sf::Uint32) 0 << (sf::Uint32) 0 << (sf::Uint32) 0xFFFFFFFF;
    socket.send(packet, address, GAME_PORT);
    clientSentAddress = address;
}

void Connection::preparePacket(sf::Packet& packet, sf::Uint32& sequenceID, sf::IpAddress address)
{
    sf::Uint32 intAddress = address.toInteger();

    auto iter = IDMap.find(intAddress);
    assert(iter == IDMap.end());

    sf::Uint32 ID = iter->second;

    sequenceID = lSequenceMap[intAddress]++;

    sf::Uint32 ack = rSequenceMap[intAddress];

    sf::Uint32 ackBitfield = ackBitfieldMap[intAddress];

    packet << (sf::Uint32) GAME_PROTOCOL_ID << ID << sequenceID << ack << ackBitfield;
}

void Connection::sendPacket(sf::Packet& packet, sf::Uint32 sequenceID, sf::IpAddress address)
{
    sendPacketQueue.push_front(PacketInfo(packet, sequenceID, address.toInteger()));
}

void Connection::receivedPacket(sf::Packet packet)
{}

void Connection::registerConnection(sf::Uint32 address, sf::Uint32 ID)
{
    heartbeatTimeMap.insert(std::make_pair(address, sf::Time()));
    elapsedTimeMap.insert(std::make_pair(address, sf::Clock()));

    if(mode == SERVER)
    {
        IDMap.insert(std::make_pair(address, generateID()));
        lSequenceMap.insert(std::make_pair(address, (sf::Uint32) 0));
    }
    else if(mode == CLIENT)
    {
        IDMap.insert(std::make_pair(address, ID));
        lSequenceMap.insert(std::make_pair(address, (sf::Uint32) 1));
    }

    rSequenceMap.insert(std::make_pair(address, (sf::Uint32) 0));

    ackBitfieldMap.insert(std::make_pair(address, (sf::Uint32) 0xFFFFFFFF));

    sentPackets.insert(std::make_pair(address, std::list<PacketInfo>()));

    rttMap.insert(std::make_pair(address, sf::Time()));
}

void Connection::unregisterConnection(sf::Uint32 address)
{
    heartbeatTimeMap.erase(address);
    elapsedTimeMap.erase(address);

    IDMap.erase(address);

    lSequenceMap.erase(address);
    rSequenceMap.erase(address);

    ackBitfieldMap.erase(address);

    sentPackets.erase(address);

    rttMap.erase(address);
}

void Connection::shiftBitfield(sf::IpAddress address, sf::Uint32 diff)
{
    ackBitfieldMap[address.toInteger()] = (ackBitfieldMap[address.toInteger()] >> diff) | (0x100000000 >> diff);
}

void Connection::checkSentPackets(sf::Uint32 ack, sf::Uint32 bitfield, sf::Uint32 address)
{
    --ack;
    for(; bitfield != 0x0; bitfield = bitfield << 1)
    {
        // if received, don't bother checking
        if((0x80000000 & bitfield) != 0x0)
        {
            --ack;
            continue;
        }

        // not received by client yet, checking if packet timed out
        for(auto iter = sentPackets[address].begin(); iter != sentPackets[address].end(); ++iter)
        {
            if(iter->ID == ack)
            {
                // timed out, adding to send queue
                if(iter->sentTime.getElapsedTime() >= sf::milliseconds(PACKET_LOST_TIMEOUT_MILLISECONDS))
                {
                    sf::Packet packetCopy = iter->packet;
                    sf::Uint32 sequenceID;
                    packetCopy >> sequenceID; // protocol ID
                    packetCopy >> sequenceID; // ID of client
                    packetCopy >> sequenceID; // sequence ID
                    sendPacket(iter->packet, sequenceID, sf::IpAddress(address));
                }
                break;
            }
        }

        --ack;
    }
}

void Connection::heartbeat(sf::Uint32 addressInteger)
{
    sf::IpAddress address(addressInteger);

    sf::Packet packet;
    sf::Uint32 sequenceID;
    preparePacket(packet, sequenceID, address);
    sendPacket(packet, sequenceID, address);
}

void Connection::lookupRtt(sf::Uint32 address, sf::Uint32 ack)
{
    for(auto iter = sentPackets[address].begin(); iter != sentPackets[address].end(); ++iter)
    {
        if(iter->ID == ack)
        {
            sf::Time time = iter->sentTime.getElapsedTime();
            if(time > rttMap[address])
            {
                rttMap[address] += (time - rttMap[address]) * 0.1f;
            }
            else
            {
                rttMap[address] += (rttMap[address] - time) * 0.1f;
            }
#ifndef NDEBUG
            std::cout << "RTT of " << sf::IpAddress(address).toString() << " = " << rttMap[address].asMilliseconds() << '\n';
#endif
            break;
        }
    }
}

void Connection::checkSentPacketsSize(sf::Uint32 address)
{
    while(sentPackets[address].size() > SENT_PACKET_LIST_MAX_SIZE)
    {
        sentPackets[address].pop_back();
    }
}

sf::Uint32 Connection::generateID()
{
    sf::Uint32 id;
    do
    {
        id = dist(rd);
    } while (network::isSpecialID(id));

    return id;
}
<commit_msg>Added debug, fix for timed out packets<commit_after>
#include "connection.hpp"

Connection::Connection() :
mode(SERVER)
{
    socket.bind(GAME_PORT);
    socket.setBlocking(false);
}

Connection::Connection(Mode mode) :
mode(mode)
{
    socket.bind(GAME_PORT);
    socket.setBlocking(false);
}

Connection::~Connection()
{
}

void Connection::update(sf::Time dt)
{
    if(mode == SERVER)
    {
        // check if clients have timed out
        std::list<sf::Uint32> disconnectQueue;
        for(auto iter = elapsedTimeMap.begin(); iter != elapsedTimeMap.end(); ++iter)
        {
            if(iter->second.getElapsedTime().asMilliseconds() >= CONNECTION_TIMEOUT_MILLISECONDS)
            {
                disconnectQueue.push_front(iter->first);
            }
        }

        for(auto iter = disconnectQueue.begin(); iter != disconnectQueue.end(); ++iter)
        {
#ifndef NDEBUG
            std::cout << "Disconnected " << sf::IpAddress(*iter).toString() << '\n';
#endif
            unregisterConnection(*iter);
        }

        // heartbeat packet
        for(auto tIter = heartbeatTimeMap.begin(); tIter != heartbeatTimeMap.end(); ++tIter)
        {
            tIter->second += dt;
            if(tIter->second.asMilliseconds() >= HEARTBEAT_MILLISECONDS)
            {
                tIter->second = sf::milliseconds(0);
                heartbeat(tIter->first);
            }
        }

        // send packet
        if(!sendPacketQueue.empty())
        {
            auto pInfo = sendPacketQueue.back();
            sendPacketQueue.pop_back();

            heartbeatTimeMap[pInfo.address] = sf::milliseconds(0);

            sentPackets[pInfo.address].push_front(PacketInfo(pInfo.packet, pInfo.ID));
            socket.send(pInfo.packet, sf::IpAddress(pInfo.address), GAME_PORT);
            checkSentPacketsSize(pInfo.address);
        }
        //TODO flow control

        // receive packet
        sf::Packet packet;
        sf::IpAddress address;
        unsigned short remotePort;
        sf::Socket::Status status = socket.receive(packet, address, remotePort);
        if(status == sf::Socket::Done)
        {
            sf::Uint32 protocolID;
            if(!(packet >> protocolID))
                return;

            // check protocol ID
            if(protocolID != GAME_PROTOCOL_ID)
                return;

            sf::Uint32 ID;
            sf::Uint32 sequence;
            sf::Uint32 ack;
            sf::Uint32 ackBitfield;

            if(!(packet >> ID) || !(packet >> sequence) || !(packet >> ack) || !(packet >> ackBitfield))
                return;

            if(ID == network::CONNECT)
            {
                if(IDMap.find(address.toInteger()) == IDMap.end())
                {
#ifndef NDEBUG
                    std::cout << "SERVER: Establishing new connection with " << address.toString() << '\n';
#endif
                    // Establish connection
                    registerConnection(address.toInteger());
                    sf::Packet newPacket;
                    sf::Uint32 sequenceID;
                    preparePacket(newPacket, sequenceID, address);
                    sendPacket(newPacket, sequenceID, address);
                }
                return;
            }
            else if(IDMap.find(address.toInteger()) == IDMap.end())
            {
                // Unknown client not attemping to connect, ignoring
                return;
            }
            else if(ID != IDMap[address.toInteger()])
            {
                // ID and address doesn't match, ignoring
                return;
            }

            // packet is valid
#ifndef NDEBUG
            std::cout << "Valid packet received from " << address.toString() << '\n';
#endif

            sf::Uint32 clientAddress = address.toInteger();

            lookupRtt(clientAddress, ack);

            elapsedTimeMap[clientAddress].restart();
            checkSentPackets(ack, ackBitfield, clientAddress);

            sf::Uint32 diff = 0;
            if(sequence > rSequenceMap[clientAddress])
            {
                diff = sequence - rSequenceMap[clientAddress];
                if(diff <= 0x7FFFFFFF)
                {
                    // sequence is more recent
                    rSequenceMap[clientAddress] = sequence;
                    shiftBitfield(address, diff);
                }
                else
                {
                    // sequence is older packet id, diff requires recalc
                    diff = sequence + (0xFFFFFFFF - rSequenceMap[clientAddress]) + 1;

                    if((ackBitfieldMap[clientAddress] & (0x100000000 >> diff)) != 0x0)
                    {
                        // already received packet
                        return;
                    }
                    ackBitfieldMap[clientAddress] |= (0x100000000 >> diff);
                }
            }
            else if(rSequenceMap[clientAddress] > sequence)
            {
                diff = rSequenceMap[clientAddress] - sequence;
                if(diff > 0x7FFFFFFF)
                {
                    // sequence is more recent, diff requires recalc
                    diff = sequence + (0xFFFFFFFF - rSequenceMap[clientAddress]) + 1;

                    rSequenceMap[clientAddress] = sequence;
                    shiftBitfield(address, diff);
                }
                else
                {
                    // sequence is older packet id
                    if((ackBitfieldMap[clientAddress] & (0x100000000 >> diff)) != 0x0)
                    {
                        // already received packet
                        return;
                    }
                    ackBitfieldMap[clientAddress] |= (0x100000000 >> diff);
                }
            }
            else
            {
                // duplicate packet, ignoring
                return;
            }

            receivedPacket(packet);
        }
    } // if(mode == SERVER)
    else if(mode == CLIENT)
    {
        // connection established
        if(IDMap.size() > 0)
        {
            sf::Uint32 serverAddress = clientSentAddress.toInteger();
            // check if timed out
            sf::Time elapsedTime = elapsedTimeMap[serverAddress].getElapsedTime();
            if(elapsedTime.asMilliseconds() > CONNECTION_TIMEOUT_MILLISECONDS)
            {
                unregisterConnection(serverAddress);
                return;
            }

            // heartbeat
            heartbeatTimeMap[serverAddress] += dt;
            if(heartbeatTimeMap[serverAddress].asMilliseconds() >= HEARTBEAT_MILLISECONDS)
            {
                heartbeatTimeMap[serverAddress] = sf::milliseconds(0);
                heartbeat(serverAddress);
            }

            // send
            if(!sendPacketQueue.empty())
            {
                PacketInfo pInfo = sendPacketQueue.back();
                sendPacketQueue.pop_back();

                sentPackets[serverAddress].push_front(PacketInfo(pInfo.packet, pInfo.ID));
                socket.send(pInfo.packet, sf::IpAddress(pInfo.address), GAME_PORT);
                checkSentPacketsSize(serverAddress);
            }

            //TODO flow control

            // receive
            sf::Packet packet;
            sf::IpAddress address;
            unsigned short port;
            sf::Socket::Status status;
            status = socket.receive(packet, address, port);

            if(status == sf::Socket::Done && address == clientSentAddress)
            {
                sf::Uint32 protocolID;
                if(!(packet >> protocolID))
                    return;

                if(protocolID != GAME_PROTOCOL_ID)
                    return;

                sf::Uint32 ID;
                sf::Uint32 sequence;
                sf::Uint32 ack;
                sf::Uint32 bitfield;

                if(!(packet >> ID) || !(packet >> sequence) || !(packet >> ack) || !(packet >> bitfield))
                    return;

                if(ID != IDMap[serverAddress])
                    return;

                // packet is valid
#ifndef NDEBUG
                std::cout << "Valid packet received from " << address.toString() << '\n';
#endif

                lookupRtt(serverAddress, ack);

                elapsedTimeMap[serverAddress].restart();
                checkSentPackets(ack, bitfield, serverAddress);

                sf::Uint32 diff = 0;
                if(sequence > rSequenceMap[serverAddress])
                {
                    diff = sequence - rSequenceMap[serverAddress];
                    if(diff <= 0x7FFFFFFF)
                    {
                        // sequence is more recent
                        rSequenceMap[serverAddress] = sequence;
                        shiftBitfield(address, diff);
                    }
                    else
                    {
                        // sequence is older packet id, diff requires recalc
                        diff = sequence + (0xFFFFFFFF - rSequenceMap[serverAddress]) + 1;

                        if((ackBitfieldMap[serverAddress] & (0x100000000 >> diff)) != 0x0)
                        {
                            // already received packet
                            return;
                        }
                        ackBitfieldMap[serverAddress] |= (0x100000000 >> diff);
                    }
                }
                else if(rSequenceMap[serverAddress] > sequence)
                {
                    diff = rSequenceMap[serverAddress] - sequence;
                    if(diff > 0x7FFFFFFF)
                    {
                        // sequence is more recent, diff requires recalc
                        diff = sequence + (0xFFFFFFFF - rSequenceMap[serverAddress]) + 1;

                        rSequenceMap[serverAddress] = sequence;
                        shiftBitfield(address, diff);
                    }
                    else
                    {
                        // sequence is older packet id
                        if((ackBitfieldMap[serverAddress] & (0x100000000 >> diff)) != 0x0)
                        {
                            // already received packet
                            return;
                        }
                        ackBitfieldMap[serverAddress] |= (0x100000000 >> diff);
                    }
                }
                else
                {
                    // duplicate packet, ignoring
                    return;
                }

                receivedPacket(packet);

            }
        }
        // connection not yet established
        else
        {
            // receive
            sf::Packet packet;
            sf::IpAddress address;
            unsigned short port;
            sf::Socket::Status status;
            status = socket.receive(packet, address, port);

            if(status == sf::Socket::Done && address == clientSentAddress)
            {
                sf::Uint32 protocolID;
                if(!(packet >> protocolID))
                    return;

                if(protocolID != GAME_PROTOCOL_ID)
                    return;

                sf::Uint32 ID;
                sf::Uint32 sequence;
                sf::Uint32 ack;
                sf::Uint32 bitfield;

                if(!(packet >> ID) || !(packet >> sequence) || !(packet >> ack) || !(packet >> bitfield))
                    return;

                registerConnection(address.toInteger(), ID);
            }
        }
    } // elif(mode == CLIENT)
}

void Connection::connectToServer(sf::IpAddress address)
{
#ifndef NDEBUG
    std::cout << "CLIENT: sending connection request to server at " << address.toString() << '\n';
#endif
    sf::Packet packet;
    packet << (sf::Uint32) GAME_PROTOCOL_ID << (sf::Uint32) network::CONNECT << (sf::Uint32) 0 << (sf::Uint32) 0 << (sf::Uint32) 0xFFFFFFFF;
    socket.send(packet, address, GAME_PORT);
    clientSentAddress = address;
}

void Connection::preparePacket(sf::Packet& packet, sf::Uint32& sequenceID, sf::IpAddress address)
{
    sf::Uint32 intAddress = address.toInteger();

    auto iter = IDMap.find(intAddress);
    assert(iter == IDMap.end());

    sf::Uint32 ID = iter->second;

    sequenceID = lSequenceMap[intAddress]++;

    sf::Uint32 ack = rSequenceMap[intAddress];

    sf::Uint32 ackBitfield = ackBitfieldMap[intAddress];

    packet << (sf::Uint32) GAME_PROTOCOL_ID << ID << sequenceID << ack << ackBitfield;
}

void Connection::sendPacket(sf::Packet& packet, sf::Uint32 sequenceID, sf::IpAddress address)
{
    sendPacketQueue.push_front(PacketInfo(packet, sequenceID, address.toInteger()));
}

void Connection::receivedPacket(sf::Packet packet)
{}

void Connection::registerConnection(sf::Uint32 address, sf::Uint32 ID)
{
    heartbeatTimeMap.insert(std::make_pair(address, sf::Time()));
    elapsedTimeMap.insert(std::make_pair(address, sf::Clock()));

    if(mode == SERVER)
    {
        IDMap.insert(std::make_pair(address, generateID()));
        lSequenceMap.insert(std::make_pair(address, (sf::Uint32) 0));
    }
    else if(mode == CLIENT)
    {
        IDMap.insert(std::make_pair(address, ID));
        lSequenceMap.insert(std::make_pair(address, (sf::Uint32) 1));
    }

    rSequenceMap.insert(std::make_pair(address, (sf::Uint32) 0));

    ackBitfieldMap.insert(std::make_pair(address, (sf::Uint32) 0xFFFFFFFF));

    sentPackets.insert(std::make_pair(address, std::list<PacketInfo>()));

    rttMap.insert(std::make_pair(address, sf::Time()));
}

void Connection::unregisterConnection(sf::Uint32 address)
{
    heartbeatTimeMap.erase(address);
    elapsedTimeMap.erase(address);

    IDMap.erase(address);

    lSequenceMap.erase(address);
    rSequenceMap.erase(address);

    ackBitfieldMap.erase(address);

    sentPackets.erase(address);

    rttMap.erase(address);
}

void Connection::shiftBitfield(sf::IpAddress address, sf::Uint32 diff)
{
    ackBitfieldMap[address.toInteger()] = (ackBitfieldMap[address.toInteger()] >> diff) | (0x100000000 >> diff);
}

void Connection::checkSentPackets(sf::Uint32 ack, sf::Uint32 bitfield, sf::Uint32 address)
{
    --ack;
    for(; bitfield != 0x0; bitfield = bitfield << 1)
    {
        // if received, don't bother checking
        if((0x80000000 & bitfield) != 0x0)
        {
            --ack;
            continue;
        }

        // not received by client yet, checking if packet timed out
        for(auto iter = sentPackets[address].begin(); iter != sentPackets[address].end(); ++iter)
        {
            if(iter->ID == ack)
            {
                // timed out, adding to send queue
                if(iter->sentTime.getElapsedTime() >= sf::milliseconds(PACKET_LOST_TIMEOUT_MILLISECONDS))
                {
#ifndef NDEBUG
                    std::cout << "Packet " << std::hex << std::showbase << ack << std::dec;
                    std::cout << " timed out\n";
#define
                    sf::Packet packetCopy = iter->packet;
                    sf::Uint32 sequenceID;
                    packetCopy >> sequenceID; // protocol ID
                    packetCopy >> sequenceID; // ID of client
                    packetCopy >> sequenceID; // sequence ID
                    sendPacket(iter->packet, sequenceID, sf::IpAddress(address));
                    iter->sentTime.restart();
                }
                break;
            }
        }

        --ack;
    }
}

void Connection::heartbeat(sf::Uint32 addressInteger)
{
    sf::IpAddress address(addressInteger);

    sf::Packet packet;
    sf::Uint32 sequenceID;
    preparePacket(packet, sequenceID, address);
    sendPacket(packet, sequenceID, address);
}

void Connection::lookupRtt(sf::Uint32 address, sf::Uint32 ack)
{
    for(auto iter = sentPackets[address].begin(); iter != sentPackets[address].end(); ++iter)
    {
        if(iter->ID == ack)
        {
            sf::Time time = iter->sentTime.getElapsedTime();
            if(time > rttMap[address])
            {
                rttMap[address] += (time - rttMap[address]) * 0.1f;
            }
            else
            {
                rttMap[address] += (rttMap[address] - time) * 0.1f;
            }
#ifndef NDEBUG
            std::cout << "RTT of " << sf::IpAddress(address).toString() << " = " << rttMap[address].asMilliseconds() << '\n';
#endif
            break;
        }
    }
}

void Connection::checkSentPacketsSize(sf::Uint32 address)
{
    while(sentPackets[address].size() > SENT_PACKET_LIST_MAX_SIZE)
    {
        sentPackets[address].pop_back();
    }
}

sf::Uint32 Connection::generateID()
{
    sf::Uint32 id;
    do
    {
        id = dist(rd);
    } while (network::isSpecialID(id));

    return id;
}
<|endoftext|>


<commit_before>#define BOOST_TEST_MODULE "test_periodic_cell_list"

#ifdef BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>
#else
#include <boost/test/included/unit_test.hpp>
#endif

#include <mjolnir/util/empty.hpp>
#include <mjolnir/core/SimulatorTraits.hpp>
#include <mjolnir/core/BoundaryCondition.hpp>
#include <mjolnir/core/PeriodicGridCellList.hpp>
#include <mjolnir/core/System.hpp>
#include <mjolnir/core/Topology.hpp>
#include <random>

template<typename T>
struct dummy_potential
{
    using real_type      = T;
    using parameter_type = mjolnir::empty_t;

    using topology_type        = mjolnir::Topology;
    using molecule_id_type     = typename topology_type::molecule_id_type;
    using connection_kind_type = typename topology_type::connection_kind_type;

    explicit dummy_potential(const real_type cutoff): cutoff_(cutoff) {}

    real_type max_cutoff_length() const noexcept {return this->cutoff_;}

    parameter_type prepare_params(std::size_t, std::size_t) const noexcept
    {
        return parameter_type{};
    }

    bool is_ignored_molecule(std::size_t, std::size_t) const {return false;}

    std::vector<std::pair<connection_kind_type, std::size_t>> ignore_within() const
    {
        return std::vector<std::pair<connection_kind_type, std::size_t>>{};
    }

    std::string name() const {return "dummy potential";}

    real_type cutoff_;
};

BOOST_AUTO_TEST_CASE(test_CellList_PeriodicBoundary)
{
    mjolnir::LoggerManager::set_default_logger("test_cell_list.log");
    using traits_type     = mjolnir::SimulatorTraits<double, mjolnir::CuboidalPeriodicBoundary>;
    using real_type       = typename traits_type::real_type;
    using boundary_type   = typename traits_type::boundary_type;
    using coordinate_type = typename traits_type::coordinate_type;
    using parameter_type  = typename dummy_potential<real_type>::parameter_type;

    constexpr std::size_t N = 1000;
    constexpr double      L = 10.0;
    constexpr double cutoff = 1.0;
    constexpr double margin = 0.5;
    constexpr double threshold = cutoff * (1.0 + margin);

    const auto distribute_particle = [](std::mt19937& mt, double l) -> coordinate_type
    {
        return coordinate_type(
            l * std::generate_canonical<real_type, std::numeric_limits<real_type>::digits>(mt),
            l * std::generate_canonical<real_type, std::numeric_limits<real_type>::digits>(mt),
            l * std::generate_canonical<real_type, std::numeric_limits<real_type>::digits>(mt)
        );
    };

    dummy_potential<real_type> pot(cutoff);

    mjolnir::System<traits_type> sys(N, boundary_type(coordinate_type(0.0, 0.0, 0.0), coordinate_type(L, L, L)));

    std::mt19937 mt(123456789);
    for(std::size_t i=0; i < N; ++i)
    {
        sys.at(i).mass     = 1.0;
        sys.at(i).position = distribute_particle(mt, L);
    }
    sys.topology().construct_molecules();

    mjolnir::PeriodicGridCellList<traits_type, parameter_type> vlist(margin);
    using neighbor_type = typename decltype(vlist)::neighbor_type;

    BOOST_TEST(!vlist.valid());

    vlist.initialize(sys, pot);
    vlist.make(sys, pot);
    BOOST_TEST(vlist.valid());

    for(std::size_t i=0; i<N; ++i)
    {
        for(std::size_t j=i+1; j<N; ++j)
        {
            const auto partners = vlist.partners(i);
            if(std::find_if(partners.begin(), partners.end(),
                    [=](const neighbor_type& elem){return elem.index == j;}
                        ) == partners.end())
            {
                // should be enough distant (>= threshold)
                const auto dist = mjolnir::math::length(sys.adjust_direction(
                            sys.position(j) - sys.position(i)));
                BOOST_TEST(dist >= threshold);
            }
            else
            {
                // should be enough close (< threshold)
                const auto dist = mjolnir::math::length(sys.adjust_direction(
                            sys.position(j) - sys.position(i)));
                BOOST_TEST(dist < threshold);
            }
        }
    }
}
<commit_msg>test: add test for PeriodicCellList with non-participants<commit_after>#define BOOST_TEST_MODULE "test_periodic_cell_list"

#ifdef BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>
#else
#include <boost/test/included/unit_test.hpp>
#endif

#include <mjolnir/util/empty.hpp>
#include <mjolnir/core/SimulatorTraits.hpp>
#include <mjolnir/core/BoundaryCondition.hpp>
#include <mjolnir/core/PeriodicGridCellList.hpp>
#include <mjolnir/core/System.hpp>
#include <mjolnir/core/Topology.hpp>
#include <random>

template<typename T>
struct dummy_potential
{
    using real_type      = T;
    using parameter_type = mjolnir::empty_t;

    using topology_type        = mjolnir::Topology;
    using molecule_id_type     = typename topology_type::molecule_id_type;
    using connection_kind_type = typename topology_type::connection_kind_type;

    explicit dummy_potential(const real_type cutoff,
                             const std::vector<std::size_t>& participants)
        : cutoff_(cutoff), participants_(participants)
    {}

    real_type max_cutoff_length() const noexcept {return this->cutoff_;}

    parameter_type prepare_params(std::size_t, std::size_t) const noexcept
    {
        return parameter_type{};
    }

    bool is_ignored_molecule(std::size_t, std::size_t) const {return false;}

    std::vector<std::pair<connection_kind_type, std::size_t>> ignore_within() const
    {
        return std::vector<std::pair<connection_kind_type, std::size_t>>{};
    }

    std::vector<std::size_t> const& participants() const noexcept
    {
        return this->participants_;
    }

    std::string name() const {return "dummy potential";}

    real_type cutoff_;
    std::vector<std::size_t> participants_;
};

BOOST_AUTO_TEST_CASE(test_CellList_PeriodicBoundary)
{
    mjolnir::LoggerManager::set_default_logger("test_cell_list.log");
    using traits_type     = mjolnir::SimulatorTraits<double, mjolnir::CuboidalPeriodicBoundary>;
    using real_type       = typename traits_type::real_type;
    using boundary_type   = typename traits_type::boundary_type;
    using coordinate_type = typename traits_type::coordinate_type;
    using parameter_type  = typename dummy_potential<real_type>::parameter_type;

    constexpr std::size_t N = 1000;
    constexpr double      L = 10.0;
    constexpr double cutoff = 1.0;
    constexpr double margin = 0.5;
    constexpr double threshold = cutoff * (1.0 + margin);

    const auto distribute_particle = [](std::mt19937& mt, double l) -> coordinate_type
    {
        return coordinate_type(
            l * std::generate_canonical<real_type, std::numeric_limits<real_type>::digits>(mt),
            l * std::generate_canonical<real_type, std::numeric_limits<real_type>::digits>(mt),
            l * std::generate_canonical<real_type, std::numeric_limits<real_type>::digits>(mt)
        );
    };

    std::vector<std::size_t> participants(N);
    std::iota(participants.begin(), participants.end(), 0u);

    dummy_potential<real_type> pot(cutoff, participants);

    mjolnir::System<traits_type> sys(N, boundary_type(
                coordinate_type(0.0, 0.0, 0.0), coordinate_type(L, L, L)));

    std::mt19937 mt(123456789);
    for(std::size_t i=0; i < N; ++i)
    {
        sys.at(i).mass     = 1.0;
        sys.at(i).position = distribute_particle(mt, L);
    }
    sys.topology().construct_molecules();

    mjolnir::PeriodicGridCellList<traits_type, parameter_type> vlist(margin);
    using neighbor_type = typename decltype(vlist)::neighbor_type;

    BOOST_TEST(!vlist.valid());

    vlist.initialize(sys, pot);
    vlist.make(sys, pot);
    BOOST_TEST(vlist.valid());

    for(std::size_t i=0; i<N; ++i)
    {
        for(std::size_t j=i+1; j<N; ++j)
        {
            const auto partners = vlist.partners(i);
            if(std::find_if(partners.begin(), partners.end(),
                    [=](const neighbor_type& elem){return elem.index == j;}
                        ) == partners.end())
            {
                // should be enough distant (>= threshold)
                const auto dist = mjolnir::math::length(sys.adjust_direction(
                            sys.position(j) - sys.position(i)));
                BOOST_TEST(dist >= threshold);
            }
            else
            {
                // should be enough close (< threshold)
                const auto dist = mjolnir::math::length(sys.adjust_direction(
                            sys.position(j) - sys.position(i)));
                BOOST_TEST(dist < threshold);
            }
        }
    }
}

BOOST_AUTO_TEST_CASE(test_PeriodicGridCellList_partial)
{
    mjolnir::LoggerManager::set_default_logger("test_periodic_grid_cell_list.log");
    using traits_type     = mjolnir::SimulatorTraits<double, mjolnir::CuboidalPeriodicBoundary>;
    using real_type       = typename traits_type::real_type;
    using boundary_type   = typename traits_type::boundary_type;
    using coordinate_type = typename traits_type::coordinate_type;

    constexpr std::size_t N = 1000;
    constexpr double      L = 10.0;
    constexpr double cutoff = 2.0;
    constexpr double margin = 0.25; // threshold = 2.0 * (1+0.25) = 2.5
    constexpr double threshold = cutoff * (1.0 + margin);

    const auto distribute_particle = [](std::mt19937& mt, double l) -> coordinate_type
    {
        return coordinate_type(
            l * std::generate_canonical<real_type, std::numeric_limits<real_type>::digits>(mt),
            l * std::generate_canonical<real_type, std::numeric_limits<real_type>::digits>(mt),
            l * std::generate_canonical<real_type, std::numeric_limits<real_type>::digits>(mt)
        );
    };

    std::vector<std::size_t> participants(500);
    // [200 ... 699]
    std::iota(participants.begin(), participants.end(), 200u);

    dummy_potential<real_type> pot(cutoff, participants);
    using parameter_type  = typename dummy_potential<real_type>::parameter_type;

    mjolnir::System<traits_type> sys(N, boundary_type(coordinate_type(0.0, 0.0, 0.0), coordinate_type(L, L, L)));

    std::mt19937 mt(123456789);
    for(std::size_t i=0; i < N; ++i)
    {
        sys.at(i).mass     = 1.0;
        sys.at(i).position = distribute_particle(mt, L);
    }
    sys.topology().construct_molecules();

    mjolnir::PeriodicGridCellList<traits_type, parameter_type> vlist(margin);

    using neighbor_type = typename decltype(vlist)::neighbor_type;

    BOOST_TEST(!vlist.valid());

    vlist.initialize(sys, pot);
    vlist.make(sys, pot);
    BOOST_TEST(vlist.valid());

    for(std::size_t i=0; i<N; ++i)
    {
        const auto partners = vlist.partners(i);

        // if particle i is not related to the potential, it should not have
        // any interacting partners.
        if(pot.participants().end() == std::find(
            pot.participants().begin(), pot.participants().end(), i))
        {
            BOOST_TEST(partners.size() == 0u);
            continue;
        }

        for(std::size_t j=i+1; j<N; ++j)
        {
            if(std::find_if(partners.begin(), partners.end(),
                [=](const neighbor_type& elem) -> bool {return elem.index == j;}
                        ) == partners.end())
            {
                // should be enough distant (>= threshold)
                const auto dist = mjolnir::math::length(sys.adjust_direction(
                            sys.position(j) - sys.position(i)));
                const bool enough_distant = dist >= threshold;

                // or not a participant
                const bool is_participant = pot.participants().end() != std::find(
                    pot.participants().begin(), pot.participants().end(), j);

                const bool is_ok = enough_distant || (!is_participant);
                BOOST_TEST(is_ok);
            }
            else
            {
                // should be a participant
                const bool found = pot.participants().end() != std::find(
                    pot.participants().begin(), pot.participants().end(), j);
                BOOST_TEST(found);

                // should be enough close (< threshold)
                const auto dist = mjolnir::math::length(sys.adjust_direction(
                            sys.position(j) - sys.position(i)));
                BOOST_TEST(dist < threshold);
            }
        }
    }
}

BOOST_AUTO_TEST_CASE(test_PeriodicGridCellList_partial_2)
{
    mjolnir::LoggerManager::set_default_logger("test_periodic_grid_cell_list.log");
    using traits_type     = mjolnir::SimulatorTraits<double, mjolnir::CuboidalPeriodicBoundary>;
    using real_type       = typename traits_type::real_type;
    using boundary_type   = typename traits_type::boundary_type;
    using coordinate_type = typename traits_type::coordinate_type;

    constexpr std::size_t N = 1000;
    constexpr double      L = 10.0;
    constexpr double cutoff = 2.0;
    constexpr double margin = 0.25; // threshold = 2.0 * (1+0.25) = 2.5
    constexpr double threshold = cutoff * (1.0 + margin);

    const auto distribute_particle = [](std::mt19937& mt, double l) -> coordinate_type
    {
        return coordinate_type(
            l * std::generate_canonical<real_type, std::numeric_limits<real_type>::digits>(mt),
            l * std::generate_canonical<real_type, std::numeric_limits<real_type>::digits>(mt),
            l * std::generate_canonical<real_type, std::numeric_limits<real_type>::digits>(mt)
        );
    };

    std::vector<std::size_t> participants; participants.reserve(500);
    for(std::size_t i=0; i<500; ++i)
    {
        participants.push_back(i * 2);
    }

    dummy_potential<real_type> pot(cutoff, participants);
    using parameter_type  = typename dummy_potential<real_type>::parameter_type;

    mjolnir::System<traits_type> sys(N, boundary_type(coordinate_type(0.0, 0.0, 0.0), coordinate_type(L, L, L)));

    std::mt19937 mt(123456789);
    for(std::size_t i=0; i < N; ++i)
    {
        sys.at(i).mass     = 1.0;
        sys.at(i).position = distribute_particle(mt, L);
    }
    sys.topology().construct_molecules();

    mjolnir::PeriodicGridCellList<traits_type, parameter_type> vlist(margin);

    using neighbor_type = typename decltype(vlist)::neighbor_type;

    BOOST_TEST(!vlist.valid());

    vlist.initialize(sys, pot);
    vlist.make(sys, pot);
    BOOST_TEST(vlist.valid());

    for(std::size_t i=0; i<N; ++i)
    {
        const auto partners = vlist.partners(i);

        // if particle i is not related to the potential, it should not have
        // any interacting partners.
        if(pot.participants().end() == std::find(
            pot.participants().begin(), pot.participants().end(), i))
        {
            BOOST_TEST(partners.size() == 0u);
            continue;
        }

        for(std::size_t j=i+1; j<N; ++j)
        {
            if(std::find_if(partners.begin(), partners.end(),
                [=](const neighbor_type& elem) -> bool {return elem.index == j;}
                        ) == partners.end())
            {
                // should be enough distant (>= threshold)
                const auto dist = mjolnir::math::length(sys.adjust_direction(
                            sys.position(j) - sys.position(i)));
                const bool enough_distant = dist >= threshold;

                // or not a participant
                const bool is_participant = pot.participants().end() != std::find(
                    pot.participants().begin(), pot.participants().end(), j);

                const bool is_ok = enough_distant || (!is_participant);
                BOOST_TEST(is_ok);
            }
            else
            {
                // should be a participant
                const bool found = pot.participants().end() != std::find(
                    pot.participants().begin(), pot.participants().end(), j);
                BOOST_TEST(found);

                // should be enough close (< threshold)
                const auto dist = mjolnir::math::length(sys.adjust_direction(
                            sys.position(j) - sys.position(i)));
                BOOST_TEST(dist < threshold);
            }
        }
    }
}
<|endoftext|>


<commit_before>#include "hecl/ClientProcess.hpp"
#include "hecl/Database.hpp"
#include "athena/FileReader.hpp"
#include "BlenderConnection.hpp"

#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#else
#include <sys/wait.h>
#endif

namespace hecl
{
static logvisor::Module Log("hecl::ClientProcess");

static int GetCPUCount()
{
#if _WIN32
    SYSTEM_INFO sysinfo;
    GetSystemInfo(&sysinfo);
    return sysinfo.dwNumberOfProcessors;
#else
    return sysconf(_SC_NPROCESSORS_ONLN);
#endif
}

void ClientProcess::BufferTransaction::run(BlenderToken& btok)
{
    athena::io::FileReader r(m_path.getAbsolutePath(), 32 * 1024, false);
    if (r.hasError())
    {
        Log.report(logvisor::Fatal, _S("unable to background-buffer '%s'"),
                   m_path.getAbsolutePath().c_str());
        return;
    }
    if (m_offset)
        r.seek(m_offset, athena::Begin);
    r.readBytesToBuf(m_targetBuf, m_maxLen);
    m_complete = true;
}

void ClientProcess::CookTransaction::run(BlenderToken& btok)
{
    m_returnResult = m_parent.syncCook(m_path, m_dataSpec, btok);
    m_complete = true;
}

void ClientProcess::LambdaTransaction::run(BlenderToken& btok)
{
    m_func(btok);
    m_complete = true;
}

ClientProcess::Worker::Worker(ClientProcess& proc)
: m_proc(proc)
{
    m_thr = std::thread(std::bind(&Worker::proc, this));
}

void ClientProcess::Worker::proc()
{
    while (m_proc.m_running)
    {
        std::unique_lock<std::mutex> lk(m_proc.m_mutex);
        if (!m_didInit)
        {
            m_proc.m_initCv.notify_one();
            m_didInit = true;
        }
        while (m_proc.m_pendingQueue.size())
        {
            std::unique_ptr<Transaction> trans = std::move(m_proc.m_pendingQueue.front());
            m_proc.m_pendingQueue.pop_front();
            lk.unlock();
            trans->run(m_blendTok);
            lk.lock();
            m_proc.m_completedQueue.push_back(std::move(trans));
        }
        m_proc.m_waitCv.notify_one();
        if (!m_proc.m_running)
            break;
        m_proc.m_cv.wait(lk);
    }
    m_blendTok.shutdown();
}

ClientProcess::ClientProcess(int verbosityLevel)
: m_verbosity(verbosityLevel)
{
#ifdef NDEBUG
    int cpuCount = GetCPUCount();
#else
    constexpr int cpuCount = 1;
#endif
    m_workers.reserve(cpuCount);
    for (int i=0 ; i<cpuCount ; ++i)
    {
        std::unique_lock<std::mutex> lk(m_mutex);
        m_workers.emplace_back(*this);
        m_initCv.wait(lk);
    }
}

const ClientProcess::BufferTransaction*
ClientProcess::addBufferTransaction(const ProjectPath& path, void* target,
                                    size_t maxLen, size_t offset)
{
    std::unique_lock<std::mutex> lk(m_mutex);
    BufferTransaction* ret = new BufferTransaction(*this, path, target, maxLen, offset);
    m_pendingQueue.emplace_back(ret);
    m_cv.notify_one();
    return ret;
}

const ClientProcess::CookTransaction*
ClientProcess::addCookTransaction(const hecl::ProjectPath& path, Database::IDataSpec* spec)
{
    std::unique_lock<std::mutex> lk(m_mutex);
    CookTransaction* ret = new CookTransaction(*this, path, spec);
    m_pendingQueue.emplace_back(ret);
    m_cv.notify_one();
    return ret;
}

const ClientProcess::LambdaTransaction*
ClientProcess::addLambdaTransaction(std::function<void(BlenderToken&)>&& func)
{
    std::unique_lock<std::mutex> lk(m_mutex);
    LambdaTransaction* ret = new LambdaTransaction(*this, std::move(func));
    m_pendingQueue.emplace_back(ret);
    m_cv.notify_one();
    return ret;
}

bool ClientProcess::syncCook(const hecl::ProjectPath& path, Database::IDataSpec* spec, BlenderToken& btok)
{
    if (spec->canCook(path, btok))
    {
        const Database::DataSpecEntry* specEnt = spec->overrideDataSpec(path, spec->getDataSpecEntry(), btok);
        if (specEnt)
        {
            hecl::ProjectPath cooked = path.getCookedPath(*specEnt);
            cooked.makeDirChain(false);
            spec->doCook(path, cooked, false, btok, [](const SystemChar*) {});
            return true;
        }
    }
    return false;
}

void ClientProcess::swapCompletedQueue(std::list<std::unique_ptr<Transaction>>& queue)
{
    std::unique_lock<std::mutex> lk(m_mutex);
    queue.swap(m_completedQueue);
}

void ClientProcess::waitUntilComplete()
{
    std::unique_lock<std::mutex> lk(m_mutex);
    while (m_pendingQueue.size())
        m_waitCv.wait(lk);
}

void ClientProcess::shutdown()
{
    if (!m_running)
        return;
    m_running = false;
    m_cv.notify_all();
    for (Worker& worker : m_workers)
        if (worker.m_thr.joinable())
            worker.m_thr.join();
}

}
<commit_msg>Safer pop location<commit_after>#include "hecl/ClientProcess.hpp"
#include "hecl/Database.hpp"
#include "athena/FileReader.hpp"
#include "BlenderConnection.hpp"

#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#else
#include <sys/wait.h>
#endif

namespace hecl
{
static logvisor::Module Log("hecl::ClientProcess");

static int GetCPUCount()
{
#if _WIN32
    SYSTEM_INFO sysinfo;
    GetSystemInfo(&sysinfo);
    return sysinfo.dwNumberOfProcessors;
#else
    return sysconf(_SC_NPROCESSORS_ONLN);
#endif
}

void ClientProcess::BufferTransaction::run(BlenderToken& btok)
{
    athena::io::FileReader r(m_path.getAbsolutePath(), 32 * 1024, false);
    if (r.hasError())
    {
        Log.report(logvisor::Fatal, _S("unable to background-buffer '%s'"),
                   m_path.getAbsolutePath().c_str());
        return;
    }
    if (m_offset)
        r.seek(m_offset, athena::Begin);
    r.readBytesToBuf(m_targetBuf, m_maxLen);
    m_complete = true;
}

void ClientProcess::CookTransaction::run(BlenderToken& btok)
{
    m_returnResult = m_parent.syncCook(m_path, m_dataSpec, btok);
    m_complete = true;
}

void ClientProcess::LambdaTransaction::run(BlenderToken& btok)
{
    m_func(btok);
    m_complete = true;
}

ClientProcess::Worker::Worker(ClientProcess& proc)
: m_proc(proc)
{
    m_thr = std::thread(std::bind(&Worker::proc, this));
}

void ClientProcess::Worker::proc()
{
    while (m_proc.m_running)
    {
        std::unique_lock<std::mutex> lk(m_proc.m_mutex);
        if (!m_didInit)
        {
            m_proc.m_initCv.notify_one();
            m_didInit = true;
        }
        while (m_proc.m_pendingQueue.size())
        {
            std::unique_ptr<Transaction> trans = std::move(m_proc.m_pendingQueue.front());
            lk.unlock();
            trans->run(m_blendTok);
            lk.lock();
            m_proc.m_completedQueue.push_back(std::move(trans));
            m_proc.m_pendingQueue.pop_front();
        }
        m_proc.m_waitCv.notify_one();
        if (!m_proc.m_running)
            break;
        m_proc.m_cv.wait(lk);
    }
    m_blendTok.shutdown();
}

ClientProcess::ClientProcess(int verbosityLevel)
: m_verbosity(verbosityLevel)
{
#ifdef NDEBUG
    int cpuCount = GetCPUCount();
#else
    constexpr int cpuCount = 1;
#endif
    m_workers.reserve(cpuCount);
    for (int i=0 ; i<cpuCount ; ++i)
    {
        std::unique_lock<std::mutex> lk(m_mutex);
        m_workers.emplace_back(*this);
        m_initCv.wait(lk);
    }
}

const ClientProcess::BufferTransaction*
ClientProcess::addBufferTransaction(const ProjectPath& path, void* target,
                                    size_t maxLen, size_t offset)
{
    std::unique_lock<std::mutex> lk(m_mutex);
    BufferTransaction* ret = new BufferTransaction(*this, path, target, maxLen, offset);
    m_pendingQueue.emplace_back(ret);
    m_cv.notify_one();
    return ret;
}

const ClientProcess::CookTransaction*
ClientProcess::addCookTransaction(const hecl::ProjectPath& path, Database::IDataSpec* spec)
{
    std::unique_lock<std::mutex> lk(m_mutex);
    CookTransaction* ret = new CookTransaction(*this, path, spec);
    m_pendingQueue.emplace_back(ret);
    m_cv.notify_one();
    return ret;
}

const ClientProcess::LambdaTransaction*
ClientProcess::addLambdaTransaction(std::function<void(BlenderToken&)>&& func)
{
    std::unique_lock<std::mutex> lk(m_mutex);
    LambdaTransaction* ret = new LambdaTransaction(*this, std::move(func));
    m_pendingQueue.emplace_back(ret);
    m_cv.notify_one();
    return ret;
}

bool ClientProcess::syncCook(const hecl::ProjectPath& path, Database::IDataSpec* spec, BlenderToken& btok)
{
    if (spec->canCook(path, btok))
    {
        const Database::DataSpecEntry* specEnt = spec->overrideDataSpec(path, spec->getDataSpecEntry(), btok);
        if (specEnt)
        {
            hecl::ProjectPath cooked = path.getCookedPath(*specEnt);
            cooked.makeDirChain(false);
            spec->doCook(path, cooked, false, btok, [](const SystemChar*) {});
            return true;
        }
    }
    return false;
}

void ClientProcess::swapCompletedQueue(std::list<std::unique_ptr<Transaction>>& queue)
{
    std::unique_lock<std::mutex> lk(m_mutex);
    queue.swap(m_completedQueue);
}

void ClientProcess::waitUntilComplete()
{
    std::unique_lock<std::mutex> lk(m_mutex);
    while (m_pendingQueue.size())
        m_waitCv.wait(lk);
}

void ClientProcess::shutdown()
{
    if (!m_running)
        return;
    m_running = false;
    m_cv.notify_all();
    for (Worker& worker : m_workers)
        if (worker.m_thr.joinable())
            worker.m_thr.join();
}

}
<|endoftext|>


<commit_before>/*
** Copyright 2011 Merethis
**
** This file is part of Centreon Clib.
**
** Centreon Clib 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.
**
** Centreon Clib 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 Centreon Clib. If not, see
** <http://www.gnu.org/licenses/>.
*/

#include <iostream>
#include <memory>
#include <sstream>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "com/centreon/exceptions/basic.hh"
#include "com/centreon/logging/engine.hh"

using namespace com::centreon::logging;

/**
 *  @class backend_test
 *  @brief litle implementation of backend to test logging engine.
 */
class                backend_test : public backend {
public:
                     backend_test() {}
                     ~backend_test() throw () {}
  std::string const& data() const throw () { return (_buffer); }
  void               flush() throw () {}
  void               log(char const* msg, unsigned int size) throw () {
    _buffer.append(msg, size);
  }
  void               reset() throw () { _buffer.clear(); }

private:
  std::string _buffer;
};

/**
 *  Check thread id.
 *
 *  @return True on success, otherwise false.
 */
static bool check_thread_id(std::string const& data, char const* msg) {
  unsigned long ptr(0);
  char message[1024];

  int ret(sscanf(
            data.c_str(),
            "[%p] %s\n",
            reinterpret_cast<void**>(&ptr),
            message));
  return (ret == 2 && !strncmp(msg, message, strlen(msg)));
}

/**
 *  Check add backend on to the logging engine.
 *
 *  @return 0 on success.
 */
int main() {
  static char msg[] = "Centreon_Clib_test";
  int retval;

  engine::load();
  try {
    engine& e(engine::instance());
    e.set_show_pid(false);
    e.set_show_thread_id(true);
    e.set_show_timestamp(engine::none);

    std::auto_ptr<backend_test> obj(new backend_test);
    e.add(obj.get(), 1, verbosity(1));

    e.log(0, verbosity(1), msg);
    if (!check_thread_id(obj->data(), msg))
      throw (basic_error() << "log with thread id failed");
    retval = 0;
  }
  catch (std::exception const& e) {
    std::cerr << "error: " << e.what() << std::endl;
    retval = 1;
  }
  engine::unload();
  return (retval);
}
<commit_msg>Fix a warning on some platforms.<commit_after>/*
** Copyright 2011 Merethis
**
** This file is part of Centreon Clib.
**
** Centreon Clib 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.
**
** Centreon Clib 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 Centreon Clib. If not, see
** <http://www.gnu.org/licenses/>.
*/

#include <iostream>
#include <memory>
#include <sstream>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "com/centreon/exceptions/basic.hh"
#include "com/centreon/logging/engine.hh"

using namespace com::centreon::logging;

/**
 *  @class backend_test
 *  @brief litle implementation of backend to test logging engine.
 */
class                backend_test : public backend {
public:
                     backend_test() {}
                     ~backend_test() throw () {}
  std::string const& data() const throw () { return (_buffer); }
  void               flush() throw () {}
  void               log(char const* msg, unsigned int size) throw () {
    _buffer.append(msg, size);
  }
  void               reset() throw () { _buffer.clear(); }

private:
  std::string _buffer;
};

/**
 *  Check thread id.
 *
 *  @return True on success, otherwise false.
 */
static bool check_thread_id(std::string const& data, char const* msg) {
  void* ptr(NULL);
  char message[1024];

  int ret(sscanf(
            data.c_str(),
            "[%p] %s\n",
            &ptr,
            message));
  return (ret == 2 && !strncmp(msg, message, strlen(msg)));
}

/**
 *  Check add backend on to the logging engine.
 *
 *  @return 0 on success.
 */
int main() {
  static char msg[] = "Centreon_Clib_test";
  int retval;

  engine::load();
  try {
    engine& e(engine::instance());
    e.set_show_pid(false);
    e.set_show_thread_id(true);
    e.set_show_timestamp(engine::none);

    std::auto_ptr<backend_test> obj(new backend_test);
    e.add(obj.get(), 1, verbosity(1));

    e.log(0, verbosity(1), msg);
    if (!check_thread_id(obj->data(), msg))
      throw (basic_error() << "log with thread id failed");
    retval = 0;
  }
  catch (std::exception const& e) {
    std::cerr << "error: " << e.what() << std::endl;
    retval = 1;
  }
  engine::unload();
  return (retval);
}
<|endoftext|>


<commit_before>#include "d2ModelScene.h"

#include <QtWidgets/QGraphicsSceneMouseEvent>
#include <QtGui/QKeyEvent>
#include <QtGui/QPainter>

#include <qrkernel/settingsManager.h>
#include <qrutils/graphicsUtils/gridDrawer.h>

#include "robotItem.h"
#include "commonTwoDModel/engine/configurer.h"

#include "src/engine/model/model.h"
#include "src/engine/items/wallItem.h"
#include "src/engine/items/stylusItem.h"
#include "src/engine/items/ellipseItem.h"

using namespace twoDModel;
using namespace view;
using namespace qReal;
using namespace graphicsUtils;

D2ModelScene::D2ModelScene(model::Model &model
		, Configurer const &configurer
		, AbstractView *view
		, QObject *parent)
	: AbstractScene(view, parent)
	, mModel(model)
	, mConfigurer(configurer)
	, mDrawingAction(none)
	, mRobot(nullptr)
	, mCurrentWall(nullptr)
	, mCurrentLine(nullptr)
	, mCurrentStylus(nullptr)
	, mCurrentEllipse(nullptr)
{
	mFirstPenWidth = 6;
	mSizeEmptyRectX = 1000;
	mSizeEmptyRectY = 1000;
	setEmptyRect(-500, -500, mSizeEmptyRectX, mSizeEmptyRectY);
	setItemIndexMethod(NoIndex);
	setEmptyPenBrushItems();

	connect(&mModel.worldModel(), &model::WorldModel::wallAdded, this, &QGraphicsScene::addItem);
	connect(&mModel.worldModel(), &model::WorldModel::wallAdded, [this](items::WallItem *wall) {
		connect(wall, &items::WallItem::wallDragged, this, &D2ModelScene::worldWallDragged);
	});
	connect(&mModel.worldModel(), &model::WorldModel::colorItemAdded, this, &QGraphicsScene::addItem);
	connect(&mModel.worldModel(), &model::WorldModel::itemRemoved, [](QGraphicsItem *item) { delete item; });

	drawInitialRobot();
}

D2ModelScene::~D2ModelScene()
{
	delete mRobot;
}

void D2ModelScene::drawInitialRobot()
{
	mRobot = new RobotItem(mConfigurer.robotImage(), mModel.robotModel());
	connect(mRobot, &RobotItem::changedPosition, this, &D2ModelScene::handleNewRobotPosition);
	connect(mRobot, &RobotItem::mousePressed, this, &D2ModelScene::robotPressed);
	addItem(mRobot);

	Rotater * const rotater = new Rotater();
	rotater->setMasterItem(mRobot);
	rotater->setVisible(false);

	mRobot->setRotater(rotater);

	centerOnRobot();
}

void D2ModelScene::handleNewRobotPosition()
{
	for (items::WallItem const *wall : mModel.worldModel().walls()) {
		if (wall->realShape().intersects(mRobot->realBoundingRect())) {
			mRobot->recoverDragStartPosition();
			return;
		}
	}
}

void D2ModelScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
	QPointF const position = mouseEvent->scenePos();

	mRobot->checkSelection();
	for (SensorItem *sensor : mRobot->sensors().values()) {
		if (sensor) {
			sensor->checkSelection();
		}
	}

	emit mousePressed();

	auto initItem = [this, mouseEvent](QGraphicsItem *item) {
		removeMoveFlag(mouseEvent, item);
		// This will deselect alll items
		setSelectionArea(QPainterPath());
	};

	auto initColorField = [this, mouseEvent, initItem](items::ColorFieldItem *item) {
		initItem(item);
		item->setPenBrush(penStyleItems(), penWidthItems(), penColorItems()
				, brushStyleItems(), brushColorItems());
		mModel.worldModel().addColorField(item);
	};

	if (!mRobot->realBoundingRect().contains(position)) {
		switch (mDrawingAction) {
		case wall:
			mCurrentWall = new items::WallItem(position, position);
			initItem(mCurrentWall);
			mModel.worldModel().addWall(mCurrentWall);
			break;
		case line:
			mCurrentLine = new items::LineItem(position, position);
			initColorField(mCurrentLine);
			break;
		case stylus:
			mCurrentStylus = new items::StylusItem(position.x(), position.y());
			initColorField(mCurrentStylus);
			break;
		case ellipse:
			mCurrentEllipse = new items::EllipseItem(position, position);
			initColorField(mCurrentEllipse);
			break;
		default:
			break;
		}
	}

	if (mDrawingAction == none) {
		forPressResize(mouseEvent);
	}

	AbstractScene::mousePressEvent(mouseEvent);
}

void D2ModelScene::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
	if (mouseEvent->buttons() & Qt::LeftButton) {
		mRobot->checkSelection();
		for (SensorItem *sensor : mRobot->sensors().values()) {
			if (sensor) {
				sensor->checkSelection();
			}
		}
	}

	bool needUpdate = true;
	switch (mDrawingAction){
	case wall:
		reshapeWall(mouseEvent);
		break;
	case line:
		reshapeLine(mouseEvent);
		break;
	case stylus:
		reshapeStylus(mouseEvent);
		break;
	case ellipse:
		reshapeEllipse(mouseEvent);
		break;
	default:
		needUpdate = false;
		if (mouseEvent->buttons() & Qt::LeftButton) {
			forMoveResize(mouseEvent, mRobot->realBoundingRect());
		}

		AbstractScene::mouseMoveEvent(mouseEvent);
		break;
	}

	if (needUpdate) {
		update();
	}

}

void D2ModelScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
	mRobot->checkSelection();
	for (SensorItem *sensor : mRobot->sensors().values()) {
		if (sensor) {
			sensor->checkSelection();
		}
	}

	emit mouseReleased();

	// After dragging item may be null. We mustn`t select it in that case.
	QGraphicsItem *itemToSelect = nullptr;
	switch (mDrawingAction){
	case wall: {
		reshapeWall(mouseEvent);
		itemToSelect = mCurrentWall;
		mCurrentWall = nullptr;
		break;
	}
	case line: {
		reshapeLine(mouseEvent);
		itemToSelect = mCurrentLine;
		mCurrentLine = nullptr;
		break;
	}
	case stylus: {
		reshapeStylus(mouseEvent);
		itemToSelect = mCurrentStylus;
		mCurrentStylus = nullptr;
		break;
	}
	case ellipse: {
		reshapeEllipse(mouseEvent);
		itemToSelect = mCurrentEllipse;
		mCurrentEllipse = nullptr;
		break;
	}
	default:
		forReleaseResize(mouseEvent, mRobot->realBoundingRect());
		break;
	}

	if (itemToSelect) {
		itemToSelect->setSelected(true);
	}

	setMoveFlag(mouseEvent);

	setSceneRect(sceneRect().united(mRobot->sceneBoundingRect()));
	update();
	AbstractScene::mouseReleaseEvent(mouseEvent);
}

void D2ModelScene::forPressResize(QGraphicsSceneMouseEvent *event)
{
	setX1andY1(event);
	mGraphicsItem = dynamic_cast<AbstractItem *>(itemAt(event->scenePos(), QTransform()));
	if (mGraphicsItem) {
		mGraphicsItem->changeDragState(mX1, mY1);
		if (mGraphicsItem->getDragState() != AbstractItem::None) {
			mView->setDragMode(QGraphicsView::NoDrag);
		}
	}

	update();
}

void D2ModelScene::deleteItem(QGraphicsItem *item)
{
	if (!items().contains(item)) {
		return;
	}

	if (SensorItem * const sensor = dynamic_cast<SensorItem *>(item)) {
		interpreterBase::robotModel::PortInfo const port = mRobot->sensors().key(sensor);
		if (port.isValid()) {
			deviceConfigurationChanged(mModel.robotModel().info().name()
					, port, interpreterBase::robotModel::DeviceInfo());
		}
	} else if (items::WallItem * const wall = dynamic_cast<items::WallItem *>(item)) {
		mModel.worldModel().removeWall(wall);
	} else if (items::ColorFieldItem *colorField = dynamic_cast<items::ColorFieldItem *>(item)) {
		mModel.worldModel().removeColorField(colorField);
	}
}

void D2ModelScene::forMoveResize(QGraphicsSceneMouseEvent *event, QRectF const &rect)
{
	reshapeItem(event, rect);
	update();
}

void D2ModelScene::forReleaseResize(QGraphicsSceneMouseEvent * event, QRectF const &rect)
{
	reshapeItem(event, rect);
	mGraphicsItem = nullptr;
	update();
}

void D2ModelScene::reshapeItem(QGraphicsSceneMouseEvent *event, QRectF const &rect)
{
	setX2andY2(event);
	if (mGraphicsItem) {
		QPointF const oldBegin = mGraphicsItem->getX1andY1();
		QPointF const oldEnd = mGraphicsItem->getX2andY2();
		if (mGraphicsItem->getDragState() != graphicsUtils::AbstractItem::None) {
			mView->setDragMode(QGraphicsView::NoDrag);
		}

		mGraphicsItem->resizeItem(event);

		if (mGraphicsItem->realShape().intersects(rect) && dynamic_cast<items::WallItem *>(mGraphicsItem)) {
			mGraphicsItem->reverseOldResizingItem(oldBegin, oldEnd);
		}
	}
}

void D2ModelScene::keyPressEvent(QKeyEvent *event)
{
	if (event->key() == Qt::Key_Delete && (selectedItems().size() > 0)) {
		for (QGraphicsItem * const item : selectedItems()) {
			deleteItem(item);
		}
	} else {
		QGraphicsScene::keyPressEvent(event);
	}
}

void D2ModelScene::drawBackground(QPainter *painter, QRectF const &rect)
{
	if (SettingsManager::value("2dShowGrid").toBool()) {
		mWidthOfGrid = SettingsManager::value("GridWidth").toReal() / 100;
		painter->setPen(QPen(Qt::black, mWidthOfGrid));
		QGraphicsScene::drawBackground(painter, rect);
		int const cellSize = SettingsManager::value("2dGridCellSize").toInt();
		mGridDrawer.drawGrid(painter, rect, cellSize);
	}
}

void D2ModelScene::addWall()
{
	mDrawingAction = wall;
}

void D2ModelScene::addLine()
{
	mDrawingAction = line;
}

void D2ModelScene::addStylus()
{
	mDrawingAction = stylus;
}

void D2ModelScene::addEllipse()
{
	mDrawingAction = ellipse;
}

void D2ModelScene::setNoneStatus()
{
	mDrawingAction = none;
}

void D2ModelScene::clearScene(bool removeRobot)
{
	mModel.worldModel().clear();
	mModel.robotModel().clear();
	if (removeRobot) {
		for (interpreterBase::robotModel::PortInfo const &port : mRobot->sensors().keys()) {
			deviceConfigurationChanged(mModel.robotModel().info().name()
					, port, interpreterBase::robotModel::DeviceInfo());
		}

		delete mRobot;
		clear();
		drawInitialRobot();
	}
}

void D2ModelScene::reshapeWall(QGraphicsSceneMouseEvent *event)
{
	QPointF const pos = event->scenePos();
	if (mCurrentWall) {
		QPointF const oldPos = mCurrentWall->end();
		mCurrentWall->setX2andY2(pos.x(), pos.y());
		if (SettingsManager::value("2dShowGrid").toBool()) {
			mCurrentWall->reshapeBeginWithGrid(SettingsManager::value("2dGridCellSize").toInt());
			mCurrentWall->reshapeEndWithGrid(SettingsManager::value("2dGridCellSize").toInt());
		} else {
			if (mCurrentWall->realShape().intersects(mRobot->realBoundingRect())) {
				mCurrentWall->setX2andY2(oldPos.x(), oldPos.y());
			}
			if (event->modifiers() & Qt::ShiftModifier) {
				mCurrentWall->reshapeRectWithShift();
			}
		}
	}
}

void D2ModelScene::reshapeLine(QGraphicsSceneMouseEvent *event)
{
	QPointF const pos = event->scenePos();
	if (mCurrentLine) {
		mCurrentLine->setX2andY2(pos.x(), pos.y());
		if (event->modifiers() & Qt::ShiftModifier) {
			mCurrentLine->reshapeRectWithShift();
		}
	}
}

void D2ModelScene::reshapeStylus(QGraphicsSceneMouseEvent *event)
{
	QPointF const pos = event->scenePos();
	if (mCurrentStylus) {
		mCurrentStylus->addLine(pos.x(), pos.y());
	}
}

void D2ModelScene::reshapeEllipse(QGraphicsSceneMouseEvent *event)
{
	QPointF const pos = event->scenePos();
	if (mCurrentEllipse) {
		mCurrentEllipse->setX2andY2(pos.x(), pos.y());
		if (event->modifiers() & Qt::ShiftModifier) {
			mCurrentEllipse->reshapeRectWithShift();
		}
	}
}

void D2ModelScene::worldWallDragged(items::WallItem *wall, QPainterPath const &shape, QPointF const &oldPos)
{
	bool const isNeedStop = shape.intersects(mRobot->realBoundingRect());
	wall->onOverlappedWithRobot(isNeedStop);
	if (wall->isDragged() && ((mDrawingAction == none) ||
			(mDrawingAction == D2ModelScene::wall && mCurrentWall == wall)))
	{
		wall->setFlag(QGraphicsItem::ItemIsMovable, !isNeedStop);
		if (isNeedStop) {
			wall->setPos(oldPos);
		}
	}
}

void D2ModelScene::alignWalls()
{
	for (items::WallItem * const wall : mModel.worldModel().walls()) {
		if (items().contains(wall)) {
			wall->setBeginCoordinatesWithGrid(SettingsManager::value("2dGridCellSize").toInt());
			wall->setEndCoordinatesWithGrid(SettingsManager::value("2dGridCellSize").toInt());
		}
	}
}

RobotItem *D2ModelScene::robot()
{
	return mRobot;
}

void D2ModelScene::centerOnRobot()
{
	for (QGraphicsView * const view : views()) {
		QRectF const viewPortRect = view->mapToScene(view->viewport()->rect()).boundingRect();
		if (!viewPortRect.contains(mRobot->sceneBoundingRect().toRect())) {
			QRectF const requiredViewPort = viewPortRect.translated(mRobot->scenePos() - viewPortRect.center());
			setSceneRect(itemsBoundingRect().united(requiredViewPort));
			view->centerOn(mRobot);
		}
	}
}
<commit_msg>Fixed taking lambda by value<commit_after>#include "d2ModelScene.h"

#include <QtWidgets/QGraphicsSceneMouseEvent>
#include <QtGui/QKeyEvent>
#include <QtGui/QPainter>

#include <qrkernel/settingsManager.h>
#include <qrutils/graphicsUtils/gridDrawer.h>

#include "robotItem.h"
#include "commonTwoDModel/engine/configurer.h"

#include "src/engine/model/model.h"
#include "src/engine/items/wallItem.h"
#include "src/engine/items/stylusItem.h"
#include "src/engine/items/ellipseItem.h"

using namespace twoDModel;
using namespace view;
using namespace qReal;
using namespace graphicsUtils;

D2ModelScene::D2ModelScene(model::Model &model
		, Configurer const &configurer
		, AbstractView *view
		, QObject *parent)
	: AbstractScene(view, parent)
	, mModel(model)
	, mConfigurer(configurer)
	, mDrawingAction(none)
	, mRobot(nullptr)
	, mCurrentWall(nullptr)
	, mCurrentLine(nullptr)
	, mCurrentStylus(nullptr)
	, mCurrentEllipse(nullptr)
{
	mFirstPenWidth = 6;
	mSizeEmptyRectX = 1000;
	mSizeEmptyRectY = 1000;
	setEmptyRect(-500, -500, mSizeEmptyRectX, mSizeEmptyRectY);
	setItemIndexMethod(NoIndex);
	setEmptyPenBrushItems();

	connect(&mModel.worldModel(), &model::WorldModel::wallAdded, this, &QGraphicsScene::addItem);
	connect(&mModel.worldModel(), &model::WorldModel::wallAdded, [this](items::WallItem *wall) {
		connect(wall, &items::WallItem::wallDragged, this, &D2ModelScene::worldWallDragged);
	});
	connect(&mModel.worldModel(), &model::WorldModel::colorItemAdded, this, &QGraphicsScene::addItem);
	connect(&mModel.worldModel(), &model::WorldModel::itemRemoved, [](QGraphicsItem *item) { delete item; });

	drawInitialRobot();
}

D2ModelScene::~D2ModelScene()
{
	delete mRobot;
}

void D2ModelScene::drawInitialRobot()
{
	mRobot = new RobotItem(mConfigurer.robotImage(), mModel.robotModel());
	connect(mRobot, &RobotItem::changedPosition, this, &D2ModelScene::handleNewRobotPosition);
	connect(mRobot, &RobotItem::mousePressed, this, &D2ModelScene::robotPressed);
	addItem(mRobot);

	Rotater * const rotater = new Rotater();
	rotater->setMasterItem(mRobot);
	rotater->setVisible(false);

	mRobot->setRotater(rotater);

	centerOnRobot();
}

void D2ModelScene::handleNewRobotPosition()
{
	for (items::WallItem const *wall : mModel.worldModel().walls()) {
		if (wall->realShape().intersects(mRobot->realBoundingRect())) {
			mRobot->recoverDragStartPosition();
			return;
		}
	}
}

void D2ModelScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
	QPointF const position = mouseEvent->scenePos();

	mRobot->checkSelection();
	for (SensorItem *sensor : mRobot->sensors().values()) {
		if (sensor) {
			sensor->checkSelection();
		}
	}

	emit mousePressed();

	auto initItem = [this, mouseEvent](QGraphicsItem *item) {
		removeMoveFlag(mouseEvent, item);
		// This will deselect alll items
		setSelectionArea(QPainterPath());
	};

	auto initColorField = [this, mouseEvent, &initItem](items::ColorFieldItem *item) {
		initItem(item);
		item->setPenBrush(penStyleItems(), penWidthItems(), penColorItems()
				, brushStyleItems(), brushColorItems());
		mModel.worldModel().addColorField(item);
	};

	if (!mRobot->realBoundingRect().contains(position)) {
		switch (mDrawingAction) {
		case wall:
			mCurrentWall = new items::WallItem(position, position);
			initItem(mCurrentWall);
			mModel.worldModel().addWall(mCurrentWall);
			break;
		case line:
			mCurrentLine = new items::LineItem(position, position);
			initColorField(mCurrentLine);
			break;
		case stylus:
			mCurrentStylus = new items::StylusItem(position.x(), position.y());
			initColorField(mCurrentStylus);
			break;
		case ellipse:
			mCurrentEllipse = new items::EllipseItem(position, position);
			initColorField(mCurrentEllipse);
			break;
		default:
			break;
		}
	}

	if (mDrawingAction == none) {
		forPressResize(mouseEvent);
	}

	AbstractScene::mousePressEvent(mouseEvent);
}

void D2ModelScene::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
	if (mouseEvent->buttons() & Qt::LeftButton) {
		mRobot->checkSelection();
		for (SensorItem *sensor : mRobot->sensors().values()) {
			if (sensor) {
				sensor->checkSelection();
			}
		}
	}

	bool needUpdate = true;
	switch (mDrawingAction){
	case wall:
		reshapeWall(mouseEvent);
		break;
	case line:
		reshapeLine(mouseEvent);
		break;
	case stylus:
		reshapeStylus(mouseEvent);
		break;
	case ellipse:
		reshapeEllipse(mouseEvent);
		break;
	default:
		needUpdate = false;
		if (mouseEvent->buttons() & Qt::LeftButton) {
			forMoveResize(mouseEvent, mRobot->realBoundingRect());
		}

		AbstractScene::mouseMoveEvent(mouseEvent);
		break;
	}

	if (needUpdate) {
		update();
	}

}

void D2ModelScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
	mRobot->checkSelection();
	for (SensorItem *sensor : mRobot->sensors().values()) {
		if (sensor) {
			sensor->checkSelection();
		}
	}

	emit mouseReleased();

	// After dragging item may be null. We mustn`t select it in that case.
	QGraphicsItem *itemToSelect = nullptr;
	switch (mDrawingAction){
	case wall: {
		reshapeWall(mouseEvent);
		itemToSelect = mCurrentWall;
		mCurrentWall = nullptr;
		break;
	}
	case line: {
		reshapeLine(mouseEvent);
		itemToSelect = mCurrentLine;
		mCurrentLine = nullptr;
		break;
	}
	case stylus: {
		reshapeStylus(mouseEvent);
		itemToSelect = mCurrentStylus;
		mCurrentStylus = nullptr;
		break;
	}
	case ellipse: {
		reshapeEllipse(mouseEvent);
		itemToSelect = mCurrentEllipse;
		mCurrentEllipse = nullptr;
		break;
	}
	default:
		forReleaseResize(mouseEvent, mRobot->realBoundingRect());
		break;
	}

	if (itemToSelect) {
		itemToSelect->setSelected(true);
	}

	setMoveFlag(mouseEvent);

	setSceneRect(sceneRect().united(mRobot->sceneBoundingRect()));
	update();
	AbstractScene::mouseReleaseEvent(mouseEvent);
}

void D2ModelScene::forPressResize(QGraphicsSceneMouseEvent *event)
{
	setX1andY1(event);
	mGraphicsItem = dynamic_cast<AbstractItem *>(itemAt(event->scenePos(), QTransform()));
	if (mGraphicsItem) {
		mGraphicsItem->changeDragState(mX1, mY1);
		if (mGraphicsItem->getDragState() != AbstractItem::None) {
			mView->setDragMode(QGraphicsView::NoDrag);
		}
	}

	update();
}

void D2ModelScene::deleteItem(QGraphicsItem *item)
{
	if (!items().contains(item)) {
		return;
	}

	if (SensorItem * const sensor = dynamic_cast<SensorItem *>(item)) {
		interpreterBase::robotModel::PortInfo const port = mRobot->sensors().key(sensor);
		if (port.isValid()) {
			deviceConfigurationChanged(mModel.robotModel().info().name()
					, port, interpreterBase::robotModel::DeviceInfo());
		}
	} else if (items::WallItem * const wall = dynamic_cast<items::WallItem *>(item)) {
		mModel.worldModel().removeWall(wall);
	} else if (items::ColorFieldItem *colorField = dynamic_cast<items::ColorFieldItem *>(item)) {
		mModel.worldModel().removeColorField(colorField);
	}
}

void D2ModelScene::forMoveResize(QGraphicsSceneMouseEvent *event, QRectF const &rect)
{
	reshapeItem(event, rect);
	update();
}

void D2ModelScene::forReleaseResize(QGraphicsSceneMouseEvent * event, QRectF const &rect)
{
	reshapeItem(event, rect);
	mGraphicsItem = nullptr;
	update();
}

void D2ModelScene::reshapeItem(QGraphicsSceneMouseEvent *event, QRectF const &rect)
{
	setX2andY2(event);
	if (mGraphicsItem) {
		QPointF const oldBegin = mGraphicsItem->getX1andY1();
		QPointF const oldEnd = mGraphicsItem->getX2andY2();
		if (mGraphicsItem->getDragState() != graphicsUtils::AbstractItem::None) {
			mView->setDragMode(QGraphicsView::NoDrag);
		}

		mGraphicsItem->resizeItem(event);

		if (mGraphicsItem->realShape().intersects(rect) && dynamic_cast<items::WallItem *>(mGraphicsItem)) {
			mGraphicsItem->reverseOldResizingItem(oldBegin, oldEnd);
		}
	}
}

void D2ModelScene::keyPressEvent(QKeyEvent *event)
{
	if (event->key() == Qt::Key_Delete && (selectedItems().size() > 0)) {
		for (QGraphicsItem * const item : selectedItems()) {
			deleteItem(item);
		}
	} else {
		QGraphicsScene::keyPressEvent(event);
	}
}

void D2ModelScene::drawBackground(QPainter *painter, QRectF const &rect)
{
	if (SettingsManager::value("2dShowGrid").toBool()) {
		mWidthOfGrid = SettingsManager::value("GridWidth").toReal() / 100;
		painter->setPen(QPen(Qt::black, mWidthOfGrid));
		QGraphicsScene::drawBackground(painter, rect);
		int const cellSize = SettingsManager::value("2dGridCellSize").toInt();
		mGridDrawer.drawGrid(painter, rect, cellSize);
	}
}

void D2ModelScene::addWall()
{
	mDrawingAction = wall;
}

void D2ModelScene::addLine()
{
	mDrawingAction = line;
}

void D2ModelScene::addStylus()
{
	mDrawingAction = stylus;
}

void D2ModelScene::addEllipse()
{
	mDrawingAction = ellipse;
}

void D2ModelScene::setNoneStatus()
{
	mDrawingAction = none;
}

void D2ModelScene::clearScene(bool removeRobot)
{
	mModel.worldModel().clear();
	mModel.robotModel().clear();
	if (removeRobot) {
		for (interpreterBase::robotModel::PortInfo const &port : mRobot->sensors().keys()) {
			deviceConfigurationChanged(mModel.robotModel().info().name()
					, port, interpreterBase::robotModel::DeviceInfo());
		}

		delete mRobot;
		clear();
		drawInitialRobot();
	}
}

void D2ModelScene::reshapeWall(QGraphicsSceneMouseEvent *event)
{
	QPointF const pos = event->scenePos();
	if (mCurrentWall) {
		QPointF const oldPos = mCurrentWall->end();
		mCurrentWall->setX2andY2(pos.x(), pos.y());
		if (SettingsManager::value("2dShowGrid").toBool()) {
			mCurrentWall->reshapeBeginWithGrid(SettingsManager::value("2dGridCellSize").toInt());
			mCurrentWall->reshapeEndWithGrid(SettingsManager::value("2dGridCellSize").toInt());
		} else {
			if (mCurrentWall->realShape().intersects(mRobot->realBoundingRect())) {
				mCurrentWall->setX2andY2(oldPos.x(), oldPos.y());
			}
			if (event->modifiers() & Qt::ShiftModifier) {
				mCurrentWall->reshapeRectWithShift();
			}
		}
	}
}

void D2ModelScene::reshapeLine(QGraphicsSceneMouseEvent *event)
{
	QPointF const pos = event->scenePos();
	if (mCurrentLine) {
		mCurrentLine->setX2andY2(pos.x(), pos.y());
		if (event->modifiers() & Qt::ShiftModifier) {
			mCurrentLine->reshapeRectWithShift();
		}
	}
}

void D2ModelScene::reshapeStylus(QGraphicsSceneMouseEvent *event)
{
	QPointF const pos = event->scenePos();
	if (mCurrentStylus) {
		mCurrentStylus->addLine(pos.x(), pos.y());
	}
}

void D2ModelScene::reshapeEllipse(QGraphicsSceneMouseEvent *event)
{
	QPointF const pos = event->scenePos();
	if (mCurrentEllipse) {
		mCurrentEllipse->setX2andY2(pos.x(), pos.y());
		if (event->modifiers() & Qt::ShiftModifier) {
			mCurrentEllipse->reshapeRectWithShift();
		}
	}
}

void D2ModelScene::worldWallDragged(items::WallItem *wall, QPainterPath const &shape, QPointF const &oldPos)
{
	bool const isNeedStop = shape.intersects(mRobot->realBoundingRect());
	wall->onOverlappedWithRobot(isNeedStop);
	if (wall->isDragged() && ((mDrawingAction == none) ||
			(mDrawingAction == D2ModelScene::wall && mCurrentWall == wall)))
	{
		wall->setFlag(QGraphicsItem::ItemIsMovable, !isNeedStop);
		if (isNeedStop) {
			wall->setPos(oldPos);
		}
	}
}

void D2ModelScene::alignWalls()
{
	for (items::WallItem * const wall : mModel.worldModel().walls()) {
		if (items().contains(wall)) {
			wall->setBeginCoordinatesWithGrid(SettingsManager::value("2dGridCellSize").toInt());
			wall->setEndCoordinatesWithGrid(SettingsManager::value("2dGridCellSize").toInt());
		}
	}
}

RobotItem *D2ModelScene::robot()
{
	return mRobot;
}

void D2ModelScene::centerOnRobot()
{
	for (QGraphicsView * const view : views()) {
		QRectF const viewPortRect = view->mapToScene(view->viewport()->rect()).boundingRect();
		if (!viewPortRect.contains(mRobot->sceneBoundingRect().toRect())) {
			QRectF const requiredViewPort = viewPortRect.translated(mRobot->scenePos() - viewPortRect.center());
			setSceneRect(itemsBoundingRect().united(requiredViewPort));
			view->centerOn(mRobot);
		}
	}
}
<|endoftext|>


<commit_before>#include <cap/super_capacitor.h>
#include <cap/utils.h>
#include <boost/property_tree/ptree.hpp>
#include <boost/foreach.hpp>
#include <iostream>
#include <string>
#include <vector>

// forward declaration (implementation is in "common.cc")
std::shared_ptr<boost::property_tree::ptree> initialize_database();
void put_default_parameters(boost::property_tree::ptree & params);

int main(int argc, char *argv[])
{
    std::shared_ptr<boost::property_tree::ptree> database = initialize_database();
    database->put("verbose", false);
    cap::SuperCapacitorProblem<2> super_capacitor(database);

    // SETTING PROBLEM PARAMETERS
    std::shared_ptr<boost::property_tree::ptree> in(new boost::property_tree::ptree);
    put_default_parameters(*in);
    in->put("test_case",       2  );
    in->put("time_step",       1.0);
    in->put("initial_time",    0.0);
    in->put("final_time",    300.0);
    in->put("max_cycles",    100  );
    in->put("boundary_values.charge_current_density",     324.65);
    in->put("boundary_values.discharge_current_density", -324.65);

    // SOLVING THE PROBLEM
    std::shared_ptr<boost::property_tree::ptree> out(new boost::property_tree::ptree);
    super_capacitor.run(in, out);

    // POSTPROCESSING QUANTITIES OF INTEREST
    double max_temperature = out->get<double>("quantities_of_interest.max_temperature");
    double energy_density  = out->get<double>("quantities_of_interest.energy_density" );
    double power_density   = out->get<double>("quantities_of_interest.power_density"  );
    double efficiency      = out->get<double>("quantities_of_interest.efficiency"     );

    std::cout<<"max_temperature="<<max_temperature<<"\n";
    std::cout<<"energy_density ="<<energy_density <<"\n";
    std::cout<<"power_density  ="<<power_density  <<"\n";
    std::cout<<"efficiency     ="<<efficiency     <<"\n";

    return 0;
}

#include "common.cc"
<commit_msg>increased to 1 hr the final time in constant current cycling problem<commit_after>#include <cap/super_capacitor.h>
#include <cap/utils.h>
#include <boost/property_tree/ptree.hpp>
#include <boost/foreach.hpp>
#include <iostream>
#include <string>
#include <vector>

// forward declaration (implementation is in "common.cc")
std::shared_ptr<boost::property_tree::ptree> initialize_database();
void put_default_parameters(boost::property_tree::ptree & params);

int main(int argc, char *argv[])
{
    std::shared_ptr<boost::property_tree::ptree> database = initialize_database();
    database->put("verbose", false);
    cap::SuperCapacitorProblem<2> super_capacitor(database);

    // SETTING PROBLEM PARAMETERS
    std::shared_ptr<boost::property_tree::ptree> in(new boost::property_tree::ptree);
    put_default_parameters(*in);
    in->put("test_case",       2  );
    in->put("time_step",       1.0);
    in->put("initial_time",    0.0);
    in->put("final_time",   3600.0);
    in->put("max_cycles",    100  );
    in->put("boundary_values.charge_current_density",     324.65);
    in->put("boundary_values.discharge_current_density", -324.65);
    in->put("boundary_values.charge_potential",             2.2 );
    in->put("boundary_values.discharge_potential",          1.1 );
    in->put("boundary_values.initial_potential",            1.4 );
    in->put("boundary_values.ambient_temperature",          0.0 );

    // SOLVING THE PROBLEM
    std::shared_ptr<boost::property_tree::ptree> out(new boost::property_tree::ptree);
    super_capacitor.run(in, out);

    // POSTPROCESSING QUANTITIES OF INTEREST
    double max_temperature = out->get<double>("quantities_of_interest.max_temperature");
    double energy_density  = out->get<double>("quantities_of_interest.energy_density" );
    double power_density   = out->get<double>("quantities_of_interest.power_density"  );
    double efficiency      = out->get<double>("quantities_of_interest.efficiency"     );

    std::cout<<"max_temperature="<<max_temperature<<"\n";
    std::cout<<"energy_density ="<<energy_density <<"\n";
    std::cout<<"power_density  ="<<power_density  <<"\n";
    std::cout<<"efficiency     ="<<efficiency     <<"\n";

    return 0;
}

#include "common.cc"
<|endoftext|>