<commit_before>/*
 *  Copyright (c) 2016, The OpenThread Authors.
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. 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.
 *  3. Neither the name of the copyright holder nor the
 *     names of its contributors may be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 *  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
 *   This file implements link quality information processing and storage.
 */

#include "link_quality.hpp"

#include <stdio.h>

#include "common/code_utils.hpp"
#include "common/instance.hpp"
#include "common/locator-getters.hpp"

namespace ot {

// This array gives the decimal point digits representing 0/8, 1/8, ..., 7/8 (does not include the '.').
static const char *const kDigitsString[8] = {
    // 0/8,  1/8,   2/8,   3/8,   4/8,   5/8,   6/8,   7/8
    "0", "125", "25", "375", "5", "625", "75", "875"};

void SuccessRateTracker::AddSample(bool aSuccess, uint16_t aWeight)
{
    uint32_t oldAverage = mFailureRate;
    uint32_t newValue   = (aSuccess) ? 0 : kMaxRateValue;
    uint32_t n          = aWeight;

    // `n/2` is added to the sum to ensure rounding the value to the nearest integer when dividing by `n`
    // (e.g., 1.2 -> 1, 3.5 -> 4).

    mFailureRate = static_cast<uint16_t>(((oldAverage * (n - 1)) + newValue + (n / 2)) / n);
}

void RssAverager::Reset(void)
{
    mAverage = 0;
    mCount   = 0;
}

otError RssAverager::Add(int8_t aRss)
{
    otError  error = OT_ERROR_NONE;
    uint16_t newValue;
    uint16_t oldAverage;

    VerifyOrExit(aRss != OT_RADIO_RSSI_INVALID, error = OT_ERROR_INVALID_ARGS);

    // Restrict the RSS value to the closed range [0, -128] so the RSS times precision multiple can fit in 11 bits.
    if (aRss > 0)
    {
        aRss = 0;
    }

    // Multiply the RSS value by a precision multiple (currently -8).

    newValue = static_cast<uint16_t>(-aRss);
    newValue <<= kPrecisionBitShift;

    oldAverage = mAverage;

    if (mCount == 0)
    {
        mCount++;
        mAverage = newValue;
    }
    else if (mCount < (1 << kCoeffBitShift) - 1)
    {
        mCount++;

        // Maintain arithmetic mean.
        // newAverage = newValue * (1/mCount) + oldAverage * ((mCount -1)/mCount)
        mAverage = static_cast<uint16_t>(((oldAverage * (mCount - 1)) + newValue) / mCount);
    }
    else
    {
        // Maintain exponentially weighted moving average using coefficient of (1/2^kCoeffBitShift).
        // newAverage = + newValue * 1/2^j + oldAverage * (1 - 1/2^j), for j = kCoeffBitShift.

        mAverage = static_cast<uint16_t>(((oldAverage << kCoeffBitShift) - oldAverage + newValue) >> kCoeffBitShift);
    }

exit:
    return error;
}

int8_t RssAverager::GetAverage(void) const
{
    int8_t average;

    VerifyOrExit(mCount != 0, average = OT_RADIO_RSSI_INVALID);

    average = -static_cast<int8_t>(mAverage >> kPrecisionBitShift);

    // Check for possible round up (e.g., average of -71.5 --> -72)

    if ((mAverage & kPrecisionBitMask) >= (kPrecision >> 1))
    {
        average--;
    }

exit:
    return average;
}

RssAverager::InfoString RssAverager::ToString(void) const
{
    InfoString string;

    VerifyOrExit(mCount != 0, OT_NOOP);
    IgnoreError(string.Set("%d.%s", -(mAverage >> kPrecisionBitShift), kDigitsString[mAverage & kPrecisionBitMask]));

exit:
    return string;
}

void LinkQualityInfo::Clear(void)
{
    mRssAverager.Reset();
    SetLinkQuality(0);
    mLastRss = OT_RADIO_RSSI_INVALID;

    mFrameErrorRate.Reset();
    mMessageErrorRate.Reset();
}

void LinkQualityInfo::AddRss(int8_t aRss)
{
    uint8_t oldLinkQuality = kNoLinkQuality;

    VerifyOrExit(aRss != OT_RADIO_RSSI_INVALID, OT_NOOP);

    mLastRss = aRss;

    if (mRssAverager.HasAverage())
    {
        oldLinkQuality = GetLinkQuality();
    }

    SuccessOrExit(mRssAverager.Add(aRss));

    SetLinkQuality(CalculateLinkQuality(GetLinkMargin(), oldLinkQuality));

exit:
    return;
}

uint8_t LinkQualityInfo::GetLinkMargin(void) const
{
    return ConvertRssToLinkMargin(Get<Mac::SubMac>().GetNoiseFloor(), GetAverageRss());
}

LinkQualityInfo::InfoString LinkQualityInfo::ToInfoString(void) const
{
    return InfoString("aveRss:%s, lastRss:%d, linkQuality:%d", mRssAverager.ToString().AsCString(), GetLastRss(),
                      GetLinkQuality());
}

uint8_t LinkQualityInfo::ConvertRssToLinkMargin(int8_t aNoiseFloor, int8_t aRss)
{
    int8_t linkMargin = aRss - aNoiseFloor;

    if (linkMargin < 0 || aRss == OT_RADIO_RSSI_INVALID)
    {
        linkMargin = 0;
    }

    return static_cast<uint8_t>(linkMargin);
}

uint8_t LinkQualityInfo::ConvertLinkMarginToLinkQuality(uint8_t aLinkMargin)
{
    return CalculateLinkQuality(aLinkMargin, kNoLinkQuality);
}

uint8_t LinkQualityInfo::ConvertRssToLinkQuality(int8_t aNoiseFloor, int8_t aRss)
{
    return ConvertLinkMarginToLinkQuality(ConvertRssToLinkMargin(aNoiseFloor, aRss));
}

int8_t LinkQualityInfo::ConvertLinkQualityToRss(int8_t aNoiseFloor, uint8_t aLinkQuality)
{
    int8_t linkmargin = 0;

    switch (aLinkQuality)
    {
    case 3:
        linkmargin = kLinkQuality3LinkMargin;
        break;

    case 2:
        linkmargin = kLinkQuality2LinkMargin;
        break;

    case 1:
        linkmargin = kLinkQuality1LinkMargin;
        break;

    default:
        linkmargin = kLinkQuality0LinkMargin;
        break;
    }

    return linkmargin + aNoiseFloor;
}

uint8_t LinkQualityInfo::CalculateLinkQuality(uint8_t aLinkMargin, uint8_t aLastLinkQuality)
{
    uint8_t threshold1, threshold2, threshold3;
    uint8_t linkQuality = 0;

    threshold1 = kThreshold1;
    threshold2 = kThreshold2;
    threshold3 = kThreshold3;

    // Apply the hysteresis threshold based on the last link quality value.

    switch (aLastLinkQuality)
    {
    case 0:
        threshold1 += kHysteresisThreshold;

        // fall-through

    case 1:
        threshold2 += kHysteresisThreshold;

        // fall-through

    case 2:
        threshold3 += kHysteresisThreshold;

        // fall-through

    default:
        break;
    }

    if (aLinkMargin > threshold3)
    {
        linkQuality = 3;
    }
    else if (aLinkMargin > threshold2)
    {
        linkQuality = 2;
    }
    else if (aLinkMargin > threshold1)
    {
        linkQuality = 1;
    }

    return linkQuality;
}

} // namespace ot
<commit_msg>[link-quality] optimize RSS calculation (#5508)<commit_after>/*
 *  Copyright (c) 2016, The OpenThread Authors.
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. 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.
 *  3. Neither the name of the copyright holder nor the
 *     names of its contributors may be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 *  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
 *   This file implements link quality information processing and storage.
 */

#include "link_quality.hpp"

#include <stdio.h>

#include "common/code_utils.hpp"
#include "common/instance.hpp"
#include "common/locator-getters.hpp"

namespace ot {

// This array gives the decimal point digits representing 0/8, 1/8, ..., 7/8 (does not include the '.').
static const char *const kDigitsString[8] = {
    // 0/8,  1/8,   2/8,   3/8,   4/8,   5/8,   6/8,   7/8
    "0", "125", "25", "375", "5", "625", "75", "875"};

void SuccessRateTracker::AddSample(bool aSuccess, uint16_t aWeight)
{
    uint32_t oldAverage = mFailureRate;
    uint32_t newValue   = (aSuccess) ? 0 : kMaxRateValue;
    uint32_t n          = aWeight;

    // `n/2` is added to the sum to ensure rounding the value to the nearest integer when dividing by `n`
    // (e.g., 1.2 -> 1, 3.5 -> 4).

    mFailureRate = static_cast<uint16_t>(((oldAverage * (n - 1)) + newValue + (n / 2)) / n);
}

void RssAverager::Reset(void)
{
    mAverage = 0;
    mCount   = 0;
}

otError RssAverager::Add(int8_t aRss)
{
    otError  error = OT_ERROR_NONE;
    uint16_t newValue;

    VerifyOrExit(aRss != OT_RADIO_RSSI_INVALID, error = OT_ERROR_INVALID_ARGS);

    // Restrict the RSS value to the closed range [0, -128] so the RSS times precision multiple can fit in 11 bits.
    if (aRss > 0)
    {
        aRss = 0;
    }

    // Multiply the RSS value by a precision multiple (currently -8).

    newValue = static_cast<uint16_t>(-aRss);
    newValue <<= kPrecisionBitShift;

    mCount += (mCount < (1 << kCoeffBitShift));
    // Maintain arithmetic mean.
    // newAverage = newValue * (1/mCount) + oldAverage * ((mCount -1)/mCount)
    mAverage = static_cast<uint16_t>(((mAverage * (mCount - 1)) + newValue) / mCount);

exit:
    return error;
}

int8_t RssAverager::GetAverage(void) const
{
    int8_t average;

    VerifyOrExit(mCount != 0, average = OT_RADIO_RSSI_INVALID);

    average = -static_cast<int8_t>(mAverage >> kPrecisionBitShift);

    // Check for possible round up (e.g., average of -71.5 --> -72)

    if ((mAverage & kPrecisionBitMask) >= (kPrecision >> 1))
    {
        average--;
    }

exit:
    return average;
}

RssAverager::InfoString RssAverager::ToString(void) const
{
    InfoString string;

    VerifyOrExit(mCount != 0, OT_NOOP);
    IgnoreError(string.Set("%d.%s", -(mAverage >> kPrecisionBitShift), kDigitsString[mAverage & kPrecisionBitMask]));

exit:
    return string;
}

void LinkQualityInfo::Clear(void)
{
    mRssAverager.Reset();
    SetLinkQuality(0);
    mLastRss = OT_RADIO_RSSI_INVALID;

    mFrameErrorRate.Reset();
    mMessageErrorRate.Reset();
}

void LinkQualityInfo::AddRss(int8_t aRss)
{
    uint8_t oldLinkQuality = kNoLinkQuality;

    VerifyOrExit(aRss != OT_RADIO_RSSI_INVALID, OT_NOOP);

    mLastRss = aRss;

    if (mRssAverager.HasAverage())
    {
        oldLinkQuality = GetLinkQuality();
    }

    SuccessOrExit(mRssAverager.Add(aRss));

    SetLinkQuality(CalculateLinkQuality(GetLinkMargin(), oldLinkQuality));

exit:
    return;
}

uint8_t LinkQualityInfo::GetLinkMargin(void) const
{
    return ConvertRssToLinkMargin(Get<Mac::SubMac>().GetNoiseFloor(), GetAverageRss());
}

LinkQualityInfo::InfoString LinkQualityInfo::ToInfoString(void) const
{
    return InfoString("aveRss:%s, lastRss:%d, linkQuality:%d", mRssAverager.ToString().AsCString(), GetLastRss(),
                      GetLinkQuality());
}

uint8_t LinkQualityInfo::ConvertRssToLinkMargin(int8_t aNoiseFloor, int8_t aRss)
{
    int8_t linkMargin = aRss - aNoiseFloor;

    if (linkMargin < 0 || aRss == OT_RADIO_RSSI_INVALID)
    {
        linkMargin = 0;
    }

    return static_cast<uint8_t>(linkMargin);
}

uint8_t LinkQualityInfo::ConvertLinkMarginToLinkQuality(uint8_t aLinkMargin)
{
    return CalculateLinkQuality(aLinkMargin, kNoLinkQuality);
}

uint8_t LinkQualityInfo::ConvertRssToLinkQuality(int8_t aNoiseFloor, int8_t aRss)
{
    return ConvertLinkMarginToLinkQuality(ConvertRssToLinkMargin(aNoiseFloor, aRss));
}

int8_t LinkQualityInfo::ConvertLinkQualityToRss(int8_t aNoiseFloor, uint8_t aLinkQuality)
{
    int8_t linkmargin = 0;

    switch (aLinkQuality)
    {
    case 3:
        linkmargin = kLinkQuality3LinkMargin;
        break;

    case 2:
        linkmargin = kLinkQuality2LinkMargin;
        break;

    case 1:
        linkmargin = kLinkQuality1LinkMargin;
        break;

    default:
        linkmargin = kLinkQuality0LinkMargin;
        break;
    }

    return linkmargin + aNoiseFloor;
}

uint8_t LinkQualityInfo::CalculateLinkQuality(uint8_t aLinkMargin, uint8_t aLastLinkQuality)
{
    uint8_t threshold1, threshold2, threshold3;
    uint8_t linkQuality = 0;

    threshold1 = kThreshold1;
    threshold2 = kThreshold2;
    threshold3 = kThreshold3;

    // Apply the hysteresis threshold based on the last link quality value.

    switch (aLastLinkQuality)
    {
    case 0:
        threshold1 += kHysteresisThreshold;

        // fall-through

    case 1:
        threshold2 += kHysteresisThreshold;

        // fall-through

    case 2:
        threshold3 += kHysteresisThreshold;

        // fall-through

    default:
        break;
    }

    if (aLinkMargin > threshold3)
    {
        linkQuality = 3;
    }
    else if (aLinkMargin > threshold2)
    {
        linkQuality = 2;
    }
    else if (aLinkMargin > threshold1)
    {
        linkQuality = 1;
    }

    return linkQuality;
}

} // namespace ot
<|endoftext|>


<commit_before>/*
 * Funambol is a mobile platform developed by Funambol, Inc.
 * Copyright (C) 2003 - 2007 Funambol, Inc.
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License version 3 as published by
 * the Free Software Foundation with the addition of the following permission
 * added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
 * WORK IN WHICH THE COPYRIGHT IS OWNED BY FUNAMBOL, FUNAMBOL DISCLAIMS THE
 * WARRANTY OF NON INFRINGEMENT  OF THIRD PARTY RIGHTS.
 *
 * This program 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 General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, see http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301 USA.
 *
 * You can contact Funambol, Inc. headquarters at 643 Bair Island Road, Suite
 * 305, Redwood City, CA 94063, USA, or at email address info@funambol.com.
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License version 3.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License
 * version 3, these Appropriate Legal Notices must retain the display of the
 * "Powered by Funambol" logo. If the display of the logo is not reasonably
 * feasible for technical reasons, the Appropriate Legal Notices must display
 * the words "Powered by Funambol".
 */

#include "push/FSocket.h"
#include "base/SymbianLog.h"
#include "base/util/stringUtils.h"
#include "base/util/symbianUtils.h"
#include "base/FConnection.h"
#include "base/globalsdef.h"

USE_NAMESPACE

StringBuffer FSocket::lIP;

FSocket* FSocket::createSocket(const StringBuffer& peer, int32_t port)
{
    return FSocket::NewL(peer, port);
}

FSocket* FSocket::NewL(const StringBuffer& peer, int32_t port)
{
    FSocket* self = FSocket::NewLC(peer, port);
    CleanupStack::Pop( self );

    if (self->getLastStatus() != KErrNone) {
        // Something wrong.
        delete self;
        return NULL;
    }
    return self;
}

FSocket* FSocket::NewLC(const StringBuffer& peer, int32_t port)
{
    FSocket* self = new ( ELeave ) FSocket();
    CleanupStack::PushL( self );
    self->ConstructL(peer, port);

    return self;
}

void FSocket::ConstructL(const StringBuffer& peer, int32_t port)
{
    //LOG.debug("FSocket::ConstructL");

    StringBuffer  errorMsg;
    RHostResolver resolver;
    RBuf          serverName;
    TNameEntry    hostAddress;
    TInetAddr     address;
    TInt          res = KErrNone;

    serverName.Assign(stringBufferToNewBuf(peer));

    //
    // Get the connection manager instance
    //
    FConnection* connection = FConnection::getInstance();
    if (!connection) {
        iStatus = -1;
        errorMsg = "Error opening connection";
        goto error;
    }
    // Session is owned by FConnection!
    RSocketServ* session = connection->getSession();

    //
    // Open the Client Socket tcp/ip
    //
#ifdef __WINSCW__
    // WINSCW: simply open the socket
    res = iSocket.Open(*session, KAfInet, KSockStream, KProtocolInetTcp);
#else
    // GCCE: use the existing connection
    // If first time, connect to gprs
    if (!connection->isConnected()) {
        LOG.debug("Starting connection");
        if (connection->startConnection()) {
            iStatus = -1;
            errorMsg = "Error starting connection";
            goto error;
        }
    }
    RConnection* conn = connection->getConnection();

    LOG.debug("Opening socket and associate with existing connection");
    res = iSocket.Open(*session, KAfInet, KSockStream, KProtocolInetTcp, *conn);
    //LOG.debug("Socket opened (err = %d)", res);
#endif
    if (res != KErrNone) {
        iStatus = -1;
        errorMsg.sprintf("FSocket : Error opening socket. code %d", res);
        goto error;
    }

    // This works if serverName is the ip address, like "x.y.z.w"
    res = address.Input(serverName);

    if (res != KErrNone) {
        //
        // Try to resolve the host address. (On GCCE, use the existing RConnection)
        //
        LOG.debug("Resolve IP address...");
#ifdef __WINSCW__
        res = resolver.Open(*session, KAfInet, KProtocolInetTcp);
#else
        res = resolver.Open(*session, KAfInet, KProtocolInetTcp, *conn);
#endif
        if (res != KErrNone) {
            iStatus = -2;
            errorMsg.sprintf("FSocket: Host resolver open failed. code %d", res);
            goto error;
        }

        resolver.GetByName(serverName, hostAddress, iStatus);
        User::WaitForRequest(iStatus);
        resolver.Close();
        if (iStatus != KErrNone) {
            errorMsg.sprintf("FSocket: DNS lookup failed. code %d", iStatus.Int());
            goto error;
        }

        // Set the socket server address/port
        address = hostAddress().iAddr;
    }

    address.SetPort(port);

    // --- Connect to host ---
    LOG.debug("Socket connect...");
    iSocket.Connect(address, iStatus);
    User::WaitForRequest(iStatus);
    if (iStatus != KErrNone) {
        errorMsg.sprintf("FSocket: Failed to connect to Server. code %d", iStatus.Int());
        goto error;
    }

    serverName.Close();
    return;

error:
    LOG.error(errorMsg.c_str());
    serverName.Close();
    return;
}

FSocket::FSocket()
{
    iStatus = KErrNone;
}

FSocket::~FSocket()
{
    close();
}

int32_t FSocket::writeBuffer(const int8_t* buffer, int32_t len)
{
    // This doesn't copy the buffer in memory.
    TPtr8 data((TUint8*)buffer, len);
    data.SetLength(len);

    // Sends data to the remote host.
    iSocket.Write(data, iStatus);
    User::WaitForRequest(iStatus);

    if (iStatus == KErrNone) {
        return len;
    }
    else {
        LOG.error("FSocket: error writing on socket (status = %d)", iStatus.Int());
        return -1;
    }
}

int32_t FSocket::readBuffer(int8_t* buffer, int32_t maxLen)
{
    StringBuffer errorMsg;

    RBuf8 data;
    data.CreateL(maxLen);

    // Receives data from a remote host and completes when data is available.
    TSockXfrLength len;
    iSocket.RecvOneOrMore(data, 0, iStatus, len);
    User::WaitForRequest(iStatus);

    TInt msgLen = len();

    if (iStatus == KErrNone) {
        if (msgLen <= maxLen) {
            // OK. Copy the message into the preallocated buffer.
            memcpy(buffer, data.Ptr(), msgLen);
            data.Close();
            return msgLen;
        }
        else {
            errorMsg.sprintf("FSocket: error reading, message too big (%d > %d) -> rejected.",
                             (int)msgLen, (int)maxLen);
            goto error;
        }
    }
    else if (iStatus == KErrEof) {
        // Either the remote connection is closed, or the socket has been shutdown.
        errorMsg = "FSocket: read interrupted (connection closed)";
        goto error;
    }
    else {
        errorMsg.sprintf("FSocket: error reading on socket (status = %d)", iStatus.Int());
        goto error;
    }

error:
    LOG.error(errorMsg.c_str());
    buffer = NULL;
    data.Close();
    return -1;
}

void FSocket::close()
{
    //LOG.debug("FSocket::close");

    // TODO: shutdown if I/O in progress?
    //iSocket.Shutdown(RSocket::EImmediate, iStatus);

    iSocket.CancelAll();
    iSocket.Close();
}

const StringBuffer& FSocket::address() const {
    return lAddress;
}

const StringBuffer& FSocket::peerAddress() const {
    return pAddress;
}

const StringBuffer& FSocket::localIP() {
    return lIP;
}
<commit_msg>Fixed distructor: CancelAll() function crashes in case the connection cannot start for network errors<commit_after>/*
 * Funambol is a mobile platform developed by Funambol, Inc.
 * Copyright (C) 2003 - 2007 Funambol, Inc.
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License version 3 as published by
 * the Free Software Foundation with the addition of the following permission
 * added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
 * WORK IN WHICH THE COPYRIGHT IS OWNED BY FUNAMBOL, FUNAMBOL DISCLAIMS THE
 * WARRANTY OF NON INFRINGEMENT  OF THIRD PARTY RIGHTS.
 *
 * This program 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 General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, see http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301 USA.
 *
 * You can contact Funambol, Inc. headquarters at 643 Bair Island Road, Suite
 * 305, Redwood City, CA 94063, USA, or at email address info@funambol.com.
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License version 3.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License
 * version 3, these Appropriate Legal Notices must retain the display of the
 * "Powered by Funambol" logo. If the display of the logo is not reasonably
 * feasible for technical reasons, the Appropriate Legal Notices must display
 * the words "Powered by Funambol".
 */

#include "push/FSocket.h"
#include "base/SymbianLog.h"
#include "base/util/stringUtils.h"
#include "base/util/symbianUtils.h"
#include "base/FConnection.h"
#include "base/globalsdef.h"

USE_NAMESPACE

StringBuffer FSocket::lIP;

FSocket* FSocket::createSocket(const StringBuffer& peer, int32_t port)
{
    return FSocket::NewL(peer, port);
}

FSocket* FSocket::NewL(const StringBuffer& peer, int32_t port)
{
    FSocket* self = FSocket::NewLC(peer, port);
    CleanupStack::Pop( self );

    if (self->getLastStatus() != KErrNone) {
        // Something wrong.
        LOG.debug("FSocket::NewL - error in ConstructL (status = %d)", self->getLastStatus());
        delete self;
        return NULL;
    }
    return self;
}

FSocket* FSocket::NewLC(const StringBuffer& peer, int32_t port)
{
    FSocket* self = new ( ELeave ) FSocket();
    CleanupStack::PushL( self );
    self->ConstructL(peer, port);

    return self;
}

void FSocket::ConstructL(const StringBuffer& peer, int32_t port)
{
    //LOG.debug("FSocket::ConstructL");

    StringBuffer  errorMsg;
    RHostResolver resolver;
    RBuf          serverName;
    TNameEntry    hostAddress;
    TInetAddr     address;
    TInt          res = KErrNone;

    serverName.Assign(stringBufferToNewBuf(peer));

    //
    // Get the connection manager instance
    //
    FConnection* connection = FConnection::getInstance();
    if (!connection) {
        iStatus = -1;
        errorMsg = "Error opening connection";
        goto error;
    }
    // Session is owned by FConnection!
    RSocketServ* session = connection->getSession();

    //
    // Open the Client Socket tcp/ip
    //
#ifdef __WINSCW__
    // WINSCW: simply open the socket
    res = iSocket.Open(*session, KAfInet, KSockStream, KProtocolInetTcp);
#else
    // GCCE: use the existing connection
    // If first time, connect to gprs
    if (!connection->isConnected()) {
        LOG.debug("FSocket: not connected, start new connection");
        if (connection->startConnection()) {
            iStatus = -1;
            errorMsg = "FSocket: error starting connection";
            goto error;
        }
    }
    RConnection* conn = connection->getConnection();

    LOG.debug("Opening socket and associate with existing connection");
    res = iSocket.Open(*session, KAfInet, KSockStream, KProtocolInetTcp, *conn);
    //LOG.debug("Socket opened (err = %d)", res);
#endif
    if (res != KErrNone) {
        iStatus = -1;
        errorMsg.sprintf("FSocket : Error opening socket. code %d", res);
        goto error;
    }

    // This works if serverName is the ip address, like "x.y.z.w"
    res = address.Input(serverName);

    if (res != KErrNone) {
        //
        // Try to resolve the host address. (On GCCE, use the existing RConnection)
        //
        LOG.debug("Resolve IP address...");
#ifdef __WINSCW__
        res = resolver.Open(*session, KAfInet, KProtocolInetTcp);
#else
        res = resolver.Open(*session, KAfInet, KProtocolInetTcp, *conn);
#endif
        if (res != KErrNone) {
            iStatus = -2;
            errorMsg.sprintf("FSocket: Host resolver open failed. code %d", res);
            goto error;
        }

        resolver.GetByName(serverName, hostAddress, iStatus);
        User::WaitForRequest(iStatus);
        resolver.Close();
        if (iStatus != KErrNone) {
            errorMsg.sprintf("FSocket: DNS lookup failed. code %d", iStatus.Int());
            goto error;
        }

        // Set the socket server address/port
        address = hostAddress().iAddr;
    }

    address.SetPort(port);

    // --- Connect to host ---
    LOG.debug("Socket connect...");
    iSocket.Connect(address, iStatus);
    User::WaitForRequest(iStatus);
    if (iStatus != KErrNone) {
        errorMsg.sprintf("FSocket: Failed to connect to Server. code %d", iStatus.Int());
        goto error;
    }

    serverName.Close();
    return;

error:
    LOG.error(errorMsg.c_str());
    serverName.Close();
    return;
}

FSocket::FSocket()
{
    iStatus = KErrNone;
}

FSocket::~FSocket()
{
    close();
}

int32_t FSocket::writeBuffer(const int8_t* buffer, int32_t len)
{
    // This doesn't copy the buffer in memory.
    TPtr8 data((TUint8*)buffer, len);
    data.SetLength(len);

    // Sends data to the remote host.
    iSocket.Write(data, iStatus);
    User::WaitForRequest(iStatus);

    if (iStatus == KErrNone) {
        return len;
    }
    else {
        LOG.error("FSocket: error writing on socket (status = %d)", iStatus.Int());
        return -1;
    }
}

int32_t FSocket::readBuffer(int8_t* buffer, int32_t maxLen)
{
    StringBuffer errorMsg;

    RBuf8 data;
    data.CreateL(maxLen);

    // Receives data from a remote host and completes when data is available.
    TSockXfrLength len;
    iSocket.RecvOneOrMore(data, 0, iStatus, len);
    User::WaitForRequest(iStatus);

    TInt msgLen = len();

    if (iStatus == KErrNone) {
        if (msgLen <= maxLen) {
            // OK. Copy the message into the preallocated buffer.
            memcpy(buffer, data.Ptr(), msgLen);
            data.Close();
            return msgLen;
        }
        else {
            errorMsg.sprintf("FSocket: error reading, message too big (%d > %d) -> rejected.",
                             (int)msgLen, (int)maxLen);
            goto error;
        }
    }
    else if (iStatus == KErrEof) {
        // Either the remote connection is closed, or the socket has been shutdown.
        errorMsg = "FSocket: read interrupted (connection closed)";
        goto error;
    }
    else {
        errorMsg.sprintf("FSocket: error reading on socket (status = %d)", iStatus.Int());
        goto error;
    }

error:
    LOG.error(errorMsg.c_str());
    buffer = NULL;
    data.Close();
    return -1;
}

void FSocket::close()
{
    //LOG.debug("FSocket::close");
    // TODO: shutdown if I/O in progress?
    //iSocket.Shutdown(RSocket::EImmediate, iStatus);

    // Seems that this call causes a crash when startConnection failed for network error
    //iSocket.CancelAll();

    iSocket.Close();
}

const StringBuffer& FSocket::address() const {
    return lAddress;
}

const StringBuffer& FSocket::peerAddress() const {
    return pAddress;
}

const StringBuffer& FSocket::localIP() {
    return lIP;
}
<|endoftext|>


<commit_before>#include "game/city/city.h"
#include "game/city/building.h"
#include "game/city/organisation.h"
#include "game/city/buildingtile.h"
#include "framework/framework.h"
#include "game/resources/gamecore.h"
#include <random>

namespace OpenApoc {

City::City(Framework &fw, UString mapName)
	: TileMap(fw, Vec3<int>{100, 100, 10}), organisations(Organisation::defaultOrganisations)
{
	auto file = fw.data->load_file("xcom3/ufodata/" + mapName);
	if (!file)
	{
		LogError("Failed to open city map \"%s\"", mapName.str().c_str());
		return;
	}

	this->buildings = loadBuildingsFromBld(fw, mapName + ".bld", this->organisations, Building::defaultNames);
	this->cityTiles = CityTile::loadTilesFromFile(fw);

	for (int z = 0; z < this->size.z; z++)
	{
		for (int y = 0; y < this->size.y; y++)
		{
			for (int x = 0; x < this->size.x; x++)
			{
				uint16_t tileID;
				if (!file.readule16(tileID))
				{
					LogError("Unexpected EOF reading citymap at %d,%d,%d",x,y,z);
					tileID = 0;
				}
				if (tileID)
				{
					Building *bld = nullptr;
					for (auto &b : this->buildings)
					{
						if (b.bounds.within(Vec2<int>{x,y}))
						{
							if (bld)
							{
								LogError("Multiple buildings on tile at %d,%d,%d", x, y, z);
							}
							bld = &b;
						}
					}
					if (tileID >= this->cityTiles.size())
					{
						LogError("Invalid tile IDX %u at %d,%d,%d", tileID, x, y, z);
					}
					else
					{
						auto tile = std::make_shared<BuildingSection>(*this, this->cityTiles[tileID], Vec3<int>{x,y,z}, bld);
						this->addObject(std::dynamic_pointer_cast<TileObject>(tile));
					}
				}
			}
		}
	}

	std::default_random_engine generator;
	std::uniform_int_distribution<int> xydistribution(0,99);
	std::uniform_int_distribution<int> zdistribution(8,9);
	//Place 1000 random cars
	LogInfo("Starting placing cars");
	for (int i = 0; i < 100; i++)
	{
		int x = 0;
		int y = 0;
		int z = 0;

		do {
			x = xydistribution(generator);
			y = xydistribution(generator);
			z = zdistribution(generator);
		} while (!this->getTile(x,y,z)->ownedObjects.empty());

		std::shared_ptr<Vehicle> testVehicle(fw.gamecore->vehicleFactory.create("POLICE_HOVERCAR", organisations[0]));
		this->vehicles.push_back(testVehicle);

		testVehicle->launch(*this, Vec3<float>{x,y,z});
	}
	LogInfo("Finished placing cars");
}

City::~City()
{

}

}; //namespace OpenApoc
<commit_msg>Add city tiles to collideableObject lists<commit_after>#include "game/city/city.h"
#include "game/city/building.h"
#include "game/city/organisation.h"
#include "game/city/buildingtile.h"
#include "framework/framework.h"
#include "game/resources/gamecore.h"
#include <random>

namespace OpenApoc {

City::City(Framework &fw, UString mapName)
	: TileMap(fw, Vec3<int>{100, 100, 10}), organisations(Organisation::defaultOrganisations)
{
	auto file = fw.data->load_file("xcom3/ufodata/" + mapName);
	if (!file)
	{
		LogError("Failed to open city map \"%s\"", mapName.str().c_str());
		return;
	}

	this->buildings = loadBuildingsFromBld(fw, mapName + ".bld", this->organisations, Building::defaultNames);
	this->cityTiles = CityTile::loadTilesFromFile(fw);

	for (int z = 0; z < this->size.z; z++)
	{
		for (int y = 0; y < this->size.y; y++)
		{
			for (int x = 0; x < this->size.x; x++)
			{
				uint16_t tileID;
				if (!file.readule16(tileID))
				{
					LogError("Unexpected EOF reading citymap at %d,%d,%d",x,y,z);
					tileID = 0;
				}
				if (tileID)
				{
					Building *bld = nullptr;
					for (auto &b : this->buildings)
					{
						if (b.bounds.within(Vec2<int>{x,y}))
						{
							if (bld)
							{
								LogError("Multiple buildings on tile at %d,%d,%d", x, y, z);
							}
							bld = &b;
						}
					}
					if (tileID >= this->cityTiles.size())
					{
						LogError("Invalid tile IDX %u at %d,%d,%d", tileID, x, y, z);
					}
					else
					{
						auto tile = std::make_shared<BuildingSection>(*this, this->cityTiles[tileID], Vec3<int>{x,y,z}, bld);
						this->addObject(std::dynamic_pointer_cast<TileObject>(tile));
						tile->setPosition(Vec3<int>{x,y,z});
					}
				}
			}
		}
	}

	std::default_random_engine generator;
	std::uniform_int_distribution<int> xydistribution(0,99);
	std::uniform_int_distribution<int> zdistribution(8,9);
	//Place 1000 random cars
	LogInfo("Starting placing cars");
	for (int i = 0; i < 100; i++)
	{
		int x = 0;
		int y = 0;
		int z = 0;

		do {
			x = xydistribution(generator);
			y = xydistribution(generator);
			z = zdistribution(generator);
		} while (!this->getTile(x,y,z)->ownedObjects.empty());

		std::shared_ptr<Vehicle> testVehicle(fw.gamecore->vehicleFactory.create("POLICE_HOVERCAR", organisations[0]));
		this->vehicles.push_back(testVehicle);

		testVehicle->launch(*this, Vec3<float>{x,y,z});
	}
	LogInfo("Finished placing cars");
}

City::~City()
{

}

}; //namespace OpenApoc
<|endoftext|>


<commit_before>#include "umundo/core.h"
#include <iostream>

using namespace umundo;

bool testRecursiveMutex() {
	Mutex mutex;
	UMUNDO_LOCK(mutex);
	if(!mutex.tryLock()) {
		LOG_ERR("tryLock should be possible from within the same thread");
		assert(false);
	}
	UMUNDO_LOCK(mutex);
	// can we unlock it as well?
	UMUNDO_UNLOCK(mutex);
	UMUNDO_UNLOCK(mutex);
	return true;
}

static Mutex testMutex;
bool testThreads() {
	class Thread1 : public Thread {
		void run() {
			if(testMutex.tryLock()) {
				LOG_ERR("tryLock should return false with a mutex locked in another thread");
				assert(false);
			}
			UMUNDO_LOCK(testMutex); // blocks
			Thread::sleepMs(50);
			UMUNDO_UNLOCK(testMutex);
			Thread::sleepMs(100);
		}
	};

	/**
	 * tl=tryLock, l=lock, u=unlock b=block, sN=sleep(N)
	 * thread1:        start tl l  l s50      u
	 *    main: start l s50       u s20  tl    l join
	 *    t->          0         50     70   100
	 */

	UMUNDO_LOCK(testMutex);
	Thread1 thread1;
	thread1.start();
	Thread::sleepMs(50); // thread1 will trylock and block on lock
	UMUNDO_UNLOCK(testMutex);  // unlock
	Thread::sleepMs(20); // yield cpu and sleep
	// thread1 sleeps with lock on mutex
	if(testMutex.tryLock()) {
		LOG_ERR("tryLock should return false with a mutex locked in another thread");
		assert(false);
	}
	UMUNDO_LOCK(testMutex);    // thread1 will unlock and sleep
	thread1.join();      // join with thread1
	if(thread1.isStarted()) {
		LOG_ERR("thread still running after join");
		assert(false);
	}
	return true;
}

static Monitor testMonitor;
static int passedMonitor = 0;
bool testMonitors() {
	struct TestThread : public Thread {
		int _ms;
		TestThread(int ms) : _ms(ms) {}
		void run() {
			testMonitor.wait();
			Thread::sleepMs(10); // avoid clash with other threads
			passedMonitor++;
		}
	};

	TestThread thread1(0);
	TestThread thread2(5);
	TestThread thread3(10);

	for (int i = 0; i < 10; i++) {
		passedMonitor = 0;

		// all will block on monitor
		thread1.start();
		thread2.start();
		thread3.start();
		Thread::sleepMs(5); // give threads a chance to run into wait
		if(passedMonitor != 0) {
			LOG_ERR("%d threads already passed the monitor", passedMonitor);
			assert(false);
		}
		UMUNDO_SIGNAL(testMonitor); // signal a single thread
		Thread::sleepMs(40); // thread will increase passedMonitor
		if(passedMonitor != 1) {
			LOG_ERR("Expected 1 threads to pass the monitor, but %d did", passedMonitor);
			assert(false);
		}
		UMUNDO_BROADCAST(testMonitor); // signal all other threads
		Thread::sleepMs(40);

		if (thread1.isStarted() || thread2.isStarted() || thread3.isStarted()) {
			LOG_ERR("Threads ran to completion but still insist on being started");
			assert(false);
		}
	}
	return true;
}

static Monitor testTimedMonitor;
static int passedTimedMonitor = 0;
bool testTimedMonitors() {
	struct TestThread : public Thread {
		int _ms;
		TestThread(int ms) : _ms(ms) {}
		void run() {
			testTimedMonitor.wait(_ms);
			passedTimedMonitor++;
		}
	};

	TestThread thread1(60);
	TestThread thread2(0); // waits forever
	TestThread thread3(0); // waits forever
	TestThread thread4(0); // waits forever
	TestThread thread5(0); // waits forever

	for (int i = 0; i < 10; i++) {
		// test waiting for a given time
		passedTimedMonitor = 0;
		thread1.start(); // wait for 15ms at mutex before resuming
		Thread::sleepMs(10);
		assert(passedTimedMonitor == 0); // thread1 should not have passed
		Thread::sleepMs(100);
		assert(passedTimedMonitor == 1); // thread1 should have passed
		assert(!thread1.isStarted());

		// test signalling a set of threads
		passedTimedMonitor = 0;
		thread2.start();
		thread3.start();
		thread4.start();
		thread5.start();
		Thread::sleepMs(20);
		testTimedMonitor.signal(2); // signal 2 threads
		Thread::sleepMs(50);
		assert(passedTimedMonitor == 2);
		testTimedMonitor.signal(1); // signal another thread
		Thread::sleepMs(50);
		assert(passedTimedMonitor == 3);
		testTimedMonitor.broadcast(); // signal last thread
		Thread::sleepMs(50);
		assert(passedTimedMonitor == 4);
		assert(!thread2.isStarted() && !thread3.isStarted() && !thread4.isStarted() && !thread5.isStarted());

		// test timed and unlimited waiting
		passedTimedMonitor = 0;
		thread1.start();
		thread2.start(); // with another thread
		thread3.start(); // with another thread
		Thread::sleepMs(10);
		testTimedMonitor.signal(); // explicit signal
		Thread::sleepMs(30);
		assert(passedTimedMonitor == 1);
		// wo do not know which thread passed
		assert(!thread1.isStarted() || !thread2.isStarted() || !thread3.isStarted());
		if (thread1.isStarted()) {
			// thread1 is still running, just wait
			Thread::sleepMs(100);
			assert(passedTimedMonitor == 2);
		}
		testTimedMonitor.broadcast(); // explicit signal
		Thread::sleepMs(100);
		assert(passedTimedMonitor == 3);
		assert(!thread1.isStarted() && !thread2.isStarted() && !thread3.isStarted());

		// test signalling prior to waiting
		passedTimedMonitor = 0;
		testTimedMonitor.signal();
		thread1.start();
		thread2.start();
		Thread::sleepMs(200);
		assert(passedTimedMonitor == 1);
		assert(!thread1.isStarted());
		assert(thread2.isStarted());
		testTimedMonitor.signal();
		Thread::sleepMs(40);
		assert(passedTimedMonitor == 2);

		assert(!thread1.isStarted());
		assert(!thread2.isStarted());
		assert(!thread3.isStarted());

	}
	return true;
}

class FooTracer : public Traceable, public Thread {
	void run() {
		while(isStarted()) {
			trace("This is foo");
			Thread::sleepMs(20);
		}
	}

	void retrace(const std::string& msg, std::map<std::string, std::string> info) {
	};

};

bool testTracing() {
	FooTracer* tr1 = new FooTracer();
	FooTracer* tr2 = new FooTracer();
	FooTracer* tr3 = new FooTracer();

	tr1->setTraceFile("trace.txt");
	tr2->setTraceFile("trace.txt");
	tr3->setTraceFile("trace.txt");

	tr1->start();
	tr2->start();
	tr3->start();

	Thread::sleepMs(100);
	delete tr1;
	delete tr2;
	delete tr3;

	Thread::sleepMs(100);

	FooTracer* tr4 = new FooTracer();
	tr4->replay("trace.txt");

	return true;
}

int main(int argc, char** argv) {
	if(!testTracing())
		return EXIT_FAILURE;
	if(!testRecursiveMutex())
		return EXIT_FAILURE;
	if(!testThreads())
		return EXIT_FAILURE;
	if(!testMonitors())
		return EXIT_FAILURE;
	if(!testTimedMonitors())
		return EXIT_FAILURE;
}
<commit_msg>more timeout issues<commit_after>#include "umundo/core.h"
#include <iostream>

using namespace umundo;

bool testRecursiveMutex() {
	Mutex mutex;
	UMUNDO_LOCK(mutex);
	if(!mutex.tryLock()) {
		LOG_ERR("tryLock should be possible from within the same thread");
		assert(false);
	}
	UMUNDO_LOCK(mutex);
	// can we unlock it as well?
	UMUNDO_UNLOCK(mutex);
	UMUNDO_UNLOCK(mutex);
	return true;
}

static Mutex testMutex;
bool testThreads() {
	class Thread1 : public Thread {
		void run() {
			if(testMutex.tryLock()) {
				LOG_ERR("tryLock should return false with a mutex locked in another thread");
				assert(false);
			}
			UMUNDO_LOCK(testMutex); // blocks
			Thread::sleepMs(50);
			UMUNDO_UNLOCK(testMutex);
			Thread::sleepMs(100);
		}
	};

	/**
	 * tl=tryLock, l=lock, u=unlock b=block, sN=sleep(N)
	 * thread1:        start tl l  l s50      u
	 *    main: start l s50       u s20  tl    l join
	 *    t->          0         50     70   100
	 */

	UMUNDO_LOCK(testMutex);
	Thread1 thread1;
	thread1.start();
	Thread::sleepMs(50); // thread1 will trylock and block on lock
	UMUNDO_UNLOCK(testMutex);  // unlock
	Thread::sleepMs(20); // yield cpu and sleep
	// thread1 sleeps with lock on mutex
	if(testMutex.tryLock()) {
		LOG_ERR("tryLock should return false with a mutex locked in another thread");
		assert(false);
	}
	UMUNDO_LOCK(testMutex);    // thread1 will unlock and sleep
	thread1.join();      // join with thread1
	if(thread1.isStarted()) {
		LOG_ERR("thread still running after join");
		assert(false);
	}
	return true;
}

static Monitor testMonitor;
static int passedMonitor = 0;
bool testMonitors() {
	struct TestThread : public Thread {
		int _ms;
		TestThread(int ms) : _ms(ms) {}
		void run() {
			testMonitor.wait();
			Thread::sleepMs(10); // avoid clash with other threads
			passedMonitor++;
		}
	};

	TestThread thread1(0);
	TestThread thread2(5);
	TestThread thread3(10);

	for (int i = 0; i < 10; i++) {
		passedMonitor = 0;

		// all will block on monitor
		thread1.start();
		thread2.start();
		thread3.start();
		Thread::sleepMs(5); // give threads a chance to run into wait
		if(passedMonitor != 0) {
			LOG_ERR("%d threads already passed the monitor", passedMonitor);
			assert(false);
		}
		UMUNDO_SIGNAL(testMonitor); // signal a single thread
		Thread::sleepMs(40); // thread will increase passedMonitor
		if(passedMonitor != 1) {
			LOG_ERR("Expected 1 threads to pass the monitor, but %d did", passedMonitor);
			assert(false);
		}
		UMUNDO_BROADCAST(testMonitor); // signal all other threads
		Thread::sleepMs(40);

		if (thread1.isStarted() || thread2.isStarted() || thread3.isStarted()) {
			LOG_ERR("Threads ran to completion but still insist on being started");
			assert(false);
		}
	}
	return true;
}

static Monitor testTimedMonitor;
static int passedTimedMonitor = 0;
bool testTimedMonitors() {
	struct TestThread : public Thread {
		int _ms;
		TestThread(int ms) : _ms(ms) {}
		void run() {
			testTimedMonitor.wait(_ms);
			passedTimedMonitor++;
		}
	};

	TestThread thread1(60);
	TestThread thread2(0); // waits forever
	TestThread thread3(0); // waits forever
	TestThread thread4(0); // waits forever
	TestThread thread5(0); // waits forever

	for (int i = 0; i < 10; i++) {
		// test waiting for a given time
		passedTimedMonitor = 0;
		thread1.start(); // wait for 15ms at mutex before resuming
		Thread::sleepMs(10);
		assert(passedTimedMonitor == 0); // thread1 should not have passed
		Thread::sleepMs(100);
		assert(passedTimedMonitor == 1); // thread1 should have passed
		assert(!thread1.isStarted());

		// test signalling a set of threads
		passedTimedMonitor = 0;
		thread2.start();
		thread3.start();
		thread4.start();
		thread5.start();
		Thread::sleepMs(20);
		testTimedMonitor.signal(2); // signal 2 threads
		Thread::sleepMs(50);
		assert(passedTimedMonitor == 2);
		testTimedMonitor.signal(1); // signal another thread
		Thread::sleepMs(50);
		assert(passedTimedMonitor == 3);
		testTimedMonitor.broadcast(); // signal last thread
		Thread::sleepMs(50);
		assert(passedTimedMonitor == 4);
		assert(!thread2.isStarted() && !thread3.isStarted() && !thread4.isStarted() && !thread5.isStarted());

		// test timed and unlimited waiting
		passedTimedMonitor = 0;
		thread1.start();
		thread2.start(); // with another thread
		thread3.start(); // with another thread
		Thread::sleepMs(10);
		testTimedMonitor.signal(); // explicit signal
		Thread::sleepMs(30);
		assert(passedTimedMonitor == 1);
		// wo do not know which thread passed
		assert(!thread1.isStarted() || !thread2.isStarted() || !thread3.isStarted());
		if (thread1.isStarted()) {
			// thread1 is still running, just wait
			Thread::sleepMs(100);
			assert(passedTimedMonitor == 2);
		}
		testTimedMonitor.broadcast(); // explicit signal
		Thread::sleepMs(100);
		assert(passedTimedMonitor == 3);
		assert(!thread1.isStarted() && !thread2.isStarted() && !thread3.isStarted());

		// test signalling prior to waiting
		passedTimedMonitor = 0;
		testTimedMonitor.signal();
		thread1.start();
		thread2.start();
		Thread::sleepMs(400);
		assert(passedTimedMonitor == 1);
		assert(!thread1.isStarted());
		assert(thread2.isStarted());
		testTimedMonitor.signal();
		Thread::sleepMs(40);
		assert(passedTimedMonitor == 2);

		assert(!thread1.isStarted());
		assert(!thread2.isStarted());
		assert(!thread3.isStarted());

	}
	return true;
}

class FooTracer : public Traceable, public Thread {
	void run() {
		while(isStarted()) {
			trace("This is foo");
			Thread::sleepMs(20);
		}
	}

	void retrace(const std::string& msg, std::map<std::string, std::string> info) {
	};

};

bool testTracing() {
	FooTracer* tr1 = new FooTracer();
	FooTracer* tr2 = new FooTracer();
	FooTracer* tr3 = new FooTracer();

	tr1->setTraceFile("trace.txt");
	tr2->setTraceFile("trace.txt");
	tr3->setTraceFile("trace.txt");

	tr1->start();
	tr2->start();
	tr3->start();

	Thread::sleepMs(100);
	delete tr1;
	delete tr2;
	delete tr3;

	Thread::sleepMs(100);

	FooTracer* tr4 = new FooTracer();
	tr4->replay("trace.txt");

	return true;
}

int main(int argc, char** argv) {
	if(!testTracing())
		return EXIT_FAILURE;
	if(!testRecursiveMutex())
		return EXIT_FAILURE;
	if(!testThreads())
		return EXIT_FAILURE;
	if(!testMonitors())
		return EXIT_FAILURE;
	if(!testTimedMonitors())
		return EXIT_FAILURE;
}
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** Contact: Qt Software Information (qt-info@nokia.com)
**
** This file is part of the QtDeclarative module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the either Technology Preview License Agreement or the
** Beta Release License Agreement.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain
** additional rights. These rights are described in the Nokia Qt LGPL
** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
** package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at qt-sales@nokia.com.
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qfxpixmap.h"
#include <QHash>
#include <QNetworkReply>
#include <QPixmapCache>
#include <private/qfxperf_p.h>
#include <QtDeclarative/qmlengine.h>
#include <QFile>
#include <QtCore/qdebug.h>

QT_BEGIN_NAMESPACE
class QSharedNetworkReply;
typedef QHash<QString, QSharedNetworkReply *> QFxSharedNetworkReplyHash;
static QFxSharedNetworkReplyHash qfxActiveNetworkReplies;

class QSharedNetworkReply
{
public:
    QSharedNetworkReply(QNetworkReply *r) : reply(r), refCount(1) {}
    ~QSharedNetworkReply()
    {
        reply->deleteLater();
    }
    QNetworkReply *reply;

    int refCount;
    void addRef()
    {
        ++refCount;
    }
    void release()
    {
        Q_ASSERT(refCount > 0);
        --refCount;
        if (refCount == 0) {
            QString key = reply->url().toString();
            qfxActiveNetworkReplies.remove(key);
            delete this;
        }
    }
};

class QFxPixmapPrivate
{
public:
    QFxPixmapPrivate() {}

    QPixmap pixmap;
};

/*!
    \internal
    \class QFxPixmap
    \ingroup group_utility
    \brief Enacapsultes a pixmap for QFx items.

    This class is NOT reentrant.
    The pixmap cache will grow indefinately.
 */
QFxPixmap::QFxPixmap()
: d(new QFxPixmapPrivate)
{
}

QFxPixmap::QFxPixmap(const QUrl &url)
: d(new QFxPixmapPrivate)
{
#ifdef Q_ENABLE_PERFORMANCE_LOG
    QFxPerfTimer<QFxPerf::PixmapLoad> perf;
#endif
#ifndef QT_NO_LOCALFILE_OPTIMIZED_QML
    if (url.scheme()==QLatin1String("file")) {
        d->pixmap.load(url.toLocalFile());
    } else
#endif
    {
        QString key = url.toString();
        if (!QPixmapCache::find(key,&d->pixmap)) {
            QFxSharedNetworkReplyHash::Iterator iter = qfxActiveNetworkReplies.find(key);
            if (iter == qfxActiveNetworkReplies.end()) {
                // API usage error
                qWarning() << "QFxPixmap: URL not loaded" << url;
            } else {
                if ((*iter)->reply->error()) {
                    qWarning() << "Network error loading" << url << (*iter)->reply->errorString();
                } else {
                    QImage img;
                    if (img.load((*iter)->reply, 0)) {
                        d->pixmap = QPixmap::fromImage(img);
                        QPixmapCache::insert(key, d->pixmap);
                    } else {
                        qWarning() << "Format error loading" << url;
                    }
                }
                (*iter)->release();
            }
        }
    }
}

QFxPixmap::QFxPixmap(const QFxPixmap &o)
: d(new QFxPixmapPrivate)
{
    d->pixmap = o.d->pixmap;
}

QFxPixmap::~QFxPixmap()
{
    delete d;
}

QFxPixmap &QFxPixmap::operator=(const QFxPixmap &o)
{
    d->pixmap = o.d->pixmap;
    return *this;
}

bool QFxPixmap::isNull() const
{
    return d->pixmap.isNull();
}

int QFxPixmap::width() const
{
    return d->pixmap.width();
}

int QFxPixmap::height() const
{
    return d->pixmap.height();
}

QFxPixmap::operator const QPixmap &() const
{
    return d->pixmap;
}

/*!
    Starts a network request to load \a url. When the URL is loaded,
    the given slot is invoked. Note that if the image is already cached,
    the slot may be invoked immediately.

    Returns a QNetworkReply if the image is not immediately available, otherwise
    returns 0.  The QNetworkReply must not be stored - it may be destroyed at any time.
*/
QNetworkReply *QFxPixmap::get(QmlEngine *engine, const QUrl& url, QObject* obj, const char* slot)
{
#ifndef QT_NO_LOCALFILE_OPTIMIZED_QML
    if (url.scheme()==QLatin1String("file")) {
        QObject dummy;
        QObject::connect(&dummy, SIGNAL(destroyed()), obj, slot);
        return 0;
    }
#endif

    QString key = url.toString();
    if (QPixmapCache::find(key,0)) {
        QObject dummy;
        QObject::connect(&dummy, SIGNAL(destroyed()), obj, slot);
        return 0;
    }

    QFxSharedNetworkReplyHash::Iterator iter = qfxActiveNetworkReplies.find(key);
    if (iter == qfxActiveNetworkReplies.end()) {
        QNetworkRequest req(url);
        req.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
        QSharedNetworkReply *item = new QSharedNetworkReply(engine->networkAccessManager()->get(req));
        iter = qfxActiveNetworkReplies.insert(key, item);
    } else {
        (*iter)->addRef();
    }

    QObject::connect((*iter)->reply, SIGNAL(finished()), obj, slot);
    return (*iter)->reply;
}

/*!
    Stops the given slot being invoked if the given url finishes loading.
    May also cancel loading (eg. if no other pending request).

    Any connections to the QNetworkReply returned by get() will be
    disconnected.
*/
void QFxPixmap::cancelGet(const QUrl& url, QObject* obj)
{
    QString key = url.toString();
    QFxSharedNetworkReplyHash::Iterator iter = qfxActiveNetworkReplies.find(key);
    if (iter == qfxActiveNetworkReplies.end())
        return;
    QObject::disconnect((*iter)->reply, 0, obj, 0);
    (*iter)->release();
}

QT_END_NAMESPACE
<commit_msg>Add some (disabled) test code for limiting image size. QFxImage et al could benefit from a limit feature, especially for remote images.<commit_after>/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** Contact: Qt Software Information (qt-info@nokia.com)
**
** This file is part of the QtDeclarative module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the either Technology Preview License Agreement or the
** Beta Release License Agreement.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain
** additional rights. These rights are described in the Nokia Qt LGPL
** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
** package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at qt-sales@nokia.com.
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qfxpixmap.h"
#include <QImageReader>
#include <QHash>
#include <QNetworkReply>
#include <QPixmapCache>
#include <private/qfxperf_p.h>
#include <QtDeclarative/qmlengine.h>
#include <QFile>
#include <QtCore/qdebug.h>

QT_BEGIN_NAMESPACE
class QSharedNetworkReply;
typedef QHash<QString, QSharedNetworkReply *> QFxSharedNetworkReplyHash;
static QFxSharedNetworkReplyHash qfxActiveNetworkReplies;

class QSharedNetworkReply
{
public:
    QSharedNetworkReply(QNetworkReply *r) : reply(r), refCount(1) {}
    ~QSharedNetworkReply()
    {
        reply->deleteLater();
    }
    QNetworkReply *reply;

    int refCount;
    void addRef()
    {
        ++refCount;
    }
    void release()
    {
        Q_ASSERT(refCount > 0);
        --refCount;
        if (refCount == 0) {
            QString key = reply->url().toString();
            qfxActiveNetworkReplies.remove(key);
            delete this;
        }
    }
};

class QFxPixmapPrivate
{
public:
    QFxPixmapPrivate() {}

    QPixmap pixmap;
};

/*!
    \internal
    \class QFxPixmap
    \ingroup group_utility
    \brief Enacapsultes a pixmap for QFx items.

    This class is NOT reentrant.
    The pixmap cache will grow indefinately.
 */
QFxPixmap::QFxPixmap()
: d(new QFxPixmapPrivate)
{
}

QFxPixmap::QFxPixmap(const QUrl &url)
: d(new QFxPixmapPrivate)
{
#ifdef Q_ENABLE_PERFORMANCE_LOG
    QFxPerfTimer<QFxPerf::PixmapLoad> perf;
#endif
#ifndef QT_NO_LOCALFILE_OPTIMIZED_QML
    if (url.scheme()==QLatin1String("file")) {
        d->pixmap.load(url.toLocalFile());
    } else
#endif
    {
        QString key = url.toString();
        if (!QPixmapCache::find(key,&d->pixmap)) {
            QFxSharedNetworkReplyHash::Iterator iter = qfxActiveNetworkReplies.find(key);
            if (iter == qfxActiveNetworkReplies.end()) {
                // API usage error
                qWarning() << "QFxPixmap: URL not loaded" << url;
            } else {
                if ((*iter)->reply->error()) {
                    qWarning() << "Network error loading" << url << (*iter)->reply->errorString();
                } else {
                    QImageReader imgio((*iter)->reply);

//#define QT_TEST_SCALED_SIZE
#ifdef QT_TEST_SCALED_SIZE
                    /*
                    Some mechanism is needed for loading images at a limited size, especially
                    for remote images. Loading only thumbnails of remote progressive JPEG
                    images can be efficient. (Qt jpeg handler does not do so currently)
                    */

                    QSize limit(60,60);
                    QSize sz = imgio.size();
                    if (sz.width() > limit.width() || sz.height() > limit.height()) {
                        sz.scale(limit,Qt::KeepAspectRatio);
                        imgio.setScaledSize(sz);
                    }
#endif

                    QImage img;
                    if (imgio.read(&img)) {
#ifdef QT_TEST_SCALED_SIZE
                        if (!sz.isValid())
                            img = img.scaled(limit,Qt::KeepAspectRatio);
#endif
                        d->pixmap = QPixmap::fromImage(img);
                        QPixmapCache::insert(key, d->pixmap);
                    } else {
                        qWarning() << "Format error loading" << url;
                    }
                }
                (*iter)->release();
            }
        }
    }
}

QFxPixmap::QFxPixmap(const QFxPixmap &o)
: d(new QFxPixmapPrivate)
{
    d->pixmap = o.d->pixmap;
}

QFxPixmap::~QFxPixmap()
{
    delete d;
}

QFxPixmap &QFxPixmap::operator=(const QFxPixmap &o)
{
    d->pixmap = o.d->pixmap;
    return *this;
}

bool QFxPixmap::isNull() const
{
    return d->pixmap.isNull();
}

int QFxPixmap::width() const
{
    return d->pixmap.width();
}

int QFxPixmap::height() const
{
    return d->pixmap.height();
}

QFxPixmap::operator const QPixmap &() const
{
    return d->pixmap;
}

/*!
    Starts a network request to load \a url. When the URL is loaded,
    the given slot is invoked. Note that if the image is already cached,
    the slot may be invoked immediately.

    Returns a QNetworkReply if the image is not immediately available, otherwise
    returns 0.  The QNetworkReply must not be stored - it may be destroyed at any time.
*/
QNetworkReply *QFxPixmap::get(QmlEngine *engine, const QUrl& url, QObject* obj, const char* slot)
{
#ifndef QT_NO_LOCALFILE_OPTIMIZED_QML
    if (url.scheme()==QLatin1String("file")) {
        QObject dummy;
        QObject::connect(&dummy, SIGNAL(destroyed()), obj, slot);
        return 0;
    }
#endif

    QString key = url.toString();
    if (QPixmapCache::find(key,0)) {
        QObject dummy;
        QObject::connect(&dummy, SIGNAL(destroyed()), obj, slot);
        return 0;
    }

    QFxSharedNetworkReplyHash::Iterator iter = qfxActiveNetworkReplies.find(key);
    if (iter == qfxActiveNetworkReplies.end()) {
        QNetworkRequest req(url);
        req.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
        QSharedNetworkReply *item = new QSharedNetworkReply(engine->networkAccessManager()->get(req));
        iter = qfxActiveNetworkReplies.insert(key, item);
    } else {
        (*iter)->addRef();
    }

    QObject::connect((*iter)->reply, SIGNAL(finished()), obj, slot);
    return (*iter)->reply;
}

/*!
    Stops the given slot being invoked if the given url finishes loading.
    May also cancel loading (eg. if no other pending request).

    Any connections to the QNetworkReply returned by get() will be
    disconnected.
*/
void QFxPixmap::cancelGet(const QUrl& url, QObject* obj)
{
    QString key = url.toString();
    QFxSharedNetworkReplyHash::Iterator iter = qfxActiveNetworkReplies.find(key);
    if (iter == qfxActiveNetworkReplies.end())
        return;
    QObject::disconnect((*iter)->reply, 0, obj, 0);
    (*iter)->release();
}

QT_END_NAMESPACE
<|endoftext|>


<commit_before>//=============================================================================
// ■ VMGS/Scene/EditorMainScene.cpp
//-----------------------------------------------------------------------------
//   编辑器场景。
//=============================================================================

#include "../VMGS.hpp"

#include "../Control/GodView.hpp"
#include "../Control/FirstPersonView.hpp"
#include "../GameObject/SkyBox/SkyBox.hpp"

namespace VM76 {

	GodView* ctl;
	FirstPersonView* ctl_fp;
	SkyBox* sky;

	bool fp_control = false;

	//-------------------------------------------------------------------------
	// ● 场景开始
	//-------------------------------------------------------------------------
	EditorMainScene::EditorMainScene() {
		obj = new GObject();

		shader_textured.add_file(GL_VERTEX_SHADER, "../Media/shaders/gbuffers_textured.vsh");
		shader_textured.add_file(GL_FRAGMENT_SHADER, "../Media/shaders/gbuffers_textured.fsh");
		shader_textured.link_program();
		shader_basic.add_file(GL_VERTEX_SHADER, "../Media/shaders/gbuffers_basic.vsh");
		shader_basic.add_file(GL_FRAGMENT_SHADER, "../Media/shaders/gbuffers_basic.fsh");
		shader_basic.link_program();
		gui.add_file(GL_VERTEX_SHADER, "../Media/shaders/gui.vsh");
		gui.add_file(GL_FRAGMENT_SHADER, "../Media/shaders/gui.fsh");
		gui.link_program();
		post_processing.add_file(GL_VERTEX_SHADER, "../Media/shaders/PostProcessing.vsh");
		post_processing.add_file(GL_FRAGMENT_SHADER, "../Media/shaders/PostProcessing.fsh");
		post_processing.link_program();

		GLuint* gbuffers_type = new GLuint[3]{GL_RGB8, GL_RGB16F, GL_RGB8};
		postBuffer = new RenderBuffer(VMDE->width, VMDE->height, 3, gbuffers_type);

		projection = glm::perspective(1.3f, aspect_ratio, 0.1f, 1000.0f);
		view = glm::lookAt(
			glm::vec3(0.0, 2.6, 0.0),
			glm::vec3(0.0, 0.0, 0.0),
			glm::vec3(0.0, 1.0, 0.0)
		);

		// Set up hand block indicator's matrix
		glm::mat4 block_display = glm::translate(
			glm::mat4(1.0),
			glm::vec3(0.02, 0.06, 0.2)
		);
		block_display = glm::scale(block_display, glm::vec3(0.1f));
		block_display = glm::rotate(block_display,
			VMath::PIf / 4.0f,
			glm::vec3(1.0, 0.0, 0.0)
		);
		block_display = glm::rotate(block_display,
			VMath::PIf / 4.0f,
			glm::vec3(0.0, 1.0, 0.0)
		);

		TiledMap::init_cinstances(clist);
		/*for (int i = 0; i < 16; i++) {
			clist[i]->mat[0] = new glm::mat4[1]; clist[i]->mat[0][0] = block_display;
			clist[i]->mat[1] = new glm::mat4[1]; clist[i]->mat[1][0] = block_display;
			clist[i]->mat[2] = new glm::mat4[1]; clist[i]->mat[2][0] = block_display;
			clist[i]->mat[3] = new glm::mat4[1]; clist[i]->mat[3][0] = block_display;
			clist[i]->mat[4] = new glm::mat4[1]; clist[i]->mat[4][0] = block_display;
			clist[i]->mat[5] = new glm::mat4[1]; clist[i]->mat[5][0] = block_display;
			clist[i]->update_instance(1,1,1,1,1,1);
		}
		block_pointer.obj->data.mat_c = 1;*/

		ctl = new GodView();
		ctl_fp = new FirstPersonView();
		ctl->init_control();
		ctl_fp->init_control();
		ctl->cam.wpos = glm::vec3(64.0, 72.0, 64.0);
		ctl_fp->game_player.wpos = glm::vec3(64.0, 72.0, 64.0);

		sky = new SkyBox("../Media/skybox.png");
	}
	//-------------------------------------------------------------------------
	// ● 按键回调
	//-------------------------------------------------------------------------
	bool magnify = false;
	bool magnifyPrev = false;

	void EditorMainScene::key_callback(
		GLFWwindow* window, int key, int scancode, int action, int mods
	) {
		#define PRESS(n) key == n && action == GLFW_PRESS
		if (PRESS(GLFW_KEY_A)) obj->move(glm::vec3(-1.0, 0.0, 0.0));
		if (PRESS(GLFW_KEY_D)) obj->move(glm::vec3(1.0, 0.0, 0.0));
		if (PRESS(GLFW_KEY_W)) obj->move(glm::vec3(0.0, 0.0, -1.0));
		if (PRESS(GLFW_KEY_S)) obj->move(glm::vec3(0.0, 0.0, 1.0));
		if (PRESS(GLFW_KEY_UP)) obj->move(glm::vec3(0.0, 1.0, 0.0));
		if (PRESS(GLFW_KEY_DOWN)) obj->move(glm::vec3(0.0, -1.0, 0.0));

		if (PRESS(GLFW_KEY_F5)) fp_control = !fp_control;

		if (PRESS(GLFW_KEY_0)) hand_id = 0;
		if (PRESS(GLFW_KEY_0)) hand_id = 0;
		if (PRESS(GLFW_KEY_1)) hand_id = 1;
		if (PRESS(GLFW_KEY_2)) hand_id = 2;
		if (PRESS(GLFW_KEY_3)) hand_id = 3;
		if (PRESS(GLFW_KEY_4)) hand_id = 4;
		if (PRESS(GLFW_KEY_5)) hand_id = 5;
		if (PRESS(GLFW_KEY_6)) hand_id = 6;
		if (PRESS(GLFW_KEY_7)) hand_id = 7;
		if (PRESS(GLFW_KEY_8)) hand_id = 8;
		if (PRESS(GLFW_KEY_9)) hand_id = 9;

		if (PRESS(GLFW_KEY_SPACE)) {
			map.place_block(obj->pos, hand_id);
		}

		if (PRESS(GLFW_KEY_O)) {
			magnify = !magnify;
			if (magnify) projection = glm::perspective(0.3f, aspect_ratio, 0.1f, 1000.0f);
			else projection = glm::perspective(1.3f, aspect_ratio, 0.1f, 1000.0f);
		}

		static Audio::Channel_Vorbis* loop = NULL;
		static Audio::Channel_Triangle* triangle = NULL;
		static Audio::Channel_Sine* sine = NULL;
		if (PRESS(GLFW_KEY_SEMICOLON)) {
			Audio::play_sound("../Media/soft-ping.ogg", false);
		}
		if (PRESS(GLFW_KEY_APOSTROPHE)) {
			if (loop) {
				Audio::stop(loop);
				loop = NULL;
			} else {
				loop = Audio::play_sound("../Media/loop-test.ogg", true, .1f);
			}
		}
		if (PRESS(GLFW_KEY_LEFT_BRACKET)) {
			if (triangle) {
				Audio::stop(triangle);
				triangle = NULL;
			} else {
				triangle = new Audio::Channel_Triangle(440);
				Audio::play_channel(triangle);
			}
		}
		if (PRESS(GLFW_KEY_RIGHT_BRACKET)) {
			if (sine) {
				Audio::stop(sine);
				sine = NULL;
			} else {
				sine = new Audio::Channel_Sine(440);
				Audio::play_channel(sine);
			}
		}
		#undef PRESS
	}
	//-------------------------------------------------------------------------
	// ● 刷新
	//-------------------------------------------------------------------------
	void EditorMainScene::update() {
		if (fp_control)
			ctl->update_control();
		else
			ctl_fp->update_control();
	}
	//-------------------------------------------------------------------------
	// ● 渲染
	//-------------------------------------------------------------------------
	void EditorMainScene::render() {
		shader_textured.use();

		// Setup uniforms
		shader_textured.set_float("brightness", VMDE->state.brightness);
		shader_textured.set_texture("colortex0", &tile_texture, 0);

		postBuffer->bind();
		RenderBuffer::clearColorDepth(0.5, 0.7, 1.0, 0.0);
		postBuffer->set_draw_buffers();

		// Textured blocks rendering
		shader_textured.ProjectionView(projection, view);
		map.render();

		sky->render();

		// Setup uniforms
		// Non textured rendering
		shader_basic.use();
		shader_basic.set_float("opaque", 0.5);
		shader_textured.ProjectionView(projection, view);
		block_pointer.mat[0] = obj->transform();
		block_pointer.update_instance(1);
		block_pointer.render();

		axe.render();

		postBuffer->unbind();
		post_processing.use();
		post_processing.set_texture("colortex", postBuffer->texture_buffer[0], 0);
		post_processing.set_texture("gnormal", postBuffer->texture_buffer[2], 1);
		glm::vec3 sunVec = glm::mat3(view) * glm::vec3(cos(VMath::PI * 0.25), sin(VMath::PI * 0.25), sin(VMath::PI * 0.25) * 0.3f);
		glUniform3f(glGetUniformLocation(post_processing.program, "sunVec"), sunVec.x, sunVec.y, sunVec.z);
		PostProcessingManager::Blit2D();

		// GUI rendering
		gui.use();
		gui.set_texture("atlastex", &tile_texture, 0);
		gui.ProjectionView(gui_2d_projection, glm::mat4(1.0));
		VMSC::disable_depth_test();
		//if (hand_id > 0) clist[hand_id - 1]->render();

		if (SceneManager::render_debug_info) {
			char info[64];
			sprintf(info, "Hand ID: %d  Pointer ID: %d",
				hand_id,
				map.map->tidQuery(obj->pos.x, obj->pos.y, obj->pos.z)
			);
			trex->instanceRenderText(
				info, gui_2d_projection,
				glm::mat4(1.0),
				glm::translate(glm::mat4(1.0), glm::vec3(0.01, 0.88, 0.0)),
				0.025, 0.05, TextRenderer::TextDecorationType::OUTLINE
			);
			sprintf(info, "Pointer pos: (%.0f, %.0f, %.0f)",
				obj->pos.x, obj->pos.y, obj->pos.z
			);
			trex->instanceRenderText(
				info, gui_2d_projection,
				glm::mat4(1.0),
				glm::translate(glm::mat4(1.0), glm::vec3(0.01, 0.82, 0.0)),
				0.025, 0.05, TextRenderer::TextDecorationType::OUTLINE
			);
		}
		VMSC::enable_depth_test();
	}
	//-------------------------------------------------------------------------
	// ● 释放
	//-------------------------------------------------------------------------
	EditorMainScene::~EditorMainScene() {
		for (int i = 0; i < 16; i++) VMDE_Dispose(delete, clist[i]);
		VMDE_Dispose(delete, trex);
		VMDE_Dispose(delete, postBuffer);
	}
}
<commit_msg>恢复手渲染<commit_after>//=============================================================================
// ■ VMGS/Scene/EditorMainScene.cpp
//-----------------------------------------------------------------------------
//   编辑器场景。
//=============================================================================

#include "../VMGS.hpp"

#include "../Control/GodView.hpp"
#include "../Control/FirstPersonView.hpp"
#include "../GameObject/SkyBox/SkyBox.hpp"

namespace VM76 {

	GodView* ctl;
	FirstPersonView* ctl_fp;
	SkyBox* sky;

	bool fp_control = false;

	GDrawable* hand_block;

	//-------------------------------------------------------------------------
	// ● 场景开始
	//-------------------------------------------------------------------------
	EditorMainScene::EditorMainScene() {
		obj = new GObject();

		hand_block = new GDrawable();
		hand_block->data.vertices = new Vertex[4 * 6];
		hand_block->data.indices = new GLuint[6 * 6];

		shader_textured.add_file(GL_VERTEX_SHADER, "../Media/shaders/gbuffers_textured.vsh");
		shader_textured.add_file(GL_FRAGMENT_SHADER, "../Media/shaders/gbuffers_textured.fsh");
		shader_textured.link_program();
		shader_basic.add_file(GL_VERTEX_SHADER, "../Media/shaders/gbuffers_basic.vsh");
		shader_basic.add_file(GL_FRAGMENT_SHADER, "../Media/shaders/gbuffers_basic.fsh");
		shader_basic.link_program();
		gui.add_file(GL_VERTEX_SHADER, "../Media/shaders/gui.vsh");
		gui.add_file(GL_FRAGMENT_SHADER, "../Media/shaders/gui.fsh");
		gui.link_program();
		post_processing.add_file(GL_VERTEX_SHADER, "../Media/shaders/PostProcessing.vsh");
		post_processing.add_file(GL_FRAGMENT_SHADER, "../Media/shaders/PostProcessing.fsh");
		post_processing.link_program();

		GLuint* gbuffers_type = new GLuint[3]{GL_RGB8, GL_RGB16F, GL_RGB8};
		postBuffer = new RenderBuffer(VMDE->width, VMDE->height, 3, gbuffers_type);

		projection = glm::perspective(1.3f, aspect_ratio, 0.1f, 1000.0f);
		view = glm::lookAt(
			glm::vec3(0.0, 2.6, 0.0),
			glm::vec3(0.0, 0.0, 0.0),
			glm::vec3(0.0, 1.0, 0.0)
		);

		// Set up hand block indicator's matrix
		glm::mat4 block_display = glm::translate(
			glm::mat4(1.0),
			glm::vec3(0.02, 0.06, 0.2)
		);
		block_display = glm::scale(block_display, glm::vec3(0.1f));
		block_display = glm::rotate(block_display,
			VMath::PIf / 4.0f,
			glm::vec3(1.0, 0.0, 0.0)
		);
		block_display = glm::rotate(block_display,
			VMath::PIf / 4.0f,
			glm::vec3(0.0, 1.0, 0.0)
		);

		TiledMap::init_cinstances(clist);
		int vtx_c = 0, ind_c = 0;
		for (int i = 0; i < 6; i++) clist[hand_id - 1]->bake(0,0,0,hand_block->data.vertices,hand_block->data.indices,&vtx_c,&ind_c,i);
		hand_block->data.vtx_c = vtx_c;
		hand_block->data.ind_c = ind_c;
		hand_block->data.mat_c = 1;
		hand_block->data.mat = (GLuint*) &block_display;
		hand_block->fbind();
		block_pointer.obj->data.mat_c = 1;

		ctl = new GodView();
		ctl_fp = new FirstPersonView();
		ctl->init_control();
		ctl_fp->init_control();
		ctl->cam.wpos = glm::vec3(64.0, 72.0, 64.0);
		ctl_fp->game_player.wpos = glm::vec3(64.0, 72.0, 64.0);

		sky = new SkyBox("../Media/skybox.png");
	}
	//-------------------------------------------------------------------------
	// ● 按键回调
	//-------------------------------------------------------------------------
	bool magnify = false;
	bool magnifyPrev = false;

	void EditorMainScene::key_callback(
		GLFWwindow* window, int key, int scancode, int action, int mods
	) {
		#define PRESS(n) key == n && action == GLFW_PRESS
		if (PRESS(GLFW_KEY_A)) obj->move(glm::vec3(-1.0, 0.0, 0.0));
		if (PRESS(GLFW_KEY_D)) obj->move(glm::vec3(1.0, 0.0, 0.0));
		if (PRESS(GLFW_KEY_W)) obj->move(glm::vec3(0.0, 0.0, -1.0));
		if (PRESS(GLFW_KEY_S)) obj->move(glm::vec3(0.0, 0.0, 1.0));
		if (PRESS(GLFW_KEY_UP)) obj->move(glm::vec3(0.0, 1.0, 0.0));
		if (PRESS(GLFW_KEY_DOWN)) obj->move(glm::vec3(0.0, -1.0, 0.0));

		if (PRESS(GLFW_KEY_F5)) fp_control = !fp_control;

		if (PRESS(GLFW_KEY_0)) hand_id = 0;
		if (PRESS(GLFW_KEY_1)) hand_id = 1;
		if (PRESS(GLFW_KEY_2)) hand_id = 2;
		if (PRESS(GLFW_KEY_3)) hand_id = 3;
		if (PRESS(GLFW_KEY_4)) hand_id = 4;
		if (PRESS(GLFW_KEY_5)) hand_id = 5;
		if (PRESS(GLFW_KEY_6)) hand_id = 6;
		if (PRESS(GLFW_KEY_7)) hand_id = 7;
		if (PRESS(GLFW_KEY_8)) hand_id = 8;
		if (PRESS(GLFW_KEY_9)) hand_id = 9;

		if (hand_id > 0) {
			int vtx_c = 0, ind_c = 0;
			for (int i = 0; i < 6; i++) clist[hand_id - 1]->bake(0,0,0,hand_block->data.vertices,hand_block->data.indices,&vtx_c,&ind_c,i);
			hand_block->update();
		}

		if (PRESS(GLFW_KEY_SPACE)) {
			map.place_block(obj->pos, hand_id);
		}

		if (PRESS(GLFW_KEY_O)) {
			projection = glm::perspective(0.3f, aspect_ratio, 0.1f, 1000.0f);
		} else if (key == GLFW_KEY_O && action == GLFW_RELEASE) {
			projection = glm::perspective(1.3f, aspect_ratio, 0.1f, 1000.0f);
		}

		static Audio::Channel_Vorbis* loop = NULL;
		static Audio::Channel_Triangle* triangle = NULL;
		static Audio::Channel_Sine* sine = NULL;
		if (PRESS(GLFW_KEY_SEMICOLON)) {
			Audio::play_sound("../Media/soft-ping.ogg", false);
		}
		if (PRESS(GLFW_KEY_APOSTROPHE)) {
			if (loop) {
				Audio::stop(loop);
				loop = NULL;
			} else {
				loop = Audio::play_sound("../Media/loop-test.ogg", true, .1f);
			}
		}
		if (PRESS(GLFW_KEY_LEFT_BRACKET)) {
			if (triangle) {
				Audio::stop(triangle);
				triangle = NULL;
			} else {
				triangle = new Audio::Channel_Triangle(440);
				Audio::play_channel(triangle);
			}
		}
		if (PRESS(GLFW_KEY_RIGHT_BRACKET)) {
			if (sine) {
				Audio::stop(sine);
				sine = NULL;
			} else {
				sine = new Audio::Channel_Sine(440);
				Audio::play_channel(sine);
			}
		}
		#undef PRESS
	}
	//-------------------------------------------------------------------------
	// ● 刷新
	//-------------------------------------------------------------------------
	void EditorMainScene::update() {
		if (fp_control)
			ctl->update_control();
		else
			ctl_fp->update_control();
	}
	//-------------------------------------------------------------------------
	// ● 渲染
	//-------------------------------------------------------------------------
	void EditorMainScene::render() {
		shader_textured.use();

		// Setup uniforms
		shader_textured.set_float("brightness", VMDE->state.brightness);
		shader_textured.set_texture("colortex0", &tile_texture, 0);

		postBuffer->bind();
		RenderBuffer::clearColorDepth(0.5, 0.7, 1.0, 0.0);
		postBuffer->set_draw_buffers();

		// Textured blocks rendering
		shader_textured.ProjectionView(projection, view);
		map.render();

		sky->render();

		// Setup uniforms
		// Non textured rendering
		shader_basic.use();
		shader_basic.set_float("opaque", 0.5);
		shader_textured.ProjectionView(projection, view);
		block_pointer.mat[0] = obj->transform();
		block_pointer.update_instance(1);
		block_pointer.render();

		axe.render();

		postBuffer->unbind();
		post_processing.use();
		post_processing.set_texture("colortex", postBuffer->texture_buffer[0], 0);
		post_processing.set_texture("gnormal", postBuffer->texture_buffer[2], 1);
		glm::vec3 sunVec = glm::mat3(view) * glm::vec3(cos(VMath::PI * 0.25), sin(VMath::PI * 0.25), sin(VMath::PI * 0.25) * 0.3f);
		glUniform3f(glGetUniformLocation(post_processing.program, "sunVec"), sunVec.x, sunVec.y, sunVec.z);
		PostProcessingManager::Blit2D();

		// GUI rendering
		gui.use();
		gui.set_texture("atlastex", &tile_texture, 0);
		gui.ProjectionView(gui_2d_projection, glm::mat4(1.0));
		VMSC::disable_depth_test();
		if (hand_id > 0) hand_block->renderOnce();

		if (SceneManager::render_debug_info) {
			char info[64];
			sprintf(info, "Hand ID: %d  Pointer ID: %d",
				hand_id,
				map.map->tidQuery(obj->pos.x, obj->pos.y, obj->pos.z)
			);
			trex->instanceRenderText(
				info, gui_2d_projection,
				glm::mat4(1.0),
				glm::translate(glm::mat4(1.0), glm::vec3(0.01, 0.88, 0.0)),
				0.025, 0.05, TextRenderer::TextDecorationType::OUTLINE
			);
			sprintf(info, "Pointer pos: (%.0f, %.0f, %.0f)",
				obj->pos.x, obj->pos.y, obj->pos.z
			);
			trex->instanceRenderText(
				info, gui_2d_projection,
				glm::mat4(1.0),
				glm::translate(glm::mat4(1.0), glm::vec3(0.01, 0.82, 0.0)),
				0.025, 0.05, TextRenderer::TextDecorationType::OUTLINE
			);
		}
		VMSC::enable_depth_test();
	}
	//-------------------------------------------------------------------------
	// ● 释放
	//-------------------------------------------------------------------------
	EditorMainScene::~EditorMainScene() {
		for (int i = 0; i < 16; i++) VMDE_Dispose(delete, clist[i]);
		VMDE_Dispose(delete, trex);
		VMDE_Dispose(delete, postBuffer);
	}
}
<|endoftext|>


<commit_before>/****************************************************************************
 *
 *   Copyright (c) 2012-2015 PX4 Development Team. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. 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.
 * 3. Neither the name PX4 nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * 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 OWNER 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 i2c.cpp
 *
 * Base class for devices attached via the I2C bus.
 *
 * @todo Bus frequency changes; currently we do nothing with the value
 *       that is supplied.  Should we just depend on the bus knowing?
 */

#include "i2c.h"
#ifdef __PX4_LINUX
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#endif
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>

#define PX4_SIMULATE_I2C 1
static int simulate = PX4_SIMULATE_I2C;

namespace device
{

I2C::I2C(const char *name,
	 const char *devname,
	 int bus,
	 uint16_t address) :
	// base class
	VDev(name, devname),
	// public
	// protected
	_retries(0),
	// private
	_bus(bus),
	_address(address),
	_fd(-1)
{
	warnx("I2C::I2C name = %s devname = %s", name, devname);
	// fill in _device_id fields for a I2C device
	_device_id.devid_s.bus_type = DeviceBusType_I2C;
	_device_id.devid_s.bus = bus;
	_device_id.devid_s.address = address;
	// devtype needs to be filled in by the driver
	_device_id.devid_s.devtype = 0;
}

I2C::~I2C()
{
	if (_fd >= 0) {
		::close(_fd);
		_fd = -1;
	}
}

int
I2C::init()
{
	int ret = PX4_OK;

	// Assume the driver set the desired bus frequency. There is no standard
	// way to set it from user space.

	// do base class init, which will create device node, etc
	ret = VDev::init();

	if (ret != PX4_OK) {
		debug("VDev::init failed");
		return ret;
	}

	_fd = px4_open(get_devname(), PX4_F_RDONLY | PX4_F_WRONLY);
	if (_fd < 0) {
		debug("px4_open failed of device %s", get_devname());
		return PX4_ERROR;
	}

	if (simulate) {
		_fd = 10000;
	}
	else {
		// Open the actual I2C device and map to the virtual dev name
		_fd = ::open(get_devname(), O_RDWR);
		if (_fd < 0) {
			warnx("could not open %s", get_devname());
			px4_errno = errno;
			return PX4_ERROR;
		}
	}

	return ret;
}

int
I2C::transfer(const uint8_t *send, unsigned send_len, uint8_t *recv, unsigned recv_len)
{
	struct i2c_msg msgv[2];
	unsigned msgs;
	struct i2c_rdwr_ioctl_data packets;
	int ret;
	unsigned retry_count = 0;

	if (_fd < 0) {
       		warnx("I2C device not opened");
		return 1;
	}

	do {
		//	debug("transfer out %p/%u  in %p/%u", send, send_len, recv, recv_len);
		msgs = 0;

		if (send_len > 0) {
			msgv[msgs].addr = _address;
			msgv[msgs].flags = 0;
			msgv[msgs].buf = const_cast<uint8_t *>(send);
			msgv[msgs].len = send_len;
			msgs++;
		}

		if (recv_len > 0) {
			msgv[msgs].addr = _address;
			msgv[msgs].flags = I2C_M_READ;
			msgv[msgs].buf = recv;
			msgv[msgs].len = recv_len;
			msgs++;
		}

		if (msgs == 0)
			return -EINVAL;

		packets.msgs  = msgv;
		packets.nmsgs = msgs;

		if (simulate) {
			warnx("I2C SIM: transfer_4 on %s", get_devname());
			ret = PX4_OK;
		}
		else {
			ret = ::ioctl(_fd, I2C_RDWR, (unsigned long)&packets);
			if (ret < 0) {
				warnx("I2C transfer failed");
				return 1;
			}
		}

		/* success */
		if (ret == PX4_OK)
			break;

	} while (retry_count++ < _retries);

	return ret;
}

int
I2C::transfer(struct i2c_msg *msgv, unsigned msgs)
{
	struct i2c_rdwr_ioctl_data packets;
	int ret;
	unsigned retry_count = 0;

	/* force the device address into the message vector */
	for (unsigned i = 0; i < msgs; i++)
		msgv[i].addr = _address;

	do {
		packets.msgs  = msgv;
		packets.nmsgs = msgs;

		if (simulate) {
			warnx("I2C SIM: transfer_2 on %s", get_devname());
			ret = PX4_OK;
		}
		else {
			ret = ::ioctl(_fd, I2C_RDWR, (unsigned long)&packets);
		}
		if (ret < 0) {
        		warnx("I2C transfer failed");
        		return 1;
    		}

		/* success */
		if (ret == PX4_OK)
			break;

	} while (retry_count++ < _retries);

	return ret;
}

int I2C::ioctl(device::file_t *filp, int cmd, unsigned long arg)
{
	//struct i2c_rdwr_ioctl_data *packets = (i2c_rdwr_ioctl_data *)(void *)arg;

	switch (cmd) {
	case I2C_RDWR:
        	warnx("Use I2C::transfer, not ioctl");
		return 0;
	default:
		/* give it to the superclass */
		return VDev::ioctl(filp, cmd, arg);
	}
}

ssize_t	I2C::read(file_t *filp, char *buffer, size_t buflen)
{
	if (simulate) {
		// FIXME no idea what this should be
		warnx ("2C SIM I2C::read");
		return 0;
	}

	return ::read(_fd, buffer, buflen);
}

ssize_t	I2C::write(file_t *filp, const char *buffer, size_t buflen)
{
	if (simulate) {
		warnx ("2C SIM I2C::write");
		return buflen;
	}

	return ::write(_fd, buffer, buflen);
}

} // namespace device
<commit_msg>Removed annoying "I2C SIM transfer_4" message<commit_after>/****************************************************************************
 *
 *   Copyright (c) 2012-2015 PX4 Development Team. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. 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.
 * 3. Neither the name PX4 nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * 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 OWNER 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 i2c.cpp
 *
 * Base class for devices attached via the I2C bus.
 *
 * @todo Bus frequency changes; currently we do nothing with the value
 *       that is supplied.  Should we just depend on the bus knowing?
 */

#include "i2c.h"
#ifdef __PX4_LINUX
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#endif
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>

#define PX4_SIMULATE_I2C 1
static int simulate = PX4_SIMULATE_I2C;

namespace device
{

I2C::I2C(const char *name,
	 const char *devname,
	 int bus,
	 uint16_t address) :
	// base class
	VDev(name, devname),
	// public
	// protected
	_retries(0),
	// private
	_bus(bus),
	_address(address),
	_fd(-1)
{
	warnx("I2C::I2C name = %s devname = %s", name, devname);
	// fill in _device_id fields for a I2C device
	_device_id.devid_s.bus_type = DeviceBusType_I2C;
	_device_id.devid_s.bus = bus;
	_device_id.devid_s.address = address;
	// devtype needs to be filled in by the driver
	_device_id.devid_s.devtype = 0;
}

I2C::~I2C()
{
	if (_fd >= 0) {
		::close(_fd);
		_fd = -1;
	}
}

int
I2C::init()
{
	int ret = PX4_OK;

	// Assume the driver set the desired bus frequency. There is no standard
	// way to set it from user space.

	// do base class init, which will create device node, etc
	ret = VDev::init();

	if (ret != PX4_OK) {
		debug("VDev::init failed");
		return ret;
	}

	_fd = px4_open(get_devname(), PX4_F_RDONLY | PX4_F_WRONLY);
	if (_fd < 0) {
		debug("px4_open failed of device %s", get_devname());
		return PX4_ERROR;
	}

	if (simulate) {
		_fd = 10000;
	}
	else {
		// Open the actual I2C device and map to the virtual dev name
		_fd = ::open(get_devname(), O_RDWR);
		if (_fd < 0) {
			warnx("could not open %s", get_devname());
			px4_errno = errno;
			return PX4_ERROR;
		}
	}

	return ret;
}

int
I2C::transfer(const uint8_t *send, unsigned send_len, uint8_t *recv, unsigned recv_len)
{
	struct i2c_msg msgv[2];
	unsigned msgs;
	struct i2c_rdwr_ioctl_data packets;
	int ret;
	unsigned retry_count = 0;

	if (_fd < 0) {
       		warnx("I2C device not opened");
		return 1;
	}

	do {
		//	debug("transfer out %p/%u  in %p/%u", send, send_len, recv, recv_len);
		msgs = 0;

		if (send_len > 0) {
			msgv[msgs].addr = _address;
			msgv[msgs].flags = 0;
			msgv[msgs].buf = const_cast<uint8_t *>(send);
			msgv[msgs].len = send_len;
			msgs++;
		}

		if (recv_len > 0) {
			msgv[msgs].addr = _address;
			msgv[msgs].flags = I2C_M_READ;
			msgv[msgs].buf = recv;
			msgv[msgs].len = recv_len;
			msgs++;
		}

		if (msgs == 0)
			return -EINVAL;

		packets.msgs  = msgv;
		packets.nmsgs = msgs;

		if (simulate) {
			//warnx("I2C SIM: transfer_4 on %s", get_devname());
			ret = PX4_OK;
		}
		else {
			ret = ::ioctl(_fd, I2C_RDWR, (unsigned long)&packets);
			if (ret < 0) {
				warnx("I2C transfer failed");
				return 1;
			}
		}

		/* success */
		if (ret == PX4_OK)
			break;

	} while (retry_count++ < _retries);

	return ret;
}

int
I2C::transfer(struct i2c_msg *msgv, unsigned msgs)
{
	struct i2c_rdwr_ioctl_data packets;
	int ret;
	unsigned retry_count = 0;

	/* force the device address into the message vector */
	for (unsigned i = 0; i < msgs; i++)
		msgv[i].addr = _address;

	do {
		packets.msgs  = msgv;
		packets.nmsgs = msgs;

		if (simulate) {
			warnx("I2C SIM: transfer_2 on %s", get_devname());
			ret = PX4_OK;
		}
		else {
			ret = ::ioctl(_fd, I2C_RDWR, (unsigned long)&packets);
		}
		if (ret < 0) {
        		warnx("I2C transfer failed");
        		return 1;
    		}

		/* success */
		if (ret == PX4_OK)
			break;

	} while (retry_count++ < _retries);

	return ret;
}

int I2C::ioctl(device::file_t *filp, int cmd, unsigned long arg)
{
	//struct i2c_rdwr_ioctl_data *packets = (i2c_rdwr_ioctl_data *)(void *)arg;

	switch (cmd) {
	case I2C_RDWR:
        	warnx("Use I2C::transfer, not ioctl");
		return 0;
	default:
		/* give it to the superclass */
		return VDev::ioctl(filp, cmd, arg);
	}
}

ssize_t	I2C::read(file_t *filp, char *buffer, size_t buflen)
{
	if (simulate) {
		// FIXME no idea what this should be
		warnx ("2C SIM I2C::read");
		return 0;
	}

	return ::read(_fd, buffer, buflen);
}

ssize_t	I2C::write(file_t *filp, const char *buffer, size_t buflen)
{
	if (simulate) {
		warnx ("2C SIM I2C::write");
		return buflen;
	}

	return ::write(_fd, buffer, buflen);
}

} // namespace device
<|endoftext|>